snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
This commit is contained in:
254
internal/cache/timeoutCache_test.go
vendored
Normal file
254
internal/cache/timeoutCache_test.go
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2019 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/internal/grpctest"
|
||||
)
|
||||
|
||||
const (
|
||||
testCacheTimeout = 100 * time.Millisecond
|
||||
)
|
||||
|
||||
type s struct {
|
||||
grpctest.Tester
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
grpctest.RunSubTests(t, s{})
|
||||
}
|
||||
|
||||
func (c *TimeoutCache) getForTesting(key any) (*cacheEntry, bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
r, ok := c.cache[key]
|
||||
return r, ok
|
||||
}
|
||||
|
||||
// TestCacheExpire attempts to add an entry to the cache and verifies that it
|
||||
// was added successfully. It then makes sure that on timeout, it's removed and
|
||||
// the associated callback is called.
|
||||
func (s) TestCacheExpire(t *testing.T) {
|
||||
const k, v = 1, "1"
|
||||
c := NewTimeoutCache(testCacheTimeout)
|
||||
|
||||
callbackChan := make(chan struct{})
|
||||
c.Add(k, v, func() { close(callbackChan) })
|
||||
|
||||
if gotV, ok := c.getForTesting(k); !ok || gotV.item != v {
|
||||
t.Fatalf("After Add(), before timeout, from cache got: %v, %v, want %v, %v", gotV.item, ok, v, true)
|
||||
}
|
||||
if l := c.Len(); l != 1 {
|
||||
t.Fatalf("%d number of items in the cache, want 1", l)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-callbackChan:
|
||||
case <-time.After(testCacheTimeout * 2):
|
||||
t.Fatalf("timeout waiting for callback")
|
||||
}
|
||||
|
||||
if _, ok := c.getForTesting(k); ok {
|
||||
t.Fatalf("After Add(), after timeout, from cache got: _, %v, want _, %v", ok, false)
|
||||
}
|
||||
if l := c.Len(); l != 0 {
|
||||
t.Fatalf("%d number of items in the cache, want 0", l)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCacheRemove attempts to remove an existing entry from the cache and
|
||||
// verifies that the entry is removed and the associated callback is not
|
||||
// invoked.
|
||||
func (s) TestCacheRemove(t *testing.T) {
|
||||
const k, v = 1, "1"
|
||||
c := NewTimeoutCache(testCacheTimeout)
|
||||
|
||||
callbackChan := make(chan struct{})
|
||||
c.Add(k, v, func() { close(callbackChan) })
|
||||
|
||||
if got, ok := c.getForTesting(k); !ok || got.item != v {
|
||||
t.Fatalf("After Add(), before timeout, from cache got: %v, %v, want %v, %v", got.item, ok, v, true)
|
||||
}
|
||||
if l := c.Len(); l != 1 {
|
||||
t.Fatalf("%d number of items in the cache, want 1", l)
|
||||
}
|
||||
|
||||
time.Sleep(testCacheTimeout / 2)
|
||||
|
||||
gotV, gotOK := c.Remove(k)
|
||||
if !gotOK || gotV != v {
|
||||
t.Fatalf("After Add(), before timeout, Remove() got: %v, %v, want %v, %v", gotV, gotOK, v, true)
|
||||
}
|
||||
|
||||
if _, ok := c.getForTesting(k); ok {
|
||||
t.Fatalf("After Add(), before timeout, after Remove(), from cache got: _, %v, want _, %v", ok, false)
|
||||
}
|
||||
if l := c.Len(); l != 0 {
|
||||
t.Fatalf("%d number of items in the cache, want 0", l)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-callbackChan:
|
||||
t.Fatalf("unexpected callback after retrieve")
|
||||
case <-time.After(testCacheTimeout * 2):
|
||||
}
|
||||
}
|
||||
|
||||
// TestCacheClearWithoutCallback attempts to clear all entries from the cache
|
||||
// and verifies that the associated callbacks are not invoked.
|
||||
func (s) TestCacheClearWithoutCallback(t *testing.T) {
|
||||
var values []string
|
||||
const itemCount = 3
|
||||
for i := 0; i < itemCount; i++ {
|
||||
values = append(values, strconv.Itoa(i))
|
||||
}
|
||||
c := NewTimeoutCache(testCacheTimeout)
|
||||
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
callbackChan := make(chan struct{}, itemCount)
|
||||
|
||||
for i, v := range values {
|
||||
callbackChanTemp := make(chan struct{})
|
||||
c.Add(i, v, func() { close(callbackChanTemp) })
|
||||
go func() {
|
||||
select {
|
||||
case <-callbackChanTemp:
|
||||
callbackChan <- struct{}{}
|
||||
case <-done:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for i, v := range values {
|
||||
if got, ok := c.getForTesting(i); !ok || got.item != v {
|
||||
t.Fatalf("After Add(), before timeout, from cache got: %v, %v, want %v, %v", got.item, ok, v, true)
|
||||
}
|
||||
}
|
||||
if l := c.Len(); l != itemCount {
|
||||
t.Fatalf("%d number of items in the cache, want %d", l, itemCount)
|
||||
}
|
||||
|
||||
time.Sleep(testCacheTimeout / 2)
|
||||
c.Clear(false)
|
||||
|
||||
for i := range values {
|
||||
if _, ok := c.getForTesting(i); ok {
|
||||
t.Fatalf("After Add(), before timeout, after Remove(), from cache got: _, %v, want _, %v", ok, false)
|
||||
}
|
||||
}
|
||||
if l := c.Len(); l != 0 {
|
||||
t.Fatalf("%d number of items in the cache, want 0", l)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-callbackChan:
|
||||
t.Fatalf("unexpected callback after Clear")
|
||||
case <-time.After(testCacheTimeout * 2):
|
||||
}
|
||||
}
|
||||
|
||||
// TestCacheClearWithCallback attempts to clear all entries from the cache and
|
||||
// verifies that the associated callbacks are invoked.
|
||||
func (s) TestCacheClearWithCallback(t *testing.T) {
|
||||
var values []string
|
||||
const itemCount = 3
|
||||
for i := 0; i < itemCount; i++ {
|
||||
values = append(values, strconv.Itoa(i))
|
||||
}
|
||||
c := NewTimeoutCache(time.Hour)
|
||||
|
||||
testDone := make(chan struct{})
|
||||
defer close(testDone)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(itemCount)
|
||||
for i, v := range values {
|
||||
callbackChanTemp := make(chan struct{})
|
||||
c.Add(i, v, func() { close(callbackChanTemp) })
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
select {
|
||||
case <-callbackChanTemp:
|
||||
case <-testDone:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
allGoroutineDone := make(chan struct{}, itemCount)
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(allGoroutineDone)
|
||||
}()
|
||||
|
||||
for i, v := range values {
|
||||
if got, ok := c.getForTesting(i); !ok || got.item != v {
|
||||
t.Fatalf("After Add(), before timeout, from cache got: %v, %v, want %v, %v", got.item, ok, v, true)
|
||||
}
|
||||
}
|
||||
if l := c.Len(); l != itemCount {
|
||||
t.Fatalf("%d number of items in the cache, want %d", l, itemCount)
|
||||
}
|
||||
|
||||
time.Sleep(testCacheTimeout / 2)
|
||||
c.Clear(true)
|
||||
|
||||
for i := range values {
|
||||
if _, ok := c.getForTesting(i); ok {
|
||||
t.Fatalf("After Add(), before timeout, after Remove(), from cache got: _, %v, want _, %v", ok, false)
|
||||
}
|
||||
}
|
||||
if l := c.Len(); l != 0 {
|
||||
t.Fatalf("%d number of items in the cache, want 0", l)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-allGoroutineDone:
|
||||
case <-time.After(testCacheTimeout * 2):
|
||||
t.Fatalf("timeout waiting for all callbacks")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCacheRetrieveTimeoutRace simulates the case where an entry's timer fires
|
||||
// around the same time that Remove() is called for it. It verifies that there
|
||||
// is no deadlock.
|
||||
func (s) TestCacheRetrieveTimeoutRace(t *testing.T) {
|
||||
c := NewTimeoutCache(time.Nanosecond)
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for i := 0; i < 1000; i++ {
|
||||
// Add starts a timer with 1 ns timeout, then remove will race
|
||||
// with the timer.
|
||||
c.Add(i, strconv.Itoa(i), func() {})
|
||||
c.Remove(i)
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("Test didn't finish within 1 second. Deadlock")
|
||||
case <-done:
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user