From 9353062dca5750c77af27de154fbbe8d92dfaad5 Mon Sep 17 00:00:00 2001 From: Younes Date: Sat, 7 Nov 2026 21:08:27 +0100 Subject: [PATCH 1/2] feat(json): Add source location to DiscoverCase Signed-off-by: Younes --- crates/libtest-json/CHANGELOG.md | 2 + crates/libtest-json/event.schema.json | 43 +++++++++++++++++++ crates/libtest-json/src/event.rs | 59 ++++++++++++++++++++++++++ crates/libtest-json/tests/roundtrip.rs | 39 +++++++++++++++++ crates/libtest2-harness/CHANGELOG.md | 4 ++ crates/libtest2-harness/src/harness.rs | 27 +++++++++++- 6 files changed, 173 insertions(+), 1 deletion(-) diff --git a/crates/libtest-json/CHANGELOG.md b/crates/libtest-json/CHANGELOG.md index c4416bc..fdc9feb 100644 --- a/crates/libtest-json/CHANGELOG.md +++ b/crates/libtest-json/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] - ReleaseDate +- Add `DiscoverCase::source` + ## [0.0.2] - 2025-09-12 - Make schema `snake_case` diff --git a/crates/libtest-json/event.schema.json b/crates/libtest-json/event.schema.json index 68c0df0..826b2f2 100644 --- a/crates/libtest-json/event.schema.json +++ b/crates/libtest-json/event.schema.json @@ -127,6 +127,39 @@ } } }, + "Source": { + "description": "Where a case comes from", + "type": "object", + "properties": { + "path": { + "description": "The file as reported by the compiler (`file!()`) or by the code that built the case", + "type": "string" + }, + "start_line": { + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "start_col": { + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "end_line": { + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "end_col": { + "type": "integer", + "format": "uint", + "minimum": 0 + } + }, + "required": [ + "path" + ] + }, "RunMode": { "type": "string", "enum": [ @@ -142,6 +175,16 @@ "description": "An identifier that is unique across the entire run", "type": "string" }, + "source": { + "anyOf": [ + { + "$ref": "#/$defs/Source" + }, + { + "type": "null" + } + ] + }, "mode": { "$ref": "#/$defs/RunMode" }, diff --git a/crates/libtest-json/src/event.rs b/crates/libtest-json/src/event.rs index 179c68f..da74c22 100644 --- a/crates/libtest-json/src/event.rs +++ b/crates/libtest-json/src/event.rs @@ -125,6 +125,11 @@ impl DiscoverStart { pub struct DiscoverCase { /// An identifier that is unique across the entire run pub name: String, + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none") + )] + pub source: Option, #[cfg_attr( feature = "serde", serde(default, skip_serializing_if = "RunMode::is_default") @@ -160,6 +165,33 @@ impl DiscoverCase { buffer.keyval_sep().unwrap(); buffer.value(&self.name).unwrap(); + if let Some(source) = &self.source { + buffer.val_sep().unwrap(); + buffer.key("source").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.open_object().unwrap(); + + buffer.key("path").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(&source.path).unwrap(); + + if let Some(lines) = &source.lines { + for (key, value) in [ + ("start_line", lines.start_line), + ("start_col", lines.start_col), + ("end_line", lines.end_line), + ("end_col", lines.end_col), + ] { + buffer.val_sep().unwrap(); + buffer.key(key).unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(value as u64).unwrap(); + } + } + + buffer.close_object().unwrap(); + } + if !self.mode.is_default() { buffer.val_sep().unwrap(); buffer.key("mode").unwrap(); @@ -187,6 +219,33 @@ impl DiscoverCase { } } +/// Where a case comes from +#[derive(Clone, Debug)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] +pub struct Source { + /// The file as reported by the compiler (`file!()`) or by the code that built the case + pub path: String, + #[cfg_attr( + feature = "serde", + serde(flatten, default, skip_serializing_if = "Option::is_none") + )] + pub lines: Option, +} + +/// 1-based, `end_col` exclusive, columns count characters +#[derive(Clone, Debug)] +#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] +pub struct LineSpan { + pub start_line: usize, + pub start_col: usize, + pub end_line: usize, + pub end_col: usize, +} + #[derive(Clone, Debug)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/crates/libtest-json/tests/roundtrip.rs b/crates/libtest-json/tests/roundtrip.rs index 13381d6..61b5a81 100644 --- a/crates/libtest-json/tests/roundtrip.rs +++ b/crates/libtest-json/tests/roundtrip.rs @@ -34,6 +34,7 @@ fn discover_case() { t( libtest_json::event::DiscoverCase { name: "Hello\tworld!".to_owned(), + source: None, mode: libtest_json::RunMode::Test, selected: true, elapsed_s: None, @@ -44,6 +45,7 @@ fn discover_case() { t( libtest_json::event::DiscoverCase { name: "Hello\tworld!".to_owned(), + source: None, mode: libtest_json::RunMode::Bench, selected: false, elapsed_s: Some(libtest_json::Elapsed(Default::default())), @@ -52,6 +54,43 @@ fn discover_case() { r#"{"event":"discover_case","name":"Hello\tworld!","mode":"bench","selected":false,"elapsed_s":"0"}"# ]], ); + + t( + libtest_json::event::DiscoverCase { + name: "Hello\tworld!".to_owned(), + source: Some(libtest_json::event::Source { + path: "src/lib.rs".to_owned(), + lines: Some(libtest_json::event::LineSpan { + start_line: 11, + start_col: 4, + end_line: 11, + end_col: 14, + }), + }), + mode: libtest_json::RunMode::Test, + selected: true, + elapsed_s: None, + }, + str![[ + r#"{"event":"discover_case","name":"Hello\tworld!","source":{"path":"src/lib.rs","start_line":11,"start_col":4,"end_line":11,"end_col":14}}"# + ]], + ); + + t( + libtest_json::event::DiscoverCase { + name: "Hello\tworld!".to_owned(), + source: Some(libtest_json::event::Source { + path: "tests/fixtures/a.toml".to_owned(), + lines: None, + }), + mode: libtest_json::RunMode::Test, + selected: true, + elapsed_s: None, + }, + str![[ + r#"{"event":"discover_case","name":"Hello\tworld!","source":{"path":"tests/fixtures/a.toml"}}"# + ]], + ); } #[test] diff --git a/crates/libtest2-harness/CHANGELOG.md b/crates/libtest2-harness/CHANGELOG.md index eaaa3c9..265088c 100644 --- a/crates/libtest2-harness/CHANGELOG.md +++ b/crates/libtest2-harness/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] - ReleaseDate +### Features + +- Report `Case::source` in `discover_case` + ## [0.0.3] - 2025-09-18 ### Fixes diff --git a/crates/libtest2-harness/src/harness.rs b/crates/libtest2-harness/src/harness.rs index cbbbd48..27eec09 100644 --- a/crates/libtest2-harness/src/harness.rs +++ b/crates/libtest2-harness/src/harness.rs @@ -1,6 +1,6 @@ use libtest_lexarg::OutputFormat; -use crate::{cli, notify, Case, RunError, RunMode, TestContext}; +use crate::{cli, notify, Case, RunError, RunMode, Source, TestContext}; pub trait HarnessState: sealed::_HarnessState_is_Sealed {} @@ -106,6 +106,7 @@ impl Harness { self.state.notifier.notify( notify::event::DiscoverCase { name: case.name().to_owned(), + source: case.source().map(notify_source), mode: RunMode::Test, selected, elapsed_s: Some(notify::Elapsed(self.state.start.elapsed())), @@ -293,6 +294,30 @@ fn matches_filter(case: &dyn Case, filter: &str, opts: &libtest_lexarg::TestOpts } } +fn notify_source(source: &Source) -> notify::event::Source { + match source { + Source::Rust { + source_file, + start_line, + start_col, + end_line, + end_col, + } => notify::event::Source { + path: source_file.to_string_lossy().into_owned(), + lines: Some(notify::event::LineSpan { + start_line: *start_line, + start_col: *start_col, + end_line: *end_line, + end_col: *end_col, + }), + }, + Source::Path(path) => notify::event::Source { + path: path.to_string_lossy().into_owned(), + lines: None, + }, + } +} + fn run( start: &std::time::Instant, opts: &libtest_lexarg::TestOpts, From 6d4639be1f938148f16031bfb6a9ab14080ccccb Mon Sep 17 00:00:00 2001 From: Younes Date: Sat, 7 Nov 2026 21:11:47 +0100 Subject: [PATCH 2/2] feat(test): Report #[test] source location Signed-off-by: Younes --- crates/libtest2-proc-macro/src/lib.rs | 46 +- crates/libtest2/CHANGELOG.md | 2 + crates/libtest2/src/lib.rs | 1 + crates/libtest2/src/macros.rs | 12 +- crates/libtest2/tests/testsuite/mixed_bag.rs | 495 ++++++++++++++++--- 5 files changed, 499 insertions(+), 57 deletions(-) diff --git a/crates/libtest2-proc-macro/src/lib.rs b/crates/libtest2-proc-macro/src/lib.rs index 43c7479..99fcdf8 100644 --- a/crates/libtest2-proc-macro/src/lib.rs +++ b/crates/libtest2-proc-macro/src/lib.rs @@ -4,7 +4,7 @@ use std::iter::FromIterator; -use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; +use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; #[allow(missing_docs)] #[proc_macro_attribute] @@ -18,6 +18,50 @@ pub fn main(attribute: TokenStream, item: TokenStream) -> TokenStream { generate("main", "libtest2", attribute, item) } +/// Expands to `(file!(), line!(), column!(), )` for a test's name +#[doc(hidden)] +#[proc_macro] +pub fn _source(input: TokenStream) -> TokenStream { + let mut input = input.into_iter(); + let name = match (input.next(), input.next()) { + (Some(TokenTree::Ident(name)), None) => name, + _ => panic!("expected the test function's name"), + }; + + // `line!()`/`column!()` report where they are invoked, so every token takes the name's span + // to point at the `fn` name instead of the `#[test]` attribute + let span = name.span(); + let punct = |ch, spacing| { + let mut punct = Punct::new(ch, spacing); + punct.set_span(span); + TokenTree::Punct(punct) + }; + + let mut fields = TokenStream::new(); + for builtin in ["file", "line", "column"] { + let mut args = Group::new(Delimiter::Parenthesis, TokenStream::new()); + args.set_span(span); + fields.extend([ + punct(':', Spacing::Joint), + punct(':', Spacing::Alone), + TokenTree::Ident(Ident::new("core", span)), + punct(':', Spacing::Joint), + punct(':', Spacing::Alone), + TokenTree::Ident(Ident::new(builtin, span)), + punct('!', Spacing::Alone), + TokenTree::Group(args), + punct(',', Spacing::Alone), + ]); + } + let mut len = Literal::usize_suffixed(name.to_string().chars().count()); + len.set_span(span); + fields.extend([TokenTree::Literal(len)]); + + let mut tuple = Group::new(Delimiter::Parenthesis, fields); + tuple.set_span(span); + TokenStream::from_iter([TokenTree::Group(tuple)]) +} + /// Generates the equivalent of this Rust code as a `TokenStream`: /// /// ```nocompile diff --git a/crates/libtest2/CHANGELOG.md b/crates/libtest2/CHANGELOG.md index 23ab270..39af263 100644 --- a/crates/libtest2/CHANGELOG.md +++ b/crates/libtest2/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] - ReleaseDate +- `#[test]` reports the test's source location + ## [0.0.3] - 2025-09-18 ## [0.0.2] - 2025-09-12 diff --git a/crates/libtest2/src/lib.rs b/crates/libtest2/src/lib.rs index c90cba8..e10d8bb 100644 --- a/crates/libtest2/src/lib.rs +++ b/crates/libtest2/src/lib.rs @@ -61,6 +61,7 @@ pub mod _private { pub use distributed_list::DistributedList; pub use libtest2_harness::Source; pub use libtest2_harness::TestKind; + pub use libtest2_proc_macro::_source as source; pub use crate::_main_parse as main_parse; pub use crate::_parse_ignore as parse_ignore; diff --git a/crates/libtest2/src/macros.rs b/crates/libtest2/src/macros.rs index 7c236f1..d33baee 100644 --- a/crates/libtest2/src/macros.rs +++ b/crates/libtest2/src/macros.rs @@ -118,7 +118,17 @@ macro_rules! _test_parse { Default::default() } fn source(&self) -> Option<&$crate::_private::Source> { - None + static SOURCE: ::std::sync::OnceLock<$crate::_private::Source> = ::std::sync::OnceLock::new(); + Some(SOURCE.get_or_init(|| { + let (file, line, col, len) = $crate::_private::source!($name); + $crate::_private::Source::Rust { + source_file: ::std::path::PathBuf::from(file), + start_line: line as usize, + start_col: col as usize, + end_line: line as usize, + end_col: col as usize + len, + } + })) } fn exclusive(&self, _: &$crate::TestContext) -> bool { false diff --git a/crates/libtest2/tests/testsuite/mixed_bag.rs b/crates/libtest2/tests/testsuite/mixed_bag.rs index 631058e..32f5405 100644 --- a/crates/libtest2/tests/testsuite/mixed_bag.rs +++ b/crates/libtest2/tests/testsuite/mixed_bag.rs @@ -936,58 +936,135 @@ fn json_list() { "elapsed_s": "[..]", "event": "discover_case", "name": "cfg_in", - "selected": false + "selected": false, + "source": { + "end_col": 10, + "end_line": 61, + "path": "test.rs", + "start_col": 4, + "start_line": 61 + } }, { "elapsed_s": "[..]", "event": "discover_case", "name": "custom_error", - "selected": false + "selected": false, + "source": { + "end_col": 16, + "end_line": 15, + "path": "test.rs", + "start_col": 4, + "start_line": 15 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "failed" + "name": "failed", + "source": { + "end_col": 10, + "end_line": 10, + "path": "test.rs", + "start_col": 4, + "start_line": 10 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_attribute" + "name": "ignore_attribute", + "source": { + "end_col": 20, + "end_line": 49, + "path": "test.rs", + "start_col": 4, + "start_line": 49 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_attribute_reason" + "name": "ignore_attribute_reason", + "source": { + "end_col": 27, + "end_line": 55, + "path": "test.rs", + "start_col": 4, + "start_line": 55 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_failed" + "name": "ignore_failed", + "source": { + "end_col": 17, + "end_line": 24, + "path": "test.rs", + "start_col": 4, + "start_line": 24 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_passed" + "name": "ignore_passed", + "source": { + "end_col": 17, + "end_line": 30, + "path": "test.rs", + "start_col": 4, + "start_line": 30 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_ignore_failed" + "name": "later_ignore_failed", + "source": { + "end_col": 23, + "end_line": 36, + "path": "test.rs", + "start_col": 4, + "start_line": 36 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_ignore_passed" + "name": "later_ignore_passed", + "source": { + "end_col": 23, + "end_line": 42, + "path": "test.rs", + "start_col": 4, + "start_line": 42 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_passed" + "name": "later_passed", + "source": { + "end_col": 16, + "end_line": 20, + "path": "test.rs", + "start_col": 4, + "start_line": 20 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "passed" + "name": "passed", + "source": { + "end_col": 10, + "end_line": 6, + "path": "test.rs", + "start_col": 4, + "start_line": 6 + } }, { "elapsed_s": "[..]", @@ -1007,58 +1084,135 @@ fn json_list() { "elapsed_s": "[..]", "event": "discover_case", "name": "cfg_in", - "selected": false + "selected": false, + "source": { + "end_col": 10, + "end_line": 61, + "path": "test.rs", + "start_col": 4, + "start_line": 61 + } }, { "elapsed_s": "[..]", "event": "discover_case", "name": "custom_error", - "selected": false + "selected": false, + "source": { + "end_col": 16, + "end_line": 15, + "path": "test.rs", + "start_col": 4, + "start_line": 15 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "failed" + "name": "failed", + "source": { + "end_col": 10, + "end_line": 10, + "path": "test.rs", + "start_col": 4, + "start_line": 10 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_attribute" + "name": "ignore_attribute", + "source": { + "end_col": 20, + "end_line": 49, + "path": "test.rs", + "start_col": 4, + "start_line": 49 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_attribute_reason" + "name": "ignore_attribute_reason", + "source": { + "end_col": 27, + "end_line": 55, + "path": "test.rs", + "start_col": 4, + "start_line": 55 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_failed" + "name": "ignore_failed", + "source": { + "end_col": 17, + "end_line": 24, + "path": "test.rs", + "start_col": 4, + "start_line": 24 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_passed" + "name": "ignore_passed", + "source": { + "end_col": 17, + "end_line": 30, + "path": "test.rs", + "start_col": 4, + "start_line": 30 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_ignore_failed" + "name": "later_ignore_failed", + "source": { + "end_col": 23, + "end_line": 36, + "path": "test.rs", + "start_col": 4, + "start_line": 36 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_ignore_passed" + "name": "later_ignore_passed", + "source": { + "end_col": 23, + "end_line": 42, + "path": "test.rs", + "start_col": 4, + "start_line": 42 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_passed" + "name": "later_passed", + "source": { + "end_col": 16, + "end_line": 20, + "path": "test.rs", + "start_col": 4, + "start_line": 20 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "passed" + "name": "passed", + "source": { + "end_col": 10, + "end_line": 6, + "path": "test.rs", + "start_col": 4, + "start_line": 6 + } }, { "elapsed_s": "[..]", @@ -1086,58 +1240,135 @@ fn json_filter() { "elapsed_s": "[..]", "event": "discover_case", "name": "cfg_in", - "selected": false + "selected": false, + "source": { + "end_col": 10, + "end_line": 61, + "path": "test.rs", + "start_col": 4, + "start_line": 61 + } }, { "elapsed_s": "[..]", "event": "discover_case", "name": "custom_error", - "selected": false + "selected": false, + "source": { + "end_col": 16, + "end_line": 15, + "path": "test.rs", + "start_col": 4, + "start_line": 15 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "failed" + "name": "failed", + "source": { + "end_col": 10, + "end_line": 10, + "path": "test.rs", + "start_col": 4, + "start_line": 10 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_attribute" + "name": "ignore_attribute", + "source": { + "end_col": 20, + "end_line": 49, + "path": "test.rs", + "start_col": 4, + "start_line": 49 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_attribute_reason" + "name": "ignore_attribute_reason", + "source": { + "end_col": 27, + "end_line": 55, + "path": "test.rs", + "start_col": 4, + "start_line": 55 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_failed" + "name": "ignore_failed", + "source": { + "end_col": 17, + "end_line": 24, + "path": "test.rs", + "start_col": 4, + "start_line": 24 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_passed" + "name": "ignore_passed", + "source": { + "end_col": 17, + "end_line": 30, + "path": "test.rs", + "start_col": 4, + "start_line": 30 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_ignore_failed" + "name": "later_ignore_failed", + "source": { + "end_col": 23, + "end_line": 36, + "path": "test.rs", + "start_col": 4, + "start_line": 36 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_ignore_passed" + "name": "later_ignore_passed", + "source": { + "end_col": 23, + "end_line": 42, + "path": "test.rs", + "start_col": 4, + "start_line": 42 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_passed" + "name": "later_passed", + "source": { + "end_col": 16, + "end_line": 20, + "path": "test.rs", + "start_col": 4, + "start_line": 20 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "passed" + "name": "passed", + "source": { + "end_col": 10, + "end_line": 6, + "path": "test.rs", + "start_col": 4, + "start_line": 6 + } }, { "elapsed_s": "[..]", @@ -1315,58 +1546,135 @@ fn json_filter() { "elapsed_s": "[..]", "event": "discover_case", "name": "custom_error", - "selected": false + "selected": false, + "source": { + "end_col": 16, + "end_line": 15, + "path": "test.rs", + "start_col": 4, + "start_line": 15 + } }, { "elapsed_s": "[..]", "event": "discover_case", "name": "cfg_in", - "selected": false + "selected": false, + "source": { + "end_col": 10, + "end_line": 61, + "path": "test.rs", + "start_col": 4, + "start_line": 61 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "failed" + "name": "failed", + "source": { + "end_col": 10, + "end_line": 10, + "path": "test.rs", + "start_col": 4, + "start_line": 10 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_attribute" + "name": "ignore_attribute", + "source": { + "end_col": 20, + "end_line": 49, + "path": "test.rs", + "start_col": 4, + "start_line": 49 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_attribute_reason" + "name": "ignore_attribute_reason", + "source": { + "end_col": 27, + "end_line": 55, + "path": "test.rs", + "start_col": 4, + "start_line": 55 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_failed" + "name": "ignore_failed", + "source": { + "end_col": 17, + "end_line": 24, + "path": "test.rs", + "start_col": 4, + "start_line": 24 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_passed" + "name": "ignore_passed", + "source": { + "end_col": 17, + "end_line": 30, + "path": "test.rs", + "start_col": 4, + "start_line": 30 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_ignore_failed" + "name": "later_ignore_failed", + "source": { + "end_col": 23, + "end_line": 36, + "path": "test.rs", + "start_col": 4, + "start_line": 36 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_ignore_passed" + "name": "later_ignore_passed", + "source": { + "end_col": 23, + "end_line": 42, + "path": "test.rs", + "start_col": 4, + "start_line": 42 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_passed" + "name": "later_passed", + "source": { + "end_col": 16, + "end_line": 20, + "path": "test.rs", + "start_col": 4, + "start_line": 20 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "passed" + "name": "passed", + "source": { + "end_col": 10, + "end_line": 6, + "path": "test.rs", + "start_col": 4, + "start_line": 6 + } }, { "elapsed_s": "[..]", @@ -1528,57 +1836,134 @@ fn json_fail_fast() { { "elapsed_s": "[..]", "event": "discover_case", - "name": "cfg_in" + "name": "cfg_in", + "source": { + "end_col": 10, + "end_line": 61, + "path": "test.rs", + "start_col": 4, + "start_line": 61 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "custom_error" + "name": "custom_error", + "source": { + "end_col": 16, + "end_line": 15, + "path": "test.rs", + "start_col": 4, + "start_line": 15 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "failed" + "name": "failed", + "source": { + "end_col": 10, + "end_line": 10, + "path": "test.rs", + "start_col": 4, + "start_line": 10 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_attribute" + "name": "ignore_attribute", + "source": { + "end_col": 20, + "end_line": 49, + "path": "test.rs", + "start_col": 4, + "start_line": 49 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_attribute_reason" + "name": "ignore_attribute_reason", + "source": { + "end_col": 27, + "end_line": 55, + "path": "test.rs", + "start_col": 4, + "start_line": 55 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_failed" + "name": "ignore_failed", + "source": { + "end_col": 17, + "end_line": 24, + "path": "test.rs", + "start_col": 4, + "start_line": 24 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "ignore_passed" + "name": "ignore_passed", + "source": { + "end_col": 17, + "end_line": 30, + "path": "test.rs", + "start_col": 4, + "start_line": 30 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_ignore_failed" + "name": "later_ignore_failed", + "source": { + "end_col": 23, + "end_line": 36, + "path": "test.rs", + "start_col": 4, + "start_line": 36 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_ignore_passed" + "name": "later_ignore_passed", + "source": { + "end_col": 23, + "end_line": 42, + "path": "test.rs", + "start_col": 4, + "start_line": 42 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "later_passed" + "name": "later_passed", + "source": { + "end_col": 16, + "end_line": 20, + "path": "test.rs", + "start_col": 4, + "start_line": 20 + } }, { "elapsed_s": "[..]", "event": "discover_case", - "name": "passed" + "name": "passed", + "source": { + "end_col": 10, + "end_line": 6, + "path": "test.rs", + "start_col": 4, + "start_line": 6 + } }, { "elapsed_s": "[..]",