github.com/chenfeining/golangci-lint@v1.0.2-0.20230730162517-14c6c67868df/internal/cache/cache_test.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cache 6 7 import ( 8 "bytes" 9 "encoding/binary" 10 "fmt" 11 "os" 12 "path/filepath" 13 "testing" 14 "time" 15 ) 16 17 func init() { 18 verify = false // even if GODEBUG is set 19 } 20 21 func TestBasic(t *testing.T) { 22 t.Parallel() 23 24 dir, err := os.MkdirTemp("", "cachetest-") 25 if err != nil { 26 t.Fatal(err) 27 } 28 defer os.RemoveAll(dir) 29 _, err = Open(filepath.Join(dir, "notexist")) 30 if err == nil { 31 t.Fatal(`Open("tmp/notexist") succeeded, want failure`) 32 } 33 34 cdir := filepath.Join(dir, "c1") 35 if err := os.Mkdir(cdir, 0744); err != nil { 36 t.Fatal(err) 37 } 38 39 c1, err := Open(cdir) 40 if err != nil { 41 t.Fatalf("Open(c1) (create): %v", err) 42 } 43 if err := c1.putIndexEntry(dummyID(1), dummyID(12), 13, true); err != nil { 44 t.Fatalf("addIndexEntry: %v", err) 45 } 46 if err := c1.putIndexEntry(dummyID(1), dummyID(2), 3, true); err != nil { // overwrite entry 47 t.Fatalf("addIndexEntry: %v", err) 48 } 49 if entry, err := c1.Get(dummyID(1)); err != nil || entry.OutputID != dummyID(2) || entry.Size != 3 { 50 t.Fatalf("c1.Get(1) = %x, %v, %v, want %x, %v, nil", entry.OutputID, entry.Size, err, dummyID(2), 3) 51 } 52 53 c2, err := Open(cdir) 54 if err != nil { 55 t.Fatalf("Open(c2) (reuse): %v", err) 56 } 57 if entry, err := c2.Get(dummyID(1)); err != nil || entry.OutputID != dummyID(2) || entry.Size != 3 { 58 t.Fatalf("c2.Get(1) = %x, %v, %v, want %x, %v, nil", entry.OutputID, entry.Size, err, dummyID(2), 3) 59 } 60 if err := c2.putIndexEntry(dummyID(2), dummyID(3), 4, true); err != nil { 61 t.Fatalf("addIndexEntry: %v", err) 62 } 63 if entry, err := c1.Get(dummyID(2)); err != nil || entry.OutputID != dummyID(3) || entry.Size != 4 { 64 t.Fatalf("c1.Get(2) = %x, %v, %v, want %x, %v, nil", entry.OutputID, entry.Size, err, dummyID(3), 4) 65 } 66 } 67 68 func TestGrowth(t *testing.T) { 69 t.Parallel() 70 71 dir, err := os.MkdirTemp("", "cachetest-") 72 if err != nil { 73 t.Fatal(err) 74 } 75 defer os.RemoveAll(dir) 76 77 c, err := Open(dir) 78 if err != nil { 79 t.Fatalf("Open: %v", err) 80 } 81 82 n := 10000 83 if testing.Short() { 84 n = 1000 85 } 86 87 for i := 0; i < n; i++ { 88 if err := c.putIndexEntry(dummyID(i), dummyID(i*99), int64(i)*101, true); err != nil { 89 t.Fatalf("addIndexEntry: %v", err) 90 } 91 id := ActionID(dummyID(i)) 92 entry, err := c.Get(id) 93 if err != nil { 94 t.Fatalf("Get(%x): %v", id, err) 95 } 96 if entry.OutputID != dummyID(i*99) || entry.Size != int64(i)*101 { 97 t.Errorf("Get(%x) = %x, %d, want %x, %d", id, entry.OutputID, entry.Size, dummyID(i*99), int64(i)*101) 98 } 99 } 100 for i := 0; i < n; i++ { 101 id := ActionID(dummyID(i)) 102 entry, err := c.Get(id) 103 if err != nil { 104 t.Fatalf("Get2(%x): %v", id, err) 105 } 106 if entry.OutputID != dummyID(i*99) || entry.Size != int64(i)*101 { 107 t.Errorf("Get2(%x) = %x, %d, want %x, %d", id, entry.OutputID, entry.Size, dummyID(i*99), int64(i)*101) 108 } 109 } 110 } 111 112 func TestVerifyPanic(t *testing.T) { 113 os.Setenv("GODEBUG", "gocacheverify=1") 114 initEnv() 115 defer func() { 116 os.Unsetenv("GODEBUG") 117 verify = false 118 }() 119 120 if !verify { 121 t.Fatal("initEnv did not set verify") 122 } 123 124 dir, err := os.MkdirTemp("", "cachetest-") 125 if err != nil { 126 t.Fatal(err) 127 } 128 defer os.RemoveAll(dir) 129 130 c, err := Open(dir) 131 if err != nil { 132 t.Fatalf("Open: %v", err) 133 } 134 135 id := ActionID(dummyID(1)) 136 if err := c.PutBytes(id, []byte("abc")); err != nil { 137 t.Fatal(err) 138 } 139 140 defer func() { 141 if err := recover(); err != nil { 142 t.Log(err) 143 return 144 } 145 }() 146 c.PutBytes(id, []byte("def")) 147 t.Fatal("mismatched Put did not panic in verify mode") 148 } 149 150 func dummyID(x int) [HashSize]byte { 151 var out [HashSize]byte 152 binary.LittleEndian.PutUint64(out[:], uint64(x)) 153 return out 154 } 155 156 func TestCacheTrim(t *testing.T) { 157 t.Parallel() 158 159 dir, err := os.MkdirTemp("", "cachetest-") 160 if err != nil { 161 t.Fatal(err) 162 } 163 defer os.RemoveAll(dir) 164 165 c, err := Open(dir) 166 if err != nil { 167 t.Fatalf("Open: %v", err) 168 } 169 const start = 1000000000 170 now := int64(start) 171 c.now = func() time.Time { return time.Unix(now, 0) } 172 173 checkTime := func(name string, mtime int64) { 174 t.Helper() 175 file := filepath.Join(c.dir, name[:2], name) 176 info, err := os.Stat(file) 177 if err != nil { 178 t.Fatal(err) 179 } 180 if info.ModTime().Unix() != mtime { 181 t.Fatalf("%s mtime = %d, want %d", name, info.ModTime().Unix(), mtime) 182 } 183 } 184 185 id := ActionID(dummyID(1)) 186 c.PutBytes(id, []byte("abc")) 187 entry, _ := c.Get(id) 188 c.PutBytes(ActionID(dummyID(2)), []byte("def")) 189 mtime := now 190 checkTime(fmt.Sprintf("%x-a", id), mtime) 191 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime) 192 193 // Get should not change recent mtimes. 194 now = start + 10 195 c.Get(id) 196 checkTime(fmt.Sprintf("%x-a", id), mtime) 197 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime) 198 199 // Get should change distant mtimes. 200 now = start + 5000 201 mtime2 := now 202 if _, err := c.Get(id); err != nil { 203 t.Fatal(err) 204 } 205 c.OutputFile(entry.OutputID) 206 checkTime(fmt.Sprintf("%x-a", id), mtime2) 207 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime2) 208 209 // Trim should leave everything alone: it's all too new. 210 c.Trim() 211 if _, err := c.Get(id); err != nil { 212 t.Fatal(err) 213 } 214 c.OutputFile(entry.OutputID) 215 data, err := os.ReadFile(filepath.Join(dir, "trim.txt")) 216 if err != nil { 217 t.Fatal(err) 218 } 219 checkTime(fmt.Sprintf("%x-a", dummyID(2)), start) 220 221 // Trim less than a day later should not do any work at all. 222 now = start + 80000 223 c.Trim() 224 if _, err := c.Get(id); err != nil { 225 t.Fatal(err) 226 } 227 c.OutputFile(entry.OutputID) 228 data2, err := os.ReadFile(filepath.Join(dir, "trim.txt")) 229 if err != nil { 230 t.Fatal(err) 231 } 232 if !bytes.Equal(data, data2) { 233 t.Fatalf("second trim did work: %q -> %q", data, data2) 234 } 235 236 // Fast-forward and do another trim just before the 5-day cutoff. 237 // Note that because of usedQuantum the cutoff is actually 5 days + 1 hour. 238 // We used c.Get(id) just now, so 5 days later it should still be kept. 239 // On the other hand almost a full day has gone by since we wrote dummyID(2) 240 // and we haven't looked at it since, so 5 days later it should be gone. 241 now += 5 * 86400 242 checkTime(fmt.Sprintf("%x-a", dummyID(2)), start) 243 c.Trim() 244 if _, err := c.Get(id); err != nil { 245 t.Fatal(err) 246 } 247 c.OutputFile(entry.OutputID) 248 mtime3 := now 249 if _, err := c.Get(dummyID(2)); err == nil { // haven't done a Get for this since original write above 250 t.Fatalf("Trim did not remove dummyID(2)") 251 } 252 253 // The c.Get(id) refreshed id's mtime again. 254 // Check that another 5 days later it is still not gone, 255 // but check by using checkTime, which doesn't bring mtime forward. 256 now += 5 * 86400 257 c.Trim() 258 checkTime(fmt.Sprintf("%x-a", id), mtime3) 259 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime3) 260 261 // Half a day later Trim should still be a no-op, because there was a Trim recently. 262 // Even though the entry for id is now old enough to be trimmed, 263 // it gets a reprieve until the time comes for a new Trim scan. 264 now += 86400 / 2 265 c.Trim() 266 checkTime(fmt.Sprintf("%x-a", id), mtime3) 267 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime3) 268 269 // Another half a day later, Trim should actually run, and it should remove id. 270 now += 86400/2 + 1 271 c.Trim() 272 if _, err := c.Get(dummyID(1)); err == nil { 273 t.Fatal("Trim did not remove dummyID(1)") 274 } 275 }