From 073fbf79486bbc50619873ed63deed49a1361077 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Wed, 31 Jul 2024 21:37:46 +0900 Subject: [PATCH] run test concurrently --- Makefile | 2 +- cmd/tlin/main_test.go | 9 +- formatter/fmt_test.go | 128 +++++----------------------- formatter/format_emit.go | 2 + internal/analysis/cfg/cfg_test.go | 49 ++++++++--- internal/fixer_test.go | 13 +-- internal/lints/detect_cycle_test.go | 1 + internal/lints/gno_analyzer_test.go | 27 +++--- internal/lints/lint_test.go | 21 +++++ internal/symbol_table_test.go | 22 +++-- 10 files changed, 125 insertions(+), 149 deletions(-) diff --git a/Makefile b/Makefile index 0e2c9c3..3364be4 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ build-mac: CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 $(GOBUILD) -o $(BINARY_MAC) -v $(MAIN_PACKAGE) test: - $(GOTEST) -v ./... + $(GOTEST) -race -v ./... clean: $(GOCLEAN) diff --git a/cmd/tlin/main_test.go b/cmd/tlin/main_test.go index 730a910..005f9c9 100644 --- a/cmd/tlin/main_test.go +++ b/cmd/tlin/main_test.go @@ -31,11 +31,10 @@ func (m *MockLintEngine) IgnoreRule(rule string) { } func TestParseFlags(t *testing.T) { - // Save original os.Args + t.Parallel() oldArgs := os.Args defer func() { os.Args = oldArgs }() - // Test case os.Args = []string{"cmd", "-timeout", "10s", "-cyclo", "-threshold", "15", "-ignore", "rule1,rule2", "file1.go", "file2.go"} config := parseFlags() @@ -48,6 +47,7 @@ func TestParseFlags(t *testing.T) { } func TestProcessFile(t *testing.T) { + t.Parallel() mockEngine := new(MockLintEngine) expectedIssues := []types.Issue{ { @@ -68,6 +68,7 @@ func TestProcessFile(t *testing.T) { } func TestProcessPath(t *testing.T) { + t.Parallel() logger, _ := zap.NewProduction() mockEngine := new(MockLintEngine) ctx := context.Background() @@ -115,6 +116,7 @@ func TestProcessPath(t *testing.T) { } func TestProcessFiles(t *testing.T) { + t.Parallel() logger, _ := zap.NewProduction() mockEngine := new(MockLintEngine) ctx := context.Background() @@ -161,6 +163,7 @@ func TestProcessFiles(t *testing.T) { } func TestHasDesiredExtension(t *testing.T) { + t.Parallel() assert.True(t, hasDesiredExtension("test.go")) assert.True(t, hasDesiredExtension("test.gno")) assert.False(t, hasDesiredExtension("test.txt")) @@ -168,6 +171,7 @@ func TestHasDesiredExtension(t *testing.T) { } func TestRunWithTimeout(t *testing.T) { + t.Parallel() ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() @@ -188,6 +192,7 @@ func TestRunWithTimeout(t *testing.T) { } func TestRunCFGAnalysis(t *testing.T) { + t.Parallel() logger, _ := zap.NewProduction() tempFile, err := os.CreateTemp("", "test*.go") diff --git a/formatter/fmt_test.go b/formatter/fmt_test.go index 2d11cf8..1748a72 100644 --- a/formatter/fmt_test.go +++ b/formatter/fmt_test.go @@ -2,19 +2,16 @@ package formatter import ( "go/token" - "os" - "path/filepath" - "strings" "testing" "github.com/gnoswap-labs/lint/internal" tt "github.com/gnoswap-labs/lint/internal/types" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestFormatIssuesWithArrows(t *testing.T) { - sourceCode := &internal.SourceCode{ + t.Parallel() + code := &internal.SourceCode{ Lines: []string{ "package main", "", @@ -56,7 +53,7 @@ error: empty-if ` - result := FormatIssuesWithArrows(issues, sourceCode) + result := FormatIssuesWithArrows(issues, code) assert.Equal(t, expected, result, "Formatted output does not match expected") @@ -92,7 +89,8 @@ error: empty-if } func TestFormatIssuesWithArrows_MultipleDigitsLineNumbers(t *testing.T) { - sourceCode := &internal.SourceCode{ + t.Parallel() + code := &internal.SourceCode{ Lines: []string{ "package main", "", @@ -151,13 +149,14 @@ error: example ` - result := FormatIssuesWithArrows(issues, sourceCode) + result := FormatIssuesWithArrows(issues, code) assert.Equal(t, expected, result, "Formatted output with multiple digit line numbers does not match expected") } func TestFormatIssuesWithArrows_UnnecessaryElse(t *testing.T) { - sourceCode := &internal.SourceCode{ + t.Parallel() + code := &internal.SourceCode{ Lines: []string{ "package main", "", @@ -203,102 +202,12 @@ The code inside the 'else' block has been moved outside, as it will only be exec ` - result := FormatIssuesWithArrows(issues, sourceCode) + result := FormatIssuesWithArrows(issues, code) assert.Equal(t, expected, result, "Formatted output does not match expected for unnecessary else") } -func TestIntegratedLintEngine(t *testing.T) { - t.Skip("skipping integrated lint engine test") - tests := []struct { - name string - code string - expected []string - }{ - { - name: "Detect unused issues", - code: ` -package main - -import ( - "fmt" -) - -func main() { - x := 1 - fmt.Println("Hello") -} -`, - expected: []string{ - "x declared and not used", - }, - }, - { - name: "Detect multiple issues", - code: ` -package main - -import ( - "fmt" - "strings" -) - -func main() { - x := 1 - y := "unused" - fmt.Println("Hello") -} -`, - expected: []string{ - "x declared and not used", - "y declared and not used", - `"strings" imported and not used`, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tmpDir, err := os.MkdirTemp("", "lint-test") - require.NoError(t, err) - defer os.RemoveAll(tmpDir) - - tmpfile := filepath.Join(tmpDir, "test.go") - err = os.WriteFile(tmpfile, []byte(tt.code), 0o644) - require.NoError(t, err) - - rootDir := "." - engine, err := internal.NewEngine(rootDir) - if err != nil { - t.Fatalf("unexpected error initializing lint engine: %v", err) - } - - issues, err := engine.Run(tmpfile) - require.NoError(t, err) - - assert.Equal(t, len(tt.expected), len(issues), "Number of issues doesn't match") - - for _, exp := range tt.expected { - found := false - for _, issue := range issues { - if strings.Contains(issue.Message, exp) { - found = true - break - } - } - assert.True(t, found, "Expected issue not found: "+exp) - } - - if len(issues) > 0 { - sourceCode, err := internal.ReadSourceCode(tmpfile) - require.NoError(t, err) - formattedIssues := FormatIssuesWithArrows(issues, sourceCode) - t.Logf("Found issues with arrows:\n%s", formattedIssues) - } - }) - } -} - func TestUnnecessaryTypeConversionFormatter(t *testing.T) { + t.Parallel() formatter := &UnnecessaryTypeConversionFormatter{} issue := tt.Issue{ @@ -339,9 +248,10 @@ Note: Unnecessary type conversions can make the code less readable and may sligh } func TestEmitFormatFormatter_Format(t *testing.T) { + t.Parallel() formatter := &EmitFormatFormatter{} - testCases := []struct { + tests := []struct { name string issue tt.Issue snippet *internal.SourceCode @@ -371,8 +281,7 @@ func TestEmitFormatFormatter_Format(t *testing.T) { "}", }, }, - expected: `error: emit-format - --> test.go + expected: ` | 3 | func main() { 4 | std.Emit("OwnershipChange", "newOwner", newOwner.String(), "oldOwner", oldOwner.String()) 5 | } @@ -390,10 +299,13 @@ Suggestion: }, } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - result := formatter.Format(tc.issue, tc.snippet) - assert.Equal(t, tc.expected, result) + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + // Format does not render lint rule and filename + result := formatter.Format(tt.issue, tt.snippet) + assert.Equal(t, tt.expected, result) }) } } diff --git a/formatter/format_emit.go b/formatter/format_emit.go index fd1ca64..a005139 100644 --- a/formatter/format_emit.go +++ b/formatter/format_emit.go @@ -19,6 +19,8 @@ func (f *EmitFormatFormatter) Format( maxLineNumWidth := calculateMaxLineNumWidth(issue.End.Line) padding := strings.Repeat(" ", maxLineNumWidth+1) + result.WriteString(lineStyle.Sprintf("%s|\n", padding)) + startLine := issue.Start.Line endLine := issue.End.Line for i := startLine; i <= endLine; i++ { diff --git a/internal/analysis/cfg/cfg_test.go b/internal/analysis/cfg/cfg_test.go index 1951e71..18a7cf2 100644 --- a/internal/analysis/cfg/cfg_test.go +++ b/internal/analysis/cfg/cfg_test.go @@ -12,6 +12,7 @@ import ( ) func TestFromStmts(t *testing.T) { + t.Parallel() src := ` package main func main() { @@ -67,6 +68,7 @@ func TestFromStmts(t *testing.T) { } func TestAnalyzeSpecificFunction(t *testing.T) { + t.Parallel() tests := []struct { name string src string @@ -122,7 +124,9 @@ func TestAnalyzeSpecificFunction(t *testing.T) { } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() fset := token.NewFileSet() file, err := parser.ParseFile(fset, "src.go", tt.src, 0) if err != nil { @@ -155,6 +159,7 @@ func TestAnalyzeSpecificFunction(t *testing.T) { } func TestCFG(t *testing.T) { + t.Parallel() tests := []struct { name string src string @@ -236,7 +241,9 @@ func TestCFG(t *testing.T) { } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() fset := token.NewFileSet() node, err := parser.ParseFile(fset, "src.go", tt.src, 0) if err != nil { @@ -275,6 +282,7 @@ func TestCFG(t *testing.T) { } func TestPrintDot2(t *testing.T) { + t.Parallel() src := ` package main func main() { @@ -337,18 +345,6 @@ digraph mgraph { } } -func normalizeDotOutput(dot string) string { - lines := strings.Split(dot, "\n") - var normalized []string - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if trimmed != "" { - normalized = append(normalized, trimmed) - } - } - return strings.Join(normalized, "\n") -} - // ref: https://github.com/godoctor/godoctor/blob/master/analysis/cfg/cfg_test.go#L500 const ( @@ -357,6 +353,7 @@ const ( ) func TestBlockStmt(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -374,6 +371,7 @@ func bar(i int) {}`) } func TestIfElseIfGoto(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -402,6 +400,7 @@ func TestIfElseIfGoto(t *testing.T) { } func TestDoubleForBreak(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -428,6 +427,7 @@ func TestDoubleForBreak(t *testing.T) { } func TestFor(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -451,6 +451,7 @@ func TestFor(t *testing.T) { } func TestForContinue(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -479,6 +480,7 @@ func TestForContinue(t *testing.T) { } func TestIfElse(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -501,6 +503,7 @@ func TestIfElse(t *testing.T) { } func TestIfNoElse(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -521,6 +524,7 @@ func TestIfNoElse(t *testing.T) { } func TestIfElseIf(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -548,6 +552,7 @@ func TestIfElseIf(t *testing.T) { } func TestDefer(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -573,6 +578,7 @@ func foo() { } func TestRange(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -605,6 +611,7 @@ func TestRange(t *testing.T) { } func TestTypeSwitchDefault(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -628,6 +635,7 @@ func TestTypeSwitchDefault(t *testing.T) { } func TestTypeSwitchNoDefault(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -650,6 +658,7 @@ func TestTypeSwitchNoDefault(t *testing.T) { } func TestSwitch(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -685,6 +694,7 @@ func TestSwitch(t *testing.T) { } func TestLabeledFallthrough(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -717,6 +727,7 @@ func TestLabeledFallthrough(t *testing.T) { } func TestSelectDefault(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -749,6 +760,7 @@ func TestSelectDefault(t *testing.T) { } func TestDietyExistence(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -940,6 +952,7 @@ func (c *CFGWrapper) expectPreds(t *testing.T, s int, exp ...int) { } func TestPrintDot(t *testing.T) { + t.Parallel() c := getWrapper(t, ` package main @@ -977,3 +990,15 @@ splines="ortho"; } } } + +func normalizeDotOutput(dot string) string { + lines := strings.Split(dot, "\n") + var normalized []string + for _, line := range lines { + trimmed := strings.TrimSpace(line) + if trimmed != "" { + normalized = append(normalized, trimmed) + } + } + return strings.Join(normalized, "\n") +} diff --git a/internal/fixer_test.go b/internal/fixer_test.go index 8d0eb2a..11f0cf7 100644 --- a/internal/fixer_test.go +++ b/internal/fixer_test.go @@ -8,7 +8,8 @@ import ( ) func TestRemoveUnnecessaryElse(t *testing.T) { - testCases := []struct { + t.Parallel() + tests := []struct { name string input string expected string @@ -68,11 +69,13 @@ if z { }, } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - improved, err := RemoveUnnecessaryElse(tc.input) + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + improved, err := RemoveUnnecessaryElse(tt.input) require.NoError(t, err) - assert.Equal(t, tc.expected, improved, "Improved code does not match expected output") + assert.Equal(t, tt.expected, improved, "Improved code does not match expected output") }) } } diff --git a/internal/lints/detect_cycle_test.go b/internal/lints/detect_cycle_test.go index 8261ef6..fe41729 100644 --- a/internal/lints/detect_cycle_test.go +++ b/internal/lints/detect_cycle_test.go @@ -7,6 +7,7 @@ import ( ) func TestDetectCycle(t *testing.T) { + t.Parallel() src := ` package main diff --git a/internal/lints/gno_analyzer_test.go b/internal/lints/gno_analyzer_test.go index 69ba337..8403cbd 100644 --- a/internal/lints/gno_analyzer_test.go +++ b/internal/lints/gno_analyzer_test.go @@ -10,6 +10,7 @@ import ( ) func TestRunLinter(t *testing.T) { + t.Parallel() _, current, _, ok := runtime.Caller(0) require.True(t, ok) @@ -63,28 +64,30 @@ func TestRunLinter(t *testing.T) { }, } - for _, tc := range tests { - t.Run(filepath.Base(tc.filename), func(t *testing.T) { - file, deps, err := analyzeFile(tc.filename) + for _, tt := range tests { + tt := tt + t.Run(filepath.Base(tt.filename), func(t *testing.T) { + t.Parallel() + file, deps, err := analyzeFile(tt.filename) require.NoError(t, err) require.NotNil(t, file) issues := runGnoPackageLinter(file, deps) - assert.Equal(t, len(tc.expectedIssues), len(issues), "Number of issues doesn't match expected for %s", tc.filename) + assert.Equal(t, len(tt.expectedIssues), len(issues), "Number of issues doesn't match expected for %s", tt.filename) - for i, expected := range tc.expectedIssues { - assert.Equal(t, expected.rule, issues[i].Rule, "Rule doesn't match for issue %d in %s", i, tc.filename) - assert.Contains(t, issues[i].Message, expected.message, "Message doesn't match for issue %d in %s", i, tc.filename) + for i, expected := range tt.expectedIssues { + assert.Equal(t, expected.rule, issues[i].Rule, "Rule doesn't match for issue %d in %s", i, tt.filename) + assert.Contains(t, issues[i].Message, expected.message, "Message doesn't match for issue %d in %s", i, tt.filename) } - for importPath, expected := range tc.expectedDeps { + for importPath, expected := range tt.expectedDeps { dep, exists := deps[importPath] - assert.True(t, exists, "Dependency %s not found in %s", importPath, tc.filename) + assert.True(t, exists, "Dependency %s not found in %s", importPath, tt.filename) if exists { - assert.Equal(t, expected.isGno, dep.IsGno, "IsGno mismatch for %s in %s", importPath, tc.filename) - assert.Equal(t, expected.isUsed, dep.IsUsed, "IsUsed mismatch for %s in %s", importPath, tc.filename) - assert.Equal(t, expected.isIgnored, dep.IsIgnored, "IsIgnored mismatch for %s in %s", importPath, tc.filename) + assert.Equal(t, expected.isGno, dep.IsGno, "IsGno mismatch for %s in %s", importPath, tt.filename) + assert.Equal(t, expected.isUsed, dep.IsUsed, "IsUsed mismatch for %s in %s", importPath, tt.filename) + assert.Equal(t, expected.isIgnored, dep.IsIgnored, "IsIgnored mismatch for %s in %s", importPath, tt.filename) } } }) diff --git a/internal/lints/lint_test.go b/internal/lints/lint_test.go index 5dcf2aa..6eb63bd 100644 --- a/internal/lints/lint_test.go +++ b/internal/lints/lint_test.go @@ -15,6 +15,7 @@ import ( ) func TestDetectUnnecessaryElse(t *testing.T) { + t.Parallel() tests := []struct { name string code string @@ -73,7 +74,9 @@ func example2() int { } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "lint-test") require.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -101,6 +104,7 @@ func example2() int { } func TestDetectUnnecessarySliceLength(t *testing.T) { + t.Parallel() baseMsg := "unnecessary use of len() in slice expression, can be simplified" tests := []struct { name string @@ -159,7 +163,9 @@ func main() { } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "lint-test") require.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -191,6 +197,7 @@ func main() { } func TestDetectUnnecessaryTypeConversion(t *testing.T) { + t.Parallel() tests := []struct { name string code string @@ -260,7 +267,9 @@ func example() { } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "lint-test") require.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -288,6 +297,7 @@ func example() { } func TestDetectLoopAllocation(t *testing.T) { + t.Parallel() tests := []struct { name string code string @@ -377,7 +387,9 @@ func main() { } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "test") require.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -402,6 +414,7 @@ func main() { } func TestDetectEmitFormat(t *testing.T) { + t.Parallel() _, current, _, _ := runtime.Caller(0) testDir := filepath.Join(filepath.Dir(current), "..", "..", "testdata", "emit") @@ -433,7 +446,9 @@ func TestDetectEmitFormat(t *testing.T) { } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() path := filepath.Join(testDir, tt.filename) content, err := os.ReadFile(path) require.NoError(t, err) @@ -463,6 +478,7 @@ func TestDetectEmitFormat(t *testing.T) { } func TestFormatEmitCall(t *testing.T) { + t.Parallel() tests := []struct { name string input string @@ -498,7 +514,9 @@ func TestFormatEmitCall(t *testing.T) { } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() expr, err := parser.ParseExpr(tt.input) assert.NoError(t, err) @@ -512,6 +530,7 @@ func TestFormatEmitCall(t *testing.T) { } func TestDetectSliceBoundCheck(t *testing.T) { + t.Parallel() tests := []struct { name string code string @@ -572,7 +591,9 @@ func main() { } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() fset := token.NewFileSet() node, err := parser.ParseFile(fset, "", tt.code, 0) if err != nil { diff --git a/internal/symbol_table_test.go b/internal/symbol_table_test.go index 2ce3136..47d031c 100644 --- a/internal/symbol_table_test.go +++ b/internal/symbol_table_test.go @@ -12,6 +12,7 @@ import ( ) func TestSymbolTable(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "symboltable-test") require.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -38,7 +39,7 @@ func AnotherFunc() {} st, err := BuildSymbolTable(tmpDir) require.NoError(t, err) - testCases := []struct { + tests := []struct { symbol string expected bool symType SymbolType @@ -53,18 +54,20 @@ func AnotherFunc() {} {"test.NonExistentSymbol", false, Function, ""}, } - for _, tc := range testCases { - t.Run(tc.symbol, func(t *testing.T) { - assert.Equal(t, tc.expected, st.IsDefined(tc.symbol)) + for _, tt := range tests { + tt := tt + t.Run(tt.symbol, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.expected, st.IsDefined(tt.symbol)) - if tc.expected { - info, exists := st.GetSymbolInfo(tc.symbol) + if tt.expected { + info, exists := st.GetSymbolInfo(tt.symbol) assert.True(t, exists) - assert.Equal(t, tc.symType, info.Type) - assert.Equal(t, tc.filePath, info.FilePath) + assert.Equal(t, tt.symType, info.Type) + assert.Equal(t, tt.filePath, info.FilePath) assert.Equal(t, "test", info.Package) } else { - _, exists := st.GetSymbolInfo(tc.symbol) + _, exists := st.GetSymbolInfo(tt.symbol) assert.False(t, exists) } }) @@ -78,6 +81,7 @@ func AnotherFunc() {} } func TestConcurrentSymbolTable(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "concurrent-symboltable-test") require.NoError(t, err) defer os.RemoveAll(tmpDir)