Skip to content

Commit ce64cf0

Browse files
committed
Improve memory efficiency and save test outputs to files
1 parent bc5b5a6 commit ce64cf0

File tree

1 file changed

+36
-26
lines changed

1 file changed

+36
-26
lines changed

tools/flakeguard/runner/runner.go

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package runner
22

33
import (
44
"bufio"
5-
"bytes"
65
"encoding/json"
76
"errors"
87
"fmt"
98
"log"
9+
"os"
1010
"os/exec"
1111
"strings"
1212

@@ -27,36 +27,35 @@ type Runner struct {
2727
// RunTests executes the tests for each provided package and aggregates all results.
2828
// It returns all test results and any error encountered during testing.
2929
func (r *Runner) RunTests() ([]reports.TestResult, error) {
30-
var jsonOutputs [][]byte
30+
var jsonFilePaths []string
3131
packages := r.SelectedTestPackages
3232
if r.RunAllTestPackages {
3333
packages = []string{"./..."}
3434
}
3535

3636
for _, p := range packages {
3737
for i := 0; i < r.RunCount; i++ {
38-
jsonOutput, passed, err := r.runTests(p)
38+
jsonFilePath, passed, err := r.runTests(p)
3939
if err != nil {
4040
return nil, fmt.Errorf("failed to run tests in package %s: %w", p, err)
4141
}
42-
jsonOutputs = append(jsonOutputs, jsonOutput)
42+
jsonFilePaths = append(jsonFilePaths, jsonFilePath)
4343
if !passed && r.FailFast {
4444
break
4545
}
4646
}
4747
}
4848

49-
return parseTestResults(jsonOutputs)
49+
return parseTestResults(jsonFilePaths)
5050
}
5151

5252
type exitCoder interface {
5353
ExitCode() int
5454
}
5555

56-
// runTestPackage executes the test command for a single test package.
57-
// It returns the command output, a boolean indicating success, and any error encountered.
58-
func (r *Runner) runTests(packageName string) ([]byte, bool, error) {
59-
args := []string{"test", packageName, "-json", "-count=1"} // Enable JSON output for parsing
56+
// runTests runs the tests for a given package and returns the path to the output file.
57+
func (r *Runner) runTests(packageName string) (string, bool, error) {
58+
args := []string{"test", packageName, "-json", "-count=1"} // Enable JSON output
6059
if r.UseRace {
6160
args = append(args, "-race")
6261
}
@@ -68,36 +67,47 @@ func (r *Runner) runTests(packageName string) ([]byte, bool, error) {
6867
if r.Verbose {
6968
log.Printf("Running command: go %s\n", strings.Join(args, " "))
7069
}
70+
71+
// Create a temporary file to store the output
72+
tmpFile, err := os.CreateTemp("", "test-output-*.json")
73+
if err != nil {
74+
return "", false, fmt.Errorf("failed to create temp file: %w", err)
75+
}
76+
defer tmpFile.Close()
77+
78+
// Run the command with output directed to the file
7179
cmd := exec.Command("go", args...)
7280
cmd.Dir = r.ProjectPath
81+
cmd.Stdout = tmpFile
82+
cmd.Stderr = tmpFile
7383

74-
var out bytes.Buffer
75-
cmd.Stdout = &out
76-
cmd.Stderr = &out
77-
78-
// Run the command
79-
err := cmd.Run()
84+
err = cmd.Run()
8085
if err != nil {
8186
var exErr exitCoder
8287
// Check if the error is due to a non-zero exit code
8388
if errors.As(err, &exErr) && exErr.ExitCode() == 0 {
84-
return nil, false, fmt.Errorf("test command failed at %s: %w", packageName, err)
89+
return "", false, fmt.Errorf("test command failed at %s: %w", packageName, err)
8590
}
86-
return out.Bytes(), false, nil // Test failed
91+
return tmpFile.Name(), false, nil // Test failed
8792
}
8893

89-
return out.Bytes(), true, nil // Test succeeded
94+
return tmpFile.Name(), true, nil // Test succeeded
9095
}
9196

92-
// parseTestResults analyzes multiple JSON outputs from 'go test -json' commands to determine test results.
93-
// It accepts a slice of []byte where each []byte represents a separate JSON output from a test run.
94-
// This function aggregates results across multiple test runs, summing runs and passes for each test.
95-
func parseTestResults(datas [][]byte) ([]reports.TestResult, error) {
97+
// parseTestResults reads the test output files and returns the parsed test results.
98+
func parseTestResults(filePaths []string) ([]reports.TestResult, error) {
9699
testDetails := make(map[string]*reports.TestResult) // Holds run, pass counts, and other details for each test
97100

98-
// Process each data set
99-
for _, data := range datas {
100-
scanner := bufio.NewScanner(bytes.NewReader(data))
101+
// Process each file
102+
for _, filePath := range filePaths {
103+
file, err := os.Open(filePath)
104+
if err != nil {
105+
return nil, fmt.Errorf("failed to open test output file: %w", err)
106+
}
107+
defer os.Remove(filePath) // Clean up file after parsing
108+
defer file.Close()
109+
110+
scanner := bufio.NewScanner(file)
101111
for scanner.Scan() {
102112
var entry struct {
103113
Action string `json:"Action"`
@@ -146,7 +156,7 @@ func parseTestResults(datas [][]byte) ([]reports.TestResult, error) {
146156
}
147157

148158
if err := scanner.Err(); err != nil {
149-
return nil, fmt.Errorf("reading standard input: %w", err)
159+
return nil, fmt.Errorf("reading test output file: %w", err)
150160
}
151161
}
152162

0 commit comments

Comments
 (0)