blob: 117662cc99045521b4a2c33e9d1efa07557ba5fb [file] [log] [blame]
package find_breaks
import (
"fmt"
"testing"
assert "github.com/stretchr/testify/require"
"go.skia.org/infra/go/testutils"
"go.skia.org/infra/go/util"
)
var (
commits = []string{"a", "b", "c", "d", "e", "f", "g"}
)
func assertSubslice(t *testing.T, v, slice slice) {
assert.Equal(t, v.Len(), v.Overlap(slice).Len())
}
func assertValidFailure(t *testing.T, f *failure) {
assert.NoError(t, f.valid())
}
func assertValidFailureGroup(t *testing.T, g *FailureGroup) {
assert.NoError(t, g.Valid())
}
func TestMerge(t *testing.T) {
testutils.SmallTest(t)
f1 := &failure{
id: "f1",
brokeIn: newSlice(0, 1), // a
failing: newSlice(0, 1), // a
fixedIn: newSlice(1, 2), // b
}
f2 := &failure{
id: "f2",
brokeIn: newSlice(0, 1), // a
failing: newSlice(0, 1), // a
fixedIn: newSlice(1, 2), // b
}
f3 := &failure{
id: "f3",
brokeIn: newSlice(1, 2), // b
failing: newSlice(1, 2), // b
fixedIn: newSlice(2, 3), // c
}
f4 := &failure{
id: "f4",
brokeIn: newSlice(0, 2), // a b
failing: newSlice(0, 2), // a b
fixedIn: newSlice(2, 3), // c
}
f5 := &failure{
id: "f5",
brokeIn: newSlice(1, 2), // b
failing: newSlice(1, 3), // b c
fixedIn: newSlice(3, 4), // d
}
f6 := &failure{
id: "f6",
brokeIn: newSlice(0, 2), // a b
failing: newSlice(0, 2), // a b
fixedIn: newSlice(2, 4), // c d
}
f7 := &failure{
id: "f7",
brokeIn: newSlice(0, 1), // a
failing: newSlice(0, 1), // a
fixedIn: newSlice(1, 4), // b c d
}
f8 := &failure{
id: "f8",
brokeIn: newSlice(2, 3), // c
failing: newSlice(2, 3), // c
fixedIn: newSlice(3, 4), // d
}
f9 := &failure{
id: "f9",
brokeIn: newSlice(0, 2), // a b
failing: newSlice(0, 2), // a b
fixedIn: newSlice(-1, -1), // ?
}
f10 := &failure{
id: "f10",
brokeIn: newSlice(0, 2), // a b
failing: newSlice(0, 2), // a b
fixedIn: newSlice(2, 3), // c
}
f11 := &failure{
id: "f11",
brokeIn: newSlice(1, 2), // b
failing: newSlice(1, 2), // b
fixedIn: newSlice(2, 3), // c
}
f12 := &failure{
id: "f12",
brokeIn: newSlice(4, 7), // e f g
failing: newSlice(4, 7), // e f g
fixedIn: newSlice(-1, -1),
}
f13 := &failure{
id: "f13",
brokeIn: newSlice(4, 7), // e f g
failing: newSlice(4, 7), // e f g
fixedIn: newSlice(-1, -1),
}
type testCase struct {
f1 *failure
f2 *failure
expect *FailureGroup
}
tc := []testCase{
// 0. Very simple equality.
testCase{
f1: f1,
f2: f2,
expect: &FailureGroup{
Ids: []string{f1.id, f2.id},
brokeIn: newSlice(0, 1),
failing: newSlice(0, 1),
fixedIn: newSlice(1, 2),
},
},
testCase{
f1: f12,
f2: f13,
expect: &FailureGroup{
Ids: []string{f12.id, f13.id},
brokeIn: newSlice(4, 7),
failing: newSlice(4, 7),
fixedIn: newSlice(-1, -1),
},
},
// 1. Not equal.
testCase{
f1: f1,
f2: f3,
expect: nil,
},
// 2. Overlap, fixes don't match.
testCase{
f1: f4,
f2: f5,
expect: nil,
},
// 3. Overlap, fixes match.
testCase{
f1: f6,
f2: f5,
expect: &FailureGroup{
Ids: []string{f5.id, f6.id},
brokeIn: newSlice(1, 2),
failing: newSlice(1, 3),
fixedIn: newSlice(3, 4),
},
},
// Inherit the non-empty fixedIn slice.
testCase{
f1: f3,
f2: f9,
expect: &FailureGroup{
Ids: []string{f3.id, f9.id},
brokeIn: newSlice(1, 2),
failing: newSlice(1, 2),
fixedIn: newSlice(2, 3),
},
},
// 4. Totally disjoint.
testCase{
f1: f7,
f2: f8,
expect: nil,
},
// 5. The fix slice of one failure cannot be wholly contained
// within the failed slice of the other.
testCase{
f1: f1,
f2: f9,
expect: nil,
},
testCase{
f1: f10,
f2: f11,
expect: &FailureGroup{
Ids: []string{f10.id, f11.id},
brokeIn: newSlice(1, 2),
failing: newSlice(1, 2),
fixedIn: newSlice(2, 3),
},
},
}
check := func(f1, f2 *failure, expect *FailureGroup) {
assertValidFailure(t, f1)
assertValidFailure(t, f2)
g1 := fromFailure(f1)
g2 := fromFailure(f2)
assertValidFailureGroup(t, g1)
assertValidFailureGroup(t, g2)
g1.maybeMerge(f2)
g2.maybeMerge(f1)
assertValidFailureGroup(t, g1)
assertValidFailureGroup(t, g2)
if expect == nil {
testutils.AssertDeepEqual(t, f1.brokeIn, g1.brokeIn)
testutils.AssertDeepEqual(t, f1.failing, g1.failing)
testutils.AssertDeepEqual(t, f1.fixedIn, g1.fixedIn)
testutils.AssertDeepEqual(t, f2.brokeIn, g2.brokeIn)
testutils.AssertDeepEqual(t, f2.failing, g2.failing)
testutils.AssertDeepEqual(t, f2.fixedIn, g2.fixedIn)
} else {
testutils.AssertDeepEqual(t, expect, g1)
testutils.AssertDeepEqual(t, expect, g2)
}
}
for _, tc := range tc {
check(tc.f1, tc.f2, tc.expect)
}
}
func TestFindFailureGroups(t *testing.T) {
testutils.SmallTest(t)
commits := []string{"a", "b", "c", "d", "e"}
f1 := &failure{
id: "1",
brokeIn: newSlice(0, 1), // a
failing: newSlice(0, 1), // a
fixedIn: newSlice(1, 3), // b c
}
f2 := &failure{
id: "2",
brokeIn: newSlice(1, 2), // b
failing: newSlice(1, 2), // b
fixedIn: newSlice(2, 3), // c
}
f3 := &failure{
id: "3",
brokeIn: newSlice(0, 2), // a b
failing: newSlice(0, 2), // a b
fixedIn: newSlice(2, 5), // c d e
}
f4 := &failure{
id: "4",
brokeIn: newSlice(1, 3), // b c
failing: newSlice(1, 3), // b c
fixedIn: newSlice(3, 5), // d e
}
f5 := &failure{
id: "5",
brokeIn: newSlice(2, 4), // c d
failing: newSlice(2, 4), // c d
fixedIn: newSlice(4, 6), // e f
}
f6 := &failure{
id: "6",
brokeIn: newSlice(3, 5), // d e
failing: newSlice(3, 5), // d e
fixedIn: newSlice(-1, -1), // ?
}
f7 := &failure{
id: "7",
brokeIn: newSlice(3, 5), // d e
failing: newSlice(3, 5), // d e
fixedIn: newSlice(-1, -1), // ?
}
check := func(input []*failure, expect [][]string) {
got, err := findFailureGroups(input, commits)
assert.NoError(t, err)
remaining := make([][]string, len(expect))
copy(remaining, expect)
for _, fg := range got {
found := false
for i, e := range remaining {
if util.SSliceEqual(e, fg.Ids) {
remaining = append(remaining[:i], remaining[i+1:]...)
found = true
break
}
}
assert.True(t, found, fmt.Sprintf("failure group: %v", fg))
}
assert.Equal(t, 0, len(remaining))
}
check([]*failure{f1, f2}, [][]string{[]string{f1.id}, []string{f2.id}})
check([]*failure{f2, f3}, [][]string{[]string{f2.id, f3.id}})
check([]*failure{f3, f4, f5}, [][]string{[]string{f3.id, f4.id}, []string{f4.id, f5.id}})
check([]*failure{f1, f2, f3, f4, f5}, [][]string{[]string{f1.id, f3.id}, []string{f2.id, f3.id}, []string{f3.id, f4.id}, []string{f4.id, f5.id}})
// f3 f5 f6 f7 f4
// e X X
// d X X X
// c X X
// b X X
// a X
//
// Expect: (f3, f4), (f5, f6, f7), (f4, f5)
check([]*failure{f3, f5, f6, f7, f4}, [][]string{
[]string{f3.id, f4.id},
[]string{f4.id, f5.id},
[]string{f5.id, f6.id, f7.id},
})
}