github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/testing/clock.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package testing
     5  
     6  import (
     7  	"sort"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/juju/testing"
    12  	"github.com/juju/utils/clock"
    13  )
    14  
    15  // timerClock exposes the underlying Clock's capabilities to a Timer.
    16  type timerClock interface {
    17  	reset(id int, d time.Duration) bool
    18  	stop(id int) bool
    19  }
    20  
    21  // Timer implements a mock clock.Timer for testing purposes.
    22  type Timer struct {
    23  	ID    int
    24  	clock timerClock
    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.ID, d)
    30  }
    31  
    32  // Stop is part of the clock.Timer interface.
    33  func (t *Timer) Stop() bool {
    34  	return t.clock.stop(t.ID)
    35  }
    36  
    37  // stoppedTimer is a no-op implementation of clock.Timer.
    38  type stoppedTimer struct{}
    39  
    40  // Reset is part of the clock.Timer interface.
    41  func (stoppedTimer) Reset(time.Duration) bool { return false }
    42  
    43  // Stop is part of the clock.Timer interface.
    44  func (stoppedTimer) Stop() bool { return false }
    45  
    46  // Clock implements a mock clock.Clock for testing purposes.
    47  type Clock struct {
    48  	mu             sync.Mutex
    49  	now            time.Time
    50  	alarms         []alarm
    51  	currentAlarmID int
    52  	notifyAlarms   chan struct{}
    53  }
    54  
    55  // NewClock returns a new clock set to the supplied time. If your SUT needs to
    56  // call After, AfterFunc, or Timer.Reset more than 1024 times: (1) you have
    57  // probably written a bad test; and (2) you'll need to read from the Alarms
    58  // chan to keep the buffer clear.
    59  func NewClock(now time.Time) *Clock {
    60  	return &Clock{
    61  		now:          now,
    62  		notifyAlarms: make(chan struct{}, 1024),
    63  	}
    64  }
    65  
    66  // Now is part of the clock.Clock interface.
    67  func (clock *Clock) Now() time.Time {
    68  	clock.mu.Lock()
    69  	defer clock.mu.Unlock()
    70  	return clock.now
    71  }
    72  
    73  // After is part of the clock.Clock interface.
    74  func (clock *Clock) After(d time.Duration) <-chan time.Time {
    75  	defer clock.notifyAlarm()
    76  	clock.mu.Lock()
    77  	defer clock.mu.Unlock()
    78  	notify := make(chan time.Time, 1)
    79  	if d <= 0 {
    80  		notify <- clock.now
    81  	} else {
    82  		clock.setAlarm(clock.now.Add(d), func() { notify <- clock.now })
    83  	}
    84  	return notify
    85  }
    86  
    87  // AfterFunc is part of the clock.Clock interface.
    88  func (clock *Clock) AfterFunc(d time.Duration, f func()) clock.Timer {
    89  	defer clock.notifyAlarm()
    90  	clock.mu.Lock()
    91  	defer clock.mu.Unlock()
    92  	if d <= 0 {
    93  		f()
    94  		return &stoppedTimer{}
    95  	}
    96  	id := clock.setAlarm(clock.now.Add(d), f)
    97  	return &Timer{id, clock}
    98  }
    99  
   100  // Advance advances the result of Now by the supplied duration, and sends
   101  // the "current" time on all alarms which are no longer "in the future".
   102  func (clock *Clock) Advance(d time.Duration) {
   103  	clock.mu.Lock()
   104  	defer clock.mu.Unlock()
   105  	clock.now = clock.now.Add(d)
   106  	triggered := 0
   107  	for _, alarm := range clock.alarms {
   108  		if clock.now.Before(alarm.time) {
   109  			break
   110  		}
   111  		alarm.trigger()
   112  		triggered++
   113  	}
   114  	clock.alarms = clock.alarms[triggered:]
   115  }
   116  
   117  // Alarms returns a channel on which you can read one value for every call to
   118  // After and AfterFunc; and for every successful Timer.Reset backed by this
   119  // Clock. It might not be elegant but it's necessary when testing time logic
   120  // that runs on a goroutine other than that of the test.
   121  func (clock *Clock) Alarms() <-chan struct{} {
   122  	return clock.notifyAlarms
   123  }
   124  
   125  // reset is the underlying implementation of clock.Timer.Reset, which may be
   126  // called by any Timer backed by this Clock.
   127  func (clock *Clock) reset(id int, d time.Duration) bool {
   128  	clock.mu.Lock()
   129  	defer clock.mu.Unlock()
   130  
   131  	for i, alarm := range clock.alarms {
   132  		if id == alarm.ID {
   133  			defer clock.notifyAlarm()
   134  			clock.alarms[i].time = clock.now.Add(d)
   135  			sort.Sort(byTime(clock.alarms))
   136  			return true
   137  		}
   138  	}
   139  	return false
   140  }
   141  
   142  // stop is the underlying implementation of clock.Timer.Reset, which may be
   143  // called by any Timer backed by this Clock.
   144  func (clock *Clock) stop(id int) bool {
   145  	clock.mu.Lock()
   146  	defer clock.mu.Unlock()
   147  
   148  	for i, alarm := range clock.alarms {
   149  		if id == alarm.ID {
   150  			clock.alarms = removeFromSlice(clock.alarms, i)
   151  			return true
   152  		}
   153  	}
   154  	return false
   155  }
   156  
   157  // setAlarm adds an alarm at time t.
   158  // It also sorts the alarms and increments the current ID by 1.
   159  func (clock *Clock) setAlarm(t time.Time, trigger func()) int {
   160  	alarm := alarm{
   161  		time:    t,
   162  		trigger: trigger,
   163  		ID:      clock.currentAlarmID,
   164  	}
   165  	clock.alarms = append(clock.alarms, alarm)
   166  	sort.Sort(byTime(clock.alarms))
   167  	clock.currentAlarmID = clock.currentAlarmID + 1
   168  	return alarm.ID
   169  }
   170  
   171  // notifyAlarm sends a value on the channel exposed by Alarms().
   172  func (clock *Clock) notifyAlarm() {
   173  	select {
   174  	case clock.notifyAlarms <- struct{}{}:
   175  	default:
   176  		panic("alarm notification buffer full")
   177  	}
   178  }
   179  
   180  // alarm records the time at which we're expected to execute trigger.
   181  type alarm struct {
   182  	ID      int
   183  	time    time.Time
   184  	trigger func()
   185  }
   186  
   187  // byTime is used to sort alarms by time.
   188  type byTime []alarm
   189  
   190  func (a byTime) Len() int           { return len(a) }
   191  func (a byTime) Less(i, j int) bool { return a[i].time.Before(a[j].time) }
   192  func (a byTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   193  
   194  // removeFromSlice removes item at the specified index from the slice.
   195  func removeFromSlice(sl []alarm, index int) []alarm {
   196  	return append(sl[:index], sl[index+1:]...)
   197  }
   198  
   199  type StubClock struct {
   200  	*testing.Stub
   201  
   202  	ReturnNow       time.Time
   203  	ReturnAfter     <-chan time.Time
   204  	ReturnAfterFunc clock.Timer
   205  }
   206  
   207  func NewStubClock(stub *testing.Stub) *StubClock {
   208  	return &StubClock{
   209  		Stub: stub,
   210  	}
   211  }
   212  
   213  func (s *StubClock) Now() time.Time {
   214  	s.AddCall("Now")
   215  	s.NextErr() // pop one off
   216  	return s.ReturnNow
   217  }
   218  
   219  func (s *StubClock) After(d time.Duration) <-chan time.Time {
   220  	s.AddCall("After", d)
   221  	s.NextErr() // pop one off
   222  	return s.ReturnAfter
   223  }
   224  
   225  func (s *StubClock) AfterFunc(d time.Duration, f func()) clock.Timer {
   226  	s.AddCall("AfterFunc", d, f)
   227  	s.NextErr() // pop one off
   228  	return s.ReturnAfterFunc
   229  }
   230  
   231  // AutoAdvancingClock wraps a clock.Clock, calling the Advance
   232  // function whenever After or AfterFunc are called.
   233  type AutoAdvancingClock struct {
   234  	clock.Clock
   235  	Advance func(time.Duration)
   236  }
   237  
   238  func (c *AutoAdvancingClock) After(d time.Duration) <-chan time.Time {
   239  	ch := c.Clock.After(d)
   240  	c.Advance(d)
   241  	return ch
   242  }
   243  
   244  func (c *AutoAdvancingClock) AfterFunc(d time.Duration, f func()) clock.Timer {
   245  	t := c.Clock.AfterFunc(d, f)
   246  	c.Advance(d)
   247  	return t
   248  }