diff --git a/merrors/doc.go b/merrors/doc.go index 0ea02d3..a670493 100644 --- a/merrors/doc.go +++ b/merrors/doc.go @@ -19,12 +19,11 @@ // // Example 3: // -// func CloseAll(closers []io.Closer) error { -// errs := merrors.New() -// for _ , c := range closers { -// errs.Add(c.Close()) -// } -// return errs.Err() -// } -// +// func CloseAll(closers []io.Closer) error { +// errs := merrors.New() +// for _ , c := range closers { +// errs.Add(c.Close()) +// } +// return errs.Err() +// } package merrors diff --git a/testutil/testutil.go b/testutil/testutil.go index 46b6fef..c74f368 100644 --- a/testutil/testutil.go +++ b/testutil/testutil.go @@ -92,6 +92,52 @@ func FaultOrPanicToErr(f func()) (err error) { return err } +// ContainsStringSlice fails the test if needle is not contained within haystack, if haystack or needle is +// an empty slice, or if needle is longer than haystack. +func ContainsStringSlice(tb testing.TB, haystack, needle []string) { + _, file, line, _ := runtime.Caller(1) + + if !contains(haystack, needle) { + tb.Fatalf(sprintfWithLimit("\033[31m%s:%d: %#v does not contain %#v\033[39m\n\n", filepath.Base(file), line, haystack, needle)) + } +} + +func contains(haystack, needle []string) bool { + if len(haystack) == 0 || len(needle) == 0 { + return false + } + + if len(haystack) < len(needle) { + return false + } + + for i := 0; i < len(haystack); i++ { + outer := i + + for j := 0; j < len(needle); j++ { + // End of the haystack but not the end of the needle, end + if outer == len(haystack) { + return false + } + + // No match, try the next index of the haystack + if haystack[outer] != needle[j] { + break + } + + // End of the needle and it still matches, end + if j == len(needle)-1 { + return true + } + + // This element matches between the two slices, try the next one + outer++ + } + } + + return false +} + func sprintfWithLimit(act string, v ...interface{}) string { s := fmt.Sprintf(act, v...) if len(s) > 10000 { diff --git a/testutil/testutil_test.go b/testutil/testutil_test.go new file mode 100644 index 0000000..ac42702 --- /dev/null +++ b/testutil/testutil_test.go @@ -0,0 +1,70 @@ +// Copyright (c) The EfficientGo Authors. +// Licensed under the Apache License 2.0. + +package testutil + +import "testing" + +func TestContains(t *testing.T) { + tests := map[string]struct { + haystack []string + needle []string + shouldMatch bool + }{ + "empty haystack": { + haystack: []string{}, + needle: []string{"key1"}, + shouldMatch: false, + }, + + "empty needle": { + haystack: []string{"key1", "key2", "key3"}, + needle: []string{}, + shouldMatch: false, + }, + + "single value needle": { + haystack: []string{"key1", "key2", "key3"}, + needle: []string{"key1"}, + shouldMatch: true, + }, + + "multiple value needle": { + haystack: []string{"key1", "key2", "key3"}, + needle: []string{"key1", "key2"}, + shouldMatch: true, + }, + + "same size needle as haystack": { + haystack: []string{"key1", "key2", "key3"}, + needle: []string{"key1", "key2", "key3"}, + shouldMatch: true, + }, + + "larger needle than haystack": { + haystack: []string{"key1", "key2", "key3"}, + needle: []string{"key1", "key2", "key3", "key4"}, + shouldMatch: false, + }, + + "needle not contained": { + haystack: []string{"key1", "key2", "key3"}, + needle: []string{"key4"}, + shouldMatch: false, + }, + + "haystack ends before needle": { + haystack: []string{"key1", "key2", "key3"}, + needle: []string{"key3", "key4"}, + shouldMatch: false, + }, + } + + for testName, testData := range tests { + t.Run(testName, func(t *testing.T) { + if testData.shouldMatch != contains(testData.haystack, testData.needle) { + t.Fatalf("unexpected result testing contains() with %#v", testData) + } + }) + } +}