mirror of
https://github.com/kbwo/testing-language-server.git
synced 2025-08-01 14:22:15 +00:00
feat(adapter): add diagnostics to show which test failed
This commit is contained in:
parent
e09a50d60c
commit
aa4883e038
6 changed files with 167 additions and 76 deletions
55
Cargo.lock
generated
55
Cargo.lock
generated
|
@ -721,31 +721,6 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "testing-language-server"
|
|
||||||
version = "0.0.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4c1a48aec4305834e4f80ad28dfe5bea8c98c0bb215a092eb2af15c4ac86efb7"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"chrono",
|
|
||||||
"clap",
|
|
||||||
"dirs",
|
|
||||||
"glob",
|
|
||||||
"globwalk",
|
|
||||||
"lsp-types",
|
|
||||||
"once_cell",
|
|
||||||
"regex",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"strum",
|
|
||||||
"thiserror",
|
|
||||||
"tracing",
|
|
||||||
"tracing-appender",
|
|
||||||
"tracing-subscriber",
|
|
||||||
"tree-sitter-php",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "testing-language-server"
|
name = "testing-language-server"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
@ -770,9 +745,35 @@ dependencies = [
|
||||||
"tree-sitter-php",
|
"tree-sitter-php",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "testing-language-server"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fae6ce9cb290e80814d1796e4207a0a0dc21c216807136d48b68c0f27be29b9"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"chrono",
|
||||||
|
"clap",
|
||||||
|
"dirs",
|
||||||
|
"glob",
|
||||||
|
"globwalk",
|
||||||
|
"lsp-types",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"strum",
|
||||||
|
"thiserror",
|
||||||
|
"toml",
|
||||||
|
"tracing",
|
||||||
|
"tracing-appender",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"tree-sitter-php",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "testing-ls-adapter"
|
name = "testing-ls-adapter"
|
||||||
version = "0.0.10"
|
version = "0.0.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
@ -782,7 +783,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"testing-language-server 0.0.11",
|
"testing-language-server 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "testing-ls-adapter"
|
name = "testing-ls-adapter"
|
||||||
version = "0.0.10"
|
version = "0.0.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "testing-language-server adapter"
|
description = "testing-language-server adapter"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
@ -8,7 +8,7 @@ license = "MIT"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
testing-language-server = "0.0.11"
|
testing-language-server = "0.1.7"
|
||||||
lsp-types = { workspace = true }
|
lsp-types = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
|
|
@ -8,6 +8,7 @@ use testing_language_server::spec::RunFileTestResult;
|
||||||
|
|
||||||
use testing_language_server::spec::DiscoverResult;
|
use testing_language_server::spec::DiscoverResult;
|
||||||
use testing_language_server::spec::DiscoverResultItem;
|
use testing_language_server::spec::DiscoverResultItem;
|
||||||
|
use testing_language_server::spec::TestItem;
|
||||||
|
|
||||||
use crate::model::Runner;
|
use crate::model::Runner;
|
||||||
|
|
||||||
|
@ -16,14 +17,6 @@ use super::util::discover_rust_tests;
|
||||||
use super::util::parse_cargo_diagnostics;
|
use super::util::parse_cargo_diagnostics;
|
||||||
use super::util::write_result_log;
|
use super::util::write_result_log;
|
||||||
|
|
||||||
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 {
|
fn detect_workspaces(file_paths: &[String]) -> DetectWorkspaceResult {
|
||||||
detect_workspaces_from_file_list(file_paths, &["Cargo.toml".to_string()])
|
detect_workspaces_from_file_list(file_paths, &["Cargo.toml".to_string()])
|
||||||
}
|
}
|
||||||
|
@ -54,19 +47,16 @@ impl Runner for CargoNextestRunner {
|
||||||
args: testing_language_server::spec::RunFileTestArgs,
|
args: testing_language_server::spec::RunFileTestArgs,
|
||||||
) -> Result<(), LSError> {
|
) -> Result<(), LSError> {
|
||||||
let file_paths = args.file_paths;
|
let file_paths = args.file_paths;
|
||||||
let tests = file_paths
|
let discovered_tests: Vec<TestItem> = file_paths
|
||||||
.iter()
|
.iter()
|
||||||
.map(|path| {
|
.map(|path| discover_rust_tests(path))
|
||||||
discover_rust_tests(path).map(|test_items| {
|
|
||||||
test_items
|
|
||||||
.into_iter()
|
|
||||||
.map(|item| item.id)
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
let test_ids = discovered_tests
|
||||||
|
.iter()
|
||||||
|
.map(|item| item.id.clone())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
let workspace_root = args.workspace;
|
let workspace_root = args.workspace;
|
||||||
let test_result = std::process::Command::new("cargo")
|
let test_result = std::process::Command::new("cargo")
|
||||||
.current_dir(&workspace_root)
|
.current_dir(&workspace_root)
|
||||||
|
@ -76,7 +66,7 @@ impl Runner for CargoNextestRunner {
|
||||||
.arg("--no-fail-fast")
|
.arg("--no-fail-fast")
|
||||||
.args(args.extra)
|
.args(args.extra)
|
||||||
.arg("--")
|
.arg("--")
|
||||||
.args(tests)
|
.args(&test_ids)
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let output = test_result;
|
let output = test_result;
|
||||||
|
@ -91,10 +81,11 @@ impl Runner for CargoNextestRunner {
|
||||||
return Err(LSError::Adapter(String::from_utf8(stderr).unwrap()));
|
return Err(LSError::Adapter(String::from_utf8(stderr).unwrap()));
|
||||||
}
|
}
|
||||||
let test_result = String::from_utf8(stderr)?;
|
let test_result = String::from_utf8(stderr)?;
|
||||||
let diagnostics: RunFileTestResult = parse_diagnostics(
|
let diagnostics: RunFileTestResult = parse_cargo_diagnostics(
|
||||||
&test_result,
|
&test_result,
|
||||||
PathBuf::from_str(&workspace_root).unwrap(),
|
PathBuf::from_str(&workspace_root).unwrap(),
|
||||||
&file_paths,
|
&file_paths,
|
||||||
|
&discovered_tests,
|
||||||
);
|
);
|
||||||
send_stdout(&diagnostics)?;
|
send_stdout(&diagnostics)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -115,7 +106,7 @@ impl Runner for CargoNextestRunner {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range};
|
use lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range};
|
||||||
use testing_language_server::spec::RunFileTestResultItem;
|
use testing_language_server::spec::{RunFileTestResultItem, TestItem};
|
||||||
|
|
||||||
use crate::runner::util::MAX_CHAR_LENGTH;
|
use crate::runner::util::MAX_CHAR_LENGTH;
|
||||||
|
|
||||||
|
@ -142,10 +133,35 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||||
"#;
|
"#;
|
||||||
let file_paths =
|
let file_paths =
|
||||||
vec!["/home/example/projects/rocks-lib/src/rocks/dependency.rs".to_string()];
|
vec!["/home/example/projects/rocks-lib/src/rocks/dependency.rs".to_string()];
|
||||||
let diagnostics: RunFileTestResult = parse_diagnostics(
|
let test_items: Vec<TestItem> = vec![TestItem {
|
||||||
|
id: "rocks::dependency::tests::parse_dependency".to_string(),
|
||||||
|
name: "rocks::dependency::tests::parse_dependency".to_string(),
|
||||||
|
start_position: Range {
|
||||||
|
start: Position {
|
||||||
|
line: 85,
|
||||||
|
character: 63,
|
||||||
|
},
|
||||||
|
end: Position {
|
||||||
|
line: 85,
|
||||||
|
character: MAX_CHAR_LENGTH,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
end_position: Range {
|
||||||
|
start: Position {
|
||||||
|
line: 85,
|
||||||
|
character: 63,
|
||||||
|
},
|
||||||
|
end: Position {
|
||||||
|
line: 85,
|
||||||
|
character: MAX_CHAR_LENGTH,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
let diagnostics: RunFileTestResult = parse_cargo_diagnostics(
|
||||||
fixture,
|
fixture,
|
||||||
PathBuf::from_str("/home/example/projects").unwrap(),
|
PathBuf::from_str("/home/example/projects").unwrap(),
|
||||||
&file_paths,
|
&file_paths,
|
||||||
|
&test_items,
|
||||||
);
|
);
|
||||||
let message = r#"called `Result::unwrap()` on an `Err` value: unexpected end of input while parsing min or version number
|
let message = r#"called `Result::unwrap()` on an `Err` value: unexpected end of input while parsing min or version number
|
||||||
Location:
|
Location:
|
||||||
|
|
|
@ -8,6 +8,7 @@ use testing_language_server::spec::RunFileTestResult;
|
||||||
|
|
||||||
use testing_language_server::spec::DiscoverResult;
|
use testing_language_server::spec::DiscoverResult;
|
||||||
use testing_language_server::spec::DiscoverResultItem;
|
use testing_language_server::spec::DiscoverResultItem;
|
||||||
|
use testing_language_server::spec::TestItem;
|
||||||
|
|
||||||
use crate::model::Runner;
|
use crate::model::Runner;
|
||||||
|
|
||||||
|
@ -16,14 +17,6 @@ use super::util::discover_rust_tests;
|
||||||
use super::util::parse_cargo_diagnostics;
|
use super::util::parse_cargo_diagnostics;
|
||||||
use super::util::write_result_log;
|
use super::util::write_result_log;
|
||||||
|
|
||||||
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 {
|
fn detect_workspaces(file_paths: &[String]) -> DetectWorkspaceResult {
|
||||||
detect_workspaces_from_file_list(file_paths, &["Cargo.toml".to_string()])
|
detect_workspaces_from_file_list(file_paths, &["Cargo.toml".to_string()])
|
||||||
}
|
}
|
||||||
|
@ -54,26 +47,23 @@ impl Runner for CargoTestRunner {
|
||||||
args: testing_language_server::spec::RunFileTestArgs,
|
args: testing_language_server::spec::RunFileTestArgs,
|
||||||
) -> Result<(), LSError> {
|
) -> Result<(), LSError> {
|
||||||
let file_paths = args.file_paths;
|
let file_paths = args.file_paths;
|
||||||
let tests = file_paths
|
let discovered_tests: Vec<TestItem> = file_paths
|
||||||
.iter()
|
.iter()
|
||||||
.map(|path| {
|
.map(|path| discover_rust_tests(path))
|
||||||
discover_rust_tests(path).map(|test_items| {
|
|
||||||
test_items
|
|
||||||
.into_iter()
|
|
||||||
.map(|item| item.id)
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
let test_ids = discovered_tests
|
||||||
|
.iter()
|
||||||
|
.map(|item| item.id.clone())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
let workspace_root = args.workspace;
|
let workspace_root = args.workspace;
|
||||||
let test_result = std::process::Command::new("cargo")
|
let test_result = std::process::Command::new("cargo")
|
||||||
.current_dir(&workspace_root)
|
.current_dir(&workspace_root)
|
||||||
.arg("test")
|
.arg("test")
|
||||||
.args(args.extra)
|
.args(args.extra)
|
||||||
.arg("--")
|
.arg("--")
|
||||||
.args(tests)
|
.args(&test_ids)
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let output = test_result;
|
let output = test_result;
|
||||||
|
@ -85,10 +75,12 @@ impl Runner for CargoTestRunner {
|
||||||
// When `--nocapture` option is set, stderr has some important information
|
// When `--nocapture` option is set, stderr has some important information
|
||||||
// to parse test result
|
// to parse test result
|
||||||
let test_result = String::from_utf8(stderr)? + &String::from_utf8(stdout)?;
|
let test_result = String::from_utf8(stderr)? + &String::from_utf8(stdout)?;
|
||||||
let diagnostics: RunFileTestResult = parse_diagnostics(
|
|
||||||
|
let diagnostics: RunFileTestResult = parse_cargo_diagnostics(
|
||||||
&test_result,
|
&test_result,
|
||||||
PathBuf::from_str(&workspace_root).unwrap(),
|
PathBuf::from_str(&workspace_root).unwrap(),
|
||||||
&file_paths,
|
&file_paths,
|
||||||
|
&discovered_tests,
|
||||||
);
|
);
|
||||||
send_stdout(&diagnostics)?;
|
send_stdout(&diagnostics)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -136,10 +128,35 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||||
"#;
|
"#;
|
||||||
let file_paths =
|
let file_paths =
|
||||||
vec!["/home/example/projects/rocks-lib/src/rocks/dependency.rs".to_string()];
|
vec!["/home/example/projects/rocks-lib/src/rocks/dependency.rs".to_string()];
|
||||||
let diagnostics: RunFileTestResult = parse_diagnostics(
|
let test_items: Vec<TestItem> = vec![TestItem {
|
||||||
|
id: "rocks::dependency::tests::parse_dependency".to_string(),
|
||||||
|
name: "rocks::dependency::tests::parse_dependency".to_string(),
|
||||||
|
start_position: Range {
|
||||||
|
start: Position {
|
||||||
|
line: 85,
|
||||||
|
character: 63,
|
||||||
|
},
|
||||||
|
end: Position {
|
||||||
|
line: 85,
|
||||||
|
character: MAX_CHAR_LENGTH,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
end_position: Range {
|
||||||
|
start: Position {
|
||||||
|
line: 85,
|
||||||
|
character: 63,
|
||||||
|
},
|
||||||
|
end: Position {
|
||||||
|
line: 85,
|
||||||
|
character: MAX_CHAR_LENGTH,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
let diagnostics: RunFileTestResult = parse_cargo_diagnostics(
|
||||||
fixture,
|
fixture,
|
||||||
PathBuf::from_str("/home/example/projects").unwrap(),
|
PathBuf::from_str("/home/example/projects").unwrap(),
|
||||||
&file_paths,
|
&file_paths,
|
||||||
|
&test_items,
|
||||||
);
|
);
|
||||||
let message = r#"called `Result::unwrap()` on an `Err` value: unexpected end of input while parsing min or version number
|
let message = r#"called `Result::unwrap()` on an `Err` value: unexpected end of input while parsing min or version number
|
||||||
Location:
|
Location:
|
||||||
|
|
|
@ -276,19 +276,35 @@ pub fn parse_cargo_diagnostics(
|
||||||
contents: &str,
|
contents: &str,
|
||||||
workspace_root: PathBuf,
|
workspace_root: PathBuf,
|
||||||
file_paths: &[String],
|
file_paths: &[String],
|
||||||
|
test_items: &[TestItem],
|
||||||
) -> RunFileTestResult {
|
) -> RunFileTestResult {
|
||||||
let contents = contents.replace("\r\n", "\n");
|
let contents = contents.replace("\r\n", "\n");
|
||||||
let lines = contents.lines();
|
let lines = contents.lines();
|
||||||
let mut result_map: HashMap<String, Vec<Diagnostic>> = HashMap::new();
|
let mut result_map: HashMap<String, Vec<Diagnostic>> = HashMap::new();
|
||||||
for (i, line) in lines.clone().enumerate() {
|
for (i, line) in lines.clone().enumerate() {
|
||||||
|
// Example:
|
||||||
|
// thread 'server::tests::test_panic' panicked at src/server.rs:584:9:
|
||||||
let re = Regex::new(r"thread '([^']+)' panicked at ([^:]+):(\d+):(\d+):").unwrap();
|
let re = Regex::new(r"thread '([^']+)' panicked at ([^:]+):(\d+):(\d+):").unwrap();
|
||||||
if let Some(m) = re.captures(line) {
|
if let Some(m) = re.captures(line) {
|
||||||
let mut message = String::new();
|
let mut message = String::new();
|
||||||
let file = m.get(2).unwrap().as_str().to_string();
|
// <filename>::<id>
|
||||||
if let Some(file_path) = file_paths
|
let id_with_file = m.get(1).unwrap().as_str().to_string();
|
||||||
.iter()
|
|
||||||
.find(|path| path.contains(workspace_root.join(&file).to_str().unwrap()))
|
// relaive path
|
||||||
{
|
let relative_file_path = m.get(2).unwrap().as_str().to_string();
|
||||||
|
// name of the file without extension
|
||||||
|
let file_stem = Path::new(&relative_file_path)
|
||||||
|
.file_stem()
|
||||||
|
.unwrap()
|
||||||
|
.to_str()
|
||||||
|
.unwrap();
|
||||||
|
let executed_test_id = id_with_file.replace(&(file_stem.to_string() + "::"), "");
|
||||||
|
|
||||||
|
if let Some(file_path) = file_paths.iter().find(|path| {
|
||||||
|
path.contains(workspace_root.join(&relative_file_path).to_str().unwrap())
|
||||||
|
}) {
|
||||||
|
let matched_test_item = test_items.iter().find(|item| item.id == executed_test_id);
|
||||||
|
|
||||||
let lnum = m.get(3).unwrap().as_str().parse::<u32>().unwrap() - 1;
|
let lnum = m.get(3).unwrap().as_str().parse::<u32>().unwrap() - 1;
|
||||||
let col = m.get(4).unwrap().as_str().parse::<u32>().unwrap() - 1;
|
let col = m.get(4).unwrap().as_str().parse::<u32>().unwrap() - 1;
|
||||||
let mut next_i = i + 1;
|
let mut next_i = i + 1;
|
||||||
|
@ -298,6 +314,33 @@ pub fn parse_cargo_diagnostics(
|
||||||
message = format!("{}{}\n", message, lines.clone().nth(next_i).unwrap());
|
message = format!("{}{}\n", message, lines.clone().nth(next_i).unwrap());
|
||||||
next_i += 1;
|
next_i += 1;
|
||||||
}
|
}
|
||||||
|
let diagnostic = Diagnostic {
|
||||||
|
range: Range {
|
||||||
|
start: Position {
|
||||||
|
line: lnum,
|
||||||
|
character: col,
|
||||||
|
},
|
||||||
|
end: Position {
|
||||||
|
line: lnum,
|
||||||
|
character: MAX_CHAR_LENGTH,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
message: message.clone(),
|
||||||
|
severity: Some(DiagnosticSeverity::ERROR),
|
||||||
|
..Diagnostic::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// if the test item is matched,
|
||||||
|
// add a diagnostic to the beginning of the test item
|
||||||
|
// in order to show which test failed.
|
||||||
|
// If this code does not exist, only panicked positions are shown
|
||||||
|
if let Some(test_item) = matched_test_item {
|
||||||
|
let message = format!(
|
||||||
|
"`{}` failed at {relative_file_path}:{lnum}:{col}\nMessage:\n{message}",
|
||||||
|
test_item.name
|
||||||
|
);
|
||||||
|
let lnum = test_item.start_position.start.line;
|
||||||
|
let col = test_item.start_position.start.character;
|
||||||
let diagnostic = Diagnostic {
|
let diagnostic = Diagnostic {
|
||||||
range: Range {
|
range: Range {
|
||||||
start: Position {
|
start: Position {
|
||||||
|
@ -317,6 +360,11 @@ pub fn parse_cargo_diagnostics(
|
||||||
.entry(file_path.to_string())
|
.entry(file_path.to_string())
|
||||||
.or_default()
|
.or_default()
|
||||||
.push(diagnostic);
|
.push(diagnostic);
|
||||||
|
}
|
||||||
|
result_map
|
||||||
|
.entry(file_path.to_string())
|
||||||
|
.or_default()
|
||||||
|
.push(diagnostic);
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,4 +53,13 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn p() {
|
||||||
|
panic!("test failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_panic() {
|
||||||
|
p();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue