Skip to content

Commit

Permalink
feat(adapter): add adapter for cargo-nextest
Browse files Browse the repository at this point in the history
close #18
  • Loading branch information
kbwo committed Jul 15, 2024
1 parent 3603e32 commit b887d65
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 172 deletions.
6 changes: 6 additions & 0 deletions crates/adapter/src/model.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::runner::cargo_nextest::CargoNextestRunner;
use crate::runner::cargo_test::CargoTestRunner;
use crate::runner::go::GoTestRunner;
use crate::runner::vitest::VitestRunner;
Expand All @@ -12,6 +13,7 @@ use crate::runner::jest::JestRunner;
#[derive(Debug, Eq, PartialEq)]
pub enum AvailableTestKind {
CargoTest(CargoTestRunner),
CargoNextest(CargoNextestRunner),
Jest(JestRunner),
Vitest(VitestRunner),
GoTest(GoTestRunner),
Expand All @@ -20,6 +22,7 @@ impl Runner for AvailableTestKind {
fn disover(&self, args: DiscoverArgs) -> Result<(), LSError> {
match self {
AvailableTestKind::CargoTest(runner) => runner.disover(args),
AvailableTestKind::CargoNextest(runner) => runner.disover(args),
AvailableTestKind::Jest(runner) => runner.disover(args),
AvailableTestKind::GoTest(runner) => runner.disover(args),
AvailableTestKind::Vitest(runner) => runner.disover(args),
Expand All @@ -29,6 +32,7 @@ impl Runner for AvailableTestKind {
fn run_file_test(&self, args: RunFileTestArgs) -> Result<(), LSError> {
match self {
AvailableTestKind::CargoTest(runner) => runner.run_file_test(args),
AvailableTestKind::CargoNextest(runner) => runner.run_file_test(args),
AvailableTestKind::Jest(runner) => runner.run_file_test(args),
AvailableTestKind::GoTest(runner) => runner.run_file_test(args),
AvailableTestKind::Vitest(runner) => runner.run_file_test(args),
Expand All @@ -38,6 +42,7 @@ impl Runner for AvailableTestKind {
fn detect_workspaces(&self, args: DetectWorkspaceArgs) -> Result<(), LSError> {
match self {
AvailableTestKind::CargoTest(runner) => runner.detect_workspaces(args),
AvailableTestKind::CargoNextest(runner) => runner.detect_workspaces(args),
AvailableTestKind::Jest(runner) => runner.detect_workspaces(args),
AvailableTestKind::GoTest(runner) => runner.detect_workspaces(args),
AvailableTestKind::Vitest(runner) => runner.detect_workspaces(args),
Expand All @@ -51,6 +56,7 @@ impl FromStr for AvailableTestKind {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"cargo-test" => Ok(AvailableTestKind::CargoTest(CargoTestRunner)),
"cargo-nextest" => Ok(AvailableTestKind::CargoNextest(CargoNextestRunner)),
"jest" => Ok(AvailableTestKind::Jest(JestRunner)),
"go-test" => Ok(AvailableTestKind::GoTest(GoTestRunner)),
"vitest" => Ok(AvailableTestKind::Vitest(VitestRunner)),
Expand Down
197 changes: 197 additions & 0 deletions crates/adapter/src/runner/cargo_nextest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
use crate::runner::util::send_stdout;
use std::path::PathBuf;
use std::process::Output;
use std::str::FromStr;
use testing_language_server::error::LSError;
use testing_language_server::spec::DetectWorkspaceResult;
use testing_language_server::spec::RunFileTestResult;

use testing_language_server::spec::DiscoverResult;
use testing_language_server::spec::DiscoverResultItem;

use crate::model::Runner;

use super::util::detect_workspaces_from_file_paths;
use super::util::discover_rust_tests;
use super::util::parse_cargo_diagnostics;

fn parse_diagnostics(
contents: &str,
workspace_root: PathBuf,
file_paths: &[String],
) -> RunFileTestResult {
parse_cargo_diagnostics(contents, workspace_root, file_paths)
}

fn detect_workspaces(file_paths: &[String]) -> DetectWorkspaceResult {
detect_workspaces_from_file_paths(file_paths, &["Cargo.toml".to_string()])
}

#[derive(Eq, PartialEq, Hash, Debug)]
pub struct CargoNextestRunner;

impl Runner for CargoNextestRunner {
fn disover(&self, args: testing_language_server::spec::DiscoverArgs) -> Result<(), LSError> {
let file_paths = args.file_paths;
let mut discover_results: DiscoverResult = vec![];

for file_path in file_paths {
let tests = discover_rust_tests(&file_path)?;
discover_results.push(DiscoverResultItem {
tests,
path: file_path,
});
}
send_stdout(&discover_results)?;
Ok(())
}

fn run_file_test(
&self,
args: testing_language_server::spec::RunFileTestArgs,
) -> Result<(), LSError> {
let file_paths = args.file_paths;
let tests = file_paths
.iter()
.map(|path| {
discover_rust_tests(path).map(|test_items| {
test_items
.into_iter()
.map(|item| item.id)
.collect::<Vec<String>>()
})
})
.filter_map(Result::ok)
.flatten()
.collect::<Vec<_>>();
let workspace_root = args.workspace;
let test_result = std::process::Command::new("cargo")
.current_dir(&workspace_root)
.arg("nextest")
.arg("run")
.arg("--workspace")
.arg("--no-fail-fast")
.args(args.extra)
.arg("--")
.args(tests)
.output()
.unwrap();
let Output {
stdout,
stderr,
status,
..
} = test_result;
let unexpected_status_code = status.code().map(|code| code != 100);
if stdout.is_empty() && !stderr.is_empty() && unexpected_status_code.unwrap_or(false) {
return Err(LSError::Adapter(String::from_utf8(stderr).unwrap()));
}
let test_result = String::from_utf8(stderr)?;
let diagnostics: RunFileTestResult = parse_diagnostics(
&test_result,
PathBuf::from_str(&workspace_root).unwrap(),
&file_paths,
);
send_stdout(&diagnostics)?;
Ok(())
}

fn detect_workspaces(
&self,
args: testing_language_server::spec::DetectWorkspaceArgs,
) -> Result<(), LSError> {
let file_paths = args.file_paths;
let detect_result = detect_workspaces(&file_paths);
send_stdout(&detect_result)?;
Ok(())
}
}

#[cfg(test)]
mod tests {
use lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range};
use testing_language_server::spec::RunFileTestResultItem;

use crate::runner::util::MAX_CHAR_LENGTH;

use super::*;

#[test]
fn parse_test_results() {
let fixture = r#"
running 1 test
test rocks::dependency::tests::parse_dependency ... FAILED
failures:
Finished test [unoptimized + debuginfo] target(s) in 0.12s
Starting 1 test across 2 binaries (17 skipped)
FAIL [ 0.004s] rocks-lib rocks::dependency::tests::parse_dependency
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 17 filtered out; finis
hed in 0.00s
--- STDERR: rocks-lib rocks::dependency::tests::parse_dependency ---
thread 'rocks::dependency::tests::parse_dependency' panicked at rocks-lib/src/rocks/dependency.rs:86:64:
called `Result::unwrap()` on an `Err` value: unexpected end of input while parsing min or version number
Location:
rocks-lib/src/rocks/dependency.rs:62:22
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
"#;
let file_paths =
vec!["/home/example/projects/rocks-lib/src/rocks/dependency.rs".to_string()];
let diagnostics: RunFileTestResult = parse_diagnostics(
fixture,
PathBuf::from_str("/home/example/projects").unwrap(),
&file_paths,
);
let message = r#"called `Result::unwrap()` on an `Err` value: unexpected end of input while parsing min or version number
Location:
rocks-lib/src/rocks/dependency.rs:62:22
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
"#;

assert_eq!(
diagnostics,
vec![RunFileTestResultItem {
path: file_paths.first().unwrap().to_owned(),
diagnostics: vec![Diagnostic {
range: Range {
start: Position {
line: 85,
character: 63
},
end: Position {
line: 85,
character: MAX_CHAR_LENGTH
}
},
message: message.to_string(),
severity: Some(DiagnosticSeverity::ERROR),
..Diagnostic::default()
}]
}]
)
}

#[test]
fn test_discover() {
let file_path = "../../demo/rust/src/lib.rs";
discover_rust_tests(file_path).unwrap();
}

#[test]
fn test_detect_workspaces() {
let current_dir = std::env::current_dir().unwrap();
let librs = current_dir.join("src/lib.rs");
let mainrs = current_dir.join("src/main.rs");
let absolute_path_of_demo = current_dir.join("../../demo/rust");
let demo_librs = absolute_path_of_demo.join("src/lib.rs");
let file_paths: Vec<String> = [librs, mainrs, demo_librs]
.iter()
.map(|file_path| file_path.to_str().unwrap().to_string())
.collect();

let workspaces = detect_workspaces(&file_paths);
assert_eq!(workspaces.len(), 2);
assert!(workspaces.contains_key(absolute_path_of_demo.to_str().unwrap()));
assert!(workspaces.contains_key(current_dir.to_str().unwrap()));
}
}
Loading

0 comments on commit b887d65

Please sign in to comment.