Running cargo test compiles the code twice before running the compiled tester binary. This is obviously irritating to look at when there are a bunch of warnings and the like, and is also much slower.
Can you provide an example to look at? What's probably happening is that cargo is running rustc src/lib.rs and rustc --test src/lib.rs, and then after the lib finishes building it rust rustc tests/foo.rs -L target.
If you don't want to build your library with --test then you can specify test = false in the manifest.
Here is a run of cargo test on a project I'm working on. As you can see it's printing the errors twice. I would expect cargo test to only run rustc --test src/lib.rs && target/parser-<something>.
> cargo test
Compiling parser v0.0.1 (file:///Users/nixpulvis/Code/parser)
/Users/nixpulvis/Code/parser/src/lexer.rs:15:7: 21:4 error: cannot infer an appropriate lifetime due to conflicting requirements
/Users/nixpulvis/Code/parser/src/lexer.rs:15:7: 21:4 error: cannot infer an appropriate lifetime due to conflicting requirements
/Users/nixpulvis/Code/parser/src/lexer.rs:15 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:16 if s.len() >= l && s[0..l] == value {
/Users/nixpulvis/Code/parser/src/lexer.rs:15 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:17 Success(box LiteralNode(s[0..l]), s[l..])
/Users/nixpulvis/Code/parser/src/lexer.rs:16 if s.len() >= l && s[0..l] == value {
/Users/nixpulvis/Code/parser/src/lexer.rs:18 } else {
/Users/nixpulvis/Code/parser/src/lexer.rs:17 Success(box LiteralNode(s[0..l]), s[l..])
/Users/nixpulvis/Code/parser/src/lexer.rs:19 Error
/Users/nixpulvis/Code/parser/src/lexer.rs:18 } else {
/Users/nixpulvis/Code/parser/src/lexer.rs:20 }
...
/Users/nixpulvis/Code/parser/src/lexer.rs:19 Error
/Users/nixpulvis/Code/parser/src/lexer.rs:20 }
...
/Users/nixpulvis/Code/parser/src/lexer.rs:12:45: 22:2 note: first, the lifetime cannot outlive the block at 12:44...
/Users/nixpulvis/Code/parser/src/lexer.rs:12 pub fn lit<'a>(value: &'a str) -> Lexer<'a> {
/Users/nixpulvis/Code/parser/src/lexer.rs:13 let l = value.len();
/Users/nixpulvis/Code/parser/src/lexer.rs:14
/Users/nixpulvis/Code/parser/src/lexer.rs:15 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:16 if s.len() >= l && s[0..l] == value {
/Users/nixpulvis/Code/parser/src/lexer.rs:17 Success(box LiteralNode(s[0..l]), s[l..])
...
/Users/nixpulvis/Code/parser/src/lexer.rs:12:45: 22:2 note: first, the lifetime cannot outlive the block at 12:44...
/Users/nixpulvis/Code/parser/src/lexer.rs:12 pub fn lit<'a>(value: &'a str) -> Lexer<'a> {
/Users/nixpulvis/Code/parser/src/lexer.rs:13 let l = value.len();
/Users/nixpulvis/Code/parser/src/lexer.rs:14
/Users/nixpulvis/Code/parser/src/lexer.rs:15 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:16 if s.len() >= l && s[0..l] == value {
/Users/nixpulvis/Code/parser/src/lexer.rs:17 Success(box LiteralNode(s[0..l]), s[l..])
...
/Users/nixpulvis/Code/parser/src/lexer.rs:16:35: 16:40 note: ...so that captured variable `value` does not outlive the enclosing closure
/Users/nixpulvis/Code/parser/src/lexer.rs:16 if s.len() >= l && s[0..l] == value {
^~~~~
/Users/nixpulvis/Code/parser/src/lexer.rs:16:35: 16:40 note: ...so that captured variable `value` does not outlive the enclosing closure
/Users/nixpulvis/Code/parser/src/lexer.rs:16 if s.len() >= l && s[0..l] == value {
^~~~~
/Users/nixpulvis/Code/parser/src/lexer.rs:12:45: 22:2 note: but, the lifetime must be valid for the lifetime 'a as defined on the block at 12:44...
/Users/nixpulvis/Code/parser/src/lexer.rs:12 pub fn lit<'a>(value: &'a str) -> Lexer<'a> {
/Users/nixpulvis/Code/parser/src/lexer.rs:13 let l = value.len();
/Users/nixpulvis/Code/parser/src/lexer.rs:14
/Users/nixpulvis/Code/parser/src/lexer.rs:15 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:16 if s.len() >= l && s[0..l] == value {
/Users/nixpulvis/Code/parser/src/lexer.rs:17 Success(box LiteralNode(s[0..l]), s[l..])
/Users/nixpulvis/Code/parser/src/lexer.rs:12:45: 22:2 note: but, the lifetime must be valid for the lifetime 'a as defined on the block at 12:44...
...
/Users/nixpulvis/Code/parser/src/lexer.rs:12 pub fn lit<'a>(value: &'a str) -> Lexer<'a> {
/Users/nixpulvis/Code/parser/src/lexer.rs:13 let l = value.len();
/Users/nixpulvis/Code/parser/src/lexer.rs:14
/Users/nixpulvis/Code/parser/src/lexer.rs:15 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:16 if s.len() >= l && s[0..l] == value {
/Users/nixpulvis/Code/parser/src/lexer.rs:17 Success(box LiteralNode(s[0..l]), s[l..])
/Users/nixpulvis/Code/parser/src/lexer.rs:15:3: 21:4 note: ...so that it can be closed over into an object
/Users/nixpulvis/Code/parser/src/lexer.rs:15 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:16 if s.len() >= l && s[0..l] == value {
/Users/nixpulvis/Code/parser/src/lexer.rs:17 Success(box LiteralNode(s[0..l]), s[l..])
/Users/nixpulvis/Code/parser/src/lexer.rs:18 } else {
/Users/nixpulvis/Code/parser/src/lexer.rs:19 Error
/Users/nixpulvis/Code/parser/src/lexer.rs:20 }
...
...
/Users/nixpulvis/Code/parser/src/lexer.rs:15:3: 21:4 note: ...so that it can be closed over into an object
/Users/nixpulvis/Code/parser/src/lexer.rs:15 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:16 if s.len() >= l && s[0..l] == value {
/Users/nixpulvis/Code/parser/src/lexer.rs:17 Success(box LiteralNode(s[0..l]), s[l..])
/Users/nixpulvis/Code/parser/src/lexer.rs:18 } else {
/Users/nixpulvis/Code/parser/src/lexer.rs:19 Error
/Users/nixpulvis/Code/parser/src/lexer.rs:20 }
...
/Users/nixpulvis/Code/parser/src/lexer.rs:25:7: 31:4 error: cannot infer an appropriate lifetime due to conflicting requirements
/Users/nixpulvis/Code/parser/src/lexer.rs:25:7: 31:4 error: cannot infer an appropriate lifetime due to conflicting requirements
/Users/nixpulvis/Code/parser/src/lexer.rs:25 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:26 let r1 = (*l1).call((s,));
/Users/nixpulvis/Code/parser/src/lexer.rs:25 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:27 match r1 {
/Users/nixpulvis/Code/parser/src/lexer.rs:26 let r1 = (*l1).call((s,));
/Users/nixpulvis/Code/parser/src/lexer.rs:28 Success(_, _) => return r1,
/Users/nixpulvis/Code/parser/src/lexer.rs:27 match r1 {
/Users/nixpulvis/Code/parser/src/lexer.rs:29 Error => return (*l2).call((s,)),
/Users/nixpulvis/Code/parser/src/lexer.rs:28 Success(_, _) => return r1,
/Users/nixpulvis/Code/parser/src/lexer.rs:30 }
/Users/nixpulvis/Code/parser/src/lexer.rs:29 Error => return (*l2).call((s,)),
/Users/nixpulvis/Code/parser/src/lexer.rs:30 }
...
...
/Users/nixpulvis/Code/parser/src/lexer.rs:24:59: 32:2 note: first, the lifetime cannot outlive the block at 24:58...
/Users/nixpulvis/Code/parser/src/lexer.rs:24:59: 32:2 note: first, the lifetime cannot outlive the block at 24:58...
/Users/nixpulvis/Code/parser/src/lexer.rs:24 pub fn alt<'a>(l1: Lexer<'a>, l2: Lexer<'a>) -> Lexer<'a> {
/Users/nixpulvis/Code/parser/src/lexer.rs:24 pub fn alt<'a>(l1: Lexer<'a>, l2: Lexer<'a>) -> Lexer<'a> {
/Users/nixpulvis/Code/parser/src/lexer.rs:25 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:25 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:26 let r1 = (*l1).call((s,));
/Users/nixpulvis/Code/parser/src/lexer.rs:26 let r1 = (*l1).call((s,));
/Users/nixpulvis/Code/parser/src/lexer.rs:27 match r1 {
/Users/nixpulvis/Code/parser/src/lexer.rs:27 match r1 {
/Users/nixpulvis/Code/parser/src/lexer.rs:28 Success(_, _) => return r1,
/Users/nixpulvis/Code/parser/src/lexer.rs:28 Success(_, _) => return r1,
/Users/nixpulvis/Code/parser/src/lexer.rs:29 Error => return (*l2).call((s,)),
/Users/nixpulvis/Code/parser/src/lexer.rs:29 Error => return (*l2).call((s,)),
...
...
/Users/nixpulvis/Code/parser/src/lexer.rs:29:25: 29:27 note: ...so that captured variable `l2` does not outlive the enclosing closure
/Users/nixpulvis/Code/parser/src/lexer.rs:29:25: 29:27 note: ...so that captured variable `l2` does not outlive the enclosing closure
/Users/nixpulvis/Code/parser/src/lexer.rs:29 Error => return (*l2).call((s,)),
/Users/nixpulvis/Code/parser/src/lexer.rs:29 Error => return (*l2).call((s,)),
^~
^~
/Users/nixpulvis/Code/parser/src/lexer.rs:24:59: 32:2 note: but, the lifetime must be valid for the lifetime 'a as defined on the block at 24:58...
/Users/nixpulvis/Code/parser/src/lexer.rs:24:59: 32:2 note: but, the lifetime must be valid for the lifetime 'a as defined on the block at 24:58...
/Users/nixpulvis/Code/parser/src/lexer.rs:24 pub fn alt<'a>(l1: Lexer<'a>, l2: Lexer<'a>) -> Lexer<'a> {
/Users/nixpulvis/Code/parser/src/lexer.rs:24 pub fn alt<'a>(l1: Lexer<'a>, l2: Lexer<'a>) -> Lexer<'a> {
/Users/nixpulvis/Code/parser/src/lexer.rs:25 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:25 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:26 let r1 = (*l1).call((s,));
/Users/nixpulvis/Code/parser/src/lexer.rs:26 let r1 = (*l1).call((s,));
/Users/nixpulvis/Code/parser/src/lexer.rs:27 match r1 {
/Users/nixpulvis/Code/parser/src/lexer.rs:27 match r1 {
/Users/nixpulvis/Code/parser/src/lexer.rs:28 Success(_, _) => return r1,
/Users/nixpulvis/Code/parser/src/lexer.rs:28 Success(_, _) => return r1,
/Users/nixpulvis/Code/parser/src/lexer.rs:29 Error => return (*l2).call((s,)),
/Users/nixpulvis/Code/parser/src/lexer.rs:29 Error => return (*l2).call((s,)),
...
...
/Users/nixpulvis/Code/parser/src/lexer.rs:25:3: 31:4 note: ...so that it can be closed over into an object
/Users/nixpulvis/Code/parser/src/lexer.rs:25:3: 31:4 note: ...so that it can be closed over into an object
/Users/nixpulvis/Code/parser/src/lexer.rs:25 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:25 box |&: s: &'a str| {
/Users/nixpulvis/Code/parser/src/lexer.rs:26 let r1 = (*l1).call((s,));
/Users/nixpulvis/Code/parser/src/lexer.rs:26 let r1 = (*l1).call((s,));
/Users/nixpulvis/Code/parser/src/lexer.rs:27 match r1 {
/Users/nixpulvis/Code/parser/src/lexer.rs:27 match r1 {
/Users/nixpulvis/Code/parser/src/lexer.rs:28 Success(_, _) => return r1,
/Users/nixpulvis/Code/parser/src/lexer.rs:28 Success(_, _) => return r1,
/Users/nixpulvis/Code/parser/src/lexer.rs:29 Error => return (*l2).call((s,)),
/Users/nixpulvis/Code/parser/src/lexer.rs:29 Error => return (*l2).call((s,)),
/Users/nixpulvis/Code/parser/src/lexer.rs:30 }
/Users/nixpulvis/Code/parser/src/lexer.rs:30 }
...
...
error: aborting due to 2 previous errors
error: aborting due to 2 previous errors
Build failed, waiting for other jobs to finish...
Could not compile `parser`.
To learn more, run the command again with --verbose.
Do you have any binaries or integration tests ([test]) specified in the manifest or discovered implicitly? If I could take a peek at the code that would also be useful.
Here is the code. It's a tiny project at the moment because I can't figure out boxed unboxed closures :stuck_out_tongue_winking_eye: but testing it with cargo test will yield duplicate warnings on master.
There are #[test]s.
Hm, did you mean to include a link in there?
Oh man, it's going to be a long night, already losing my mind. Here you go. https://github.com/nixpulvis/parser
Aha! That definitely shouldn't be compiling twice, thanks @nixpulvis! I'll take a look into this hopefully soon.
:heart:
Isn鈥檛 the other compile a dependency of running rustdoc --test? You can disable this with doctest = false in Cargo.toml, but it would be nice if Cargo did that implicitly when it can figure out that the source does not contain any doctest. (Though I鈥檓 not sure how it would do _that_, maybe split rustdoc --test into doctest collection (which doesn鈥檛 require the crate to be built) and doctest compile/run?)
I don't think doctests should be opt in. I like that they are part of cargo test. It's just hard to read the warnings are errors as is.
This might be a bad idea, but maybe it actually might make sense to allow rustc to include doctests when passes an additional flag.
I don鈥檛 mean making doctest opt-in, only to avoid running zero doctests when Cargo can determine there are indeed zero, if that can be determine quickly.
But then the moment you add doctests then your back into the same problem.
I don鈥檛 see the problem. When you add doctests you don鈥檛 have zero doctests anymore and so Cargo should not detect that you have zero doctests anymore (again, assuming this detection is feasible) and would start running doctests again. This magic "detection" would run every time you run cargo test.
The problem is with the original problem of showing two compilations right next to each other with all their warnings and errors.
Ah, sorry I misunderstood. Yes indeed. I believe the reason for that is that rustdoc --test need a library to link against, but rustc --test creates an executable with the test harness, not a library.
Ah yes, @SimonSapin is right, I forgot about doc tests! For now I don't think Cargo will try to detect the presence or non-presence of doc tests as it would involve a dependency on rustdoc itself. If you'd like to just see one set of warnings at a time you can pass -j1 to ensure only one crate is built at a time. Otherwise this is currently working as intended.
This is going to be a pain point for adoption and usability.
Just my 2 cents.
I was experiencing this problem, or something similar. When I would run "cargo test" the tests defined in my library are getting run by the main executable as well as in the library, so they ran twice. Adding "test = false" for the library in my manifest fixed it.
Thanks Alex!
It doesn't matter now, but here is my code:
https://github.com/drewm1980/rust-omr
I think this is a poor user experience. For quite a while I've been dealing with duplicate output from cargo test and assumed it was just a bug that was going to get fixed. Now that I've found this issue, I don't think it's at all obvious that the duplicate output is coming from attempting to run non-existent doctests. If there really isn't a way to disable running doctests when there are none, it'd be great if there were an obvious warning message (or some other form of easily discoverable documentation) that explains why the output shows up twice by default.
@jimmycuadra You can add doctest = false in the [lib] section of your Cargo.toml. (Though I still wish Cargo would do better by default.)
Yes, that is what I did. It's just totally non-obvious that that's why you see double output by default.
This is still painful FWIW, getting a lot of unused warnings, and double errors on failed compiles.
@alexcrichton I understand the technical reason why this happens now, but since it is still a confusing user experience (as noted by duplicates of this issue in various forms continuing to be opened), would you be amenable to either reopening this issue, or me opening a new issue specifically for discussion and tracking of possible ways to address user confusion about the output?
@jimmycuadra that's tracked by #1854
cargo test --lib fixes this for me, but not doctest = false under [lib]. Using nightly- 2016-11-04. Is this expected or should doctest=false always eliminate duplicate output?
(FWIW, I ignored duplicates for three months in hopes the "bug" would be fixed. If there's a pain points FAQ/wiki I'd be glad to contribute to it).
@nathanaeljones do you have integration tests (tests/*.rs) or binaries? In that case cargo test would also have to build the library for those targets.
Yes, I do. That makes sense, thanks!
I'd echo the user experience complaints. As a newcomer, the current behaviour (still the case as of 1.30) is extremely baffling. I don't think it should be considered "fixed".
I'm adding my voice to the chorus with this. Duplicate messages by default makes an extremely poor and unprofessional user experience. Though a minor issue, the fact that it continues to be pointedly ignored after so many years (now 1.41.0) only exacerbates matters. It reveals an administrative problem that doesn't speak well for Rust as a project.
Hello, from Jul 2020; 6 years later, and still a thing.
As with @lilith, [lib]doctest = false didn't work, cargo test --lib did.
Latest nightly here. This makes it pretty difficult to find unique errors that I need to fix in the middle of a refactor:

Especially when I don't just want to test just by --lib, I really do want to test everything.
Most helpful comment
I'm adding my voice to the chorus with this. Duplicate messages by default makes an extremely poor and unprofessional user experience. Though a minor issue, the fact that it continues to be pointedly ignored after so many years (now 1.41.0) only exacerbates matters. It reveals an administrative problem that doesn't speak well for Rust as a project.