1use serde::Serialize;
2use std::path::Path;
3
4#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
5pub struct TestRunner {
6 pub name: String,
7 pub command: String,
8 pub args: Vec<String>,
9}
10
11impl TestRunner {
12 pub fn display_command(&self) -> String {
13 std::iter::once(self.command.as_str())
14 .chain(self.args.iter().map(String::as_str))
15 .collect::<Vec<_>>()
16 .join(" ")
17 }
18}
19
20pub fn detect_test_runner(root: &Path) -> Option<TestRunner> {
21 if root.join("Cargo.toml").exists() {
22 return Some(TestRunner {
23 name: "cargo".into(),
24 command: "cargo".into(),
25 args: vec!["test".into(), "--all-targets".into()],
26 });
27 }
28 if root.join("package.json").exists() {
29 return Some(TestRunner {
30 name: "npm".into(),
31 command: "npm".into(),
32 args: vec!["test".into()],
33 });
34 }
35 if root.join("pyproject.toml").exists()
36 || root.join("pytest.ini").exists()
37 || root.join("setup.cfg").exists()
38 {
39 return Some(TestRunner {
40 name: "pytest".into(),
41 command: "python".into(),
42 args: vec!["-m".into(), "pytest".into()],
43 });
44 }
45 None
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn detects_cargo_runner_first() {
54 let temp = tempfile::tempdir().unwrap();
55 std::fs::write(temp.path().join("Cargo.toml"), "[package]\nname='x'\n").unwrap();
56 let runner = detect_test_runner(temp.path()).unwrap();
57 assert_eq!(runner.name, "cargo");
58 assert_eq!(runner.display_command(), "cargo test --all-targets");
59 }
60
61 #[test]
62 fn detects_node_and_python_runners() {
63 let node = tempfile::tempdir().unwrap();
64 std::fs::write(node.path().join("package.json"), "{}").unwrap();
65 assert_eq!(detect_test_runner(node.path()).unwrap().name, "npm");
66
67 let py = tempfile::tempdir().unwrap();
68 std::fs::write(py.path().join("pyproject.toml"), "[project]\nname='x'\n").unwrap();
69 assert_eq!(detect_test_runner(py.path()).unwrap().name, "pytest");
70 }
71}