1/* 2Copyright 2015 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package errors 18 19import ( 20 "fmt" 21 "reflect" 22 "sort" 23 "testing" 24) 25 26func TestEmptyAggregate(t *testing.T) { 27 var slice []error 28 var agg Aggregate 29 var err error 30 31 agg = NewAggregate(slice) 32 if agg != nil { 33 t.Errorf("expected nil, got %#v", agg) 34 } 35 err = NewAggregate(slice) 36 if err != nil { 37 t.Errorf("expected nil, got %#v", err) 38 } 39 40 // This is not normally possible, but pedantry demands I test it. 41 agg = aggregate(slice) // empty aggregate 42 if s := agg.Error(); s != "" { 43 t.Errorf("expected empty string, got %q", s) 44 } 45 if s := agg.Errors(); len(s) != 0 { 46 t.Errorf("expected empty slice, got %#v", s) 47 } 48 err = agg.(error) 49 if s := err.Error(); s != "" { 50 t.Errorf("expected empty string, got %q", s) 51 } 52} 53 54func TestAggregateWithNil(t *testing.T) { 55 var slice []error 56 slice = []error{nil} 57 var agg Aggregate 58 var err error 59 60 agg = NewAggregate(slice) 61 if agg != nil { 62 t.Errorf("expected nil, got %#v", agg) 63 } 64 err = NewAggregate(slice) 65 if err != nil { 66 t.Errorf("expected nil, got %#v", err) 67 } 68 69 // Append a non-nil error 70 slice = append(slice, fmt.Errorf("err")) 71 agg = NewAggregate(slice) 72 if agg == nil { 73 t.Errorf("expected non-nil") 74 } 75 if s := agg.Error(); s != "err" { 76 t.Errorf("expected 'err', got %q", s) 77 } 78 if s := agg.Errors(); len(s) != 1 { 79 t.Errorf("expected one-element slice, got %#v", s) 80 } 81 if s := agg.Errors()[0].Error(); s != "err" { 82 t.Errorf("expected 'err', got %q", s) 83 } 84 85 err = agg.(error) 86 if err == nil { 87 t.Errorf("expected non-nil") 88 } 89 if s := err.Error(); s != "err" { 90 t.Errorf("expected 'err', got %q", s) 91 } 92} 93 94func TestSingularAggregate(t *testing.T) { 95 var slice []error = []error{fmt.Errorf("err")} 96 var agg Aggregate 97 var err error 98 99 agg = NewAggregate(slice) 100 if agg == nil { 101 t.Errorf("expected non-nil") 102 } 103 if s := agg.Error(); s != "err" { 104 t.Errorf("expected 'err', got %q", s) 105 } 106 if s := agg.Errors(); len(s) != 1 { 107 t.Errorf("expected one-element slice, got %#v", s) 108 } 109 if s := agg.Errors()[0].Error(); s != "err" { 110 t.Errorf("expected 'err', got %q", s) 111 } 112 113 err = agg.(error) 114 if err == nil { 115 t.Errorf("expected non-nil") 116 } 117 if s := err.Error(); s != "err" { 118 t.Errorf("expected 'err', got %q", s) 119 } 120} 121 122func TestPluralAggregate(t *testing.T) { 123 var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("123")} 124 var agg Aggregate 125 var err error 126 127 agg = NewAggregate(slice) 128 if agg == nil { 129 t.Errorf("expected non-nil") 130 } 131 if s := agg.Error(); s != "[abc, 123]" { 132 t.Errorf("expected '[abc, 123]', got %q", s) 133 } 134 if s := agg.Errors(); len(s) != 2 { 135 t.Errorf("expected two-elements slice, got %#v", s) 136 } 137 if s := agg.Errors()[0].Error(); s != "abc" { 138 t.Errorf("expected '[abc, 123]', got %q", s) 139 } 140 141 err = agg.(error) 142 if err == nil { 143 t.Errorf("expected non-nil") 144 } 145 if s := err.Error(); s != "[abc, 123]" { 146 t.Errorf("expected '[abc, 123]', got %q", s) 147 } 148} 149 150func TestDedupeAggregate(t *testing.T) { 151 var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("abc")} 152 var agg Aggregate 153 154 agg = NewAggregate(slice) 155 if agg == nil { 156 t.Errorf("expected non-nil") 157 } 158 if s := agg.Error(); s != "abc" { 159 t.Errorf("expected 'abc', got %q", s) 160 } 161 if s := agg.Errors(); len(s) != 2 { 162 t.Errorf("expected two-elements slice, got %#v", s) 163 } 164} 165 166func TestDedupePluralAggregate(t *testing.T) { 167 var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("abc"), fmt.Errorf("123")} 168 var agg Aggregate 169 170 agg = NewAggregate(slice) 171 if agg == nil { 172 t.Errorf("expected non-nil") 173 } 174 if s := agg.Error(); s != "[abc, 123]" { 175 t.Errorf("expected '[abc, 123]', got %q", s) 176 } 177 if s := agg.Errors(); len(s) != 3 { 178 t.Errorf("expected three-elements slice, got %#v", s) 179 } 180} 181 182func TestFlattenAndDedupeAggregate(t *testing.T) { 183 var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("abc"), NewAggregate([]error{fmt.Errorf("abc")})} 184 var agg Aggregate 185 186 agg = NewAggregate(slice) 187 if agg == nil { 188 t.Errorf("expected non-nil") 189 } 190 if s := agg.Error(); s != "abc" { 191 t.Errorf("expected 'abc', got %q", s) 192 } 193 if s := agg.Errors(); len(s) != 3 { 194 t.Errorf("expected three-elements slice, got %#v", s) 195 } 196} 197 198func TestFlattenAggregate(t *testing.T) { 199 var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("abc"), NewAggregate([]error{fmt.Errorf("abc"), fmt.Errorf("def"), NewAggregate([]error{fmt.Errorf("def"), fmt.Errorf("ghi")})})} 200 var agg Aggregate 201 202 agg = NewAggregate(slice) 203 if agg == nil { 204 t.Errorf("expected non-nil") 205 } 206 if s := agg.Error(); s != "[abc, def, ghi]" { 207 t.Errorf("expected '[abc, def, ghi]', got %q", s) 208 } 209 if s := agg.Errors(); len(s) != 3 { 210 t.Errorf("expected three-elements slice, got %#v", s) 211 } 212} 213 214func TestFilterOut(t *testing.T) { 215 testCases := []struct { 216 err error 217 filter []Matcher 218 expected error 219 }{ 220 { 221 nil, 222 []Matcher{}, 223 nil, 224 }, 225 { 226 aggregate{}, 227 []Matcher{}, 228 nil, 229 }, 230 { 231 aggregate{fmt.Errorf("abc")}, 232 []Matcher{}, 233 aggregate{fmt.Errorf("abc")}, 234 }, 235 { 236 aggregate{fmt.Errorf("abc")}, 237 []Matcher{func(err error) bool { return false }}, 238 aggregate{fmt.Errorf("abc")}, 239 }, 240 { 241 aggregate{fmt.Errorf("abc")}, 242 []Matcher{func(err error) bool { return true }}, 243 nil, 244 }, 245 { 246 aggregate{fmt.Errorf("abc")}, 247 []Matcher{func(err error) bool { return false }, func(err error) bool { return false }}, 248 aggregate{fmt.Errorf("abc")}, 249 }, 250 { 251 aggregate{fmt.Errorf("abc")}, 252 []Matcher{func(err error) bool { return false }, func(err error) bool { return true }}, 253 nil, 254 }, 255 { 256 aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")}, 257 []Matcher{func(err error) bool { return err.Error() == "def" }}, 258 aggregate{fmt.Errorf("abc"), fmt.Errorf("ghi")}, 259 }, 260 { 261 aggregate{aggregate{fmt.Errorf("abc")}}, 262 []Matcher{}, 263 aggregate{aggregate{fmt.Errorf("abc")}}, 264 }, 265 { 266 aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}}, 267 []Matcher{}, 268 aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}}, 269 }, 270 { 271 aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}}, 272 []Matcher{func(err error) bool { return err.Error() == "def" }}, 273 aggregate{aggregate{fmt.Errorf("abc")}}, 274 }, 275 } 276 for i, testCase := range testCases { 277 err := FilterOut(testCase.err, testCase.filter...) 278 if !reflect.DeepEqual(testCase.expected, err) { 279 t.Errorf("%d: expected %v, got %v", i, testCase.expected, err) 280 } 281 } 282} 283 284func TestFlatten(t *testing.T) { 285 testCases := []struct { 286 agg Aggregate 287 expected Aggregate 288 }{ 289 { 290 nil, 291 nil, 292 }, 293 { 294 aggregate{}, 295 nil, 296 }, 297 { 298 aggregate{fmt.Errorf("abc")}, 299 aggregate{fmt.Errorf("abc")}, 300 }, 301 { 302 aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")}, 303 aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")}, 304 }, 305 { 306 aggregate{aggregate{fmt.Errorf("abc")}}, 307 aggregate{fmt.Errorf("abc")}, 308 }, 309 { 310 aggregate{aggregate{aggregate{fmt.Errorf("abc")}}}, 311 aggregate{fmt.Errorf("abc")}, 312 }, 313 { 314 aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}}, 315 aggregate{fmt.Errorf("abc"), fmt.Errorf("def")}, 316 }, 317 { 318 aggregate{aggregate{aggregate{fmt.Errorf("abc")}, fmt.Errorf("def"), aggregate{fmt.Errorf("ghi")}}}, 319 aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")}, 320 }, 321 } 322 for i, testCase := range testCases { 323 agg := Flatten(testCase.agg) 324 if !reflect.DeepEqual(testCase.expected, agg) { 325 t.Errorf("%d: expected %v, got %v", i, testCase.expected, agg) 326 } 327 } 328} 329 330func TestCreateAggregateFromMessageCountMap(t *testing.T) { 331 testCases := []struct { 332 name string 333 mcm MessageCountMap 334 expected Aggregate 335 }{ 336 { 337 "input has single instance of one message", 338 MessageCountMap{"abc": 1}, 339 aggregate{fmt.Errorf("abc")}, 340 }, 341 { 342 "input has multiple messages", 343 MessageCountMap{"abc": 2, "ghi": 1}, 344 aggregate{fmt.Errorf("abc (repeated 2 times)"), fmt.Errorf("ghi")}, 345 }, 346 { 347 "input has multiple messages", 348 MessageCountMap{"ghi": 1, "abc": 2}, 349 aggregate{fmt.Errorf("abc (repeated 2 times)"), fmt.Errorf("ghi")}, 350 }, 351 } 352 353 var expected, agg []error 354 for _, testCase := range testCases { 355 t.Run(testCase.name, func(t *testing.T) { 356 if testCase.expected != nil { 357 expected = testCase.expected.Errors() 358 sort.Slice(expected, func(i, j int) bool { return expected[i].Error() < expected[j].Error() }) 359 } 360 if testCase.mcm != nil { 361 agg = CreateAggregateFromMessageCountMap(testCase.mcm).Errors() 362 sort.Slice(agg, func(i, j int) bool { return agg[i].Error() < agg[j].Error() }) 363 } 364 if !reflect.DeepEqual(expected, agg) { 365 t.Errorf("expected %v, got %v", expected, agg) 366 } 367 }) 368 } 369} 370 371func TestAggregateGoroutines(t *testing.T) { 372 testCases := []struct { 373 errs []error 374 expected map[string]bool // can't compare directly to Aggregate due to non-deterministic ordering 375 }{ 376 { 377 []error{}, 378 nil, 379 }, 380 { 381 []error{nil}, 382 nil, 383 }, 384 { 385 []error{nil, nil}, 386 nil, 387 }, 388 { 389 []error{fmt.Errorf("1")}, 390 map[string]bool{"1": true}, 391 }, 392 { 393 []error{fmt.Errorf("1"), nil}, 394 map[string]bool{"1": true}, 395 }, 396 { 397 []error{fmt.Errorf("1"), fmt.Errorf("267")}, 398 map[string]bool{"1": true, "267": true}, 399 }, 400 { 401 []error{fmt.Errorf("1"), nil, fmt.Errorf("1234")}, 402 map[string]bool{"1": true, "1234": true}, 403 }, 404 { 405 []error{nil, fmt.Errorf("1"), nil, fmt.Errorf("1234"), fmt.Errorf("22")}, 406 map[string]bool{"1": true, "1234": true, "22": true}, 407 }, 408 } 409 for i, testCase := range testCases { 410 funcs := make([]func() error, len(testCase.errs)) 411 for i := range testCase.errs { 412 err := testCase.errs[i] 413 funcs[i] = func() error { return err } 414 } 415 agg := AggregateGoroutines(funcs...) 416 if agg == nil { 417 if len(testCase.expected) > 0 { 418 t.Errorf("%d: expected %v, got nil", i, testCase.expected) 419 } 420 continue 421 } 422 if len(agg.Errors()) != len(testCase.expected) { 423 t.Errorf("%d: expected %d errors in aggregate, got %v", i, len(testCase.expected), agg) 424 continue 425 } 426 for _, err := range agg.Errors() { 427 if !testCase.expected[err.Error()] { 428 t.Errorf("%d: expected %v, got aggregate containing %v", i, testCase.expected, err) 429 } 430 } 431 } 432} 433