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  }