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  }