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:
parent
6ce2a089a8
commit
69ca44d8e2
3 changed files with 146 additions and 168 deletions
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!(
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue