ALL

Solo para leer y verificar. No puedes copiar, usar, modificar ni redistribuir este código. Aviso completo en la página principal.

tunnel/clock_test.go

SHA-256: c1eb5209c548b4daa2d28a31ec19b32da4118f8ab55d6850da13cf6f77d5c346
200 líneas · 6128 bytes · Ver en GitHub

1package main
2
3import (
4 "context"
5 "errors"
6 "sort"
7 "sync"
8 "testing"
9 "time"
10)
11
12// fakeClock is a clock the tests move by hand: Advance sets the time forward and runs every timer that came due, each in its own goroutine (as
13// time.AfterFunc does), in the order of their moments. A timer asked for a moment that is already past runs at once. Nothing else in the node reads
14// it: socket deadlines stay on the real clock.
15type fakeClock struct {
16 mu sync.Mutex
17 now time.Time
18 timers []*fakeTimer
19 fired int // timers run so far (a test waits for them)
20}
21
22type fakeTimer struct {
23 when time.Time
24 f func()
25 stopped bool
26 done bool
27}
28
29func newFakeClock() *fakeClock { return &fakeClock{now: time.Now()} }
30
31func (c *fakeClock) Now() time.Time {
32 c.mu.Lock()
33 defer c.mu.Unlock()
34 return c.now
35}
36
37func (c *fakeClock) AfterFunc(d time.Duration, f func()) func() bool {
38 c.mu.Lock()
39 t := &fakeTimer{when: c.now.Add(d), f: f}
40 if d <= 0 {
41 t.done = true
42 c.fired++
43 c.mu.Unlock()
44 go f()
45 return func() bool { return false }
46 }
47 c.timers = append(c.timers, t)
48 c.mu.Unlock()
49 return func() bool {
50 c.mu.Lock()
51 defer c.mu.Unlock()
52 was := !t.stopped && !t.done
53 t.stopped = true
54 return was
55 }
56}
57
58// Advance moves the clock forward by d and runs what came due.
59func (c *fakeClock) Advance(d time.Duration) {
60 c.mu.Lock()
61 c.now = c.now.Add(d)
62 var due []*fakeTimer
63 rest := c.timers[:0]
64 for _, t := range c.timers {
65 switch {
66 case t.stopped || t.done:
67 case !t.when.After(c.now):
68 t.done = true
69 c.fired++
70 due = append(due, t)
71 default:
72 rest = append(rest, t)
73 }
74 }
75 c.timers = rest
76 c.mu.Unlock()
77 sort.SliceStable(due, func(i, j int) bool { return due[i].when.Before(due[j].when) })
78 for _, t := range due {
79 go t.f()
80 }
81}
82
83// pending is the number of timers waiting.
84func (c *fakeClock) pending() int {
85 c.mu.Lock()
86 defer c.mu.Unlock()
87 n := 0
88 for _, t := range c.timers {
89 if !t.stopped && !t.done {
90 n++
91 }
92 }
93 return n
94}
95
96// ---------------------------------------------------------------------------------------------------------------------------- the helpers of clock.go
97
98func TestClock_TheFakeClockRunsDueTimersInOrderStopsOnRequestAndRunsAPastOneAtOnce(t *testing.T) {
99 c := newFakeClock()
100 var mu sync.Mutex
101 var order []string
102 note := func(s string) func() { return func() { mu.Lock(); order = append(order, s); mu.Unlock() } }
103 c.AfterFunc(20*time.Second, note("b"))
104 c.AfterFunc(10*time.Second, note("a"))
105 stopped := c.AfterFunc(15*time.Second, note("stopped"))
106 c.AfterFunc(40*time.Second, note("later"))
107 if !stopped() {
108 t.Fatal("a pending timer was not stopped")
109 }
110 if stopped() {
111 t.Fatal("a timer was stopped twice")
112 }
113 c.Advance(25 * time.Second)
114 eventually(t, "the two due timers", time.Second, func() bool { mu.Lock(); defer mu.Unlock(); return len(order) == 2 })
115 if c.pending() != 1 {
116 t.Fatalf("pending %d", c.pending())
117 }
118 c.AfterFunc(-time.Second, note("past")) // a moment already gone runs at once
119 eventually(t, "a past timer", time.Second, func() bool { mu.Lock(); defer mu.Unlock(); return len(order) == 3 })
120 mu.Lock()
121 defer mu.Unlock()
122 if order[0] != "a" && order[0] != "b" || order[2] != "past" {
123 t.Fatalf("order %v", order)
124 }
125}
126
127func TestClock_SleepEndsWithTheClockOrWithTheContext(t *testing.T) {
128 c := newFakeClock()
129 clk := newClock(c.Now, c.AfterFunc)
130 done := make(chan bool, 1)
131 go func() { done <- clk.sleep(context.Background(), time.Minute) }()
132 eventually(t, "the sleeper", time.Second, func() bool { return c.pending() == 1 })
133 c.Advance(time.Minute)
134 if !<-done {
135 t.Fatal("a sleep that ran to its end reported that it was cut")
136 }
137 ctx, cancel := context.WithCancel(context.Background())
138 go func() { done <- clk.sleep(ctx, time.Hour) }()
139 eventually(t, "the sleeper", time.Second, func() bool { return c.pending() == 1 })
140 cancel()
141 if <-done {
142 t.Fatal("a cancelled sleep reported that it ran to its end")
143 }
144 if c.pending() != 0 {
145 t.Fatal("a cancelled sleep left its timer behind")
146 }
147 if clk.sleep(ctx, time.Second) {
148 t.Fatal("a sleep started with a cancelled context")
149 }
150}
151
152func TestClock_BackoffDoublesFromThePolicysMinimumToItsMaximumAndJitterStaysInItsBand(t *testing.T) {
153 want := []time.Duration{1, 2, 4, 8, 16, 32, 60, 60}
154 for i, w := range want {
155 if got := backoff(i + 1); got != w*time.Second {
156 t.Errorf("backoff(%d) = %s, want %s", i+1, got, w*time.Second)
157 }
158 }
159 if backoff(0) != secs(policyData.Timing.BackoffMinS) || backoff(1000) != secs(policyData.Timing.BackoffMaxS) {
160 t.Fatal("backoff out of its bounds")
161 }
162 for i := 0; i < 500; i++ {
163 if got := jitter(100*time.Second, 10); got < 90*time.Second || got > 110*time.Second {
164 t.Fatalf("jitter %s", got)
165 }
166 }
167 if jitter(time.Second, 0) != time.Second || jitter(0, 10) != 0 {
168 t.Fatal("jitter of nothing")
169 }
170}
171
172func TestHealth_AnAnswerOfAnyKindIsReachabilityAndDownNeedsAFailureAfterItAndQuiet(t *testing.T) {
173 var h cpHealth
174 now := time.Now()
175 if h.down(now, time.Second) {
176 t.Fatal("down before anything was seen")
177 }
178 h.record(now, nil)
179 h.record(now.Add(time.Second), &ControlError{Status: 402, Code: codeLicenseInactive}) // a refusal is an answer
180 h.record(now.Add(2*time.Second), &ControlError{Status: 404, Code: "not_enrolled"})
181 if h.down(now.Add(time.Hour), time.Second) {
182 t.Fatal("a control plane that said no is down")
183 }
184 for name, err := range map[string]error{"a 503": &ControlError{Status: 503, Code: "end_to_end_unavailable"}, "a 429": &ControlError{Status: 429, Code: "busy"},
185 "a timeout": context.DeadlineExceeded, "a transport error": errors.New("connection refused"), "an unreadable answer": &ControlError{Status: 502, Code: "bad_response"}} {
186 var g cpHealth
187 g.record(now, nil)
188 g.record(now.Add(time.Second), err)
189 if g.down(now.Add(2*time.Second), time.Minute) {
190 t.Errorf("%s: down before the quiet period", name)
191 }
192 if !g.down(now.Add(2*time.Minute), time.Minute) {
193 t.Errorf("%s: not down after the quiet period", name)
194 }
195 g.record(now.Add(3*time.Minute), nil)
196 if g.down(now.Add(10*time.Minute), time.Minute) {
197 t.Errorf("%s: still down after an answer", name)
198 }
199 }
200}