github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/metacache-entries_test.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "fmt" 22 "math/rand" 23 "reflect" 24 "sort" 25 "testing" 26 "time" 27 ) 28 29 func Test_metaCacheEntries_sort(t *testing.T) { 30 entries := loadMetacacheSampleEntries(t) 31 32 o := entries.entries() 33 if !o.isSorted() { 34 t.Fatal("Expected sorted objects") 35 } 36 37 // Swap first and last 38 o[0], o[len(o)-1] = o[len(o)-1], o[0] 39 if o.isSorted() { 40 t.Fatal("Expected unsorted objects") 41 } 42 43 sorted := o.sort() 44 if !o.isSorted() { 45 t.Fatal("Expected sorted o objects") 46 } 47 if !sorted.entries().isSorted() { 48 t.Fatal("Expected sorted wrapped objects") 49 } 50 want := loadMetacacheSampleNames 51 for i, got := range o { 52 if got.name != want[i] { 53 t.Errorf("entry %d, want %q, got %q", i, want[i], got.name) 54 } 55 } 56 } 57 58 func Test_metaCacheEntries_forwardTo(t *testing.T) { 59 org := loadMetacacheSampleEntries(t) 60 entries := org 61 want := []string{"src/compress/zlib/reader_test.go", "src/compress/zlib/writer.go", "src/compress/zlib/writer_test.go"} 62 entries.forwardTo("src/compress/zlib/reader_test.go") 63 got := entries.entries().names() 64 if !reflect.DeepEqual(got, want) { 65 t.Errorf("got unexpected result: %#v", got) 66 } 67 68 // Try with prefix 69 entries = org 70 entries.forwardTo("src/compress/zlib/reader_t") 71 got = entries.entries().names() 72 if !reflect.DeepEqual(got, want) { 73 t.Errorf("got unexpected result: %#v", got) 74 } 75 } 76 77 func Test_metaCacheEntries_merge(t *testing.T) { 78 org := loadMetacacheSampleEntries(t) 79 a, b := org.shallowClone(), org.shallowClone() 80 be := b.entries() 81 for i := range be { 82 // Modify b so it isn't deduplicated. 83 be[i].metadata = []byte("something-else") 84 } 85 // Merge b into a 86 a.merge(b, -1) 87 //nolint:gocritic 88 want := append(loadMetacacheSampleNames, loadMetacacheSampleNames...) 89 sort.Strings(want) 90 got := a.entries().names() 91 if len(got) != len(want) { 92 t.Errorf("unexpected count, want %v, got %v", len(want), len(got)) 93 } 94 95 for i, name := range got { 96 if want[i] != name { 97 t.Errorf("unexpected name, want %q, got %q", want[i], name) 98 } 99 } 100 } 101 102 func Test_metaCacheEntries_filterObjects(t *testing.T) { 103 data := loadMetacacheSampleEntries(t) 104 data.filterObjectsOnly() 105 got := data.entries().names() 106 want := []string{"src/compress/bzip2/bit_reader.go", "src/compress/bzip2/bzip2.go", "src/compress/bzip2/bzip2_test.go", "src/compress/bzip2/huffman.go", "src/compress/bzip2/move_to_front.go", "src/compress/bzip2/testdata/Isaac.Newton-Opticks.txt.bz2", "src/compress/bzip2/testdata/e.txt.bz2", "src/compress/bzip2/testdata/fail-issue5747.bz2", "src/compress/bzip2/testdata/pass-random1.bin", "src/compress/bzip2/testdata/pass-random1.bz2", "src/compress/bzip2/testdata/pass-random2.bin", "src/compress/bzip2/testdata/pass-random2.bz2", "src/compress/bzip2/testdata/pass-sawtooth.bz2", "src/compress/bzip2/testdata/random.data.bz2", "src/compress/flate/deflate.go", "src/compress/flate/deflate_test.go", "src/compress/flate/deflatefast.go", "src/compress/flate/dict_decoder.go", "src/compress/flate/dict_decoder_test.go", "src/compress/flate/example_test.go", "src/compress/flate/flate_test.go", "src/compress/flate/huffman_bit_writer.go", "src/compress/flate/huffman_bit_writer_test.go", "src/compress/flate/huffman_code.go", "src/compress/flate/inflate.go", "src/compress/flate/inflate_test.go", "src/compress/flate/reader_test.go", "src/compress/flate/testdata/huffman-null-max.dyn.expect", "src/compress/flate/testdata/huffman-null-max.dyn.expect-noinput", "src/compress/flate/testdata/huffman-null-max.golden", "src/compress/flate/testdata/huffman-null-max.in", "src/compress/flate/testdata/huffman-null-max.wb.expect", "src/compress/flate/testdata/huffman-null-max.wb.expect-noinput", "src/compress/flate/testdata/huffman-pi.dyn.expect", "src/compress/flate/testdata/huffman-pi.dyn.expect-noinput", "src/compress/flate/testdata/huffman-pi.golden", "src/compress/flate/testdata/huffman-pi.in", "src/compress/flate/testdata/huffman-pi.wb.expect", "src/compress/flate/testdata/huffman-pi.wb.expect-noinput", "src/compress/flate/testdata/huffman-rand-1k.dyn.expect", "src/compress/flate/testdata/huffman-rand-1k.dyn.expect-noinput", "src/compress/flate/testdata/huffman-rand-1k.golden", "src/compress/flate/testdata/huffman-rand-1k.in", "src/compress/flate/testdata/huffman-rand-1k.wb.expect", "src/compress/flate/testdata/huffman-rand-1k.wb.expect-noinput", "src/compress/flate/testdata/huffman-rand-limit.dyn.expect", "src/compress/flate/testdata/huffman-rand-limit.dyn.expect-noinput", "src/compress/flate/testdata/huffman-rand-limit.golden", "src/compress/flate/testdata/huffman-rand-limit.in", "src/compress/flate/testdata/huffman-rand-limit.wb.expect", "src/compress/flate/testdata/huffman-rand-limit.wb.expect-noinput", "src/compress/flate/testdata/huffman-rand-max.golden", "src/compress/flate/testdata/huffman-rand-max.in", "src/compress/flate/testdata/huffman-shifts.dyn.expect", "src/compress/flate/testdata/huffman-shifts.dyn.expect-noinput", "src/compress/flate/testdata/huffman-shifts.golden", "src/compress/flate/testdata/huffman-shifts.in", "src/compress/flate/testdata/huffman-shifts.wb.expect", "src/compress/flate/testdata/huffman-shifts.wb.expect-noinput", "src/compress/flate/testdata/huffman-text-shift.dyn.expect", "src/compress/flate/testdata/huffman-text-shift.dyn.expect-noinput", "src/compress/flate/testdata/huffman-text-shift.golden", "src/compress/flate/testdata/huffman-text-shift.in", "src/compress/flate/testdata/huffman-text-shift.wb.expect", "src/compress/flate/testdata/huffman-text-shift.wb.expect-noinput", "src/compress/flate/testdata/huffman-text.dyn.expect", "src/compress/flate/testdata/huffman-text.dyn.expect-noinput", "src/compress/flate/testdata/huffman-text.golden", "src/compress/flate/testdata/huffman-text.in", "src/compress/flate/testdata/huffman-text.wb.expect", "src/compress/flate/testdata/huffman-text.wb.expect-noinput", "src/compress/flate/testdata/huffman-zero.dyn.expect", "src/compress/flate/testdata/huffman-zero.dyn.expect-noinput", "src/compress/flate/testdata/huffman-zero.golden", "src/compress/flate/testdata/huffman-zero.in", "src/compress/flate/testdata/huffman-zero.wb.expect", "src/compress/flate/testdata/huffman-zero.wb.expect-noinput", "src/compress/flate/testdata/null-long-match.dyn.expect-noinput", "src/compress/flate/testdata/null-long-match.wb.expect-noinput", "src/compress/flate/token.go", "src/compress/flate/writer_test.go", "src/compress/gzip/example_test.go", "src/compress/gzip/gunzip.go", "src/compress/gzip/gunzip_test.go", "src/compress/gzip/gzip.go", "src/compress/gzip/gzip_test.go", "src/compress/gzip/issue14937_test.go", "src/compress/gzip/testdata/issue6550.gz.base64", "src/compress/lzw/reader.go", "src/compress/lzw/reader_test.go", "src/compress/lzw/writer.go", "src/compress/lzw/writer_test.go", "src/compress/testdata/e.txt", "src/compress/testdata/gettysburg.txt", "src/compress/testdata/pi.txt", "src/compress/zlib/example_test.go", "src/compress/zlib/reader.go", "src/compress/zlib/reader_test.go", "src/compress/zlib/writer.go", "src/compress/zlib/writer_test.go"} 107 if !reflect.DeepEqual(want, got) { 108 t.Errorf("got unexpected result: %#v", got) 109 } 110 } 111 112 func Test_metaCacheEntries_filterPrefixes(t *testing.T) { 113 data := loadMetacacheSampleEntries(t) 114 data.filterPrefixesOnly() 115 got := data.entries().names() 116 want := []string{"src/compress/bzip2/", "src/compress/bzip2/testdata/", "src/compress/flate/", "src/compress/flate/testdata/", "src/compress/gzip/", "src/compress/gzip/testdata/", "src/compress/lzw/", "src/compress/testdata/", "src/compress/zlib/"} 117 if !reflect.DeepEqual(want, got) { 118 t.Errorf("got unexpected result: %#v", got) 119 } 120 } 121 122 func Test_metaCacheEntries_filterRecursive(t *testing.T) { 123 data := loadMetacacheSampleEntries(t) 124 data.filterRecursiveEntries("src/compress/bzip2/", slashSeparator) 125 got := data.entries().names() 126 want := []string{"src/compress/bzip2/", "src/compress/bzip2/bit_reader.go", "src/compress/bzip2/bzip2.go", "src/compress/bzip2/bzip2_test.go", "src/compress/bzip2/huffman.go", "src/compress/bzip2/move_to_front.go"} 127 if !reflect.DeepEqual(want, got) { 128 t.Errorf("got unexpected result: %#v", got) 129 } 130 } 131 132 func Test_metaCacheEntries_filterRecursiveRoot(t *testing.T) { 133 data := loadMetacacheSampleEntries(t) 134 data.filterRecursiveEntries("", slashSeparator) 135 got := data.entries().names() 136 want := []string{} 137 if !reflect.DeepEqual(want, got) { 138 t.Errorf("got unexpected result: %#v", got) 139 } 140 } 141 142 func Test_metaCacheEntries_filterRecursiveRootSep(t *testing.T) { 143 data := loadMetacacheSampleEntries(t) 144 // This will remove anything with "bzip2/" in the path since it is separator 145 data.filterRecursiveEntries("", "bzip2/") 146 got := data.entries().names() 147 want := []string{"src/compress/flate/", "src/compress/flate/deflate.go", "src/compress/flate/deflate_test.go", "src/compress/flate/deflatefast.go", "src/compress/flate/dict_decoder.go", "src/compress/flate/dict_decoder_test.go", "src/compress/flate/example_test.go", "src/compress/flate/flate_test.go", "src/compress/flate/huffman_bit_writer.go", "src/compress/flate/huffman_bit_writer_test.go", "src/compress/flate/huffman_code.go", "src/compress/flate/inflate.go", "src/compress/flate/inflate_test.go", "src/compress/flate/reader_test.go", "src/compress/flate/testdata/", "src/compress/flate/testdata/huffman-null-max.dyn.expect", "src/compress/flate/testdata/huffman-null-max.dyn.expect-noinput", "src/compress/flate/testdata/huffman-null-max.golden", "src/compress/flate/testdata/huffman-null-max.in", "src/compress/flate/testdata/huffman-null-max.wb.expect", "src/compress/flate/testdata/huffman-null-max.wb.expect-noinput", "src/compress/flate/testdata/huffman-pi.dyn.expect", "src/compress/flate/testdata/huffman-pi.dyn.expect-noinput", "src/compress/flate/testdata/huffman-pi.golden", "src/compress/flate/testdata/huffman-pi.in", "src/compress/flate/testdata/huffman-pi.wb.expect", "src/compress/flate/testdata/huffman-pi.wb.expect-noinput", "src/compress/flate/testdata/huffman-rand-1k.dyn.expect", "src/compress/flate/testdata/huffman-rand-1k.dyn.expect-noinput", "src/compress/flate/testdata/huffman-rand-1k.golden", "src/compress/flate/testdata/huffman-rand-1k.in", "src/compress/flate/testdata/huffman-rand-1k.wb.expect", "src/compress/flate/testdata/huffman-rand-1k.wb.expect-noinput", "src/compress/flate/testdata/huffman-rand-limit.dyn.expect", "src/compress/flate/testdata/huffman-rand-limit.dyn.expect-noinput", "src/compress/flate/testdata/huffman-rand-limit.golden", "src/compress/flate/testdata/huffman-rand-limit.in", "src/compress/flate/testdata/huffman-rand-limit.wb.expect", "src/compress/flate/testdata/huffman-rand-limit.wb.expect-noinput", "src/compress/flate/testdata/huffman-rand-max.golden", "src/compress/flate/testdata/huffman-rand-max.in", "src/compress/flate/testdata/huffman-shifts.dyn.expect", "src/compress/flate/testdata/huffman-shifts.dyn.expect-noinput", "src/compress/flate/testdata/huffman-shifts.golden", "src/compress/flate/testdata/huffman-shifts.in", "src/compress/flate/testdata/huffman-shifts.wb.expect", "src/compress/flate/testdata/huffman-shifts.wb.expect-noinput", "src/compress/flate/testdata/huffman-text-shift.dyn.expect", "src/compress/flate/testdata/huffman-text-shift.dyn.expect-noinput", "src/compress/flate/testdata/huffman-text-shift.golden", "src/compress/flate/testdata/huffman-text-shift.in", "src/compress/flate/testdata/huffman-text-shift.wb.expect", "src/compress/flate/testdata/huffman-text-shift.wb.expect-noinput", "src/compress/flate/testdata/huffman-text.dyn.expect", "src/compress/flate/testdata/huffman-text.dyn.expect-noinput", "src/compress/flate/testdata/huffman-text.golden", "src/compress/flate/testdata/huffman-text.in", "src/compress/flate/testdata/huffman-text.wb.expect", "src/compress/flate/testdata/huffman-text.wb.expect-noinput", "src/compress/flate/testdata/huffman-zero.dyn.expect", "src/compress/flate/testdata/huffman-zero.dyn.expect-noinput", "src/compress/flate/testdata/huffman-zero.golden", "src/compress/flate/testdata/huffman-zero.in", "src/compress/flate/testdata/huffman-zero.wb.expect", "src/compress/flate/testdata/huffman-zero.wb.expect-noinput", "src/compress/flate/testdata/null-long-match.dyn.expect-noinput", "src/compress/flate/testdata/null-long-match.wb.expect-noinput", "src/compress/flate/token.go", "src/compress/flate/writer_test.go", "src/compress/gzip/", "src/compress/gzip/example_test.go", "src/compress/gzip/gunzip.go", "src/compress/gzip/gunzip_test.go", "src/compress/gzip/gzip.go", "src/compress/gzip/gzip_test.go", "src/compress/gzip/issue14937_test.go", "src/compress/gzip/testdata/", "src/compress/gzip/testdata/issue6550.gz.base64", "src/compress/lzw/", "src/compress/lzw/reader.go", "src/compress/lzw/reader_test.go", "src/compress/lzw/writer.go", "src/compress/lzw/writer_test.go", "src/compress/testdata/", "src/compress/testdata/e.txt", "src/compress/testdata/gettysburg.txt", "src/compress/testdata/pi.txt", "src/compress/zlib/", "src/compress/zlib/example_test.go", "src/compress/zlib/reader.go", "src/compress/zlib/reader_test.go", "src/compress/zlib/writer.go", "src/compress/zlib/writer_test.go"} 148 if !reflect.DeepEqual(want, got) { 149 t.Errorf("got unexpected result: %#v", got) 150 } 151 } 152 153 func Test_metaCacheEntries_filterPrefix(t *testing.T) { 154 data := loadMetacacheSampleEntries(t) 155 data.filterPrefix("src/compress/bzip2/") 156 got := data.entries().names() 157 want := []string{"src/compress/bzip2/", "src/compress/bzip2/bit_reader.go", "src/compress/bzip2/bzip2.go", "src/compress/bzip2/bzip2_test.go", "src/compress/bzip2/huffman.go", "src/compress/bzip2/move_to_front.go", "src/compress/bzip2/testdata/", "src/compress/bzip2/testdata/Isaac.Newton-Opticks.txt.bz2", "src/compress/bzip2/testdata/e.txt.bz2", "src/compress/bzip2/testdata/fail-issue5747.bz2", "src/compress/bzip2/testdata/pass-random1.bin", "src/compress/bzip2/testdata/pass-random1.bz2", "src/compress/bzip2/testdata/pass-random2.bin", "src/compress/bzip2/testdata/pass-random2.bz2", "src/compress/bzip2/testdata/pass-sawtooth.bz2", "src/compress/bzip2/testdata/random.data.bz2"} 158 if !reflect.DeepEqual(want, got) { 159 t.Errorf("got unexpected result: %#v", got) 160 } 161 } 162 163 func Test_metaCacheEntry_isInDir(t *testing.T) { 164 tests := []struct { 165 testName string 166 entry string 167 dir string 168 sep string 169 want bool 170 }{ 171 { 172 testName: "basic-file", 173 entry: "src/file", 174 dir: "src/", 175 sep: slashSeparator, 176 want: true, 177 }, 178 { 179 testName: "basic-dir", 180 entry: "src/dir/", 181 dir: "src/", 182 sep: slashSeparator, 183 want: true, 184 }, 185 { 186 testName: "deeper-file", 187 entry: "src/dir/somewhere.ext", 188 dir: "src/", 189 sep: slashSeparator, 190 want: false, 191 }, 192 { 193 testName: "deeper-dir", 194 entry: "src/dir/somewhere/", 195 dir: "src/", 196 sep: slashSeparator, 197 want: false, 198 }, 199 { 200 testName: "root-dir", 201 entry: "doc/", 202 dir: "", 203 sep: slashSeparator, 204 want: true, 205 }, 206 { 207 testName: "root-file", 208 entry: "word.doc", 209 dir: "", 210 sep: slashSeparator, 211 want: true, 212 }, 213 } 214 for _, tt := range tests { 215 t.Run(tt.testName, func(t *testing.T) { 216 e := metaCacheEntry{ 217 name: tt.entry, 218 } 219 if got := e.isInDir(tt.dir, tt.sep); got != tt.want { 220 t.Errorf("isInDir() = %v, want %v", got, tt.want) 221 } 222 }) 223 } 224 } 225 226 func Test_metaCacheEntries_resolve(t *testing.T) { 227 baseTime, err := time.Parse("2006/01/02", "2015/02/25") 228 if err != nil { 229 t.Fatal(err) 230 } 231 inputs := []xlMetaV2{ 232 0: { 233 versions: []xlMetaV2ShallowVersion{ 234 {header: xlMetaV2VersionHeader{ 235 VersionID: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 236 ModTime: baseTime.Add(30 * time.Minute).UnixNano(), 237 Signature: [4]byte{1, 1, 1, 1}, 238 Type: ObjectType, 239 Flags: 0, 240 }}, 241 }, 242 }, 243 // Mismatches Modtime+Signature and older... 244 1: { 245 versions: []xlMetaV2ShallowVersion{ 246 {header: xlMetaV2VersionHeader{ 247 VersionID: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 248 ModTime: baseTime.Add(15 * time.Minute).UnixNano(), 249 Signature: [4]byte{2, 1, 1, 1}, 250 Type: ObjectType, 251 Flags: 0, 252 }}, 253 }, 254 }, 255 // Has another version prior to the one we want. 256 2: { 257 versions: []xlMetaV2ShallowVersion{ 258 {header: xlMetaV2VersionHeader{ 259 VersionID: [16]byte{2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 260 ModTime: baseTime.Add(60 * time.Minute).UnixNano(), 261 Signature: [4]byte{2, 1, 1, 1}, 262 Type: ObjectType, 263 Flags: 0, 264 }}, 265 {header: xlMetaV2VersionHeader{ 266 VersionID: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 267 ModTime: baseTime.Add(30 * time.Minute).UnixNano(), 268 Signature: [4]byte{1, 1, 1, 1}, 269 Type: ObjectType, 270 Flags: 0, 271 }}, 272 }, 273 }, 274 // Has a completely different version id 275 3: { 276 versions: []xlMetaV2ShallowVersion{ 277 {header: xlMetaV2VersionHeader{ 278 VersionID: [16]byte{3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 279 ModTime: baseTime.Add(60 * time.Minute).UnixNano(), 280 Signature: [4]byte{1, 1, 1, 1}, 281 Type: ObjectType, 282 Flags: 0, 283 }}, 284 }, 285 }, 286 4: { 287 versions: []xlMetaV2ShallowVersion{}, 288 }, 289 // Has a zero version id 290 5: { 291 versions: []xlMetaV2ShallowVersion{ 292 {header: xlMetaV2VersionHeader{ 293 VersionID: [16]byte{}, 294 ModTime: baseTime.Add(60 * time.Minute).UnixNano(), 295 Signature: [4]byte{5, 1, 1, 1}, 296 Type: ObjectType, 297 Flags: 0, 298 }}, 299 }, 300 }, 301 // Zero version, modtime newer.. 302 6: { 303 versions: []xlMetaV2ShallowVersion{ 304 {header: xlMetaV2VersionHeader{ 305 VersionID: [16]byte{}, 306 ModTime: baseTime.Add(90 * time.Minute).UnixNano(), 307 Signature: [4]byte{6, 1, 1, 1}, 308 Type: ObjectType, 309 Flags: 0, 310 }}, 311 }, 312 }, 313 7: { 314 versions: []xlMetaV2ShallowVersion{ 315 {header: xlMetaV2VersionHeader{ 316 VersionID: [16]byte{}, 317 ModTime: baseTime.Add(90 * time.Minute).UnixNano(), 318 Signature: [4]byte{6, 1, 1, 1}, 319 Type: ObjectType, 320 Flags: 0, 321 }}, 322 {header: xlMetaV2VersionHeader{ 323 VersionID: [16]byte{2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 324 ModTime: baseTime.Add(60 * time.Minute).UnixNano(), 325 Signature: [4]byte{2, 1, 1, 1}, 326 Type: ObjectType, 327 Flags: 0, 328 }}, 329 {header: xlMetaV2VersionHeader{ 330 VersionID: [16]byte{3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 331 ModTime: baseTime.Add(60 * time.Minute).UnixNano(), 332 Signature: [4]byte{1, 1, 1, 1}, 333 Type: ObjectType, 334 Flags: 0, 335 }}, 336 337 {header: xlMetaV2VersionHeader{ 338 VersionID: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 339 ModTime: baseTime.Add(30 * time.Minute).UnixNano(), 340 Signature: [4]byte{1, 1, 1, 1}, 341 Type: ObjectType, 342 Flags: 0, 343 }}, 344 }, 345 }, 346 // Delete marker. 347 8: { 348 versions: []xlMetaV2ShallowVersion{ 349 {header: xlMetaV2VersionHeader{ 350 VersionID: [16]byte{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}, 351 ModTime: baseTime.Add(90 * time.Minute).UnixNano(), 352 Signature: [4]byte{6, 1, 1, 1}, 353 Type: DeleteType, 354 Flags: 0, 355 }}, 356 }, 357 }, 358 // Delete marker and version from 1 359 9: { 360 versions: []xlMetaV2ShallowVersion{ 361 {header: xlMetaV2VersionHeader{ 362 VersionID: [16]byte{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}, 363 ModTime: baseTime.Add(90 * time.Minute).UnixNano(), 364 Signature: [4]byte{6, 1, 1, 1}, 365 Type: DeleteType, 366 Flags: 0, 367 }}, 368 {header: xlMetaV2VersionHeader{ 369 VersionID: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 370 ModTime: baseTime.Add(15 * time.Minute).UnixNano(), 371 Signature: [4]byte{2, 1, 1, 1}, 372 Type: ObjectType, 373 Flags: 0, 374 }}, 375 }, 376 }, 377 } 378 inputSerialized := make([]metaCacheEntry, len(inputs)) 379 for i, xl := range inputs { 380 xl.sortByModTime() 381 var err error 382 entry := metaCacheEntry{ 383 name: "testobject", 384 } 385 entry.metadata, err = xl.AppendTo(nil) 386 if err != nil { 387 t.Fatal(err) 388 } 389 inputSerialized[i] = entry 390 } 391 392 tests := []struct { 393 name string 394 m metaCacheEntries 395 r metadataResolutionParams 396 wantSelected *metaCacheEntry 397 wantOk bool 398 }{ 399 { 400 name: "consistent", 401 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], inputSerialized[0]}, 402 r: metadataResolutionParams{dirQuorum: 4, objQuorum: 4, strict: false}, 403 wantSelected: &inputSerialized[0], 404 wantOk: true, 405 }, 406 { 407 name: "consistent-strict", 408 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], inputSerialized[0]}, 409 r: metadataResolutionParams{dirQuorum: 4, objQuorum: 4, strict: false}, 410 wantSelected: &inputSerialized[0], 411 wantOk: true, 412 }, 413 { 414 name: "one zero, below quorum", 415 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], metaCacheEntry{}}, 416 r: metadataResolutionParams{dirQuorum: 4, objQuorum: 4, strict: false}, 417 wantSelected: nil, 418 wantOk: false, 419 }, 420 { 421 name: "one zero, below quorum, strict", 422 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], metaCacheEntry{}}, 423 r: metadataResolutionParams{dirQuorum: 4, objQuorum: 4, strict: true}, 424 wantSelected: nil, 425 wantOk: false, 426 }, 427 { 428 name: "one zero, at quorum", 429 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], metaCacheEntry{}}, 430 r: metadataResolutionParams{dirQuorum: 3, objQuorum: 3, strict: false}, 431 wantSelected: &inputSerialized[0], 432 wantOk: true, 433 }, 434 { 435 name: "one zero, at quorum, strict", 436 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], metaCacheEntry{}}, 437 r: metadataResolutionParams{dirQuorum: 3, objQuorum: 3, strict: true}, 438 wantSelected: &inputSerialized[0], 439 wantOk: true, 440 }, 441 { 442 name: "modtime, signature mismatch", 443 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], inputSerialized[1]}, 444 r: metadataResolutionParams{dirQuorum: 4, objQuorum: 4, strict: false}, 445 wantSelected: &inputSerialized[0], 446 wantOk: true, 447 }, 448 { 449 name: "modtime,signature mismatch, strict", 450 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], inputSerialized[1]}, 451 r: metadataResolutionParams{dirQuorum: 4, objQuorum: 4, strict: true}, 452 wantSelected: nil, 453 wantOk: false, 454 }, 455 { 456 name: "modtime, signature mismatch, at quorum", 457 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], inputSerialized[1]}, 458 r: metadataResolutionParams{dirQuorum: 3, objQuorum: 3, strict: false}, 459 wantSelected: &inputSerialized[0], 460 wantOk: true, 461 }, 462 { 463 name: "modtime,signature mismatch, at quorum, strict", 464 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], inputSerialized[1]}, 465 r: metadataResolutionParams{dirQuorum: 3, objQuorum: 3, strict: true}, 466 wantSelected: &inputSerialized[0], 467 wantOk: true, 468 }, 469 { 470 name: "additional version", 471 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], inputSerialized[2]}, 472 r: metadataResolutionParams{dirQuorum: 4, objQuorum: 4, strict: false}, 473 wantSelected: &inputSerialized[0], 474 wantOk: true, 475 }, 476 { 477 // Since we have the same version in all inputs, that is strictly ok. 478 name: "additional version, strict", 479 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], inputSerialized[2]}, 480 r: metadataResolutionParams{dirQuorum: 4, objQuorum: 4, strict: true}, 481 wantSelected: &inputSerialized[0], 482 wantOk: true, 483 }, 484 { 485 // Since we have the same version in all inputs, that is strictly ok. 486 name: "additional version, quorum one", 487 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], inputSerialized[2]}, 488 r: metadataResolutionParams{dirQuorum: 1, objQuorum: 1, strict: true}, 489 // We get the both versions, since we only request quorum 1 490 wantSelected: &inputSerialized[2], 491 wantOk: true, 492 }, 493 { 494 name: "additional version, quorum two", 495 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[0], inputSerialized[2]}, 496 r: metadataResolutionParams{dirQuorum: 2, objQuorum: 2, strict: true}, 497 wantSelected: &inputSerialized[0], 498 wantOk: true, 499 }, 500 { 501 name: "2 additional versions, quorum two", 502 m: metaCacheEntries{inputSerialized[0], inputSerialized[0], inputSerialized[2], inputSerialized[2]}, 503 r: metadataResolutionParams{dirQuorum: 2, objQuorum: 2, strict: true}, 504 wantSelected: &inputSerialized[2], 505 wantOk: true, 506 }, 507 { 508 // inputSerialized[1] have older versions of the second in inputSerialized[2] 509 name: "modtimemismatch", 510 m: metaCacheEntries{inputSerialized[1], inputSerialized[1], inputSerialized[2], inputSerialized[2]}, 511 r: metadataResolutionParams{dirQuorum: 2, objQuorum: 2, strict: false}, 512 wantSelected: &inputSerialized[2], 513 wantOk: true, 514 }, 515 { 516 // inputSerialized[1] have older versions of the second in inputSerialized[2] 517 name: "modtimemismatch,strict", 518 m: metaCacheEntries{inputSerialized[1], inputSerialized[1], inputSerialized[2], inputSerialized[2]}, 519 r: metadataResolutionParams{dirQuorum: 2, objQuorum: 2, strict: true}, 520 wantSelected: &inputSerialized[2], 521 wantOk: true, 522 }, 523 { 524 // inputSerialized[1] have older versions of the second in inputSerialized[2], but 525 // since it is not strict, we should get it that one (with latest modtime) 526 name: "modtimemismatch,not strict", 527 m: metaCacheEntries{inputSerialized[1], inputSerialized[1], inputSerialized[2], inputSerialized[2]}, 528 r: metadataResolutionParams{dirQuorum: 4, objQuorum: 4, strict: false}, 529 wantSelected: &inputSerialized[0], 530 wantOk: true, 531 }, 532 { 533 name: "one-q1", 534 m: metaCacheEntries{inputSerialized[0], inputSerialized[4], inputSerialized[4], inputSerialized[4]}, 535 r: metadataResolutionParams{dirQuorum: 1, objQuorum: 1, strict: false}, 536 wantSelected: &inputSerialized[0], 537 wantOk: true, 538 }, 539 { 540 name: "one-q1-strict", 541 m: metaCacheEntries{inputSerialized[0], inputSerialized[4], inputSerialized[4], inputSerialized[4]}, 542 r: metadataResolutionParams{dirQuorum: 1, objQuorum: 1, strict: true}, 543 wantSelected: &inputSerialized[0], 544 wantOk: true, 545 }, 546 { 547 name: "one-q2", 548 m: metaCacheEntries{inputSerialized[0], inputSerialized[4], inputSerialized[4], inputSerialized[4]}, 549 r: metadataResolutionParams{dirQuorum: 2, objQuorum: 2, strict: false}, 550 wantSelected: nil, 551 wantOk: false, 552 }, 553 { 554 name: "one-q2-strict", 555 m: metaCacheEntries{inputSerialized[0], inputSerialized[4], inputSerialized[4], inputSerialized[4]}, 556 r: metadataResolutionParams{dirQuorum: 2, objQuorum: 2, strict: true}, 557 wantSelected: nil, 558 wantOk: false, 559 }, 560 { 561 name: "two-diff-q2", 562 m: metaCacheEntries{inputSerialized[0], inputSerialized[3], inputSerialized[4], inputSerialized[4]}, 563 r: metadataResolutionParams{dirQuorum: 2, objQuorum: 2, strict: false}, 564 wantSelected: nil, 565 wantOk: false, 566 }, 567 { 568 name: "zeroid", 569 m: metaCacheEntries{inputSerialized[5], inputSerialized[5], inputSerialized[6], inputSerialized[6]}, 570 r: metadataResolutionParams{dirQuorum: 2, objQuorum: 2, strict: false}, 571 wantSelected: &inputSerialized[6], 572 wantOk: true, 573 }, 574 { 575 // When ID is zero, do not allow non-strict matches to reach quorum. 576 name: "zeroid-belowq", 577 m: metaCacheEntries{inputSerialized[5], inputSerialized[5], inputSerialized[6], inputSerialized[6]}, 578 r: metadataResolutionParams{dirQuorum: 3, objQuorum: 3, strict: false}, 579 wantSelected: nil, 580 wantOk: false, 581 }, 582 { 583 name: "merge4", 584 m: metaCacheEntries{inputSerialized[2], inputSerialized[3], inputSerialized[5], inputSerialized[6]}, 585 r: metadataResolutionParams{dirQuorum: 1, objQuorum: 1, strict: false}, 586 wantSelected: &inputSerialized[7], 587 wantOk: true, 588 }, 589 { 590 name: "deletemarker", 591 m: metaCacheEntries{inputSerialized[8], inputSerialized[4], inputSerialized[4], inputSerialized[4]}, 592 r: metadataResolutionParams{dirQuorum: 1, objQuorum: 1, strict: false}, 593 wantSelected: &inputSerialized[8], 594 wantOk: true, 595 }, 596 { 597 name: "deletemarker-nonq", 598 m: metaCacheEntries{inputSerialized[8], inputSerialized[8], inputSerialized[4], inputSerialized[4]}, 599 r: metadataResolutionParams{dirQuorum: 3, objQuorum: 3, strict: false}, 600 wantSelected: nil, 601 wantOk: false, 602 }, 603 { 604 name: "deletemarker-nonq", 605 m: metaCacheEntries{inputSerialized[8], inputSerialized[8], inputSerialized[8], inputSerialized[1]}, 606 r: metadataResolutionParams{dirQuorum: 3, objQuorum: 3, strict: false}, 607 wantSelected: &inputSerialized[8], 608 wantOk: true, 609 }, 610 { 611 name: "deletemarker-mixed", 612 m: metaCacheEntries{inputSerialized[8], inputSerialized[8], inputSerialized[1], inputSerialized[1]}, 613 r: metadataResolutionParams{dirQuorum: 2, objQuorum: 2, strict: false}, 614 wantSelected: &inputSerialized[9], 615 wantOk: true, 616 }, 617 { 618 name: "deletemarker-q3", 619 m: metaCacheEntries{inputSerialized[8], inputSerialized[9], inputSerialized[9], inputSerialized[1]}, 620 r: metadataResolutionParams{dirQuorum: 3, objQuorum: 3, strict: false}, 621 wantSelected: &inputSerialized[9], 622 wantOk: true, 623 }, 624 { 625 name: "deletemarker-q3-strict", 626 m: metaCacheEntries{inputSerialized[8], inputSerialized[9], inputSerialized[9], inputSerialized[1]}, 627 r: metadataResolutionParams{dirQuorum: 3, objQuorum: 3, strict: true}, 628 wantSelected: &inputSerialized[9], 629 wantOk: true, 630 }, 631 } 632 633 for testID, tt := range tests { 634 rng := rand.New(rand.NewSource(0)) 635 // Run for a number of times, shuffling the input to ensure that output is consistent. 636 for i := 0; i < 10; i++ { 637 t.Run(fmt.Sprintf("test-%d-%s-run-%d", testID, tt.name, i), func(t *testing.T) { 638 if i > 0 { 639 rng.Shuffle(len(tt.m), func(i, j int) { 640 tt.m[i], tt.m[j] = tt.m[j], tt.m[i] 641 }) 642 } 643 gotSelected, gotOk := tt.m.resolve(&tt.r) 644 if gotOk != tt.wantOk { 645 t.Errorf("resolve() gotOk = %v, want %v", gotOk, tt.wantOk) 646 } 647 if gotSelected != nil { 648 gotSelected.cached = nil 649 gotSelected.reusable = false 650 } 651 if !reflect.DeepEqual(gotSelected, tt.wantSelected) { 652 wantM, _ := tt.wantSelected.xlmeta() 653 gotM, _ := gotSelected.xlmeta() 654 t.Errorf("resolve() gotSelected = \n%#v, want \n%#v", *gotM, *wantM) 655 } 656 }) 657 } 658 } 659 }