1package memberlist 2 3import ( 4 "testing" 5 "time" 6) 7 8func TestSuspicion_remainingSuspicionTime(t *testing.T) { 9 cases := []struct { 10 n int32 11 k int32 12 elapsed time.Duration 13 min time.Duration 14 max time.Duration 15 expected time.Duration 16 }{ 17 {0, 3, 0, 2 * time.Second, 30 * time.Second, 30 * time.Second}, 18 {1, 3, 2 * time.Second, 2 * time.Second, 30 * time.Second, 14 * time.Second}, 19 {2, 3, 3 * time.Second, 2 * time.Second, 30 * time.Second, 4810 * time.Millisecond}, 20 {3, 3, 4 * time.Second, 2 * time.Second, 30 * time.Second, -2 * time.Second}, 21 {4, 3, 5 * time.Second, 2 * time.Second, 30 * time.Second, -3 * time.Second}, 22 {5, 3, 10 * time.Second, 2 * time.Second, 30 * time.Second, -8 * time.Second}, 23 } 24 for i, c := range cases { 25 remaining := remainingSuspicionTime(c.n, c.k, c.elapsed, c.min, c.max) 26 if remaining != c.expected { 27 t.Errorf("case %d: remaining %9.6f != expected %9.6f", i, remaining.Seconds(), c.expected.Seconds()) 28 } 29 } 30} 31 32func TestSuspicion_Timer(t *testing.T) { 33 const k = 3 34 const min = 500 * time.Millisecond 35 const max = 2 * time.Second 36 37 type pair struct { 38 from string 39 newInfo bool 40 } 41 cases := []struct { 42 numConfirmations int 43 from string 44 confirmations []pair 45 expected time.Duration 46 }{ 47 { 48 0, 49 "me", 50 []pair{}, 51 max, 52 }, 53 { 54 1, 55 "me", 56 []pair{ 57 pair{"me", false}, 58 pair{"foo", true}, 59 }, 60 1250 * time.Millisecond, 61 }, 62 { 63 1, 64 "me", 65 []pair{ 66 pair{"me", false}, 67 pair{"foo", true}, 68 pair{"foo", false}, 69 pair{"foo", false}, 70 }, 71 1250 * time.Millisecond, 72 }, 73 { 74 2, 75 "me", 76 []pair{ 77 pair{"me", false}, 78 pair{"foo", true}, 79 pair{"bar", true}, 80 }, 81 810 * time.Millisecond, 82 }, 83 { 84 3, 85 "me", 86 []pair{ 87 pair{"me", false}, 88 pair{"foo", true}, 89 pair{"bar", true}, 90 pair{"baz", true}, 91 }, 92 min, 93 }, 94 { 95 3, 96 "me", 97 []pair{ 98 pair{"me", false}, 99 pair{"foo", true}, 100 pair{"bar", true}, 101 pair{"baz", true}, 102 pair{"zoo", false}, 103 }, 104 min, 105 }, 106 } 107 for i, c := range cases { 108 ch := make(chan time.Duration, 1) 109 start := time.Now() 110 f := func(numConfirmations int) { 111 if numConfirmations != c.numConfirmations { 112 t.Errorf("case %d: bad %d != %d", i, numConfirmations, c.numConfirmations) 113 } 114 115 ch <- time.Now().Sub(start) 116 } 117 118 // Create the timer and add the requested confirmations. Wait 119 // the fudge amount to help make sure we calculate the timeout 120 // overall, and don't accumulate extra time. 121 s := newSuspicion(c.from, k, min, max, f) 122 fudge := 25 * time.Millisecond 123 for _, p := range c.confirmations { 124 time.Sleep(fudge) 125 if s.Confirm(p.from) != p.newInfo { 126 t.Fatalf("case %d: newInfo mismatch for %s", i, p.from) 127 } 128 } 129 130 // Wait until right before the timeout and make sure the 131 // timer hasn't fired. 132 already := time.Duration(len(c.confirmations)) * fudge 133 time.Sleep(c.expected - already - fudge) 134 select { 135 case d := <-ch: 136 t.Fatalf("case %d: should not have fired (%9.6f)", i, d.Seconds()) 137 default: 138 } 139 140 // Wait through the timeout and a little after and make sure it 141 // fires. 142 time.Sleep(2 * fudge) 143 select { 144 case <-ch: 145 default: 146 t.Fatalf("case %d: should have fired", i) 147 } 148 149 // Confirm after to make sure it handles a negative remaining 150 // time correctly and doesn't fire again. 151 s.Confirm("late") 152 time.Sleep(c.expected + 2*fudge) 153 select { 154 case d := <-ch: 155 t.Fatalf("case %d: should not have fired (%9.6f)", i, d.Seconds()) 156 default: 157 } 158 } 159} 160 161func TestSuspicion_Timer_ZeroK(t *testing.T) { 162 ch := make(chan struct{}, 1) 163 f := func(int) { 164 ch <- struct{}{} 165 } 166 167 // This should select the min time since there are no expected 168 // confirmations to accelerate the timer. 169 s := newSuspicion("me", 0, 25*time.Millisecond, 30*time.Second, f) 170 if s.Confirm("foo") { 171 t.Fatalf("should not provide new information") 172 } 173 174 select { 175 case <-ch: 176 case <-time.After(50 * time.Millisecond): 177 t.Fatalf("should have fired") 178 } 179} 180 181func TestSuspicion_Timer_Immediate(t *testing.T) { 182 ch := make(chan struct{}, 1) 183 f := func(int) { 184 ch <- struct{}{} 185 } 186 187 // This should underflow the timeout and fire immediately. 188 s := newSuspicion("me", 1, 100*time.Millisecond, 30*time.Second, f) 189 time.Sleep(200 * time.Millisecond) 190 s.Confirm("foo") 191 192 // Wait a little while since the function gets called in a goroutine. 193 select { 194 case <-ch: 195 case <-time.After(25 * time.Millisecond): 196 t.Fatalf("should have fired") 197 } 198} 199