1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-24 08:09:08 -05:00

refactor(cli/tools/test_runner): split reporter into distinct stages (#11395)

This splits up the reporter into smaller functions, one for each
distinct event that happens during the testing process.
This commit is contained in:
Casper Beyer 2021-07-15 03:05:16 +08:00 committed by GitHub
parent 6ce2a089a8
commit 69ca44d8e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 146 additions and 168 deletions

View file

@ -1,5 +1,4 @@
use crate::tools::test_runner::TestEvent; use crate::tools::test_runner::TestEvent;
use crate::tools::test_runner::TestMessage;
use deno_core::error::generic_error; use deno_core::error::generic_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::JsRuntime; use deno_core::JsRuntime;
@ -8,7 +7,6 @@ use deno_core::OpState;
use deno_runtime::ops::worker_host::create_worker_permissions; use deno_runtime::ops::worker_host::create_worker_permissions;
use deno_runtime::ops::worker_host::PermissionsArg; use deno_runtime::ops::worker_host::PermissionsArg;
use deno_runtime::permissions::Permissions; use deno_runtime::permissions::Permissions;
use serde::Deserialize;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use uuid::Uuid; use uuid::Uuid;
@ -19,7 +17,8 @@ pub fn init(rt: &mut JsRuntime) {
"op_restore_test_permissions", "op_restore_test_permissions",
op_restore_test_permissions, op_restore_test_permissions,
); );
super::reg_sync(rt, "op_post_test_message", op_post_test_message); super::reg_sync(rt, "op_get_test_origin", op_get_test_origin);
super::reg_sync(rt, "op_dispatch_test_event", op_dispatch_test_event);
} }
#[derive(Clone)] #[derive(Clone)]
@ -65,27 +64,21 @@ pub fn op_restore_test_permissions(
} }
} }
#[derive(Debug, Deserialize)] fn op_get_test_origin(
#[serde(rename_all = "camelCase")]
struct PostTestMessageArgs {
message: TestMessage,
}
fn op_post_test_message(
state: &mut OpState, state: &mut OpState,
args: PostTestMessageArgs,
_: (), _: (),
) -> Result<bool, AnyError> { _: (),
let origin = state.borrow::<ModuleSpecifier>().to_string(); ) -> Result<String, AnyError> {
let message = args.message; Ok(state.borrow::<ModuleSpecifier>().to_string())
}
let event = TestEvent { origin, message };
fn op_dispatch_test_event(
let sender = state.borrow::<Sender<TestEvent>>().clone(); state: &mut OpState,
event: TestEvent,
if sender.send(event).is_err() { _: (),
Ok(false) ) -> Result<(), AnyError> {
} else { let sender = state.borrow::<Sender<TestEvent>>().clone();
Ok(true) sender.send(event).ok();
}
Ok(())
} }

View file

@ -35,6 +35,13 @@ use std::time::Duration;
use std::time::Instant; use std::time::Instant;
use swc_common::comments::CommentKind; use swc_common::comments::CommentKind;
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TestDescription {
pub origin: String,
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Deserialize)] #[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum TestResult { pub enum TestResult {
@ -43,28 +50,21 @@ pub enum TestResult {
Failed(String), Failed(String),
} }
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(tag = "kind", content = "data", rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum TestMessage { pub struct TestPlan {
Plan { pub origin: String,
pending: usize, pub total: usize,
filtered: usize, pub filtered_out: usize,
only: bool, pub used_only: bool,
},
Wait {
name: String,
},
Result {
name: String,
duration: usize,
result: TestResult,
},
} }
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct TestEvent { #[serde(rename_all = "camelCase")]
pub origin: String, pub enum TestEvent {
pub message: TestMessage, Plan(TestPlan),
Wait(TestDescription),
Result(TestDescription, TestResult, u64),
} }
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
@ -75,7 +75,7 @@ pub struct TestSummary {
pub ignored: usize, pub ignored: usize,
pub filtered_out: usize, pub filtered_out: usize,
pub measured: usize, pub measured: usize,
pub failures: Vec<(String, String)>, pub failures: Vec<(TestDescription, String)>,
} }
impl TestSummary { impl TestSummary {
@ -101,8 +101,15 @@ impl TestSummary {
} }
trait TestReporter { trait TestReporter {
fn visit_event(&mut self, event: TestEvent); fn report_plan(&mut self, plan: &TestPlan);
fn done(&mut self, summary: &TestSummary, elapsed: &Duration); fn report_wait(&mut self, description: &TestDescription);
fn report_result(
&mut self,
description: &TestDescription,
result: &TestResult,
elapsed: u64,
);
fn report_summary(&mut self, summary: &TestSummary, elapsed: &Duration);
} }
struct PrettyTestReporter { struct PrettyTestReporter {
@ -116,76 +123,52 @@ impl PrettyTestReporter {
} }
impl TestReporter for PrettyTestReporter { impl TestReporter for PrettyTestReporter {
fn visit_event(&mut self, event: TestEvent) { fn report_plan(&mut self, plan: &TestPlan) {
match &event.message { let inflection = if plan.total == 1 { "test" } else { "tests" };
TestMessage::Plan { println!("running {} {} from {}", plan.total, inflection, plan.origin);
pending, }
filtered: _,
only: _,
} => {
if *pending == 1 {
println!("running {} test from {}", pending, event.origin);
} else {
println!("running {} tests from {}", pending, event.origin);
}
}
TestMessage::Wait { name } => { fn report_wait(&mut self, description: &TestDescription) {
if !self.concurrent { if !self.concurrent {
print!("test {} ...", name); print!("test {} ...", description.name);
}
}
TestMessage::Result {
name,
duration,
result,
} => {
if self.concurrent {
print!("test {} ...", name);
}
match result {
TestResult::Ok => {
println!(
" {} {}",
colors::green("ok"),
colors::gray(format!("({}ms)", duration))
);
}
TestResult::Ignored => {
println!(
" {} {}",
colors::yellow("ignored"),
colors::gray(format!("({}ms)", duration))
);
}
TestResult::Failed(_) => {
println!(
" {} {}",
colors::red("FAILED"),
colors::gray(format!("({}ms)", duration))
);
}
}
}
} }
} }
fn done(&mut self, summary: &TestSummary, elapsed: &Duration) { fn report_result(
&mut self,
description: &TestDescription,
result: &TestResult,
elapsed: u64,
) {
if self.concurrent {
print!("test {} ...", description.name);
}
let status = match result {
TestResult::Ok => colors::green("ok").to_string(),
TestResult::Ignored => colors::yellow("ignored").to_string(),
TestResult::Failed(_) => colors::red("FAILED").to_string(),
};
println!(
" {} {}",
status,
colors::gray(format!("({}ms)", elapsed)).to_string()
);
}
fn report_summary(&mut self, summary: &TestSummary, elapsed: &Duration) {
if !summary.failures.is_empty() { if !summary.failures.is_empty() {
println!("\nfailures:\n"); println!("\nfailures:\n");
for (name, error) in &summary.failures { for (description, error) in &summary.failures {
println!("{}", name); println!("{}", description.name);
println!("{}", error); println!("{}", error);
println!(); println!();
} }
println!("failures:\n"); println!("failures:\n");
for (name, _) in &summary.failures { for (description, _) in &summary.failures {
println!("\t{}", name); println!("\t{}", description.name);
} }
} }
@ -196,15 +179,15 @@ impl TestReporter for PrettyTestReporter {
}; };
println!( println!(
"\ntest result: {}. {} passed; {} failed; {} ignored; {} measured; {} filtered out {}\n", "\ntest result: {}. {} passed; {} failed; {} ignored; {} measured; {} filtered out {}\n",
status, status,
summary.passed, summary.passed,
summary.failed, summary.failed,
summary.ignored, summary.ignored,
summary.measured, summary.measured,
summary.filtered_out, summary.filtered_out,
colors::gray(format!("({}ms)", elapsed.as_millis())), colors::gray(format!("({}ms)", elapsed.as_millis())),
); );
} }
} }
@ -522,43 +505,42 @@ pub async fn run_tests(
let mut used_only = false; let mut used_only = false;
for event in receiver.iter() { for event in receiver.iter() {
match event.message.clone() { match event {
TestMessage::Plan { TestEvent::Plan(plan) => {
pending, summary.total += plan.total;
filtered, summary.filtered_out += plan.filtered_out;
only,
} => {
summary.total += pending;
summary.filtered_out += filtered;
if only { if plan.used_only {
used_only = true; used_only = true;
} }
reporter.report_plan(&plan);
} }
TestMessage::Result { TestEvent::Wait(description) => {
name, reporter.report_wait(&description);
duration: _, }
result,
} => match result { TestEvent::Result(description, result, elapsed) => {
TestResult::Ok => { match &result {
summary.passed += 1; TestResult::Ok => {
summary.passed += 1;
}
TestResult::Ignored => {
summary.ignored += 1;
}
TestResult::Failed(error) => {
summary.failed += 1;
summary.failures.push((description.clone(), error.clone()));
}
} }
TestResult::Ignored => { reporter.report_result(&description, &result, elapsed);
summary.ignored += 1; }
}
TestResult::Failed(error) => {
summary.failed += 1;
summary.failures.push((name.clone(), error.clone()));
}
},
_ => {}
} }
reporter.visit_event(event);
if let Some(x) = fail_fast { if let Some(x) = fail_fast {
if summary.failed >= x { if summary.failed >= x {
break; break;
@ -567,7 +549,7 @@ pub async fn run_tests(
} }
let elapsed = Instant::now().duration_since(earlier); let elapsed = Instant::now().duration_since(earlier);
reporter.done(&summary, &elapsed); reporter.report_summary(&summary, &elapsed);
if used_only { if used_only {
println!( println!(

View file

@ -186,10 +186,6 @@ finishing test case.`;
ArrayPrototypePush(tests, testDef); ArrayPrototypePush(tests, testDef);
} }
function postTestMessage(kind, data) {
return core.opSync("op_post_test_message", { message: { kind, data } });
}
function createTestFilter(filter) { function createTestFilter(filter) {
return (def) => { return (def) => {
if (filter) { if (filter) {
@ -223,25 +219,38 @@ finishing test case.`;
} }
} }
function getTestOrigin() {
return core.opSync("op_get_test_origin");
}
function dispatchTestEvent(event) {
return core.opSync("op_dispatch_test_event", event);
}
async function runTests({ async function runTests({
disableLog = false, disableLog = false,
filter = null, filter = null,
shuffle = null, shuffle = null,
} = {}) { } = {}) {
const origin = getTestOrigin();
const originalConsole = globalThis.console; const originalConsole = globalThis.console;
if (disableLog) { if (disableLog) {
globalThis.console = new Console(() => {}); globalThis.console = new Console(() => {});
} }
const only = ArrayPrototypeFilter(tests, (test) => test.only); const only = ArrayPrototypeFilter(tests, (test) => test.only);
const pending = ArrayPrototypeFilter( const filtered = ArrayPrototypeFilter(
(only.length > 0 ? only : tests), (only.length > 0 ? only : tests),
createTestFilter(filter), createTestFilter(filter),
); );
postTestMessage("plan", {
filtered: tests.length - pending.length, dispatchTestEvent({
pending: pending.length, plan: {
only: only.length > 0, origin,
total: filtered.length,
filteredOut: tests.length - filtered.length,
usedOnly: only.length > 0,
},
}); });
if (shuffle !== null) { if (shuffle !== null) {
@ -256,31 +265,25 @@ finishing test case.`;
}; };
}(shuffle)); }(shuffle));
for (let i = pending.length - 1; i > 0; i--) { for (let i = filtered.length - 1; i > 0; i--) {
const j = nextInt(i); const j = nextInt(i);
[pending[i], pending[j]] = [pending[j], pending[i]]; [filtered[i], filtered[j]] = [filtered[j], filtered[i]];
} }
} }
for (const test of pending) { for (const test of filtered) {
const { const description = {
name, origin,
} = test; name: test.name,
};
const earlier = DateNow(); const earlier = DateNow();
postTestMessage("wait", { dispatchTestEvent({ wait: description });
name,
});
const result = await runTest(test); const result = await runTest(test);
const duration = DateNow() - earlier; const elapsed = DateNow() - earlier;
postTestMessage("result", { dispatchTestEvent({ result: [description, result, elapsed] });
name,
result,
duration,
});
} }
if (disableLog) { if (disableLog) {