Skip to content

Commit 6118f26

Browse files
author
Ralph Küpper
committed
fix(test): format nested reporter output
1 parent 504e180 commit 6118f26

2 files changed

Lines changed: 78 additions & 13 deletions

File tree

crates/perry-runtime/src/node_submodules/test_reporters.rs

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,12 @@ pub(crate) extern "C" fn thunk_reporter_lcov(_closure: *const ClosureHeader, sou
3636
}
3737

3838
fn reporter_transform(kind: i32) -> f64 {
39-
let transform = make_closure(reporter_transform_chunk as *const u8, 3, 1);
39+
let captures = if kind == REPORTER_SPEC { 2 } else { 1 };
40+
let transform = make_closure(reporter_transform_chunk as *const u8, 3, captures);
4041
js_closure_set_capture_f64(transform, 0, kind as f64);
42+
if kind == REPORTER_SPEC {
43+
js_closure_set_capture_f64(transform, 1, undefined_value());
44+
}
4145
let opts = js_object_alloc(0, 1);
4246
set_field(opts, "transform", boxed_ptr(transform));
4347
crate::node_stream::js_node_stream_transform_new(boxed_ptr(opts))
@@ -50,7 +54,26 @@ extern "C" fn reporter_transform_chunk(
5054
callback: f64,
5155
) -> f64 {
5256
let kind = js_closure_get_capture_f64(closure, 0) as i32;
53-
let output = format_reporter_event(kind, chunk);
57+
let scope = crate::gc::RuntimeHandleScope::new();
58+
let closure_handle = scope.root_raw_mut_ptr(closure as *mut ClosureHeader);
59+
let chunk_handle = scope.root_nanbox_f64(chunk);
60+
let stack_handle = (kind == REPORTER_SPEC)
61+
.then(|| scope.root_nanbox_f64(js_closure_get_capture_f64(closure, 1)));
62+
let mut starts = stack_handle
63+
.as_ref()
64+
.and_then(|handle| array_values(handle.get_nanbox_f64()))
65+
.unwrap_or_default();
66+
let output = format_reporter_event(kind, chunk_handle.get_nanbox_f64(), &mut starts);
67+
68+
if kind == REPORTER_SPEC {
69+
let mut stack = crate::array::js_array_alloc(starts.len() as u32);
70+
for start in starts {
71+
stack = crate::array::js_array_push_f64(stack, start);
72+
}
73+
closure_handle.with_mut_ptr(|closure: *mut ClosureHeader| {
74+
js_closure_set_capture_f64(closure, 1, boxed_ptr(stack));
75+
});
76+
}
5477
if !output.is_empty() {
5578
let this = crate::object::js_implicit_this_get();
5679
let handle = (this.to_bits() & POINTER_MASK) as i64;
@@ -88,6 +111,18 @@ fn event_data(event: f64) -> f64 {
88111
object_property(event, b"data").unwrap_or(undefined_value())
89112
}
90113

114+
fn event_nesting(data: f64) -> usize {
115+
object_property(data, b"nesting")
116+
.map(|value| JSValue::from_bits(value.to_bits()).to_number())
117+
.filter(|value| value.is_finite() && *value > 0.0)
118+
.map(|value| value as usize)
119+
.unwrap_or(0)
120+
}
121+
122+
fn event_indent(data: f64, width: usize) -> String {
123+
" ".repeat(event_nesting(data).saturating_mul(width))
124+
}
125+
91126
fn format_reporter_events(kind: i32, events: &[f64]) -> String {
92127
if kind == REPORTER_LCOV {
93128
return String::new();
@@ -98,8 +133,9 @@ fn format_reporter_events(kind: i32, events: &[f64]) -> String {
98133
} else if kind == REPORTER_JUNIT {
99134
out.push_str("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<testsuites>\n");
100135
}
136+
let mut starts = Vec::new();
101137
for &event in events {
102-
out.push_str(&format_reporter_event(kind, event));
138+
out.push_str(&format_reporter_event(kind, event, &mut starts));
103139
}
104140
if kind == REPORTER_DOT && !out.is_empty() && !out.ends_with('\n') {
105141
out.push('\n');
@@ -110,34 +146,51 @@ fn format_reporter_events(kind: i32, events: &[f64]) -> String {
110146
out
111147
}
112148

113-
fn format_reporter_event(kind: i32, event: f64) -> String {
149+
fn format_reporter_event(kind: i32, event: f64, starts: &mut Vec<f64>) -> String {
114150
let Some(typ) = event_type(event) else {
115151
return String::new();
116152
};
117153
let data = event_data(event);
118154
match kind {
119155
REPORTER_SPEC => match typ.as_str() {
120-
"test:pass" => object_string(data, b"name")
121-
.map(|name| format!("✔ {name}\n"))
122-
.unwrap_or_default(),
156+
"test:start" => {
157+
starts.push(data);
158+
String::new()
159+
}
160+
"test:pass" => {
161+
starts.pop();
162+
let mut output = String::new();
163+
for parent in starts.drain(..) {
164+
if let Some(name) = object_string(parent, b"name") {
165+
output.push_str(&format!("{}▶ {name}\n", event_indent(parent, 2)));
166+
}
167+
}
168+
if let Some(name) = object_string(data, b"name") {
169+
output.push_str(&format!("{}✔ {name}\n", event_indent(data, 2)));
170+
}
171+
output
172+
}
123173
"test:diagnostic" => object_string(data, b"message")
124-
.map(|message| format!("ℹ {message}\n"))
174+
.map(|message| format!("{}ℹ {message}\n", event_indent(data, 2)))
125175
.unwrap_or_default(),
126176
_ => String::new(),
127177
},
128178
REPORTER_TAP => match typ.as_str() {
129179
"test:start" => object_string(data, b"name")
130-
.map(|name| format!("# Subtest: {name}\n"))
180+
.map(|name| format!("{}# Subtest: {name}\n", event_indent(data, 4)))
131181
.unwrap_or_default(),
132182
"test:pass" => {
133183
let name = object_string(data, b"name").unwrap_or_default();
184+
let indent = event_indent(data, 4);
134185
let detail_type = object_property(data, b"details")
135186
.and_then(|details| object_string(details, b"type"))
136187
.unwrap_or_else(|| "test".to_string());
137-
format!("ok undefined - {name}\n ---\n type: '{detail_type}'\n ...\n")
188+
format!(
189+
"{indent}ok undefined - {name}\n{indent} ---\n{indent} type: '{detail_type}'\n{indent} ...\n"
190+
)
138191
}
139192
"test:diagnostic" => object_string(data, b"message")
140-
.map(|message| format!("# {message}\n"))
193+
.map(|message| format!("{}# {message}\n", event_indent(data, 4)))
141194
.unwrap_or_default(),
142195
_ => String::new(),
143196
},

test-parity/node-suite/test/reporters/nested.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,21 @@ const events = [
1414
},
1515
];
1616

17-
async function collect(name: string, reporter: any) {
17+
async function collect(name: string, reporter: any): Promise<void> {
1818
let output = "";
19-
for await (const chunk of reporter(Readable.from(events))) output += String(chunk);
19+
const result = reporter(Readable.from(events));
20+
if (typeof result.write === "function") {
21+
const transform = reporter();
22+
transform.on("data", (chunk: unknown) => {
23+
output += String(chunk);
24+
});
25+
await new Promise<void>((resolve) => {
26+
transform.on("end", resolve);
27+
Readable.from(events).pipe(transform);
28+
});
29+
} else {
30+
for await (const chunk of result) output += String(chunk);
31+
}
2032
console.log(`${name}:`, JSON.stringify(output));
2133
}
2234

0 commit comments

Comments
 (0)