| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This is an example of a Go use of FakeClock to show that the syscalls are |
| // being overwritten. This file is based on the C++ test bed located in |
| // //src/lib/fake-clock/lib/fake_clock_test.cc and include a subset of those |
| // tests. |
| |
| package fake_clock |
| |
| import ( |
| "runtime" |
| "testing" |
| |
| "app/context" |
| "syscall/zx" |
| "syscall/zx/zxwait" |
| |
| mock_clock "fidl/fuchsia/testing" |
| ) |
| |
| var ctx *context.Context |
| |
| func init() { |
| ctx = context.CreateFromStartupInfo() |
| } |
| |
| type FakeClockTest struct { |
| control *mock_clock.FakeClockControlInterface |
| } |
| |
| // Helper for setting up the test, returns a control handle for ease of |
| // interacting with the fake clock. Also pauses so that the fake clock always |
| // starts in the same state. |
| func setup(t *testing.T) FakeClockTest { |
| service, control, _ := mock_clock.NewFakeClockControlInterfaceRequest() |
| ctx.ConnectToEnvService(service) |
| |
| // Always pause the fake clock before the test starts. |
| if err := control.Pause(); err != nil { |
| t.Fatalf("Error pausing: %s", err) |
| } |
| |
| return FakeClockTest{control} |
| } |
| |
| func (f *FakeClockTest) Close() error { |
| return f.control.Close() |
| } |
| |
| // Utility for advancing the fake clock by an integer amount. |
| func (f *FakeClockTest) advance(i int64) { |
| f.control.Advance(mock_clock.IncrementWithDetermined(i)) |
| } |
| |
| // Utility for calling the syscall for time which is intercepted by the fake |
| // clock. |
| func getSysTime() zx.Time { |
| return zx.Sys_clock_get_monotonic() |
| } |
| |
| // Advance the time by a constant, and ensure that time only advances by that |
| // constant. |
| func TestTimeAdvance(t *testing.T) { |
| // TODO(fxb/45644): Disabled due to flake |
| t.Skip("Skipped: zx_futex_wait flake (fxb/45644)") |
| control := setup(t) |
| defer control.Close() |
| |
| t1 := getSysTime() |
| control.advance(1) |
| t2 := getSysTime() |
| |
| if t2 != t1+1 { |
| t.Fatalf("Got t1 = %d, t2 = %d, want t2 = %d", t1, t2, t1+1) |
| } |
| } |
| |
| // Test the deadline_after syscall, ensure that the time returned is the paused |
| // time plus the duration. |
| func TestDeadlineAfter(t *testing.T) { |
| // TODO(fxb/45644): Disabled due to flake |
| t.Skip("Skipped: zx_futex_wait flake (fxb/45644)") |
| control := setup(t) |
| defer control.Close() |
| |
| var dur zx.Duration = 50 |
| |
| t1 := getSysTime() |
| t2 := zx.Sys_deadline_after(dur) |
| |
| if t2 != t1+50 { |
| t.Fatalf("Got t1 = %d, t2 = %d, want t2 = %d", t1, t2, t1+50) |
| } |
| } |
| |
| // Tests that the nanosleep blocking syscall wakes up after advancing the fake |
| // clock by the expected number of ns. |
| func TestNanosleep(t *testing.T) { |
| // TODO(fxb/45644): Disabled due to flake |
| t.Skip("Skipped: zx_futex_wait flake (fxb/45644)") |
| control := setup(t) |
| defer control.Close() |
| done := make(chan bool) |
| |
| var dur zx.Duration = 500 |
| deadline := zx.Sys_deadline_after(dur) |
| go func() { |
| zx.Sys_nanosleep(deadline) |
| done <- true |
| }() |
| |
| control.advance(250) |
| select { |
| case <-done: |
| t.Fatal("Nanosleep terminated before expected 500 ns.") |
| default: |
| // Expected case. |
| } |
| |
| control.advance(250) |
| // Waits for the goroutine to finish. |
| <-done |
| } |
| |
| // Ensures that the output of Sys_clock_get(ZX_CLOCK_MONOTONIC) matches the |
| // output of Sys_clock_get_monotonic. |
| func TestClockGet(t *testing.T) { |
| // TODO(fxb/45644): Disabled due to flake |
| t.Skip("Skipped: zx_futex_wait flake (fxb/45644)") |
| control := setup(t) |
| defer control.Close() |
| |
| control.advance(90000) |
| t1 := getSysTime() |
| var t2 zx.Time |
| if status := zx.Sys_clock_get(runtime.ZX_CLOCK_MONOTONIC, &t2); status != zx.ErrOk { |
| t.Fatalf("Sys_clock_get(ZX_CLOCK_MONOTONIC, %d) returned error: %s", t2, status) |
| } |
| if t1 != t2 { |
| t.Fatalf("clock_get_monotonic returned: %d, clock_get(ZX_CLOCK_MONOTONIC) returned: %d", t1, t2) |
| } |
| } |
| |
| // Tests that timing out on a Wait works as expected and status/signals are |
| // correct. |
| func TestWaitOneTimeout(t *testing.T) { |
| // TODO(fxb/45644): Disabled due to flake |
| t.Skip("Skipped: zx_futex_wait flake (fxb/45644)") |
| control := setup(t) |
| defer control.Close() |
| done := make(chan bool) |
| |
| var dur zx.Duration = 500 |
| deadline := zx.Sys_deadline_after(dur) |
| |
| event, err := zx.NewEvent(0) |
| if err != nil { |
| t.Fatalf("zx.NewEvent failed with err = %s", err) |
| } |
| defer event.Close() |
| |
| var obs zx.Signals |
| var error error |
| go func() { |
| obs, error = zxwait.Wait(*event.Handle(), zx.SignalEventSignaled, deadline) |
| done <- true |
| }() |
| |
| control.advance(500) |
| <-done |
| |
| if error == nil { |
| t.Fatal("Got: nil, want: zx.ErrTimedOut") |
| } |
| switch error := error.(type) { |
| case *zx.Error: |
| if error.Status != zx.ErrTimedOut { |
| t.Fatalf("Got status = %s, want status = %s", error.Status, zx.ErrTimedOut) |
| } |
| default: |
| t.Fatalf("Unexpected error type: %T", error) |
| } |
| |
| if obs != zx.SignalNone { |
| t.Fatalf("Got signal = %#v, want signal = %#v", obs, zx.SignalNone) |
| } |
| } |
| |
| // Tests that signaling on a Wait works as expected and status/signals are |
| // correct. |
| func TestWaitOneSignal(t *testing.T) { |
| // TODO(fxb/45644): Disabled due to flake |
| t.Skip("Skipped: zx_futex_wait flake (fxb/45644)") |
| control := setup(t) |
| defer control.Close() |
| done := make(chan bool) |
| |
| var dur zx.Duration = 500 |
| deadline := zx.Sys_deadline_after(dur) |
| |
| event, err := zx.NewEvent(0) |
| if err != nil { |
| t.Fatalf("zx.NewEvent failed with err = %s", err) |
| } |
| defer event.Close() |
| |
| var obs zx.Signals |
| var error error |
| go func() { |
| obs, error = zxwait.Wait(*event.Handle(), zx.SignalEventSignaled, deadline) |
| done <- true |
| }() |
| |
| event.Handle().Signal(0, zx.SignalEventSignaled) |
| <-done |
| |
| if error != nil { |
| t.Fatalf("Got: err = %s, want nil", error) |
| } |
| if obs != zx.SignalEventSignaled { |
| t.Fatalf("Got signal = %#v, want signal = %#v", obs, zx.SignalEventSignaled) |
| } |
| } |
| |
| // Tests that zx.WaitMany times out as expected with just 2 WaitItems. |
| func TestWaitManyTimeoutSmall(t *testing.T) { |
| // TODO(fxb/45644): Disabled due to flake |
| t.Skip("Skipped: zx_futex_wait flake (fxb/45644)") |
| control := setup(t) |
| defer control.Close() |
| done := make(chan bool) |
| |
| var dur zx.Duration = 500 |
| deadline := zx.Sys_deadline_after(dur) |
| |
| event1, err := zx.NewEvent(0) |
| if err != nil { |
| t.Fatalf("zx.NewEvent failed with err = %s", err) |
| } |
| defer event1.Close() |
| event2, err := zx.NewEvent(0) |
| if err != nil { |
| t.Fatalf("zx.NewEvent failed with err = %s", err) |
| } |
| defer event2.Close() |
| |
| items := []zx.WaitItem{ |
| {*event1.Handle(), zx.SignalEventSignaled, 0}, |
| {*event2.Handle(), zx.SignalEventSignaled, 0}, |
| } |
| |
| var error error |
| go func() { |
| error = zx.WaitMany(items, deadline) |
| done <- true |
| }() |
| |
| control.advance(500) |
| <-done |
| |
| if error == nil { |
| t.Fatal("Got: nil, want: zx.ErrTimedOut") |
| } |
| switch error := error.(type) { |
| case *zx.Error: |
| if error.Status != zx.ErrTimedOut { |
| t.Fatalf("Got status = %s, want status = %s", error.Status, zx.ErrTimedOut) |
| } |
| default: |
| t.Fatalf("Unexpected error type: %T", error) |
| } |
| |
| if obs := items[0].Pending; obs != zx.SignalNone { |
| t.Fatalf("Got signal = %#v, want signal = %#v", obs, zx.SignalNone) |
| } |
| if obs := items[1].Pending; obs != zx.SignalNone { |
| t.Fatalf("Got signal = %#v, want signal = %#v", obs, zx.SignalNone) |
| } |
| } |
| |
| // Tests that zx.WaitMany signals as expected with just 2 WaitItems. |
| func TestWaitManySignalSmall(t *testing.T) { |
| // TODO(fxb/45644): Disabled due to flake |
| t.Skip("Skipped: zx_futex_wait flake (fxb/45644)") |
| control := setup(t) |
| defer control.Close() |
| done := make(chan bool) |
| |
| var dur zx.Duration = 500 |
| deadline := zx.Sys_deadline_after(dur) |
| |
| event1, err := zx.NewEvent(0) |
| if err != nil { |
| t.Fatalf("zx.NewEvent failed with err = %s", err) |
| } |
| defer event1.Close() |
| event2, err := zx.NewEvent(0) |
| if err != nil { |
| t.Fatalf("zx.NewEvent failed with err = %s", err) |
| } |
| defer event2.Close() |
| |
| items := []zx.WaitItem{ |
| {*event1.Handle(), zx.SignalEventSignaled, 0}, |
| {*event2.Handle(), zx.SignalEventSignaled, 0}, |
| } |
| |
| var error error |
| go func() { |
| error = zx.WaitMany(items, deadline) |
| done <- true |
| }() |
| |
| event1.Handle().Signal(0, zx.SignalEventSignaled) |
| <-done |
| |
| if error != nil { |
| t.Fatalf("Got: err = %s, want nil", error) |
| } |
| if obs := items[0].Pending; obs != zx.SignalEventSignaled { |
| t.Fatalf("Got signal = %#v, want signal = %#v", obs, zx.SignalEventSignaled) |
| } |
| if obs := items[1].Pending; obs != zx.SignalNone { |
| t.Fatalf("Got signal = %#v, want signal = %#v", obs, zx.SignalNone) |
| } |
| } |