summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/golang/groupcache/groupcache_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/golang/groupcache/groupcache_test.go')
-rw-r--r--vendor/github.com/golang/groupcache/groupcache_test.go447
1 files changed, 0 insertions, 447 deletions
diff --git a/vendor/github.com/golang/groupcache/groupcache_test.go b/vendor/github.com/golang/groupcache/groupcache_test.go
deleted file mode 100644
index 3a4ecc2cc..000000000
--- a/vendor/github.com/golang/groupcache/groupcache_test.go
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-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.
-*/
-
-// Tests for groupcache.
-
-package groupcache
-
-import (
- "errors"
- "fmt"
- "hash/crc32"
- "math/rand"
- "reflect"
- "sync"
- "testing"
- "time"
-
- "github.com/golang/protobuf/proto"
-
- pb "github.com/golang/groupcache/groupcachepb"
- testpb "github.com/golang/groupcache/testpb"
-)
-
-var (
- once sync.Once
- stringGroup, protoGroup Getter
-
- stringc = make(chan string)
-
- dummyCtx Context
-
- // cacheFills is the number of times stringGroup or
- // protoGroup's Getter have been called. Read using the
- // cacheFills function.
- cacheFills AtomicInt
-)
-
-const (
- stringGroupName = "string-group"
- protoGroupName = "proto-group"
- testMessageType = "google3/net/groupcache/go/test_proto.TestMessage"
- fromChan = "from-chan"
- cacheSize = 1 << 20
-)
-
-func testSetup() {
- stringGroup = NewGroup(stringGroupName, cacheSize, GetterFunc(func(_ Context, key string, dest Sink) error {
- if key == fromChan {
- key = <-stringc
- }
- cacheFills.Add(1)
- return dest.SetString("ECHO:" + key)
- }))
-
- protoGroup = NewGroup(protoGroupName, cacheSize, GetterFunc(func(_ Context, key string, dest Sink) error {
- if key == fromChan {
- key = <-stringc
- }
- cacheFills.Add(1)
- return dest.SetProto(&testpb.TestMessage{
- Name: proto.String("ECHO:" + key),
- City: proto.String("SOME-CITY"),
- })
- }))
-}
-
-// tests that a Getter's Get method is only called once with two
-// outstanding callers. This is the string variant.
-func TestGetDupSuppressString(t *testing.T) {
- once.Do(testSetup)
- // Start two getters. The first should block (waiting reading
- // from stringc) and the second should latch on to the first
- // one.
- resc := make(chan string, 2)
- for i := 0; i < 2; i++ {
- go func() {
- var s string
- if err := stringGroup.Get(dummyCtx, fromChan, StringSink(&s)); err != nil {
- resc <- "ERROR:" + err.Error()
- return
- }
- resc <- s
- }()
- }
-
- // Wait a bit so both goroutines get merged together via
- // singleflight.
- // TODO(bradfitz): decide whether there are any non-offensive
- // debug/test hooks that could be added to singleflight to
- // make a sleep here unnecessary.
- time.Sleep(250 * time.Millisecond)
-
- // Unblock the first getter, which should unblock the second
- // as well.
- stringc <- "foo"
-
- for i := 0; i < 2; i++ {
- select {
- case v := <-resc:
- if v != "ECHO:foo" {
- t.Errorf("got %q; want %q", v, "ECHO:foo")
- }
- case <-time.After(5 * time.Second):
- t.Errorf("timeout waiting on getter #%d of 2", i+1)
- }
- }
-}
-
-// tests that a Getter's Get method is only called once with two
-// outstanding callers. This is the proto variant.
-func TestGetDupSuppressProto(t *testing.T) {
- once.Do(testSetup)
- // Start two getters. The first should block (waiting reading
- // from stringc) and the second should latch on to the first
- // one.
- resc := make(chan *testpb.TestMessage, 2)
- for i := 0; i < 2; i++ {
- go func() {
- tm := new(testpb.TestMessage)
- if err := protoGroup.Get(dummyCtx, fromChan, ProtoSink(tm)); err != nil {
- tm.Name = proto.String("ERROR:" + err.Error())
- }
- resc <- tm
- }()
- }
-
- // Wait a bit so both goroutines get merged together via
- // singleflight.
- // TODO(bradfitz): decide whether there are any non-offensive
- // debug/test hooks that could be added to singleflight to
- // make a sleep here unnecessary.
- time.Sleep(250 * time.Millisecond)
-
- // Unblock the first getter, which should unblock the second
- // as well.
- stringc <- "Fluffy"
- want := &testpb.TestMessage{
- Name: proto.String("ECHO:Fluffy"),
- City: proto.String("SOME-CITY"),
- }
- for i := 0; i < 2; i++ {
- select {
- case v := <-resc:
- if !reflect.DeepEqual(v, want) {
- t.Errorf(" Got: %v\nWant: %v", proto.CompactTextString(v), proto.CompactTextString(want))
- }
- case <-time.After(5 * time.Second):
- t.Errorf("timeout waiting on getter #%d of 2", i+1)
- }
- }
-}
-
-func countFills(f func()) int64 {
- fills0 := cacheFills.Get()
- f()
- return cacheFills.Get() - fills0
-}
-
-func TestCaching(t *testing.T) {
- once.Do(testSetup)
- fills := countFills(func() {
- for i := 0; i < 10; i++ {
- var s string
- if err := stringGroup.Get(dummyCtx, "TestCaching-key", StringSink(&s)); err != nil {
- t.Fatal(err)
- }
- }
- })
- if fills != 1 {
- t.Errorf("expected 1 cache fill; got %d", fills)
- }
-}
-
-func TestCacheEviction(t *testing.T) {
- once.Do(testSetup)
- testKey := "TestCacheEviction-key"
- getTestKey := func() {
- var res string
- for i := 0; i < 10; i++ {
- if err := stringGroup.Get(dummyCtx, testKey, StringSink(&res)); err != nil {
- t.Fatal(err)
- }
- }
- }
- fills := countFills(getTestKey)
- if fills != 1 {
- t.Fatalf("expected 1 cache fill; got %d", fills)
- }
-
- g := stringGroup.(*Group)
- evict0 := g.mainCache.nevict
-
- // Trash the cache with other keys.
- var bytesFlooded int64
- // cacheSize/len(testKey) is approximate
- for bytesFlooded < cacheSize+1024 {
- var res string
- key := fmt.Sprintf("dummy-key-%d", bytesFlooded)
- stringGroup.Get(dummyCtx, key, StringSink(&res))
- bytesFlooded += int64(len(key) + len(res))
- }
- evicts := g.mainCache.nevict - evict0
- if evicts <= 0 {
- t.Errorf("evicts = %v; want more than 0", evicts)
- }
-
- // Test that the key is gone.
- fills = countFills(getTestKey)
- if fills != 1 {
- t.Fatalf("expected 1 cache fill after cache trashing; got %d", fills)
- }
-}
-
-type fakePeer struct {
- hits int
- fail bool
-}
-
-func (p *fakePeer) Get(_ Context, in *pb.GetRequest, out *pb.GetResponse) error {
- p.hits++
- if p.fail {
- return errors.New("simulated error from peer")
- }
- out.Value = []byte("got:" + in.GetKey())
- return nil
-}
-
-type fakePeers []ProtoGetter
-
-func (p fakePeers) PickPeer(key string) (peer ProtoGetter, ok bool) {
- if len(p) == 0 {
- return
- }
- n := crc32.Checksum([]byte(key), crc32.IEEETable) % uint32(len(p))
- return p[n], p[n] != nil
-}
-
-// tests that peers (virtual, in-process) are hit, and how much.
-func TestPeers(t *testing.T) {
- once.Do(testSetup)
- rand.Seed(123)
- peer0 := &fakePeer{}
- peer1 := &fakePeer{}
- peer2 := &fakePeer{}
- peerList := fakePeers([]ProtoGetter{peer0, peer1, peer2, nil})
- const cacheSize = 0 // disabled
- localHits := 0
- getter := func(_ Context, key string, dest Sink) error {
- localHits++
- return dest.SetString("got:" + key)
- }
- testGroup := newGroup("TestPeers-group", cacheSize, GetterFunc(getter), peerList)
- run := func(name string, n int, wantSummary string) {
- // Reset counters
- localHits = 0
- for _, p := range []*fakePeer{peer0, peer1, peer2} {
- p.hits = 0
- }
-
- for i := 0; i < n; i++ {
- key := fmt.Sprintf("key-%d", i)
- want := "got:" + key
- var got string
- err := testGroup.Get(dummyCtx, key, StringSink(&got))
- if err != nil {
- t.Errorf("%s: error on key %q: %v", name, key, err)
- continue
- }
- if got != want {
- t.Errorf("%s: for key %q, got %q; want %q", name, key, got, want)
- }
- }
- summary := func() string {
- return fmt.Sprintf("localHits = %d, peers = %d %d %d", localHits, peer0.hits, peer1.hits, peer2.hits)
- }
- if got := summary(); got != wantSummary {
- t.Errorf("%s: got %q; want %q", name, got, wantSummary)
- }
- }
- resetCacheSize := func(maxBytes int64) {
- g := testGroup
- g.cacheBytes = maxBytes
- g.mainCache = cache{}
- g.hotCache = cache{}
- }
-
- // Base case; peers all up, with no problems.
- resetCacheSize(1 << 20)
- run("base", 200, "localHits = 49, peers = 51 49 51")
-
- // Verify cache was hit. All localHits are gone, and some of
- // the peer hits (the ones randomly selected to be maybe hot)
- run("cached_base", 200, "localHits = 0, peers = 49 47 48")
- resetCacheSize(0)
-
- // With one of the peers being down.
- // TODO(bradfitz): on a peer number being unavailable, the
- // consistent hashing should maybe keep trying others to
- // spread the load out. Currently it fails back to local
- // execution if the first consistent-hash slot is unavailable.
- peerList[0] = nil
- run("one_peer_down", 200, "localHits = 100, peers = 0 49 51")
-
- // Failing peer
- peerList[0] = peer0
- peer0.fail = true
- run("peer0_failing", 200, "localHits = 100, peers = 51 49 51")
-}
-
-func TestTruncatingByteSliceTarget(t *testing.T) {
- var buf [100]byte
- s := buf[:]
- if err := stringGroup.Get(dummyCtx, "short", TruncatingByteSliceSink(&s)); err != nil {
- t.Fatal(err)
- }
- if want := "ECHO:short"; string(s) != want {
- t.Errorf("short key got %q; want %q", s, want)
- }
-
- s = buf[:6]
- if err := stringGroup.Get(dummyCtx, "truncated", TruncatingByteSliceSink(&s)); err != nil {
- t.Fatal(err)
- }
- if want := "ECHO:t"; string(s) != want {
- t.Errorf("truncated key got %q; want %q", s, want)
- }
-}
-
-func TestAllocatingByteSliceTarget(t *testing.T) {
- var dst []byte
- sink := AllocatingByteSliceSink(&dst)
-
- inBytes := []byte("some bytes")
- sink.SetBytes(inBytes)
- if want := "some bytes"; string(dst) != want {
- t.Errorf("SetBytes resulted in %q; want %q", dst, want)
- }
- v, err := sink.view()
- if err != nil {
- t.Fatalf("view after SetBytes failed: %v", err)
- }
- if &inBytes[0] == &dst[0] {
- t.Error("inBytes and dst share memory")
- }
- if &inBytes[0] == &v.b[0] {
- t.Error("inBytes and view share memory")
- }
- if &dst[0] == &v.b[0] {
- t.Error("dst and view share memory")
- }
-}
-
-// orderedFlightGroup allows the caller to force the schedule of when
-// orig.Do will be called. This is useful to serialize calls such
-// that singleflight cannot dedup them.
-type orderedFlightGroup struct {
- mu sync.Mutex
- stage1 chan bool
- stage2 chan bool
- orig flightGroup
-}
-
-func (g *orderedFlightGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
- <-g.stage1
- <-g.stage2
- g.mu.Lock()
- defer g.mu.Unlock()
- return g.orig.Do(key, fn)
-}
-
-// TestNoDedup tests invariants on the cache size when singleflight is
-// unable to dedup calls.
-func TestNoDedup(t *testing.T) {
- const testkey = "testkey"
- const testval = "testval"
- g := newGroup("testgroup", 1024, GetterFunc(func(_ Context, key string, dest Sink) error {
- return dest.SetString(testval)
- }), nil)
-
- orderedGroup := &orderedFlightGroup{
- stage1: make(chan bool),
- stage2: make(chan bool),
- orig: g.loadGroup,
- }
- // Replace loadGroup with our wrapper so we can control when
- // loadGroup.Do is entered for each concurrent request.
- g.loadGroup = orderedGroup
-
- // Issue two idential requests concurrently. Since the cache is
- // empty, it will miss. Both will enter load(), but we will only
- // allow one at a time to enter singleflight.Do, so the callback
- // function will be called twice.
- resc := make(chan string, 2)
- for i := 0; i < 2; i++ {
- go func() {
- var s string
- if err := g.Get(dummyCtx, testkey, StringSink(&s)); err != nil {
- resc <- "ERROR:" + err.Error()
- return
- }
- resc <- s
- }()
- }
-
- // Ensure both goroutines have entered the Do routine. This implies
- // both concurrent requests have checked the cache, found it empty,
- // and called load().
- orderedGroup.stage1 <- true
- orderedGroup.stage1 <- true
- orderedGroup.stage2 <- true
- orderedGroup.stage2 <- true
-
- for i := 0; i < 2; i++ {
- if s := <-resc; s != testval {
- t.Errorf("result is %s want %s", s, testval)
- }
- }
-
- const wantItems = 1
- if g.mainCache.items() != wantItems {
- t.Errorf("mainCache has %d items, want %d", g.mainCache.items(), wantItems)
- }
-
- // If the singleflight callback doesn't double-check the cache again
- // upon entry, we would increment nbytes twice but the entry would
- // only be in the cache once.
- const wantBytes = int64(len(testkey) + len(testval))
- if g.mainCache.nbytes != wantBytes {
- t.Errorf("cache has %d bytes, want %d", g.mainCache.nbytes, wantBytes)
- }
-}
-
-// TODO(bradfitz): port the Google-internal full integration test into here,
-// using HTTP requests instead of our RPC system.