~juju-qa/ubuntu/xenial/juju/2.0-rc2

« back to all changes in this revision

Viewing changes to src/github.com/juju/testing/clock.go

  • Committer: Nicholas Skaggs
  • Date: 2016-09-30 14:39:30 UTC
  • mfrom: (1.8.1)
  • Revision ID: nicholas.skaggs@canonical.com-20160930143930-vwwhrefh6ftckccy
import upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
        "github.com/juju/utils/clock"
12
12
)
13
13
 
14
 
// timerClock exposes the underlying Clock's capabilities to a Timer.
15
 
type timerClock interface {
16
 
        reset(id int, d time.Duration) bool
17
 
        stop(id int) bool
18
 
}
19
 
 
20
 
// Timer implements a mock clock.Timer for testing purposes.
21
 
type Timer struct {
22
 
        ID    int
23
 
        clock timerClock
24
 
}
25
 
 
26
 
// Reset is part of the clock.Timer interface.
27
 
func (t *Timer) Reset(d time.Duration) bool {
28
 
        return t.clock.reset(t.ID, d)
29
 
}
30
 
 
31
 
// Stop is part of the clock.Timer interface.
32
 
func (t *Timer) Stop() bool {
33
 
        return t.clock.stop(t.ID)
34
 
}
35
 
 
36
 
// stoppedTimer is a no-op implementation of clock.Timer.
37
 
type stoppedTimer struct{}
38
 
 
39
 
// Reset is part of the clock.Timer interface.
40
 
func (stoppedTimer) Reset(time.Duration) bool { return false }
41
 
 
42
 
// Stop is part of the clock.Timer interface.
43
 
func (stoppedTimer) Stop() bool { return false }
 
14
// timer implements a mock clock.Timer for testing purposes.
 
15
type timer struct {
 
16
        deadline time.Time
 
17
        clock    *Clock
 
18
        c        chan time.Time
 
19
        // trigger is called when the timer expires. It is
 
20
        // called with the clock mutex held and will not block.
 
21
        trigger func()
 
22
}
 
23
 
 
24
// Reset is part of the clock.Timer interface.
 
25
func (t *timer) Reset(d time.Duration) bool {
 
26
        return t.clock.reset(t, d)
 
27
}
 
28
 
 
29
// Stop is part of the clock.Timer interface.
 
30
func (t *timer) Stop() bool {
 
31
        return t.clock.stop(t)
 
32
}
 
33
 
 
34
// Chan is part of the clock.Timer interface.
 
35
func (t *timer) Chan() <-chan time.Time {
 
36
        return t.c
 
37
}
44
38
 
45
39
// Clock implements a mock clock.Clock for testing purposes.
46
40
type Clock struct {
47
 
        mu             sync.Mutex
48
 
        now            time.Time
49
 
        alarms         []alarm
50
 
        currentAlarmID int
51
 
        notifyAlarms   chan struct{}
 
41
        mu           sync.Mutex
 
42
        now          time.Time
 
43
        waiting      []*timer // timers waiting to fire, sorted by deadline.
 
44
        notifyAlarms chan struct{}
52
45
}
53
46
 
54
47
// NewClock returns a new clock set to the supplied time. If your SUT needs to
55
 
// call After, AfterFunc, or Timer.Reset more than 1024 times: (1) you have
 
48
// call After, AfterFunc, NewTimer or Timer.Reset more than 10000 times: (1) you have
56
49
// probably written a bad test; and (2) you'll need to read from the Alarms
57
50
// chan to keep the buffer clear.
58
51
func NewClock(now time.Time) *Clock {
59
52
        return &Clock{
60
53
                now:          now,
61
 
                notifyAlarms: make(chan struct{}, 1024),
 
54
                notifyAlarms: make(chan struct{}, 10000),
62
55
        }
63
56
}
64
57
 
71
64
 
72
65
// After is part of the clock.Clock interface.
73
66
func (clock *Clock) After(d time.Duration) <-chan time.Time {
74
 
        defer clock.notifyAlarm()
75
 
        clock.mu.Lock()
76
 
        defer clock.mu.Unlock()
77
 
        notify := make(chan time.Time, 1)
78
 
        if d <= 0 {
79
 
                notify <- clock.now
80
 
        } else {
81
 
                clock.setAlarm(clock.now.Add(d), func() { notify <- clock.now })
82
 
        }
83
 
        return notify
 
67
        return clock.NewTimer(d).Chan()
 
68
}
 
69
 
 
70
func (clock *Clock) NewTimer(d time.Duration) clock.Timer {
 
71
        c := make(chan time.Time, 1)
 
72
        return clock.addAlarm(d, c, func() {
 
73
                c <- clock.now
 
74
        })
84
75
}
85
76
 
86
77
// AfterFunc is part of the clock.Clock interface.
87
78
func (clock *Clock) AfterFunc(d time.Duration, f func()) clock.Timer {
 
79
        return clock.addAlarm(d, nil, func() {
 
80
                go f()
 
81
        })
 
82
}
 
83
 
 
84
func (clock *Clock) addAlarm(d time.Duration, c chan time.Time, trigger func()) *timer {
88
85
        defer clock.notifyAlarm()
89
86
        clock.mu.Lock()
90
87
        defer clock.mu.Unlock()
91
 
        if d <= 0 {
92
 
                f()
93
 
                return &stoppedTimer{}
 
88
        t := &timer{
 
89
                c:        c,
 
90
                deadline: clock.now.Add(d),
 
91
                clock:    clock,
 
92
                trigger:  trigger,
94
93
        }
95
 
        id := clock.setAlarm(clock.now.Add(d), f)
96
 
        return &Timer{id, clock}
 
94
        clock.addTimer(t)
 
95
        clock.triggerAll()
 
96
        return t
97
97
}
98
98
 
99
99
// Advance advances the result of Now by the supplied duration, and sends
102
102
        clock.mu.Lock()
103
103
        defer clock.mu.Unlock()
104
104
        clock.now = clock.now.Add(d)
105
 
        triggered := 0
106
 
        for _, alarm := range clock.alarms {
107
 
                if clock.now.Before(alarm.time) {
108
 
                        break
109
 
                }
110
 
                alarm.trigger()
111
 
                triggered++
112
 
        }
113
 
        clock.alarms = clock.alarms[triggered:]
 
105
        clock.triggerAll()
114
106
}
115
107
 
116
108
// Alarms returns a channel on which you can read one value for every call to
121
113
        return clock.notifyAlarms
122
114
}
123
115
 
 
116
// triggerAll triggers any alarms that are currently due and removes them
 
117
// from clock.waiting.
 
118
func (clock *Clock) triggerAll() {
 
119
        triggered := 0
 
120
        for _, t := range clock.waiting {
 
121
                if clock.now.Before(t.deadline) {
 
122
                        break
 
123
                }
 
124
                t.trigger()
 
125
                triggered++
 
126
        }
 
127
        clock.waiting = clock.waiting[triggered:]
 
128
}
 
129
 
124
130
// reset is the underlying implementation of clock.Timer.Reset, which may be
125
131
// called by any Timer backed by this Clock.
126
 
func (clock *Clock) reset(id int, d time.Duration) bool {
 
132
func (clock *Clock) reset(t *timer, d time.Duration) bool {
 
133
        defer clock.notifyAlarm()
127
134
        clock.mu.Lock()
128
135
        defer clock.mu.Unlock()
129
136
 
130
 
        for i, alarm := range clock.alarms {
131
 
                if id == alarm.ID {
132
 
                        defer clock.notifyAlarm()
133
 
                        clock.alarms[i].time = clock.now.Add(d)
134
 
                        sort.Sort(byTime(clock.alarms))
135
 
                        return true
 
137
        found := false
 
138
        for _, wt := range clock.waiting {
 
139
                if wt == t {
 
140
                        found = true
136
141
                }
137
142
        }
138
 
        return false
 
143
        if !found {
 
144
                clock.waiting = append(clock.waiting, t)
 
145
        }
 
146
        t.deadline = clock.now.Add(d)
 
147
        sort.Sort(byDeadline(clock.waiting))
 
148
        return found
139
149
}
140
150
 
141
151
// stop is the underlying implementation of clock.Timer.Reset, which may be
142
152
// called by any Timer backed by this Clock.
143
 
func (clock *Clock) stop(id int) bool {
 
153
func (clock *Clock) stop(t *timer) bool {
144
154
        clock.mu.Lock()
145
155
        defer clock.mu.Unlock()
146
156
 
147
 
        for i, alarm := range clock.alarms {
148
 
                if id == alarm.ID {
149
 
                        clock.alarms = removeFromSlice(clock.alarms, i)
 
157
        for i, wt := range clock.waiting {
 
158
                if wt == t {
 
159
                        clock.waiting = removeFromSlice(clock.waiting, i)
150
160
                        return true
151
161
                }
152
162
        }
153
163
        return false
154
164
}
155
165
 
156
 
// setAlarm adds an alarm at time t.
157
 
// It also sorts the alarms and increments the current ID by 1.
158
 
func (clock *Clock) setAlarm(t time.Time, trigger func()) int {
159
 
        alarm := alarm{
160
 
                time:    t,
161
 
                trigger: trigger,
162
 
                ID:      clock.currentAlarmID,
163
 
        }
164
 
        clock.alarms = append(clock.alarms, alarm)
165
 
        sort.Sort(byTime(clock.alarms))
166
 
        clock.currentAlarmID = clock.currentAlarmID + 1
167
 
        return alarm.ID
 
166
// addTimer adds an alarm at time t.
 
167
func (clock *Clock) addTimer(t *timer) {
 
168
        clock.waiting = append(clock.waiting, t)
 
169
        sort.Sort(byDeadline(clock.waiting))
168
170
}
169
171
 
170
172
// notifyAlarm sends a value on the channel exposed by Alarms().
176
178
        }
177
179
}
178
180
 
179
 
// alarm records the time at which we're expected to execute trigger.
180
 
type alarm struct {
181
 
        ID      int
182
 
        time    time.Time
183
 
        trigger func()
184
 
}
185
 
 
186
 
// byTime is used to sort alarms by time.
187
 
type byTime []alarm
188
 
 
189
 
func (a byTime) Len() int           { return len(a) }
190
 
func (a byTime) Less(i, j int) bool { return a[i].time.Before(a[j].time) }
191
 
func (a byTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 
181
// byDeadline is used to sort alarms by time.
 
182
type byDeadline []*timer
 
183
 
 
184
func (a byDeadline) Len() int           { return len(a) }
 
185
func (a byDeadline) Less(i, j int) bool { return a[i].deadline.Before(a[j].deadline) }
 
186
func (a byDeadline) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
192
187
 
193
188
// removeFromSlice removes item at the specified index from the slice.
194
 
func removeFromSlice(sl []alarm, index int) []alarm {
 
189
func removeFromSlice(sl []*timer, index int) []*timer {
195
190
        return append(sl[:index], sl[index+1:]...)
196
191
}
197
192