Smoke test for git support
authorCarl Lerche + Yehuda Katz <engineering@tilde.io>
Thu, 12 Jun 2014 22:51:16 +0000 (15:51 -0700)
committerTim Carey-Smith <tim@spork.in>
Thu, 12 Jun 2014 22:51:16 +0000 (15:51 -0700)
12 files changed:
Makefile
src/bin/cargo-compile.rs
src/cargo/ops/cargo_compile.rs
src/cargo/sources/git/source.rs
src/cargo/sources/git/utils.rs
src/cargo/util/result.rs
tests/support.rs [deleted file]
tests/support/mod.rs [new file with mode: 0644]
tests/support/paths.rs [new file with mode: 0644]
tests/test_cargo_compile.rs
tests/test_cargo_compile_git_deps.rs [new file with mode: 0644]
tests/tests.rs

index 0a44bd9f136157e393e1d6466370526a2997a1c4..edb5b69095f340e24bab3398e3ef5ce0b592b246 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -47,7 +47,7 @@ $(BIN_TARGETS): target/%: src/bin/%.rs $(HAMMER) $(TOML) $(LIBCARGO)
 
 # === Tests
 
-TEST_SRC = $(wildcard tests/*.rs)
+TEST_SRC = $(shell find tests -name '*.rs')
 TEST_DEPS = $(DEPS) -L libs/hamcrest-rust/target
 
 target/tests/test-integration: $(HAMCREST) $(TEST_SRC) $(BIN_TARGETS)
index b4eae3c0fc67e6e18f350bd18b795932e2bcea45..fd741c691c7d5e42c5c8d1626bcf3c5bd687c7c3 100644 (file)
@@ -13,7 +13,7 @@ use hammer::FlagConfig;
 use cargo::{execute_main_without_stdin,CLIResult,CLIError,ToResult};
 use cargo::ops;
 use cargo::util::important_paths::find_project;
-use cargo::util::ToCLI;
+use cargo::util::{ToCLI,simple_human};
 
 #[deriving(PartialEq,Clone,Decodable,Encodable)]
 pub struct Options {
index 072b2bc95fceba822c3c8fc9dd569f8b7a572d62..5591b27a556b649d86f8dce999f7e9f6cca0cd43 100644 (file)
@@ -35,6 +35,7 @@ pub fn compile(manifest_path: &Path) -> CargoResult<()> {
 
     let sources = try!(sources_for(&package));
 
+    try!(sources.update().wrap("unable to update sources"));
     let summaries = try!(sources.list().wrap("unable to list packages from source"));
     let resolved = try!(resolve(package.get_dependencies(), &summaries).wrap("unable to resolve dependencies"));
 
index 2e2de1b58d45f350a3f10e04cf01c317bb3eae7d..a3872be8e17223fdaf2fcba25b30f1939100da0c 100644 (file)
@@ -37,6 +37,7 @@ impl Show for GitSource {
 
 impl Source for GitSource {
     fn update(&self) -> CargoResult<()> {
+        log!(5, "updating git source `{}`", self.remote);
         let repo = try!(self.remote.checkout(&self.db_path));
         try!(repo.copy_to(self.reference.as_slice(), &self.checkout_path));
 
@@ -44,6 +45,7 @@ impl Source for GitSource {
     }
 
     fn list(&self) -> CargoResult<Vec<Summary>> {
+        log!(5, "listing summaries in git source `{}`", self.remote);
         let pkg = try!(read_manifest(&self.checkout_path, self.get_namespace()));
         Ok(vec!(pkg.get_summary().clone()))
     }
@@ -53,6 +55,7 @@ impl Source for GitSource {
     }
 
     fn get(&self, package_ids: &[PackageId]) -> CargoResult<Vec<Package>> {
+        log!(5, "getting packages for package ids `{}` from `{}`", package_ids, self.remote);
         // TODO: Support multiple manifests per repo
         let pkg = try!(read_manifest(&self.checkout_path, self.remote.get_url()));
 
index 7218ee41fb1e8ac2781ea2706b735790fdc76ab0..4118364cd25153f48460facdbcfde817c1590798 100644 (file)
@@ -67,7 +67,7 @@ macro_rules! errln(
  * GitRemote represents a remote repository. It gets cloned into a local GitDatabase.
  */
 
-#[deriving(PartialEq,Clone)]
+#[deriving(PartialEq,Clone,Show)]
 pub struct GitRemote {
     url: Url,
     verbose: bool
@@ -170,7 +170,7 @@ impl GitRemote {
     }
 
     fn fetch_into(&self, path: &Path) -> CargoResult<()> {
-        Ok(git!(*path, self.verbose, "fetch --force --quiet --tags {} refs/heads/*:refs/heads/*", self.url))
+        Ok(git!(*path, self.verbose, "fetch --force --quiet --tags {} refs/heads/*:refs/heads/*", self.fetch_location()))
     }
 
     fn clone_into(&self, path: &Path) -> CargoResult<()> {
@@ -179,7 +179,14 @@ impl GitRemote {
         try!(mkdir_recursive(path, UserDir).map_err(|err|
             human_error(format!("Couldn't recursively create `{}`", dirname.display()), format!("path={}", dirname.display()), io_error(err))));
 
-        Ok(git!(dirname, self.verbose, "clone {} {} --bare --no-hardlinks --quiet", self.url, path.display()))
+        Ok(git!(dirname, self.verbose, "clone {} {} --bare --no-hardlinks --quiet", self.fetch_location(), path.display()))
+    }
+
+    fn fetch_location(&self) -> String {
+        match self.url.scheme.as_slice() {
+            "file" => self.url.path.clone(),
+            _ => self.url.to_str()
+        }
     }
 }
 
@@ -254,9 +261,7 @@ impl GitCheckout {
 }
 
 fn git(path: &Path, verbose: bool, str: &str) -> ProcessBuilder {
-    if verbose {
-        errln!("Executing git {} @ {}", str, path.display());
-    }
+    debug!("Executing git {} @ {}", str, path.display());
 
     process("git").args(str.split(' ').collect::<Vec<&str>>().as_slice()).cwd(path.clone())
 }
index e0419e1626b9c2e00b563bdacb8f5796ec1a17cf..8d26fc1a5e3af0d8389e8a2cfe5bf1e3c458ff75 100644 (file)
@@ -116,14 +116,14 @@ impl CargoError {
             CargoError { kind: HumanReadableError, desc: BoxedDescription(desc), detail: detail, .. } => {
                 CLIError::new(desc, detail, exit_code)
             },
-            CargoError { kind: InternalError, desc: StaticDescription(desc), detail: None, .. } => {
-                CLIError::new("An unexpected error occurred", Some(desc), exit_code)
+            ref err @ CargoError { kind: InternalError, desc: StaticDescription(desc), detail: None, .. } => {
+                CLIError::new(format!("An unexpected error occurred: {}", err), Some(desc), exit_code)
             },
-            CargoError { kind: InternalError, desc: StaticDescription(desc), detail: Some(detail), .. } => {
-                CLIError::new("An unexpected error occurred", Some(format!("{}\n{}", desc, detail)), exit_code)
+            ref err @ CargoError { kind: InternalError, desc: StaticDescription(desc), detail: Some(ref detail), .. } => {
+                CLIError::new(format!("An unexpected error occurred: {}", err), Some(format!("{}\n{}", desc, detail)), exit_code)
             },
-            _ => {
-                CLIError::new("An unexpected error occurred", None::<&str>, exit_code)
+            ref err @ _ => {
+                CLIError::new(format!("An unexpected error occurred: {}", err), None::<&str>, exit_code)
             }
         }
     }
diff --git a/tests/support.rs b/tests/support.rs
deleted file mode 100644 (file)
index ec4f617..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-// use std::io::fs::{mkdir_recursive,rmdir_recursive};
-use std;
-use std::io;
-use std::io::fs;
-use std::io::process::{ProcessOutput,ProcessExit};
-use std::os;
-use std::path::{Path,BytesContainer};
-use std::str;
-use std::vec::Vec;
-use std::fmt::Show;
-use ham = hamcrest;
-use cargo::core::shell;
-use cargo::util::{process,ProcessBuilder,CargoError};
-use cargo::util::result::ProcessError;
-
-static CARGO_INTEGRATION_TEST_DIR : &'static str = "cargo-integration-tests";
-
-/*
- *
- * ===== Builders =====
- *
- */
-
-#[deriving(PartialEq,Clone)]
-struct FileBuilder {
-    path: Path,
-    body: String
-}
-
-impl FileBuilder {
-    pub fn new(path: Path, body: &str) -> FileBuilder {
-        FileBuilder { path: path, body: body.to_str() }
-    }
-
-    fn mk(&self) -> Result<(), String> {
-        try!(mkdir_recursive(&self.dirname()));
-
-        let mut file = try!(
-            fs::File::create(&self.path)
-                .with_err_msg(format!("Could not create file; path={}", self.path.display())));
-
-        file.write_str(self.body.as_slice())
-            .with_err_msg(format!("Could not write to file; path={}", self.path.display()))
-    }
-
-    fn dirname(&self) -> Path {
-        Path::new(self.path.dirname())
-    }
-}
-
-#[deriving(PartialEq,Clone)]
-struct ProjectBuilder {
-    name: String,
-    root: Path,
-    files: Vec<FileBuilder>
-}
-
-impl ProjectBuilder {
-    pub fn new(name: &str, root: Path) -> ProjectBuilder {
-        ProjectBuilder {
-            name: name.to_str(),
-            root: root,
-            files: vec!()
-        }
-    }
-
-    pub fn root(&self) -> Path {
-      self.root.clone()
-    }
-
-    pub fn cargo_process(&self, program: &str) -> ProcessBuilder {
-        self.build();
-
-        process(program)
-            .cwd(self.root())
-            .extra_path(cargo_dir())
-    }
-
-    pub fn file<B: BytesContainer>(mut self, path: B, body: &str) -> ProjectBuilder {
-        self.files.push(FileBuilder::new(self.root.join(path), body));
-        self
-    }
-
-    // TODO: return something different than a ProjectBuilder
-    pub fn build<'a>(&'a self) -> &'a ProjectBuilder {
-        match self.build_with_result() {
-            Err(e) => fail!(e),
-            _ => return self
-        }
-    }
-
-    pub fn build_with_result(&self) -> Result<(), String> {
-        // First, clean the directory if it already exists
-        try!(self.rm_root());
-
-        // Create the empty directory
-        try!(mkdir_recursive(&self.root));
-
-        for file in self.files.iter() {
-          try!(file.mk());
-        }
-
-        Ok(())
-    }
-
-    fn rm_root(&self) -> Result<(), String> {
-        if self.root.exists() {
-            rmdir_recursive(&self.root)
-        }
-        else {
-            Ok(())
-        }
-    }
-}
-
-// Generates a project layout
-pub fn project(name: &str) -> ProjectBuilder {
-    ProjectBuilder::new(name, os::tmpdir().join(CARGO_INTEGRATION_TEST_DIR))
-}
-
-// === Helpers ===
-
-pub fn mkdir_recursive(path: &Path) -> Result<(), String> {
-    fs::mkdir_recursive(path, io::UserDir)
-        .with_err_msg(format!("could not create directory; path={}", path.display()))
-}
-
-pub fn rmdir_recursive(path: &Path) -> Result<(), String> {
-    fs::rmdir_recursive(path)
-        .with_err_msg(format!("could not rm directory; path={}", path.display()))
-}
-
-trait ErrMsg<T> {
-    fn with_err_msg(self, val: String) -> Result<T, String>;
-}
-
-impl<T, E: Show> ErrMsg<T> for Result<T, E> {
-    fn with_err_msg(self, val: String) -> Result<T, String> {
-        match self {
-            Ok(val) => Ok(val),
-            Err(err) => Err(format!("{}; original={}", val, err))
-        }
-    }
-}
-
-// Path to cargo executables
-pub fn cargo_dir() -> Path {
-    os::getenv("CARGO_BIN_PATH")
-        .map(|s| Path::new(s))
-        .unwrap_or_else(|| fail!("CARGO_BIN_PATH wasn't set. Cannot continue running test"))
-}
-
-/// Returns an absolute path in the filesystem that `path` points to. The
-/// returned path does not contain any symlinks in its hierarchy.
-/*
- *
- * ===== Matchers =====
- *
- */
-
-#[deriving(Clone)]
-struct Execs {
-    expect_stdout: Option<String>,
-    expect_stdin: Option<String>,
-    expect_stderr: Option<String>,
-    expect_exit_code: Option<int>
-}
-
-impl Execs {
-
-  pub fn with_stdout<S: ToStr>(mut ~self, expected: S) -> Box<Execs> {
-    self.expect_stdout = Some(expected.to_str());
-    self
-  }
-
-  pub fn with_stderr<S: ToStr>(mut ~self, expected: S) -> Box<Execs> {
-      self.expect_stderr = Some(expected.to_str());
-      self
-  }
-
-  pub fn with_status(mut ~self, expected: int) -> Box<Execs> {
-       self.expect_exit_code = Some(expected);
-       self
-  }
-
-  fn match_output(&self, actual: &ProcessOutput) -> ham::MatchResult {
-    self.match_status(actual.status)
-      .and(self.match_stdout(actual))
-      .and(self.match_stderr(actual))
-  }
-
-  fn match_status(&self, actual: ProcessExit) -> ham::MatchResult {
-    match self.expect_exit_code {
-      None => ham::success(),
-      Some(code) => {
-        ham::expect(
-          actual.matches_exit_status(code),
-          format!("exited with {}", actual))
-      }
-    }
-  }
-
-  fn match_stdout(&self, actual: &ProcessOutput) -> ham::MatchResult {
-      self.match_std(self.expect_stdout.as_ref(), actual.output.as_slice(), "stdout", actual.error.as_slice())
-  }
-
-  fn match_stderr(&self, actual: &ProcessOutput) -> ham::MatchResult {
-      self.match_std(self.expect_stderr.as_ref(), actual.error.as_slice(), "stderr", actual.output.as_slice())
-  }
-
-  fn match_std(&self, expected: Option<&String>, actual: &[u8], description: &str, extra: &[u8]) -> ham::MatchResult {
-    match expected.as_ref().map(|s| s.as_slice()) {
-      None => ham::success(),
-      Some(out) => {
-        match str::from_utf8(actual) {
-          None => Err(format!("{} was not utf8 encoded", description)),
-          Some(actual) => {
-            ham::expect(actual == out, format!("{} was:\n`{}`\n\nexpected:\n`{}`\n\nother output:\n`{}`", description, actual, out, str::from_utf8_lossy(extra)))
-          }
-        }
-      }
-    }
-  }
-}
-
-impl ham::SelfDescribing for Execs {
-  fn describe(&self) -> String {
-    "execs".to_str()
-  }
-}
-
-impl ham::Matcher<ProcessBuilder> for Execs {
-  fn matches(&self, process: ProcessBuilder) -> ham::MatchResult {
-    let res = process.exec_with_output();
-
-    match res {
-      Ok(out) => self.match_output(&out),
-      Err(CargoError { kind: ProcessError(_, ref out), .. }) => self.match_output(out.get_ref()),
-      Err(e) => Err(format!("could not exec process {}: {}", process, e))
-    }
-  }
-}
-
-pub fn execs() -> Box<Execs> {
-    box Execs {
-        expect_stdout: None,
-        expect_stderr: None,
-        expect_stdin: None,
-        expect_exit_code: None
-    }
-}
-
-#[deriving(Clone)]
-struct ShellWrites {
-    expected: String
-}
-
-impl ham::SelfDescribing for ShellWrites {
-    fn describe(&self) -> String {
-        format!("`{}` written to the shell", self.expected)
-    }
-}
-
-impl<'a> ham::Matcher<&'a mut shell::Shell<std::io::MemWriter>> for ShellWrites {
-    fn matches(&self, actual: &mut shell::Shell<std::io::MemWriter>) -> ham::MatchResult {
-        use term::Terminal;
-
-        let actual = std::str::from_utf8_lossy(actual.get_ref().get_ref()).to_str();
-        ham::expect(actual == self.expected, actual)
-    }
-}
-
-pub fn shell_writes<T: Show>(string: T) -> Box<ShellWrites> {
-    box ShellWrites { expected: string.to_str() }
-}
-
-pub trait ResultTest<T,E> {
-    fn assert(self) -> T;
-}
-
-impl<T,E: Show> ResultTest<T,E> for Result<T,E> {
-    fn assert(self) -> T {
-        match self {
-            Ok(val) => val,
-            Err(err) => fail!("Result was error: {}", err)
-        }
-    }
-}
-
-impl<T> ResultTest<T,()> for Option<T> {
-    fn assert(self) -> T {
-        match self {
-            Some(val) => val,
-            None => fail!("Option was None")
-        }
-    }
-}
-
-pub trait Tap {
-    fn tap(mut self, callback: |&mut Self|) -> Self;
-}
-
-impl<T> Tap for T {
-    fn tap(mut self, callback: |&mut T|) -> T {
-        callback(&mut self);
-        self
-    }
-}
diff --git a/tests/support/mod.rs b/tests/support/mod.rs
new file mode 100644 (file)
index 0000000..ac2326a
--- /dev/null
@@ -0,0 +1,334 @@
+// use std::io::fs::{mkdir_recursive,rmdir_recursive};
+use std;
+use std::io;
+use std::io::fs;
+use std::io::process::{ProcessOutput,ProcessExit};
+use std::os;
+use std::path::{Path,BytesContainer};
+use std::str;
+use std::vec::Vec;
+use std::fmt::Show;
+use ham = hamcrest;
+use cargo::core::shell;
+use cargo::util::{process,ProcessBuilder,CargoError};
+use cargo::util::result::ProcessError;
+
+pub mod paths;
+
+/*
+ *
+ * ===== Builders =====
+ *
+ */
+
+#[deriving(PartialEq,Clone)]
+struct FileBuilder {
+    path: Path,
+    body: String
+}
+
+impl FileBuilder {
+    pub fn new(path: Path, body: &str) -> FileBuilder {
+        FileBuilder { path: path, body: body.to_str() }
+    }
+
+    fn mk(&self) -> Result<(), String> {
+        try!(mkdir_recursive(&self.dirname()));
+
+        let mut file = try!(
+            fs::File::create(&self.path)
+                .with_err_msg(format!("Could not create file; path={}", self.path.display())));
+
+        file.write_str(self.body.as_slice())
+            .with_err_msg(format!("Could not write to file; path={}", self.path.display()))
+    }
+
+    fn dirname(&self) -> Path {
+        Path::new(self.path.dirname())
+    }
+}
+
+#[deriving(PartialEq,Clone)]
+pub struct ProjectBuilder {
+    name: String,
+    root: Path,
+    files: Vec<FileBuilder>
+}
+
+impl ProjectBuilder {
+    pub fn new(name: &str, root: Path) -> ProjectBuilder {
+        ProjectBuilder {
+            name: name.to_str(),
+            root: root,
+            files: vec!()
+        }
+    }
+
+    pub fn join<T: Str>(&self, name: &str, path: T) -> ProjectBuilder {
+        ProjectBuilder {
+            name: name.as_slice().to_str(),
+            root: self.root.join(path.as_slice()).clone(),
+            files: vec!()
+        }
+    }
+
+    pub fn root(&self) -> Path {
+      self.root.clone()
+    }
+
+    pub fn process(&self, program: &str) -> ProcessBuilder {
+        process(program)
+            .cwd(self.root())
+            .env("HOME", Some(paths::home().display().to_str().as_slice()))
+    }
+
+    pub fn cargo_process(&self, program: &str) -> ProcessBuilder {
+        self.build();
+        self.process(program)
+            .extra_path(cargo_dir())
+    }
+
+    pub fn file<B: BytesContainer, S: Str>(mut self, path: B, body: S) -> ProjectBuilder { 
+        self.files.push(FileBuilder::new(self.root.join(path), body.as_slice()));
+        self
+    }
+
+    // TODO: return something different than a ProjectBuilder
+    pub fn build<'a>(&'a self) -> &'a ProjectBuilder {
+        match self.build_with_result() {
+            Err(e) => fail!(e),
+            _ => return self
+        }
+    }
+
+    pub fn build_with_result(&self) -> Result<(), String> {
+        // First, clean the directory if it already exists
+        try!(self.rm_root());
+
+        // Create the empty directory
+        try!(mkdir_recursive(&self.root));
+
+        for file in self.files.iter() {
+          try!(file.mk());
+        }
+
+        Ok(())
+    }
+
+    fn rm_root(&self) -> Result<(), String> {
+        if self.root.exists() {
+            rmdir_recursive(&self.root)
+        }
+        else {
+            Ok(())
+        }
+    }
+}
+
+// Generates a project layout
+pub fn project(name: &str) -> ProjectBuilder {
+    ProjectBuilder::new(name, paths::root().join(name))
+}
+
+// === Helpers ===
+
+pub fn mkdir_recursive(path: &Path) -> Result<(), String> {
+    fs::mkdir_recursive(path, io::UserDir)
+        .with_err_msg(format!("could not create directory; path={}", path.display()))
+}
+
+pub fn rmdir_recursive(path: &Path) -> Result<(), String> {
+    fs::rmdir_recursive(path)
+        .with_err_msg(format!("could not rm directory; path={}", path.display()))
+}
+
+pub fn main_file<T: Str>(println: T, deps: &[&str]) -> String {
+    let mut buf = String::new();
+
+    for dep in deps.iter() {
+        buf.push_str(format!("extern crate {};\n", dep).as_slice());
+    }
+
+    buf.push_str("fn main() { println!(");
+    buf.push_str(println.as_slice());
+    buf.push_str("); }\n");
+
+    buf.to_str()
+}
+
+trait ErrMsg<T> {
+    fn with_err_msg(self, val: String) -> Result<T, String>;
+}
+
+impl<T, E: Show> ErrMsg<T> for Result<T, E> {
+    fn with_err_msg(self, val: String) -> Result<T, String> {
+        match self {
+            Ok(val) => Ok(val),
+            Err(err) => Err(format!("{}; original={}", val, err))
+        }
+    }
+}
+
+// Path to cargo executables
+pub fn cargo_dir() -> Path {
+    os::getenv("CARGO_BIN_PATH")
+        .map(|s| Path::new(s))
+        .unwrap_or_else(|| fail!("CARGO_BIN_PATH wasn't set. Cannot continue running test"))
+}
+
+/// Returns an absolute path in the filesystem that `path` points to. The
+/// returned path does not contain any symlinks in its hierarchy.
+/*
+ *
+ * ===== Matchers =====
+ *
+ */
+
+#[deriving(Clone)]
+struct Execs {
+    expect_stdout: Option<String>,
+    expect_stdin: Option<String>,
+    expect_stderr: Option<String>,
+    expect_exit_code: Option<int>
+}
+
+impl Execs {
+
+  pub fn with_stdout<S: ToStr>(mut ~self, expected: S) -> Box<Execs> {
+    self.expect_stdout = Some(expected.to_str());
+    self
+  }
+
+  pub fn with_stderr<S: ToStr>(mut ~self, expected: S) -> Box<Execs> {
+      self.expect_stderr = Some(expected.to_str());
+      self
+  }
+
+  pub fn with_status(mut ~self, expected: int) -> Box<Execs> {
+       self.expect_exit_code = Some(expected);
+       self
+  }
+
+  fn match_output(&self, actual: &ProcessOutput) -> ham::MatchResult {
+    self.match_status(actual.status)
+      .and(self.match_stdout(actual))
+      .and(self.match_stderr(actual))
+  }
+
+  fn match_status(&self, actual: ProcessExit) -> ham::MatchResult {
+    match self.expect_exit_code {
+      None => ham::success(),
+      Some(code) => {
+        ham::expect(
+          actual.matches_exit_status(code),
+          format!("exited with {}", actual))
+      }
+    }
+  }
+
+  fn match_stdout(&self, actual: &ProcessOutput) -> ham::MatchResult {
+      self.match_std(self.expect_stdout.as_ref(), actual.output.as_slice(), "stdout", actual.error.as_slice())
+  }
+
+  fn match_stderr(&self, actual: &ProcessOutput) -> ham::MatchResult {
+      self.match_std(self.expect_stderr.as_ref(), actual.error.as_slice(), "stderr", actual.output.as_slice())
+  }
+
+  fn match_std(&self, expected: Option<&String>, actual: &[u8], description: &str, extra: &[u8]) -> ham::MatchResult {
+    match expected.as_ref().map(|s| s.as_slice()) {
+      None => ham::success(),
+      Some(out) => {
+        match str::from_utf8(actual) {
+          None => Err(format!("{} was not utf8 encoded", description)),
+          Some(actual) => {
+            ham::expect(actual == out, format!("{} was:\n`{}`\n\nexpected:\n`{}`\n\nother output:\n`{}`", description, actual, out, str::from_utf8_lossy(extra)))
+          }
+        }
+      }
+    }
+  }
+}
+
+impl ham::SelfDescribing for Execs {
+  fn describe(&self) -> String {
+    "execs".to_str()
+  }
+}
+
+impl ham::Matcher<ProcessBuilder> for Execs {
+  fn matches(&self, process: ProcessBuilder) -> ham::MatchResult {
+    let res = process.exec_with_output();
+
+    match res {
+      Ok(out) => self.match_output(&out),
+      Err(CargoError { kind: ProcessError(_, ref out), .. }) => self.match_output(out.get_ref()),
+      Err(e) => Err(format!("could not exec process {}: {}", process, e))
+    }
+  }
+}
+
+pub fn execs() -> Box<Execs> {
+    box Execs {
+        expect_stdout: None,
+        expect_stderr: None,
+        expect_stdin: None,
+        expect_exit_code: None
+    }
+}
+
+#[deriving(Clone)]
+struct ShellWrites {
+    expected: String
+}
+
+impl ham::SelfDescribing for ShellWrites {
+    fn describe(&self) -> String {
+        format!("`{}` written to the shell", self.expected)
+    }
+}
+
+impl<'a> ham::Matcher<&'a mut shell::Shell<std::io::MemWriter>> for ShellWrites {
+    fn matches(&self, actual: &mut shell::Shell<std::io::MemWriter>) -> ham::MatchResult {
+        use term::Terminal;
+
+        let actual = std::str::from_utf8_lossy(actual.get_ref().get_ref()).to_str();
+        ham::expect(actual == self.expected, actual)
+    }
+}
+
+pub fn shell_writes<T: Show>(string: T) -> Box<ShellWrites> {
+    box ShellWrites { expected: string.to_str() }
+}
+
+pub trait ResultTest<T,E> {
+    fn assert(self) -> T;
+}
+
+impl<T,E: Show> ResultTest<T,E> for Result<T,E> {
+    fn assert(self) -> T {
+        match self {
+            Ok(val) => val,
+            Err(err) => fail!("Result was error: {}", err)
+        }
+    }
+}
+
+impl<T> ResultTest<T,()> for Option<T> {
+    fn assert(self) -> T {
+        match self {
+            Some(val) => val,
+            None => fail!("Option was None")
+        }
+    }
+}
+
+pub trait Tap {
+    fn tap(mut self, callback: |&mut Self|) -> Self;
+}
+
+impl<T> Tap for T {
+    fn tap(mut self, callback: |&mut T|) -> T {
+        callback(&mut self);
+        self
+    }
+}
diff --git a/tests/support/paths.rs b/tests/support/paths.rs
new file mode 100644 (file)
index 0000000..84013ac
--- /dev/null
@@ -0,0 +1,46 @@
+use std::{io,os};
+use std::io::IoResult;
+use std::io::fs;
+use cargo::util::realpath;
+
+static CARGO_INTEGRATION_TEST_DIR : &'static str = "cargo-integration-tests";
+
+pub fn root() -> Path {
+    realpath(&os::tmpdir().join(CARGO_INTEGRATION_TEST_DIR)).unwrap()
+}
+
+pub fn home() -> Path {
+    root().join("home")
+}
+
+pub trait PathExt {
+    fn rm_rf(&self) -> IoResult<()>;
+    fn mkdir_p(&self) -> IoResult<()>;
+}
+
+impl PathExt for Path {
+    /* Technically there is a potential race condition, but we don't
+     * care all that much for our tests
+     */
+    fn rm_rf(&self) -> IoResult<()> {
+        if self.exists() {
+            fs::rmdir_recursive(self)
+        }
+        else {
+            Ok(())
+        }
+    }
+
+    fn mkdir_p(&self) -> IoResult<()> {
+        fs::mkdir_recursive(self, io::UserDir)
+    }
+}
+
+/**
+ * Ensure required test directories exist and are empty
+ */
+pub fn setup() {
+    debug!("path setup; root={}; home={}", root().display(), home().display());
+    root().rm_rf().unwrap();
+    home().mkdir_p().unwrap();
+}
index 1fad32a04727176498cff9bc0d378607c67fff89..39cc570269fe1cc82e7d2d9387e38f8244191ac5 100644 (file)
@@ -1,4 +1,4 @@
-use support::{ResultTest,project,execs};
+use support::{ResultTest,project,execs,main_file};
 use hamcrest::{assert_that,existing_file};
 use cargo;
 use cargo::util::{process,realpath};
@@ -284,18 +284,4 @@ test!(cargo_compile_with_nested_deps_longhand {
       execs().with_stdout("test passed\n"));
 })
 
-fn main_file(println: &str, deps: &[&str]) -> String {
-    let mut buf = String::new();
-
-    for dep in deps.iter() {
-        buf.push_str(format!("extern crate {};\n", dep).as_slice());
-    }
-
-    buf.push_str("fn main() { println!(");
-    buf.push_str(println);
-    buf.push_str("); }\n");
-
-    buf.to_str()
-}
-
 // test!(compiling_project_with_invalid_manifest)
diff --git a/tests/test_cargo_compile_git_deps.rs b/tests/test_cargo_compile_git_deps.rs
new file mode 100644 (file)
index 0000000..2574a36
--- /dev/null
@@ -0,0 +1,79 @@
+use support::{ProjectBuilder,ResultTest,project,execs,main_file};
+use hamcrest::{assert_that,existing_file};
+use cargo;
+use cargo::util::{CargoResult,process};
+
+fn setup() {
+}
+
+fn git_repo(name: &str, callback: |ProjectBuilder| -> ProjectBuilder) -> CargoResult<ProjectBuilder> {
+    let mut git_project = project(name);
+    git_project = callback(git_project);
+    git_project.build();
+
+    log!(5, "git init");
+    try!(git_project.process("git").args(["init"]).exec_with_output());
+    log!(5, "building git project");
+    log!(5, "git add .");
+    try!(git_project.process("git").args(["add", "."]).exec_with_output());
+    log!(5, "git commit");
+    try!(git_project.process("git").args(["commit", "-m", "Initial commit"]).exec_with_output());
+    Ok(git_project)
+}
+
+test!(cargo_compile_simple_git_dep {
+    let project = project("foo");
+    let git_project = git_repo("dep1", |project| {
+        project
+            .file("Cargo.toml", r#"
+                [project]
+
+                name = "dep1"
+                version = "0.5.0"
+                authors = ["carlhuda@example.com"]
+
+                [[lib]]
+
+                name = "dep1"
+            "#)
+            .file("src/dep1.rs", r#"
+                pub fn hello() -> &'static str {
+                    "hello world"
+                }
+            "#)
+    }).assert();
+
+    let project = project
+        .file("Cargo.toml", format!(r#"
+            [project]
+
+            name = "foo"
+            version = "0.5.0"
+            authors = ["wycats@example.com"]
+
+            [dependencies.dep1]
+
+            version = "0.5.0"
+            git = "file://{}"
+
+            [[bin]]
+
+            name = "foo"
+        "#, git_project.root().display()))
+        .file("src/foo.rs", main_file(r#""{}", dep1::hello()"#, ["dep1"]));
+
+    let root = project.root();
+    let git_root = git_project.root();
+
+    assert_that(project.cargo_process("cargo-compile"),
+        execs()
+        .with_stdout(format!("Compiling dep1 v0.5.0 (file:{})\nCompiling foo v0.5.0 (file:{})\n",
+                             git_root.display(), root.display()))
+        .with_stderr(""));
+
+    assert_that(&project.root().join("target/foo"), existing_file());
+
+    assert_that(
+      cargo::util::process("foo").extra_path(project.root().join("target")),
+      execs().with_stdout("hello world\n"));
+})
index c9dfb93e2dfd3d54f2f986b9a7eb308e0162bb62..3357e0a629cef6496c7feb9afc7ca1719dad89d1 100644 (file)
@@ -8,16 +8,18 @@ extern crate hamcrest;
 #[phase(plugin, link)]
 extern crate log;
 
+mod support;
 macro_rules! test(
     ($name:ident $expr:expr) => (
         #[test]
         fn $name() {
+            ::support::paths::setup();
             setup();
             $expr;
         }
     )
 )
 
-mod support;
 mod test_cargo_compile;
+mod test_cargo_compile_git_deps;
 mod test_shell;