From 162c9bbc29ac2b2209a1ab1addca8ee9057386f6 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Tue, 30 Sep 2025 11:18:00 +0200 Subject: [PATCH] paasio: Prevent non-unwinding panic in tests Related forum post: https://forum.exercism.org/t/test-runner-fail-on-paas/19426 --- exercises/practice/paasio/tests/paasio.rs | 34 +++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/exercises/practice/paasio/tests/paasio.rs b/exercises/practice/paasio/tests/paasio.rs index 6f75a96aa..7a8c0acc4 100644 --- a/exercises/practice/paasio/tests/paasio.rs +++ b/exercises/practice/paasio/tests/paasio.rs @@ -136,7 +136,22 @@ mod write_string { fn sink_buffered_windowed() { let data = INPUT; let size = data.len(); - let mut writer = BufWriter::new(WriteStats::new(io::sink())); + + // We store the inner writer in a separate variable so its destructor + // is called correctly. We then wrap a mutable reference to it in a + // buffered writer. The destructor of the buffered writer is suppressed, + // because it tries to flush the inner writer. (It doesn't do anything + // else, so it's fine to skip it.) This would cause a non-unwinding + // panic if the inner writer hasn't implemented `write` yet. The + // standard library implementation of `BufWriter` does try to keep track + // of a panic by the inner writer, but it's not perfect. We access the + // inner writer later with `.get_ref()`. If there is a panic in that + // situation, the buffered writer cannot observe and track it. + // + // Related forum discussion: + // https://forum.exercism.org/t/test-runner-fail-on-paas/19426 + let mut inner_writer = WriteStats::new(io::sink()); + let mut writer = std::mem::ManuallyDrop::new(BufWriter::new(&mut inner_writer)); for chunk in data.chunks(CHUNK_SIZE) { let written = writer.write(chunk); @@ -286,7 +301,22 @@ mod write_byte_literal { fn sink_buffered_windowed() { let data = INPUT; let size = data.len(); - let mut writer = BufWriter::new(WriteStats::new(io::sink())); + + // We store the inner writer in a separate variable so its destructor + // is called correctly. We then wrap a mutable reference to it in a + // buffered writer. The destructor of the buffered writer is suppressed, + // because it tries to flush the inner writer. (It doesn't do anything + // else, so it's fine to skip it.) This would cause a non-unwinding + // panic if the inner writer hasn't implemented `write` yet. The + // standard library implementation of `BufWriter` does try to keep track + // of a panic by the inner writer, but it's not perfect. We access the + // inner writer later with `.get_ref()`. If there is a panic in that + // situation, the buffered writer cannot observe and track it. + // + // Related forum discussion: + // https://forum.exercism.org/t/test-runner-fail-on-paas/19426 + let mut inner_writer = WriteStats::new(io::sink()); + let mut writer = std::mem::ManuallyDrop::new(BufWriter::new(&mut inner_writer)); for chunk in data.chunks(CHUNK_SIZE) { let written = writer.write(chunk);