Skip to content
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

Simulate the inaccuracy of schedulers #71

Open
runloop opened this issue Jan 11, 2023 · 3 comments
Open

Simulate the inaccuracy of schedulers #71

runloop opened this issue Jan 11, 2023 · 3 comments

Comments

@runloop
Copy link

runloop commented Jan 11, 2023

This is a fantastic library and aids testing of time based asynchrony really well. But in a way, it only simulates the happiest path due to the precision of the scheduler. As we all know, when a timer is set it will complete at a point of timer after the scheduled time, if only a fraction of a second after. In your videos on Clocks, you demonstrate how this drift can add relatively quickly.

Would it be possible to add a feature where we can test against these small inaccuracies? So after advancing the scheduler, rather than the schedulers' .now property being set exactly to the next scheduled actions date, it could be set with a defined drift, maybe even the .minimumTolerance. This way, if we need to, we can test against these small inaccuracies of timing that can add up after many events.

@runloop
Copy link
Author

runloop commented Jan 11, 2023

This may be a naive approach, but it appears to work and all the tests still pass. It allows for the creation of a new test scheduler where the now date is always set to the next action date plus the minimum tolerance.

public final class TestScheduler<SchedulerTimeType, SchedulerOptions> ... {
  // ...
  public let minimumTolerance: SchedulerTimeType.Stride

  public init(now: SchedulerTimeType, minimumTolerance: SchedulerTimeType.Stride = .zero) {
    self.now = now
    self.minimumTolerance = minimumTolerance
  }


  // in both advance methods update now to next.date but include minimumTolerance.
  public func advance(to instant: SchedulerTimeType) {
    while self.lock.sync(operation: { self.now }) <= instant {
      self.lock.lock()
      self.scheduled.sort { ($0.date, $0.sequence) < ($1.date, $1.sequence) }
      
      guard
        let next = self.scheduled.first,
        instant >= next.date
      else {
        self.now = instant
        self.lock.unlock()
        return
      }
      
      self.now = next.date.advanced(by: minimumTolerance) // <-- include minimum tolerance to have predictable inaccuracy
      self.scheduled.removeFirst()
      self.lock.unlock()
      next.action()
    }
  }

// create a new test scheduler that includes defined inaccuracy.

extension RunLoop {
  /// ...
  public static func inaccurate(
    by minimumTolerance: SchedulerTimeType.Stride
  ) -> TestSchedulerOf<RunLoop> {
    .init(now: .init(.init(timeIntervalSince1970: 0)), minimumTolerance: minimumTolerance)
  }
}

@mbrandonw
Copy link
Member

Hey @runloop, this is a really great idea! Wanna PR it?

@runloop
Copy link
Author

runloop commented Jan 11, 2023

I gave it a shot.

#72

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants