storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/erasure-metadata-utils_test.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2015, 2016, 2017 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  	"context"
    21  	"encoding/hex"
    22  	"fmt"
    23  	"math/rand"
    24  	"reflect"
    25  	"testing"
    26  )
    27  
    28  // Tests caclculating disk count.
    29  func TestDiskCount(t *testing.T) {
    30  	testCases := []struct {
    31  		disks     []StorageAPI
    32  		diskCount int
    33  	}{
    34  		// Test case - 1
    35  		{
    36  			disks:     []StorageAPI{&xlStorage{}, &xlStorage{}, &xlStorage{}, &xlStorage{}},
    37  			diskCount: 4,
    38  		},
    39  		// Test case - 2
    40  		{
    41  			disks:     []StorageAPI{nil, &xlStorage{}, &xlStorage{}, &xlStorage{}},
    42  			diskCount: 3,
    43  		},
    44  	}
    45  	for i, testCase := range testCases {
    46  		cdiskCount := diskCount(testCase.disks)
    47  		if cdiskCount != testCase.diskCount {
    48  			t.Errorf("Test %d: Expected %d, got %d", i+1, testCase.diskCount, cdiskCount)
    49  		}
    50  	}
    51  }
    52  
    53  // Test for reduceErrs, reduceErr reduces collection
    54  // of errors into a single maximal error with in the list.
    55  func TestReduceErrs(t *testing.T) {
    56  	// List all of all test cases to validate various cases of reduce errors.
    57  	testCases := []struct {
    58  		errs        []error
    59  		ignoredErrs []error
    60  		err         error
    61  	}{
    62  		// Validate if have reduced properly.
    63  		{[]error{
    64  			errDiskNotFound,
    65  			errDiskNotFound,
    66  			errDiskFull,
    67  		}, []error{}, errErasureReadQuorum},
    68  		// Validate if have no consensus.
    69  		{[]error{
    70  			errDiskFull,
    71  			errDiskNotFound,
    72  			nil, nil,
    73  		}, []error{}, errErasureReadQuorum},
    74  		// Validate if have consensus and errors ignored.
    75  		{[]error{
    76  			errVolumeNotFound,
    77  			errVolumeNotFound,
    78  			errVolumeNotFound,
    79  			errVolumeNotFound,
    80  			errVolumeNotFound,
    81  			errDiskNotFound,
    82  			errDiskNotFound,
    83  		}, []error{errDiskNotFound}, errVolumeNotFound},
    84  		{[]error{}, []error{}, errErasureReadQuorum},
    85  		{[]error{errFileNotFound, errFileNotFound, errFileNotFound,
    86  			errFileNotFound, errFileNotFound, nil, nil, nil, nil, nil},
    87  			nil, nil},
    88  	}
    89  	// Validates list of all the testcases for returning valid errors.
    90  	for i, testCase := range testCases {
    91  		gotErr := reduceReadQuorumErrs(context.Background(), testCase.errs, testCase.ignoredErrs, 5)
    92  		if gotErr != testCase.err {
    93  			t.Errorf("Test %d : expected %s, got %s", i+1, testCase.err, gotErr)
    94  		}
    95  		gotNewErr := reduceWriteQuorumErrs(context.Background(), testCase.errs, testCase.ignoredErrs, 6)
    96  		if gotNewErr != errErasureWriteQuorum {
    97  			t.Errorf("Test %d : expected %s, got %s", i+1, errErasureWriteQuorum, gotErr)
    98  		}
    99  	}
   100  }
   101  
   102  // TestHashOrder - test order of ints in array
   103  func TestHashOrder(t *testing.T) {
   104  	testCases := []struct {
   105  		objectName  string
   106  		hashedOrder []int
   107  	}{
   108  		// cases which should pass the test.
   109  		// passing in valid object name.
   110  		{"object", []int{14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}},
   111  		{"The Shining Script <v1>.pdf", []int{16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
   112  		{"Cost Benefit Analysis (2009-2010).pptx", []int{15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}},
   113  		{"117Gn8rfHL2ACARPAhaFd0AGzic9pUbIA/5OCn5A", []int{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2}},
   114  		{"SHØRT", []int{11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
   115  		{"There are far too many object names, and far too few bucket names!", []int{15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}},
   116  		{"a/b/c/", []int{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2}},
   117  		{"/a/b/c", []int{6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5}},
   118  		{string([]byte{0xff, 0xfe, 0xfd}), []int{15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}},
   119  	}
   120  
   121  	// Tests hashing order to be consistent.
   122  	for i, testCase := range testCases {
   123  		hashedOrder := hashOrder(testCase.objectName, 16)
   124  		if !reflect.DeepEqual(testCase.hashedOrder, hashedOrder) {
   125  			t.Errorf("Test case %d: Expected \"%v\" but failed \"%v\"", i+1, testCase.hashedOrder, hashedOrder)
   126  		}
   127  	}
   128  
   129  	// Tests hashing order to fail for when order is '-1'.
   130  	if hashedOrder := hashOrder("This will fail", -1); hashedOrder != nil {
   131  		t.Errorf("Test: Expect \"nil\" but failed \"%#v\"", hashedOrder)
   132  	}
   133  
   134  	if hashedOrder := hashOrder("This will fail", 0); hashedOrder != nil {
   135  		t.Errorf("Test: Expect \"nil\" but failed \"%#v\"", hashedOrder)
   136  	}
   137  }
   138  
   139  func TestShuffleDisks(t *testing.T) {
   140  	ctx, cancel := context.WithCancel(context.Background())
   141  	defer cancel()
   142  
   143  	nDisks := 16
   144  	disks, err := getRandomDisks(nDisks)
   145  	if err != nil {
   146  		t.Fatal(err)
   147  	}
   148  	objLayer, _, err := initObjectLayer(ctx, mustGetPoolEndpoints(disks...))
   149  	if err != nil {
   150  		removeRoots(disks)
   151  		t.Fatal(err)
   152  	}
   153  	defer removeRoots(disks)
   154  	z := objLayer.(*erasureServerPools)
   155  	testShuffleDisks(t, z)
   156  }
   157  
   158  // Test shuffleDisks which returns shuffled slice of disks for their actual distribution.
   159  func testShuffleDisks(t *testing.T, z *erasureServerPools) {
   160  	disks := z.serverPools[0].GetDisks(0)()
   161  	distribution := []int{16, 14, 12, 10, 8, 6, 4, 2, 1, 3, 5, 7, 9, 11, 13, 15}
   162  	shuffledDisks := shuffleDisks(disks, distribution)
   163  	// From the "distribution" above you can notice that:
   164  	// 1st data block is in the 9th disk (i.e distribution index 8)
   165  	// 2nd data block is in the 8th disk (i.e distribution index 7) and so on.
   166  	if shuffledDisks[0] != disks[8] ||
   167  		shuffledDisks[1] != disks[7] ||
   168  		shuffledDisks[2] != disks[9] ||
   169  		shuffledDisks[3] != disks[6] ||
   170  		shuffledDisks[4] != disks[10] ||
   171  		shuffledDisks[5] != disks[5] ||
   172  		shuffledDisks[6] != disks[11] ||
   173  		shuffledDisks[7] != disks[4] ||
   174  		shuffledDisks[8] != disks[12] ||
   175  		shuffledDisks[9] != disks[3] ||
   176  		shuffledDisks[10] != disks[13] ||
   177  		shuffledDisks[11] != disks[2] ||
   178  		shuffledDisks[12] != disks[14] ||
   179  		shuffledDisks[13] != disks[1] ||
   180  		shuffledDisks[14] != disks[15] ||
   181  		shuffledDisks[15] != disks[0] {
   182  		t.Errorf("shuffleDisks returned incorrect order.")
   183  	}
   184  }
   185  
   186  // TestEvalDisks tests the behavior of evalDisks
   187  func TestEvalDisks(t *testing.T) {
   188  	ctx, cancel := context.WithCancel(context.Background())
   189  	defer cancel()
   190  
   191  	nDisks := 16
   192  	disks, err := getRandomDisks(nDisks)
   193  	if err != nil {
   194  		t.Fatal(err)
   195  	}
   196  	objLayer, _, err := initObjectLayer(ctx, mustGetPoolEndpoints(disks...))
   197  	if err != nil {
   198  		removeRoots(disks)
   199  		t.Fatal(err)
   200  	}
   201  	defer removeRoots(disks)
   202  	z := objLayer.(*erasureServerPools)
   203  	testShuffleDisks(t, z)
   204  }
   205  
   206  func Test_hashOrder(t *testing.T) {
   207  	for x := 1; x < 17; x++ {
   208  		t.Run(fmt.Sprintf("%d", x), func(t *testing.T) {
   209  			var first [17]int
   210  			rng := rand.New(rand.NewSource(0))
   211  			var tmp [16]byte
   212  			rng.Read(tmp[:])
   213  			prefix := hex.EncodeToString(tmp[:])
   214  			for i := 0; i < 10000; i++ {
   215  				rng.Read(tmp[:])
   216  
   217  				y := hashOrder(fmt.Sprintf("%s/%x", prefix, hex.EncodeToString(tmp[:3])), x)
   218  				first[y[0]]++
   219  			}
   220  			t.Log("first:", first[:x])
   221  		})
   222  	}
   223  }