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  }