diff --git a/.coverage-baseline-v2.rust b/.coverage-baseline-v2.rust new file mode 100644 index 000000000..99a0e076a --- /dev/null +++ b/.coverage-baseline-v2.rust @@ -0,0 +1 @@ +87.8 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bbe373cc3..a83cc31e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,7 +72,7 @@ jobs: # Both runner choices are Ubuntu. Confirm the manifest's Cranelift # component and install the Linux linker before debug Make targets. run: | - rustup component add rustc-codegen-cranelift-preview --toolchain nightly-2026-03-26 + rustup component add rustc-codegen-cranelift-preview --toolchain nightly-2026-10-22 sudo apt-get update sudo apt-get install -y mold mold --version @@ -147,6 +147,10 @@ jobs: output-path: lcov.info format: lcov with-ratchet: 'true' + # A new path starts a new baseline: the cache entry's version hashes + # this path, so the restore misses the 89.0% measured on the previous + # toolchain and the committed file sets the baseline. + baseline-rust-file: .coverage-baseline-v2.rust # The ratchet reads this report; nothing else does, so it is not # published as an artefact. publish-artefact: 'false' diff --git a/.github/workflows/coverage-main.yml b/.github/workflows/coverage-main.yml index 384aa49d5..bc966f014 100644 --- a/.github/workflows/coverage-main.yml +++ b/.github/workflows/coverage-main.yml @@ -71,6 +71,10 @@ jobs: output-path: lcov.info format: lcov with-ratchet: 'true' + # A new path starts a new baseline: the cache entry's version hashes + # this path, so the restore misses the 89.0% measured on the previous + # toolchain and the committed file sets the baseline. + baseline-rust-file: .coverage-baseline-v2.rust # Reports whether the token is configured without binding it anywhere: # the expression is evaluated before the shell runs, so this step # writes `available=true` or `available=false` and the token is in no diff --git a/docs/developers-guide.md b/docs/developers-guide.md index ea6a4a276..ddb50648f 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -963,7 +963,7 @@ as a test assertion on the SHA string. ## Development builds -The repository pins `nightly-2026-03-26` with `rustfmt`, `clippy`, and +The repository pins `nightly-2026-10-22` with `rustfmt`, `clippy`, and `rust-analyzer`. The toolchain also needs `rustc-codegen-cranelift-preview` for the optional development backend. Native Linux development builds use `mold` as the linker; CI provisions the component and linker before running the standard @@ -1025,9 +1025,10 @@ making the faster backend an explicit choice for routine debug work. ## Cranelift The Make test targets still select the development fragment, but the test -profile overrides Cranelift with LLVM on the pinned `nightly-2026-03-26` -toolchain. This exception follows reproduced test failures: with Cranelift, -`make test` aborted in +profile overrides Cranelift with LLVM. The failures behind this exception were +reproduced on the previously pinned `nightly-2026-03-26` and have not been +re-measured on `nightly-2026-10-22`. This exception follows reproduced test +failures: with Cranelift, `make test` aborted in `client::pool::sync::tests::lock_or_recover_reads_poisoned_mutex` with `SIGABRT` and `fatal runtime error: failed to initiate panic, error 5`. The test passed with the LLVM test-profile override. Under Cranelift, a metrics @@ -1041,9 +1042,9 @@ source fragment; reproduce the recorded failures and reassess the override when the toolchain pin changes. The failures above are the first measurement; the re-measurement below supersedes their details. -Re-measured on 2026-09-28 on the pinned `nightly-2026-03-26`, with Cranelift -for both the development and test profiles, as part of adopting the build -standard. The exception stands. Three test binaries fail: +Re-measured on 2026-09-28 on the then-pinned `nightly-2026-03-26`, with +Cranelift for both the development and test profiles, as part of adopting the +build standard. The exception stands. Three test binaries fail: - `wireframe` library tests abort with `SIGABRT` and `failed to initiate panic, error 5` at diff --git a/examples/resp_codec_impl/codec.rs b/examples/resp_codec_impl/codec.rs index 7b2ae2202..fe7fc1722 100644 --- a/examples/resp_codec_impl/codec.rs +++ b/examples/resp_codec_impl/codec.rs @@ -98,3 +98,26 @@ impl FrameCodec for RespFrameCodec { fn max_frame_length(&self) -> usize { self.max_frame_length } } + +#[cfg(test)] +mod tests { + use bytes::BytesMut; + use tokio_util::codec::Encoder; + + use super::RespAdapter; + use crate::resp_codec_impl::frame::RespFrame; + + /// `:-99\r\n` is six bytes: it fits a six-byte limit exactly and is + /// rejected by a five-byte one. + #[test] + fn negative_integer_frame_is_accepted_at_its_exact_size_limit() { + let frame = RespFrame::Integer(-99); + let mut dst = BytesMut::new(); + assert!(RespAdapter::new(6).encode(frame.clone(), &mut dst).is_ok()); + assert_eq!(dst.as_ref(), b":-99\r\n"); + + let mut rejected = BytesMut::new(); + assert!(RespAdapter::new(5).encode(frame, &mut rejected).is_err()); + assert!(rejected.is_empty()); + } +} diff --git a/examples/resp_codec_impl/encode.rs b/examples/resp_codec_impl/encode.rs index 5d313dc7c..2d83c7651 100644 --- a/examples/resp_codec_impl/encode.rs +++ b/examples/resp_codec_impl/encode.rs @@ -114,7 +114,7 @@ fn encode_frame_at(frame: &RespFrame, dst: &mut BytesMut, depth: usize) -> Resul fn digits_len_usize(mut value: usize) -> usize { let mut digits = 1; while value >= 10 { - value /= 10; + value = value.div_euclid(10); digits += 1; } digits @@ -123,14 +123,73 @@ fn digits_len_usize(mut value: usize) -> usize { /// Count digits in a signed integer, including its possible minus sign. fn digits_len_i64(value: i64) -> usize { let mut digits = 1; - let mut cursor = value; - while cursor <= -10 || cursor >= 10 { - cursor /= 10; + let mut cursor = value.unsigned_abs(); + while cursor >= 10 { + cursor = cursor.div_euclid(10); digits += 1; } if value < 0 { digits + 1 } else { digits } } +#[cfg(test)] +mod tests { + use super::digits_len_i64; + + /// Negative values must count their magnitude, not round toward negative + /// infinity: `-99` is three characters and `-10` is three. + #[test] + fn digits_len_i64_matches_the_rendered_length() { + for value in [i64::MIN, -100, -99, -10, -9, -1, 0, 1, 9, 10, 99, i64::MAX] { + assert_eq!(digits_len_i64(value), value.to_string().len(), "{value}"); + } + } + + /// Exact length and wire bytes for the signed boundaries. + #[test] + fn integer_frames_encode_to_their_measured_length() -> Result<(), std::io::Error> { + use bytes::BytesMut; + + use super::{encode_frame, encoded_len}; + use crate::resp_codec_impl::frame::RespFrame; + + for (value, wire) in [ + (-99_i64, ":-99\r\n".to_owned()), + (i64::MIN, format!(":{}\r\n", i64::MIN)), + ] { + let frame = RespFrame::Integer(value); + let mut dst = BytesMut::new(); + encode_frame(&frame, &mut dst)?; + assert_eq!(dst.as_ref(), wire.as_bytes()); + assert_eq!(encoded_len(&frame)?, wire.len()); + } + Ok(()) + } + + proptest::proptest! { + /// The measured length equals the emitted bytes for any integer frame. + #[test] + fn encoded_len_matches_emitted_bytes(value in proptest::prelude::any::()) { + use bytes::BytesMut; + + use super::{encode_frame, encoded_len}; + use crate::resp_codec_impl::frame::RespFrame; + + let frame = RespFrame::Integer(value); + let mut dst = BytesMut::new(); + proptest::prop_assert!(encode_frame(&frame, &mut dst).is_ok()); + proptest::prop_assert_eq!(encoded_len(&frame).ok(), Some(dst.len())); + } + } + + proptest::proptest! { + /// Any `i64` renders with exactly the length the helper reports. + #[test] + fn digits_len_i64_matches_for_any_value(value in proptest::prelude::any::()) { + proptest::prop_assert_eq!(digits_len_i64(value), value.to_string().len()); + } + } +} + /// Add encoded-size components and report overflow as a frame-size error. fn checked_add(lhs: usize, rhs: usize) -> Result { lhs.checked_add(rhs) diff --git a/examples/resp_codec_impl/parse.rs b/examples/resp_codec_impl/parse.rs index c27445256..be5bad7a4 100644 --- a/examples/resp_codec_impl/parse.rs +++ b/examples/resp_codec_impl/parse.rs @@ -52,7 +52,7 @@ const MAX_RECURSION_DEPTH: usize = 64; fn decimal_digits(mut value: usize) -> usize { let mut digits = 1; while value >= 10 { - value /= 10; + value = value.div_euclid(10); digits += 1; } digits diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 4ec7fed8f..94325fba4 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2026-03-26" +channel = "nightly-2026-10-22" components = ["rustfmt", "clippy", "rust-analyzer", "rustc-codegen-cranelift-preview"] diff --git a/src/app/builder/protocol.rs b/src/app/builder/protocol.rs index 04b472979..a3d1ee0ea 100644 --- a/src/app/builder/protocol.rs +++ b/src/app/builder/protocol.rs @@ -96,8 +96,7 @@ where pub fn protocol_hooks(&self) -> ProtocolHooks { self.protocol .as_ref() - .map(ProtocolHooks::from_protocol) - .unwrap_or_default() + .map_or_else(ProtocolHooks::default, ProtocolHooks::from_protocol) } /// Get the configured message assembler, if any. diff --git a/src/app/codec_driver.rs b/src/app/codec_driver.rs index 5c9e2ab71..dcdee6591 100644 --- a/src/app/codec_driver.rs +++ b/src/app/codec_driver.rs @@ -223,7 +223,7 @@ mod tests { assert_eq!(first.len(), 1); let second = pipeline.drain_output(); - assert!(second.is_empty()); + assert_eq!(second, [] as [crate::app::Envelope; 0]); } #[rstest] diff --git a/src/app/inbound_handler/mod.rs b/src/app/inbound_handler/mod.rs index e170349e0..6bb846b5b 100644 --- a/src/app/inbound_handler/mod.rs +++ b/src/app/inbound_handler/mod.rs @@ -178,7 +178,10 @@ where /// # Errors /// /// Returns an [`io::Error`] if stream processing or handler execution fails. - #[deprecated(note = "prepare the app once, then call PreparedApp::handle_connection_result")] + #[deprecated( + since = "0.3.0", + note = "prepare the app once, then call PreparedApp::handle_connection_result" + )] pub async fn handle_connection_result(&self, stream: W) -> io::Result<()> where W: AsyncRead + AsyncWrite + Send + Unpin + 'static, @@ -187,7 +190,10 @@ where } /// Handle a connection through the compatibility preparation path. - #[deprecated(note = "prepare the app once, then call PreparedApp::handle_connection")] + #[deprecated( + since = "0.3.0", + note = "prepare the app once, then call PreparedApp::handle_connection" + )] pub async fn handle_connection(&self, stream: W) where W: AsyncRead + AsyncWrite + Send + Unpin + 'static, diff --git a/src/app/prepared_app.rs b/src/app/prepared_app.rs index dbacaeee9..c95a1eb7e 100644 --- a/src/app/prepared_app.rs +++ b/src/app/prepared_app.rs @@ -327,10 +327,10 @@ where /// ``` #[must_use] pub fn protocol_hooks(&self) -> crate::hooks::ProtocolHooks { - self.protocol - .as_ref() - .map(crate::hooks::ProtocolHooks::from_protocol) - .unwrap_or_default() + self.protocol.as_ref().map_or_else( + crate::hooks::ProtocolHooks::default, + crate::hooks::ProtocolHooks::from_protocol, + ) } /// Get the configured message assembler, if any. diff --git a/src/client/pool/lease.rs b/src/client/pool/lease.rs index f1db6f165..3b5a7d0bd 100644 --- a/src/client/pool/lease.rs +++ b/src/client/pool/lease.rs @@ -209,7 +209,7 @@ where self.pending_responses.fetch_add(1, Ordering::AcqRel); } PendingResponseChange::Remove => { - let _ = self.pending_responses.fetch_update( + let _ = self.pending_responses.try_update( Ordering::AcqRel, Ordering::Acquire, |pending| Some(pending.saturating_sub(1)), diff --git a/src/client/pool/scheduler/service.rs b/src/client/pool/scheduler/service.rs index 2b4d30678..02bc024c3 100644 --- a/src/client/pool/scheduler/service.rs +++ b/src/client/pool/scheduler/service.rs @@ -35,10 +35,6 @@ where } /// Race slot capacity against shutdown or cancellation for one waiter. - #[expect( - clippy::integer_division_remainder_used, - reason = "tokio::select! macro internally uses % for random branch selection" - )] async fn service_one_waiter( &self, mut sender: WaiterSender, diff --git a/src/connection/mod.rs b/src/connection/mod.rs index 505a83123..9932cbf20 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -337,10 +337,6 @@ where /// /// The `strict_priority_order` and `shutdown_signal_precedence` tests /// assert that this ordering is preserved across refactors. - #[expect( - clippy::integer_division_remainder_used, - reason = "tokio::select! expands to modulus operations internally" - )] async fn next_event(&mut self, state: &ActorState) -> Event { let avail = self.compute_availability(state); let can_poll_multi_packet = avail.output == AvailableOutput::MultiPacket; diff --git a/src/extractor/streaming.rs b/src/extractor/streaming.rs index d3b7c55e2..7dd1360da 100644 --- a/src/extractor/streaming.rs +++ b/src/extractor/streaming.rs @@ -54,7 +54,6 @@ impl StreamingBody { /// /// Use this when you need direct access to the stream for custom /// processing with [`futures::StreamExt`] methods. - #[must_use] pub fn into_stream(self) -> crate::request::RequestBodyStream { self.stream } /// Convert to an [`AsyncRead`] adapter. diff --git a/src/fragment/fragmenter.rs b/src/fragment/fragmenter.rs index 3fe49866f..30cb37b43 100644 --- a/src/fragment/fragmenter.rs +++ b/src/fragment/fragmenter.rs @@ -70,7 +70,7 @@ impl Fragmenter { pub fn next_message_id(&self) -> MessageId { match self .next_message_id - .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| { + .try_update(Ordering::Relaxed, Ordering::Relaxed, |current| { current.checked_add(1) }) { Ok(previous) => MessageId::new(previous), diff --git a/src/fragment/tests/fragmenter_tests.rs b/src/fragment/tests/fragmenter_tests.rs index d8a61e124..aa3bcc143 100644 --- a/src/fragment/tests/fragmenter_tests.rs +++ b/src/fragment/tests/fragmenter_tests.rs @@ -76,7 +76,7 @@ fn fragmenter_handles_empty_payload() { .fragments() .first() .expect("batch should contain at least one fragment"); - assert!(fragment.payload().is_empty()); + assert_eq!(fragment.payload(), []); assert!(fragment.header().is_last_fragment()); assert_eq!(fragment.header().fragment_index(), FragmentIndex::zero()); } @@ -153,3 +153,40 @@ fn fragmenter_returns_error_for_out_of_bounds_slice() { other => panic!("expected SliceBounds, got {other:?}"), } } + +/// Concurrent callers each receive a distinct identifier and none observes the +/// exhaustion panic, so the compare-exchange behind `try_update` retries on +/// contention rather than failing. +#[test] +fn fragmenter_allocates_distinct_ids_under_contention() -> TestResult { + const THREADS: usize = 8; + const IDS_PER_THREAD: usize = 2_000; + + let fragmenter = std::sync::Arc::new(Fragmenter::new( + NonZeroUsize::new(8).ok_or("fragment size must be non-zero")?, + )); + let handles: Vec<_> = (0..THREADS) + .map(|_| { + let fragmenter = std::sync::Arc::clone(&fragmenter); + std::thread::spawn(move || { + (0..IDS_PER_THREAD) + .map(|_| fragmenter.next_message_id()) + .collect::>() + }) + }) + .collect(); + let mut ids = std::collections::HashSet::new(); + for handle in handles { + let batch = handle.join().map_err(|_| "id allocation thread panicked")?; + ids.extend(batch); + } + if ids.len() != THREADS * IDS_PER_THREAD { + return Err(format!( + "expected {} distinct identifiers, observed {}", + THREADS * IDS_PER_THREAD, + ids.len() + ) + .into()); + } + Ok(()) +} diff --git a/src/preamble/tests.rs b/src/preamble/tests.rs index a5c5e67fe..9ef65bcc8 100644 --- a/src/preamble/tests.rs +++ b/src/preamble/tests.rs @@ -65,7 +65,7 @@ async fn bounded_preamble_preserves_valid_containers_and_following_bytes(#[case] .await .expect("preamble within the budget must decode"); assert_eq!(decoded, value); - assert!(leftover.is_empty()); + assert_eq!(leftover, [] as [u8; 0]); assert_eq!(reader.position(), encoded_len as u64); assert!(encoded_len <= MAX_PREAMBLE_LEN); assert_eq!( diff --git a/src/push/queues/mod.rs b/src/push/queues/mod.rs index e7e9c580d..2a5e4b055 100644 --- a/src/push/queues/mod.rs +++ b/src/push/queues/mod.rs @@ -287,10 +287,6 @@ impl PushQueues { /// assert_eq!(frame, 2); /// } /// ``` - #[expect( - clippy::integer_division_remainder_used, - reason = "tokio::select! expands to modulus internally" - )] pub async fn recv(&mut self) -> Option<(PushPriority, F)> { let mut high_closed = false; let mut low_closed = false; diff --git a/src/request/mod.rs b/src/request/mod.rs index 51293eae4..d07f539af 100644 --- a/src/request/mod.rs +++ b/src/request/mod.rs @@ -111,7 +111,6 @@ pub const DEFAULT_BODY_CHANNEL_CAPACITY: usize = 16; /// // tx: send chunks from connection /// // rx: consume in handler /// ``` -#[must_use] pub fn body_channel( capacity: usize, ) -> (mpsc::Sender>, RequestBodyStream) { @@ -178,7 +177,6 @@ impl RequestBodyReader { /// **Safe to call when:** the reader has been fully drained (all reads /// returned zero bytes or an error), or when deliberately abandoning /// any remaining buffered content. - #[must_use] pub fn into_inner(self) -> RequestBodyStream { self.inner.into_inner() } } impl AsyncRead for RequestBodyReader { diff --git a/src/request/tests.rs b/src/request/tests.rs index 181ca2c19..5d71fb9c8 100644 --- a/src/request/tests.rs +++ b/src/request/tests.rs @@ -58,5 +58,5 @@ fn inherit_correlation_variants( #[rstest] fn empty_metadata_is_valid() { let parts = RequestParts::new(1, None, vec![]); - assert!(parts.metadata().is_empty()); + assert_eq!(parts.metadata(), []); } diff --git a/src/response.rs b/src/response.rs index 525556f92..072aea18f 100644 --- a/src/response.rs +++ b/src/response.rs @@ -183,7 +183,6 @@ impl Response { /// assert_eq!(frames, vec![1]); /// # } /// ``` - #[must_use] pub fn into_stream(self) -> FrameStream { match self { Response::Single(f) => { diff --git a/src/server/runtime/accept.rs b/src/server/runtime/accept.rs index e8f79c4c2..f2ecb4cb3 100644 --- a/src/server/runtime/accept.rs +++ b/src/server/runtime/accept.rs @@ -241,10 +241,6 @@ fn record_accept_loop_exit(shutdown: &CancellationToken, lifecycle: &SupervisorL } /// Select between shutdown and one accept attempt, returning the next delay. -#[expect( - clippy::integer_division_remainder_used, - reason = "tokio::select! expands to modulus internally" -)] async fn accept_iteration( listener: &Arc, handles: &AcceptHandles<'_, T, Ser, Ctx, E, Codec>, diff --git a/src/server/runtime/startup.rs b/src/server/runtime/startup.rs index a5baf6b0f..8d270631c 100644 --- a/src/server/runtime/startup.rs +++ b/src/server/runtime/startup.rs @@ -52,10 +52,6 @@ where S: Future + Send, { let mut shutdown = Box::pin(shutdown); - #[expect( - clippy::integer_division_remainder_used, - reason = "tokio::select! expands to modulus internally" - )] let startup_result = select! { () = &mut shutdown => { record_server_startup_duration( diff --git a/src/server/runtime/supervisor.rs b/src/server/runtime/supervisor.rs index 28523b310..d2a1fc876 100644 --- a/src/server/runtime/supervisor.rs +++ b/src/server/runtime/supervisor.rs @@ -127,10 +127,6 @@ impl Drop for SupervisorCancellationDropGuard<'_> { } /// Wait for deliberate shutdown or for all tracked workers to complete. -#[expect( - clippy::integer_division_remainder_used, - reason = "tokio::select! expands to modulus internally" -)] pub(super) async fn await_supervisor_termination( shutdown: S, shutdown_token: &CancellationToken, diff --git a/tests/connection_actor_errors.rs b/tests/connection_actor_errors.rs index 61361fefa..242f4055a 100644 --- a/tests/connection_actor_errors.rs +++ b/tests/connection_actor_errors.rs @@ -175,7 +175,7 @@ async fn protocol_error_logs_warning( .run(&mut out) .await .map_err(|e| io::Error::other(format!("actor run failed: {e:?}")))?; - assert!(out.is_empty()); + assert_eq!(out, [] as [u8; 0]); let mut found = false; while let Some(record) = logger.pop() { if record.level() == log::Level::Warn && record.args().contains("protocol error") { diff --git a/tests/connection_actor_shutdown.rs b/tests/connection_actor_shutdown.rs index e10087016..9722e1719 100644 --- a/tests/connection_actor_shutdown.rs +++ b/tests/connection_actor_shutdown.rs @@ -42,7 +42,7 @@ async fn shutdown_signal_precedence( // drop the handle after actor creation to mimic early disconnection let mut out = Vec::new(); actor.run(&mut out).await.expect("actor run failed"); - assert!(out.is_empty()); + assert_eq!(out, [] as [u8; 0]); } #[rstest] diff --git a/tests/fixtures/client_tracing.rs b/tests/fixtures/client_tracing.rs index 3212403ad..991b6d065 100644 --- a/tests/fixtures/client_tracing.rs +++ b/tests/fixtures/client_tracing.rs @@ -213,7 +213,7 @@ impl ClientTracingWorld { /// Return the peer address string. pub fn peer_addr_string(&self) -> String { - self.addr.map(|a| a.to_string()).unwrap_or_default() + self.addr.map_or_else(String::new, |a| a.to_string()) } /// Check whether captured tracing output contains a needle. diff --git a/tests/fixtures/multi_packet.rs b/tests/fixtures/multi_packet.rs index 23c8accb2..5b4a62437 100644 --- a/tests/fixtures/multi_packet.rs +++ b/tests/fixtures/multi_packet.rs @@ -140,7 +140,7 @@ impl MultiPacketWorld { /// # Panics /// Panics if any messages are present. pub fn verify_empty(&self) { - assert!(self.messages.is_empty()); + assert_eq!(self.messages, [] as [u8; 0]); } /// Verify messages were received in order. diff --git a/tests/formal_tooling/targets.rs b/tests/formal_tooling/targets.rs index 2715833af..9710ba442 100644 --- a/tests/formal_tooling/targets.rs +++ b/tests/formal_tooling/targets.rs @@ -323,7 +323,6 @@ proptest! { let expected = candidate .split('.') .collect::>() - .as_slice() .iter() .copied() .all(|part| !part.is_empty() && part.chars().all(|character| character.is_ascii_digit())) diff --git a/tests/middleware.rs b/tests/middleware.rs index bd4b3f2ee..700beecfe 100644 --- a/tests/middleware.rs +++ b/tests/middleware.rs @@ -80,7 +80,7 @@ async fn middleware_modifies_request_and_response_preserves_cid(correlation_id: #[tokio::test] async fn from_fn_clones_captured_state_across_routes() { - let marker = Arc::new([b'!', b'?']); + let marker = Arc::new(*b"!?"); let middleware = from_fn( move |mut request: ServiceRequest, _next: Next<'_, HandlerService>| { let [request_marker, response_marker] = marker.as_ref(); diff --git a/tests/server.rs b/tests/server.rs index bc0ad080a..2b0bd043d 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -133,10 +133,6 @@ async fn dropping_server_supervisor_releases_listener() -> TestResult { .run_with_shutdown(std::future::pending()), ); - #[expect( - clippy::integer_division_remainder_used, - reason = "tokio::select! expands to modulus internally" - )] let readiness: TestResult = timeout(Duration::from_secs(1), async { tokio::select! { readiness = &mut ready_rx => { diff --git a/tests/server_lifecycle_model.rs b/tests/server_lifecycle_model.rs index 6522980a9..3f8032140 100644 --- a/tests/server_lifecycle_model.rs +++ b/tests/server_lifecycle_model.rs @@ -162,10 +162,6 @@ async fn drop_supervisor(requested_workers: usize) -> TestResult { .ready_signal(ready_tx) .run_with_shutdown(future::pending()), ); - #[expect( - clippy::integer_division_remainder_used, - reason = "tokio::select! expands to modulus internally" - )] let readiness: TestResult = timeout(Duration::from_secs(1), async { tokio::select! { ready = &mut ready_rx => ready.map_err(|error| { diff --git a/tests/server_lifecycle_observability.rs b/tests/server_lifecycle_observability.rs index 52ec379ca..50072e29f 100644 --- a/tests/server_lifecycle_observability.rs +++ b/tests/server_lifecycle_observability.rs @@ -184,10 +184,6 @@ async fn drop_supervisor() -> TestResult { .ready_signal(ready_tx) .run_with_shutdown(future::pending()), ); - #[expect( - clippy::integer_division_remainder_used, - reason = "tokio::select! expands to modulus internally" - )] let readiness: TestResult = timeout(Duration::from_secs(1), async { tokio::select! { ready = &mut ready_rx => ready.map_err(|error| { diff --git a/tests/steps/client_request_hooks_steps.rs b/tests/steps/client_request_hooks_steps.rs index fb8335bfb..808bbc367 100644 --- a/tests/steps/client_request_hooks_steps.rs +++ b/tests/steps/client_request_hooks_steps.rs @@ -119,7 +119,7 @@ define_counter_step!( #[then("the markers appear in registration order")] fn then_markers_in_order(client_request_hooks_world: &mut ClientRequestHooksWorld) -> TestResult { let log = client_request_hooks_world.marker_log()?; - if log != [b'A', b'B'] { + if log != *b"AB" { return Err(format!("expected markers [A, B] in registration order, got {log:?}").into()); } Ok(()) diff --git a/tests/ui/server_rejects_non_send_factory.stderr b/tests/ui/server_rejects_non_send_factory.stderr index afb1f5cb5..a30021fcf 100644 --- a/tests/ui/server_rejects_non_send_factory.stderr +++ b/tests/ui/server_rejects_non_send_factory.stderr @@ -1,47 +1,31 @@ error[E0277]: `Rc<()>` cannot be sent between threads safely - --> tests/ui/server_rejects_non_send_factory.rs:9:19 - | -9 | let _server = WireframeServer::new(move || { - | ^^^^^^^^^^^^^^^ ------- within this `{closure@$DIR/tests/ui/server_rejects_non_send_factory.rs:9:40: 9:47}` - | | - | `Rc<()>` cannot be sent between threads safely - | - = help: within `{closure@$DIR/tests/ui/server_rejects_non_send_factory.rs:9:40: 9:47}`, the trait `Send` is not implemented for `Rc<()>` -note: required because it's used within this closure - --> tests/ui/server_rejects_non_send_factory.rs:9:40 - | -9 | let _server = WireframeServer::new(move || { - | ^^^^^^^ - = note: required for `{closure@$DIR/tests/ui/server_rejects_non_send_factory.rs:9:40: 9:47}` to implement `AppFactory<_, _, _, _>` -note: required by a bound in `WireframeServer` - --> src/server/mod.rs - | - | pub struct WireframeServer< - | --------------- required by a bound in this struct + --> tests/ui/server_rejects_non_send_factory.rs:9:40 + | + 9 | let _server = WireframeServer::new(move || { + | -------------------- ^------ + | | | + | ___________________|____________________within this `{closure@$DIR/tests/ui/server_rejects_non_send_factory.rs:9:40: 9:47}` + | | | + | | required by a bound introduced by this call +10 | | let _ = &captured; +11 | | WireframeApp::default() +12 | | }); + | |_____^ `Rc<()>` cannot be sent between threads safely + | + = help: within `{closure@$DIR/tests/ui/server_rejects_non_send_factory.rs:9:40: 9:47}`, the trait `Send` is not implemented for `Rc<()>` +note: required because `captured` is used within this closure + --> tests/ui/server_rejects_non_send_factory.rs:10:18 + | +10 | let _ = &captured; + | ^^^^^^^^ + = note: required for `{closure@$DIR/tests/ui/server_rejects_non_send_factory.rs:9:40: 9:47}` to implement `AppFactory<_, _, _, _>` +note: required by a bound in `server::config::>::new` + --> src/server/config/mod.rs + | + | impl WireframeServer + | where + | F: AppFactory, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `server::config::>::new` ... - | F: AppFactory, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `WireframeServer` - -error[E0277]: `Rc<()>` cannot be shared between threads safely - --> tests/ui/server_rejects_non_send_factory.rs:9:19 - | -9 | let _server = WireframeServer::new(move || { - | ^^^^^^^^^^^^^^^ ------- within this `{closure@$DIR/tests/ui/server_rejects_non_send_factory.rs:9:40: 9:47}` - | | - | `Rc<()>` cannot be shared between threads safely - | - = help: within `{closure@$DIR/tests/ui/server_rejects_non_send_factory.rs:9:40: 9:47}`, the trait `Sync` is not implemented for `Rc<()>` -note: required because it's used within this closure - --> tests/ui/server_rejects_non_send_factory.rs:9:40 - | -9 | let _server = WireframeServer::new(move || { - | ^^^^^^^ - = note: required for `{closure@$DIR/tests/ui/server_rejects_non_send_factory.rs:9:40: 9:47}` to implement `AppFactory<_, _, _, _>` -note: required by a bound in `WireframeServer` - --> src/server/mod.rs - | - | pub struct WireframeServer< - | --------------- required by a bound in this struct -... - | F: AppFactory, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `WireframeServer` + | pub fn new(factory: F) -> Self { + | diff --git a/tests/ui/zero_test_budget.stderr b/tests/ui/zero_test_budget.stderr index 560af9393..91bfb0742 100644 --- a/tests/ui/zero_test_budget.stderr +++ b/tests/ui/zero_test_budget.stderr @@ -14,6 +14,8 @@ error[E0080]: evaluation panicked: test budget must be non-zero note: erroneous constant encountered --> tests/ui/../support/non_zero.rs | + | macro_rules! nz { +... | NON_ZERO | ^^^^^^^^ | @@ -21,8 +23,6 @@ note: erroneous constant encountered | 5 | const ZERO_BUDGET: std::num::NonZeroUsize = nz!(0); | ------ in this macro invocation - | - = note: this note originates in the macro `nz` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> tests/ui/zero_test_budget.rs:9:13 diff --git a/tests/workflow_contracts/ci_workflow_test.py b/tests/workflow_contracts/ci_workflow_test.py index a8711d83d..b21dbe1e0 100644 --- a/tests/workflow_contracts/ci_workflow_test.py +++ b/tests/workflow_contracts/ci_workflow_test.py @@ -181,6 +181,7 @@ def test_coverage_generation_stays_pull_request_only_and_ratcheted() -> None: "output-path": "lcov.info", "format": "lcov", "with-ratchet": "true", + "baseline-rust-file": ".coverage-baseline-v2.rust", "publish-artefact": "false", }, "coverage generation must produce the ratcheted LCOV report" diff --git a/tests/workflow_contracts/dev_fast_routing_test.py b/tests/workflow_contracts/dev_fast_routing_test.py index a2662caa3..270c1ae75 100644 --- a/tests/workflow_contracts/dev_fast_routing_test.py +++ b/tests/workflow_contracts/dev_fast_routing_test.py @@ -305,7 +305,7 @@ def test_ci_provisions_backend_and_linker_before_lint() -> None: assert isinstance(commands, str), "backend and linker provisioning needs run commands" required_commands = ( "rustup component add rustc-codegen-cranelift-preview " - "--toolchain nightly-2026-03-26", + "--toolchain nightly-2026-10-22", "sudo apt-get update", "sudo apt-get install -y mold", "mold --version", diff --git a/typos.local.toml b/typos.local.toml index 5ae3b451f..9cf135091 100644 --- a/typos.local.toml +++ b/typos.local.toml @@ -34,6 +34,9 @@ ignore = [ "Ser: Serializer", "DecodeWith", "EncodeWith", + # The compiler diagnostics snapshot quotes the server impl header verbatim. + "impl", + "Unbound,\\ Ser,\\ Ctx", "st\\(start_paused\\ =\\ true,\\ flavor\\ =\\ \"current_thread\"\\)\\]", ] diff --git a/typos.toml b/typos.toml index 794559960..d43dda95f 100644 --- a/typos.toml +++ b/typos.toml @@ -42,6 +42,7 @@ extend-ignore-re = [ "HashiCorp", "How to organise your Rust tests", "Ser: Serializer", + "Unbound,\\ Ser,\\ Ctx", "\\ \\ \\ \\ \\ \\ CARGO_TERM_COLOR:\\ always", "\\ reopened,\\ synchronize,\\ labeled,\\ ready_for_review\\]", "\\#\\[tokio::test\\(flavor\\ =\\ \"current_thread\",\\ sta", @@ -61,6 +62,7 @@ extend-ignore-re = [ "`color`", "`var\\.iamge_id` instead of `var\\.image_id`", "currentColor", + "impl", "install-mold", "mold --version", "st\\(start_paused\\ =\\ true,\\ flavor\\ =\\ \"current_thread\"\\)\\]", diff --git a/wireframe_testing/src/client_pair.rs b/wireframe_testing/src/client_pair.rs index 2fc6053d2..f4ff1d4ae 100644 --- a/wireframe_testing/src/client_pair.rs +++ b/wireframe_testing/src/client_pair.rs @@ -185,10 +185,6 @@ impl Drop for WireframePair { /// This gives the server task a chance to run `tracker.close()` and /// `tracker.wait().await` for spawned connection tasks before being /// force-aborted. -#[expect( - clippy::integer_division_remainder_used, - reason = "Tokio's select macro expands to scheduler arithmetic outside this harness" -)] fn spawn_bounded_shutdown( mut handle: JoinHandle>, timeout: std::time::Duration, diff --git a/wireframe_testing/src/runtime_ownership_benchmarks/client_pool_benchmark_support/measurement/distribution/tests.rs b/wireframe_testing/src/runtime_ownership_benchmarks/client_pool_benchmark_support/measurement/distribution/tests.rs index 98cfa454c..bb5e69f96 100644 --- a/wireframe_testing/src/runtime_ownership_benchmarks/client_pool_benchmark_support/measurement/distribution/tests.rs +++ b/wireframe_testing/src/runtime_ownership_benchmarks/client_pool_benchmark_support/measurement/distribution/tests.rs @@ -33,7 +33,7 @@ fn an_empty_distribution_reports_no_statistics() { let latency = ContendedLatency::default(); assert_eq!(latency.successes(), 0); assert_eq!(latency.failure_count(), 0); - assert!(latency.samples().is_empty()); + assert_eq!(latency.samples(), []); assert_eq!(latency.min(), None); assert_eq!(latency.max(), None); assert_eq!(latency.median(), None);