]> git.lizzy.rs Git - rust.git/commitdiff
rustpkg: Implement `rustpkg test`
authorTim Chevalier <chevalier@alum.wellesley.edu>
Thu, 26 Sep 2013 23:04:49 +0000 (16:04 -0700)
committerTim Chevalier <chevalier@alum.wellesley.edu>
Fri, 27 Sep 2013 16:38:22 +0000 (09:38 -0700)
Towards #7401

src/librustpkg/package_source.rs
src/librustpkg/rustpkg.rs
src/librustpkg/target.rs
src/librustpkg/tests.rs

index c2fddafd5fe221bf217e73631d7c263432fd214e..006a58e042f8c331c854b29ef323914c570b9768 100644 (file)
@@ -246,13 +246,17 @@ pub fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) {
     /// Infers crates to build. Called only in the case where there
     /// is no custom build logic
     pub fn find_crates(&mut self) {
+        self.find_crates_with_filter(|_| true);
+    }
+
+    pub fn find_crates_with_filter(&mut self, filter: &fn(&str) -> bool) {
         use conditions::missing_pkg_files::cond;
 
         let prefix = self.start_dir.components.len();
         debug!("Matching against %s", self.id.short_name);
         do os::walk_dir(&self.start_dir) |pth| {
             let maybe_known_crate_set = match pth.filename() {
-                Some(filename) => match filename {
+                Some(filename) if filter(filename) => match filename {
                     "lib.rs" => Some(&mut self.libs),
                     "main.rs" => Some(&mut self.mains),
                     "test.rs" => Some(&mut self.tests),
index c10ea2fb4247d814e5082df6f6a13d92519a9f8a..09b67bb5291120dd5251782d8226fabf6641f7f7 100644 (file)
@@ -33,7 +33,7 @@
 use syntax::{ast, diagnostic};
 use util::*;
 use messages::{error, warn, note};
-use path_util::build_pkg_id_in_workspace;
+use path_util::{build_pkg_id_in_workspace, built_test_in_workspace};
 use path_util::{U_RWX, in_rust_path};
 use path_util::{built_executable_in_workspace, built_library_in_workspace, default_workspace};
 use path_util::{target_executable_in_workspace, target_library_in_workspace};
@@ -44,7 +44,7 @@
                        LLVMAssemble, LLVMCompileBitcode};
 use package_id::PkgId;
 use package_source::PkgSrc;
-use target::{WhatToBuild, Everything, is_lib, is_main, is_test, is_bench};
+use target::{WhatToBuild, Everything, is_lib, is_main, is_test, is_bench, Tests};
 // use workcache_support::{discover_outputs, digest_only_date};
 use workcache_support::digest_only_date;
 use exit_codes::COPY_FAILED_CODE;
@@ -177,6 +177,8 @@ fn hash(&self) -> ~str {
 pub trait CtxMethods {
     fn run(&self, cmd: &str, args: ~[~str]);
     fn do_cmd(&self, _cmd: &str, _pkgname: &str);
+    /// Returns a pair of the selected package ID, and the destination workspace
+    fn build_args(&self, args: ~[~str], what: &WhatToBuild) -> Option<(PkgId, Path)>;
     /// Returns the destination workspace
     fn build(&self, pkg_src: &mut PkgSrc, what: &WhatToBuild) -> Path;
     fn clean(&self, workspace: &Path, id: &PkgId);
@@ -190,43 +192,53 @@ fn install_no_build(&self,
                         target_workspace: &Path,
                         id: &PkgId) -> ~[~str];
     fn prefer(&self, _id: &str, _vers: Option<~str>);
-    fn test(&self);
+    fn test(&self, id: &PkgId, workspace: &Path);
     fn uninstall(&self, _id: &str, _vers: Option<~str>);
     fn unprefer(&self, _id: &str, _vers: Option<~str>);
     fn init(&self);
 }
 
 impl CtxMethods for BuildContext {
+    fn build_args(&self, args: ~[~str], what: &WhatToBuild) -> Option<(PkgId, Path)> {
+        if args.len() < 1 {
+            match cwd_to_workspace() {
+                None if self.context.use_rust_path_hack => {
+                    let cwd = os::getcwd();
+                    let pkgid = PkgId::new(cwd.components[cwd.components.len() - 1]);
+                    let mut pkg_src = PkgSrc::new(cwd, true, pkgid);
+                    let dest_ws = self.build(&mut pkg_src, what);
+                    Some((pkg_src.id, dest_ws))
+                }
+                None => { usage::build(); None }
+                Some((ws, pkgid)) => {
+                    let mut pkg_src = PkgSrc::new(ws, false, pkgid);
+                    let dest_ws = self.build(&mut pkg_src, what);
+                    Some((pkg_src.id, dest_ws))
+                }
+            }
+        } else {
+            // The package id is presumed to be the first command-line
+            // argument
+            let pkgid = PkgId::new(args[0].clone());
+            let mut dest_ws = None;
+            do each_pkg_parent_workspace(&self.context, &pkgid) |workspace| {
+                debug!("found pkg %s in workspace %s, trying to build",
+                       pkgid.to_str(), workspace.to_str());
+                let mut pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone());
+                dest_ws = Some(self.build(&mut pkg_src, what));
+                true
+            };
+            assert!(dest_ws.is_some());
+            // n.b. If this builds multiple packages, it only returns the workspace for
+            // the last one. The whole building-multiple-packages-with-the-same-ID is weird
+            // anyway and there are no tests for it, so maybe take it out
+            Some((pkgid, dest_ws.unwrap()))
+        }
+    }
     fn run(&self, cmd: &str, args: ~[~str]) {
         match cmd {
             "build" => {
-                if args.len() < 1 {
-                    match cwd_to_workspace() {
-                        None if self.context.use_rust_path_hack => {
-                            let cwd = os::getcwd();
-                            let pkgid = PkgId::new(cwd.components[cwd.components.len() - 1]);
-                            let mut pkg_src = PkgSrc::new(cwd, true, pkgid);
-                            self.build(&mut pkg_src, &Everything);
-                        }
-                        None => { usage::build(); return; }
-                        Some((ws, pkgid)) => {
-                            let mut pkg_src = PkgSrc::new(ws, false, pkgid);
-                            self.build(&mut pkg_src, &Everything);
-                        }
-                    }
-                }
-                else {
-                    // The package id is presumed to be the first command-line
-                    // argument
-                    let pkgid = PkgId::new(args[0].clone());
-                    do each_pkg_parent_workspace(&self.context, &pkgid) |workspace| {
-                        debug!("found pkg %s in workspace %s, trying to build",
-                               pkgid.to_str(), workspace.to_str());
-                        let mut pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone());
-                        self.build(&mut pkg_src, &Everything);
-                        true
-                    };
-                }
+                self.build_args(args, &Everything);
             }
             "clean" => {
                 if args.len() < 1 {
@@ -310,7 +322,17 @@ fn run(&self, cmd: &str, args: ~[~str]) {
                 self.prefer(args[0], None);
             }
             "test" => {
-                self.test();
+                // Build the test executable
+                let maybe_id_and_workspace = self.build_args(args, &Tests);
+                match maybe_id_and_workspace {
+                    Some((pkg_id, workspace)) => {
+                        // Assuming it's built, run the tests
+                        self.test(&pkg_id, &workspace);
+                    }
+                    None => {
+                        error("Testing failed because building the specified package failed.");
+                    }
+                }
             }
             "init" => {
                 if args.len() != 0 {
@@ -425,6 +447,8 @@ fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) -> Path {
             match what_to_build {
                 // Find crates inside the workspace
                 &Everything => pkg_src.find_crates(),
+                // Find only tests
+                &Tests => pkg_src.find_crates_with_filter(|s| { is_test(&Path(s)) }),
                 // Don't infer any crates -- just build the one that was requested
                 &JustOne(ref p) => {
                     // We expect that p is relative to the package source's start directory,
@@ -592,9 +616,25 @@ fn prefer(&self, _id: &str, _vers: Option<~str>)  {
         fail!("prefer not yet implemented");
     }
 
-    fn test(&self)  {
-        // stub
-        fail!("test not yet implemented");
+    fn test(&self, pkgid: &PkgId, workspace: &Path)  {
+        match built_test_in_workspace(pkgid, workspace) {
+            Some(test_exec) => {
+                debug!("test: test_exec = %s", test_exec.to_str());
+                let p_output = run::process_output(test_exec.to_str(), [~"--test"]);
+                if p_output.status == 0 {
+                    println(str::from_utf8(p_output.output));
+                }
+                    else {
+                    println(str::from_utf8(p_output.error));
+                }
+                os::set_exit_status(p_output.status);
+            }
+            None => {
+                error(fmt!("Internal error: test executable for package ID %s in workspace %s \
+                           wasn't built! Please report this as a bug.",
+                           pkgid.to_str(), workspace.to_str()));
+            }
+        }
     }
 
     fn init(&self) {
index 9d3ad1f39a74ac0cd6c9fece12a46e6be37cc48b..664f6807227d8fdfd99f7bb2d2d40b93bf42d780 100644 (file)
@@ -26,6 +26,8 @@ pub enum Target {
 pub enum WhatToBuild {
     /// Build just one lib.rs file in `path`, which is relative to the active workspace's src/ dir
     JustOne(Path),
+    /// Build any test.rs files that can be recursively found in the active workspace
+    Tests,
     /// Build everything
     Everything
 }
index 52545d60420f9f4aaf80fbdccd6d1606936a3900..87abff45dc7594755c3adf34001c4a97204ab9ae 100644 (file)
@@ -350,11 +350,19 @@ fn assert_executable_exists(repo: &Path, short_name: &str) {
 }
 
 fn executable_exists(repo: &Path, short_name: &str) -> bool {
-    debug!("assert_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
+    debug!("executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
     let exec = target_executable_in_workspace(&PkgId::new(short_name), repo);
     os::path_exists(&exec) && is_rwx(&exec)
 }
 
+fn test_executable_exists(repo: &Path, short_name: &str) -> bool {
+    debug!("test_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name);
+    let exec = built_test_in_workspace(&PkgId::new(short_name), repo);
+    do exec.map_default(false) |exec| {
+        os::path_exists(exec) && is_rwx(exec)
+    }
+}
+
 fn remove_executable_file(p: &PkgId, workspace: &Path) {
     let exec = target_executable_in_workspace(&PkgId::new(p.short_name), workspace);
     if os::path_exists(&exec) {
@@ -1045,19 +1053,8 @@ fn test_info() {
 }
 
 #[test]
-#[ignore(reason = "test not yet implemented")]
-fn test_rustpkg_test() {
-    let expected_results = ~"1 out of 1 tests passed"; // fill in
-    let workspace = create_local_package_with_test(&PkgId::new("foo"));
-    let output = command_line_test([~"test", ~"foo"], &workspace);
-    assert_eq!(str::from_utf8(output.output), expected_results);
-}
-
-#[test]
-#[ignore(reason = "test not yet implemented")]
 fn test_uninstall() {
     let workspace = create_local_package(&PkgId::new("foo"));
-    let _output = command_line_test([~"info", ~"foo"], &workspace);
     command_line_test([~"uninstall", ~"foo"], &workspace);
     let output = command_line_test([~"list"], &workspace);
     assert!(!str::from_utf8(output.output).contains("foo"));
@@ -1800,6 +1797,64 @@ fn correct_package_name_with_rust_path_hack() {
     assert!(!lib_exists(&foo_workspace, &foo_id.path.clone(), foo_id.version.clone()));
 }
 
+#[test]
+fn test_rustpkg_test_creates_exec() {
+    let foo_id = PkgId::new("foo");
+    let foo_workspace = create_local_package(&foo_id);
+    writeFile(&foo_workspace.push_many(["src", "foo-0.1", "test.rs"]),
+              "#[test] fn f() { assert!('a' == 'a'); }");
+    command_line_test([~"test", ~"foo"], &foo_workspace);
+    assert!(test_executable_exists(&foo_workspace, "foo"));
+}
+
+#[test]
+fn test_rustpkg_test_output() {
+    let workspace = create_local_package_with_test(&PkgId::new("foo"));
+    let output = command_line_test([~"test", ~"foo"], &workspace);
+    let output_str = str::from_utf8(output.output);
+    assert!(output_str.contains("test f ... ok"));
+    assert!(output_str.contains(
+        "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured"));
+}
+
+#[test]
+#[ignore(reason = "See issue #9441")]
+fn test_rebuild_when_needed() {
+    let foo_id = PkgId::new("foo");
+    let foo_workspace = create_local_package(&foo_id);
+    let test_crate = foo_workspace.push_many(["src", "foo-0.1", "test.rs"]);
+    writeFile(&test_crate, "#[test] fn f() { assert!('a' == 'a'); }");
+    command_line_test([~"test", ~"foo"], &foo_workspace);
+    assert!(test_executable_exists(&foo_workspace, "foo"));
+    let test_executable = built_test_in_workspace(&foo_id,
+            &foo_workspace).expect("test_rebuild_when_needed failed");
+    frob_source_file(&foo_workspace, &foo_id, "test.rs");
+    chmod_read_only(&test_executable);
+    match command_line_test_partial([~"test", ~"foo"], &foo_workspace) {
+        Success(*) => fail!("test_rebuild_when_needed didn't rebuild"),
+        Fail(status) if status == 65 => (), // ok
+        Fail(_) => fail!("test_rebuild_when_needed failed for some other reason")
+    }
+}
+
+#[test]
+fn test_no_rebuilding() {
+    let foo_id = PkgId::new("foo");
+    let foo_workspace = create_local_package(&foo_id);
+    let test_crate = foo_workspace.push_many(["src", "foo-0.1", "test.rs"]);
+    writeFile(&test_crate, "#[test] fn f() { assert!('a' == 'a'); }");
+    command_line_test([~"test", ~"foo"], &foo_workspace);
+    assert!(test_executable_exists(&foo_workspace, "foo"));
+    let test_executable = built_test_in_workspace(&foo_id,
+                            &foo_workspace).expect("test_no_rebuilding failed");
+    chmod_read_only(&test_executable);
+    match command_line_test_partial([~"test", ~"foo"], &foo_workspace) {
+        Success(*) => (), // ok
+        Fail(status) if status == 65 => fail!("test_no_rebuilding failed: it rebuilt the tests"),
+        Fail(_) => fail!("test_no_rebuilding failed for some other reason")
+    }
+}
+
 /// Returns true if p exists and is executable
 fn is_executable(p: &Path) -> bool {
     use std::libc::consts::os::posix88::{S_IXUSR};