storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/xl-storage-format_test.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2020 MinIO, Inc. 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 cmd 18 19 import ( 20 "bytes" 21 "encoding/hex" 22 "encoding/json" 23 "testing" 24 25 "github.com/dustin/go-humanize" 26 jsoniter "github.com/json-iterator/go" 27 ) 28 29 func TestIsXLMetaFormatValid(t *testing.T) { 30 tests := []struct { 31 name int 32 version string 33 format string 34 want bool 35 }{ 36 {1, "123", "fs", false}, 37 {2, "123", xlMetaFormat, false}, 38 {3, xlMetaVersion100, "test", false}, 39 {4, xlMetaVersion101, "hello", false}, 40 {5, xlMetaVersion100, xlMetaFormat, true}, 41 {6, xlMetaVersion101, xlMetaFormat, true}, 42 } 43 for _, tt := range tests { 44 if got := isXLMetaFormatValid(tt.version, tt.format); got != tt.want { 45 t.Errorf("Test %d: Expected %v but received %v", tt.name, got, tt.want) 46 } 47 } 48 } 49 50 func TestIsXLMetaErasureInfoValid(t *testing.T) { 51 tests := []struct { 52 name int 53 data int 54 parity int 55 want bool 56 }{ 57 {1, 5, 6, false}, 58 {2, 5, 5, true}, 59 {3, 0, 5, false}, 60 {4, 5, 0, false}, 61 {5, 5, 0, false}, 62 {6, 5, 4, true}, 63 } 64 for _, tt := range tests { 65 if got := isXLMetaErasureInfoValid(tt.data, tt.parity); got != tt.want { 66 t.Errorf("Test %d: Expected %v but received %v", tt.name, got, tt.want) 67 } 68 } 69 } 70 71 // newTestXLMetaV1 - initializes new xlMetaV1Object, adds version, allocates a fresh erasure info and metadata. 72 func newTestXLMetaV1() xlMetaV1Object { 73 xlMeta := xlMetaV1Object{} 74 xlMeta.Version = xlMetaVersion101 75 xlMeta.Format = xlMetaFormat 76 xlMeta.Minio.Release = "test" 77 xlMeta.Erasure = ErasureInfo{ 78 Algorithm: "klauspost/reedsolomon/vandermonde", 79 DataBlocks: 5, 80 ParityBlocks: 5, 81 BlockSize: 10485760, 82 Index: 10, 83 Distribution: []int{9, 10, 1, 2, 3, 4, 5, 6, 7, 8}, 84 } 85 xlMeta.Stat = StatInfo{ 86 Size: int64(20), 87 ModTime: UTCNow(), 88 } 89 // Set meta data. 90 xlMeta.Meta = make(map[string]string) 91 xlMeta.Meta["testKey1"] = "val1" 92 xlMeta.Meta["testKey2"] = "val2" 93 return xlMeta 94 } 95 96 func (m *xlMetaV1Object) AddTestObjectCheckSum(partNumber int, algorithm BitrotAlgorithm, hash string) { 97 checksum, err := hex.DecodeString(hash) 98 if err != nil { 99 panic(err) 100 } 101 m.Erasure.Checksums[partNumber-1] = ChecksumInfo{partNumber, algorithm, checksum} 102 } 103 104 // AddTestObjectPart - add a new object part in order. 105 func (m *xlMetaV1Object) AddTestObjectPart(partNumber int, partSize int64) { 106 partInfo := ObjectPartInfo{ 107 Number: partNumber, 108 Size: partSize, 109 } 110 111 // Proceed to include new part info. 112 m.Parts[partNumber-1] = partInfo 113 } 114 115 // Constructs xlMetaV1Object{} for given number of parts and converts it into bytes. 116 func getXLMetaBytes(totalParts int) []byte { 117 xlSampleMeta := getSampleXLMeta(totalParts) 118 xlMetaBytes, err := json.Marshal(xlSampleMeta) 119 if err != nil { 120 panic(err) 121 } 122 return xlMetaBytes 123 } 124 125 // Returns sample xlMetaV1Object{} for number of parts. 126 func getSampleXLMeta(totalParts int) xlMetaV1Object { 127 xlMeta := newTestXLMetaV1() 128 // Number of checksum info == total parts. 129 xlMeta.Erasure.Checksums = make([]ChecksumInfo, totalParts) 130 // total number of parts. 131 xlMeta.Parts = make([]ObjectPartInfo, totalParts) 132 for i := 0; i < totalParts; i++ { 133 // hard coding hash and algo value for the checksum, Since we are benchmarking the parsing of xl.meta the magnitude doesn't affect the test, 134 // The magnitude doesn't make a difference, only the size does. 135 xlMeta.AddTestObjectCheckSum(i+1, BLAKE2b512, "a23f5eff248c4372badd9f3b2455a285cd4ca86c3d9a570b091d3fc5cd7ca6d9484bbea3f8c5d8d4f84daae96874419eda578fd736455334afbac2c924b3915a") 136 xlMeta.AddTestObjectPart(i+1, 67108864) 137 } 138 return xlMeta 139 } 140 141 // Compare the unmarshaled XLMetaV1 with the one obtained from jsoniter parsing. 142 func compareXLMetaV1(t *testing.T, unMarshalXLMeta, jsoniterXLMeta xlMetaV1Object) { 143 // Start comparing the fields of xlMetaV1Object obtained from jsoniter parsing with one parsed using json unmarshaling. 144 if unMarshalXLMeta.Version != jsoniterXLMeta.Version { 145 t.Errorf("Expected the Version to be \"%s\", but got \"%s\".", unMarshalXLMeta.Version, jsoniterXLMeta.Version) 146 } 147 if unMarshalXLMeta.Format != jsoniterXLMeta.Format { 148 t.Errorf("Expected the format to be \"%s\", but got \"%s\".", unMarshalXLMeta.Format, jsoniterXLMeta.Format) 149 } 150 if unMarshalXLMeta.Stat.Size != jsoniterXLMeta.Stat.Size { 151 t.Errorf("Expected the stat size to be %v, but got %v.", unMarshalXLMeta.Stat.Size, jsoniterXLMeta.Stat.Size) 152 } 153 if !unMarshalXLMeta.Stat.ModTime.Equal(jsoniterXLMeta.Stat.ModTime) { 154 t.Errorf("Expected the modTime to be \"%v\", but got \"%v\".", unMarshalXLMeta.Stat.ModTime, jsoniterXLMeta.Stat.ModTime) 155 } 156 if unMarshalXLMeta.Erasure.Algorithm != jsoniterXLMeta.Erasure.Algorithm { 157 t.Errorf("Expected the erasure algorithm to be \"%v\", but got \"%v\".", unMarshalXLMeta.Erasure.Algorithm, jsoniterXLMeta.Erasure.Algorithm) 158 } 159 if unMarshalXLMeta.Erasure.DataBlocks != jsoniterXLMeta.Erasure.DataBlocks { 160 t.Errorf("Expected the erasure data blocks to be %v, but got %v.", unMarshalXLMeta.Erasure.DataBlocks, jsoniterXLMeta.Erasure.DataBlocks) 161 } 162 if unMarshalXLMeta.Erasure.ParityBlocks != jsoniterXLMeta.Erasure.ParityBlocks { 163 t.Errorf("Expected the erasure parity blocks to be %v, but got %v.", unMarshalXLMeta.Erasure.ParityBlocks, jsoniterXLMeta.Erasure.ParityBlocks) 164 } 165 if unMarshalXLMeta.Erasure.BlockSize != jsoniterXLMeta.Erasure.BlockSize { 166 t.Errorf("Expected the erasure block size to be %v, but got %v.", unMarshalXLMeta.Erasure.BlockSize, jsoniterXLMeta.Erasure.BlockSize) 167 } 168 if unMarshalXLMeta.Erasure.Index != jsoniterXLMeta.Erasure.Index { 169 t.Errorf("Expected the erasure index to be %v, but got %v.", unMarshalXLMeta.Erasure.Index, jsoniterXLMeta.Erasure.Index) 170 } 171 if len(unMarshalXLMeta.Erasure.Distribution) != len(jsoniterXLMeta.Erasure.Distribution) { 172 t.Errorf("Expected the size of Erasure Distribution to be %d, but got %d.", len(unMarshalXLMeta.Erasure.Distribution), len(jsoniterXLMeta.Erasure.Distribution)) 173 } else { 174 for i := 0; i < len(unMarshalXLMeta.Erasure.Distribution); i++ { 175 if unMarshalXLMeta.Erasure.Distribution[i] != jsoniterXLMeta.Erasure.Distribution[i] { 176 t.Errorf("Expected the Erasure Distribution to be %d, got %d.", unMarshalXLMeta.Erasure.Distribution[i], jsoniterXLMeta.Erasure.Distribution[i]) 177 } 178 } 179 } 180 181 if len(unMarshalXLMeta.Erasure.Checksums) != len(jsoniterXLMeta.Erasure.Checksums) { 182 t.Errorf("Expected the size of Erasure Checksums to be %d, but got %d.", len(unMarshalXLMeta.Erasure.Checksums), len(jsoniterXLMeta.Erasure.Checksums)) 183 } else { 184 for i := 0; i < len(unMarshalXLMeta.Erasure.Checksums); i++ { 185 if unMarshalXLMeta.Erasure.Checksums[i].PartNumber != jsoniterXLMeta.Erasure.Checksums[i].PartNumber { 186 t.Errorf("Expected the Erasure Checksum PartNumber to be \"%d\", got \"%d\".", unMarshalXLMeta.Erasure.Checksums[i].PartNumber, jsoniterXLMeta.Erasure.Checksums[i].PartNumber) 187 } 188 if unMarshalXLMeta.Erasure.Checksums[i].Algorithm != jsoniterXLMeta.Erasure.Checksums[i].Algorithm { 189 t.Errorf("Expected the Erasure Checksum Algorithm to be \"%s\", got \"%s\".", unMarshalXLMeta.Erasure.Checksums[i].Algorithm, jsoniterXLMeta.Erasure.Checksums[i].Algorithm) 190 } 191 if !bytes.Equal(unMarshalXLMeta.Erasure.Checksums[i].Hash, jsoniterXLMeta.Erasure.Checksums[i].Hash) { 192 t.Errorf("Expected the Erasure Checksum Hash to be \"%s\", got \"%s\".", unMarshalXLMeta.Erasure.Checksums[i].Hash, jsoniterXLMeta.Erasure.Checksums[i].Hash) 193 } 194 } 195 } 196 197 if unMarshalXLMeta.Minio.Release != jsoniterXLMeta.Minio.Release { 198 t.Errorf("Expected the Release string to be \"%s\", but got \"%s\".", unMarshalXLMeta.Minio.Release, jsoniterXLMeta.Minio.Release) 199 } 200 if len(unMarshalXLMeta.Parts) != len(jsoniterXLMeta.Parts) { 201 t.Errorf("Expected info of %d parts to be present, but got %d instead.", len(unMarshalXLMeta.Parts), len(jsoniterXLMeta.Parts)) 202 } else { 203 for i := 0; i < len(unMarshalXLMeta.Parts); i++ { 204 if unMarshalXLMeta.Parts[i].Number != jsoniterXLMeta.Parts[i].Number { 205 t.Errorf("Expected the number of part %d to be \"%d\", got \"%d\".", i+1, unMarshalXLMeta.Parts[i].Number, jsoniterXLMeta.Parts[i].Number) 206 } 207 if unMarshalXLMeta.Parts[i].Size != jsoniterXLMeta.Parts[i].Size { 208 t.Errorf("Expected the size of part %d to be %v, got %v.", i+1, unMarshalXLMeta.Parts[i].Size, jsoniterXLMeta.Parts[i].Size) 209 } 210 } 211 } 212 213 for key, val := range unMarshalXLMeta.Meta { 214 jsoniterVal, exists := jsoniterXLMeta.Meta[key] 215 if !exists { 216 t.Errorf("No meta data entry for Key \"%s\" exists.", key) 217 } 218 if val != jsoniterVal { 219 t.Errorf("Expected the value for Meta data key \"%s\" to be \"%s\", but got \"%s\".", key, val, jsoniterVal) 220 } 221 222 } 223 } 224 225 // Tests the correctness of constructing XLMetaV1 using jsoniter lib. 226 // The result will be compared with the result obtained from json.unMarshal of the byte data. 227 func TestGetXLMetaV1Jsoniter1(t *testing.T) { 228 xlMetaJSON := getXLMetaBytes(1) 229 230 var unMarshalXLMeta xlMetaV1Object 231 if err := json.Unmarshal(xlMetaJSON, &unMarshalXLMeta); err != nil { 232 t.Errorf("Unmarshalling failed: %v", err) 233 } 234 235 var jsoniterXLMeta xlMetaV1Object 236 var json = jsoniter.ConfigCompatibleWithStandardLibrary 237 if err := json.Unmarshal(xlMetaJSON, &jsoniterXLMeta); err != nil { 238 t.Errorf("jsoniter parsing of XLMeta failed: %v", err) 239 } 240 compareXLMetaV1(t, unMarshalXLMeta, jsoniterXLMeta) 241 } 242 243 // Tests the correctness of constructing XLMetaV1 using jsoniter lib for XLMetaV1 of size 10 parts. 244 // The result will be compared with the result obtained from json.unMarshal of the byte data. 245 func TestGetXLMetaV1Jsoniter10(t *testing.T) { 246 247 xlMetaJSON := getXLMetaBytes(10) 248 249 var unMarshalXLMeta xlMetaV1Object 250 if err := json.Unmarshal(xlMetaJSON, &unMarshalXLMeta); err != nil { 251 t.Errorf("Unmarshalling failed: %v", err) 252 } 253 254 var jsoniterXLMeta xlMetaV1Object 255 var json = jsoniter.ConfigCompatibleWithStandardLibrary 256 if err := json.Unmarshal(xlMetaJSON, &jsoniterXLMeta); err != nil { 257 t.Errorf("jsoniter parsing of XLMeta failed: %v", err) 258 } 259 260 compareXLMetaV1(t, unMarshalXLMeta, jsoniterXLMeta) 261 } 262 263 // Test the predicted part size from the part index 264 func TestGetPartSizeFromIdx(t *testing.T) { 265 // Create test cases 266 testCases := []struct { 267 totalSize int64 268 partSize int64 269 partIndex int 270 expectedSize int64 271 }{ 272 // Total size is zero 273 {0, 10, 1, 0}, 274 // part size 2MiB, total size 4MiB 275 {4 * humanize.MiByte, 2 * humanize.MiByte, 1, 2 * humanize.MiByte}, 276 {4 * humanize.MiByte, 2 * humanize.MiByte, 2, 2 * humanize.MiByte}, 277 {4 * humanize.MiByte, 2 * humanize.MiByte, 3, 0}, 278 // part size 2MiB, total size 5MiB 279 {5 * humanize.MiByte, 2 * humanize.MiByte, 1, 2 * humanize.MiByte}, 280 {5 * humanize.MiByte, 2 * humanize.MiByte, 2, 2 * humanize.MiByte}, 281 {5 * humanize.MiByte, 2 * humanize.MiByte, 3, 1 * humanize.MiByte}, 282 {5 * humanize.MiByte, 2 * humanize.MiByte, 4, 0}, 283 } 284 285 for i, testCase := range testCases { 286 s, err := calculatePartSizeFromIdx(GlobalContext, testCase.totalSize, testCase.partSize, testCase.partIndex) 287 if err != nil { 288 t.Errorf("Test %d: Expected to pass but failed. %s", i+1, err) 289 } 290 if err == nil && s != testCase.expectedSize { 291 t.Errorf("Test %d: The calculated part size is incorrect: expected = %d, found = %d\n", i+1, testCase.expectedSize, s) 292 } 293 } 294 295 testCasesFailure := []struct { 296 totalSize int64 297 partSize int64 298 partIndex int 299 err error 300 }{ 301 // partSize is 0, returns error. 302 {10, 0, 1, errPartSizeZero}, 303 // partIndex is 0, returns error. 304 {10, 1, 0, errPartSizeIndex}, 305 // Total size is -1, returns error. 306 {-2, 10, 1, errInvalidArgument}, 307 } 308 309 for i, testCaseFailure := range testCasesFailure { 310 _, err := calculatePartSizeFromIdx(GlobalContext, testCaseFailure.totalSize, testCaseFailure.partSize, testCaseFailure.partIndex) 311 if err == nil { 312 t.Errorf("Test %d: Expected to failed but passed. %s", i+1, err) 313 } 314 if err != nil && err != testCaseFailure.err { 315 t.Errorf("Test %d: Expected err %s, but got %s", i+1, testCaseFailure.err, err) 316 } 317 } 318 }