diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index c2fddafd5fe..006a58e042f 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -246,13 +246,17 @@ impl PkgSrc { /// 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), diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index c10ea2fb424..09b67bb5291 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -33,7 +33,7 @@ use extra::{getopts}; 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 @@ use context::{Context, BuildContext, 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 @@ impl<'self> PkgScript<'self> { 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 @@ pub trait CtxMethods { 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 @@ impl CtxMethods for BuildContext { 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 @@ impl CtxMethods for BuildContext { 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 @@ impl CtxMethods for BuildContext { 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) { diff --git a/src/librustpkg/target.rs b/src/librustpkg/target.rs index 9d3ad1f39a7..664f6807227 100644 --- a/src/librustpkg/target.rs +++ b/src/librustpkg/target.rs @@ -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 } diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index 52545d60420..87abff45dc7 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -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};