github.com/gofunct/common@v0.0.0-20190131174352-fd058c7fbf22/pkg/clock/testing/fake_clock.go (about)

     1  package testing
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"k8s.io/utils/clock"
     8  )
     9  
    10  var (
    11  	_ = clock.Clock(&FakeClock{})
    12  	_ = clock.Clock(&IntervalClock{})
    13  )
    14  
    15  // FakeClock implements clock.Clock, but returns an arbitrary time.
    16  type FakeClock struct {
    17  	lock sync.RWMutex
    18  	time time.Time
    19  
    20  	// waiters are waiting for the fake time to pass their specified time
    21  	waiters []*fakeClockWaiter
    22  }
    23  
    24  type fakeClockWaiter struct {
    25  	targetTime    time.Time
    26  	stepInterval  time.Duration
    27  	skipIfBlocked bool
    28  	destChan      chan time.Time
    29  	fired         bool
    30  }
    31  
    32  // NewFakeClock constructs a fake clock set to the provided time.
    33  func NewFakeClock(t time.Time) *FakeClock {
    34  	return &FakeClock{
    35  		time: t,
    36  	}
    37  }
    38  
    39  // Now returns f's time.
    40  func (f *FakeClock) Now() time.Time {
    41  	f.lock.RLock()
    42  	defer f.lock.RUnlock()
    43  	return f.time
    44  }
    45  
    46  // Since returns time since the time in f.
    47  func (f *FakeClock) Since(ts time.Time) time.Duration {
    48  	f.lock.RLock()
    49  	defer f.lock.RUnlock()
    50  	return f.time.Sub(ts)
    51  }
    52  
    53  // After is the fake version of time.After(d).
    54  func (f *FakeClock) After(d time.Duration) <-chan time.Time {
    55  	f.lock.Lock()
    56  	defer f.lock.Unlock()
    57  	stopTime := f.time.Add(d)
    58  	ch := make(chan time.Time, 1) // Don't block!
    59  	f.waiters = append(f.waiters, &fakeClockWaiter{
    60  		targetTime: stopTime,
    61  		destChan:   ch,
    62  	})
    63  	return ch
    64  }
    65  
    66  // NewTimer constructs a fake timer, akin to time.NewTimer(d).
    67  func (f *FakeClock) NewTimer(d time.Duration) clock.Timer {
    68  	f.lock.Lock()
    69  	defer f.lock.Unlock()
    70  	stopTime := f.time.Add(d)
    71  	ch := make(chan time.Time, 1) // Don't block!
    72  	timer := &fakeTimer{
    73  		fakeClock: f,
    74  		waiter: fakeClockWaiter{
    75  			targetTime: stopTime,
    76  			destChan:   ch,
    77  		},
    78  	}
    79  	f.waiters = append(f.waiters, &timer.waiter)
    80  	return timer
    81  }
    82  
    83  // Tick constructs a fake ticker, akin to time.Tick
    84  func (f *FakeClock) Tick(d time.Duration) <-chan time.Time {
    85  	if d <= 0 {
    86  		return nil
    87  	}
    88  	f.lock.Lock()
    89  	defer f.lock.Unlock()
    90  	tickTime := f.time.Add(d)
    91  	ch := make(chan time.Time, 1) // hold one tick
    92  	f.waiters = append(f.waiters, &fakeClockWaiter{
    93  		targetTime:    tickTime,
    94  		stepInterval:  d,
    95  		skipIfBlocked: true,
    96  		destChan:      ch,
    97  	})
    98  
    99  	return ch
   100  }
   101  
   102  // Step moves the clock by Duration and notifies anyone that's called After,
   103  // Tick, or NewTimer.
   104  func (f *FakeClock) Step(d time.Duration) {
   105  	f.lock.Lock()
   106  	defer f.lock.Unlock()
   107  	f.setTimeLocked(f.time.Add(d))
   108  }
   109  
   110  // SetTime sets the time.
   111  func (f *FakeClock) SetTime(t time.Time) {
   112  	f.lock.Lock()
   113  	defer f.lock.Unlock()
   114  	f.setTimeLocked(t)
   115  }
   116  
   117  // Actually changes the time and checks any waiters. f must be write-locked.
   118  func (f *FakeClock) setTimeLocked(t time.Time) {
   119  	f.time = t
   120  	newWaiters := make([]*fakeClockWaiter, 0, len(f.waiters))
   121  	for i := range f.waiters {
   122  		w := f.waiters[i]
   123  		if !w.targetTime.After(t) {
   124  
   125  			if w.skipIfBlocked {
   126  				select {
   127  				case w.destChan <- t:
   128  					w.fired = true
   129  				default:
   130  				}
   131  			} else {
   132  				w.destChan <- t
   133  				w.fired = true
   134  			}
   135  
   136  			if w.stepInterval > 0 {
   137  				for !w.targetTime.After(t) {
   138  					w.targetTime = w.targetTime.Add(w.stepInterval)
   139  				}
   140  				newWaiters = append(newWaiters, w)
   141  			}
   142  
   143  		} else {
   144  			newWaiters = append(newWaiters, f.waiters[i])
   145  		}
   146  	}
   147  	f.waiters = newWaiters
   148  }
   149  
   150  // HasWaiters returns true if After has been called on f but not yet satisfied (so you can
   151  // write race-free tests).
   152  func (f *FakeClock) HasWaiters() bool {
   153  	f.lock.RLock()
   154  	defer f.lock.RUnlock()
   155  	return len(f.waiters) > 0
   156  }
   157  
   158  // Sleep is akin to time.Sleep
   159  func (f *FakeClock) Sleep(d time.Duration) {
   160  	f.Step(d)
   161  }
   162  
   163  // IntervalClock implements clock.Clock, but each invocation of Now steps the clock forward the specified duration
   164  type IntervalClock struct {
   165  	Time     time.Time
   166  	Duration time.Duration
   167  }
   168  
   169  // Now returns i's time.
   170  func (i *IntervalClock) Now() time.Time {
   171  	i.Time = i.Time.Add(i.Duration)
   172  	return i.Time
   173  }
   174  
   175  // Since returns time since the time in i.
   176  func (i *IntervalClock) Since(ts time.Time) time.Duration {
   177  	return i.Time.Sub(ts)
   178  }
   179  
   180  // After is unimplemented, will panic.
   181  // TODO: make interval clock use FakeClock so this can be implemented.
   182  func (*IntervalClock) After(d time.Duration) <-chan time.Time {
   183  	panic("IntervalClock doesn't implement After")
   184  }
   185  
   186  // NewTimer is unimplemented, will panic.
   187  // TODO: make interval clock use FakeClock so this can be implemented.
   188  func (*IntervalClock) NewTimer(d time.Duration) clock.Timer {
   189  	panic("IntervalClock doesn't implement NewTimer")
   190  }
   191  
   192  // Tick is unimplemented, will panic.
   193  // TODO: make interval clock use FakeClock so this can be implemented.
   194  func (*IntervalClock) Tick(d time.Duration) <-chan time.Time {
   195  	panic("IntervalClock doesn't implement Tick")
   196  }
   197  
   198  // Sleep is unimplemented, will panic.
   199  func (*IntervalClock) Sleep(d time.Duration) {
   200  	panic("IntervalClock doesn't implement Sleep")
   201  }
   202  
   203  var _ = clock.Timer(&fakeTimer{})
   204  
   205  // fakeTimer implements clock.Timer based on a FakeClock.
   206  type fakeTimer struct {
   207  	fakeClock *FakeClock
   208  	waiter    fakeClockWaiter
   209  }
   210  
   211  // C returns the channel that notifies when this timer has fired.
   212  func (f *fakeTimer) C() <-chan time.Time {
   213  	return f.waiter.destChan
   214  }
   215  
   216  // Stop stops the timer and returns true if the timer has not yet fired, or false otherwise.
   217  func (f *fakeTimer) Stop() bool {
   218  	f.fakeClock.lock.Lock()
   219  	defer f.fakeClock.lock.Unlock()
   220  
   221  	newWaiters := make([]*fakeClockWaiter, 0, len(f.fakeClock.waiters))
   222  	for i := range f.fakeClock.waiters {
   223  		w := f.fakeClock.waiters[i]
   224  		if w != &f.waiter {
   225  			newWaiters = append(newWaiters, w)
   226  		}
   227  	}
   228  
   229  	f.fakeClock.waiters = newWaiters
   230  
   231  	return !f.waiter.fired
   232  }
   233  
   234  // Reset resets the timer to the fake clock's "now" + d. It returns true if the timer has not yet
   235  // fired, or false otherwise.
   236  func (f *fakeTimer) Reset(d time.Duration) bool {
   237  	f.fakeClock.lock.Lock()
   238  	defer f.fakeClock.lock.Unlock()
   239  
   240  	active := !f.waiter.fired
   241  
   242  	f.waiter.fired = false
   243  	f.waiter.targetTime = f.fakeClock.time.Add(d)
   244  
   245  	var isWaiting bool
   246  	for i := range f.fakeClock.waiters {
   247  		w := f.fakeClock.waiters[i]
   248  		if w == &f.waiter {
   249  			isWaiting = true
   250  			break
   251  		}
   252  	}
   253  	if !isWaiting {
   254  		f.fakeClock.waiters = append(f.fakeClock.waiters, &f.waiter)
   255  	}
   256  
   257  	return active
   258  }