VirtualTimeScheduler

main

A Combine Scheduler that executes actions based on a virtual clock.
berikv/VirtualTimeScheduler

VirtualTimeScheduler

A Combine Scheduler that executes actions based on a virtual clock. Ideal for testing custom Publishers or testing time-dependent Pub-sub code.

The scheduler will run scheduled actions when they are due, based on a "virtual" clock. The schedulers clock can be adjusted using one of these methods.

  • run() - run until there are no more actions scheduled
  • step() - set the time to the due time of the first action that is due
  • advanceTime(by:) - advance the internal clock by a specified value
  • setTime(to:) - set the time to a value, the clock starts at .referenceTime

Actions can schedule other actions. The scheduler will run those actions if they are due, in the same time update call.

Combine provides scheduling publishers through these methods.

  • delay(for:tolerance:scheduler:options:)
  • debounce(for:scheduler:options:)
  • throttle(for:scheduler:latest:)

Tests for code that uses these methods will have to wait for the events to happen. When using the RunLoop or DispatchQueue schedulers, the test will take wall-clock time. Meaning that your test takes longer.

The VirtualTimeScheduler allows testing of such code without waiting in wall-clock time.

Usage

step() finds the next action due and updates the time to that actions due time and runs all actions due by that new time.

The run method increases the virtual time step-wise until all scheduled actions have completed.

"3 seconds later" refers to the "virtual time".

        let scheduler = VirtualTimeScheduler()

        let cancellable = Just(42)
            .delay(for: .seconds(3), scheduler: scheduler)
            .measureInterval(using: scheduler)
            .sink { value in
                print("Received \(value.magnitude) seconds later")
            }

        print("Before run \(Date())")
        scheduler.run()
        print("After run \(Date())")
        
        //  Before run 2022-01-20 14:13:11 +0000
        //  Received 3.0 seconds later
        //  After run 2022-01-20 14:13:11 +0000

The advanceTime(by:) method advances the virtual time and runs any scheduled actions that are due by the new time.

        let scheduler = VirtualTimeScheduler()

        let subject = PassthroughSubject<Int, Never>()
        let cancellable = subject
            .debounce(for: .seconds(1), scheduler: scheduler)
            .sink { value in
                let timeInterval = scheduler.now.timeIntervalSinceReferenceTime
                print("Received \(value), \(timeInterval) seconds later")
            }

        for value in 0..<4 {
            subject.send(value)
            scheduler.advanceTime(by: .seconds(0.1))
        }

        scheduler.advanceTime(by: .seconds(1))

        for value in 4..<8 {
            subject.send(value)
            scheduler.advanceTime(by: .seconds(0.1))
        }

        scheduler.advanceTime(by: .seconds(1))
        _ = cancellable

        // Prints:
        //   Received 3, 1.4 seconds later
        //   Received 7, 2.8 seconds later

It is also possible to set the time directly using setTime(to:).

Install

Package.swift

Edit the Package.swift file. Add the VirtualTimeScheduler as a dependency:

let package = Package(
    name: " ... ",
    products: [ ... ],
    dependencies: [
        .package(url: "https://github.com/berikv/VirtualTimeScheduler.git", from: "1.0.0") // here
    ],
    targets: [
        .target(
            name: " ... ",
            dependencies: [
                "VirtualTimeScheduler" // and here
            ]),
    ]
)

For .xcodeproj projects

  1. Open menu File > Add Packages...
  2. Search for "https://github.com/berikv/VirtualTimeScheduler.git" and click Add Package.
  3. Open your project file, select your target in "Targets".
  4. Open Dependencies
  5. Click the + sign
  6. Add VirtualTimeScheduler

Description

  • Swift Tools 5.5.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sun Mar 17 2024 23:35:50 GMT-0900 (Hawaii-Aleutian Daylight Time)