github.com/minio/console@v1.4.1/pkg/utils/parity_test.go (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2021 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 package utils 18 19 import ( 20 "reflect" 21 "testing" 22 23 "github.com/minio/pkg/v3/ellipses" 24 ) 25 26 func TestGetDivisibleSize(t *testing.T) { 27 testCases := []struct { 28 totalSizes []uint64 29 result uint64 30 }{ 31 {[]uint64{24, 32, 16}, 8}, 32 {[]uint64{32, 8, 4}, 4}, 33 {[]uint64{8, 8, 8}, 8}, 34 {[]uint64{24}, 24}, 35 } 36 37 for _, testCase := range testCases { 38 testCase := testCase 39 t.Run("", func(_ *testing.T) { 40 gotGCD := getDivisibleSize(testCase.totalSizes) 41 if testCase.result != gotGCD { 42 t.Errorf("Expected %v, got %v", testCase.result, gotGCD) 43 } 44 }) 45 } 46 } 47 48 // Test tests calculating set indexes. 49 func TestGetSetIndexes(t *testing.T) { 50 testCases := []struct { 51 args []string 52 totalSizes []uint64 53 indexes [][]uint64 54 success bool 55 }{ 56 // Invalid inputs. 57 { 58 []string{"data{1...3}"}, 59 []uint64{3}, 60 nil, 61 false, 62 }, 63 { 64 []string{"data/controller1/export{1...2}, data/controller2/export{1...4}, data/controller3/export{1...8}"}, 65 []uint64{2, 4, 8}, 66 nil, 67 false, 68 }, 69 { 70 []string{"data{1...17}/export{1...52}"}, 71 []uint64{14144}, 72 nil, 73 false, 74 }, 75 // Valid inputs. 76 { 77 []string{"data{1...27}"}, 78 []uint64{27}, 79 [][]uint64{{9, 9, 9}}, 80 true, 81 }, 82 { 83 []string{"http://host{1...3}/data{1...180}"}, 84 []uint64{540}, 85 [][]uint64{{15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}}, 86 true, 87 }, 88 { 89 []string{"http://host{1...2}.rack{1...4}/data{1...180}"}, 90 []uint64{1440}, 91 [][]uint64{{16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}}, 92 true, 93 }, 94 { 95 []string{"http://host{1...2}/data{1...180}"}, 96 []uint64{360}, 97 [][]uint64{{12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}}, 98 true, 99 }, 100 { 101 []string{"data/controller1/export{1...4}, data/controller2/export{1...8}, data/controller3/export{1...12}"}, 102 []uint64{4, 8, 12}, 103 [][]uint64{{4}, {4, 4}, {4, 4, 4}}, 104 true, 105 }, 106 { 107 []string{"data{1...64}"}, 108 []uint64{64}, 109 [][]uint64{{16, 16, 16, 16}}, 110 true, 111 }, 112 { 113 []string{"data{1...24}"}, 114 []uint64{24}, 115 [][]uint64{{12, 12}}, 116 true, 117 }, 118 { 119 []string{"data/controller{1...11}/export{1...8}"}, 120 []uint64{88}, 121 [][]uint64{{11, 11, 11, 11, 11, 11, 11, 11}}, 122 true, 123 }, 124 { 125 []string{"data{1...4}"}, 126 []uint64{4}, 127 [][]uint64{{4}}, 128 true, 129 }, 130 { 131 []string{"data/controller1/export{1...10}, data/controller2/export{1...10}, data/controller3/export{1...10}"}, 132 []uint64{10, 10, 10}, 133 [][]uint64{{10}, {10}, {10}}, 134 true, 135 }, 136 { 137 []string{"data{1...16}/export{1...52}"}, 138 []uint64{832}, 139 [][]uint64{{16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}}, 140 true, 141 }, 142 } 143 144 for _, testCase := range testCases { 145 testCase := testCase 146 t.Run("", func(_ *testing.T) { 147 argPatterns := make([]ellipses.ArgPattern, len(testCase.args)) 148 for i, arg := range testCase.args { 149 patterns, err := ellipses.FindEllipsesPatterns(arg) 150 if err != nil { 151 t.Fatalf("Unexpected failure %s", err) 152 } 153 argPatterns[i] = patterns 154 } 155 gotIndexes, err := getSetIndexes(testCase.args, testCase.totalSizes, argPatterns) 156 if err != nil && testCase.success { 157 t.Errorf("Expected success but failed instead %s", err) 158 } 159 if err == nil && !testCase.success { 160 t.Errorf("Expected failure but passed instead") 161 } 162 if !reflect.DeepEqual(testCase.indexes, gotIndexes) { 163 t.Errorf("Expected %v, got %v", testCase.indexes, gotIndexes) 164 } 165 }) 166 } 167 } 168 169 // Test tests possible parities returned for any input args 170 func TestPossibleParities(t *testing.T) { 171 testCases := []struct { 172 arg string 173 parities []string 174 success bool 175 }{ 176 // Tests invalid inputs. 177 { 178 "...", 179 nil, 180 false, 181 }, 182 // No range specified. 183 { 184 "{...}", 185 nil, 186 false, 187 }, 188 // Invalid range. 189 { 190 "http://minio{2...3}/export/set{1...0}", 191 nil, 192 false, 193 }, 194 // Range cannot be smaller than 4 minimum. 195 { 196 "/export{1..2}", 197 nil, 198 false, 199 }, 200 // Unsupported characters. 201 { 202 "/export/test{1...2O}", 203 nil, 204 false, 205 }, 206 // Tests valid inputs. 207 { 208 "{1...27}", 209 []string{"EC:4", "EC:3", "EC:2"}, 210 true, 211 }, 212 { 213 "/export/set{1...64}", 214 []string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"}, 215 true, 216 }, 217 // Valid input for distributed setup. 218 { 219 "http://minio{2...3}/export/set{1...64}", 220 []string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"}, 221 true, 222 }, 223 // Supporting some advanced cases. 224 { 225 "http://minio{1...64}.mydomain.net/data", 226 []string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"}, 227 true, 228 }, 229 { 230 "http://rack{1...4}.mydomain.minio{1...16}/data", 231 []string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"}, 232 true, 233 }, 234 // Supporting kubernetes cases. 235 { 236 "http://minio{0...15}.mydomain.net/data{0...1}", 237 []string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"}, 238 true, 239 }, 240 // No host regex, just disks. 241 { 242 "http://server1/data{1...32}", 243 []string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"}, 244 true, 245 }, 246 // No host regex, just disks with two position numerics. 247 { 248 "http://server1/data{01...32}", 249 []string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"}, 250 true, 251 }, 252 // More than 2 ellipses are supported as well. 253 { 254 "http://minio{2...3}/export/set{1...64}/test{1...2}", 255 []string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"}, 256 true, 257 }, 258 // More than 1 ellipses per argument for standalone setup. 259 { 260 "/export{1...10}/disk{1...10}", 261 []string{"EC:5", "EC:4", "EC:3", "EC:2"}, 262 true, 263 }, 264 // IPv6 ellipses with hexadecimal expansion 265 { 266 "http://[2001:3984:3989::{1...a}]/disk{1...10}", 267 []string{"EC:5", "EC:4", "EC:3", "EC:2"}, 268 true, 269 }, 270 // IPv6 ellipses with hexadecimal expansion with 3 position numerics. 271 { 272 "http://[2001:3984:3989::{001...00a}]/disk{1...10}", 273 []string{"EC:5", "EC:4", "EC:3", "EC:2"}, 274 true, 275 }, 276 } 277 278 for _, testCase := range testCases { 279 testCase := testCase 280 t.Run("", func(_ *testing.T) { 281 gotPs, err := PossibleParityValues(testCase.arg) 282 if err != nil && testCase.success { 283 t.Errorf("Expected success but failed instead %s", err) 284 } 285 if err == nil && !testCase.success { 286 t.Errorf("Expected failure but passed instead") 287 } 288 if !reflect.DeepEqual(testCase.parities, gotPs) { 289 t.Errorf("Expected %v, got %v", testCase.parities, gotPs) 290 } 291 }) 292 } 293 }