Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/test/reporters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Every test file that ran has a `<testsuite>`, even if it has no tests. Its `time

An uncaught exception or unhandled rejection while a test is running fails that test, and its `<failure>`, `<flakyFailure>` or `<rerunFailure>` has `uncaught="true"`. Examples are an error thrown by a timer and a rejected promise that the test didn't await. The attribute is absent when the test function itself throws, returns a promise that rejects, or calls `done(error)`.

An error outside of any test is reported as a failed `<testcase>` named `(unhandled error)` in that file's `<testsuite>`. Examples are a test file or a preload script that throws while it loads, has a syntax error, or imports a module that doesn't exist.
An error outside of any test is reported as a failed `<testcase>` named `(unhandled error)` in that file's `<testsuite>`. Examples are a test file or a preload script that throws while it loads, has a syntax error, or imports a module that doesn't exist. The `<testcase>` is directly inside the file's `<testsuite>`, also when the error comes while the tests of a `describe` block run, for example from a timer that an earlier test left behind.

With `--parallel`, a file whose worker crashed has a failed `<testcase>` named `(worker crashed)`. The files that the crash kept from finishing or from starting have one named `(aborted)`. After `SIGINT` or `SIGTERM`, a file that was running has one named `(interrupted)`, and a file that hadn't started has one named `(aborted)`.

Expand All @@ -85,7 +85,9 @@ junit = "path/to/junit.xml" # Output path for JUnit XML report

#### Describe Blocks in JUnit Reports

By default, each `describe` block becomes a `<testsuite>` nested inside its file's `<testsuite>`. Some tools only read the `<testcase>` elements directly inside a file's `<testsuite>`. For those tools, use `--reporter-junit-suites=flat`:
By default, each `describe` block becomes a `<testsuite>` nested inside its file's `<testsuite>`. A block is one `<testsuite>`, in whatever order its tests end. Two blocks with the same name share one `<testsuite>` when nothing is between them in the report, for example the rows of a `describe.each` that have the same title. A block with an empty title, or with no test in the report, has no `<testsuite>`.

Some tools only read the `<testcase>` elements directly inside a file's `<testsuite>`. For those tools, use `--reporter-junit-suites=flat`:

```sh terminal icon="terminal"
bun test --reporter=junit --reporter-outfile=./junit.xml --reporter-junit-suites=flat
Expand Down
6 changes: 3 additions & 3 deletions src/runtime/cli/test/parallel/Frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub(crate) enum Kind {
/// then, only when the worker runs with `--reporter`, the structured
/// result (`runner::encode_test_case`): u32 status, u32 assertions,
/// u64 elapsed_ns, u32 line, str name, u32 n × {str scope_name,
/// u32 scope_line}, u32 has_failure [, str name, str message, str body, u32 uncaught]
/// u32 scope_line, u32 scope_run, u64 scope_address}, u32 has_failure, u32 earlier_failures, (earlier_failures + has_failure) × {str name, str message, str body, u32 uncaught}
TestDone,
/// 10 × u32: file_idx, pass, fail, skip, todo, expectations, skipped_label,
/// files, shuffled, unhandled; u64 elapsed_ns; 4 × u32, 0 where nobody measures: the megabytes by which the worker grew at most, those of its largest child,
Expand Down Expand Up @@ -97,10 +97,10 @@ impl Frame {
// Never let a single frame exceed `MAX_PAYLOAD` — the receiver treats that
// as a corrupt-channel signal and closes, which would surface as a spurious
// worker crash. Truncate the string in place instead. Leave headroom
// so the fixed-size tail of the frame (u32s, short paths, and the
// so the fixed-size tail of the frame (u32s and u64s, short paths, and the
// 4-byte prefixes of any further strings, which go empty) still fits.
const TRUNC: &[u8] = b"\n... [output truncated: would exceed --parallel IPC frame limit]\n";
const HEADROOM: usize = 4096;
const HEADROOM: usize = 8192;
let used: usize = (self.buf.len() - 5) + 4; // current payload + str-len prefix
let room: usize = ((MAX_PAYLOAD as usize) - HEADROOM).saturating_sub(used);
let (keep, marker): (usize, &[u8]) = if s.len() <= room {
Expand Down
15 changes: 11 additions & 4 deletions src/runtime/cli/test/parallel/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1204,9 +1204,12 @@ fn encode_test_case(wf: &mut Frame, t: &test_command::TestCaseReport<'_>) {
wf.u32(t.line_number);
wf.str(t.name);
wf.u32(u32::try_from(t.scopes.len()).expect("int cast"));
for &(name, line) in &t.scopes {
wf.str(name);
wf.u32(line);
for scope in &t.scopes {
wf.str(scope.name);
wf.u32(scope.line);
let (run, address) = scope.block.to_wire();
wf.u32(run);
wf.u64(address);
Comment thread
claude[bot] marked this conversation as resolved.
Resolved
}
// Earlier attempts' failures first, then the last attempt's if it failed.
wf.u32(u32::from(t.failure.is_some()));
Expand Down Expand Up @@ -1238,7 +1241,11 @@ pub(crate) fn decode_test_case<'a>(
}
let mut scopes = Vec::with_capacity(n);
for _ in 0..n {
scopes.push((rd.str(), rd.u32()));
scopes.push(test_command::ScopeReport {
name: rd.str(),
line: rd.u32(),
block: test_command::BlockKey::from_wire(rd.u32(), rd.u64()),
});
}
let has_failure = rd.u32() != 0;
let earlier = rd.u32() as usize;
Expand Down
Loading
Loading