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