Skip to content

Commit

Permalink
auto merge of #302 : alexcrichton/cargo/doc-test, r=wycats
Browse files Browse the repository at this point in the history
Whenever `cargo test` is run and a testable library target is available, the doc
tests will be run. This can be opted out of with `test = false` as usual.

This is currently not super useful due to rust-lang/rust#16157, but I expect
that to be merged soon. In the meantime examples will need to `extern crate foo`
explicitly.

Closes #334
  • Loading branch information
bors committed Aug 6, 2014
2 parents 823b5a3 + 35ac31e commit 0eed0e1
Show file tree
Hide file tree
Showing 9 changed files with 332 additions and 132 deletions.
29 changes: 11 additions & 18 deletions src/bin/cargo-test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ extern crate docopt;
use std::io::process::ExitStatus;

use cargo::ops;
use cargo::{execute_main_without_stdin};
use cargo::core::{MultiShell};
use cargo::util;
use cargo::execute_main_without_stdin;
use cargo::core::MultiShell;
use cargo::util::{CliResult, CliError, CargoError};
use cargo::util::important_paths::{find_root_manifest_for_cwd};

Expand Down Expand Up @@ -48,24 +47,18 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
target: None,
};

let test_executables = try!(ops::compile(&root,
&mut compile_opts).map_err(|err| {
let err = try!(ops::run_tests(&root, &mut compile_opts,
options.arg_args.as_slice()).map_err(|err| {
CliError::from_boxed(err, 101)
}));

let test_dir = root.dir_path().join("target").join("test");

for file in test_executables.iter() {
try!(util::process(test_dir.join(file.as_slice()))
.args(options.arg_args.as_slice())
.exec().map_err(|e| {
let exit_status = match e.exit {
match err {
None => Ok(None),
Some(err) => {
let status = match err.exit {
Some(ExitStatus(i)) => i as uint,
_ => 1,
_ => 101,
};
CliError::from_boxed(e.mark_human(), exit_status)
}));
Err(CliError::from_boxed(err.mark_human(), status))
}
}

Ok(None)
}
22 changes: 6 additions & 16 deletions src/cargo/ops/cargo_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ pub struct CompileOptions<'a> {
}

pub fn compile(manifest_path: &Path,
options: &mut CompileOptions) -> CargoResult<Vec<String>> {
options: &mut CompileOptions)
-> CargoResult<HashMap<PackageId, Vec<Path>>> {
let CompileOptions { update, env, ref mut shell, jobs, target } = *options;
let target = target.map(|s| s.to_string());

Expand Down Expand Up @@ -114,31 +115,20 @@ pub fn compile(manifest_path: &Path,
}
}).collect::<Vec<&Target>>();

{
let ret = {
let _p = profile::start("compiling");
let mut config = try!(Config::new(*shell, update, jobs, target));
try!(scrape_target_config(&mut config, &user_configs));

try!(ops::compile_targets(env.as_slice(), targets.as_slice(), &package,
&PackageSet::new(packages.as_slice()),
&resolve_with_overrides, &sources,
&mut config));
}
&mut config))
};

try!(ops::write_resolve(&package, &resolve));

let test_executables: Vec<String> = targets.iter()
.filter_map(|target| {
if target.get_profile().is_test() {
debug!("Run Target: {}", target.get_name());
Some(target.file_stem())
} else {
debug!("Skip Target: {}", target.get_name());
None
}
}).collect();

Ok(test_executables)
return Ok(ret);
}

fn source_ids_from_config(configs: &HashMap<String, config::ConfigValue>,
Expand Down
51 changes: 46 additions & 5 deletions src/cargo/ops/cargo_rustc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashSet;
use std::collections::{HashSet, HashMap};
use std::dynamic_lib::DynamicLibrary;
use std::io::{fs, UserRWX};
use std::os;
Expand Down Expand Up @@ -41,11 +41,15 @@ fn uniq_target_dest<'a>(targets: &[&'a Target]) -> Option<&'a str> {
curr.unwrap()
}

// Returns a mapping of the root package plus its immediate dependencies to
// where the compiled libraries are all located.
pub fn compile_targets<'a>(env: &str, targets: &[&'a Target], pkg: &'a Package,
deps: &PackageSet, resolve: &'a Resolve, sources: &'a SourceMap,
config: &'a mut Config<'a>) -> CargoResult<()> {
deps: &PackageSet, resolve: &'a Resolve,
sources: &'a SourceMap,
config: &'a mut Config<'a>)
-> CargoResult<HashMap<PackageId, Vec<Path>>> {
if targets.is_empty() {
return Ok(());
return Ok(HashMap::new());
}

debug!("compile_targets; targets={}; pkg={}; deps={}", targets, pkg, deps);
Expand Down Expand Up @@ -82,8 +86,12 @@ pub fn compile_targets<'a>(env: &str, targets: &[&'a Target], pkg: &'a Package,
cx.primary();
try!(compile(targets, pkg, &mut cx, &mut queue));

let ret = build_return_map(&cx, pkg, deps);

// Now that we've figured out everything that we're going to do, do it!
queue.execute(cx.config)
try!(queue.execute(cx.config));

Ok(ret)
}

fn compile<'a, 'b>(targets: &[&'a Target], pkg: &'a Package,
Expand Down Expand Up @@ -447,3 +455,36 @@ fn pre_version_component(v: &Version) -> Option<String> {

Some(ret)
}

fn build_return_map(cx: &Context, root: &Package, deps: &PackageSet)
-> HashMap<PackageId, Vec<Path>> {
let mut ret = HashMap::new();
match cx.resolve.deps(root.get_package_id()) {
Some(mut my_deps) => {
for dep in my_deps {
let pkg = deps.iter().find(|p| p.get_package_id() == dep).unwrap();
ret.insert(dep.clone(), build_paths(cx, pkg, false));
}
}
None => {}
}
ret.insert(root.get_package_id().clone(), build_paths(cx, root, true));
return ret;

fn build_paths(cx: &Context, pkg: &Package, root: bool) -> Vec<Path> {
pkg.get_targets().iter().filter(|target| {
target.get_profile().is_compile() && target.is_lib()
}).flat_map(|target| {
let kind = if target.get_profile().is_plugin() {
KindPlugin
} else {
KindTarget
};
let layout = cx.layout(kind);
cx.target_filenames(target).move_iter().map(|filename| {
let root = if root {layout.root()} else {layout.deps()};
root.join(filename)
}).collect::<Vec<Path>>().move_iter()
}).collect()
}
}
77 changes: 77 additions & 0 deletions src/cargo/ops/cargo_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use std::os;

use core::Source;
use sources::PathSource;
use ops;
use util::{process, CargoResult, ProcessError};

pub fn run_tests(manifest_path: &Path,
options: &mut ops::CompileOptions,
args: &[String]) -> CargoResult<Option<ProcessError>> {
let mut source = PathSource::for_path(&manifest_path.dir_path());
try!(source.update());
let package = try!(source.get_root_package());

let compiled_libs = try!(ops::compile(manifest_path, options));

let mut exes: Vec<Path> = package.get_targets().iter().filter_map(|target| {
if !target.get_profile().is_test() { return None }
let root = package.get_root().join("target");
let root = match target.get_profile().get_dest() {
Some(dest) => root.join(dest),
None => root,
};
Some(root.join(target.file_stem()))
}).collect();
exes.sort();

let cwd = os::getcwd();
for exe in exes.iter() {
let to_display = match exe.path_relative_from(&cwd) {
Some(path) => path,
None => exe.clone(),
};
try!(options.shell.status("Running", to_display.display()));
match process(exe).args(args).exec() {
Ok(()) => {}
Err(e) => return Ok(Some(e))
}
}

let mut libs = package.get_targets().iter().filter_map(|target| {
if !target.get_profile().is_test() || !target.is_lib() {
return None
}
Some((target.get_src_path(), target.get_name()))
});

for (lib, name) in libs {
try!(options.shell.status("Doc-tests", name));
let mut p = process("rustdoc").arg("--test").arg(lib)
.arg("--crate-name").arg(name)
.arg("-L").arg("target/test")
.arg("-L").arg("target/test/deps")
.cwd(package.get_root());

// FIXME(rust-lang/rust#16272): this should just always be passed.
if args.len() > 0 {
p = p.arg("--test-args").arg(args.connect(" "));
}

for (pkg, libs) in compiled_libs.iter() {
for lib in libs.iter() {
let mut arg = pkg.get_name().as_bytes().to_vec();
arg.push(b'=');
arg.push_all(lib.as_vec());
p = p.arg("--extern").arg(arg.as_slice());
}
}

match p.exec() {
Ok(()) => {}
Err(e) => return Ok(Some(e)),
}
}

Ok(None)
}
2 changes: 2 additions & 0 deletions src/cargo/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub use self::cargo_new::{new, NewOptions};
pub use self::cargo_doc::{doc, DocOptions};
pub use self::cargo_generate_lockfile::{generate_lockfile, write_resolve};
pub use self::cargo_generate_lockfile::{update_lockfile, load_lockfile};
pub use self::cargo_test::run_tests;

mod cargo_clean;
mod cargo_compile;
Expand All @@ -16,3 +17,4 @@ mod cargo_run;
mod cargo_new;
mod cargo_doc;
mod cargo_generate_lockfile;
mod cargo_test;
2 changes: 1 addition & 1 deletion src/cargo/util/toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ fn normalize(libs: &[TomlLibTarget],
|bin| format!("src/bin/{}.rs", bin.name));
},
([_, ..], []) => {
lib_targets(&mut ret, libs, test_dep, metadata);
lib_targets(&mut ret, libs, Needed, metadata);
},
([], [_, ..]) => {
bin_targets(&mut ret, bins, test_dep, metadata,
Expand Down
1 change: 1 addition & 0 deletions tests/support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,3 +499,4 @@ pub static RUNNING: &'static str = " Running";
pub static COMPILING: &'static str = " Compiling";
pub static FRESH: &'static str = " Fresh";
pub static UPDATING: &'static str = " Updating";
pub static DOCTEST: &'static str = " Doc-tests";
6 changes: 0 additions & 6 deletions tests/test_cargo_freshness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ test!(modify_only_some_files {

let lib = p.root().join("src/lib.rs");
let bin = p.root().join("src/b.rs");
let test = p.root().join("tests/test.rs");

File::create(&lib).write_str("invalid rust code").assert();
lib.move_into_the_past().assert();
Expand All @@ -85,9 +84,4 @@ test!(modify_only_some_files {
{compiling} foo v0.0.1 (file:{dir})
", compiling = COMPILING, dir = p.root().display())));
assert_that(&p.bin("foo"), existing_file());

// Make sure the tests don't recompile the lib
File::create(&test).write_str("fn foo() {}").assert();
assert_that(p.process(cargo_dir().join("cargo-test")),
execs().with_status(0));
})
Loading

0 comments on commit 0eed0e1

Please sign in to comment.