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