mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 15:49:44 -05:00
fix(test): capture inherited stdout and stderr for subprocesses in test output (#14395)
This commit is contained in:
parent
f07f246ae8
commit
a1b4aa2ae6
8 changed files with 84 additions and 33 deletions
|
@ -760,9 +760,8 @@ impl test::TestReporter for LspTestReporter {
|
|||
.and_then(|v| v.last().map(|td| td.into()))
|
||||
});
|
||||
let value = match output {
|
||||
test::TestOutput::PrintStdout(value)
|
||||
| test::TestOutput::PrintStderr(value) => value.replace('\n', "\r\n"),
|
||||
test::TestOutput::Stdout(bytes) | test::TestOutput::Stderr(bytes) => {
|
||||
test::TestOutput::String(value) => value.replace('\n', "\r\n"),
|
||||
test::TestOutput::Bytes(bytes) => {
|
||||
String::from_utf8_lossy(bytes).replace('\n', "\r\n")
|
||||
}
|
||||
};
|
||||
|
|
|
@ -79,12 +79,8 @@ pub fn create_stdout_stderr_pipes(
|
|||
let (stdout_reader, stdout_writer) = os_pipe::pipe().unwrap();
|
||||
let (stderr_reader, stderr_writer) = os_pipe::pipe().unwrap();
|
||||
|
||||
start_output_redirect_thread(stdout_reader, sender.clone(), |bytes| {
|
||||
TestOutput::Stdout(bytes)
|
||||
});
|
||||
start_output_redirect_thread(stderr_reader, sender, |bytes| {
|
||||
TestOutput::Stderr(bytes)
|
||||
});
|
||||
start_output_redirect_thread(stdout_reader, sender.clone());
|
||||
start_output_redirect_thread(stderr_reader, sender);
|
||||
|
||||
(stdout_writer, stderr_writer)
|
||||
}
|
||||
|
@ -92,7 +88,6 @@ pub fn create_stdout_stderr_pipes(
|
|||
fn start_output_redirect_thread(
|
||||
mut pipe_reader: os_pipe::PipeReader,
|
||||
sender: UnboundedSender<TestEvent>,
|
||||
map_test_output: impl Fn(Vec<u8>) -> TestOutput + Send + 'static,
|
||||
) {
|
||||
tokio::task::spawn_blocking(move || loop {
|
||||
let mut buffer = [0; 512];
|
||||
|
@ -101,7 +96,9 @@ fn start_output_redirect_thread(
|
|||
Ok(size) => size,
|
||||
};
|
||||
if sender
|
||||
.send(TestEvent::Output(map_test_output(buffer[0..size].to_vec())))
|
||||
.send(TestEvent::Output(TestOutput::Bytes(
|
||||
buffer[0..size].to_vec(),
|
||||
)))
|
||||
.is_err()
|
||||
{
|
||||
break;
|
||||
|
@ -170,14 +167,10 @@ fn op_dispatch_test_event(
|
|||
pub fn op_print(
|
||||
state: &mut OpState,
|
||||
msg: String,
|
||||
is_err: bool,
|
||||
_is_err: bool,
|
||||
) -> Result<(), AnyError> {
|
||||
let sender = state.borrow::<UnboundedSender<TestEvent>>().clone();
|
||||
let msg = if is_err {
|
||||
TestOutput::PrintStderr(msg)
|
||||
} else {
|
||||
TestOutput::PrintStdout(msg)
|
||||
};
|
||||
let msg = TestOutput::String(msg);
|
||||
sender.send(TestEvent::Output(msg)).ok();
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -302,6 +302,12 @@ itest!(no_prompt_with_denied_perms {
|
|||
output: "test/no_prompt_with_denied_perms.out",
|
||||
});
|
||||
|
||||
itest!(captured_subprocess_output {
|
||||
args: "test --allow-run --allow-read --unstable test/captured_subprocess_output.ts",
|
||||
exit_code: 0,
|
||||
output: "test/captured_subprocess_output.out",
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn recursive_permissions_pledge() {
|
||||
let output = util::deno_cmd()
|
||||
|
|
17
cli/tests/testdata/test/captured_subprocess_output.out
vendored
Normal file
17
cli/tests/testdata/test/captured_subprocess_output.out
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
[WILDCARD]
|
||||
running 1 test from [WILDCARD]/captured_subprocess_output.ts
|
||||
output ...
|
||||
------- output -------
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
----- output end -----
|
||||
ok ([WILDCARD]s)
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ([WILDCARD]s)
|
||||
[WILDCARD]
|
23
cli/tests/testdata/test/captured_subprocess_output.ts
vendored
Normal file
23
cli/tests/testdata/test/captured_subprocess_output.ts
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
Deno.test("output", async () => {
|
||||
const p = Deno.run({
|
||||
cmd: [Deno.execPath(), "eval", "console.log(1); console.error(2);"],
|
||||
});
|
||||
await p.status();
|
||||
await p.close();
|
||||
Deno.spawnSync(Deno.execPath(), {
|
||||
args: ["eval", "console.log(3); console.error(4);"],
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
});
|
||||
await Deno.spawn(Deno.execPath(), {
|
||||
args: ["eval", "console.log(5); console.error(6);"],
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
});
|
||||
const c = await Deno.spawnChild(Deno.execPath(), {
|
||||
args: ["eval", "console.log(7); console.error(8);"],
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
});
|
||||
await c.status;
|
||||
});
|
|
@ -83,10 +83,8 @@ pub struct TestDescription {
|
|||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum TestOutput {
|
||||
PrintStdout(String),
|
||||
PrintStderr(String),
|
||||
Stdout(Vec<u8>),
|
||||
Stderr(Vec<u8>),
|
||||
String(String),
|
||||
Bytes(Vec<u8>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||
|
@ -351,18 +349,14 @@ impl TestReporter for PrettyTestReporter {
|
|||
println!("{}", colors::gray("------- output -------"));
|
||||
}
|
||||
match output {
|
||||
TestOutput::PrintStdout(line) => {
|
||||
TestOutput::String(line) => {
|
||||
// output everything to stdout in order to prevent
|
||||
// stdout and stderr racing
|
||||
print!("{}", line)
|
||||
}
|
||||
TestOutput::PrintStderr(line) => {
|
||||
eprint!("{}", line)
|
||||
}
|
||||
TestOutput::Stdout(bytes) => {
|
||||
TestOutput::Bytes(bytes) => {
|
||||
std::io::stdout().write_all(bytes).unwrap();
|
||||
}
|
||||
TestOutput::Stderr(bytes) => {
|
||||
std::io::stderr().write_all(bytes).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -186,8 +186,20 @@ fn op_run(state: &mut OpState, run_args: RunArgs) -> Result<RunInfo, AnyError> {
|
|||
|
||||
// TODO: make this work with other resources, eg. sockets
|
||||
c.stdin(run_args.stdin.as_stdio(state)?);
|
||||
c.stdout(run_args.stdout.as_stdio(state)?);
|
||||
c.stderr(run_args.stderr.as_stdio(state)?);
|
||||
c.stdout(
|
||||
match run_args.stdout {
|
||||
StdioOrRid::Stdio(Stdio::Inherit) => StdioOrRid::Rid(1),
|
||||
value => value,
|
||||
}
|
||||
.as_stdio(state)?,
|
||||
);
|
||||
c.stderr(
|
||||
match run_args.stderr {
|
||||
StdioOrRid::Stdio(Stdio::Inherit) => StdioOrRid::Rid(2),
|
||||
value => value,
|
||||
}
|
||||
.as_stdio(state)?,
|
||||
);
|
||||
|
||||
// We want to kill child when it's closed
|
||||
c.kill_on_drop(true);
|
||||
|
|
|
@ -4,6 +4,7 @@ use super::io::ChildStderrResource;
|
|||
use super::io::ChildStdinResource;
|
||||
use super::io::ChildStdoutResource;
|
||||
use super::process::Stdio;
|
||||
use super::process::StdioOrRid;
|
||||
use crate::permissions::Permissions;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op;
|
||||
|
@ -147,8 +148,14 @@ fn create_command(
|
|||
}
|
||||
|
||||
command.stdin(args.stdio.stdin.as_stdio());
|
||||
command.stdout(args.stdio.stdout.as_stdio());
|
||||
command.stderr(args.stdio.stderr.as_stdio());
|
||||
command.stdout(match args.stdio.stdout {
|
||||
Stdio::Inherit => StdioOrRid::Rid(1).as_stdio(state)?,
|
||||
value => value.as_stdio(),
|
||||
});
|
||||
command.stderr(match args.stdio.stderr {
|
||||
Stdio::Inherit => StdioOrRid::Rid(2).as_stdio(state)?,
|
||||
value => value.as_stdio(),
|
||||
});
|
||||
|
||||
Ok(command)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue