diff options
Diffstat (limited to 'cli/lsp/testing/definitions.rs')
-rw-r--r-- | cli/lsp/testing/definitions.rs | 251 |
1 files changed, 130 insertions, 121 deletions
diff --git a/cli/lsp/testing/definitions.rs b/cli/lsp/testing/definitions.rs index 6992c995d..30b0d3bb0 100644 --- a/cli/lsp/testing/definitions.rs +++ b/cli/lsp/testing/definitions.rs @@ -1,172 +1,181 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. use super::lsp_custom; +use super::lsp_custom::TestData; -use crate::lsp::analysis::source_range_to_lsp_range; use crate::lsp::client::TestingNotification; +use crate::lsp::logging::lsp_warn; +use crate::tools::test::TestDescription; +use crate::tools::test::TestStepDescription; use crate::util::checksum; -use deno_ast::SourceRange; -use deno_ast::SourceTextInfo; use deno_core::ModuleSpecifier; +use lsp::Range; use std::collections::HashMap; +use std::collections::HashSet; use tower_lsp::lsp_types as lsp; #[derive(Debug, Clone, PartialEq)] pub struct TestDefinition { pub id: String, pub name: String, - pub range: SourceRange, - pub steps: Vec<TestDefinition>, + pub range: Option<Range>, + pub is_dynamic: bool, + pub parent_id: Option<String>, + pub step_ids: HashSet<String>, } -impl TestDefinition { - pub fn new( - specifier: &ModuleSpecifier, +#[derive(Debug, Clone, PartialEq)] +pub struct TestModule { + pub specifier: ModuleSpecifier, + /// The version of the document that the discovered tests relate to. + pub script_version: String, + pub defs: HashMap<String, TestDefinition>, +} + +impl TestModule { + pub fn new(specifier: ModuleSpecifier, script_version: String) -> Self { + Self { + specifier, + script_version, + defs: Default::default(), + } + } + + /// Returns `(id, is_newly_registered)`. + pub fn register( + &mut self, name: String, - range: SourceRange, - mut steps: Vec<TestDefinition>, - ) -> Self { + range: Option<Range>, + is_dynamic: bool, + parent_id: Option<String>, + ) -> (String, bool) { let mut id_components = Vec::with_capacity(7); - id_components.push(specifier.as_str().as_bytes()); id_components.push(name.as_bytes()); + let mut current_parent_id = &parent_id; + while let Some(parent_id) = current_parent_id { + let parent = match self.defs.get(parent_id) { + Some(d) => d, + None => { + lsp_warn!("Internal Error: parent_id \"{}\" of test \"{}\" was not registered.", parent_id, &name); + id_components.push("<unknown>".as_bytes()); + break; + } + }; + id_components.push(parent.name.as_bytes()); + current_parent_id = &parent.parent_id; + } + id_components.push(self.specifier.as_str().as_bytes()); + id_components.reverse(); let id = checksum::gen(&id_components); - Self::fix_ids(&mut steps, &mut id_components); - Self { - id, - name, - range, - steps, + if self.defs.contains_key(&id) { + return (id, false); } - } - - fn fix_ids<'a>( - steps: &'a mut Vec<TestDefinition>, - id_components: &mut Vec<&'a [u8]>, - ) { - for step in steps { - id_components.push(step.name.as_bytes()); - step.id = checksum::gen(id_components); - Self::fix_ids(&mut step.steps, id_components); - id_components.pop(); + if let Some(parent_id) = &parent_id { + let parent = self.defs.get_mut(parent_id).unwrap(); + parent.step_ids.insert(id.clone()); } + self.defs.insert( + id.clone(), + TestDefinition { + id: id.clone(), + name, + range, + is_dynamic, + parent_id, + step_ids: Default::default(), + }, + ); + (id, true) } - pub fn new_step( - name: String, - range: SourceRange, - steps: Vec<TestDefinition>, - ) -> Self { - Self { - // ID will be fixed later when the entire ancestry is available. - id: "".to_string(), - name, - range, - steps, - } + /// Returns `(id, was_newly_registered)`. + pub fn register_dynamic(&mut self, desc: &TestDescription) -> (String, bool) { + self.register(desc.name.clone(), None, true, None) } - fn as_test_data( - &self, - source_text_info: &SourceTextInfo, - ) -> lsp_custom::TestData { - lsp_custom::TestData { - id: self.id.clone(), - label: self.name.clone(), - 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)), - } + /// Returns `(id, was_newly_registered)`. + pub fn register_step_dynamic( + &mut self, + desc: &TestStepDescription, + parent_static_id: &str, + ) -> (String, bool) { + self.register( + desc.name.clone(), + None, + true, + Some(parent_static_id.to_string()), + ) } - 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)) + pub fn get(&self, id: &str) -> Option<&TestDefinition> { + self.defs.get(id) } -} - -#[derive(Debug, Clone)] -pub struct TestDefinitions { - /// definitions of tests and their steps which were statically discovered from - /// the source document. - pub discovered: Vec<TestDefinition>, - /// Tests and steps which the test runner notified us of, which were - /// dynamically added - pub injected: Vec<lsp_custom::TestData>, - /// The version of the document that the discovered tests relate to. - pub script_version: String, -} -impl Default for TestDefinitions { - fn default() -> Self { - TestDefinitions { - script_version: "1".to_string(), - discovered: vec![], - injected: vec![], + pub fn get_test_data(&self, id: &str) -> TestData { + fn get_test_data_inner(tm: &TestModule, id: &str) -> TestData { + let def = tm.defs.get(id).unwrap(); + TestData { + id: def.id.clone(), + label: def.name.clone(), + steps: def + .step_ids + .iter() + .map(|id| get_test_data_inner(tm, id)) + .collect(), + range: def.range, + } + } + let def = self.defs.get(id).unwrap(); + let mut current_data = get_test_data_inner(self, &def.id); + let mut current_parent_id = &def.parent_id; + while let Some(parent_id) = current_parent_id { + let parent = self.defs.get(parent_id).unwrap(); + current_data = TestData { + id: parent.id.clone(), + label: parent.name.clone(), + steps: vec![current_data], + range: None, + }; + current_parent_id = &parent.parent_id; } + current_data } -} -impl TestDefinitions { /// Return the test definitions as a testing module notification. - pub fn as_notification( + pub fn as_replace_notification( &self, - specifier: &ModuleSpecifier, - maybe_root: Option<&ModuleSpecifier>, - source_text_info: &SourceTextInfo, + maybe_root_uri: Option<&ModuleSpecifier>, ) -> TestingNotification { - let label = if let Some(root) = maybe_root { - 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()) - }; - let mut tests_map: HashMap<String, lsp_custom::TestData> = self - .injected - .iter() - .map(|td| (td.id.clone(), td.clone())) - .collect(); - tests_map.extend(self.discovered.iter().map(|td| { - let test_data = td.as_test_data(source_text_info); - (test_data.id.clone(), test_data) - })); + let label = self.label(maybe_root_uri); TestingNotification::Module(lsp_custom::TestModuleNotificationParams { text_document: lsp::TextDocumentIdentifier { - uri: specifier.clone(), + uri: self.specifier.clone(), }, kind: lsp_custom::TestModuleNotificationKind::Replace, label, - tests: tests_map.into_values().collect(), + tests: self + .defs + .iter() + .filter(|(_, def)| def.parent_id.is_none()) + .map(|(id, _)| self.get_test_data(id)) + .collect(), }) } - /// 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; + pub fn label(&self, maybe_root_uri: Option<&ModuleSpecifier>) -> String { + if let Some(root) = maybe_root_uri { + self.specifier.as_str().replace(root.as_str(), "") + } else { + self + .specifier + .path_segments() + .and_then(|s| s.last().map(|s| s.to_string())) + .unwrap_or_else(|| "<unknown>".to_string()) } - 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 - .discovered - .iter() - .find(|td| td.id.as_str() == id.as_ref()) } pub fn is_empty(&self) -> bool { - self.discovered.is_empty() && self.injected.is_empty() + self.defs.is_empty() } } |