Log per-test durations during integration tests

To spot slow or anomalous tests across CI runs, record each test's run
duration when LAZYGIT_TEST_TIMING is set (to a file path);
run_integration_tests.sh prints them at the end, sorted by slowest
first. CI sets it for all integration jobs.

The harness appends to a file rather than writing to stdout/stderr
because `go test` captures those and only surfaces them with -v, which
would drown the signal in every test's verbose logs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller 2026-07-10 17:28:19 +02:00
parent 03a914c04c
commit b6b5436d57
4 changed files with 48 additions and 1 deletions

View file

@ -109,6 +109,9 @@ jobs:
# by default on the Linux runner, but we set it explicitly to be safe.
LAZYGIT_RACE_DETECTOR: ${{ matrix.race && '1' || '' }}
CGO_ENABLED: ${{ matrix.race && '1' || '' }}
# Append each test's duration to this file; run_integration_tests.sh
# prints the slowest at the end, to spot slow/anomalous tests.
LAZYGIT_TEST_TIMING: /tmp/test_timings.txt
run: |
mkdir -p /tmp/code_coverage
./scripts/run_integration_tests.sh

View file

@ -30,6 +30,7 @@ func TestIntegration(t *testing.T) {
parallelTotal := tryConvert(os.Getenv("PARALLEL_TOTAL"), 1)
parallelIndex := tryConvert(os.Getenv("PARALLEL_INDEX"), 0)
raceDetector := os.Getenv("LAZYGIT_RACE_DETECTOR") != ""
logTimingsPath := os.Getenv("LAZYGIT_TEST_TIMING")
// LAZYGIT_GOCOVERDIR is the directory where we write coverage files to. If this directory
// is defined, go binaries built with the -cover flag will write coverage files to
// to it.
@ -58,7 +59,8 @@ func TestIntegration(t *testing.T) {
CodeCoverageDir: codeCoverageDir,
InputDelay: 0,
// Allow two attempts at each test to get around flakiness
MaxAttempts: 1,
MaxAttempts: 1,
LogTimingsPath: logTimingsPath,
})
assert.NoError(t, err)

View file

@ -5,6 +5,8 @@ import (
"os"
"os/exec"
"path/filepath"
"sync"
"time"
lazycoreUtils "github.com/jesseduffield/lazycore/pkg/utils"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
@ -24,6 +26,12 @@ type RunTestArgs struct {
CodeCoverageDir string
InputDelay int
MaxAttempts int
// If set, each test's run duration is appended to this file (as
// "<seconds> <test name>"). run_integration_tests.sh prints the slowest at
// the end, so slow or anomalous tests can be spotted across CI runs. We
// write to a file rather than stdout/stderr because `go test` captures
// those and only shows them with -v. Empty disables it.
LogTimingsPath string
}
// This function lets you run tests either from within `go test` or from a regular binary.
@ -47,6 +55,11 @@ func RunTests(args RunTestArgs) error {
return err
}
// Start each run with a fresh timings file (see RunTestArgs.LogTimingsPath).
if args.LogTimingsPath != "" {
_ = os.Remove(args.LogTimingsPath)
}
for _, test := range args.Tests {
args.TestWrapper(test, func() error {
paths := NewPaths(
@ -99,7 +112,11 @@ func runTest(
return err
}
start := time.Now()
pid, err := args.RunCmd(cmd)
if args.LogTimingsPath != "" {
logTestTiming(args.LogTimingsPath, test.Name(), time.Since(start))
}
// Print race detector log regardless of the command's exit status
if args.RaceDetector {
@ -112,6 +129,23 @@ func runTest(
return err
}
// timingsMutex serializes appends to the timings file, since tests run in
// parallel.
var timingsMutex sync.Mutex
func logTestTiming(path, name string, duration time.Duration) {
timingsMutex.Lock()
defer timingsMutex.Unlock()
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return
}
defer f.Close()
fmt.Fprintf(f, "%.2f %s\n", duration.Seconds(), name)
}
func prepareTestDir(
test *IntegrationTest,
paths Paths,

View file

@ -37,4 +37,12 @@ if test -f ~/.gitconfig.lazygit.bak; then
mv ~/.gitconfig.lazygit.bak ~/.gitconfig
fi
# If per-test timings were collected (LAZYGIT_TEST_TIMING points at the file the
# harness appends to), print them sorted by slowest first so they show up in the
# CI log.
if [ -n "$LAZYGIT_TEST_TIMING" ] && [ -f "$LAZYGIT_TEST_TIMING" ]; then
echo "Test timings (seconds):"
sort -rn "$LAZYGIT_TEST_TIMING"
fi
exit $EXITCODE