tree: 31f5378f787491436eb80e4e1b109b8855fef30f [path history] [tgz]
  1. examples/
  2. fidl/
  3. lib/
  4. named-timer/
  5. svc/
  6. BUILD.gn
  7. OWNERS
  8. README.md
src/lib/fake-clock/README.md

Fake-clock

A system enabling a test component to manipulate monotonic and boot time for components under test.

Fake-clock consists of a library and service component.

The service component maintains a fake monotonic time, which may be accessed through the fuchsia.testing.FakeClock protocol and may be controlled through the fuchsia.testing.FakeClockControl protocol.

The library overrides syscalls defined in the vDSO that interact with time or have a built in deadline. The overrides route time-based waits and signals to the service component via the fuchsia.testing.FakeClock protocol.

In addition, the named-timer crate allows an author to annotate critical deadlines in a component with names. When linked against the fake-clock library, the deadline set and expired events are reported to the fake-clock service. A test author may register interest in these events to stop time when these critical points are reached.

Use

A test that wishes to use fake-clock must correctly link the library to each component in the test and route the fuchsia.testing.* protocols.

Add the service component as a dependency in the test package.

fuchsia_test_package("my-integ-test") {
  test_components = [ "integ-test-component" ]
  deps = [
    ":component_with_fake_time",
    "//src/lib/fake-clock/svc",
  ]
}

Link the library against each binary. Since the library is only available to test targets, create a new version of the binary that depends on “//src/lib/fake-clock/lib”.

# Rust binary
rustc_binary("bin_with_fake_time") {
    ...
    testonly = true
    non_rust_deps = [ "//src/lib/fake-clock/lib" ]
}

# C++ binary
executable("bin_with_fake_time") {
    ...
    testonly = true
    deps = [
        ...
        "//src/lib/fake-clock/lib"
    ]
}

# Component with fake time
fuchsia_component("component_with_fake_time") {
    testonly = true
    component_name = "component_with_fake_time"
    manifest "meta/component_with_fake_time.cml"
    deps = [":bin_with_fake_time"]
}

Include the fake-clock shard in the manifest for each component with fake time.

    ...
    "include": [
        "//src/lib/fake-clock/lib/client.shard.cml"
    ],

Add the fuchsia.testing.FakeClockControl protocol to the test component's manifest.

    ...
    "use": [
        {
            "protocol": [
                "fuchsia.testing.FakeClockControl"
            ]
        }
    ]

The test may then connect to fuchsia.testing.FakeClockControl to control monotonic or boot time.

See the examples directory for example setups.

Design Invariants

When using FakeClockControl to manipulate time, it's important to be aware of the following invariants to avoid putting the system into an inconsistent state:

  • Advance of Monotonic Clock Advances the Boot Clock: The Advance method in FakeClockControl takes a single increment which is applied to both clocks equally. This prevents the monotonic clock from moving forward independently of the boot clock.

  • Mono-to-Boot Offset is Always Non-Negative and Never Decreases: The offset between the boot and monotonic clocks (boot_time - monotonic_time) must obey these two invariants. The FIDL API offers IncrementMonoToBootOffsetBy to allow advances on the boot clock independently of the monotonic clock.

Adhering to these invariants is crucial for maintaining a consistent and predictable timeline during testing.

Boot Clock Support

In addition to the monotonic clock, the fake-clock library provides full support for ZX_CLOCK_BOOT. Tests can create, set, and cancel boot timers, and the fake-clock service will correctly manage their lifecycle.

How it Works

The library intercepts the zx_timer_create syscall. When a timer is created, its handle and the specified clock type (ZX_CLOCK_MONOTONIC or ZX_CLOCK_BOOT) are stored in a client-side map. Subsequent calls like zx_timer_set use this map to determine which clock the timer belongs to and forward the request to the correct implementation in the fake-clock service.

Example Usage

Here is a simple example of creating and waiting on a boot timer in a test:

TEST_F(FakeClockTest, timer_fire_boot) {
  auto [t1_boot, _] = GetTime();
  auto deadline = t1_boot + zx::msec(500);
  zx::timer timer;
  ASSERT_OK(zx::timer::create(0, ZX_CLOCK_BOOT, &timer));
  ASSERT_OK(timer.set(deadline, /*slack*/zx::msec(10)));
  Advance(zx::msec(500), zx::msec(600));
  zx_signals_t signals;
  ASSERT_OK(timer.wait_one(ZX_TIMER_SIGNALED, zx::time::infinite(), &signals));
  ASSERT_EQ(signals, ZX_TIMER_SIGNALED);
}

Troubleshooting

“UTC clock may interact in unexpected ways with the fake-clock library”

If your test binary crashes with this message, this means that your tests are attempting to use the fake-clock library together with a real UTC clock. This can happen even if your code does not explicitly get the UTC clock handle, for example if one of your dependencies does so instead.

Having this happen in your fake-clock test is not a good idea. Your tests may be subtly wrong as a result of the interaction of the fake monotonic clock and the real UTC clock, depending on whether there is a code path in which this interaction is important.

You should make a choice that determines what happens next. Your options to fix this are:

  1. Use the Timekeeper Test Realm Factory to spin up a Test Realm that wires all the clock-related dependencies properly. It is slightly more involved to set up, but will work correctly.

  2. Use the dependency //src/lib/fake-clock/lib:lib_with_utc instead of //src/lib/fake-clock/lib. This dependency ignores the potential UTC problems. To use this you must accept the possibility that your test might be subtly broken as a result.