k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/greenhouse/diskcache/cache_test.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 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 diskcache 18 19 import ( 20 "bytes" 21 "crypto/rand" 22 "crypto/sha256" 23 "encoding/hex" 24 "io" 25 "path/filepath" 26 "testing" 27 28 "k8s.io/apimachinery/pkg/util/sets" 29 ) 30 31 func hashBytes(b []byte) string { 32 hasher := sha256.New() 33 hasher.Write(b) 34 return hex.EncodeToString(hasher.Sum(nil)) 35 } 36 37 func makeRandomBytes(n int) (b []byte, err error) { 38 b = make([]byte, n) 39 _, err = rand.Read(b) 40 if err != nil { 41 return nil, err 42 } 43 return b, nil 44 } 45 46 // test cache.PathToKey and cache.KeyToPath 47 func TestPathToKeyKeyToPath(t *testing.T) { 48 cache := NewCache("/some/dir") 49 testCases := []struct { 50 Key string 51 ExpectedPath string 52 }{ 53 { 54 Key: "key", 55 ExpectedPath: filepath.Join("/some/dir", "key"), 56 }, 57 { 58 Key: "namespaced/key", 59 ExpectedPath: filepath.Join("/some/dir", "namespaced/key"), 60 }, 61 { 62 Key: "entry/nested/a/few/times", 63 ExpectedPath: filepath.Join("/some/dir", "entry/nested/a/few/times"), 64 }, 65 { 66 Key: "foobar/cas/asdf", 67 ExpectedPath: filepath.Join("/some/dir", "foobar/cas/asdf"), 68 }, 69 { 70 Key: "foobar/ac/asdf", 71 ExpectedPath: filepath.Join("/some/dir", "foobar/ac/asdf"), 72 }, 73 } 74 for _, tc := range testCases { 75 path := cache.KeyToPath(tc.Key) 76 if path != tc.ExpectedPath { 77 t.Fatalf("expected KeyToPath(%s) to be %s", tc.Key, path) 78 } 79 backToKey := cache.PathToKey(path) 80 if backToKey != tc.Key { 81 t.Fatalf("cache.KeyToPath(cache.PathToKey(%s)) was not idempotent, got %s", tc.Key, backToKey) 82 } 83 } 84 } 85 86 // test stateful cache methods 87 func TestCacheStorage(t *testing.T) { 88 // create a cache in a tempdir 89 dir := t.TempDir() 90 cache := NewCache(dir) 91 92 // sanity checks 93 if cache.DiskRoot() != dir { 94 t.Fatalf("Expected DiskRoot to be %v not %v", dir, cache.DiskRoot()) 95 } 96 // we haven't put anything yet, so get should return exists == false 97 err := cache.Get("some/key", func(exists bool, contents io.ReadSeeker) error { 98 if exists { 99 t.Fatal("no keys should exist yet!") 100 } 101 return nil 102 }) 103 if err != nil { 104 t.Fatalf("Got unexpected error testing non-existent key: %v", err) 105 } 106 107 // Test Put and Get together 108 // create 1 MB of random bytes 109 lotsOfRandomBytes, err := makeRandomBytes(1000000) 110 if err != nil { 111 t.Fatalf("Failed to create random test data: %v", err) 112 } 113 testCases := []struct { 114 Name string 115 Key string 116 Contents []byte 117 Hash string 118 PutShouldError bool 119 }{ 120 { 121 Name: "Normal", 122 Key: "foo", 123 Contents: []byte{1, 3, 3, 7}, 124 Hash: hashBytes([]byte{1, 3, 3, 7}), 125 PutShouldError: false, 126 }, 127 { 128 Name: "Bad Hash", 129 Key: "test/foo/baz", 130 Contents: []byte{1, 3, 3, 7}, 131 Hash: hashBytes([]byte{3, 1, 3, 3, 7}), 132 PutShouldError: true, 133 }, 134 { 135 Name: "Normal with Path Segments", 136 Key: "test/bar/baz", 137 Contents: []byte{107, 56, 115}, 138 Hash: hashBytes([]byte{107, 56, 115}), 139 PutShouldError: false, 140 }, 141 { 142 Name: "Lots of Random Bytes", 143 Key: "a/b/c", 144 Contents: lotsOfRandomBytes, 145 Hash: hashBytes(lotsOfRandomBytes), 146 PutShouldError: false, 147 }, 148 } 149 expectedKeys := sets.NewString() 150 for _, tc := range testCases { 151 err := cache.Put(tc.Key, bytes.NewReader(tc.Contents), tc.Hash) 152 if err != nil && !tc.PutShouldError { 153 t.Fatalf("Got error '%v' for test case '%s' and expected none.", err, tc.Name) 154 } else if err == nil && tc.PutShouldError { 155 t.Fatalf("Did not get error for test case '%s' and expected one.", tc.Name) 156 } else if err == nil { 157 expectedKeys.Insert(tc.Key) 158 } 159 160 err = cache.Get(tc.Key, func(exists bool, contents io.ReadSeeker) error { 161 if exists && tc.PutShouldError { 162 t.Fatalf("Got key exists for test case '%s' which should not.", tc.Name) 163 } else if !exists && !tc.PutShouldError { 164 t.Fatalf("Got key does not exist for test case '%s' which should.", tc.Name) 165 } 166 if exists { 167 read, err2 := io.ReadAll(contents) 168 if err2 != nil { 169 t.Fatalf("Failed to read contents for test case '%s", tc.Name) 170 } 171 if !bytes.Equal(read, tc.Contents) { 172 t.Fatalf("Contents did not match expected for test case '%s' (got: %v expected: %v)", tc.Name, read, tc.Contents) 173 } 174 } 175 return nil 176 }) 177 if err != nil { 178 t.Fatalf("Got unepected error getting cache key for test case '%s': %v", tc.Name, err) 179 } 180 } 181 182 // test GetEntries 183 entries := cache.GetEntries() 184 receivedKeys := sets.NewString() 185 for _, entry := range entries { 186 receivedKeys.Insert(cache.PathToKey(entry.Path)) 187 } 188 if !expectedKeys.Equal(receivedKeys) { 189 t.Fatalf("entries %v does not equal expected: %v", receivedKeys, expectedKeys) 190 } 191 192 // test deleting all keys 193 for _, key := range expectedKeys.List() { 194 err = cache.Delete(key) 195 if err != nil { 196 t.Fatalf("failed to delete key: %v", err) 197 } 198 } 199 entries = cache.GetEntries() 200 if len(entries) != 0 { 201 t.Fatalf("cache.GetEntries() should be empty after deleting all keys, got: %v", entries) 202 } 203 }