go.temporal.io/server@v1.23.0/common/clock/event_time_source_test.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package clock_test
    26  
    27  import (
    28  	"fmt"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/stretchr/testify/assert"
    33  	"go.temporal.io/server/common/clock"
    34  )
    35  
    36  // event is a helper to verify how many times a callback was triggered. Because callbacks are triggered synchronously
    37  // with calls to EventTimeSource.Advance, we don't need any further synchronization.
    38  type event struct {
    39  	t     *testing.T
    40  	count int
    41  }
    42  
    43  // Fire is the callback to be triggered.
    44  func (e *event) Fire() {
    45  	e.count++
    46  }
    47  
    48  // AssertFiredOnce asserts that the callback was triggered exactly once.
    49  func (e *event) AssertFiredOnce(msg string) {
    50  	e.t.Helper()
    51  	assert.Equal(e.t, 1, e.count, msg)
    52  }
    53  
    54  // AssertNotFired asserts that the callback was not triggered.
    55  func (e *event) AssertNotFired(msg string) {
    56  	e.t.Helper()
    57  	assert.Zero(e.t, e.count, msg)
    58  }
    59  
    60  func ExampleEventTimeSource() {
    61  	// Create a new fake timeSource.
    62  	source := clock.NewEventTimeSource()
    63  
    64  	// Create a timer which fires after 1 second.
    65  	source.AfterFunc(time.Second, func() {
    66  		fmt.Println("timer fired")
    67  	})
    68  
    69  	// Advance the time source by 1 second.
    70  	fmt.Println("advancing time source by 1 second")
    71  	source.Advance(time.Second)
    72  	fmt.Println("time source advanced")
    73  
    74  	// Output:
    75  	// advancing time source by 1 second
    76  	// timer fired
    77  	// time source advanced
    78  }
    79  
    80  func TestEventTimeSource_AfterFunc(t *testing.T) {
    81  	t.Parallel()
    82  
    83  	// Create a new fake time source and an event to fire.
    84  	source := clock.NewEventTimeSource()
    85  	ev := event{t: t}
    86  
    87  	// Create a timer which fires after 2ns.
    88  	source.AfterFunc(2, ev.Fire)
    89  
    90  	// Advance the time source by 1ns.
    91  	source.Advance(1)
    92  	ev.AssertNotFired(
    93  		"Advancing the time source should not fire the timer if its deadline still has not been reached",
    94  	)
    95  
    96  	// Advance the time source by 1ns more.
    97  	source.Advance(1)
    98  	ev.AssertFiredOnce("Advancing a time source past a timer's deadline should fire the timer")
    99  }
   100  
   101  func TestEventTimeSource_AfterFunc_Reset(t *testing.T) {
   102  	t.Parallel()
   103  
   104  	// Create a new fake time source and two events to fire.
   105  	source := clock.NewEventTimeSource()
   106  	ev1 := event{t: t}
   107  	ev2 := event{t: t}
   108  
   109  	// Create a timer for each event which fires after 2ns.
   110  	timer := source.AfterFunc(2, ev1.Fire)
   111  	source.AfterFunc(2, ev2.Fire)
   112  
   113  	// Advance the time source by 1ns and verify that neither timer has fired.
   114  	source.Advance(1)
   115  	ev1.AssertNotFired("Timer should not fire before deadline")
   116  	ev2.AssertNotFired("Timer should not fire before deadline")
   117  
   118  	// Reset the first timer to fire after an additional 2ns.
   119  	assert.True(t, timer.Reset(2), "`Reset` should return true if the timer was not already stopped")
   120  
   121  	// Advance the time source by 1ns and verify that the first timer has not fired but the second timer has.
   122  	source.Advance(1)
   123  	ev1.AssertNotFired("Timer which was reset should not fire after original deadline but before new deadline")
   124  	ev2.AssertFiredOnce("Timer which was not reset should fire after deadline")
   125  
   126  	// Advance the time source by 1ns more and verify that the reset timer has fired.
   127  	source.Advance(1)
   128  	ev1.AssertFiredOnce("The reset timer should fire after its new deadline")
   129  
   130  	// Reset the first timer and advance the time source past the new deadline to verify that the timer does not fire
   131  	// again.
   132  	assert.False(t, timer.Reset(1), "`Reset` should return false if the timer was already stopped")
   133  	source.Advance(1)
   134  	ev1.AssertFiredOnce("The timer should never fire twice, even if it was reset")
   135  }
   136  
   137  func TestEventTimeSource_AfterFunc_Stop(t *testing.T) {
   138  	t.Parallel()
   139  
   140  	// Create a new fake time source and two events to fire.
   141  	source := clock.NewEventTimeSource()
   142  	ev1 := event{t: t}
   143  	ev2 := event{t: t}
   144  
   145  	// Create a timer for each event which fires after 1ns.
   146  	timer := source.AfterFunc(1, ev1.Fire)
   147  	source.AfterFunc(1, ev2.Fire)
   148  
   149  	// Stop the first timer.
   150  	assert.True(t, timer.Stop(), "`Stop` should return true if the timer was not already stopped")
   151  
   152  	// Advance the time source by 1ns and verify that the first timer has not fired and the second timer has.
   153  	source.Advance(1)
   154  	ev1.AssertNotFired("A timer should not fire if it was already stopped")
   155  	ev2.AssertFiredOnce("A timer which was not stopped should fire after its deadline")
   156  
   157  	// Verify that subsequent calls to `Stop` return false.
   158  	assert.False(t, timer.Stop(), "`Stop` return false if the timer was already stopped")
   159  }
   160  
   161  func TestEventTimeSource_AfterFunc_NegativeDelay(t *testing.T) {
   162  	t.Parallel()
   163  
   164  	// Create a new fake time source and one event to fire.
   165  	source := clock.NewEventTimeSource()
   166  	ev1 := event{t: t}
   167  
   168  	// Create a timer which fires after -1ns. This should fire immediately.
   169  	timer := source.AfterFunc(-1, ev1.Fire)
   170  
   171  	// Verify that the timer has fired.
   172  	ev1.AssertFiredOnce("A timer with a negative delay should fire immediately")
   173  
   174  	// Verify that the timer is stopped.
   175  	assert.False(t, timer.Stop(), "`Stop` should return false if the timer was already stopped")
   176  }
   177  
   178  func TestEventTimeSource_Update(t *testing.T) {
   179  	t.Parallel()
   180  
   181  	// Create a new fake time source and two events to fire.
   182  	source := clock.NewEventTimeSource()
   183  	ev1 := event{t: t}
   184  	ev2 := event{t: t}
   185  
   186  	// Create a timer for each event which fires after 1ns.
   187  	source.AfterFunc(1, ev1.Fire)
   188  	source.AfterFunc(1, ev2.Fire)
   189  
   190  	// Verify that the time source starts at Unix epoch.
   191  	assert.Equal(
   192  		t, time.Unix(0, 0), source.Now(), "The fake time source should start at the unix epoch",
   193  	)
   194  
   195  	// Update to move the time source forward by 1ns.
   196  	source.Update(time.Unix(0, 1))
   197  	assert.Equal(t, time.Unix(0, 1), source.Now())
   198  	ev1.AssertFiredOnce("Timer should fire after deadline")
   199  	ev2.AssertFiredOnce("Timer should fire after deadline")
   200  }