Skip to content

Commit 35ac31e

Browse files
committed
Implement doc tests
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.
1 parent 48bfdaa commit 35ac31e

File tree

7 files changed

+290
-107
lines changed

7 files changed

+290
-107
lines changed

src/cargo/ops/cargo_compile.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ pub struct CompileOptions<'a> {
4444
}
4545

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

@@ -114,20 +115,20 @@ pub fn compile(manifest_path: &Path,
114115
}
115116
}).collect::<Vec<&Target>>();
116117

117-
{
118+
let ret = {
118119
let _p = profile::start("compiling");
119120
let mut config = try!(Config::new(*shell, update, jobs, target));
120121
try!(scrape_target_config(&mut config, &user_configs));
121122

122123
try!(ops::compile_targets(env.as_slice(), targets.as_slice(), &package,
123124
&PackageSet::new(packages.as_slice()),
124125
&resolve_with_overrides, &sources,
125-
&mut config));
126-
}
126+
&mut config))
127+
};
127128

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

130-
Ok(())
131+
return Ok(ret);
131132
}
132133

133134
fn source_ids_from_config(configs: &HashMap<String, config::ConfigValue>,

src/cargo/ops/cargo_rustc/mod.rs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashSet;
1+
use std::collections::{HashSet, HashMap};
22
use std::dynamic_lib::DynamicLibrary;
33
use std::io::{fs, UserRWX};
44
use std::os;
@@ -41,11 +41,15 @@ fn uniq_target_dest<'a>(targets: &[&'a Target]) -> Option<&'a str> {
4141
curr.unwrap()
4242
}
4343

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

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

89+
let ret = build_return_map(&cx, pkg, deps);
90+
8591
// Now that we've figured out everything that we're going to do, do it!
86-
queue.execute(cx.config)
92+
try!(queue.execute(cx.config));
93+
94+
Ok(ret)
8795
}
8896

8997
fn compile<'a, 'b>(targets: &[&'a Target], pkg: &'a Package,
@@ -453,3 +461,36 @@ fn pre_version_component(v: &Version) -> Option<String> {
453461

454462
Some(ret)
455463
}
464+
465+
fn build_return_map(cx: &Context, root: &Package, deps: &PackageSet)
466+
-> HashMap<PackageId, Vec<Path>> {
467+
let mut ret = HashMap::new();
468+
match cx.resolve.deps(root.get_package_id()) {
469+
Some(mut my_deps) => {
470+
for dep in my_deps {
471+
let pkg = deps.iter().find(|p| p.get_package_id() == dep).unwrap();
472+
ret.insert(dep.clone(), build_paths(cx, pkg, false));
473+
}
474+
}
475+
None => {}
476+
}
477+
ret.insert(root.get_package_id().clone(), build_paths(cx, root, true));
478+
return ret;
479+
480+
fn build_paths(cx: &Context, pkg: &Package, root: bool) -> Vec<Path> {
481+
pkg.get_targets().iter().filter(|target| {
482+
target.get_profile().is_compile() && target.is_lib()
483+
}).flat_map(|target| {
484+
let kind = if target.get_profile().is_plugin() {
485+
KindPlugin
486+
} else {
487+
KindTarget
488+
};
489+
let layout = cx.layout(kind);
490+
cx.target_filenames(target).move_iter().map(|filename| {
491+
let root = if root {layout.root()} else {layout.deps()};
492+
root.join(filename)
493+
}).collect::<Vec<Path>>().move_iter()
494+
}).collect()
495+
}
496+
}

src/cargo/ops/cargo_test.rs

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::os;
2+
13
use core::Source;
24
use sources::PathSource;
35
use ops;
@@ -10,24 +12,66 @@ pub fn run_tests(manifest_path: &Path,
1012
try!(source.update());
1113
let package = try!(source.get_root_package());
1214

13-
try!(ops::compile(manifest_path, options));
15+
let compiled_libs = try!(ops::compile(manifest_path, options));
1416

15-
let mut exes = package.get_targets().iter().filter_map(|target| {
17+
let mut exes: Vec<Path> = package.get_targets().iter().filter_map(|target| {
1618
if !target.get_profile().is_test() { return None }
1719
let root = package.get_root().join("target");
1820
let root = match target.get_profile().get_dest() {
1921
Some(dest) => root.join(dest),
2022
None => root,
2123
};
2224
Some(root.join(target.file_stem()))
23-
});
25+
}).collect();
26+
exes.sort();
2427

25-
for exe in exes {
28+
let cwd = os::getcwd();
29+
for exe in exes.iter() {
30+
let to_display = match exe.path_relative_from(&cwd) {
31+
Some(path) => path,
32+
None => exe.clone(),
33+
};
34+
try!(options.shell.status("Running", to_display.display()));
2635
match process(exe).args(args).exec() {
2736
Ok(()) => {}
2837
Err(e) => return Ok(Some(e))
2938
}
3039
}
3140

41+
let mut libs = package.get_targets().iter().filter_map(|target| {
42+
if !target.get_profile().is_test() || !target.is_lib() {
43+
return None
44+
}
45+
Some((target.get_src_path(), target.get_name()))
46+
});
47+
48+
for (lib, name) in libs {
49+
try!(options.shell.status("Doc-tests", name));
50+
let mut p = process("rustdoc").arg("--test").arg(lib)
51+
.arg("--crate-name").arg(name)
52+
.arg("-L").arg("target/test")
53+
.arg("-L").arg("target/test/deps")
54+
.cwd(package.get_root());
55+
56+
// FIXME(rust-lang/rust#16272): this should just always be passed.
57+
if args.len() > 0 {
58+
p = p.arg("--test-args").arg(args.connect(" "));
59+
}
60+
61+
for (pkg, libs) in compiled_libs.iter() {
62+
for lib in libs.iter() {
63+
let mut arg = pkg.get_name().as_bytes().to_vec();
64+
arg.push(b'=');
65+
arg.push_all(lib.as_vec());
66+
p = p.arg("--extern").arg(arg.as_slice());
67+
}
68+
}
69+
70+
match p.exec() {
71+
Ok(()) => {}
72+
Err(e) => return Ok(Some(e)),
73+
}
74+
}
75+
3276
Ok(None)
3377
}

src/cargo/util/toml.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ fn normalize(libs: &[TomlLibTarget],
623623
|bin| format!("src/bin/{}.rs", bin.name));
624624
},
625625
([_, ..], []) => {
626-
lib_targets(&mut ret, libs, test_dep, metadata);
626+
lib_targets(&mut ret, libs, Needed, metadata);
627627
},
628628
([], [_, ..]) => {
629629
bin_targets(&mut ret, bins, test_dep, metadata,

tests/support/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,3 +499,4 @@ pub static RUNNING: &'static str = " Running";
499499
pub static COMPILING: &'static str = " Compiling";
500500
pub static FRESH: &'static str = " Fresh";
501501
pub static UPDATING: &'static str = " Updating";
502+
pub static DOCTEST: &'static str = " Doc-tests";

tests/test_cargo_freshness.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ test!(modify_only_some_files {
7070

7171
let lib = p.root().join("src/lib.rs");
7272
let bin = p.root().join("src/b.rs");
73-
let test = p.root().join("tests/test.rs");
7473

7574
File::create(&lib).write_str("invalid rust code").assert();
7675
lib.move_into_the_past().assert();
@@ -85,9 +84,4 @@ test!(modify_only_some_files {
8584
{compiling} foo v0.0.1 (file:{dir})
8685
", compiling = COMPILING, dir = p.root().display())));
8786
assert_that(&p.bin("foo"), existing_file());
88-
89-
// Make sure the tests don't recompile the lib
90-
File::create(&test).write_str("fn foo() {}").assert();
91-
assert_that(p.process(cargo_dir().join("cargo-test")),
92-
execs().with_status(0));
9387
})

0 commit comments

Comments
 (0)