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