kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/platform/cache/cache_test.go (about)

     1  /*
     2   * Copyright 2014 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cache
    18  
    19  import (
    20  	goflag "flag"
    21  	"testing"
    22  )
    23  
    24  const fileData = "expected file contents"
    25  
    26  type mockFetcher struct {
    27  	path, digest string
    28  }
    29  
    30  func (m *mockFetcher) Fetch(path, digest string) ([]byte, error) {
    31  	m.path, m.digest = path, digest
    32  	return []byte(fileData), nil
    33  }
    34  
    35  func TestDelegation(t *testing.T) {
    36  	// Verify that the compilation wrapper correctly delegates to the
    37  	// underlying implementation of the Compilation interface.
    38  
    39  	const filePath = "file/to/fetch"
    40  	const fileDigest = "0123456789abcdef9876543210fedcba"
    41  	var mock mockFetcher
    42  	test := Fetcher(&mock, New(128))
    43  
    44  	data, err := test.Fetch(filePath, fileDigest)
    45  	if err != nil {
    46  		t.Errorf("Fetch %q: unexpected error: %s", filePath, err)
    47  	}
    48  	if s := string(data); s != fileData {
    49  		t.Errorf("Fetch %q: got %q, want %q", filePath, s, fileData)
    50  	}
    51  	if mock.path != filePath || mock.digest != fileDigest {
    52  		t.Errorf("Fetch %q: delegate was not invoked correctly", filePath)
    53  	}
    54  
    55  	// Verify that the Fetch caching worked, i.e., we don't call the delegate a
    56  	// second time given that the results do fit in the cache.
    57  
    58  	mock.path, mock.digest = "", "" // Reset the delegate
    59  	data, err = test.Fetch(filePath, fileDigest)
    60  	if err != nil {
    61  		t.Errorf("Fetch %q: unexpected error: %s", filePath, err)
    62  	}
    63  	if s := string(data); s != fileData {
    64  		t.Errorf("Fetch %q: got %q, want %q", filePath, s, fileData)
    65  	}
    66  	if mock.path != "" {
    67  		t.Errorf("Fetch %q: delegate was invoked again (%q)", filePath, mock.path)
    68  	}
    69  }
    70  
    71  func TestCacheBounds(t *testing.T) {
    72  	testData := map[string]string{
    73  		"k1":   "abcd",
    74  		"k2":   "123456",
    75  		"k3":   "planet10",
    76  		"last": "pdq",
    77  	}
    78  
    79  	// Construct a cache with a little less space than will fit all the keys in
    80  	// the test data, so that the last key entered will force an eviction.
    81  	totalSize := 0
    82  	for _, v := range testData {
    83  		totalSize += len(v)
    84  	}
    85  	totalSize -= len(testData["last"])
    86  
    87  	// Put everything but the last key into the cache; as a result, the cache
    88  	// should be at capacity.
    89  	c := New(totalSize)
    90  	for _, key := range []string{"k1", "k2", "k3"} {
    91  		c.Put(key, []byte(testData[key]))
    92  		t.Logf("Put %q, size now: %d bytes", key, c.curBytes)
    93  	}
    94  	if c.curBytes != c.maxBytes {
    95  		t.Errorf("cache size: got %d, want %d", c.curBytes, c.maxBytes)
    96  	}
    97  
    98  	// Simulate some lookups.  After this, k3 should be the least frequently used,
    99  	// and should be the first evicted key when we insert.
   100  	for _, key := range []string{"k2", "x", "k1", "y", "k1", "k1", "z", "k2"} {
   101  		v := c.Get(key)
   102  		if key[0] == 'k' {
   103  			if s := string(v); s != testData[key] {
   104  				t.Errorf("Get %q: got %q, want %q", key, s, testData[key])
   105  			}
   106  		} else if v != nil {
   107  			t.Errorf("Get %q: got %q, should be missing", key, string(v))
   108  		}
   109  	}
   110  
   111  	for k, v := range c.data {
   112  		t.Logf("Key %q entry %+v", k, v)
   113  	}
   114  
   115  	// Adding the last key should evict k3, and leave everything else alone,
   116  	// including the one that was just added.
   117  	c.Put("last", []byte(testData["last"]))
   118  	if v := c.Get("k3"); v != nil {
   119  		t.Errorf("Get %q: got %q, should be missing", "k3", string(v))
   120  	}
   121  	for _, key := range []string{"k1", "k2", "last"} {
   122  		if v := c.Get(key); v == nil {
   123  			t.Errorf("Get %q: is missing, want %q", key, testData[key])
   124  		} else if s := string(v); s != testData[key] {
   125  			t.Errorf("Get %q: got %q, want %q", key, s, testData[key])
   126  		}
   127  	}
   128  }
   129  
   130  func TestCacheCorners(t *testing.T) {
   131  	if c := New(0); c != nil {
   132  		t.Errorf("New 0: got %+v, want nil", c)
   133  	}
   134  	if c := New(-1); c != nil {
   135  		t.Errorf("New -1: got %+v, want nil", c)
   136  	}
   137  
   138  	c := New(10)
   139  	c.Put("a", []byte("abc"))
   140  	c.Put("b", []byte("def"))
   141  	c.Put("c", []byte("g"))
   142  
   143  	// Trying to insert something too big to ever fit in the cache should not
   144  	// evict any of the stuff that's already there.
   145  	c.Put("huge", []byte("0123456789ABCDEF"))
   146  
   147  	for _, key := range []string{"a", "b", "c"} {
   148  		if c.Get(key) == nil {
   149  			t.Errorf("Get %q: unexpectedly missing", key)
   150  		}
   151  	}
   152  	if v := c.Get("huge"); v != nil {
   153  		t.Errorf("Get %q: got %q, should be missing", "huge", string(v))
   154  	}
   155  
   156  	// Inserting something that just fits should evict everything else.
   157  	c.Put("snug", []byte("0123456789"))
   158  	if c.Get("snug") == nil {
   159  		t.Errorf("Get %q: unexpectedly missing", "snug")
   160  	}
   161  	for _, key := range []string{"a", "b", "c"} {
   162  		if v := c.Get(key); v != nil {
   163  			t.Errorf("Get %q: got %q, should be missing", key, string(v))
   164  		}
   165  	}
   166  }
   167  
   168  // Verify that ParseByteSize works as intended.
   169  func TestParseByteSize(t *testing.T) {
   170  	// Enforce that *ByteSize implements the flag.Value interface for the Go flag package.
   171  	var _ goflag.Value = (*ByteSize)(nil)
   172  
   173  	tests := []struct {
   174  		input string
   175  		want  int
   176  	}{
   177  		// Valid responses
   178  		{"0", 0},
   179  		{"0G", 0},
   180  		{"1", 1},
   181  		{"5b", 5},
   182  		{"101B", 101},
   183  		{"1K", 1024},
   184  		{"1.3k", 1331},
   185  		{"2m", 2097152},
   186  		{"2.5M", 2621440},
   187  		{".25g", 268435456},
   188  		{"0.125T", 137438953472},
   189  
   190  		// Error conditions
   191  		{"", -1},
   192  		{"-523g", -1},
   193  		{"11blah", -1},
   194  	}
   195  	for _, test := range tests {
   196  		got, err := ParseByteSize(test.input)
   197  		if err != nil && test.want >= 0 {
   198  			t.Errorf("parse %q: unexpected error: %s", test.input, err)
   199  		} else if got != test.want {
   200  			t.Errorf("parse %q: got %d, want %d", test.input, got, test.want)
   201  		}
   202  	}
   203  }