github.com/rogpeppe/clock@v0.0.0-20190514195947-2896927a307a/testclock/clock.go (about)

     1  // Copyright 2015-2018 Canonical Ltd.
     2  // Licensed under the LGPLv3, see LICENCE file for details.
     3  
     4  package testclock
     5  
     6  import (
     7  	"fmt"
     8  	"runtime/debug"
     9  	"sort"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/rogpeppe/clock"
    14  )
    15  
    16  // timer implements a mock clock.Timer for testing purposes.
    17  type timer struct {
    18  	deadline time.Time
    19  	clock    *Clock
    20  	c        chan time.Time
    21  	// trigger is called when the timer expires. It is
    22  	// called with the clock mutex held and will not block.
    23  	trigger func()
    24  	stack   []byte
    25  }
    26  
    27  // Reset is part of the clock.Timer interface.
    28  func (t *timer) Reset(d time.Duration) bool {
    29  	return t.clock.reset(t, d)
    30  }
    31  
    32  // Stop is part of the clock.Timer interface.
    33  func (t *timer) Stop() bool {
    34  	return t.clock.stop(t)
    35  }
    36  
    37  // Chan is part of the clock.Timer interface.
    38  func (t *timer) Chan() <-chan time.Time {
    39  	return t.c
    40  }
    41  
    42  // Clock implements a mock clock.Clock for testing purposes.
    43  type Clock struct {
    44  	mu           sync.Mutex
    45  	log          func(string)
    46  	now          time.Time
    47  	waiting      []*timer // timers waiting to fire, sorted by deadline.
    48  	notifyAlarms chan struct{}
    49  }
    50  
    51  // NewClock returns a new clock set to the supplied time. If your SUT needs to
    52  // call After, AfterFunc, NewTimer or Timer.Reset more than 10000 times: (1)
    53  // you have probably written a bad test; and (2) you'll need to read from the
    54  // Alarms chan to keep the buffer clear.
    55  func NewClock(now time.Time) *Clock {
    56  	return &Clock{
    57  		log:          func(string) {},
    58  		now:          now,
    59  		notifyAlarms: make(chan struct{}, 10000),
    60  	}
    61  }
    62  
    63  // SetLog sets the log function that's called if something that probably
    64  // shouldn't happen occurs.
    65  func (clock *Clock) SetLog(log func(msg string)) {
    66  	clock.log = log
    67  }
    68  
    69  // Now is part of the clock.Clock interface.
    70  func (clock *Clock) Now() time.Time {
    71  	clock.mu.Lock()
    72  	defer clock.mu.Unlock()
    73  	return clock.now
    74  }
    75  
    76  // After is part of the clock.Clock interface.
    77  func (clock *Clock) After(d time.Duration) <-chan time.Time {
    78  	return clock.NewTimer(d).Chan()
    79  }
    80  
    81  func (clock *Clock) NewTimer(d time.Duration) clock.Timer {
    82  	c := make(chan time.Time, 1)
    83  	return clock.addAlarm(d, c, func() {
    84  		c <- clock.now
    85  	})
    86  }
    87  
    88  // AfterFunc is part of the clock.Clock interface.
    89  func (clock *Clock) AfterFunc(d time.Duration, f func()) clock.Timer {
    90  	return clock.addAlarm(d, nil, func() {
    91  		go f()
    92  	})
    93  }
    94  
    95  func (clock *Clock) addAlarm(d time.Duration, c chan time.Time, trigger func()) *timer {
    96  	defer clock.notifyAlarm()
    97  	clock.mu.Lock()
    98  	defer clock.mu.Unlock()
    99  	t := &timer{
   100  		c:        c,
   101  		deadline: clock.now.Add(d),
   102  		clock:    clock,
   103  		trigger:  trigger,
   104  		stack:    debug.Stack(),
   105  	}
   106  	clock.addTimer(t)
   107  	clock.triggerAll()
   108  	return t
   109  }
   110  
   111  // Advance advances the result of Now by the supplied duration, and sends
   112  // the "current" time on all alarms which are no longer "in the future".
   113  func (clock *Clock) Advance(d time.Duration) {
   114  	clock.mu.Lock()
   115  	defer clock.mu.Unlock()
   116  	clock.now = clock.now.Add(d)
   117  	if len(clock.waiting) == 0 {
   118  		clock.log("advancing a clock that has nothing waiting: cf. https://github.com/juju/juju/wiki/Intermittent-failures")
   119  	}
   120  	clock.triggerAll()
   121  }
   122  
   123  // WaitAdvance functions the same as Advance, but only if there is n timers in
   124  // clock.waiting. This came about while fixing lp:1607044 intermittent
   125  // failures.  It turns out that testing.Clock.Advance might advance the time
   126  // and trigger notifications before triggers are set. So we wait a limited time
   127  // 'w' for 'n' timers to show up in clock.waiting, and if they do we advance
   128  // 'd'.
   129  func (clock *Clock) WaitAdvance(d, w time.Duration, n int) error {
   130  	pause := w / 10
   131  	if pause > 10*time.Millisecond {
   132  		pause = 10 * time.Millisecond
   133  	}
   134  	finalTimeout := time.After(w)
   135  	next := time.After(0)
   136  	for {
   137  		select {
   138  		case <-finalTimeout:
   139  			if clock.hasNWaiters(n) {
   140  				clock.Advance(d)
   141  				return nil
   142  			}
   143  			clock.mu.Lock()
   144  			got := len(clock.waiting)
   145  			var stacks string
   146  			for _, t := range clock.waiting {
   147  				stacks += fmt.Sprintf("timer deadline: %v\n%s", t.deadline, string(t.stack))
   148  			}
   149  			clock.mu.Unlock()
   150  			return fmt.Errorf(
   151  				"got %d timers added after waiting %s: wanted %d, stacks:\n%s",
   152  				got, w.String(), n, stacks)
   153  		case <-next:
   154  			if clock.hasNWaiters(n) {
   155  				clock.Advance(d)
   156  				return nil
   157  			}
   158  			next = time.After(pause)
   159  		}
   160  	}
   161  }
   162  
   163  // hasNWaiters checks if the clock currently has 'n' timers waiting to fire.
   164  func (clock *Clock) hasNWaiters(n int) bool {
   165  	clock.mu.Lock()
   166  	hasWaiters := len(clock.waiting) == n
   167  	clock.mu.Unlock()
   168  	return hasWaiters
   169  }
   170  
   171  // Alarms returns a channel on which you can read one value for every call to
   172  // After and AfterFunc; and for every successful Timer.Reset backed by this
   173  // Clock. It might not be elegant but it's necessary when testing time logic
   174  // that runs on a goroutine other than that of the test.
   175  func (clock *Clock) Alarms() <-chan struct{} {
   176  	return clock.notifyAlarms
   177  }
   178  
   179  // triggerAll triggers any alarms that are currently due and removes them
   180  // from clock.waiting.
   181  func (clock *Clock) triggerAll() {
   182  	triggered := 0
   183  	for _, t := range clock.waiting {
   184  		if clock.now.Before(t.deadline) {
   185  			break
   186  		}
   187  		t.trigger()
   188  		triggered++
   189  	}
   190  	clock.waiting = clock.waiting[triggered:]
   191  }
   192  
   193  // reset is the underlying implementation of clock.Timer.Reset, which may be
   194  // called by any Timer backed by this Clock.
   195  func (clock *Clock) reset(t *timer, d time.Duration) bool {
   196  	defer clock.notifyAlarm()
   197  	clock.mu.Lock()
   198  	defer clock.mu.Unlock()
   199  
   200  	found := false
   201  	for _, wt := range clock.waiting {
   202  		if wt == t {
   203  			found = true
   204  		}
   205  	}
   206  	if !found {
   207  		clock.waiting = append(clock.waiting, t)
   208  	}
   209  	t.deadline = clock.now.Add(d)
   210  	sort.Sort(byDeadline(clock.waiting))
   211  	if d <= 0 {
   212  		// If duration is <= 0, that means we should be triggering the
   213  		// Timer right away, as "now" has already occured.
   214  		clock.triggerAll()
   215  	}
   216  	return found
   217  }
   218  
   219  // stop is the underlying implementation of clock.Timer.Reset, which may be
   220  // called by any Timer backed by this Clock.
   221  func (clock *Clock) stop(t *timer) bool {
   222  	clock.mu.Lock()
   223  	defer clock.mu.Unlock()
   224  
   225  	for i, wt := range clock.waiting {
   226  		if wt == t {
   227  			clock.waiting = removeFromSlice(clock.waiting, i)
   228  			return true
   229  		}
   230  	}
   231  	return false
   232  }
   233  
   234  // addTimer adds an alarm at time t.
   235  func (clock *Clock) addTimer(t *timer) {
   236  	clock.waiting = append(clock.waiting, t)
   237  	sort.Sort(byDeadline(clock.waiting))
   238  }
   239  
   240  // notifyAlarm sends a value on the channel exposed by Alarms().
   241  func (clock *Clock) notifyAlarm() {
   242  	select {
   243  	case clock.notifyAlarms <- struct{}{}:
   244  	default:
   245  		panic("alarm notification buffer full")
   246  	}
   247  }
   248  
   249  // byDeadline is used to sort alarms by time.
   250  type byDeadline []*timer
   251  
   252  func (a byDeadline) Len() int           { return len(a) }
   253  func (a byDeadline) Less(i, j int) bool { return a[i].deadline.Before(a[j].deadline) }
   254  func (a byDeadline) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   255  
   256  // removeFromSlice removes item at the specified index from the slice.
   257  func removeFromSlice(sl []*timer, index int) []*timer {
   258  	return append(sl[:index], sl[index+1:]...)
   259  }
   260  
   261  // AutoAdvancingClock wraps a clock.Clock, calling the Advance
   262  // function whenever After or AfterFunc are called.
   263  type AutoAdvancingClock struct {
   264  	clock.Clock
   265  	Advance func(time.Duration)
   266  }
   267  
   268  func (c *AutoAdvancingClock) After(d time.Duration) <-chan time.Time {
   269  	ch := c.Clock.After(d)
   270  	c.Advance(d)
   271  	return ch
   272  }
   273  
   274  func (c *AutoAdvancingClock) AfterFunc(d time.Duration, f func()) clock.Timer {
   275  	t := c.Clock.AfterFunc(d, f)
   276  	c.Advance(d)
   277  	return t
   278  }