diff --git a/src/output/macros.rs b/src/output/macros.rs index 39ab47a..9f49d03 100644 --- a/src/output/macros.rs +++ b/src/output/macros.rs @@ -27,8 +27,7 @@ /// /// use ocptv::ocptv_error; /// -/// let test_run = TestRun::new("run_name", "my_dut", "1.0"); -/// test_run.start().await?; +/// let test_run = TestRun::new("run_name", "my_dut", "1.0").start().await?; /// ocptv_error!(test_run, "symptom"); /// test_run.end(TestStatus::Complete, TestResult::Pass).await?; /// @@ -44,8 +43,7 @@ /// /// use ocptv::ocptv_error; /// -/// let test_run = TestRun::new("run_name", "my_dut", "1.0"); -/// test_run.start().await?; +/// let test_run = TestRun::new("run_name", "my_dut", "1.0").start().await?; /// ocptv_error!(test_run, "symptom", "Error message"); /// test_run.end(TestStatus::Complete, TestResult::Pass).await?; /// @@ -54,7 +52,7 @@ /// ``` #[macro_export] macro_rules! ocptv_error { - ($runner:expr , $symptom:expr, $msg:expr) => { + ($runner:expr, $symptom:expr, $msg:expr) => { async { $runner .error_with_details( @@ -99,8 +97,7 @@ macro_rules! ocptv_error { /// /// use ocptv::ocptv_log_debug; /// -/// let test_run = TestRun::new("run_name", "my_dut", "1.0"); -/// test_run.start().await?; +/// let test_run = TestRun::new("run_name", "my_dut", "1.0").start().await?; /// ocptv_log_debug!(test_run, "Log message"); /// test_run.end(TestStatus::Complete, TestResult::Pass).await?; /// @@ -210,23 +207,25 @@ mod tests { "symptom": "symptom" } }, - "sequenceNumber": 1 + "sequenceNumber": 3 }); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); let dut = DutInfo::builder("dut_id").build(); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - ocptv_error!(test_run, "symptom", "Error message").await?; + ocptv_error!(run, "symptom", "Error message").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing error log message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -252,23 +251,25 @@ mod tests { "symptom": "symptom" } }, - "sequenceNumber": 1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - ocptv_error!(test_run, "symptom").await?; + ocptv_error!(run, "symptom").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing error log message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -295,23 +296,25 @@ mod tests { "severity": "DEBUG" } }, - "sequenceNumber":1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - ocptv_log_debug!(test_run, "log message").await?; + ocptv_log_debug!(run, "log message").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing the log message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -338,23 +341,25 @@ mod tests { "severity": "INFO" } }, - "sequenceNumber": 1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - ocptv_log_info!(test_run, "log message").await?; + ocptv_log_info!(run, "log message").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing the log message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -382,23 +387,25 @@ mod tests { "severity": "WARNING" } }, - "sequenceNumber": 1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - ocptv_log_warning!(test_run, "log message").await?; + ocptv_log_warning!(run, "log message").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing the log message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -425,23 +432,25 @@ mod tests { "severity": "ERROR" } }, - "sequenceNumber":1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - ocptv_log_error!(test_run, "log message").await?; + ocptv_log_error!(run, "log message").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing the error message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -468,23 +477,25 @@ mod tests { "severity": "FATAL" } }, - "sequenceNumber": 1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - ocptv_log_fatal!(test_run, "log message").await?; + ocptv_log_fatal!(run, "log message").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing the error message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -511,25 +522,28 @@ mod tests { "symptom":"symptom" } }, - "sequenceNumber": 1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - let step = test_run.step("step_name")?; + let step = run.step("step_name")?; ocptv_error!(step, "symptom", "Error message").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing the error message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -555,25 +569,27 @@ mod tests { "symptom": "symptom" } }, - "sequenceNumber": 1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - let step = test_run.step("step_name")?; + let step = run.step("step_name")?; ocptv_error!(step, "symptom").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing the error message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -600,24 +616,26 @@ mod tests { "severity": "DEBUG" } }, - "sequenceNumber": 1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - let step = test_run.step("step_name")?; + let step = run.step("step_name")?; ocptv_log_debug!(step, "log message").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing the log message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -644,24 +662,26 @@ mod tests { "severity": "INFO" } }, - "sequenceNumber": 1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - let step = test_run.step("step_name")?; + let step = run.step("step_name")?; ocptv_log_info!(step, "log message").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing the log message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -688,24 +708,26 @@ mod tests { "severity":"WARNING" } }, - "sequenceNumber": 1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - let step = test_run.step("step_name")?; + let step = run.step("step_name")?; ocptv_log_warning!(step, "log message").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing the log message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -732,24 +754,26 @@ mod tests { "severity": "ERROR" } }, - "sequenceNumber": 1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - let step = test_run.step("step_name")?; + let step = run.step("step_name")?; ocptv_log_error!(step, "log message").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing the log message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); @@ -776,24 +800,26 @@ mod tests { "severity": "FATAL" } }, - "sequenceNumber": 1 + "sequenceNumber": 3 }); let dut = DutInfo::builder("dut_id").build(); let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::builder("run_name", &dut, "1.0") + let run = TestRun::builder("run_name", &dut, "1.0") .config(Config::builder().with_buffer_output(buffer.clone()).build()) - .build(); + .build() + .start() + .await?; - let step = test_run.step("step_name")?; + let step = run.step("step_name")?; ocptv_log_fatal!(step, "log message").await?; let actual = serde_json::from_str::( - buffer + &buffer .lock() .await - .first() - .ok_or(anyhow!("Buffer is empty"))?, + .first_chunk::<3>() + .ok_or(anyhow!("Buffer is missing the log message"))?[2], )?; assert_json_include!(actual: actual.clone(), expected: &expected); diff --git a/src/output/runner.rs b/src/output/runner.rs index 976b1f4..547d644 100644 --- a/src/output/runner.rs +++ b/src/output/runner.rs @@ -109,10 +109,8 @@ impl TestState { } /// The main diag test run. -/// This object describes a single run instance of the diag, and therefore drives the test session. /// -/// ref: https://github.com/opencomputeproject/ocp-diag-core/tree/main/json_spec#testrunstart - +/// This object describes a single run instance of the diag, and therefore drives the test session. pub struct TestRun { name: String, version: String, @@ -145,7 +143,7 @@ impl TestRun { /// ```rust /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0"); /// ``` pub fn new(name: &str, dut_id: &str, version: &str) -> TestRun { let dut = objects::DutInfo::new(dut_id); @@ -163,13 +161,13 @@ impl TestRun { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0"); + /// run.start().await?; /// /// # Ok::<(), WriterError>(()) /// # }); /// ``` - pub async fn start(&self) -> Result<(), emitters::WriterError> { + pub async fn start(self) -> Result { let version = objects::SchemaVersion::new(); self.state .lock() @@ -199,9 +197,178 @@ impl TestRun { .emitter .emit(&start.to_artifact()) .await?; - Ok(()) + + Ok(StartedTestRun { run: self }) + } + + // disabling this for the moment so we don't publish api that's unusable. + // see: https://github.com/rust-lang/rust/issues/70263 + // + // /// Builds a scope in the [`TestRun`] object, taking care of starting and + // /// ending it. View [`TestRun::start`] and [`TestRun::end`] methods. + // /// After the scope is constructed, additional objects may be added to it. + // /// This is the preferred usage for the [`TestRun`], since it guarantees + // /// all the messages are emitted between the start and end messages, the order + // /// is respected and no messages is lost. + // /// + // /// # Examples + // /// + // /// ```rust + // /// # tokio_test::block_on(async { + // /// # use ocptv::output::*; + // /// + // /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0"); + // /// run.scope(|r| async { + // /// r.log(LogSeverity::Info, "First message").await?; + // /// Ok(TestRunOutcome { + // /// status: TestStatus::Complete, + // /// result: TestResult::Pass, + // /// }) + // /// }).await?; + // /// + // /// # Ok::<(), WriterError>(()) + // /// # }); + // /// ``` + // pub async fn scope(self, func: F) -> Result<(), emitters::WriterError> + // where + // R: Future>, + // for<'a> F: Fut2<'a, R>, + // { + // let run = self.start().await?; + // let outcome = func(&run).await?; + // run.end(outcome.status, outcome.result).await?; + + // Ok(()) + // } +} + +/// Builder for the [`TestRun`] object. +pub struct TestRunBuilder { + name: String, + dut: objects::DutInfo, + version: String, + parameters: Map, + command_line: String, + metadata: Option>, + config: Option, +} + +impl TestRunBuilder { + pub fn new(name: &str, dut: &objects::DutInfo, version: &str) -> Self { + Self { + name: name.to_string(), + dut: dut.clone(), + version: version.to_string(), + parameters: Map::new(), + command_line: env::args().collect::>()[1..].join(" "), + metadata: None, + config: None, + } + } + + /// Adds a user defined parameter to the future [`TestRun`] object. + /// + /// # Examples + /// + /// ```rust + /// # use ocptv::output::*; + /// + /// let dut = DutInfo::builder("dut_id").build(); + /// let run = TestRunBuilder::new("run_name", &dut, "1.0") + /// .add_parameter("param1", "value1".into()) + /// .build(); + /// ``` + pub fn add_parameter(mut self, key: &str, value: Value) -> TestRunBuilder { + self.parameters.insert(key.to_string(), value.clone()); + self + } + + /// Adds the command line used to run the test session to the future + /// [`TestRun`] object. + /// + /// # Examples + /// + /// ```rust + /// # use ocptv::output::*; + /// + /// let dut = DutInfo::builder("dut_id").build(); + /// let run = TestRunBuilder::new("run_name", &dut, "1.0") + /// .command_line("my_diag --arg value") + /// .build(); + /// ``` + pub fn command_line(mut self, cmd: &str) -> TestRunBuilder { + self.command_line = cmd.to_string(); + self + } + + /// Adds the configuration for the test session to the future [`TestRun`] object + /// + /// # Examples + /// + /// ```rust + /// use ocptv::output::{Config, TestRunBuilder, DutInfo}; + /// + /// let dut = DutInfo::builder("dut_id").build(); + /// let run = TestRunBuilder::new("run_name", &dut, "1.0") + /// .config(Config::builder().build()) + /// .build(); + /// ``` + pub fn config(mut self, value: Config) -> TestRunBuilder { + self.config = Some(value); + self + } + + /// Adds user defined metadata to the future [`TestRun`] object + /// + /// # Examples + /// + /// ```rust + /// # use ocptv::output::*; + /// + /// let dut = DutInfo::builder("dut_id").build(); + /// let run = TestRunBuilder::new("run_name", &dut, "1.0") + /// .add_metadata("meta1", "value1".into()) + /// .build(); + /// ``` + pub fn add_metadata(mut self, key: &str, value: Value) -> TestRunBuilder { + self.metadata = match self.metadata { + Some(mut metadata) => { + metadata.insert(key.to_string(), value.clone()); + Some(metadata) + } + None => { + let mut metadata = Map::new(); + metadata.insert(key.to_string(), value.clone()); + Some(metadata) + } + }; + self + } + + pub fn build(self) -> TestRun { + let config = self.config.unwrap_or(Config::builder().build()); + let emitter = emitters::JsonEmitter::new(config.timezone, config.writer); + let state = TestState::new(emitter); + TestRun { + name: self.name, + dut: self.dut, + version: self.version, + parameters: self.parameters, + command_line: self.command_line, + metadata: self.metadata, + state: Arc::new(Mutex::new(state)), + } } +} + +/// A test run that was started. +/// +/// ref: https://github.com/opencomputeproject/ocp-diag-core/tree/main/json_spec#testrunstart +pub struct StartedTestRun { + run: TestRun, +} +impl StartedTestRun { /// Ends the test run. /// /// ref: https://github.com/opencomputeproject/ocp-diag-core/tree/main/json_spec#testrunend @@ -212,9 +379,8 @@ impl TestRun { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; - /// test_run.end(TestStatus::Complete, TestResult::Pass).await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// run.end(TestStatus::Complete, TestResult::Pass).await?; /// /// # Ok::<(), WriterError>(()) /// # }); @@ -228,48 +394,10 @@ impl TestRun { .status(status) .result(result) .build(); - self.state - .lock() - .await - .emitter - .emit(&end.to_artifact()) - .await?; - Ok(()) - } - /// Builds a scope in the [`TestRun`] object, taking care of starting and - /// ending it. View [`TestRun::start`] and [`TestRun::end`] methods. - /// After the scope is constructed, additional objects may be added to it. - /// This is the preferred usage for the [`TestRun`], since it guarantees - /// all the messages are emitted between the start and end messages, the order - /// is respected and no messages is lost. - /// - /// # Examples - /// - /// ```rust - /// # tokio_test::block_on(async { - /// # use ocptv::output::*; - /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.scope(|r| async { - /// r.log(LogSeverity::Info, "First message").await?; - /// Ok(TestRunOutcome { - /// status: TestStatus::Complete, - /// result: TestResult::Pass, - /// }) - /// }).await?; - /// - /// # Ok::<(), WriterError>(()) - /// # }); - /// ``` - pub async fn scope<'a, F, R>(&'a self, func: F) -> Result<(), emitters::WriterError> - where - R: Future>, - F: std::ops::FnOnce(&'a TestRun) -> R, - { - self.start().await?; - let outcome = func(self).await?; - self.end(outcome.status, outcome.result).await?; + let emitter = &self.run.state.lock().await.emitter; + + emitter.emit(&end.to_artifact()).await?; Ok(()) } @@ -285,13 +413,12 @@ impl TestRun { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; - /// test_run.log( + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// run.log( /// LogSeverity::Info, /// "This is a log message with INFO severity", /// ).await?; - /// test_run.end(TestStatus::Complete, TestResult::Pass).await?; + /// run.end(TestStatus::Complete, TestResult::Pass).await?; /// /// # Ok::<(), WriterError>(()) /// # }); @@ -302,10 +429,10 @@ impl TestRun { msg: &str, ) -> Result<(), emitters::WriterError> { let log = objects::Log::builder(msg).severity(severity).build(); - self.state - .lock() - .await - .emitter + + let emitter = &self.run.state.lock().await.emitter; + + emitter .emit(&log.to_artifact(objects::ArtifactContext::TestRun)) .await?; Ok(()) @@ -322,24 +449,22 @@ impl TestRun { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; - /// test_run.log_with_details( + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// run.log_with_details( /// &Log::builder("This is a log message with INFO severity") /// .severity(LogSeverity::Info) /// .source("file", 1) /// .build(), /// ).await?; - /// test_run.end(TestStatus::Complete, TestResult::Pass).await?; + /// run.end(TestStatus::Complete, TestResult::Pass).await?; /// /// # Ok::<(), WriterError>(()) /// # }); /// ``` pub async fn log_with_details(&self, log: &objects::Log) -> Result<(), emitters::WriterError> { - self.state - .lock() - .await - .emitter + let emitter = &self.run.state.lock().await.emitter; + + emitter .emit(&log.to_artifact(objects::ArtifactContext::TestRun)) .await?; Ok(()) @@ -356,20 +481,18 @@ impl TestRun { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; - /// test_run.error("symptom").await?; - /// test_run.end(TestStatus::Complete, TestResult::Pass).await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// run.error("symptom").await?; + /// run.end(TestStatus::Complete, TestResult::Pass).await?; /// /// # Ok::<(), WriterError>(()) /// # }); /// ``` pub async fn error(&self, symptom: &str) -> Result<(), emitters::WriterError> { let error = objects::Error::builder(symptom).build(); - self.state - .lock() - .await - .emitter + let emitter = &self.run.state.lock().await.emitter; + + emitter .emit(&error.to_artifact(objects::ArtifactContext::TestRun)) .await?; Ok(()) @@ -387,10 +510,9 @@ impl TestRun { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; - /// test_run.error_with_msg("symptom", "error messasge").await?; - /// test_run.end(TestStatus::Complete, TestResult::Pass).await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// run.error_with_msg("symptom", "error messasge").await?; + /// run.end(TestStatus::Complete, TestResult::Pass).await?; /// /// # Ok::<(), WriterError>(()) /// # }); @@ -401,10 +523,9 @@ impl TestRun { msg: &str, ) -> Result<(), emitters::WriterError> { let error = objects::Error::builder(symptom).message(msg).build(); - self.state - .lock() - .await - .emitter + let emitter = &self.run.state.lock().await.emitter; + + emitter .emit(&error.to_artifact(objects::ArtifactContext::TestRun)) .await?; Ok(()) @@ -421,16 +542,15 @@ impl TestRun { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; - /// test_run.error_with_details( + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// run.error_with_details( /// &Error::builder("symptom") /// .message("Error message") /// .source("file", 1) /// .add_software_info(&SoftwareInfo::builder("id", "name").build()) /// .build(), /// ).await?; - /// test_run.end(TestStatus::Complete, TestResult::Pass).await?; + /// run.end(TestStatus::Complete, TestResult::Pass).await?; /// /// # Ok::<(), WriterError>(()) /// # }); @@ -439,136 +559,16 @@ impl TestRun { &self, error: &objects::Error, ) -> Result<(), emitters::WriterError> { - self.state - .lock() - .await - .emitter + let emitter = &self.run.state.lock().await.emitter; + + emitter .emit(&error.to_artifact(objects::ArtifactContext::TestRun)) .await?; Ok(()) } pub fn step(&self, name: &str) -> Result { - Ok(TestStep::new(name, self.state.clone())) - } -} - -/// Builder for the [`TestRun`] object. -pub struct TestRunBuilder { - name: String, - dut: objects::DutInfo, - version: String, - parameters: Map, - command_line: String, - metadata: Option>, - config: Option, -} - -impl TestRunBuilder { - pub fn new(name: &str, dut: &objects::DutInfo, version: &str) -> Self { - Self { - name: name.to_string(), - dut: dut.clone(), - version: version.to_string(), - parameters: Map::new(), - command_line: env::args().collect::>()[1..].join(" "), - metadata: None, - config: None, - } - } - - /// Adds a user defined parameter to the future [`TestRun`] object. - /// - /// # Examples - /// - /// ```rust - /// # use ocptv::output::*; - /// - /// let dut = DutInfo::builder("dut_id").build(); - /// let test_run = TestRunBuilder::new("run_name", &dut, "1.0") - /// .add_parameter("param1", "value1".into()) - /// .build(); - /// ``` - pub fn add_parameter(mut self, key: &str, value: Value) -> TestRunBuilder { - self.parameters.insert(key.to_string(), value.clone()); - self - } - - /// Adds the command line used to run the test session to the future - /// [`TestRun`] object. - /// - /// # Examples - /// - /// ```rust - /// # use ocptv::output::*; - /// - /// let dut = DutInfo::builder("dut_id").build(); - /// let test_run = TestRunBuilder::new("run_name", &dut, "1.0") - /// .command_line("my_diag --arg value") - /// .build(); - /// ``` - pub fn command_line(mut self, cmd: &str) -> TestRunBuilder { - self.command_line = cmd.to_string(); - self - } - - /// Adds the configuration for the test session to the future [`TestRun`] object - /// - /// # Examples - /// - /// ```rust - /// use ocptv::output::{Config, TestRunBuilder, DutInfo}; - /// - /// let dut = DutInfo::builder("dut_id").build(); - /// let test_run = TestRunBuilder::new("run_name", &dut, "1.0") - /// .config(Config::builder().build()) - /// .build(); - /// ``` - pub fn config(mut self, value: Config) -> TestRunBuilder { - self.config = Some(value); - self - } - - /// Adds user defined metadata to the future [`TestRun`] object - /// - /// # Examples - /// - /// ```rust - /// # use ocptv::output::*; - /// - /// let dut = DutInfo::builder("dut_id").build(); - /// let test_run = TestRunBuilder::new("run_name", &dut, "1.0") - /// .add_metadata("meta1", "value1".into()) - /// .build(); - /// ``` - pub fn add_metadata(mut self, key: &str, value: Value) -> TestRunBuilder { - self.metadata = match self.metadata { - Some(mut metadata) => { - metadata.insert(key.to_string(), value.clone()); - Some(metadata) - } - None => { - let mut metadata = Map::new(); - metadata.insert(key.to_string(), value.clone()); - Some(metadata) - } - }; - self - } - - pub fn build(self) -> TestRun { - let config = self.config.unwrap_or(Config::builder().build()); - let emitter = emitters::JsonEmitter::new(config.timezone, config.writer); - let state = TestState::new(emitter); - TestRun { - name: self.name, - dut: self.dut, - version: self.version, - parameters: self.parameters, - command_line: self.command_line, - metadata: self.metadata, - state: Arc::new(Mutex::new(state)), - } + Ok(TestStep::new(name, self.run.state.clone())) } } @@ -600,10 +600,9 @@ impl TestStep { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; /// - /// let step = test_run.step("step_name")?; + /// let step = run.step("step_name")?; /// step.start().await?; /// /// # Ok::<(), WriterError>(()) @@ -630,10 +629,9 @@ impl TestStep { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; /// - /// let step = test_run.step("step_name")?; + /// let step = run.step("step_name")?; /// step.start().await?; /// step.end(TestStatus::Complete).await?; /// @@ -664,10 +662,9 @@ impl TestStep { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; /// - /// let step = test_run.step("first step")?; + /// let step = run.step("first step")?; /// step.scope(|s| async { /// s.log( /// LogSeverity::Info, @@ -702,10 +699,9 @@ impl TestStep { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; /// - /// let step = test_run.step("step_name")?; + /// let step = run.step("step_name")?; /// step.start().await?; /// step.log( /// LogSeverity::Info, @@ -724,10 +720,9 @@ impl TestStep { /// /// use ocptv::ocptv_log_info; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; /// - /// let step = test_run.step("step_name")?; + /// let step = run.step("step_name")?; /// step.start().await?; /// ocptv_log_info!(step, "This is a log message with INFO severity").await?; /// step.end(TestStatus::Complete).await?; @@ -761,10 +756,9 @@ impl TestStep { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; /// - /// let step = test_run.step("step_name")?; + /// let step = run.step("step_name")?; /// step.start().await?; /// step.log_with_details( /// &Log::builder("This is a log message with INFO severity") @@ -798,8 +792,9 @@ impl TestStep { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// let step = test_run.step("step_name")?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// + /// let step = run.step("step_name")?; /// step.start().await?; /// step.error("symptom").await?; /// step.end(TestStatus::Complete).await?; @@ -816,10 +811,9 @@ impl TestStep { /// /// use ocptv::ocptv_error; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; /// - /// let step = test_run.step("step_name")?; + /// let step = run.step("step_name")?; /// step.start().await?; /// ocptv_error!(step, "symptom").await?; /// step.end(TestStatus::Complete).await?; @@ -850,8 +844,9 @@ impl TestStep { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// let step = test_run.step("step_name")?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// + /// let step = run.step("step_name")?; /// step.start().await?; /// step.error_with_msg("symptom", "error message").await?; /// step.end(TestStatus::Complete).await?; @@ -868,9 +863,9 @@ impl TestStep { /// /// use ocptv::ocptv_error; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; - /// let step = test_run.step("step_name")?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// + /// let step = run.step("step_name")?; /// step.start().await?; /// ocptv_error!(step, "symptom", "error message").await?; /// step.end(TestStatus::Complete).await?; @@ -904,8 +899,9 @@ impl TestStep { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// let step = test_run.step("step_name")?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// + /// let step = run.step("step_name")?; /// step.start().await?; /// step.error_with_details( /// &Error::builder("symptom") @@ -942,8 +938,9 @@ impl TestStep { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// let step = test_run.step("step_name")?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// + /// let step = run.step("step_name")?; /// step.start().await?; /// step.add_measurement("name", 50.into()).await?; /// step.end(TestStatus::Complete).await?; @@ -978,9 +975,8 @@ impl TestStep { /// # use ocptv::output::*; /// /// let hwinfo = HardwareInfo::builder("id", "fan").build(); - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// let step = test_run.step("step_name")?; - /// step.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// let step = run.step("step_name")?; /// /// let measurement = Measurement::builder("name", 5000.into()) /// .hardware_info(&hwinfo) @@ -1019,8 +1015,8 @@ impl TestStep { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// let step = test_run.step("step_name")?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// let step = run.step("step_name")?; /// step.start().await?; /// let series = step.measurement_series("name"); /// @@ -1049,8 +1045,8 @@ impl TestStep { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// let step = test_run.step("step_name")?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; + /// let step = run.step("step_name")?; /// step.start().await?; /// let series = /// step.measurement_series_with_details(MeasurementSeriesStart::new("name", "series_id")); @@ -1117,10 +1113,9 @@ impl MeasurementSeries { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; /// - /// let step = test_run.step("step_name")?; + /// let step = run.step("step_name")?; /// step.start().await?; /// /// let series = step.measurement_series("name"); @@ -1149,10 +1144,9 @@ impl MeasurementSeries { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; /// - /// let step = test_run.step("step_name")?; + /// let step = run.step("step_name")?; /// step.start().await?; /// /// let series = step.measurement_series("name"); @@ -1186,10 +1180,9 @@ impl MeasurementSeries { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; /// - /// let step = test_run.step("step_name")?; + /// let step = run.step("step_name")?; /// step.start().await?; /// /// let series = step.measurement_series("name"); @@ -1227,10 +1220,9 @@ impl MeasurementSeries { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; /// - /// let step = test_run.step("step_name")?; + /// let step = run.step("step_name")?; /// step.start().await?; /// /// let series = step.measurement_series("name"); @@ -1276,10 +1268,9 @@ impl MeasurementSeries { /// # tokio_test::block_on(async { /// # use ocptv::output::*; /// - /// let test_run = TestRun::new("diagnostic_name", "my_dut", "1.0"); - /// test_run.start().await?; + /// let run = TestRun::new("diagnostic_name", "my_dut", "1.0").start().await?; /// - /// let step = test_run.step("step_name")?; + /// let step = run.step("step_name")?; /// step.start().await?; /// /// let series = step.measurement_series("name"); diff --git a/tests/output/runner.rs b/tests/output/runner.rs index 55307e1..242708a 100644 --- a/tests/output/runner.rs +++ b/tests/output/runner.rs @@ -3,6 +3,7 @@ // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +#![allow(unused_imports)] use std::fs; use std::sync::Arc; @@ -20,8 +21,8 @@ use tokio::sync::Mutex; use ocptv::output as tv; use tv::{ Config, DutInfo, Error, HardwareInfo, Log, LogSeverity, Measurement, MeasurementSeriesStart, - SoftwareInfo, Subcomponent, TestResult, TestRun, TestRunBuilder, TestRunOutcome, TestStatus, - TestStep, Validator, ValidatorType, + SoftwareInfo, StartedTestRun, Subcomponent, TestResult, TestRun, TestRunBuilder, + TestRunOutcome, TestStatus, TestStep, Validator, ValidatorType, }; fn json_schema_version() -> serde_json::Value { @@ -113,12 +114,12 @@ where async fn check_output_run(expected: &[serde_json::Value], test_fn: F) -> Result<()> where - F: for<'a> FnOnce(&'a TestRun) -> BoxFuture<'a, Result<(), tv::WriterError>> + Send, + F: for<'a> FnOnce(&'a StartedTestRun) -> BoxFuture<'a, Result<(), tv::WriterError>> + Send, { check_output(expected, |run_builder| async { let run = run_builder.build(); - run.start().await?; + let run = run.start().await?; test_fn(&run).await?; run.end(TestStatus::Complete, TestResult::Pass).await?; @@ -132,8 +133,7 @@ where F: for<'a> FnOnce(&'a TestStep) -> BoxFuture<'a, Result<(), tv::WriterError>>, { check_output(expected, |run_builder| async { - let run = run_builder.build(); - run.start().await?; + let run = run_builder.build().start().await?; let step = run.step("first step")?; step.start().await?; @@ -310,39 +310,39 @@ async fn test_testrun_with_error_with_details() -> Result<()> { .await } -#[tokio::test] -async fn test_testrun_with_scope() -> Result<()> { - let expected = [ - json_schema_version(), - json_run_default_start(), - json!({ - "testRunArtifact": { - "log": { - "message": "First message", - "severity": "INFO" - } - }, - "sequenceNumber": 3 - }), - json_run_pass(4), - ]; - - check_output(&expected, |run_builder| async { - let run = run_builder.build(); - - run.scope(|r| async { - r.log(LogSeverity::Info, "First message").await?; - Ok(TestRunOutcome { - status: TestStatus::Complete, - result: TestResult::Pass, - }) - }) - .await?; - - Ok(()) - }) - .await -} +// #[tokio::test] +// async fn test_testrun_with_scope() -> Result<()> { +// let expected = [ +// json_schema_version(), +// json_run_default_start(), +// json!({ +// "testRunArtifact": { +// "log": { +// "message": "First message", +// "severity": "INFO" +// } +// }, +// "sequenceNumber": 3 +// }), +// json_run_pass(4), +// ]; + +// check_output(&expected, |run_builder| async { +// let run = run_builder.build(); + +// run.scope(|r| async { +// r.log(LogSeverity::Info, "First message").await?; +// Ok(TestRunOutcome { +// status: TestStatus::Complete, +// result: TestResult::Pass, +// }) +// }) +// .await?; + +// Ok(()) +// }) +// .await +// } #[tokio::test] async fn test_testrun_with_step() -> Result<()> { @@ -1235,17 +1235,13 @@ async fn test_config_builder_with_file() -> Result<()> { .await? .build(), ) - .build(); + .build() + .start() + .await?; - run.scope(|r| async { - r.error_with_msg("symptom", "Error message").await?; + run.error_with_msg("symptom", "Error message").await?; - Ok(TestRunOutcome { - status: TestStatus::Complete, - result: TestResult::Pass, - }) - }) - .await?; + run.end(TestStatus::Complete, TestResult::Pass).await?; output_file.assert(predicate::path::exists()); let content = fs::read_to_string(output_file.path())?; @@ -1267,9 +1263,8 @@ async fn test_testrun_instantiation_with_new() -> Result<()> { ]; let buffer: Arc>> = Arc::new(Mutex::new(vec![])); - let test_run = TestRun::new("run_name", "dut_id", "1.0"); - test_run.start().await?; - test_run.end(TestStatus::Complete, TestResult::Pass).await?; + let run = TestRun::new("run_name", "dut_id", "1.0").start().await?; + run.end(TestStatus::Complete, TestResult::Pass).await?; for (idx, entry) in buffer.lock().await.iter().enumerate() { let value = serde_json::from_str::(entry)?; @@ -1301,8 +1296,12 @@ async fn test_testrun_metadata() -> Result<()> { ]; check_output(&expected, |run_builder| async { - let run = run_builder.add_metadata("key", "value".into()).build(); - run.start().await?; + let run = run_builder + .add_metadata("key", "value".into()) + .build() + .start() + .await?; + run.end(TestStatus::Complete, TestResult::Pass).await?; Ok(()) }) @@ -1342,8 +1341,10 @@ async fn test_testrun_builder() -> Result<()> { .add_metadata("key2", "value2".into()) .add_parameter("key", "value".into()) .command_line("cmd_line") - .build(); - run.start().await?; + .build() + .start() + .await?; + run.end(TestStatus::Complete, TestResult::Pass).await?; Ok(()) })