summaryrefslogtreecommitdiff
path: root/cli/lsp/testing
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp/testing')
-rw-r--r--cli/lsp/testing/collectors.rs54
-rw-r--r--cli/lsp/testing/definitions.rs70
-rw-r--r--cli/lsp/testing/execution.rs387
-rw-r--r--cli/lsp/testing/lsp_custom.rs10
4 files changed, 229 insertions, 292 deletions
diff --git a/cli/lsp/testing/collectors.rs b/cli/lsp/testing/collectors.rs
index 537dd5806..d338ac088 100644
--- a/cli/lsp/testing/collectors.rs
+++ b/cli/lsp/testing/collectors.rs
@@ -15,7 +15,7 @@ fn arrow_to_steps(
parent: &str,
level: usize,
arrow_expr: &ast::ArrowExpr,
-) -> Option<Vec<TestDefinition>> {
+) -> Vec<TestDefinition> {
if let Some((maybe_test_context, maybe_step_var)) =
parse_test_context_param(arrow_expr.params.get(0))
{
@@ -26,14 +26,9 @@ fn arrow_to_steps(
maybe_step_var,
);
arrow_expr.body.visit_with(&mut collector);
- let steps = collector.take();
- if !steps.is_empty() {
- Some(steps)
- } else {
- None
- }
+ collector.take()
} else {
- None
+ vec![]
}
}
@@ -42,7 +37,7 @@ fn fn_to_steps(
parent: &str,
level: usize,
function: &ast::Function,
-) -> Option<Vec<TestDefinition>> {
+) -> Vec<TestDefinition> {
if let Some((maybe_test_context, maybe_step_var)) =
parse_test_context_param(function.params.get(0).map(|p| &p.pat))
{
@@ -53,14 +48,9 @@ fn fn_to_steps(
maybe_step_var,
);
function.body.visit_with(&mut collector);
- let steps = collector.take();
- if !steps.is_empty() {
- Some(steps)
- } else {
- None
- }
+ collector.take()
} else {
- None
+ vec![]
}
}
@@ -139,12 +129,12 @@ fn check_call_expr(
parent: &str,
node: &ast::CallExpr,
level: usize,
-) -> Option<(String, Option<Vec<TestDefinition>>)> {
+) -> Option<(String, Vec<TestDefinition>)> {
if let Some(expr) = node.args.get(0).map(|es| es.expr.as_ref()) {
match expr {
ast::Expr::Object(obj_lit) => {
let mut maybe_name = None;
- let mut steps = None;
+ let mut steps = vec![];
for prop in &obj_lit.props {
if let ast::PropOrSpread::Prop(prop) = prop {
match prop.as_ref() {
@@ -203,7 +193,7 @@ fn check_call_expr(
}
ast::Expr::Lit(ast::Lit::Str(lit_str)) => {
let name = lit_str.value.to_string();
- let mut steps = None;
+ let mut steps = vec![];
match node.args.get(1).map(|es| es.expr.as_ref()) {
Some(ast::Expr::Fn(fn_expr)) => {
steps = fn_to_steps(parent, level, &fn_expr.function);
@@ -256,7 +246,7 @@ impl TestStepCollector {
&mut self,
name: N,
range: SourceRange,
- steps: Option<Vec<TestDefinition>>,
+ steps: Vec<TestDefinition>,
) {
let step = TestDefinition::new_step(
name.as_ref().to_string(),
@@ -388,7 +378,7 @@ impl TestCollector {
&mut self,
name: N,
range: SourceRange,
- steps: Option<Vec<TestDefinition>>,
+ steps: Vec<TestDefinition>,
) {
let definition = TestDefinition::new(
&self.specifier,
@@ -553,59 +543,59 @@ pub mod tests {
level: 0,
name: "test a".to_string(),
range: new_range(12, 16),
- steps: Some(vec![
+ steps: vec![
TestDefinition {
id: "4c7333a1e47721631224408c467f32751fe34b876cab5ec1f6ac71980ff15ad3".to_string(),
level: 1,
name: "a step".to_string(),
range: new_range(83, 87),
- steps: Some(vec![
+ steps: vec![
TestDefinition {
id: "abf356f59139b77574089615f896a6f501c010985d95b8a93abeb0069ccb2201".to_string(),
level: 2,
name: "sub step".to_string(),
range: new_range(132, 136),
- steps: None,
+ steps: vec![],
}
- ])
+ ]
}
- ]),
+ ],
},
TestDefinition {
id: "86b4c821900e38fc89f24bceb0e45193608ab3f9d2a6019c7b6a5aceff5d7df2".to_string(),
level: 0,
name: "useFnName".to_string(),
range: new_range(254, 258),
- steps: Some(vec![
+ steps: vec![
TestDefinition {
id: "67a390d0084ae5fb88f3510c470a72a553581f1d0d5ba5fa89aee7a754f3953a".to_string(),
level: 1,
name: "step c".to_string(),
range: new_range(313, 314),
- steps: None,
+ steps: vec![],
}
- ])
+ ]
},
TestDefinition {
id: "580eda89d7f5e619774c20e13b7d07a8e77c39cba101d60565144d48faa837cb".to_string(),
level: 0,
name: "test b".to_string(),
range: new_range(358, 362),
- steps: None,
+ steps: vec![],
},
TestDefinition {
id: "0b7c6bf3cd617018d33a1bf982a08fe088c5bb54fcd5eb9e802e7c137ec1af94".to_string(),
level: 0,
name: "test c".to_string(),
range: new_range(420, 424),
- steps: None,
+ steps: vec![],
},
TestDefinition {
id: "69d9fe87f64f5b66cb8b631d4fd2064e8224b8715a049be54276c42189ff8f9f".to_string(),
level: 0,
name: "test d".to_string(),
range: new_range(480, 481),
- steps: None,
+ steps: vec![],
}
]
);
diff --git a/cli/lsp/testing/definitions.rs b/cli/lsp/testing/definitions.rs
index c810b6a25..14ac165fd 100644
--- a/cli/lsp/testing/definitions.rs
+++ b/cli/lsp/testing/definitions.rs
@@ -18,7 +18,7 @@ pub struct TestDefinition {
pub level: usize,
pub name: String,
pub range: SourceRange,
- pub steps: Option<Vec<TestDefinition>>,
+ pub steps: Vec<TestDefinition>,
}
impl TestDefinition {
@@ -26,7 +26,7 @@ impl TestDefinition {
specifier: &ModuleSpecifier,
name: String,
range: SourceRange,
- steps: Option<Vec<TestDefinition>>,
+ steps: Vec<TestDefinition>,
) -> Self {
let id = checksum::gen(&[specifier.as_str().as_bytes(), name.as_bytes()]);
Self {
@@ -43,7 +43,7 @@ impl TestDefinition {
range: SourceRange,
parent: String,
level: usize,
- steps: Option<Vec<TestDefinition>>,
+ steps: Vec<TestDefinition>,
) -> Self {
let id = checksum::gen(&[
parent.as_bytes(),
@@ -66,27 +66,18 @@ impl TestDefinition {
lsp_custom::TestData {
id: self.id.clone(),
label: self.name.clone(),
- steps: self.steps.as_ref().map(|steps| {
- steps
- .iter()
- .map(|step| step.as_test_data(source_text_info))
- .collect()
- }),
+ steps: self
+ .steps
+ .iter()
+ .map(|step| step.as_test_data(source_text_info))
+ .collect(),
range: Some(source_range_to_lsp_range(&self.range, source_text_info)),
}
}
- fn find_step(&self, name: &str, level: usize) -> Option<&TestDefinition> {
- if let Some(steps) = &self.steps {
- for step in steps {
- if step.name == name && step.level == level {
- return Some(step);
- } else if let Some(step) = step.find_step(name, level) {
- return Some(step);
- }
- }
- }
- None
+ fn contains_id<S: AsRef<str>>(&self, id: S) -> bool {
+ let id = id.as_ref();
+ self.id == id || self.steps.iter().any(|td| td.contains_id(id))
}
}
@@ -102,6 +93,16 @@ pub struct TestDefinitions {
pub script_version: String,
}
+impl Default for TestDefinitions {
+ fn default() -> Self {
+ TestDefinitions {
+ script_version: "1".to_string(),
+ discovered: vec![],
+ injected: vec![],
+ }
+ }
+}
+
impl TestDefinitions {
/// Return the test definitions as a testing module notification.
pub fn as_notification(
@@ -137,6 +138,19 @@ impl TestDefinitions {
})
}
+ /// Register a dynamically-detected test. Returns false if a test with the
+ /// same static id was already registered statically or dynamically. Otherwise
+ /// returns true.
+ pub fn inject(&mut self, data: lsp_custom::TestData) -> bool {
+ if self.discovered.iter().any(|td| td.contains_id(&data.id))
+ || self.injected.iter().any(|td| td.id == data.id)
+ {
+ return false;
+ }
+ self.injected.push(data);
+ true
+ }
+
/// Return a test definition identified by the test ID.
pub fn get_by_id<S: AsRef<str>>(&self, id: S) -> Option<&TestDefinition> {
self
@@ -144,20 +158,4 @@ impl TestDefinitions {
.iter()
.find(|td| td.id.as_str() == id.as_ref())
}
-
- /// Return a test definition by the test name.
- pub fn get_by_name(&self, name: &str) -> Option<&TestDefinition> {
- self.discovered.iter().find(|td| td.name.as_str() == name)
- }
-
- pub fn get_step_by_name(
- &self,
- test_name: &str,
- level: usize,
- name: &str,
- ) -> Option<&TestDefinition> {
- self
- .get_by_name(test_name)
- .and_then(|td| td.find_step(name, level))
- }
}
diff --git a/cli/lsp/testing/execution.rs b/cli/lsp/testing/execution.rs
index d5c0f6278..7c0552a0a 100644
--- a/cli/lsp/testing/execution.rs
+++ b/cli/lsp/testing/execution.rs
@@ -25,13 +25,13 @@ use deno_core::futures::future;
use deno_core::futures::stream;
use deno_core::futures::StreamExt;
use deno_core::parking_lot::Mutex;
-use deno_core::serde_json::json;
-use deno_core::serde_json::Value;
+use deno_core::parking_lot::RwLock;
use deno_core::ModuleSpecifier;
use deno_runtime::ops::io::Stdio;
use deno_runtime::ops::io::StdioPipe;
use deno_runtime::permissions::Permissions;
use deno_runtime::tokio_util::run_local;
+use indexmap::IndexMap;
use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::Arc;
@@ -48,10 +48,10 @@ fn as_queue_and_filters(
tests: &HashMap<ModuleSpecifier, TestDefinitions>,
) -> (
HashSet<ModuleSpecifier>,
- HashMap<ModuleSpecifier, TestFilter>,
+ HashMap<ModuleSpecifier, LspTestFilter>,
) {
let mut queue: HashSet<ModuleSpecifier> = HashSet::new();
- let mut filters: HashMap<ModuleSpecifier, TestFilter> = HashMap::new();
+ let mut filters: HashMap<ModuleSpecifier, LspTestFilter> = HashMap::new();
if let Some(include) = &params.include {
for item in include {
@@ -61,12 +61,12 @@ fn as_queue_and_filters(
if let Some(test) = test_definitions.get_by_id(id) {
let filter =
filters.entry(item.text_document.uri.clone()).or_default();
- if let Some(include) = filter.maybe_include.as_mut() {
+ if let Some(include) = filter.include.as_mut() {
include.insert(test.id.clone(), test.clone());
} else {
let mut include = HashMap::new();
include.insert(test.id.clone(), test.clone());
- filter.maybe_include = Some(include);
+ filter.include = Some(include);
}
}
}
@@ -80,29 +80,20 @@ fn as_queue_and_filters(
queue.extend(tests.keys().cloned());
}
- if let Some(exclude) = &params.exclude {
- for item in exclude {
- if let Some(test_definitions) = tests.get(&item.text_document.uri) {
- if let Some(id) = &item.id {
- // there is currently no way to filter out a specific test, so we have
- // to ignore the exclusion
- if item.step_id.is_none() {
- if let Some(test) = test_definitions.get_by_id(id) {
- let filter =
- filters.entry(item.text_document.uri.clone()).or_default();
- if let Some(exclude) = filter.maybe_exclude.as_mut() {
- exclude.insert(test.id.clone(), test.clone());
- } else {
- let mut exclude = HashMap::new();
- exclude.insert(test.id.clone(), test.clone());
- filter.maybe_exclude = Some(exclude);
- }
- }
+ for item in &params.exclude {
+ if let Some(test_definitions) = tests.get(&item.text_document.uri) {
+ if let Some(id) = &item.id {
+ // there is no way to exclude a test step
+ if item.step_id.is_none() {
+ if let Some(test) = test_definitions.get_by_id(id) {
+ let filter =
+ filters.entry(item.text_document.uri.clone()).or_default();
+ filter.exclude.insert(test.id.clone(), test.clone());
}
- } else {
- // the entire test module is excluded
- queue.remove(&item.text_document.uri);
}
+ } else {
+ // the entire test module is excluded
+ queue.remove(&item.text_document.uri);
}
}
}
@@ -131,14 +122,14 @@ fn as_test_messages<S: AsRef<str>>(
}
#[derive(Debug, Clone, Default, PartialEq)]
-struct TestFilter {
- maybe_include: Option<HashMap<String, TestDefinition>>,
- maybe_exclude: Option<HashMap<String, TestDefinition>>,
+struct LspTestFilter {
+ include: Option<HashMap<String, TestDefinition>>,
+ exclude: HashMap<String, TestDefinition>,
}
-impl TestFilter {
+impl LspTestFilter {
fn as_ids(&self, test_definitions: &TestDefinitions) -> Vec<String> {
- let ids: Vec<String> = if let Some(include) = &self.maybe_include {
+ let ids: Vec<String> = if let Some(include) = &self.include {
include.keys().cloned().collect()
} else {
test_definitions
@@ -147,33 +138,10 @@ impl TestFilter {
.map(|td| td.id.clone())
.collect()
};
- if let Some(exclude) = &self.maybe_exclude {
- ids
- .into_iter()
- .filter(|id| !exclude.contains_key(id))
- .collect()
- } else {
- ids
- }
- }
-
- /// return the filter as a JSON value, suitable for sending as a filter to the
- /// test runner.
- fn as_test_options(&self) -> Value {
- let maybe_include: Option<Vec<String>> = self
- .maybe_include
- .as_ref()
- .map(|inc| inc.iter().map(|(_, td)| td.name.clone()).collect());
- let maybe_exclude: Option<Vec<String>> = self
- .maybe_exclude
- .as_ref()
- .map(|ex| ex.iter().map(|(_, td)| td.name.clone()).collect());
- json!({
- "filter": {
- "include": maybe_include,
- "exclude": maybe_exclude,
- }
- })
+ ids
+ .into_iter()
+ .filter(|id| !self.exclude.contains_key(id))
+ .collect()
}
}
@@ -184,14 +152,14 @@ async fn test_specifier(
mode: test::TestMode,
sender: &TestEventSender,
token: CancellationToken,
- options: Option<Value>,
+ filter: test::TestFilter,
) -> Result<(), AnyError> {
if !token.is_cancelled() {
let mut worker = create_main_worker(
&ps,
specifier.clone(),
permissions,
- vec![ops::testing::init(sender.clone())],
+ vec![ops::testing::init(sender.clone(), filter)],
Stdio {
stdin: StdioPipe::Inherit,
stdout: StdioPipe::File(sender.stdout()),
@@ -217,10 +185,9 @@ async fn test_specifier(
worker.dispatch_load_event(&located_script_name!())?;
- let options = options.unwrap_or_else(|| json!({}));
let test_result = worker.js_runtime.execute_script(
&located_script_name!(),
- &format!(r#"Deno[Deno.internal].runTests({})"#, json!(options)),
+ r#"Deno[Deno.internal].runTests()"#,
)?;
worker.js_runtime.resolve_value(test_result).await?;
@@ -241,7 +208,7 @@ async fn test_specifier(
pub struct TestRun {
id: u32,
kind: lsp_custom::TestRunKind,
- filters: HashMap<ModuleSpecifier, TestFilter>,
+ filters: HashMap<ModuleSpecifier, LspTestFilter>,
queue: HashSet<ModuleSpecifier>,
tests: Arc<Mutex<HashMap<ModuleSpecifier, TestDefinitions>>>,
token: CancellationToken,
@@ -343,13 +310,31 @@ impl TestRun {
let mut queue = self.queue.iter().collect::<Vec<&ModuleSpecifier>>();
queue.sort();
+ let tests: Arc<RwLock<IndexMap<usize, test::TestDescription>>> =
+ Arc::new(RwLock::new(IndexMap::new()));
+ let mut test_steps = IndexMap::new();
+
+ let tests_ = tests.clone();
let join_handles = queue.into_iter().map(move |specifier| {
let specifier = specifier.clone();
let ps = ps.clone();
let permissions = permissions.clone();
let mut sender = sender.clone();
- let options = self.filters.get(&specifier).map(|f| f.as_test_options());
+ let lsp_filter = self.filters.get(&specifier);
+ let filter = test::TestFilter {
+ substring: None,
+ regex: None,
+ include: lsp_filter.and_then(|f| {
+ f.include
+ .as_ref()
+ .map(|i| i.values().map(|t| t.name.clone()).collect())
+ }),
+ exclude: lsp_filter
+ .map(|f| f.exclude.values().map(|t| t.name.clone()).collect())
+ .unwrap_or_default(),
+ };
let token = self.token.clone();
+ let tests = tests_.clone();
tokio::task::spawn_blocking(move || {
let origin = specifier.to_string();
@@ -360,14 +345,23 @@ impl TestRun {
test::TestMode::Executable,
&sender,
token,
- options,
+ filter,
));
if let Err(error) = file_result {
if error.is::<JsError>() {
sender.send(test::TestEvent::UncaughtError(
- origin,
+ origin.clone(),
Box::new(error.downcast::<JsError>().unwrap()),
))?;
+ for desc in tests.read().values() {
+ if desc.origin == origin {
+ sender.send(test::TestEvent::Result(
+ desc.id,
+ test::TestResult::Cancelled,
+ 0,
+ ))?
+ }
+ }
} else {
return Err(error);
}
@@ -396,6 +390,10 @@ impl TestRun {
while let Some(event) = receiver.recv().await {
match event {
+ test::TestEvent::Register(description) => {
+ reporter.report_register(&description);
+ tests.write().insert(description.id, description);
+ }
test::TestEvent::Plan(plan) => {
summary.total += plan.total;
summary.filtered_out += plan.filtered_out;
@@ -406,13 +404,14 @@ impl TestRun {
reporter.report_plan(&plan);
}
- test::TestEvent::Wait(description) => {
- reporter.report_wait(&description);
+ test::TestEvent::Wait(id) => {
+ reporter.report_wait(tests.read().get(&id).unwrap());
}
test::TestEvent::Output(output) => {
reporter.report_output(&output);
}
- test::TestEvent::Result(description, result, elapsed) => {
+ test::TestEvent::Result(id, result, elapsed) => {
+ let description = tests.read().get(&id).unwrap().clone();
match &result {
test::TestResult::Ok => summary.passed += 1,
test::TestResult::Ignored => summary.ignored += 1,
@@ -420,6 +419,9 @@ impl TestRun {
summary.failed += 1;
summary.failures.push((description.clone(), error.clone()));
}
+ test::TestResult::Cancelled => {
+ summary.failed += 1;
+ }
}
reporter.report_result(&description, &result, elapsed);
@@ -429,10 +431,14 @@ impl TestRun {
summary.failed += 1;
summary.uncaught_errors.push((origin, error));
}
- test::TestEvent::StepWait(description) => {
- reporter.report_step_wait(&description);
+ test::TestEvent::StepRegister(description) => {
+ reporter.report_step_register(&description);
+ test_steps.insert(description.id, description);
}
- test::TestEvent::StepResult(description, result, duration) => {
+ test::TestEvent::StepWait(id) => {
+ reporter.report_step_wait(test_steps.get(&id).unwrap());
+ }
+ test::TestEvent::StepResult(id, result, duration) => {
match &result {
test::TestStepResult::Ok => {
summary.passed_steps += 1;
@@ -447,7 +453,11 @@ impl TestRun {
summary.pending_steps += 1;
}
}
- reporter.report_step_result(&description, &result, duration);
+ reporter.report_step_result(
+ test_steps.get(&id).unwrap(),
+ &result,
+ duration,
+ );
}
}
@@ -562,10 +572,8 @@ impl From<&TestOrTestStepDescription> for lsp_custom::TestData {
impl From<&test::TestDescription> for lsp_custom::TestData {
fn from(desc: &test::TestDescription) -> Self {
- let id = checksum::gen(&[desc.origin.as_bytes(), desc.name.as_bytes()]);
-
Self {
- id,
+ id: desc.static_id(),
label: desc.name.clone(),
steps: Default::default(),
range: None,
@@ -576,14 +584,9 @@ impl From<&test::TestDescription> for lsp_custom::TestData {
impl From<&test::TestDescription> for lsp_custom::TestIdentifier {
fn from(desc: &test::TestDescription) -> Self {
let uri = ModuleSpecifier::parse(&desc.origin).unwrap();
- let id = Some(checksum::gen(&[
- desc.origin.as_bytes(),
- desc.name.as_bytes(),
- ]));
-
Self {
text_document: lsp::TextDocumentIdentifier { uri },
- id,
+ id: Some(desc.static_id()),
step_id: None,
}
}
@@ -591,14 +594,8 @@ impl From<&test::TestDescription> for lsp_custom::TestIdentifier {
impl From<&test::TestStepDescription> for lsp_custom::TestData {
fn from(desc: &test::TestStepDescription) -> Self {
- let id = checksum::gen(&[
- desc.test.origin.as_bytes(),
- &desc.level.to_be_bytes(),
- desc.name.as_bytes(),
- ]);
-
Self {
- id,
+ id: desc.static_id(),
label: desc.name.clone(),
steps: Default::default(),
range: None,
@@ -608,21 +605,14 @@ impl From<&test::TestStepDescription> for lsp_custom::TestData {
impl From<&test::TestStepDescription> for lsp_custom::TestIdentifier {
fn from(desc: &test::TestStepDescription) -> Self {
- let uri = ModuleSpecifier::parse(&desc.test.origin).unwrap();
- let id = Some(checksum::gen(&[
- desc.test.origin.as_bytes(),
- desc.test.name.as_bytes(),
- ]));
- let step_id = Some(checksum::gen(&[
- desc.test.origin.as_bytes(),
- &desc.level.to_be_bytes(),
- desc.name.as_bytes(),
- ]));
-
+ let uri = ModuleSpecifier::parse(&desc.origin).unwrap();
Self {
text_document: lsp::TextDocumentIdentifier { uri },
- id,
- step_id,
+ id: Some(checksum::gen(&[
+ desc.origin.as_bytes(),
+ desc.root_name.as_bytes(),
+ ])),
+ step_id: Some(desc.static_id()),
}
}
}
@@ -653,61 +643,28 @@ impl LspTestReporter {
}
}
- fn add_step(&self, desc: &test::TestStepDescription) {
- if let Ok(specifier) = ModuleSpecifier::parse(&desc.test.origin) {
- let mut tests = self.tests.lock();
- let entry =
- tests
- .entry(specifier.clone())
- .or_insert_with(|| TestDefinitions {
- discovered: Default::default(),
- injected: Default::default(),
- script_version: "1".to_string(),
- });
- let mut prev: lsp_custom::TestData = desc.into();
- if let Some(stack) = self.stack.get(&desc.test.origin) {
- for item in stack.iter().rev() {
- let mut data: lsp_custom::TestData = item.into();
- data.steps = Some(vec![prev]);
- prev = data;
- }
- entry.injected.push(prev.clone());
- let label = if let Some(root) = &self.maybe_root_uri {
- specifier.as_str().replace(root.as_str(), "")
- } else {
- specifier
- .path_segments()
- .and_then(|s| s.last().map(|s| s.to_string()))
- .unwrap_or_else(|| "<unknown>".to_string())
- };
- self
- .client
- .send_test_notification(TestingNotification::Module(
- lsp_custom::TestModuleNotificationParams {
- text_document: lsp::TextDocumentIdentifier { uri: specifier },
- kind: lsp_custom::TestModuleNotificationKind::Insert,
- label,
- tests: vec![prev],
- },
- ));
- }
- }
+ fn progress(&self, message: lsp_custom::TestRunProgressMessage) {
+ self
+ .client
+ .send_test_notification(TestingNotification::Progress(
+ lsp_custom::TestRunProgressParams {
+ id: self.id,
+ message,
+ },
+ ));
}
+}
- /// Add a test which is being reported from the test runner but was not
- /// statically identified
- fn add_test(&self, desc: &test::TestDescription) {
- if let Ok(specifier) = ModuleSpecifier::parse(&desc.origin) {
- let mut tests = self.tests.lock();
- let entry =
- tests
- .entry(specifier.clone())
- .or_insert_with(|| TestDefinitions {
- discovered: Default::default(),
- injected: Default::default(),
- script_version: "1".to_string(),
- });
- entry.injected.push(desc.into());
+impl test::TestReporter for LspTestReporter {
+ fn report_plan(&mut self, _plan: &test::TestPlan) {}
+
+ fn report_register(&mut self, desc: &test::TestDescription) {
+ let mut tests = self.tests.lock();
+ let tds = tests
+ .entry(ModuleSpecifier::parse(&desc.location.file_name).unwrap())
+ .or_default();
+ if tds.inject(desc.into()) {
+ let specifier = ModuleSpecifier::parse(&desc.origin).unwrap();
let label = if let Some(root) = &self.maybe_root_uri {
specifier.as_str().replace(root.as_str(), "")
} else {
@@ -729,49 +686,7 @@ impl LspTestReporter {
}
}
- fn progress(&self, message: lsp_custom::TestRunProgressMessage) {
- self
- .client
- .send_test_notification(TestingNotification::Progress(
- lsp_custom::TestRunProgressParams {
- id: self.id,
- message,
- },
- ));
- }
-
- fn includes_step(&self, desc: &test::TestStepDescription) -> bool {
- if let Ok(specifier) = ModuleSpecifier::parse(&desc.test.origin) {
- let tests = self.tests.lock();
- if let Some(test_definitions) = tests.get(&specifier) {
- return test_definitions
- .get_step_by_name(&desc.test.name, desc.level, &desc.name)
- .is_some();
- }
- }
- false
- }
-
- fn includes_test(&self, desc: &test::TestDescription) -> bool {
- if let Ok(specifier) = ModuleSpecifier::parse(&desc.origin) {
- let tests = self.tests.lock();
- if let Some(test_definitions) = tests.get(&specifier) {
- return test_definitions.get_by_name(&desc.name).is_some();
- }
- }
- false
- }
-}
-
-impl test::TestReporter for LspTestReporter {
- fn report_plan(&mut self, _plan: &test::TestPlan) {
- // there is nothing to do on report_plan
- }
-
fn report_wait(&mut self, desc: &test::TestDescription) {
- if !self.includes_test(desc) {
- self.add_test(desc);
- }
self.current_origin = Some(desc.origin.clone());
let test: lsp_custom::TestIdentifier = desc.into();
let stack = self.stack.entry(desc.origin.clone()).or_default();
@@ -827,6 +742,13 @@ impl test::TestReporter for LspTestReporter {
duration: Some(elapsed as u32),
})
}
+ test::TestResult::Cancelled => {
+ self.progress(lsp_custom::TestRunProgressMessage::Failed {
+ test: desc.into(),
+ messages: vec![],
+ duration: Some(elapsed as u32),
+ })
+ }
}
}
@@ -861,13 +783,46 @@ impl test::TestReporter for LspTestReporter {
}
}
- fn report_step_wait(&mut self, desc: &test::TestStepDescription) {
- if !self.includes_step(desc) {
- self.add_step(desc);
+ fn report_step_register(&mut self, desc: &test::TestStepDescription) {
+ let mut tests = self.tests.lock();
+ let tds = tests
+ .entry(ModuleSpecifier::parse(&desc.location.file_name).unwrap())
+ .or_default();
+ if tds.inject(desc.into()) {
+ let specifier = ModuleSpecifier::parse(&desc.origin).unwrap();
+ let mut prev: lsp_custom::TestData = desc.into();
+ if let Some(stack) = self.stack.get(&desc.origin) {
+ for item in stack.iter().rev() {
+ let mut data: lsp_custom::TestData = item.into();
+ data.steps = vec![prev];
+ prev = data;
+ }
+ let label = if let Some(root) = &self.maybe_root_uri {
+ specifier.as_str().replace(root.as_str(), "")
+ } else {
+ specifier
+ .path_segments()
+ .and_then(|s| s.last().map(|s| s.to_string()))
+ .unwrap_or_else(|| "<unknown>".to_string())
+ };
+ self
+ .client
+ .send_test_notification(TestingNotification::Module(
+ lsp_custom::TestModuleNotificationParams {
+ text_document: lsp::TextDocumentIdentifier { uri: specifier },
+ kind: lsp_custom::TestModuleNotificationKind::Insert,
+ label,
+ tests: vec![prev],
+ },
+ ));
+ }
}
+ }
+
+ fn report_step_wait(&mut self, desc: &test::TestStepDescription) {
let test: lsp_custom::TestIdentifier = desc.into();
- let stack = self.stack.entry(desc.test.origin.clone()).or_default();
- self.current_origin = Some(desc.test.origin.clone());
+ let stack = self.stack.entry(desc.origin.clone()).or_default();
+ self.current_origin = Some(desc.origin.clone());
assert!(!stack.is_empty());
stack.push(desc.into());
self.progress(lsp_custom::TestRunProgressMessage::Started { test });
@@ -879,7 +834,7 @@ impl test::TestReporter for LspTestReporter {
result: &test::TestStepResult,
elapsed: u64,
) {
- let stack = self.stack.entry(desc.test.origin.clone()).or_default();
+ let stack = self.stack.entry(desc.origin.clone()).or_default();
assert_eq!(stack.pop(), Some(desc.into()));
match result {
test::TestStepResult::Ok => {
@@ -927,6 +882,7 @@ impl test::TestReporter for LspTestReporter {
mod tests {
use super::*;
use crate::lsp::testing::collectors::tests::new_range;
+ use deno_core::serde_json::json;
#[test]
fn test_as_queue_and_filters() {
@@ -941,7 +897,7 @@ mod tests {
id: None,
step_id: None,
}]),
- exclude: Some(vec![lsp_custom::TestIdentifier {
+ exclude: vec![lsp_custom::TestIdentifier {
text_document: lsp::TextDocumentIdentifier {
uri: specifier.clone(),
},
@@ -950,7 +906,7 @@ mod tests {
.to_string(),
),
step_id: None,
- }]),
+ }],
};
let mut tests = HashMap::new();
let test_def_a = TestDefinition {
@@ -959,7 +915,7 @@ mod tests {
level: 0,
name: "test a".to_string(),
range: new_range(420, 424),
- steps: None,
+ steps: vec![],
};
let test_def_b = TestDefinition {
id: "69d9fe87f64f5b66cb8b631d4fd2064e8224b8715a049be54276c42189ff8f9f"
@@ -967,7 +923,7 @@ mod tests {
level: 0,
name: "test b".to_string(),
range: new_range(480, 481),
- steps: None,
+ steps: vec![],
};
let test_definitions = TestDefinitions {
discovered: vec![test_def_a, test_def_b.clone()],
@@ -988,9 +944,9 @@ mod tests {
let filter = maybe_filter.unwrap();
assert_eq!(
filter,
- &TestFilter {
- maybe_include: None,
- maybe_exclude: Some(exclude),
+ &LspTestFilter {
+ include: None,
+ exclude,
}
);
assert_eq!(
@@ -1000,14 +956,5 @@ mod tests {
.to_string()
]
);
- assert_eq!(
- filter.as_test_options(),
- json!({
- "filter": {
- "include": null,
- "exclude": vec!["test b"],
- }
- })
- );
}
}
diff --git a/cli/lsp/testing/lsp_custom.rs b/cli/lsp/testing/lsp_custom.rs
index 8182371ca..59df9884d 100644
--- a/cli/lsp/testing/lsp_custom.rs
+++ b/cli/lsp/testing/lsp_custom.rs
@@ -21,8 +21,9 @@ pub struct TestData {
pub id: String,
/// The human readable test to display for the test.
pub label: String,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub steps: Option<Vec<TestData>>,
+ #[serde(skip_serializing_if = "Vec::is_empty")]
+ #[serde(default)]
+ pub steps: Vec<TestData>,
/// The range where the test is located.
#[serde(skip_serializing_if = "Option::is_none")]
pub range: Option<lsp::Range>,
@@ -92,8 +93,9 @@ pub enum TestRunKind {
pub struct TestRunRequestParams {
pub id: u32,
pub kind: TestRunKind,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub exclude: Option<Vec<TestIdentifier>>,
+ #[serde(skip_serializing_if = "Vec::is_empty")]
+ #[serde(default)]
+ pub exclude: Vec<TestIdentifier>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include: Option<Vec<TestIdentifier>>,
}