github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/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 "github.com/go-asm/go/testenv" 17 ) 18 19 func init() { 20 verify = false // even if GODEBUG is set 21 } 22 23 func TestBasic(t *testing.T) { 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, 0777); 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 dir, err := os.MkdirTemp("", "cachetest-") 70 if err != nil { 71 t.Fatal(err) 72 } 73 defer os.RemoveAll(dir) 74 75 c, err := Open(dir) 76 if err != nil { 77 t.Fatalf("Open: %v", err) 78 } 79 80 n := 10000 81 if testing.Short() { 82 n = 10 83 } 84 85 for i := 0; i < n; i++ { 86 if err := c.putIndexEntry(dummyID(i), dummyID(i*99), int64(i)*101, true); err != nil { 87 t.Fatalf("addIndexEntry: %v", err) 88 } 89 id := ActionID(dummyID(i)) 90 entry, err := c.Get(id) 91 if err != nil { 92 t.Fatalf("Get(%x): %v", id, err) 93 } 94 if entry.OutputID != dummyID(i*99) || entry.Size != int64(i)*101 { 95 t.Errorf("Get(%x) = %x, %d, want %x, %d", id, entry.OutputID, entry.Size, dummyID(i*99), int64(i)*101) 96 } 97 } 98 for i := 0; i < n; i++ { 99 id := ActionID(dummyID(i)) 100 entry, err := c.Get(id) 101 if err != nil { 102 t.Fatalf("Get2(%x): %v", id, err) 103 } 104 if entry.OutputID != dummyID(i*99) || entry.Size != int64(i)*101 { 105 t.Errorf("Get2(%x) = %x, %d, want %x, %d", id, entry.OutputID, entry.Size, dummyID(i*99), int64(i)*101) 106 } 107 } 108 } 109 110 func TestVerifyPanic(t *testing.T) { 111 os.Setenv("GODEBUG", "gocacheverify=1") 112 initEnv() 113 defer func() { 114 os.Unsetenv("GODEBUG") 115 verify = false 116 }() 117 118 if !verify { 119 t.Fatal("initEnv did not set verify") 120 } 121 122 dir, err := os.MkdirTemp("", "cachetest-") 123 if err != nil { 124 t.Fatal(err) 125 } 126 defer os.RemoveAll(dir) 127 128 c, err := Open(dir) 129 if err != nil { 130 t.Fatalf("Open: %v", err) 131 } 132 133 id := ActionID(dummyID(1)) 134 if err := PutBytes(c, id, []byte("abc")); err != nil { 135 t.Fatal(err) 136 } 137 138 defer func() { 139 if err := recover(); err != nil { 140 t.Log(err) 141 return 142 } 143 }() 144 PutBytes(c, id, []byte("def")) 145 t.Fatal("mismatched Put did not panic in verify mode") 146 } 147 148 func dummyID(x int) [HashSize]byte { 149 var out [HashSize]byte 150 binary.LittleEndian.PutUint64(out[:], uint64(x)) 151 return out 152 } 153 154 func TestCacheTrim(t *testing.T) { 155 dir, err := os.MkdirTemp("", "cachetest-") 156 if err != nil { 157 t.Fatal(err) 158 } 159 defer os.RemoveAll(dir) 160 161 c, err := Open(dir) 162 if err != nil { 163 t.Fatalf("Open: %v", err) 164 } 165 const start = 1000000000 166 now := int64(start) 167 c.now = func() time.Time { return time.Unix(now, 0) } 168 169 checkTime := func(name string, mtime int64) { 170 t.Helper() 171 file := filepath.Join(c.dir, name[:2], name) 172 info, err := os.Stat(file) 173 if err != nil { 174 t.Fatal(err) 175 } 176 if info.ModTime().Unix() != mtime { 177 t.Fatalf("%s mtime = %d, want %d", name, info.ModTime().Unix(), mtime) 178 } 179 } 180 181 id := ActionID(dummyID(1)) 182 PutBytes(c, id, []byte("abc")) 183 entry, _ := c.Get(id) 184 PutBytes(c, ActionID(dummyID(2)), []byte("def")) 185 mtime := now 186 checkTime(fmt.Sprintf("%x-a", id), mtime) 187 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime) 188 189 // Get should not change recent mtimes. 190 now = start + 10 191 c.Get(id) 192 checkTime(fmt.Sprintf("%x-a", id), mtime) 193 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime) 194 195 // Get should change distant mtimes. 196 now = start + 5000 197 mtime2 := now 198 if _, err := c.Get(id); err != nil { 199 t.Fatal(err) 200 } 201 c.OutputFile(entry.OutputID) 202 checkTime(fmt.Sprintf("%x-a", id), mtime2) 203 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime2) 204 205 // Trim should leave everything alone: it's all too new. 206 if err := c.Trim(); err != nil { 207 if testenv.SyscallIsNotSupported(err) { 208 t.Skipf("skipping: Trim is unsupported (%v)", err) 209 } 210 t.Fatal(err) 211 } 212 if _, err := c.Get(id); err != nil { 213 t.Fatal(err) 214 } 215 c.OutputFile(entry.OutputID) 216 data, err := os.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 if err := c.Trim(); err != nil { 225 t.Fatal(err) 226 } 227 if _, err := c.Get(id); err != nil { 228 t.Fatal(err) 229 } 230 c.OutputFile(entry.OutputID) 231 data2, err := os.ReadFile(filepath.Join(dir, "trim.txt")) 232 if err != nil { 233 t.Fatal(err) 234 } 235 if !bytes.Equal(data, data2) { 236 t.Fatalf("second trim did work: %q -> %q", data, data2) 237 } 238 239 // Fast forward and do another trim just before the 5 day cutoff. 240 // Note that because of usedQuantum the cutoff is actually 5 days + 1 hour. 241 // We used c.Get(id) just now, so 5 days later it should still be kept. 242 // On the other hand almost a full day has gone by since we wrote dummyID(2) 243 // and we haven't looked at it since, so 5 days later it should be gone. 244 now += 5 * 86400 245 checkTime(fmt.Sprintf("%x-a", dummyID(2)), start) 246 if err := c.Trim(); err != nil { 247 t.Fatal(err) 248 } 249 if _, err := c.Get(id); err != nil { 250 t.Fatal(err) 251 } 252 c.OutputFile(entry.OutputID) 253 mtime3 := now 254 if _, err := c.Get(dummyID(2)); err == nil { // haven't done a Get for this since original write above 255 t.Fatalf("Trim did not remove dummyID(2)") 256 } 257 258 // The c.Get(id) refreshed id's mtime again. 259 // Check that another 5 days later it is still not gone, 260 // but check by using checkTime, which doesn't bring mtime forward. 261 now += 5 * 86400 262 if err := c.Trim(); err != nil { 263 t.Fatal(err) 264 } 265 checkTime(fmt.Sprintf("%x-a", id), mtime3) 266 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime3) 267 268 // Half a day later Trim should still be a no-op, because there was a Trim recently. 269 // Even though the entry for id is now old enough to be trimmed, 270 // it gets a reprieve until the time comes for a new Trim scan. 271 now += 86400 / 2 272 if err := c.Trim(); err != nil { 273 t.Fatal(err) 274 } 275 checkTime(fmt.Sprintf("%x-a", id), mtime3) 276 checkTime(fmt.Sprintf("%x-d", entry.OutputID), mtime3) 277 278 // Another half a day later, Trim should actually run, and it should remove id. 279 now += 86400/2 + 1 280 if err := c.Trim(); err != nil { 281 t.Fatal(err) 282 } 283 if _, err := c.Get(dummyID(1)); err == nil { 284 t.Fatal("Trim did not remove dummyID(1)") 285 } 286 }