Skip to content

Commit aa22cd4

Browse files
authored
TT-1715 Create tests for the new flaky tests detector (#1299)
1 parent d931474 commit aa22cd4

File tree

13 files changed

+485
-15
lines changed

13 files changed

+485
-15
lines changed

.github/workflows/test.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ jobs:
2222
path: ./tools/ghlatestreleasechecker/
2323
- name: asciitable
2424
path: ./tools/asciitable/
25+
- name: flakeguard
26+
path: ./tools/flakeguard/
2527
- name: workflowresultparser
2628
path: ./tools/workflowresultparser/
2729
runs-on: ubuntu-latest

tools/flakeguard/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
test_unit:
2+
go test -timeout 5m -json -cover -covermode=count -coverprofile=unit-test-coverage.out ./... 2>&1 | tee /tmp/gotest.log | gotestloghelper -ci

tools/flakeguard/cmd/find.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,6 @@ func findAffectedPackages(baseRef, projectPath string, excludes []string, levels
154154

155155
func outputResults(packages []string, jsonOutput bool) {
156156
if jsonOutput {
157-
if packages == nil {
158-
packages = make([]string, 0) // Ensure the slice is initialized to an empty array
159-
}
160157
data, err := json.Marshal(packages)
161158
if err != nil {
162159
log.Fatalf("Error marshaling test files to JSON: %v", err)

tools/flakeguard/cmd/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ var RunTestsCmd = &cobra.Command{
5757
if len(failedTests) > 0 {
5858
fmt.Printf("PassRatio threshold for flaky tests: %.2f\n", threshold)
5959
fmt.Printf("%d failed tests:\n", len(failedTests))
60-
reports.PrintTests(failedTests)
60+
reports.PrintTests(failedTests, os.Stdout)
6161
}
6262

6363
fmt.Printf("Summary: %d passed, %d skipped, %d failed\n", len(passedTests), len(skippedTests), len(failedTests))

tools/flakeguard/git/git_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package git
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestGetChangedGoPackagesFromDiff(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
out string
14+
projectPath string
15+
excludes []string
16+
fileMap map[string][]string
17+
expected []string
18+
expectError bool
19+
}{
20+
{
21+
name: "Basic Case",
22+
out: "pkg1/file1.go\npkg2/file2.go\n",
23+
excludes: []string{},
24+
fileMap: map[string][]string{
25+
"pkg1/file1.go": {"pkg1"},
26+
"pkg2/file2.go": {"pkg2"},
27+
},
28+
expected: []string{"pkg1", "pkg2"},
29+
expectError: false,
30+
},
31+
{
32+
name: "Empty Input",
33+
out: "",
34+
excludes: []string{},
35+
fileMap: map[string][]string{},
36+
expected: []string{},
37+
expectError: false,
38+
},
39+
{
40+
name: "Non-Go Files Ignored",
41+
out: "pkg1/file1.txt\npkg2/file2.go\n",
42+
excludes: []string{},
43+
fileMap: map[string][]string{
44+
"pkg2/file2.go": {"pkg2"},
45+
},
46+
expected: []string{"pkg2"},
47+
expectError: false,
48+
},
49+
{
50+
name: "Exclusions Applied",
51+
out: "pkg1/file1.go\npkg2/file2.go\npkg3/file3.go\n",
52+
excludes: []string{"pkg2"},
53+
fileMap: map[string][]string{
54+
"pkg1/file1.go": {"pkg1"},
55+
"pkg2/file2.go": {"pkg2"},
56+
"pkg3/file3.go": {"pkg3"},
57+
},
58+
expected: []string{"pkg1", "pkg3"},
59+
expectError: false,
60+
},
61+
{
62+
name: "Multiple Imports",
63+
out: "pkg1/file1.go\n",
64+
excludes: []string{},
65+
fileMap: map[string][]string{
66+
"pkg1/file1.go": {"pkg1", "pkg1/subpkg"},
67+
},
68+
expected: []string{"pkg1", "pkg1/subpkg"},
69+
expectError: false,
70+
},
71+
{
72+
name: "Duplicate Packages",
73+
out: "pkg1/file1.go\npkg1/file1.go\n",
74+
excludes: []string{},
75+
fileMap: map[string][]string{
76+
"pkg1/file1.go": {"pkg1"},
77+
},
78+
expected: []string{"pkg1"},
79+
expectError: false,
80+
},
81+
}
82+
83+
for _, tt := range tests {
84+
t.Run(tt.name, func(t *testing.T) {
85+
outBuffer := bytes.Buffer{}
86+
outBuffer.WriteString(tt.out)
87+
result, err := GetChangedGoPackagesFromDiff(outBuffer, tt.projectPath, tt.excludes, tt.fileMap)
88+
if tt.expectError {
89+
assert.Error(t, err)
90+
} else {
91+
assert.NoError(t, err)
92+
assert.ElementsMatch(t, tt.expected, result)
93+
}
94+
})
95+
}
96+
}

tools/flakeguard/go.mod

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ go 1.21.9
44

55
require github.com/spf13/cobra v1.8.1
66

7+
require (
8+
github.com/davecgh/go-spew v1.1.1 // indirect
9+
github.com/pmezard/go-difflib v1.0.0 // indirect
10+
gopkg.in/yaml.v3 v3.0.1 // indirect
11+
)
12+
713
require (
814
github.com/inconshreveable/mousetrap v1.1.0 // indirect
915
github.com/spf13/pflag v1.0.5 // indirect
16+
github.com/stretchr/testify v1.9.0
1017
)

tools/flakeguard/go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
24
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
35
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
6+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
7+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
48
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
59
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
610
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
711
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
812
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
13+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
14+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
915
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
16+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
1017
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

tools/flakeguard/golang/golang.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ func GetFilePackages(files []string) ([]string, error) {
168168
}
169169

170170
// Function to check if a package contains any test functions
171-
func hasTests(pkgName string) (bool, error) {
171+
var hasTests = func(pkgName string) (bool, error) {
172172
cmd := exec.Command("go", "test", pkgName, "-run=^$", "-list", ".")
173173

174174
var out bytes.Buffer
@@ -184,7 +184,7 @@ func hasTests(pkgName string) (bool, error) {
184184

185185
// Filter out test packages with no actual test functions
186186
func FilterPackagesWithTests(pkgs []string) []string {
187-
var testPkgs []string
187+
testPkgs := []string{}
188188
for _, pkg := range pkgs {
189189
hasT, err := hasTests(pkg)
190190
if err != nil {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package golang
2+
3+
import (
4+
"errors"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
// Mock version of hasTests function to simulate various scenarios
11+
func mockHasTests(pkgName string) (bool, error) {
12+
switch pkgName {
13+
case "pkgWithTests":
14+
return true, nil
15+
case "pkgWithoutTests":
16+
return false, nil
17+
case "pkgWithError":
18+
return false, errors.New("test error")
19+
default:
20+
return false, nil
21+
}
22+
}
23+
24+
func TestFilterPackagesWithTests(t *testing.T) {
25+
// Replace hasTests with mock function
26+
originalHasTests := hasTests
27+
hasTests = mockHasTests
28+
defer func() { hasTests = originalHasTests }() // Restore original function after test
29+
30+
t.Run("should return packages that contain tests", func(t *testing.T) {
31+
pkgs := []string{"pkgWithTests", "pkgWithoutTests", "pkgWithError"}
32+
expected := []string{"pkgWithTests"}
33+
34+
result := FilterPackagesWithTests(pkgs)
35+
36+
assert.Equal(t, expected, result, "Expected packages with tests only")
37+
})
38+
39+
t.Run("should return an empty slice when all packages have no tests", func(t *testing.T) {
40+
pkgs := []string{"pkgWithoutTests"}
41+
expected := []string{}
42+
43+
result := FilterPackagesWithTests(pkgs)
44+
45+
assert.Equal(t, expected, result, "Expected empty slice for packages without tests")
46+
})
47+
48+
t.Run("should handle error scenarios gracefully", func(t *testing.T) {
49+
pkgs := []string{"pkgWithError"}
50+
expected := []string{}
51+
52+
result := FilterPackagesWithTests(pkgs)
53+
54+
assert.Equal(t, expected, result, "Expected empty slice for packages with errors")
55+
})
56+
}

tools/flakeguard/reports/reports.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package reports
22

33
import (
44
"fmt"
5+
"io"
56
"strings"
67
)
78

@@ -49,19 +50,19 @@ func FilterSkippedTests(results []TestResult) []TestResult {
4950
}
5051

5152
// PrintTests prints tests in a pretty format
52-
func PrintTests(tests []TestResult) {
53+
func PrintTests(tests []TestResult, w io.Writer) {
5354
for i, test := range tests {
54-
fmt.Printf("\n--- Test %d ---\n", i+1)
55-
fmt.Printf("TestName: %s\n", test.TestName)
56-
fmt.Printf("TestPackage: %s\n", test.TestPackage)
57-
fmt.Printf("PassRatio: %.2f\n", test.PassRatio)
58-
fmt.Printf("Skipped: %v\n", test.Skipped)
59-
fmt.Printf("Runs: %d\n", test.Runs)
55+
fmt.Fprintf(w, "\n--- Test %d ---\n", i+1)
56+
fmt.Fprintf(w, "TestName: %s\n", test.TestName)
57+
fmt.Fprintf(w, "TestPackage: %s\n", test.TestPackage)
58+
fmt.Fprintf(w, "PassRatio: %.2f\n", test.PassRatio)
59+
fmt.Fprintf(w, "Skipped: %v\n", test.Skipped)
60+
fmt.Fprintf(w, "Runs: %d\n", test.Runs)
6061
durationsStr := make([]string, len(test.Durations))
6162
for i, duration := range test.Durations {
6263
durationsStr[i] = fmt.Sprintf("%.2fs", duration)
6364
}
64-
fmt.Printf("Durations: %s\n", strings.Join(durationsStr, ", "))
65-
fmt.Printf("Outputs:\n%s\n", strings.Join(test.Outputs, ""))
65+
fmt.Fprintf(w, "Durations: %s\n", strings.Join(durationsStr, ", "))
66+
fmt.Fprintf(w, "Outputs:\n%s\n", strings.Join(test.Outputs, ""))
6667
}
6768
}

0 commit comments

Comments
 (0)