Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests do not capture stdout() / stderr() #90785

Open
akhouderchah opened this issue Nov 10, 2021 · 10 comments
Open

Tests do not capture stdout() / stderr() #90785

akhouderchah opened this issue Nov 10, 2021 · 10 comments
Labels
A-libtest Area: `#[test]` / the `test` library T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@akhouderchah
Copy link

Output capture for tests (d0d0e78, 11ce918) causes print_to (and consequently _print & macros) to redirect to OUTPUT_CAPTURE if set. IIUC, this leaves any direct usage of stdout() uncaptured.

This is inconvenient for systems which use stdout() with generic code over io::Write.

Basically a dupe of the unresolved-but-closed #12309.

@mina86
Copy link
Contributor

mina86 commented Jan 7, 2022

Steps to reproduce:

$ cat src/lib.rs
#[test]
fn test() {
    use std::io::Write;

    eprintln!("eprintln");
    println!("println");
    std::io::stdout().write_all(b"stdout\n").unwrap();
    std::io::stderr().write_all(b"stderr\n").unwrap();
}
$ cargo test --tests
    Finished test [unoptimized] target(s) in 0.00s
     Running unittests (target/debug/deps/a-7d5a981fc2a1d521)

running 1 test
stdout
stderr
test test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

$ cargo --version
cargo 1.58.0-nightly (7f08ace4f 2021-11-24)

Expected output:

$ cargo test --tests
    Finished test [unoptimized] target(s) in 0.00s
     Running unittests (target/debug/deps/a-7d5a981fc2a1d521)

running 1 test
test test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Without looking at libtest code, the proper solution seems to be:

old_stdout = dup(1);
old_stderr = dup(2);

tmp = mktemp(…);
tmp_fd = open(tmp);

dup2(tmp_fd, 1);
dup2(tmp_fd, 2);

success = run_the_test();

dup2(old_stdout, 1);
dup2(old_stderr, 2);

if !success {
    fseek(tmp_fd, 0);
    cat(tmp_fd);
}

close(tmp_fd);
unlink(tmp);

This would also solve #92370

mina86 added a commit to mina86/nearcore that referenced this issue Jan 7, 2022
Rust’s `libtest` has a… limitation where it doesn’t capture data
written directly to `std::io::stdout` or `std::io::stderr` (see
rust-lang/rust#90785).  Since we are test
logger to use the latter, none of the log messages are captured by the
test harness.

The solution is to use `TestWriter` as the writer instead which
cooperates `libtest` better.

Issue: near#4490
@davepacheco
Copy link

I ran into this today as well. I'm not sure how hard this is to fix. Even if it's decided not to fix it, I think some better documentation around output capturing could help. I understood it to be stdout/stderr that was captured, but it's really just thread-local print! and friends. I ran into this because I was using term::stdout(), but I think you'd also be likely to see this in async tests that print output from other tasks.

@vojtechkral
Copy link
Contributor

vojtechkral commented Nov 7, 2022

Ran into this today. This is particularly unpleasant in the following scenario:

eprintln!("Something something");
stderr.write_all(tty_escape_cursor_up_clear_line).unwrap();
eprintln!("Something else");

Running that code from the test harness leads to the eprintlns being captured, but the escape sequences not, and they then end up messing up the test harness output. The worst bit is I can't seem to find a way to detect this somehow (in tests the stderr is still a tty), I'm probably going to have to introduce some kind of a switch to disable tty stuff from tests explicitly.

@saethlin
Copy link
Member

saethlin commented Nov 8, 2022

@rustbot label +T-libs +A-libtest

@rustbot rustbot added A-libtest Area: `#[test]` / the `test` library T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Nov 8, 2022
@mathstuf
Copy link
Contributor

mathstuf commented Feb 3, 2023

Without looking at libtest code, the proper solution seems to be: [[dup juggling]]

Alas, dup and friends are process-wide, so this strategy forces a limit of -j1 on test execution.

@vojtechkral
Copy link
Contributor

vojtechkral commented Feb 7, 2023

Without looking at libtest code, the proper solution seems to be: [[dup juggling]]

Alas, dup and friends are process-wide, so this strategy forces a limit of -j1 on test execution.

Is that a problem though? Arguably you could dup original 1 and 2, use that for all logging/cli output and make 1 and 2 pipes (for tests to write to).

Edit: A bonus of this solution would be that test output would have no way of messing up test harness output (which it can do now since it has access to the tty).

@vojtechkral
Copy link
Contributor

Anyone knows what nextest does? Chances are they've got this solved already...

@mathstuf
Copy link
Contributor

mathstuf commented Feb 8, 2023

make 1 and 2 pipes (for tests to write to).

Pipes to what? There still wouldn't be per-test places for things to go, so "what happened for failed tests" would be jumbled.

@vojtechkral
Copy link
Contributor

vojtechkral commented Feb 8, 2023

Pipes to what?

A thread (not) copying that to the real std{out,err} based on settings. It could also filter out escape sequences.

There still wouldn't be per-test places for things to go, so "what happened for failed tests" would be jumbled.

Sure, but at least the output would actually be captured. And you can always use -j1 when tests fail and you need clean output.

@AlejandroFNadal
Copy link

For people searching and finding this issue:

Add in each test, instead of
#[test]
put:

#[test_log::test]

Then, to cargo toml

[dev-dependencies]
test-log = "*"

Finally, set env variable to desired debugging level:
export RUST_LOG="debug"

@epage epage changed the title Tests do not capture stdout() Tests do not capture stdout() / stderr() Jun 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-libtest Area: `#[test]` / the `test` library T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
Status: No status
Development

No branches or pull requests

8 participants