summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/lsp/testing/execution.rs7
-rw-r--r--cli/tools/test/channel.rs71
-rw-r--r--cli/tools/test/mod.rs79
-rw-r--r--cli/tools/test/reporters/compound.rs6
-rw-r--r--cli/tools/test/reporters/dot.rs2
-rw-r--r--cli/tools/test/reporters/junit.rs5
-rw-r--r--cli/tools/test/reporters/mod.rs1
-rw-r--r--cli/tools/test/reporters/pretty.rs24
-rw-r--r--cli/tools/test/reporters/tap.rs2
9 files changed, 180 insertions, 17 deletions
diff --git a/cli/lsp/testing/execution.rs b/cli/lsp/testing/execution.rs
index 78ee6c7f8..3c7921c88 100644
--- a/cli/lsp/testing/execution.rs
+++ b/cli/lsp/testing/execution.rs
@@ -397,6 +397,9 @@ impl TestRun {
);
}
}
+ test::TestEvent::Completed => {
+ reporter.report_completed();
+ }
test::TestEvent::ForceEndReport => {}
test::TestEvent::Sigint => {}
}
@@ -742,6 +745,10 @@ impl LspTestReporter {
}
}
+ fn report_completed(&mut self) {
+ // there is nothing to do on report_completed
+ }
+
fn report_summary(
&mut self,
_summary: &test::TestSummary,
diff --git a/cli/tools/test/channel.rs b/cli/tools/test/channel.rs
index 07fee6521..3784d58f9 100644
--- a/cli/tools/test/channel.rs
+++ b/cli/tools/test/channel.rs
@@ -384,6 +384,8 @@ impl TestEventSender {
#[cfg(test)]
mod tests {
+ use crate::tools::test::TestResult;
+
use super::*;
use deno_core::unsync::spawn;
use deno_core::unsync::spawn_blocking;
@@ -466,6 +468,75 @@ mod tests {
assert_eq!(messages.len(), 100000);
}
+ /// Test that large numbers of interleaved steps are routed properly.
+ #[tokio::test]
+ async fn test_interleave() {
+ test_util::timeout!(60);
+ const MESSAGE_COUNT: usize = 10_000;
+ let (mut worker, mut receiver) = create_single_test_event_channel();
+ let recv_handle = spawn(async move {
+ let mut i = 0;
+ while let Some((_, message)) = receiver.recv().await {
+ if i % 2 == 0 {
+ let expected_text = format!("{:08x}", i / 2).into_bytes();
+ let TestEvent::Output(TestStdioStream::Stderr, text) = message else {
+ panic!("Incorrect message: {message:?}");
+ };
+ assert_eq!(text, expected_text);
+ } else {
+ let TestEvent::Result(index, TestResult::Ok, 0) = message else {
+ panic!("Incorrect message: {message:?}");
+ };
+ assert_eq!(index, i / 2);
+ }
+ i += 1;
+ }
+ eprintln!("Receiver closed");
+ i
+ });
+ let send_handle: deno_core::unsync::JoinHandle<()> =
+ spawn_blocking(move || {
+ for i in 0..MESSAGE_COUNT {
+ worker
+ .stderr
+ .write_all(format!("{i:08x}").as_str().as_bytes())
+ .unwrap();
+ worker
+ .sender
+ .send(TestEvent::Result(i, TestResult::Ok, 0))
+ .unwrap();
+ }
+ eprintln!("Sent all messages");
+ });
+ send_handle.await.unwrap();
+ let messages = recv_handle.await.unwrap();
+ assert_eq!(messages, MESSAGE_COUNT * 2);
+ }
+
+ #[tokio::test]
+ async fn test_sender_shutdown_before_receive() {
+ test_util::timeout!(60);
+ for _ in 0..10 {
+ let (mut worker, mut receiver) = create_single_test_event_channel();
+ worker.stderr.write_all(b"hello").unwrap();
+ worker
+ .sender
+ .send(TestEvent::Result(0, TestResult::Ok, 0))
+ .unwrap();
+ drop(worker);
+ let (_, message) = receiver.recv().await.unwrap();
+ let TestEvent::Output(TestStdioStream::Stderr, text) = message else {
+ panic!("Incorrect message: {message:?}");
+ };
+ assert_eq!(text.as_slice(), b"hello");
+ let (_, message) = receiver.recv().await.unwrap();
+ let TestEvent::Result(..) = message else {
+ panic!("Incorrect message: {message:?}");
+ };
+ assert!(receiver.recv().await.is_none());
+ }
+ }
+
/// Ensure nothing panics if we're racing the runtime shutdown.
#[test]
fn test_runtime_shutdown() {
diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs
index 13c1c3ed6..4ec90050e 100644
--- a/cli/tools/test/mod.rs
+++ b/cli/tools/test/mod.rs
@@ -391,8 +391,14 @@ pub enum TestEvent {
StepRegister(TestStepDescription),
StepWait(usize),
StepResult(usize, TestStepResult, u64),
- ForceEndReport,
+ /// Indicates that this worker has completed.
+ Completed,
+ /// Indicates that the user has cancelled the test run with Ctrl+C and
+ /// the run should be aborted.
Sigint,
+ /// Used by the REPL to force a report to end without closing the worker
+ /// or receiver.
+ ForceEndReport,
}
impl TestEvent {
@@ -401,11 +407,13 @@ impl TestEvent {
pub fn requires_stdio_sync(&self) -> bool {
matches!(
self,
- TestEvent::Result(..)
+ TestEvent::Plan(..)
+ | TestEvent::Result(..)
| TestEvent::StepWait(..)
| TestEvent::StepResult(..)
| TestEvent::UncaughtError(..)
| TestEvent::ForceEndReport
+ | TestEvent::Completed
)
}
}
@@ -641,24 +649,64 @@ pub async fn run_tests_for_worker(
};
let tests: Arc<TestDescriptions> = tests.into();
sender.send(TestEvent::Register(tests.clone()))?;
+ let res = run_tests_for_worker_inner(
+ worker,
+ specifier,
+ tests,
+ test_functions,
+ &mut sender,
+ options,
+ fail_fast_tracker,
+ )
+ .await;
+ _ = sender.send(TestEvent::Completed);
+ res
+}
+
+async fn run_tests_for_worker_inner(
+ worker: &mut MainWorker,
+ specifier: &ModuleSpecifier,
+ tests: Arc<TestDescriptions>,
+ test_functions: Vec<v8::Global<v8::Function>>,
+ sender: &mut TestEventSender,
+ options: &TestSpecifierOptions,
+ fail_fast_tracker: &FailFastTracker,
+) -> Result<(), AnyError> {
let unfiltered = tests.len();
- let tests = tests
- .into_iter()
- .filter(|(_, d)| options.filter.includes(&d.name))
- .collect::<Vec<_>>();
- let (only, no_only): (Vec<_>, Vec<_>) =
- tests.into_iter().partition(|(_, d)| d.only);
- let used_only = !only.is_empty();
- let mut tests = if used_only { only } else { no_only };
+
+ // Build the test plan in a single pass
+ let mut tests_to_run = Vec::with_capacity(tests.len());
+ let mut used_only = false;
+ for ((_, d), f) in tests.tests.iter().zip(test_functions) {
+ if !options.filter.includes(&d.name) {
+ continue;
+ }
+
+ // If we've seen an "only: true" test, the remaining tests must be "only: true" to be added
+ if used_only && !d.only {
+ continue;
+ }
+
+ // If this is the first "only: true" test we've seen, clear the other tests since they were
+ // only: false.
+ if d.only && !used_only {
+ used_only = true;
+ tests_to_run.clear();
+ }
+ tests_to_run.push((d, f));
+ }
+
if let Some(seed) = options.shuffle {
- tests.shuffle(&mut SmallRng::seed_from_u64(seed));
+ tests_to_run.shuffle(&mut SmallRng::seed_from_u64(seed));
}
+
sender.send(TestEvent::Plan(TestPlan {
origin: specifier.to_string(),
- total: tests.len(),
- filtered_out: unfiltered - tests.len(),
+ total: tests_to_run.len(),
+ filtered_out: unfiltered - tests_to_run.len(),
used_only,
}))?;
+
let mut had_uncaught_error = false;
let stats = worker.js_runtime.runtime_activity_stats_factory();
let ops = worker.js_runtime.op_names();
@@ -683,7 +731,7 @@ pub async fn run_tests_for_worker(
filter = filter.omit_op(op_id_host_recv_ctrl as _);
filter = filter.omit_op(op_id_host_recv_message as _);
- for ((_, desc), function) in tests.into_iter().zip(test_functions) {
+ for (desc, function) in tests_to_run.into_iter() {
if fail_fast_tracker.should_stop() {
break;
}
@@ -1254,6 +1302,9 @@ pub async fn report_tests(
TestEvent::ForceEndReport => {
break;
}
+ TestEvent::Completed => {
+ reporter.report_completed();
+ }
TestEvent::Sigint => {
let elapsed = start_time
.map(|t| Instant::now().duration_since(t))
diff --git a/cli/tools/test/reporters/compound.rs b/cli/tools/test/reporters/compound.rs
index c50018467..1af0b284b 100644
--- a/cli/tools/test/reporters/compound.rs
+++ b/cli/tools/test/reporters/compound.rs
@@ -101,6 +101,12 @@ impl TestReporter for CompoundTestReporter {
}
}
+ fn report_completed(&mut self) {
+ for reporter in &mut self.test_reporters {
+ reporter.report_completed();
+ }
+ }
+
fn flush_report(
&mut self,
elapsed: &Duration,
diff --git a/cli/tools/test/reporters/dot.rs b/cli/tools/test/reporters/dot.rs
index 39a4e44d9..395b5f0b6 100644
--- a/cli/tools/test/reporters/dot.rs
+++ b/cli/tools/test/reporters/dot.rs
@@ -206,6 +206,8 @@ impl TestReporter for DotTestReporter {
);
}
+ fn report_completed(&mut self) {}
+
fn flush_report(
&mut self,
_elapsed: &Duration,
diff --git a/cli/tools/test/reporters/junit.rs b/cli/tools/test/reporters/junit.rs
index 9f82b9239..464f47b8d 100644
--- a/cli/tools/test/reporters/junit.rs
+++ b/cli/tools/test/reporters/junit.rs
@@ -158,6 +158,11 @@ impl TestReporter for JunitTestReporter {
}
}
+ fn report_completed(&mut self) {
+ // TODO(mmastrac): This reporter does not handle stdout/stderr yet, and when we do, we may need to redirect
+ // pre-and-post-test output somewhere.
+ }
+
fn flush_report(
&mut self,
elapsed: &Duration,
diff --git a/cli/tools/test/reporters/mod.rs b/cli/tools/test/reporters/mod.rs
index 6eaed24f1..a152029c4 100644
--- a/cli/tools/test/reporters/mod.rs
+++ b/cli/tools/test/reporters/mod.rs
@@ -49,6 +49,7 @@ pub trait TestReporter {
tests: &IndexMap<usize, TestDescription>,
test_steps: &IndexMap<usize, TestStepDescription>,
);
+ fn report_completed(&mut self);
fn flush_report(
&mut self,
elapsed: &Duration,
diff --git a/cli/tools/test/reporters/pretty.rs b/cli/tools/test/reporters/pretty.rs
index 4e8a1f402..1cd1f084f 100644
--- a/cli/tools/test/reporters/pretty.rs
+++ b/cli/tools/test/reporters/pretty.rs
@@ -8,12 +8,14 @@ pub struct PrettyTestReporter {
parallel: bool,
echo_output: bool,
in_new_line: bool,
+ phase: &'static str,
filter: bool,
repl: bool,
scope_test_id: Option<usize>,
cwd: Url,
did_have_user_output: bool,
started_tests: bool,
+ ended_tests: bool,
child_results_buffer:
HashMap<usize, IndexMap<usize, (TestStepDescription, TestStepResult, u64)>>,
summary: TestSummary,
@@ -31,12 +33,14 @@ impl PrettyTestReporter {
parallel,
echo_output,
in_new_line: true,
+ phase: "",
filter,
repl,
scope_test_id: None,
cwd: Url::from_directory_path(std::env::current_dir().unwrap()).unwrap(),
did_have_user_output: false,
started_tests: false,
+ ended_tests: false,
child_results_buffer: Default::default(),
summary: TestSummary::new(),
writer: Box::new(std::io::stdout()),
@@ -149,7 +153,7 @@ impl PrettyTestReporter {
writeln!(
&mut self.writer,
"{}",
- colors::gray("----- output end -----")
+ colors::gray(format!("----- {}output end -----", self.phase))
)
.unwrap();
self.in_new_line = true;
@@ -161,6 +165,7 @@ impl PrettyTestReporter {
impl TestReporter for PrettyTestReporter {
fn report_register(&mut self, _description: &TestDescription) {}
fn report_plan(&mut self, plan: &TestPlan) {
+ self.write_output_end();
self.summary.total += plan.total;
self.summary.filtered_out += plan.filtered_out;
if self.repl {
@@ -196,15 +201,22 @@ impl TestReporter for PrettyTestReporter {
return;
}
- if !self.did_have_user_output && self.started_tests {
+ if !self.did_have_user_output {
self.did_have_user_output = true;
if !self.in_new_line {
writeln!(&mut self.writer).unwrap();
}
+ self.phase = if !self.started_tests {
+ "pre-test "
+ } else if self.ended_tests {
+ "post-test "
+ } else {
+ ""
+ };
writeln!(
&mut self.writer,
"{}",
- colors::gray("------- output -------")
+ colors::gray(format!("------- {}output -------", self.phase))
)
.unwrap();
self.in_new_line = true;
@@ -369,6 +381,7 @@ impl TestReporter for PrettyTestReporter {
_tests: &IndexMap<usize, TestDescription>,
_test_steps: &IndexMap<usize, TestStepDescription>,
) {
+ self.write_output_end();
common::report_summary(&mut self.writer, &self.cwd, &self.summary, elapsed);
if !self.repl {
writeln!(&mut self.writer).unwrap();
@@ -392,6 +405,11 @@ impl TestReporter for PrettyTestReporter {
self.in_new_line = true;
}
+ fn report_completed(&mut self) {
+ self.write_output_end();
+ self.ended_tests = true;
+ }
+
fn flush_report(
&mut self,
_elapsed: &Duration,
diff --git a/cli/tools/test/reporters/tap.rs b/cli/tools/test/reporters/tap.rs
index a67e592a2..610f0bec9 100644
--- a/cli/tools/test/reporters/tap.rs
+++ b/cli/tools/test/reporters/tap.rs
@@ -236,6 +236,8 @@ impl TestReporter for TapTestReporter {
);
}
+ fn report_completed(&mut self) {}
+
fn flush_report(
&mut self,
_elapsed: &Duration,