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