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  }