diff --git a/crates/libtest-json/event.schema.json b/crates/libtest-json/event.schema.json index 0483369..6e1a40e 100644 --- a/crates/libtest-json/event.schema.json +++ b/crates/libtest-json/event.schema.json @@ -3,6 +3,103 @@ "title": "Event", "oneOf": [ { + "type": "object", + "properties": { + "event": { + "type": "string", + "const": "discover_start" + } + }, + "$ref": "#/$defs/DiscoverStart", + "required": [ + "event" + ] + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "const": "discover_case" + } + }, + "$ref": "#/$defs/DiscoverCase", + "required": [ + "event" + ] + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "const": "discover_complete" + } + }, + "$ref": "#/$defs/DiscoverComplete", + "required": [ + "event" + ] + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "const": "run_start" + } + }, + "$ref": "#/$defs/RunStart", + "required": [ + "event" + ] + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "const": "case_start" + } + }, + "$ref": "#/$defs/CaseStart", + "required": [ + "event" + ] + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "const": "case_complete" + } + }, + "$ref": "#/$defs/CaseComplete", + "required": [ + "event" + ] + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "const": "run_complete" + } + }, + "$ref": "#/$defs/RunComplete", + "required": [ + "event" + ] + } + ], + "$defs": { + "Elapsed": { + "description": "Time elapsed since process start", + "type": "string" + }, + "DiscoverStart": { "type": "object", "properties": { "elapsed_s": { @@ -14,17 +111,17 @@ "type": "null" } ] - }, - "event": { - "type": "string", - "const": "discover_start" } - }, - "required": [ - "event" + } + }, + "RunMode": { + "type": "string", + "enum": [ + "test", + "bench" ] }, - { + "DiscoverCase": { "type": "object", "properties": { "name": { @@ -46,18 +143,13 @@ "type": "null" } ] - }, - "event": { - "type": "string", - "const": "discover_case" } }, "required": [ - "event", "name" ] }, - { + "DiscoverComplete": { "type": "object", "properties": { "elapsed_s": { @@ -69,17 +161,10 @@ "type": "null" } ] - }, - "event": { - "type": "string", - "const": "discover_complete" } - }, - "required": [ - "event" - ] + } }, - { + "RunStart": { "type": "object", "properties": { "elapsed_s": { @@ -91,17 +176,10 @@ "type": "null" } ] - }, - "event": { - "type": "string", - "const": "run_start" } - }, - "required": [ - "event" - ] + } }, - { + "CaseStart": { "type": "object", "properties": { "name": { @@ -116,18 +194,20 @@ "type": "null" } ] - }, - "event": { - "type": "string", - "const": "case_start" } }, "required": [ - "event", "name" ] }, - { + "RunStatus": { + "type": "string", + "enum": [ + "ignored", + "failed" + ] + }, + "CaseComplete": { "type": "object", "properties": { "name": { @@ -159,18 +239,13 @@ "type": "null" } ] - }, - "event": { - "type": "string", - "const": "case_complete" } }, "required": [ - "event", "name" ] }, - { + "RunComplete": { "type": "object", "properties": { "elapsed_s": { @@ -182,35 +257,8 @@ "type": "null" } ] - }, - "event": { - "type": "string", - "const": "run_complete" } - }, - "required": [ - "event" - ] - } - ], - "$defs": { - "Elapsed": { - "description": "Time elapsed since process start", - "type": "string" - }, - "RunMode": { - "type": "string", - "enum": [ - "test", - "bench" - ] - }, - "RunStatus": { - "type": "string", - "enum": [ - "ignored", - "failed" - ] + } } } } \ No newline at end of file diff --git a/crates/libtest-json/src/event.rs b/crates/libtest-json/src/event.rs index d33eec2..5eb9c9c 100644 --- a/crates/libtest-json/src/event.rs +++ b/crates/libtest-json/src/event.rs @@ -4,228 +4,393 @@ #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[cfg_attr(feature = "serde", serde(tag = "event"))] pub enum Event { - DiscoverStart { - #[cfg_attr( - feature = "serde", - serde(default, skip_serializing_if = "Option::is_none") - )] - elapsed_s: Option, - }, - DiscoverCase { - name: String, - #[cfg_attr( - feature = "serde", - serde(default, skip_serializing_if = "RunMode::is_default") - )] - mode: RunMode, - /// Whether selected to be run by the user - #[cfg_attr( - feature = "serde", - serde(default = "true_default", skip_serializing_if = "is_true") - )] - selected: bool, - #[cfg_attr( - feature = "serde", - serde(default, skip_serializing_if = "Option::is_none") - )] - elapsed_s: Option, - }, - DiscoverComplete { - #[cfg_attr( - feature = "serde", - serde(default, skip_serializing_if = "Option::is_none") - )] - elapsed_s: Option, - }, - RunStart { - #[cfg_attr( - feature = "serde", - serde(default, skip_serializing_if = "Option::is_none") - )] - elapsed_s: Option, - }, - CaseStart { - name: String, - #[cfg_attr( - feature = "serde", - serde(default, skip_serializing_if = "Option::is_none") - )] - elapsed_s: Option, - }, - CaseComplete { - name: String, - /// `None` means success - #[cfg_attr( - feature = "serde", - serde(default, skip_serializing_if = "Option::is_none") - )] - status: Option, - #[cfg_attr( - feature = "serde", - serde(default, skip_serializing_if = "Option::is_none") - )] - message: Option, - #[cfg_attr( - feature = "serde", - serde(default, skip_serializing_if = "Option::is_none") - )] - elapsed_s: Option, - }, - RunComplete { - #[cfg_attr( - feature = "serde", - serde(default, skip_serializing_if = "Option::is_none") - )] - elapsed_s: Option, - }, + DiscoverStart(DiscoverStart), + DiscoverCase(DiscoverCase), + DiscoverComplete(DiscoverComplete), + RunStart(RunStart), + CaseStart(CaseStart), + CaseComplete(CaseComplete), + RunComplete(RunComplete), } impl Event { + #[cfg(feature = "json")] + pub fn to_jsonline(&self) -> String { + match self { + Self::DiscoverStart(event) => event.to_jsonline(), + Self::DiscoverCase(event) => event.to_jsonline(), + Self::DiscoverComplete(event) => event.to_jsonline(), + Self::RunStart(event) => event.to_jsonline(), + Self::CaseStart(event) => event.to_jsonline(), + Self::CaseComplete(event) => event.to_jsonline(), + Self::RunComplete(event) => event.to_jsonline(), + } + } +} + +impl From for Event { + fn from(inner: DiscoverStart) -> Self { + Self::DiscoverStart(inner) + } +} + +impl From for Event { + fn from(inner: DiscoverCase) -> Self { + Self::DiscoverCase(inner) + } +} + +impl From for Event { + fn from(inner: DiscoverComplete) -> Self { + Self::DiscoverComplete(inner) + } +} + +impl From for Event { + fn from(inner: RunStart) -> Self { + Self::RunStart(inner) + } +} + +impl From for Event { + fn from(inner: CaseStart) -> Self { + Self::CaseStart(inner) + } +} + +impl From for Event { + fn from(inner: CaseComplete) -> Self { + Self::CaseComplete(inner) + } +} + +impl From for Event { + fn from(inner: RunComplete) -> Self { + Self::RunComplete(inner) + } +} + +#[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 DiscoverStart { + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none") + )] + pub elapsed_s: Option, +} + +impl DiscoverStart { #[cfg(feature = "json")] pub fn to_jsonline(&self) -> String { use json_write::JsonWrite as _; let mut buffer = String::new(); buffer.open_object().unwrap(); - match self { - Self::DiscoverStart { elapsed_s } => { - buffer.key("event").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value("discover_start").unwrap(); - - if let Some(elapsed_s) = elapsed_s { - buffer.val_sep().unwrap(); - buffer.key("elapsed_s").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(String::from(*elapsed_s)).unwrap(); - } - } - Self::DiscoverCase { - name, - mode, - selected, - elapsed_s, - } => { - buffer.key("event").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value("discover_case").unwrap(); - - buffer.val_sep().unwrap(); - buffer.key("name").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(name).unwrap(); - - if !mode.is_default() { - buffer.val_sep().unwrap(); - buffer.key("mode").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(mode.as_str()).unwrap(); - } - - if !selected { - buffer.val_sep().unwrap(); - buffer.key("selected").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(selected).unwrap(); - } - - if let Some(elapsed_s) = elapsed_s { - buffer.val_sep().unwrap(); - buffer.key("elapsed_s").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(String::from(*elapsed_s)).unwrap(); - } - } - Self::DiscoverComplete { elapsed_s } => { - buffer.key("event").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value("discover_complete").unwrap(); - - if let Some(elapsed_s) = elapsed_s { - buffer.val_sep().unwrap(); - buffer.key("elapsed_s").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(String::from(*elapsed_s)).unwrap(); - } - } - Self::RunStart { elapsed_s } => { - buffer.key("event").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value("run_start").unwrap(); - - if let Some(elapsed_s) = elapsed_s { - buffer.val_sep().unwrap(); - buffer.key("elapsed_s").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(String::from(*elapsed_s)).unwrap(); - } - } - Self::CaseStart { name, elapsed_s } => { - buffer.key("event").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value("case_start").unwrap(); - - buffer.val_sep().unwrap(); - buffer.key("name").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(name).unwrap(); - - if let Some(elapsed_s) = elapsed_s { - buffer.val_sep().unwrap(); - buffer.key("elapsed_s").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(String::from(*elapsed_s)).unwrap(); - } - } - Self::CaseComplete { - name, - status, - message, - elapsed_s, - } => { - buffer.key("event").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value("case_complete").unwrap(); - - buffer.val_sep().unwrap(); - buffer.key("name").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(name).unwrap(); - - if let Some(status) = status { - buffer.val_sep().unwrap(); - buffer.key("status").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(status.as_str()).unwrap(); - } - - if let Some(message) = message { - buffer.val_sep().unwrap(); - buffer.key("message").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(message).unwrap(); - } - - if let Some(elapsed_s) = elapsed_s { - buffer.val_sep().unwrap(); - buffer.key("elapsed_s").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(String::from(*elapsed_s)).unwrap(); - } - } - Self::RunComplete { elapsed_s } => { - buffer.key("event").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value("run_complete").unwrap(); - - if let Some(elapsed_s) = elapsed_s { - buffer.val_sep().unwrap(); - buffer.key("elapsed_s").unwrap(); - buffer.keyval_sep().unwrap(); - buffer.value(String::from(*elapsed_s)).unwrap(); - } - } + + buffer.key("event").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value("discover_start").unwrap(); + + if let Some(elapsed_s) = self.elapsed_s { + buffer.val_sep().unwrap(); + buffer.key("elapsed_s").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(String::from(elapsed_s)).unwrap(); } + + buffer.close_object().unwrap(); + + buffer + } +} + +#[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 DiscoverCase { + pub name: String, + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "RunMode::is_default") + )] + pub mode: RunMode, + /// Whether selected to be run by the user + #[cfg_attr( + feature = "serde", + serde(default = "true_default", skip_serializing_if = "is_true") + )] + pub selected: bool, + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none") + )] + pub elapsed_s: Option, +} + +impl DiscoverCase { + #[cfg(feature = "json")] + pub fn to_jsonline(&self) -> String { + use json_write::JsonWrite as _; + + let mut buffer = String::new(); + buffer.open_object().unwrap(); + + buffer.key("event").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value("discover_case").unwrap(); + + buffer.val_sep().unwrap(); + buffer.key("name").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(&self.name).unwrap(); + + if !self.mode.is_default() { + buffer.val_sep().unwrap(); + buffer.key("mode").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(self.mode.as_str()).unwrap(); + } + + if !self.selected { + buffer.val_sep().unwrap(); + buffer.key("selected").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(self.selected).unwrap(); + } + + if let Some(elapsed_s) = self.elapsed_s { + buffer.val_sep().unwrap(); + buffer.key("elapsed_s").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(String::from(elapsed_s)).unwrap(); + } + + buffer.close_object().unwrap(); + + buffer + } +} + +#[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 DiscoverComplete { + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none") + )] + pub elapsed_s: Option, +} + +impl DiscoverComplete { + #[cfg(feature = "json")] + pub fn to_jsonline(&self) -> String { + use json_write::JsonWrite as _; + + let mut buffer = String::new(); + buffer.open_object().unwrap(); + + buffer.key("event").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value("discover_complete").unwrap(); + + if let Some(elapsed_s) = self.elapsed_s { + buffer.val_sep().unwrap(); + buffer.key("elapsed_s").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(String::from(elapsed_s)).unwrap(); + } + + buffer.close_object().unwrap(); + + buffer + } +} + +#[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 RunStart { + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none") + )] + pub elapsed_s: Option, +} + +impl RunStart { + #[cfg(feature = "json")] + pub fn to_jsonline(&self) -> String { + use json_write::JsonWrite as _; + + let mut buffer = String::new(); + buffer.open_object().unwrap(); + + buffer.key("event").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value("run_start").unwrap(); + + if let Some(elapsed_s) = self.elapsed_s { + buffer.val_sep().unwrap(); + buffer.key("elapsed_s").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(String::from(elapsed_s)).unwrap(); + } + + buffer.close_object().unwrap(); + + buffer + } +} + +#[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 CaseStart { + pub name: String, + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none") + )] + pub elapsed_s: Option, +} + +impl CaseStart { + #[cfg(feature = "json")] + pub fn to_jsonline(&self) -> String { + use json_write::JsonWrite as _; + + let mut buffer = String::new(); + buffer.open_object().unwrap(); + + buffer.key("event").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value("case_start").unwrap(); + + buffer.val_sep().unwrap(); + buffer.key("name").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(&self.name).unwrap(); + + if let Some(elapsed_s) = self.elapsed_s { + buffer.val_sep().unwrap(); + buffer.key("elapsed_s").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(String::from(elapsed_s)).unwrap(); + } + + buffer.close_object().unwrap(); + + buffer + } +} + +#[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 CaseComplete { + pub name: String, + /// `None` means success + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none") + )] + pub status: Option, + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none") + )] + pub message: Option, + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none") + )] + pub elapsed_s: Option, +} + +impl CaseComplete { + #[cfg(feature = "json")] + pub fn to_jsonline(&self) -> String { + use json_write::JsonWrite as _; + + let mut buffer = String::new(); + buffer.open_object().unwrap(); + + buffer.key("event").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value("case_complete").unwrap(); + + buffer.val_sep().unwrap(); + buffer.key("name").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(&self.name).unwrap(); + + if let Some(status) = self.status { + buffer.val_sep().unwrap(); + buffer.key("status").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(status.as_str()).unwrap(); + } + + if let Some(message) = &self.message { + buffer.val_sep().unwrap(); + buffer.key("message").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(message).unwrap(); + } + + if let Some(elapsed_s) = self.elapsed_s { + buffer.val_sep().unwrap(); + buffer.key("elapsed_s").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(String::from(elapsed_s)).unwrap(); + } + + buffer.close_object().unwrap(); + + buffer + } +} + +#[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 RunComplete { + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none") + )] + pub elapsed_s: Option, +} + +impl RunComplete { + #[cfg(feature = "json")] + pub fn to_jsonline(&self) -> String { + use json_write::JsonWrite as _; + + let mut buffer = String::new(); + buffer.open_object().unwrap(); + + buffer.key("event").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value("run_complete").unwrap(); + + if let Some(elapsed_s) = self.elapsed_s { + buffer.val_sep().unwrap(); + buffer.key("elapsed_s").unwrap(); + buffer.keyval_sep().unwrap(); + buffer.value(String::from(elapsed_s)).unwrap(); + } + buffer.close_object().unwrap(); buffer diff --git a/crates/libtest-json/src/lib.rs b/crates/libtest-json/src/lib.rs index 5de41d3..2a20c9a 100644 --- a/crates/libtest-json/src/lib.rs +++ b/crates/libtest-json/src/lib.rs @@ -5,7 +5,7 @@ #![warn(clippy::print_stdout)] #![allow(clippy::todo)] -mod event; +pub mod event; pub use event::Elapsed; pub use event::Event; diff --git a/crates/libtest-json/tests/roundtrip.rs b/crates/libtest-json/tests/roundtrip.rs index f410252..4f33bbc 100644 --- a/crates/libtest-json/tests/roundtrip.rs +++ b/crates/libtest-json/tests/roundtrip.rs @@ -5,7 +5,8 @@ use snapbox::prelude::*; use snapbox::str; #[track_caller] -fn t(input: libtest_json::Event, snapshot: impl IntoData) { +fn t(input: impl Into, snapshot: impl IntoData) { + let input = input.into(); let actual_encoded = input.to_jsonline(); let expected_encoded = serde_json::to_string(&input).unwrap(); snapbox::assert_data_eq!(&actual_encoded, expected_encoded.raw()); @@ -17,11 +18,11 @@ fn t(input: libtest_json::Event, snapshot: impl IntoData) { #[test] fn discover_start() { t( - libtest_json::Event::DiscoverStart { elapsed_s: None }, + libtest_json::event::DiscoverStart { elapsed_s: None }, str![[r#"{"event":"discover_start"}"#]], ); t( - libtest_json::Event::DiscoverStart { + libtest_json::event::DiscoverStart { elapsed_s: Some(libtest_json::Elapsed(Default::default())), }, str![[r#"{"event":"discover_start","elapsed_s":"0"}"#]], @@ -31,7 +32,7 @@ fn discover_start() { #[test] fn discover_case() { t( - libtest_json::Event::DiscoverCase { + libtest_json::event::DiscoverCase { name: "Hello\tworld!".to_owned(), mode: libtest_json::RunMode::Test, selected: true, @@ -41,7 +42,7 @@ fn discover_case() { ); t( - libtest_json::Event::DiscoverCase { + libtest_json::event::DiscoverCase { name: "Hello\tworld!".to_owned(), mode: libtest_json::RunMode::Bench, selected: false, @@ -56,12 +57,12 @@ fn discover_case() { #[test] fn discover_complete() { t( - libtest_json::Event::DiscoverComplete { elapsed_s: None }, + libtest_json::event::DiscoverComplete { elapsed_s: None }, str![[r#"{"event":"discover_complete"}"#]], ); t( - libtest_json::Event::DiscoverComplete { + libtest_json::event::DiscoverComplete { elapsed_s: Some(libtest_json::Elapsed(Default::default())), }, str![[r#"{"event":"discover_complete","elapsed_s":"0"}"#]], @@ -71,11 +72,11 @@ fn discover_complete() { #[test] fn suite_start() { t( - libtest_json::Event::RunStart { elapsed_s: None }, + libtest_json::event::RunStart { elapsed_s: None }, str![[r#"{"event":"run_start"}"#]], ); t( - libtest_json::Event::RunStart { + libtest_json::event::RunStart { elapsed_s: Some(libtest_json::Elapsed(Default::default())), }, str![[r#"{"event":"run_start","elapsed_s":"0"}"#]], @@ -85,14 +86,14 @@ fn suite_start() { #[test] fn case_start() { t( - libtest_json::Event::CaseStart { + libtest_json::event::CaseStart { name: "Hello\tworld!".to_owned(), elapsed_s: None, }, str![[r#"{"event":"case_start","name":"Hello\tworld!"}"#]], ); t( - libtest_json::Event::CaseStart { + libtest_json::event::CaseStart { name: "Hello\tworld!".to_owned(), elapsed_s: Some(libtest_json::Elapsed(Default::default())), }, @@ -103,7 +104,7 @@ fn case_start() { #[test] fn case_complete() { t( - libtest_json::Event::CaseComplete { + libtest_json::event::CaseComplete { name: "Hello\tworld!".to_owned(), status: None, message: None, @@ -113,7 +114,7 @@ fn case_complete() { ); t( - libtest_json::Event::CaseComplete { + libtest_json::event::CaseComplete { name: "Hello\tworld!".to_owned(), status: Some(libtest_json::RunStatus::Ignored), message: Some("This\tfailed".to_owned()), @@ -128,12 +129,12 @@ fn case_complete() { #[test] fn suite_complete() { t( - libtest_json::Event::RunComplete { elapsed_s: None }, + libtest_json::event::RunComplete { elapsed_s: None }, str![[r#"{"event":"run_complete"}"#]], ); t( - libtest_json::Event::RunComplete { + libtest_json::event::RunComplete { elapsed_s: Some(libtest_json::Elapsed(Default::default())), }, str![[r#"{"event":"run_complete","elapsed_s":"0"}"#]], diff --git a/crates/libtest2-harness/src/harness.rs b/crates/libtest2-harness/src/harness.rs index c2a1017..49a4917 100644 --- a/crates/libtest2-harness/src/harness.rs +++ b/crates/libtest2-harness/src/harness.rs @@ -190,9 +190,12 @@ fn discover( cases: &mut Vec>, notifier: &mut dyn notify::Notifier, ) -> std::io::Result<()> { - notifier.notify(notify::Event::DiscoverStart { - elapsed_s: Some(notify::Elapsed(start.elapsed())), - })?; + notifier.notify( + notify::event::DiscoverStart { + elapsed_s: Some(notify::Elapsed(start.elapsed())), + } + .into(), + )?; let matches_filter = |case: &dyn Case, filter: &str| { let test_name = case.name(); @@ -227,19 +230,25 @@ fn discover( !opts.skip.is_empty() && opts.skip.iter().any(|sf| matches_filter(case.as_ref(), sf)); let retain_case = filtered_in && !filtered_out; retain_cases.push(retain_case); - notifier.notify(notify::Event::DiscoverCase { - name: case.name().to_owned(), - mode: RunMode::Test, - selected: retain_case, - elapsed_s: Some(notify::Elapsed(start.elapsed())), - })?; + notifier.notify( + notify::event::DiscoverCase { + name: case.name().to_owned(), + mode: RunMode::Test, + selected: retain_case, + elapsed_s: Some(notify::Elapsed(start.elapsed())), + } + .into(), + )?; } let mut retain_cases = retain_cases.into_iter(); cases.retain(|_| retain_cases.next().unwrap()); - notifier.notify(notify::Event::DiscoverComplete { - elapsed_s: Some(notify::Elapsed(start.elapsed())), - })?; + notifier.notify( + notify::event::DiscoverComplete { + elapsed_s: Some(notify::Elapsed(start.elapsed())), + } + .into(), + )?; Ok(()) } @@ -250,9 +259,12 @@ fn run( cases: Vec>, notifier: &mut dyn notify::Notifier, ) -> std::io::Result { - notifier.notify(notify::Event::RunStart { - elapsed_s: Some(notify::Elapsed(start.elapsed())), - })?; + notifier.notify( + notify::event::RunStart { + elapsed_s: Some(notify::Elapsed(start.elapsed())), + } + .into(), + )?; if opts.no_capture { todo!("`--no-capture` is not yet supported"); @@ -298,17 +310,10 @@ fn run( } impl RunningTest { - fn join(self, event: &mut notify::Event) { - if self.join_handle.join().is_err() { - if let notify::Event::CaseComplete { - status, message, .. - } = event - { - if status.is_none() { - *status = Some(notify::RunStatus::Failed); - *message = Some("panicked after reporting success".to_owned()); - } - } + fn join(self, event: &mut notify::event::CaseComplete) { + if self.join_handle.join().is_err() && event.status.is_none() { + event.status = Some(notify::RunStatus::Failed); + event.message = Some("panicked after reporting success".to_owned()); } } } @@ -375,9 +380,9 @@ fn run( } let mut event = rx.recv().unwrap(); - if let notify::Event::CaseComplete { name, .. } = &event { - let running_test = running_tests.remove(name).unwrap(); - running_test.join(&mut event); + if let notify::Event::CaseComplete(event) = &mut event { + let running_test = running_tests.remove(&event.name).unwrap(); + running_test.join(event); pending -= 1; } notifier.notify(event)?; @@ -398,9 +403,12 @@ fn run( } } - notifier.notify(notify::Event::RunComplete { - elapsed_s: Some(notify::Elapsed(start.elapsed())), - })?; + notifier.notify( + notify::event::RunComplete { + elapsed_s: Some(notify::Elapsed(start.elapsed())), + } + .into(), + )?; Ok(success) } @@ -411,10 +419,13 @@ fn run_case( state: &State, notifier: &mut dyn notify::Notifier, ) -> std::io::Result { - notifier.notify(notify::Event::CaseStart { - name: case.name().to_owned(), - elapsed_s: Some(notify::Elapsed(start.elapsed())), - })?; + notifier.notify( + notify::event::CaseStart { + name: case.name().to_owned(), + elapsed_s: Some(notify::Elapsed(start.elapsed())), + } + .into(), + )?; let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { __rust_begin_short_backtrace(|| case.run(state)) @@ -438,12 +449,15 @@ fn run_case( let err = outcome.as_ref().err(); let status = err.map(|e| e.status()); let message = err.and_then(|e| e.cause().map(|c| c.to_string())); - notifier.notify(notify::Event::CaseComplete { - name: case.name().to_owned(), - status, - message, - elapsed_s: Some(notify::Elapsed(start.elapsed())), - })?; + notifier.notify( + notify::event::CaseComplete { + name: case.name().to_owned(), + status, + message, + elapsed_s: Some(notify::Elapsed(start.elapsed())), + } + .into(), + )?; Ok(status != Some(notify::RunStatus::Failed)) } diff --git a/crates/libtest2-harness/src/notify/mod.rs b/crates/libtest2-harness/src/notify/mod.rs index c74930d..d387368 100644 --- a/crates/libtest2-harness/src/notify/mod.rs +++ b/crates/libtest2-harness/src/notify/mod.rs @@ -24,8 +24,6 @@ pub(crate) trait Notifier { fn notify(&mut self, event: Event) -> std::io::Result<()>; } -pub(crate) use libtest_json::Elapsed; -pub(crate) use libtest_json::Event; -pub(crate) use libtest_json::RunStatus; +pub(crate) use libtest_json::*; pub use libtest_json::RunMode; diff --git a/crates/libtest2-harness/src/notify/pretty.rs b/crates/libtest2-harness/src/notify/pretty.rs index 017d527..72f411e 100644 --- a/crates/libtest2-harness/src/notify/pretty.rs +++ b/crates/libtest2-harness/src/notify/pretty.rs @@ -31,35 +31,43 @@ impl super::Notifier for PrettyRunNotifier { fn notify(&mut self, event: Event) -> std::io::Result<()> { self.summary.notify(event.clone())?; match event { - Event::DiscoverStart { .. } => {} - Event::DiscoverCase { name, selected, .. } => { - if selected { - self.name_width = name.len().max(self.name_width); + Event::DiscoverStart(_) => {} + Event::DiscoverCase(inner) => { + if inner.selected { + self.name_width = inner.name.len().max(self.name_width); } } - Event::DiscoverComplete { .. } => {} - Event::RunStart { .. } => { + Event::DiscoverComplete(_) => {} + Event::RunStart(_) => { self.summary.write_start(&mut self.writer)?; } - Event::CaseStart { name, .. } => { + Event::CaseStart(inner) => { if !self.is_multithreaded { - write!(self.writer, "test {: <1$} ... ", name, self.name_width)?; + write!( + self.writer, + "test {: <1$} ... ", + inner.name, self.name_width + )?; self.writer.flush()?; } } - Event::CaseComplete { name, status, .. } => { - let (s, style) = match status { + Event::CaseComplete(inner) => { + let (s, style) = match inner.status { Some(RunStatus::Ignored) => ("ignored", IGNORED), Some(RunStatus::Failed) => ("FAILED", FAILED), None => ("ok", OK), }; if self.is_multithreaded { - write!(self.writer, "test {: <1$} ... ", name, self.name_width)?; + write!( + self.writer, + "test {: <1$} ... ", + inner.name, self.name_width + )?; } writeln!(self.writer, "{style}{s}{style:#}")?; } - Event::RunComplete { .. } => { + Event::RunComplete(_) => { self.summary.write_complete(&mut self.writer)?; } } diff --git a/crates/libtest2-harness/src/notify/summary.rs b/crates/libtest2-harness/src/notify/summary.rs index 223e73d..43a77ed 100644 --- a/crates/libtest2-harness/src/notify/summary.rs +++ b/crates/libtest2-harness/src/notify/summary.rs @@ -87,36 +87,31 @@ impl Summary { impl super::Notifier for Summary { fn notify(&mut self, event: Event) -> std::io::Result<()> { match event { - Event::DiscoverStart { .. } => {} - Event::DiscoverCase { selected, .. } => { - if selected { + Event::DiscoverStart(_) => {} + Event::DiscoverCase(inner) => { + if inner.selected { self.num_run += 1; } else { self.num_filtered_out += 1; } } - Event::DiscoverComplete { .. } => {} - Event::RunStart { .. } => {} - Event::CaseStart { .. } => {} - Event::CaseComplete { - name, - status, - message, - .. - } => match status { + Event::DiscoverComplete(_) => {} + Event::RunStart(_) => {} + Event::CaseStart(_) => {} + Event::CaseComplete(inner) => match inner.status { Some(RunStatus::Ignored) => { self.num_ignored += 1; } Some(RunStatus::Failed) => { self.num_failed += 1; - self.failures.insert(name, message); + self.failures.insert(inner.name, inner.message); } None => { self.num_passed += 1; } }, - Event::RunComplete { elapsed_s, .. } => { - self.elapsed_s = elapsed_s; + Event::RunComplete(inner) => { + self.elapsed_s = inner.elapsed_s; } } Ok(()) diff --git a/crates/libtest2-harness/src/notify/terse.rs b/crates/libtest2-harness/src/notify/terse.rs index 8821a9d..d67a9cc 100644 --- a/crates/libtest2-harness/src/notify/terse.rs +++ b/crates/libtest2-harness/src/notify/terse.rs @@ -19,28 +19,24 @@ impl TerseListNotifier { impl super::Notifier for TerseListNotifier { fn notify(&mut self, event: Event) -> std::io::Result<()> { match event { - Event::DiscoverStart { .. } => {} - Event::DiscoverCase { - name, - mode, - selected, - .. - } => { - if selected { - let mode = mode.as_str(); + Event::DiscoverStart(_) => {} + Event::DiscoverCase(inner) => { + if inner.selected { + let name = &inner.name; + let mode = inner.mode.as_str(); writeln!(self.writer, "{name}: {mode}")?; self.tests += 1; } } - Event::DiscoverComplete { .. } => { + Event::DiscoverComplete(_) => { writeln!(self.writer)?; writeln!(self.writer, "{} tests", self.tests)?; writeln!(self.writer)?; } - Event::RunStart { .. } => {} - Event::CaseStart { .. } => {} - Event::CaseComplete { .. } => {} - Event::RunComplete { .. } => {} + Event::RunStart(_) => {} + Event::CaseStart(_) => {} + Event::CaseComplete(_) => {} + Event::RunComplete(_) => {} } Ok(()) } @@ -65,15 +61,15 @@ impl super::Notifier for TerseRunNotifier { fn notify(&mut self, event: Event) -> std::io::Result<()> { self.summary.notify(event.clone())?; match event { - Event::DiscoverStart { .. } => {} - Event::DiscoverCase { .. } => {} - Event::DiscoverComplete { .. } => {} - Event::RunStart { .. } => { + Event::DiscoverStart(_) => {} + Event::DiscoverCase(_) => {} + Event::DiscoverComplete(_) => {} + Event::RunStart(_) => { self.summary.write_start(&mut self.writer)?; } - Event::CaseStart { .. } => {} - Event::CaseComplete { status, .. } => { - let (c, style) = match status { + Event::CaseStart(_) => {} + Event::CaseComplete(inner) => { + let (c, style) = match inner.status { Some(RunStatus::Ignored) => ('i', IGNORED), Some(RunStatus::Failed) => ('F', FAILED), None => ('.', OK), @@ -81,7 +77,7 @@ impl super::Notifier for TerseRunNotifier { write!(self.writer, "{style}{c}{style:#}")?; self.writer.flush()?; } - Event::RunComplete { .. } => { + Event::RunComplete(_) => { self.summary.write_complete(&mut self.writer)?; } }