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 }