go.mway.dev/chrono@v0.6.1-0.20240126030049-189c5aef20d2/periodic/handle_test.go (about) 1 // Copyright (c) 2023 Matt Way 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to 5 // deal in the Software without restriction, including without limitation the 6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 // sell copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 // IN THE THE SOFTWARE. 20 21 package periodic_test 22 23 import ( 24 "context" 25 "testing" 26 "time" 27 28 "github.com/stretchr/testify/require" 29 "go.mway.dev/chrono/clock" 30 "go.mway.dev/chrono/periodic" 31 ) 32 33 func TestStart(t *testing.T) { 34 var ( 35 calls = make(chan struct{}) 36 clk = clock.NewFakeClock() 37 handle = periodic.Start( 38 time.Second, 39 func(ctx context.Context) { 40 select { 41 case <-ctx.Done(): 42 case calls <- struct{}{}: 43 } 44 }, 45 periodic.WithClock(clk), 46 ) 47 ) 48 49 defer handle.Stop() 50 51 timeout := time.NewTimer(5 * time.Second) 52 defer timeout.Stop() 53 54 for seen := 0; seen < 1000; /* noincr */ { 55 select { 56 case <-calls: 57 seen++ 58 case <-timeout.C: 59 require.FailNow(t, "timed out waiting for periodic calls") 60 default: 61 clk.Add(time.Second) 62 } 63 } 64 } 65 66 func TestStart_Freespin(t *testing.T) { 67 var ( 68 calls = make(chan struct{}) 69 clk = clock.NewFakeClock() 70 handle = periodic.Start( 71 -1, 72 func(ctx context.Context) { 73 select { 74 case <-ctx.Done(): 75 case calls <- struct{}{}: 76 } 77 }, 78 periodic.WithClock(clk), 79 ) 80 ) 81 82 defer handle.Stop() 83 84 timeout := time.NewTimer(5 * time.Second) 85 defer timeout.Stop() 86 87 for seen := 0; seen < 1000; /* noincr */ { 88 select { 89 case <-calls: 90 seen++ 91 case <-timeout.C: 92 require.FailNow(t, "timed out waiting for periodic calls") 93 default: 94 clk.Add(time.Second) 95 } 96 } 97 } 98 99 func TestStartWithContext(t *testing.T) { 100 var ( 101 calls = make(chan struct{}) 102 clk = clock.NewFakeClock() 103 handle = periodic.StartWithContext( 104 context.Background(), 105 time.Second, 106 func(ctx context.Context) { 107 select { 108 case <-ctx.Done(): 109 case calls <- struct{}{}: 110 } 111 }, 112 periodic.WithClock(clk), 113 ) 114 ) 115 116 defer handle.Stop() 117 118 timeout := time.NewTimer(5 * time.Second) 119 defer timeout.Stop() 120 121 for seen := 0; seen < 1000; /* noincr */ { 122 select { 123 case <-calls: 124 seen++ 125 case <-timeout.C: 126 require.FailNow(t, "timed out waiting for periodic calls") 127 default: 128 clk.Add(time.Second) 129 } 130 } 131 } 132 133 func TestStartWithContext_ContextCanceled(t *testing.T) { 134 ctx, cancel := context.WithCancel(context.Background()) 135 136 var ( 137 calls = make(chan struct{}, 1) 138 clk = clock.NewFakeClock() 139 handle = periodic.StartWithContext( 140 ctx, 141 time.Second, 142 func(ctx context.Context) { 143 <-ctx.Done() 144 select { 145 case calls <- struct{}{}: 146 default: 147 } 148 }, 149 periodic.WithClock(clk), 150 ) 151 ) 152 153 defer handle.Stop() 154 155 clk.Add(time.Second) 156 157 timeout := time.NewTimer(100 * time.Millisecond) 158 defer timeout.Stop() 159 160 for done := false; !done; /* noincr */ { 161 select { 162 case <-calls: 163 require.FailNow(t, "unexpected periodic call") 164 case <-timeout.C: 165 done = true 166 default: 167 clk.Add(time.Second) 168 } 169 } 170 171 cancel() 172 173 select { 174 case <-calls: 175 case <-time.After(time.Second): 176 require.FailNow(t, "timed out waiting for cancel call") 177 } 178 } 179 180 func TestStartWithContext_ContextPreCanceled(t *testing.T) { 181 canceled, cancel := context.WithCancel(context.Background()) 182 cancel() 183 184 var ( 185 calls = make(chan struct{}) 186 clk = clock.NewFakeClock() 187 handle = periodic.StartWithContext( 188 canceled, 189 time.Second, 190 func(ctx context.Context) { 191 select { 192 case <-ctx.Done(): 193 case calls <- struct{}{}: 194 } 195 }, 196 periodic.WithClock(clk), 197 ) 198 ) 199 200 defer handle.Stop() 201 202 timeout := time.NewTimer(100 * time.Millisecond) 203 defer timeout.Stop() 204 205 for seen := 0; seen < 1; /* noincr */ { 206 select { 207 case <-calls: 208 seen++ 209 case <-timeout.C: 210 require.Equal(t, seen, 0) 211 return 212 default: 213 clk.Add(time.Second) 214 } 215 } 216 }