Skip to content

Commit 47046f8

Browse files
authored
petri: trace the test result (#859)
Right now, a test result on failure (the panic text or the anyhow error) only gets written to stdout, not to the split text logs or to the JSON log. This makes it hard to diagnose test failures by just looking at the log files. Trace the test result to the other logs, too. Also, add a file to make it easy to find the tests that passed and failed in the test result artifact directory tree without having to parse the junit XML (which is enormous).
1 parent 32da639 commit 47046f8

File tree

1 file changed

+49
-8
lines changed

1 file changed

+49
-8
lines changed

petri/src/test.rs

+49-8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use crate::TestArtifactRequirements;
1818
use crate::TestArtifacts;
1919
use anyhow::Context as _;
2020
use petri_artifacts_core::ArtifactResolver;
21+
use std::panic::catch_unwind;
22+
use std::panic::AssertUnwindSafe;
2123
use std::path::Path;
2224
use test_macro_support::TESTS;
2325

@@ -104,14 +106,52 @@ impl Test {
104106
resolve(&name, self.requirements()).context("failed to resolve artifacts")?;
105107
let output_dir = artifacts.get(petri_artifacts_common::artifacts::TEST_LOG_DIRECTORY);
106108
let logger = try_init_tracing(output_dir).context("failed to initialize tracing")?;
107-
self.test.0.run(
108-
PetriTestParams {
109-
test_name: &name,
110-
logger: &logger,
111-
output_dir,
112-
},
113-
&artifacts,
114-
)
109+
110+
// Catch test panics in order to cleanly log the panic result. Without
111+
// this, `libtest_mimic` will report the panic to stdout and fail the
112+
// test, but the details won't end up in our per-test JSON log.
113+
let r = catch_unwind(AssertUnwindSafe(|| {
114+
self.test.0.run(
115+
PetriTestParams {
116+
test_name: &name,
117+
logger: &logger,
118+
output_dir,
119+
},
120+
&artifacts,
121+
)
122+
}));
123+
let r = r.unwrap_or_else(|err| {
124+
// The error from `catch_unwind` is almost always either a
125+
// `&str` or a `String`, since that's what `panic!` produces.
126+
let msg = err
127+
.downcast_ref::<&str>()
128+
.copied()
129+
.or_else(|| err.downcast_ref::<String>().map(|x| x.as_str()));
130+
131+
let err = if let Some(msg) = msg {
132+
anyhow::anyhow!("test panicked: {msg}")
133+
} else {
134+
anyhow::anyhow!("test panicked (unknown payload type)")
135+
};
136+
Err(err)
137+
});
138+
let result_path = match &r {
139+
Ok(()) => {
140+
tracing::info!("test passed");
141+
"petri.passed"
142+
}
143+
Err(err) => {
144+
tracing::error!(
145+
error = err.as_ref() as &dyn std::error::Error,
146+
"test failed"
147+
);
148+
"petri.failed"
149+
}
150+
};
151+
// Write a file to the output directory to indicate whether the test
152+
// passed, for easy scanning via tools.
153+
fs_err::write(output_dir.join(result_path), &name).unwrap();
154+
r
115155
}
116156

117157
/// Returns a libtest-mimic trial to run the test.
@@ -168,6 +208,7 @@ impl<T: RunTest> DynRunTest for T {
168208
}
169209

170210
/// Parameters passed to a [`RunTest`] when it is run.
211+
#[non_exhaustive]
171212
pub struct PetriTestParams<'a> {
172213
/// The name of the running test.
173214
pub test_name: &'a str,

0 commit comments

Comments
 (0)