From 153554d2b7dd6044e202c6822c1b91d02ed99fb8 Mon Sep 17 00:00:00 2001 From: James Elford Date: Sun, 19 Feb 2017 13:44:21 +0000 Subject: [PATCH 1/4] Store downloaded files in a persistent directory until installation This PR should go some way to addressing #889 by caching downloaded components in a persistent directory until they have been installed. This way, if the download/install process is interrupted, the file that made it can be re-used. This should help ease the pain of intermittent connections --- src/rustup-dist/src/dist.rs | 89 +++++++++++++++++++++++++++- src/rustup-dist/src/lib.rs | 1 + src/rustup-dist/src/manifestation.rs | 38 ++++-------- src/rustup-dist/src/notifications.rs | 8 ++- src/rustup/config.rs | 3 + src/rustup/toolchain.rs | 5 +- 6 files changed, 113 insertions(+), 31 deletions(-) diff --git a/src/rustup-dist/src/dist.rs b/src/rustup-dist/src/dist.rs index c8f811e5cc..0827715a80 100644 --- a/src/rustup-dist/src/dist.rs +++ b/src/rustup-dist/src/dist.rs @@ -8,9 +8,12 @@ use manifest::Component; use manifest::Manifest as ManifestV2; use manifestation::{Manifestation, UpdateStatus, Changes}; -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::ops; +use url::Url; use std::fmt; use std::env; +use std::fs; use regex::Regex; use sha2::{Sha256, Digest}; @@ -482,9 +485,91 @@ pub fn download_and_check<'a>(url_str: &str, pub struct DownloadCfg<'a> { pub dist_root: &'a str, pub temp_cfg: &'a temp::Cfg, + pub download_dir: &'a PathBuf, pub notify_handler: &'a Fn(Notification), } + +pub struct File { + path: PathBuf, +} + +impl ops::Deref for File { + type Target = Path; + + fn deref(&self) -> &Path { + ops::Deref::deref(&self.path) + } +} + +impl<'a> DownloadCfg<'a> { + + fn notify_handler(&self, event: Notification) { + (self.notify_handler)(event); + } + + pub fn download(&self, url: &Url, hash: &str) -> Result { + + try!(utils::ensure_dir_exists("Download Directory", &self.download_dir, &|n| self.notify_handler(n.into()))); + let target_file = self.download_dir.join(Path::new(hash)); + + if target_file.exists() { + let mut hasher = Sha256::new(); + use std::io::Read; + let mut downloaded = try!(fs::File::open(&target_file).chain_err(|| "opening already downloaded file")); + let mut buf = [0; 1024]; + loop { + if let Ok(n) = downloaded.read(&mut buf) { + if n == 0 { break; } + hasher.input(&buf[..n]); + } else { + break; + } + } + let cached_result = hasher.result_str(); + if hash == cached_result { + self.notify_handler(Notification::FileAlreadyDownloaded); + self.notify_handler(Notification::ChecksumValid(&url.to_string())); + return Ok(File { path: target_file, }); + } else { + self.notify_handler(Notification::CachedFileChecksumFailed); + try!(fs::remove_file(&target_file).chain_err(|| "cleaning up previous download")); + } + } + + let mut hasher = Sha256::new(); + + try!(utils::download_file(&url, + &target_file, + Some(&mut hasher), + &|n| self.notify_handler(n.into()))); + + let actual_hash = hasher.result_str(); + + if hash != actual_hash { + // Incorrect hash + return Err(ErrorKind::ChecksumFailed { + url: url.to_string(), + expected: hash.to_string(), + calculated: actual_hash, + }.into()); + } else { + self.notify_handler(Notification::ChecksumValid(&url.to_string())); + return Ok(File { path: target_file, }) + } + } + + pub fn clean(&self, hashes: &Vec) -> Result<()> { + for hash in hashes.iter() { + let used_file = self.download_dir.join(hash); + if self.download_dir.join(&used_file).exists() { + try!(fs::remove_file(used_file).chain_err(|| "cleaning up cached downloads")); + } + } + Ok(()) + } +} + pub fn download_hash(url: &str, cfg: DownloadCfg) -> Result { let hash_url = try!(utils::parse_url(&(url.to_owned() + ".sha256"))); let hash_file = try!(cfg.temp_cfg.new_file()); @@ -551,7 +636,7 @@ pub fn update_from_dist_<'a>(download: DownloadCfg<'a>, Ok(Some((m, hash))) => { return match try!(manifestation.update(&m, changes, - &download.temp_cfg, + &download, download.notify_handler.clone())) { UpdateStatus::Unchanged => Ok(None), UpdateStatus::Changed => Ok(Some(hash)), diff --git a/src/rustup-dist/src/lib.rs b/src/rustup-dist/src/lib.rs index d87cd868aa..694bd6c8fb 100644 --- a/src/rustup-dist/src/lib.rs +++ b/src/rustup-dist/src/lib.rs @@ -7,6 +7,7 @@ extern crate walkdir; extern crate toml; extern crate flate2; extern crate tar; +extern crate url; #[macro_use] extern crate rustup_utils; #[macro_use] diff --git a/src/rustup-dist/src/manifestation.rs b/src/rustup-dist/src/manifestation.rs index c40677d33d..12f45f1647 100644 --- a/src/rustup-dist/src/manifestation.rs +++ b/src/rustup-dist/src/manifestation.rs @@ -3,14 +3,13 @@ use config::Config; use manifest::{Component, Manifest, TargetedPackage}; -use dist::{download_and_check, DownloadCfg, TargetTriple, DEFAULT_DIST_SERVER}; +use dist::{download_and_check, DownloadCfg, TargetTriple, DEFAULT_DIST_SERVER, File}; use component::{Components, Transaction, TarGzPackage, Package}; use temp; use errors::*; use notifications::*; use rustup_utils::utils; use prefix::InstallPrefix; -use sha2::{Sha256, Digest}; use std::path::Path; pub const DIST_MANIFEST: &'static str = "multirust-channel-manifest.toml"; @@ -73,10 +72,11 @@ impl Manifestation { pub fn update(&self, new_manifest: &Manifest, changes: Changes, - temp_cfg: &temp::Cfg, + download_cfg: &DownloadCfg, notify_handler: &Fn(Notification)) -> Result { // Some vars we're going to need a few times + let temp_cfg = download_cfg.temp_cfg; let prefix = self.installation.prefix(); let ref rel_installed_manifest_path = prefix.rel_manifest_file(DIST_MANIFEST); let ref installed_manifest_path = prefix.path().join(rel_installed_manifest_path); @@ -125,7 +125,8 @@ impl Manifestation { let altered = temp_cfg.dist_server != DEFAULT_DIST_SERVER; // Download component packages and validate hashes - let mut things_to_install: Vec<(Component, temp::File)> = Vec::new(); + let mut things_to_install: Vec<(Component, File)> = Vec::new(); + let mut things_downloaded: Vec = Vec::new(); for (component, url, hash) in components_urls_and_hashes { notify_handler(Notification::DownloadingComponent(&component.pkg, @@ -137,32 +138,14 @@ impl Manifestation { url }; - // Download each package to temp file - let temp_file = try!(temp_cfg.new_file()); let url_url = try!(utils::parse_url(&url)); - let mut hasher = Sha256::new(); - try!(utils::download_file(&url_url, - &temp_file, - Some(&mut hasher), - &|n| notify_handler(n.into())).chain_err(|| { + let dowloaded_file = try!(download_cfg.download(&url_url, &hash).chain_err(|| { ErrorKind::ComponentDownloadFailed(component.clone()) })); + things_downloaded.push(hash); - let actual_hash = hasher.result_str(); - - if hash != actual_hash { - // Incorrect hash - return Err(ErrorKind::ChecksumFailed { - url: url, - expected: hash, - calculated: actual_hash, - }.into()); - } else { - notify_handler(Notification::ChecksumValid(&url)); - } - - things_to_install.push((component, temp_file)); + things_to_install.push((component, dowloaded_file)); } // Begin transaction @@ -226,6 +209,8 @@ impl Manifestation { // End transaction tx.commit(); + try!(download_cfg.clean(&things_downloaded)); + Ok(UpdateStatus::Changed) } @@ -315,8 +300,11 @@ impl Manifestation { &self.target_triple, Some(&self.target_triple))); + use std::path::PathBuf; + let dld_dir = PathBuf::from("bogus"); let dlcfg = DownloadCfg { dist_root: "bogus", + download_dir: &dld_dir, temp_cfg: temp_cfg, notify_handler: notify_handler }; diff --git a/src/rustup-dist/src/notifications.rs b/src/rustup-dist/src/notifications.rs index 3f4d9ec4a0..44580597f2 100644 --- a/src/rustup-dist/src/notifications.rs +++ b/src/rustup-dist/src/notifications.rs @@ -18,6 +18,8 @@ pub enum Notification<'a> { NoUpdateHash(&'a Path), ChecksumValid(&'a str), SignatureValid(&'a str), + FileAlreadyDownloaded, + CachedFileChecksumFailed, RollingBack, ExtensionNotInstalled(&'a Component), NonFatalError(&'a Error), @@ -48,6 +50,7 @@ impl<'a> Notification<'a> { Temp(ref n) => n.level(), Utils(ref n) => n.level(), ChecksumValid(_) | NoUpdateHash(_) | + FileAlreadyDownloaded | DownloadingLegacyManifest => NotificationLevel::Verbose, Extracting(_, _) | SignatureValid(_) | DownloadingComponent(_, _, _) | @@ -56,7 +59,7 @@ impl<'a> Notification<'a> { ManifestChecksumFailedHack | RollingBack | DownloadingManifest(_) => NotificationLevel::Info, CantReadUpdateHash(_) | ExtensionNotInstalled(_) | - MissingInstalledComponent(_) => NotificationLevel::Warn, + MissingInstalledComponent(_) | CachedFileChecksumFailed => NotificationLevel::Warn, NonFatalError(_) => NotificationLevel::Error, } } @@ -80,6 +83,8 @@ impl<'a> Display for Notification<'a> { NoUpdateHash(path) => write!(f, "no update hash at: '{}'", path.display()), ChecksumValid(_) => write!(f, "checksum passed"), SignatureValid(_) => write!(f, "signature valid"), + FileAlreadyDownloaded => write!(f, "reusing previously downloaded file"), + CachedFileChecksumFailed => write!(f, "bad checksum for cached download"), RollingBack => write!(f, "rolling back changes"), ExtensionNotInstalled(c) => { write!(f, "extension '{}' was not installed", c.name()) @@ -106,4 +111,3 @@ impl<'a> Display for Notification<'a> { } } } - diff --git a/src/rustup/config.rs b/src/rustup/config.rs index 15b95ec2dc..1ccca4fa03 100644 --- a/src/rustup/config.rs +++ b/src/rustup/config.rs @@ -38,6 +38,7 @@ pub struct Cfg { pub settings_file: SettingsFile, pub toolchains_dir: PathBuf, pub update_hash_dir: PathBuf, + pub download_dir: PathBuf, pub temp_cfg: temp::Cfg, pub gpg_key: Cow<'static, str>, pub env_override: Option, @@ -60,6 +61,7 @@ impl Cfg { let toolchains_dir = multirust_dir.join("toolchains"); let update_hash_dir = multirust_dir.join("update-hashes"); + let download_dir = multirust_dir.join("downloads"); // GPG key let gpg_key = if let Some(path) = env::var_os("RUSTUP_GPG_KEY") @@ -103,6 +105,7 @@ impl Cfg { settings_file: settings_file, toolchains_dir: toolchains_dir, update_hash_dir: update_hash_dir, + download_dir: download_dir, temp_cfg: temp_cfg, gpg_key: gpg_key, notify_handler: notify_handler, diff --git a/src/rustup/toolchain.rs b/src/rustup/toolchain.rs index 7486ee16e2..51f25ba4a1 100644 --- a/src/rustup/toolchain.rs +++ b/src/rustup/toolchain.rs @@ -158,6 +158,7 @@ impl<'a> Toolchain<'a> { dist::DownloadCfg { dist_root: &self.cfg.dist_root_url, temp_cfg: &self.cfg.temp_cfg, + download_dir: &self.cfg.download_dir, notify_handler: &*self.dist_handler, } } @@ -582,7 +583,7 @@ impl<'a> Toolchain<'a> { try!(manifestation.update(&manifest, changes, - self.download_cfg().temp_cfg, + &self.download_cfg(), self.download_cfg().notify_handler.clone())); Ok(()) @@ -631,7 +632,7 @@ impl<'a> Toolchain<'a> { try!(manifestation.update(&manifest, changes, - self.download_cfg().temp_cfg, + &self.download_cfg(), self.download_cfg().notify_handler.clone())); Ok(()) From b1ad3859dbf1c077c84450e4419de42eed0397a7 Mon Sep 17 00:00:00 2001 From: James Elford Date: Sun, 19 Feb 2017 19:01:58 +0000 Subject: [PATCH 2/4] Update tests for the rustup-dist package to pass around DownloadCfg Also replaced the usage of download::DownloadCfg with a simple call to utils::download_file, to simplify the number of names being used in the file. The error behaviour when a component fails to download has changed slightly; underlying errors get wrapped in a ComponentDownloadFailed, rather than propogating out of manifest::update directly. I think this should be okay because the error_chain doesn't throw the info away. --- src/rustup-dist/tests/dist.rs | 177 +++++++++++++++++----------------- 1 file changed, 89 insertions(+), 88 deletions(-) diff --git a/src/rustup-dist/tests/dist.rs b/src/rustup-dist/tests/dist.rs index fac01e2a3b..fc61db268b 100644 --- a/src/rustup-dist/tests/dist.rs +++ b/src/rustup-dist/tests/dist.rs @@ -18,7 +18,7 @@ use rustup_dist::prefix::InstallPrefix; use rustup_dist::ErrorKind; use rustup_dist::errors::Result; use rustup_dist::dist::{ToolchainDesc, TargetTriple, DEFAULT_DIST_SERVER}; -use rustup_dist::download::DownloadCfg; +use rustup_dist::dist; use rustup_dist::Notification; use rustup_utils::utils; use rustup_utils::raw as utils_raw; @@ -27,10 +27,8 @@ use rustup_dist::manifestation::{Manifestation, UpdateStatus, Changes}; use rustup_dist::manifest::{Manifest, Component}; use url::Url; use std::fs; -use std::io::Write; use std::path::Path; use tempdir::TempDir; -use itertools::Itertools; // Creates a mock dist server populated with some test data pub fn create_mock_dist_server(path: &Path, @@ -268,17 +266,14 @@ fn update_from_dist(dist_server: &Url, prefix: &InstallPrefix, add: &[Component], remove: &[Component], + download_cfg: &dist::DownloadCfg, temp_cfg: &temp::Cfg, notify_handler: &Fn(Notification)) -> Result { // Download the dist manifest and place it into the installation prefix let ref manifest_url = try!(make_manifest_url(dist_server, toolchain)); - let download = DownloadCfg { - temp_cfg: temp_cfg, - notify_handler: notify_handler.clone(), - gpg_key: None, - }; - let manifest_file = try!(download.get(manifest_url.as_str())); + let manifest_file = try!(temp_cfg.new_file()); + try!(utils::download_file(manifest_url, &manifest_file, None, &|_| {})); let manifest_str = try!(utils::read_file("manifest", &manifest_file)); let manifest = try!(Manifest::parse(&manifest_str)); @@ -291,7 +286,7 @@ fn update_from_dist(dist_server: &Url, remove_extensions: remove.to_owned(), }; - manifestation.update(&manifest, changes, temp_cfg, notify_handler.clone()) + manifestation.update(&manifest, changes, download_cfg, notify_handler.clone()) } fn make_manifest_url(dist_server: &Url, toolchain: &ToolchainDesc) -> Result { @@ -311,7 +306,7 @@ fn uninstall(toolchain: &ToolchainDesc, prefix: &InstallPrefix, temp_cfg: &temp: } fn setup(edit: Option<&Fn(&str, &mut MockPackage)>, - f: &Fn(&Url, &ToolchainDesc, &InstallPrefix, &temp::Cfg)) { + f: &Fn(&Url, &ToolchainDesc, &InstallPrefix, &dist::DownloadCfg, &temp::Cfg)) { let dist_tempdir = TempDir::new("multirust").unwrap(); create_mock_dist_server(dist_tempdir.path(), edit).write(&[ManifestVersion::V2]); @@ -325,14 +320,20 @@ fn setup(edit: Option<&Fn(&str, &mut MockPackage)>, let ref url = Url::parse(&format!("file://{}", dist_tempdir.path().to_string_lossy())).unwrap(); let ref toolchain = ToolchainDesc::from_str("nightly-x86_64-apple-darwin").unwrap(); let ref prefix = InstallPrefix::from(prefix_tempdir.path().to_owned()); + let ref download_cfg = dist::DownloadCfg { + dist_root: "phony", + temp_cfg: temp_cfg, + download_dir: &prefix.path().to_owned().join("downloads"), + notify_handler: &|_|{} + }; - f(url, toolchain, prefix, temp_cfg); + f(url, toolchain, prefix, download_cfg, temp_cfg); } #[test] fn initial_install() { - setup(None, &|url, toolchain, prefix, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/rustc"))); assert!(utils::path_exists(&prefix.path().join("lib/libstd.rlib"))); @@ -341,8 +342,8 @@ fn initial_install() { #[test] fn test_uninstall() { - setup(None, &|url, toolchain, prefix, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); uninstall(toolchain, prefix, temp_cfg, &|_| ()).unwrap(); assert!(!utils::path_exists(&prefix.path().join("bin/rustc"))); @@ -352,8 +353,8 @@ fn test_uninstall() { #[test] fn uninstall_removes_config_file() { - setup(None, &|url, toolchain, prefix, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.manifest_file("multirust-config.toml"))); uninstall(toolchain, prefix, temp_cfg, &|_| ()).unwrap(); assert!(!utils::path_exists(&prefix.manifest_file("multirust-config.toml"))); @@ -362,12 +363,12 @@ fn uninstall_removes_config_file() { #[test] fn upgrade() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert_eq!("2016-02-01", utils_raw::read_file(&prefix.path().join("bin/rustc")).unwrap()); change_channel_date(url, "nightly", "2016-02-02"); - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert_eq!("2016-02-02", utils_raw::read_file(&prefix.path().join("bin/rustc")).unwrap()); }); } @@ -384,19 +385,19 @@ fn update_removes_components_that_dont_exist() { }); } }; - setup(Some(edit), &|url, toolchain, prefix, temp_cfg| { + setup(Some(edit), &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/bonus"))); change_channel_date(url, "nightly", "2016-02-02"); - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(!utils::path_exists(&prefix.path().join("bin/bonus"))); }); } #[test] fn update_preserves_extensions() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { let ref adds = vec![ Component { pkg: "rust-std".to_string(), target: Some(TargetTriple::from_str("i686-apple-darwin")) @@ -407,13 +408,13 @@ fn update_preserves_extensions() { ]; change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); change_channel_date(url, "nightly", "2016-02-02"); - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -438,7 +439,7 @@ fn update_preserves_extensions_that_became_components() { }); } }; - setup(Some(edit), &|url, toolchain, prefix, temp_cfg| { + setup(Some(edit), &|url, toolchain, prefix, download_cfg, temp_cfg| { let ref adds = vec![ Component { pkg: "bonus".to_string(), target: Some(TargetTriple::from_str("x86_64-apple-darwin")) @@ -446,12 +447,12 @@ fn update_preserves_extensions_that_became_components() { ]; change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/bonus"))); change_channel_date(url, "nightly", "2016-02-02"); - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/bonus"))); }); } @@ -474,29 +475,29 @@ fn update_preserves_components_that_became_extensions() { }); } }; - setup(Some(edit), &|url, toolchain, prefix, temp_cfg| { + setup(Some(edit), &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/bonus"))); change_channel_date(url, "nightly", "2016-02-02"); - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/bonus"))); }); } #[test] fn update_makes_no_changes_for_identical_manifest() { - setup(None, &|url, toolchain, prefix, temp_cfg| { - let status = update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { + let status = update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert_eq!(status, UpdateStatus::Changed); - let status = update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + let status = update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert_eq!(status, UpdateStatus::Unchanged); }); } #[test] fn add_extensions_for_initial_install() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { let ref adds = vec![ Component { pkg: "rust-std".to_string(), target: Some(TargetTriple::from_str("i686-apple-darwin")) @@ -506,7 +507,7 @@ fn add_extensions_for_initial_install() { } ]; - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); }); @@ -514,8 +515,8 @@ fn add_extensions_for_initial_install() { #[test] fn add_extensions_for_same_manifest() { - setup(None, &|url, toolchain, prefix, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); let ref adds = vec![ Component { @@ -526,7 +527,7 @@ fn add_extensions_for_same_manifest() { } ]; - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -535,10 +536,10 @@ fn add_extensions_for_same_manifest() { #[test] fn add_extensions_for_upgrade() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); change_channel_date(url, "nightly", "2016-02-02"); @@ -551,7 +552,7 @@ fn add_extensions_for_upgrade() { } ]; - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -561,28 +562,28 @@ fn add_extensions_for_upgrade() { #[test] #[should_panic] fn add_extension_not_in_manifest() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { let ref adds = vec![ Component { pkg: "rust-bogus".to_string(), target: Some(TargetTriple::from_str("i686-apple-darwin")) }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); }); } #[test] #[should_panic] fn add_extension_that_is_required_component() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { let ref adds = vec![ Component { pkg: "rustc".to_string(), target: Some(TargetTriple::from_str("x86_64-apple-darwin")) }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); }); } @@ -598,8 +599,8 @@ fn add_extensions_for_same_manifest_when_extension_already_installed() { #[test] fn add_extensions_does_not_remove_other_components() { - setup(None, &|url, toolchain, prefix, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); let ref adds = vec![ Component { @@ -607,7 +608,7 @@ fn add_extensions_does_not_remove_other_components() { }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/rustc"))); }); @@ -617,20 +618,20 @@ fn add_extensions_does_not_remove_other_components() { #[test] #[should_panic] fn remove_extensions_for_initial_install() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { let ref removes = vec![ Component { pkg: "rustc".to_string(), target: Some(TargetTriple::from_str("x86_64-apple-darwin")) }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); }); } #[test] fn remove_extensions_for_same_manifest() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { let ref adds = vec![ Component { pkg: "rust-std".to_string(), target: Some(TargetTriple::from_str("i686-apple-darwin")) @@ -640,7 +641,7 @@ fn remove_extensions_for_same_manifest() { } ]; - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); let ref removes = vec![ Component { @@ -648,7 +649,7 @@ fn remove_extensions_for_same_manifest() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(!utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -657,7 +658,7 @@ fn remove_extensions_for_same_manifest() { #[test] fn remove_extensions_for_upgrade() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); let ref adds = vec![ @@ -669,7 +670,7 @@ fn remove_extensions_for_upgrade() { } ]; - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); change_channel_date(url, "nightly", "2016-02-02"); @@ -679,7 +680,7 @@ fn remove_extensions_for_upgrade() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(!utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -689,10 +690,10 @@ fn remove_extensions_for_upgrade() { #[test] #[should_panic] fn remove_extension_not_in_manifest() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); change_channel_date(url, "nightly", "2016-02-02"); @@ -702,7 +703,7 @@ fn remove_extension_not_in_manifest() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); }); } @@ -721,7 +722,7 @@ fn remove_extension_not_in_manifest_but_is_already_installed() { }); } }; - setup(Some(edit), &|url, toolchain, prefix, temp_cfg| { + setup(Some(edit), &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); let ref adds = vec![ @@ -729,7 +730,7 @@ fn remove_extension_not_in_manifest_but_is_already_installed() { pkg: "bonus".to_string(), target: Some(TargetTriple::from_str("x86_64-apple-darwin")) }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/bonus"))); change_channel_date(url, "nightly", "2016-02-02"); @@ -739,15 +740,15 @@ fn remove_extension_not_in_manifest_but_is_already_installed() { pkg: "bonus".to_string(), target: Some(TargetTriple::from_str("x86_64-apple-darwin")) }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); }); } #[test] #[should_panic] fn remove_extension_that_is_required_component() { - setup(None, &|url, toolchain, prefix, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); let ref removes = vec![ Component { @@ -755,15 +756,15 @@ fn remove_extension_that_is_required_component() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); }); } #[test] #[should_panic] fn remove_extension_not_installed() { - setup(None, &|url, toolchain, prefix, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); let ref removes = vec![ Component { @@ -771,7 +772,7 @@ fn remove_extension_not_installed() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); }); } @@ -782,14 +783,14 @@ fn remove_extensions_for_same_manifest_does_not_reinstall_other_components() { #[test] fn remove_extensions_does_not_remove_other_components() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { let ref adds = vec![ Component { pkg: "rust-std".to_string(), target: Some(TargetTriple::from_str("i686-apple-darwin")) }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); let ref removes = vec![ Component { @@ -797,7 +798,7 @@ fn remove_extensions_does_not_remove_other_components() { }, ]; - update_from_dist(url, toolchain, prefix, &[], removes, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, &[], removes, download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("bin/rustc"))); }); @@ -805,7 +806,7 @@ fn remove_extensions_does_not_remove_other_components() { #[test] fn add_and_remove_for_upgrade() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { change_channel_date(url, "nightly", "2016-02-01"); let ref adds = vec![ @@ -814,7 +815,7 @@ fn add_and_remove_for_upgrade() { }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); change_channel_date(url, "nightly", "2016-02-02"); @@ -830,7 +831,7 @@ fn add_and_remove_for_upgrade() { }, ]; - update_from_dist(url, toolchain, prefix, adds, removes, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, removes, download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(!utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -839,14 +840,14 @@ fn add_and_remove_for_upgrade() { #[test] fn add_and_remove() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { let ref adds = vec![ Component { pkg: "rust-std".to_string(), target: Some(TargetTriple::from_str("i686-unknown-linux-gnu")) }, ]; - update_from_dist(url, toolchain, prefix, adds, &[], temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, &[], download_cfg, temp_cfg, &|_| ()).unwrap(); let ref adds = vec![ Component { @@ -860,7 +861,7 @@ fn add_and_remove() { }, ]; - update_from_dist(url, toolchain, prefix, adds, removes, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, removes, download_cfg, temp_cfg, &|_| ()).unwrap(); assert!(utils::path_exists(&prefix.path().join("lib/i686-apple-darwin/libstd.rlib"))); assert!(!utils::path_exists(&prefix.path().join("lib/i686-unknown-linux-gnu/libstd.rlib"))); @@ -870,8 +871,8 @@ fn add_and_remove() { #[test] #[should_panic] fn add_and_remove_same_component() { - setup(None, &|url, toolchain, prefix, temp_cfg| { - update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap(); + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap(); let ref adds = vec![ Component { @@ -885,21 +886,21 @@ fn add_and_remove_same_component() { }, ]; - update_from_dist(url, toolchain, prefix, adds, removes, temp_cfg, &|_| ()).unwrap(); + update_from_dist(url, toolchain, prefix, adds, removes, download_cfg, temp_cfg, &|_| ()).unwrap(); }); } #[test] fn bad_component_hash() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { let path = url.to_file_path().unwrap(); let path = path.join("dist/2016-02-02/rustc-nightly-x86_64-apple-darwin.tar.gz"); utils_raw::write_file(&path, "bogus").unwrap(); - let err = update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap_err(); + let err = update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap_err(); match *err.kind() { - ErrorKind::ChecksumFailed { .. } => (), + ErrorKind::ComponentDownloadFailed(_) => (), _ => panic!() } }); @@ -907,12 +908,12 @@ fn bad_component_hash() { #[test] fn unable_to_download_component() { - setup(None, &|url, toolchain, prefix, temp_cfg| { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { let path = url.to_file_path().unwrap(); let path = path.join("dist/2016-02-02/rustc-nightly-x86_64-apple-darwin.tar.gz"); fs::remove_file(&path).unwrap(); - let err = update_from_dist(url, toolchain, prefix, &[], &[], temp_cfg, &|_| ()).unwrap_err(); + let err = update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| ()).unwrap_err(); match *err.kind() { ErrorKind::ComponentDownloadFailed(..) => (), From 8ab727af35d92dac41b41d6872d76ba6a3e7ae65 Mon Sep 17 00:00:00 2001 From: James Elford Date: Fri, 17 Mar 2017 16:43:18 +0000 Subject: [PATCH 3/4] Remove unused variant of `download` utility that didn't accept a path parameter --- src/download/src/lib.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/download/src/lib.rs b/src/download/src/lib.rs index 3f726289c7..821ce5fc17 100644 --- a/src/download/src/lib.rs +++ b/src/download/src/lib.rs @@ -47,21 +47,6 @@ pub fn download(url: &Url, Err("no working backends".into()) } -pub fn download_to_path(url: &Url, - path: &Path, - callback: Option<&Fn(Event) -> Result<()>>) - -> Result<()> { - for &backend in BACKENDS { - match download_to_path_with_backend(backend, url, path, callback) { - Err(Error(ErrorKind::BackendUnavailable(_), _)) => (), - Err(e) => return Err(e), - Ok(()) => return Ok(()), - } - } - - Err("no working backends".into()) -} - pub fn download_with_backend(backend: Backend, url: &Url, callback: &Fn(Event) -> Result<()>) From 2ce8d7216fe708dd15417a8310518463c807e555 Mon Sep 17 00:00:00 2001 From: James Elford Date: Mon, 20 Mar 2017 12:38:09 +0000 Subject: [PATCH 4/4] Test that previously-downloaded files are found and re-used The test is only that the file-reuse notification is fired. In order to detect this in the test, I've pushed through the notification handler that gets given to `manifestation.update`, which in practice is nearly always a clone of the one in DownloadCfg (but notably not when called as in the test cases). --- src/rustup-dist/src/dist.rs | 18 ++++----- src/rustup-dist/src/manifestation.rs | 2 +- src/rustup-dist/tests/dist.rs | 58 ++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/rustup-dist/src/dist.rs b/src/rustup-dist/src/dist.rs index 0827715a80..f16499ac11 100644 --- a/src/rustup-dist/src/dist.rs +++ b/src/rustup-dist/src/dist.rs @@ -504,13 +504,9 @@ impl ops::Deref for File { impl<'a> DownloadCfg<'a> { - fn notify_handler(&self, event: Notification) { - (self.notify_handler)(event); - } - - pub fn download(&self, url: &Url, hash: &str) -> Result { + pub fn download(&self, url: &Url, hash: &str, notify_handler: &'a Fn(Notification)) -> Result { - try!(utils::ensure_dir_exists("Download Directory", &self.download_dir, &|n| self.notify_handler(n.into()))); + try!(utils::ensure_dir_exists("Download Directory", &self.download_dir, &|n| notify_handler(n.into()))); let target_file = self.download_dir.join(Path::new(hash)); if target_file.exists() { @@ -528,11 +524,11 @@ impl<'a> DownloadCfg<'a> { } let cached_result = hasher.result_str(); if hash == cached_result { - self.notify_handler(Notification::FileAlreadyDownloaded); - self.notify_handler(Notification::ChecksumValid(&url.to_string())); + notify_handler(Notification::FileAlreadyDownloaded); + notify_handler(Notification::ChecksumValid(&url.to_string())); return Ok(File { path: target_file, }); } else { - self.notify_handler(Notification::CachedFileChecksumFailed); + notify_handler(Notification::CachedFileChecksumFailed); try!(fs::remove_file(&target_file).chain_err(|| "cleaning up previous download")); } } @@ -542,7 +538,7 @@ impl<'a> DownloadCfg<'a> { try!(utils::download_file(&url, &target_file, Some(&mut hasher), - &|n| self.notify_handler(n.into()))); + &|n| notify_handler(n.into()))); let actual_hash = hasher.result_str(); @@ -554,7 +550,7 @@ impl<'a> DownloadCfg<'a> { calculated: actual_hash, }.into()); } else { - self.notify_handler(Notification::ChecksumValid(&url.to_string())); + notify_handler(Notification::ChecksumValid(&url.to_string())); return Ok(File { path: target_file, }) } } diff --git a/src/rustup-dist/src/manifestation.rs b/src/rustup-dist/src/manifestation.rs index 12f45f1647..db6d9ef9d7 100644 --- a/src/rustup-dist/src/manifestation.rs +++ b/src/rustup-dist/src/manifestation.rs @@ -140,7 +140,7 @@ impl Manifestation { let url_url = try!(utils::parse_url(&url)); - let dowloaded_file = try!(download_cfg.download(&url_url, &hash).chain_err(|| { + let dowloaded_file = try!(download_cfg.download(&url_url, &hash, ¬ify_handler).chain_err(|| { ErrorKind::ComponentDownloadFailed(component.clone()) })); things_downloaded.push(hash); diff --git a/src/rustup-dist/tests/dist.rs b/src/rustup-dist/tests/dist.rs index fc61db268b..17b62a48fe 100644 --- a/src/rustup-dist/tests/dist.rs +++ b/src/rustup-dist/tests/dist.rs @@ -26,8 +26,11 @@ use rustup_dist::temp; use rustup_dist::manifestation::{Manifestation, UpdateStatus, Changes}; use rustup_dist::manifest::{Manifest, Component}; use url::Url; +use std::cell::Cell; use std::fs; use std::path::Path; +use std::sync::Arc; + use tempdir::TempDir; // Creates a mock dist server populated with some test data @@ -921,3 +924,58 @@ fn unable_to_download_component() { } }); } + +fn prevent_installation(prefix: &InstallPrefix) { + utils::ensure_dir_exists("installation path", &prefix.path().join("lib"), &|_|{}).unwrap(); + let install_blocker = prefix.path().join("lib").join("rustlib"); + utils::write_file("install-blocker", &install_blocker, "fail-installation").unwrap(); +} + +fn allow_installation(prefix: &InstallPrefix) { + let install_blocker = prefix.path().join("lib").join("rustlib"); + utils::remove_file("install-blocker", &install_blocker).unwrap(); +} + +#[test] +fn reuse_downloaded_file() { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { + + prevent_installation(prefix); + + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|_| {}).unwrap_err(); + + allow_installation(&prefix); + + let reuse_notification_fired = Arc::new(Cell::new(false)); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|n| { + if let Notification::FileAlreadyDownloaded = n { + reuse_notification_fired.set(true); + } + }).unwrap(); + + assert!(reuse_notification_fired.get()); + }) +} + +#[test] +fn checks_files_hashes_before_reuse() { + setup(None, &|url, toolchain, prefix, download_cfg, temp_cfg| { + + let path = url.to_file_path().unwrap(); + let target_hash = utils::read_file("target hash", &path.join("dist/2016-02-02/rustc-nightly-x86_64-apple-darwin.tar.gz.sha256")).unwrap()[.. 64].to_owned(); + let prev_download = download_cfg.download_dir.join(target_hash); + utils::ensure_dir_exists("download dir", &download_cfg.download_dir, &|_|{}).unwrap(); + utils::write_file("bad previous download", &prev_download, "bad content").unwrap(); + println!("wrote previous download to {}", prev_download.display()); + + let noticed_bad_checksum = Arc::new(Cell::new(false)); + update_from_dist(url, toolchain, prefix, &[], &[], download_cfg, temp_cfg, &|n| { + println!("{:?}", n); + if let Notification::CachedFileChecksumFailed = n { + noticed_bad_checksum.set(true); + } + }).unwrap(); + + assert!(noticed_bad_checksum.get()); + }) +}