Repository navigation
fix(mux): advance the SI debounce test on the paused clock #4860
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,38 @@ use std::time::{Duration, Instant, SystemTime}; | |
|
|
||
| use hang::catalog::{MAX_SAFE_INTEGER, MOQ_EPOCH_UNIX_MILLIS}; | ||
|
|
||
| /// Host instant [`Clock`] measures from. | ||
| /// | ||
| /// This crate's tests use Tokio's clock, so a paused runtime can advance it. | ||
| /// Production keeps [`std::time::Instant`], which is what [`Clock::at`] and | ||
| /// [`Clock::capture`] take. A paused test in another crate does not move this clock. | ||
| #[cfg(not(test))] | ||
| type Mono = Instant; | ||
| #[cfg(test)] | ||
| type Mono = tokio::time::Instant; | ||
|
|
||
| fn mono_now() -> Mono { | ||
| #[cfg(not(test))] | ||
| { | ||
| Instant::now() | ||
| } | ||
| #[cfg(test)] | ||
| { | ||
| tokio::time::Instant::now() | ||
| } | ||
| } | ||
|
|
||
| fn mono_from(instant: Instant) -> Mono { | ||
| #[cfg(not(test))] | ||
| { | ||
| instant | ||
| } | ||
| #[cfg(test)] | ||
| { | ||
| tokio::time::Instant::from_std(instant) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: rg -n -A2 '^name = "tokio"$' Cargo.lockRepository: moq-dev/moq Length of output: 263 🏁 Script executed: git diff --unified=50 75387292dcb44eefa126eba279809d4d1eb21789 1a159163c8b1c00b2286c09af326811d807c7846 -- rs/moq-mux/src/clock.rs
printf '\n--- current clock.rs ---\n'
nl -ba rs/moq-mux/src/clock.rs | sed -n '1,190p'
printf '\n--- lockfile Tokio ---\n'
rg -n -A3 '^name = "tokio"$' Cargo.lockRepository: moq-dev/moq Length of output: 19766 🌐 Web query:
💡 Result: 🌐 Web query:
💡 Result: Use Tokio instants for paused-time inputs. Under 🐛 Suggested fix+ #[tokio::test(start_paused = true)]
+ async fn paused_time_aligns_at_and_capture() {
+ tokio::time::advance(Duration::from_secs(3)).await;
+ let epoch = tokio::time::Instant::now();
+ let clock = Clock::at(epoch.into_std(), moq_epoch()).unwrap();
+ assert_eq!(clock.now().as_micros(), 0);
+
+ tokio::time::advance(Duration::from_secs(3)).await;
+ let capture = clock
+ .capture(tokio::time::Instant::now().into_std())
+ .unwrap();
+ assert_eq!(capture.as_micros(), 3_000_000);
+ }🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
|
|
||
| /// The catalog clock for PTS zero at `wall`. | ||
| fn wall_clock(wall: SystemTime) -> crate::Result<hang::catalog::Clock> { | ||
| let unix_micros = wall | ||
|
|
@@ -51,7 +83,7 @@ fn wall_clock(wall: SystemTime) -> crate::Result<hang::catalog::Clock> { | |
| #[derive(Clone, Copy, Debug)] | ||
| pub struct Clock { | ||
| /// A monotonic instant, and what the clock read then in micros. | ||
| instant: Instant, | ||
| instant: Mono, | ||
| reading: u64, | ||
| wall: hang::catalog::Clock, | ||
| } | ||
|
|
@@ -81,7 +113,7 @@ impl Clock { | |
| /// sampling. Refuses an unrepresentable wall. | ||
| pub fn at(epoch: Instant, wall: SystemTime) -> crate::Result<Self> { | ||
| Ok(Self { | ||
| instant: epoch, | ||
| instant: mono_from(epoch), | ||
| reading: 0, | ||
| wall: wall_clock(wall)?, | ||
| }) | ||
|
|
@@ -92,7 +124,7 @@ impl Clock { | |
| /// Refuses a `since` so large that PTS zero lands before the moq epoch (2020), which the wall | ||
| /// mapping cannot name. | ||
| pub(crate) fn arrival(since: Duration) -> crate::Result<Self> { | ||
| let (instant, now) = (Instant::now(), SystemTime::now()); | ||
| let (instant, now) = (mono_now(), SystemTime::now()); | ||
| let unmappable = || crate::Error::UnmappableTimestamp(format!("{since:?} puts PTS zero before 2020")); | ||
| let zero = now.checked_sub(since).ok_or_else(unmappable)?; | ||
| Ok(Self { | ||
|
|
@@ -115,7 +147,8 @@ impl Clock { | |
| /// Refuses an instant ahead of now, which would claim the payload reached the transport before | ||
| /// it existed, and one before PTS zero, which no timestamp can name. | ||
| pub fn capture(&self, at: Instant) -> crate::Result<moq_net::Timestamp> { | ||
| if at > Instant::now() { | ||
| let at = mono_from(at); | ||
| if at > mono_now() { | ||
| return Err(crate::Error::InvalidCapture); | ||
| } | ||
| let micros = match at.checked_duration_since(self.instant) { | ||
|
|
@@ -281,4 +314,14 @@ mod tests { | |
| let max = moq_net::Timestamp::from_micros((1u64 << 62) - 1).unwrap(); | ||
| assert!(clock.wall_clock(max).is_err(), "past the JSON-safe range is refused"); | ||
| } | ||
|
|
||
| /// The SI debounce reads this clock. A paused runtime has to move it, or the | ||
| /// test sleeps real time for a window the runtime will not advance. | ||
| #[tokio::test(start_paused = true)] | ||
| async fn paused_time_advances_now() { | ||
| let clock = Clock::new(); | ||
| let start = clock.now().as_micros(); | ||
| tokio::time::advance(Duration::from_secs(3)).await; | ||
| assert_eq!(clock.now().as_micros() - start, 3_000_000); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a
#[tokio::test(start_paused = true)]after virtual time has advanced,tokio::time::Instant::now()includes the runtime's offset, butfrom_stdpreserves the raw host instant. Consequently,Clock::at(std::time::Instant::now(), wall)immediately reads as though the elapsed virtual time had already passed, andcapture(std::time::Instant::now())maps a current capture behindClock::now()or rejects it once the advance exceedsLEAD. Apply the current virtual offset during conversion, or isolate the virtual source to the SI debounce clock so the public clock'sat/capturecontract remains valid in the paused tests required here.AGENTS.md reference: rs/AGENTS.md:L51-L54
Useful? React with 👍 / 👎.