github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/internal/cache/timeoutCache_test.go (about) 1 /* 2 * 3 * Copyright 2019 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package cache 19 20 import ( 21 "strconv" 22 "sync" 23 "testing" 24 "time" 25 26 "github.com/hxx258456/ccgo/grpc/internal/grpctest" 27 ) 28 29 const ( 30 testCacheTimeout = 100 * time.Millisecond 31 ) 32 33 type s struct { 34 grpctest.Tester 35 } 36 37 func Test(t *testing.T) { 38 grpctest.RunSubTests(t, s{}) 39 } 40 41 func (c *TimeoutCache) getForTesting(key interface{}) (*cacheEntry, bool) { 42 c.mu.Lock() 43 defer c.mu.Unlock() 44 r, ok := c.cache[key] 45 return r, ok 46 } 47 48 // TestCacheExpire attempts to add an entry to the cache and verifies that it 49 // was added successfully. It then makes sure that on timeout, it's removed and 50 // the associated callback is called. 51 func (s) TestCacheExpire(t *testing.T) { 52 const k, v = 1, "1" 53 c := NewTimeoutCache(testCacheTimeout) 54 55 callbackChan := make(chan struct{}) 56 c.Add(k, v, func() { close(callbackChan) }) 57 58 if gotV, ok := c.getForTesting(k); !ok || gotV.item != v { 59 t.Fatalf("After Add(), before timeout, from cache got: %v, %v, want %v, %v", gotV.item, ok, v, true) 60 } 61 62 select { 63 case <-callbackChan: 64 case <-time.After(testCacheTimeout * 2): 65 t.Fatalf("timeout waiting for callback") 66 } 67 68 if _, ok := c.getForTesting(k); ok { 69 t.Fatalf("After Add(), after timeout, from cache got: _, %v, want _, %v", ok, false) 70 } 71 } 72 73 // TestCacheRemove attempts to remove an existing entry from the cache and 74 // verifies that the entry is removed and the associated callback is not 75 // invoked. 76 func (s) TestCacheRemove(t *testing.T) { 77 const k, v = 1, "1" 78 c := NewTimeoutCache(testCacheTimeout) 79 80 callbackChan := make(chan struct{}) 81 c.Add(k, v, func() { close(callbackChan) }) 82 83 if got, ok := c.getForTesting(k); !ok || got.item != v { 84 t.Fatalf("After Add(), before timeout, from cache got: %v, %v, want %v, %v", got.item, ok, v, true) 85 } 86 87 time.Sleep(testCacheTimeout / 2) 88 89 gotV, gotOK := c.Remove(k) 90 if !gotOK || gotV != v { 91 t.Fatalf("After Add(), before timeout, Remove() got: %v, %v, want %v, %v", gotV, gotOK, v, true) 92 } 93 94 if _, ok := c.getForTesting(k); ok { 95 t.Fatalf("After Add(), before timeout, after Remove(), from cache got: _, %v, want _, %v", ok, false) 96 } 97 98 select { 99 case <-callbackChan: 100 t.Fatalf("unexpected callback after retrieve") 101 case <-time.After(testCacheTimeout * 2): 102 } 103 } 104 105 // TestCacheClearWithoutCallback attempts to clear all entries from the cache 106 // and verifies that the associated callbacks are not invoked. 107 func (s) TestCacheClearWithoutCallback(t *testing.T) { 108 var values []string 109 const itemCount = 3 110 for i := 0; i < itemCount; i++ { 111 values = append(values, strconv.Itoa(i)) 112 } 113 c := NewTimeoutCache(testCacheTimeout) 114 115 done := make(chan struct{}) 116 defer close(done) 117 callbackChan := make(chan struct{}, itemCount) 118 119 for i, v := range values { 120 callbackChanTemp := make(chan struct{}) 121 c.Add(i, v, func() { close(callbackChanTemp) }) 122 go func() { 123 select { 124 case <-callbackChanTemp: 125 callbackChan <- struct{}{} 126 case <-done: 127 } 128 }() 129 } 130 131 for i, v := range values { 132 if got, ok := c.getForTesting(i); !ok || got.item != v { 133 t.Fatalf("After Add(), before timeout, from cache got: %v, %v, want %v, %v", got.item, ok, v, true) 134 } 135 } 136 137 time.Sleep(testCacheTimeout / 2) 138 c.Clear(false) 139 140 for i := range values { 141 if _, ok := c.getForTesting(i); ok { 142 t.Fatalf("After Add(), before timeout, after Remove(), from cache got: _, %v, want _, %v", ok, false) 143 } 144 } 145 146 select { 147 case <-callbackChan: 148 t.Fatalf("unexpected callback after Clear") 149 case <-time.After(testCacheTimeout * 2): 150 } 151 } 152 153 // TestCacheClearWithCallback attempts to clear all entries from the cache and 154 // verifies that the associated callbacks are invoked. 155 func (s) TestCacheClearWithCallback(t *testing.T) { 156 var values []string 157 const itemCount = 3 158 for i := 0; i < itemCount; i++ { 159 values = append(values, strconv.Itoa(i)) 160 } 161 c := NewTimeoutCache(time.Hour) 162 163 testDone := make(chan struct{}) 164 defer close(testDone) 165 166 var wg sync.WaitGroup 167 wg.Add(itemCount) 168 for i, v := range values { 169 callbackChanTemp := make(chan struct{}) 170 c.Add(i, v, func() { close(callbackChanTemp) }) 171 go func() { 172 defer wg.Done() 173 select { 174 case <-callbackChanTemp: 175 case <-testDone: 176 } 177 }() 178 } 179 180 allGoroutineDone := make(chan struct{}, itemCount) 181 go func() { 182 wg.Wait() 183 close(allGoroutineDone) 184 }() 185 186 for i, v := range values { 187 if got, ok := c.getForTesting(i); !ok || got.item != v { 188 t.Fatalf("After Add(), before timeout, from cache got: %v, %v, want %v, %v", got.item, ok, v, true) 189 } 190 } 191 192 time.Sleep(testCacheTimeout / 2) 193 c.Clear(true) 194 195 for i := range values { 196 if _, ok := c.getForTesting(i); ok { 197 t.Fatalf("After Add(), before timeout, after Remove(), from cache got: _, %v, want _, %v", ok, false) 198 } 199 } 200 201 select { 202 case <-allGoroutineDone: 203 case <-time.After(testCacheTimeout * 2): 204 t.Fatalf("timeout waiting for all callbacks") 205 } 206 } 207 208 // TestCacheRetrieveTimeoutRace simulates the case where an entry's timer fires 209 // around the same time that Remove() is called for it. It verifies that there 210 // is no deadlock. 211 func (s) TestCacheRetrieveTimeoutRace(t *testing.T) { 212 c := NewTimeoutCache(time.Nanosecond) 213 214 done := make(chan struct{}) 215 go func() { 216 for i := 0; i < 1000; i++ { 217 // Add starts a timer with 1 ns timeout, then remove will race 218 // with the timer. 219 c.Add(i, strconv.Itoa(i), func() {}) 220 c.Remove(i) 221 } 222 close(done) 223 }() 224 225 select { 226 case <-time.After(time.Second): 227 t.Fatalf("Test didn't finish within 1 second. Deadlock") 228 case <-done: 229 } 230 }