Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/test_explorer.rs')
-rw-r--r--crates/ide/src/test_explorer.rs135
1 files changed, 135 insertions, 0 deletions
diff --git a/crates/ide/src/test_explorer.rs b/crates/ide/src/test_explorer.rs
new file mode 100644
index 0000000000..2e741021ea
--- /dev/null
+++ b/crates/ide/src/test_explorer.rs
@@ -0,0 +1,135 @@
+//! Discovers tests
+
+use hir::{Crate, Module, ModuleDef, Semantics};
+use ide_db::{
+ base_db::{CrateGraph, CrateId, FileId, SourceDatabase},
+ RootDatabase,
+};
+use syntax::TextRange;
+
+use crate::{navigation_target::ToNav, runnables::runnable_fn, Runnable, TryToNav};
+
+#[derive(Debug)]
+pub enum TestItemKind {
+ Crate,
+ Module,
+ Function,
+}
+
+#[derive(Debug)]
+pub struct TestItem {
+ pub id: String,
+ pub kind: TestItemKind,
+ pub label: String,
+ pub parent: Option<String>,
+ pub file: Option<FileId>,
+ pub text_range: Option<TextRange>,
+ pub runnable: Option<Runnable>,
+}
+
+pub(crate) fn discover_test_roots(db: &RootDatabase) -> Vec<TestItem> {
+ let crate_graph = db.crate_graph();
+ crate_graph
+ .iter()
+ .filter(|&id| crate_graph[id].origin.is_local())
+ .filter_map(|id| Some(crate_graph[id].display_name.as_ref()?.to_string()))
+ .map(|id| TestItem {
+ kind: TestItemKind::Crate,
+ label: id.clone(),
+ id,
+ parent: None,
+ file: None,
+ text_range: None,
+ runnable: None,
+ })
+ .collect()
+}
+
+fn find_crate_by_id(crate_graph: &CrateGraph, crate_id: &str) -> Option<CrateId> {
+ // here, we use display_name as the crate id. This is not super ideal, but it works since we
+ // only show tests for the local crates.
+ crate_graph.iter().find(|&id| {
+ crate_graph[id].origin.is_local()
+ && crate_graph[id].display_name.as_ref().is_some_and(|x| x.to_string() == crate_id)
+ })
+}
+
+fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String) -> Vec<TestItem> {
+ let sema = Semantics::new(db);
+
+ let mut r = vec![];
+ for c in module.children(db) {
+ let module_name =
+ c.name(db).as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]").to_owned();
+ let module_id = format!("{prefix_id}::{module_name}");
+ let module_children = discover_tests_in_module(db, c, module_id.clone());
+ if !module_children.is_empty() {
+ let nav = c.to_nav(db).call_site;
+ r.push(TestItem {
+ id: module_id,
+ kind: TestItemKind::Module,
+ label: module_name,
+ parent: Some(prefix_id.clone()),
+ file: Some(nav.file_id),
+ text_range: Some(nav.focus_or_full_range()),
+ runnable: None,
+ });
+ r.extend(module_children);
+ }
+ }
+ for def in module.declarations(db) {
+ let ModuleDef::Function(f) = def else {
+ continue;
+ };
+ if !f.is_test(db) {
+ continue;
+ }
+ let nav = f.try_to_nav(db).map(|r| r.call_site);
+ let fn_name = f.name(db).as_str().unwrap_or("[function without name]").to_owned();
+ r.push(TestItem {
+ id: format!("{prefix_id}::{fn_name}"),
+ kind: TestItemKind::Function,
+ label: fn_name,
+ parent: Some(prefix_id.clone()),
+ file: nav.as_ref().map(|n| n.file_id),
+ text_range: nav.as_ref().map(|n| n.focus_or_full_range()),
+ runnable: runnable_fn(&sema, f),
+ });
+ }
+ r
+}
+
+pub(crate) fn discover_tests_in_crate_by_test_id(
+ db: &RootDatabase,
+ crate_test_id: &str,
+) -> Vec<TestItem> {
+ let crate_graph = db.crate_graph();
+ let Some(crate_id) = find_crate_by_id(&crate_graph, crate_test_id) else {
+ return vec![];
+ };
+ discover_tests_in_crate(db, crate_id)
+}
+
+pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> Vec<TestItem> {
+ let crate_graph = db.crate_graph();
+ if !crate_graph[crate_id].origin.is_local() {
+ return vec![];
+ }
+ let Some(crate_test_id) = &crate_graph[crate_id].display_name else {
+ return vec![];
+ };
+ let crate_test_id = crate_test_id.to_string();
+ let crate_id: Crate = crate_id.into();
+ let module = crate_id.root_module();
+ let mut r = vec![TestItem {
+ id: crate_test_id.clone(),
+ kind: TestItemKind::Crate,
+ label: crate_test_id.clone(),
+ parent: None,
+ file: None,
+ text_range: None,
+ runnable: None,
+ }];
+ r.extend(discover_tests_in_module(db, module, crate_test_id));
+ r
+}