github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/presence/whitebox_pingbatcher_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package presence 5 6 import ( 7 "math/rand" 8 "time" 9 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 13 "github.com/juju/juju/testing" 14 ) 15 16 // WhiteboxPingBatcherSuite tests pieces of PingBatcher that need direct inspection but don't need database access. 17 type WhiteboxPingBatcherSuite struct { 18 testing.BaseSuite 19 } 20 21 var _ = gc.Suite(&WhiteboxPingBatcherSuite{}) 22 23 func checkSleepRange(c *gc.C, interval, minTime, maxTime time.Duration) { 24 pingBatcher := NewPingBatcher(nil, interval) 25 defer pingBatcher.Stop() 26 var lastTime time.Duration 27 var measuredMinTime time.Duration 28 var measuredMaxTime time.Duration 29 30 r := rand.New(rand.NewSource(time.Now().UnixNano())) 31 for i := 0; i < 1000; i++ { 32 next := pingBatcher.nextSleep(r) 33 // We use Assert rather than Check because we don't want 100s of failures 34 c.Assert(next, jc.GreaterThan, minTime) 35 c.Assert(next, jc.LessThan, maxTime) 36 if lastTime == 0 { 37 measuredMinTime = next 38 measuredMaxTime = next 39 } else { 40 // We are using a range in 100s of milliseconds at a 41 // resolution of nanoseconds. The chance of getting the 42 // same random value 2x in a row is sufficiently low that 43 // we can just assert the value is changing. 44 // (Chance of collision is roughly 1 in 100 Million) 45 c.Assert(next, gc.Not(gc.Equals), lastTime) 46 if next < measuredMinTime { 47 measuredMinTime = next 48 } 49 if next > measuredMaxTime { 50 measuredMaxTime = next 51 } 52 } 53 lastTime = next 54 } 55 // Check that we're actually using the full range that was requested. 56 // Assert that after 1000 tries we've used a good portion of the range 57 // If we sampled perfectly, then we would have fully sampled the range, 58 // spread very 1/1000 of the range. 59 // If we set the required range to 1/100, then a given sample would fail 99% 60 // of the time, 1000 samples would fail 0.99^1000=4e-5 or ~1-in-20,000 times. 61 // (actual measurements showed 18 in 20,000, probably due to double ended range vs single ended) 62 // However, at 1/10 its 0.9^1000=1.7e-46, or 10^41 times less likely to fail. 63 // In 100,000 runs, a range of 1/10 never failed 64 expectedCloseness := (maxTime - minTime) / 10 65 c.Check(measuredMinTime, jc.LessThan, minTime+expectedCloseness) 66 c.Check(measuredMaxTime, jc.GreaterThan, maxTime-expectedCloseness) 67 } 68 69 func (s *WhiteboxPingBatcherSuite) TestNextSleep(c *gc.C) { 70 // nextSleep should select a random range of time to sleep before the 71 // next flush will be called, however it should be within a valid range 72 // of times 73 // range is [800ms, 1200ms] inclusive, but we only can easily assert exclusive 74 checkSleepRange(c, 1*time.Second, 799*time.Millisecond, 1201*time.Millisecond) 75 checkSleepRange(c, 2*time.Second, 1599*time.Millisecond, 2401*time.Millisecond) 76 } 77 78 func (s *WhiteboxPingBatcherSuite) TestSyncWaitsForFlush(c *gc.C) { 79 // We can do this without a database, because we don't actually Ping so 80 // we don't write to the database 81 // Don't let a flush happen based on time 82 pb := NewPingBatcher(nil, time.Hour) 83 pb.syncDelay = time.Hour 84 done := make(chan struct{}) 85 go func() { 86 c.Check(pb.Sync(), jc.ErrorIsNil) 87 close(done) 88 }() 89 select { 90 case <-done: 91 c.Fatalf("done was closed before flush was called") 92 case <-time.After(testing.ShortWait): 93 } 94 // Now when we flush, we should be closed 95 pb.flush() 96 select { 97 case <-done: 98 case <-time.After(testing.LongWait): 99 c.Fatalf("done was not closed after flush") 100 } 101 } 102 103 func (s *WhiteboxPingBatcherSuite) TestFlushWakesUpAllSync(c *gc.C) { 104 // Don't let a flush happen based on time 105 pb := NewPingBatcher(nil, time.Hour) 106 pb.syncDelay = time.Hour 107 const count = 10 108 done := make(chan struct{}, count) 109 for i := 0; i < count; i++ { 110 go func() { 111 c.Check(pb.Sync(), jc.ErrorIsNil) 112 done <- struct{}{} 113 }() 114 } 115 select { 116 case <-done: 117 c.Fatalf("some routines finished before flush") 118 case <-time.After(testing.ShortWait): 119 } 120 // Now when we flush, all should have responded 121 pb.flush() 122 timeout := time.After(testing.LongWait) 123 for i := 0; i < count; i++ { 124 select { 125 case <-done: 126 case <-timeout: 127 c.Fatalf("not all callers were done after flush") 128 } 129 } 130 } 131 132 func (s *WhiteboxPingBatcherSuite) TestSyncReturnsOnShutdown(c *gc.C) { 133 // Don't let a flush happen based on time 134 pb := NewPingBatcher(nil, time.Hour) 135 pb.syncDelay = time.Hour 136 done := make(chan struct{}) 137 go func() { 138 pb.Sync() 139 close(done) 140 }() 141 select { 142 case <-done: 143 c.Fatalf("done was closed before PingBatcher was stopped") 144 case <-time.After(testing.ShortWait): 145 } 146 pb.Kill() 147 timeout := time.After(testing.LongWait) 148 select { 149 case <-done: 150 case <-timeout: 151 c.Fatalf("not all callers were done after flush") 152 } 153 err := pb.Sync() 154 c.Assert(err, gc.ErrorMatches, "PingBatcher is stopped") 155 } 156 157 func (s *WhiteboxPingBatcherSuite) TestContinualSyncDoesntPreventFlush(c *gc.C) { 158 pb := NewPingBatcher(nil, time.Hour) 159 pb.syncDelay = 100 * time.Millisecond 160 // the first routine to call Sync gets the channel we synchronize on 161 done := make(chan struct{}) 162 start := time.Now() 163 go func() { 164 c.Check(pb.Sync(), jc.ErrorIsNil) 165 close(done) 166 }() 167 finished := false 168 select { 169 case <-done: 170 c.Fatalf("we shouldn't be done already") 171 case <-time.After(time.Millisecond): 172 } 173 for i := 0; i < 1000; i++ { 174 select { 175 case <-done: 176 finished = true 177 case <-time.After(time.Millisecond): 178 // start another Sync, it should block, but only until 179 // the first request causes it to go off. 180 go pb.Sync() 181 } 182 if finished { 183 break 184 } 185 } 186 c.Logf("done was finally triggered after %v", time.Since(start)) 187 c.Check(finished, jc.IsTrue) 188 }