kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/platform/cache/cache_test.go (about) 1 /* 2 * Copyright 2014 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cache 18 19 import ( 20 goflag "flag" 21 "testing" 22 ) 23 24 const fileData = "expected file contents" 25 26 type mockFetcher struct { 27 path, digest string 28 } 29 30 func (m *mockFetcher) Fetch(path, digest string) ([]byte, error) { 31 m.path, m.digest = path, digest 32 return []byte(fileData), nil 33 } 34 35 func TestDelegation(t *testing.T) { 36 // Verify that the compilation wrapper correctly delegates to the 37 // underlying implementation of the Compilation interface. 38 39 const filePath = "file/to/fetch" 40 const fileDigest = "0123456789abcdef9876543210fedcba" 41 var mock mockFetcher 42 test := Fetcher(&mock, New(128)) 43 44 data, err := test.Fetch(filePath, fileDigest) 45 if err != nil { 46 t.Errorf("Fetch %q: unexpected error: %s", filePath, err) 47 } 48 if s := string(data); s != fileData { 49 t.Errorf("Fetch %q: got %q, want %q", filePath, s, fileData) 50 } 51 if mock.path != filePath || mock.digest != fileDigest { 52 t.Errorf("Fetch %q: delegate was not invoked correctly", filePath) 53 } 54 55 // Verify that the Fetch caching worked, i.e., we don't call the delegate a 56 // second time given that the results do fit in the cache. 57 58 mock.path, mock.digest = "", "" // Reset the delegate 59 data, err = test.Fetch(filePath, fileDigest) 60 if err != nil { 61 t.Errorf("Fetch %q: unexpected error: %s", filePath, err) 62 } 63 if s := string(data); s != fileData { 64 t.Errorf("Fetch %q: got %q, want %q", filePath, s, fileData) 65 } 66 if mock.path != "" { 67 t.Errorf("Fetch %q: delegate was invoked again (%q)", filePath, mock.path) 68 } 69 } 70 71 func TestCacheBounds(t *testing.T) { 72 testData := map[string]string{ 73 "k1": "abcd", 74 "k2": "123456", 75 "k3": "planet10", 76 "last": "pdq", 77 } 78 79 // Construct a cache with a little less space than will fit all the keys in 80 // the test data, so that the last key entered will force an eviction. 81 totalSize := 0 82 for _, v := range testData { 83 totalSize += len(v) 84 } 85 totalSize -= len(testData["last"]) 86 87 // Put everything but the last key into the cache; as a result, the cache 88 // should be at capacity. 89 c := New(totalSize) 90 for _, key := range []string{"k1", "k2", "k3"} { 91 c.Put(key, []byte(testData[key])) 92 t.Logf("Put %q, size now: %d bytes", key, c.curBytes) 93 } 94 if c.curBytes != c.maxBytes { 95 t.Errorf("cache size: got %d, want %d", c.curBytes, c.maxBytes) 96 } 97 98 // Simulate some lookups. After this, k3 should be the least frequently used, 99 // and should be the first evicted key when we insert. 100 for _, key := range []string{"k2", "x", "k1", "y", "k1", "k1", "z", "k2"} { 101 v := c.Get(key) 102 if key[0] == 'k' { 103 if s := string(v); s != testData[key] { 104 t.Errorf("Get %q: got %q, want %q", key, s, testData[key]) 105 } 106 } else if v != nil { 107 t.Errorf("Get %q: got %q, should be missing", key, string(v)) 108 } 109 } 110 111 for k, v := range c.data { 112 t.Logf("Key %q entry %+v", k, v) 113 } 114 115 // Adding the last key should evict k3, and leave everything else alone, 116 // including the one that was just added. 117 c.Put("last", []byte(testData["last"])) 118 if v := c.Get("k3"); v != nil { 119 t.Errorf("Get %q: got %q, should be missing", "k3", string(v)) 120 } 121 for _, key := range []string{"k1", "k2", "last"} { 122 if v := c.Get(key); v == nil { 123 t.Errorf("Get %q: is missing, want %q", key, testData[key]) 124 } else if s := string(v); s != testData[key] { 125 t.Errorf("Get %q: got %q, want %q", key, s, testData[key]) 126 } 127 } 128 } 129 130 func TestCacheCorners(t *testing.T) { 131 if c := New(0); c != nil { 132 t.Errorf("New 0: got %+v, want nil", c) 133 } 134 if c := New(-1); c != nil { 135 t.Errorf("New -1: got %+v, want nil", c) 136 } 137 138 c := New(10) 139 c.Put("a", []byte("abc")) 140 c.Put("b", []byte("def")) 141 c.Put("c", []byte("g")) 142 143 // Trying to insert something too big to ever fit in the cache should not 144 // evict any of the stuff that's already there. 145 c.Put("huge", []byte("0123456789ABCDEF")) 146 147 for _, key := range []string{"a", "b", "c"} { 148 if c.Get(key) == nil { 149 t.Errorf("Get %q: unexpectedly missing", key) 150 } 151 } 152 if v := c.Get("huge"); v != nil { 153 t.Errorf("Get %q: got %q, should be missing", "huge", string(v)) 154 } 155 156 // Inserting something that just fits should evict everything else. 157 c.Put("snug", []byte("0123456789")) 158 if c.Get("snug") == nil { 159 t.Errorf("Get %q: unexpectedly missing", "snug") 160 } 161 for _, key := range []string{"a", "b", "c"} { 162 if v := c.Get(key); v != nil { 163 t.Errorf("Get %q: got %q, should be missing", key, string(v)) 164 } 165 } 166 } 167 168 // Verify that ParseByteSize works as intended. 169 func TestParseByteSize(t *testing.T) { 170 // Enforce that *ByteSize implements the flag.Value interface for the Go flag package. 171 var _ goflag.Value = (*ByteSize)(nil) 172 173 tests := []struct { 174 input string 175 want int 176 }{ 177 // Valid responses 178 {"0", 0}, 179 {"0G", 0}, 180 {"1", 1}, 181 {"5b", 5}, 182 {"101B", 101}, 183 {"1K", 1024}, 184 {"1.3k", 1331}, 185 {"2m", 2097152}, 186 {"2.5M", 2621440}, 187 {".25g", 268435456}, 188 {"0.125T", 137438953472}, 189 190 // Error conditions 191 {"", -1}, 192 {"-523g", -1}, 193 {"11blah", -1}, 194 } 195 for _, test := range tests { 196 got, err := ParseByteSize(test.input) 197 if err != nil && test.want >= 0 { 198 t.Errorf("parse %q: unexpected error: %s", test.input, err) 199 } else if got != test.want { 200 t.Errorf("parse %q: got %d, want %d", test.input, got, test.want) 201 } 202 } 203 }