1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-08-08 22:36:49 +02:00

Support passing -race flag to integration tests (#3019)

This commit is contained in:
Stefan Haller
2023-09-25 09:14:38 +02:00
committed by GitHub
6 changed files with 92 additions and 45 deletions

View File

@@ -44,7 +44,7 @@ update-cheatsheet:
# For more details about integration test, see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md. # For more details about integration test, see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md.
.PHONY: integration-test-tui .PHONY: integration-test-tui
integration-test-tui: integration-test-tui:
go run cmd/integration_test/main.go tui go run cmd/integration_test/main.go tui $(filter-out $@,$(MAKECMDGOALS))
.PHONY: integration-test-cli .PHONY: integration-test-cli
integration-test-cli: integration-test-cli:

View File

@@ -26,6 +26,29 @@ Usage:
> go run cmd/integration_test/main.go help > go run cmd/integration_test/main.go help
` `
type flagInfo struct {
name string // name of the flag; can be used with "-" or "--"
flag *bool // a pointer to the variable that should be set to true when this flag is passed
}
// Takes the args that you want to parse (excluding the program name and any
// subcommands), and returns the remaining args with the flags removed
func parseFlags(args []string, flags []flagInfo) []string {
outer:
for len(args) > 0 {
for _, f := range flags {
if args[0] == "-"+f.name || args[0] == "--"+f.name {
*f.flag = true
args = args[1:]
continue outer
}
}
break
}
return args
}
func main() { func main() {
if len(os.Args) < 2 { if len(os.Args) < 2 {
log.Fatal(usage) log.Fatal(usage)
@@ -35,27 +58,26 @@ func main() {
case "help": case "help":
fmt.Println(usage) fmt.Println(usage)
case "cli": case "cli":
testNames := os.Args[2:]
slow := false slow := false
sandbox := false sandbox := false
waitForDebugger := false waitForDebugger := false
// get the next arg if it's --slow raceDetector := false
if len(os.Args) > 2 { testNames := parseFlags(os.Args[2:], []flagInfo{
if os.Args[2] == "--slow" || os.Args[2] == "-slow" { {"slow", &slow},
testNames = os.Args[3:] {"sandbox", &sandbox},
slow = true {"debug", &waitForDebugger},
} else if os.Args[2] == "--sandbox" || os.Args[2] == "-sandbox" { {"race", &raceDetector},
testNames = os.Args[3:] })
sandbox = true clients.RunCLI(testNames, slow, sandbox, waitForDebugger, raceDetector)
} else if os.Args[2] == "--debug" || os.Args[2] == "-debug" {
testNames = os.Args[3:]
waitForDebugger = true
}
}
clients.RunCLI(testNames, slow, sandbox, waitForDebugger)
case "tui": case "tui":
clients.RunTUI() raceDetector := false
remainingArgs := parseFlags(os.Args[2:], []flagInfo{
{"race", &raceDetector},
})
if len(remainingArgs) > 0 {
log.Fatal("tui only supports the -race argument.")
}
clients.RunTUI(raceDetector)
default: default:
log.Fatal(usage) log.Fatal(usage)
} }

View File

@@ -23,7 +23,7 @@ import (
// If invoked directly, you can specify tests to run by passing their names as positional arguments // If invoked directly, you can specify tests to run by passing their names as positional arguments
func RunCLI(testNames []string, slow bool, sandbox bool, waitForDebugger bool) { func RunCLI(testNames []string, slow bool, sandbox bool, waitForDebugger bool, raceDetector bool) {
inputDelay := tryConvert(os.Getenv("INPUT_DELAY"), 0) inputDelay := tryConvert(os.Getenv("INPUT_DELAY"), 0)
if slow { if slow {
inputDelay = SLOW_INPUT_DELAY inputDelay = SLOW_INPUT_DELAY
@@ -36,6 +36,7 @@ func RunCLI(testNames []string, slow bool, sandbox bool, waitForDebugger bool) {
runAndPrintFatalError, runAndPrintFatalError,
sandbox, sandbox,
waitForDebugger, waitForDebugger,
raceDetector,
inputDelay, inputDelay,
1, 1,
) )
@@ -87,12 +88,15 @@ outer:
return testsToRun return testsToRun
} }
func runCmdInTerminal(cmd *exec.Cmd) error { func runCmdInTerminal(cmd *exec.Cmd) (int, error) {
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
return cmd.Run() if err := cmd.Start(); err != nil {
return -1, err
}
return cmd.Process.Pid, cmd.Wait()
} }
func tryConvert(numStr string, defaultVal int) int { func tryConvert(numStr string, defaultVal int) int {

View File

@@ -27,6 +27,7 @@ func TestIntegration(t *testing.T) {
parallelTotal := tryConvert(os.Getenv("PARALLEL_TOTAL"), 1) parallelTotal := tryConvert(os.Getenv("PARALLEL_TOTAL"), 1)
parallelIndex := tryConvert(os.Getenv("PARALLEL_INDEX"), 0) parallelIndex := tryConvert(os.Getenv("PARALLEL_INDEX"), 0)
raceDetector := os.Getenv("LAZYGIT_RACE_DETECTOR") != ""
testNumber := 0 testNumber := 0
err := components.RunTests( err := components.RunTests(
@@ -53,6 +54,7 @@ func TestIntegration(t *testing.T) {
}, },
false, false,
false, false,
raceDetector,
0, 0,
// Allow two attempts at each test to get around flakiness // Allow two attempts at each test to get around flakiness
2, 2,
@@ -61,7 +63,7 @@ func TestIntegration(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
} }
func runCmdHeadless(cmd *exec.Cmd) error { func runCmdHeadless(cmd *exec.Cmd) (int, error) {
cmd.Env = append( cmd.Env = append(
cmd.Env, cmd.Env,
"HEADLESS=true", "HEADLESS=true",
@@ -79,15 +81,16 @@ func runCmdHeadless(cmd *exec.Cmd) error {
// running other commands in a pty. // running other commands in a pty.
f, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: 300, Cols: 300}) f, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: 300, Cols: 300})
if err != nil { if err != nil {
return err return -1, err
} }
_, _ = io.Copy(io.Discard, f) _, _ = io.Copy(io.Discard, f)
if cmd.Wait() != nil { if cmd.Wait() != nil {
_ = f.Close()
// return an error with the stderr output // return an error with the stderr output
return errors.New(stderr.String()) return cmd.Process.Pid, errors.New(stderr.String())
} }
return f.Close() return cmd.Process.Pid, f.Close()
} }

View File

@@ -21,7 +21,7 @@ import (
var SLOW_INPUT_DELAY = 600 var SLOW_INPUT_DELAY = 600
func RunTUI() { func RunTUI(raceDetector bool) {
rootDir := utils.GetLazyRootDirectory() rootDir := utils.GetLazyRootDirectory()
testDir := filepath.Join(rootDir, "test", "integration") testDir := filepath.Join(rootDir, "test", "integration")
@@ -85,7 +85,7 @@ func RunTUI() {
return nil return nil
} }
suspendAndRunTest(currentTest, true, false, 0) suspendAndRunTest(currentTest, true, false, raceDetector, 0)
return nil return nil
}); err != nil { }); err != nil {
@@ -98,7 +98,7 @@ func RunTUI() {
return nil return nil
} }
suspendAndRunTest(currentTest, false, false, 0) suspendAndRunTest(currentTest, false, false, raceDetector, 0)
return nil return nil
}); err != nil { }); err != nil {
@@ -111,7 +111,7 @@ func RunTUI() {
return nil return nil
} }
suspendAndRunTest(currentTest, false, false, SLOW_INPUT_DELAY) suspendAndRunTest(currentTest, false, false, raceDetector, SLOW_INPUT_DELAY)
return nil return nil
}); err != nil { }); err != nil {
@@ -124,7 +124,7 @@ func RunTUI() {
return nil return nil
} }
suspendAndRunTest(currentTest, false, true, 0) suspendAndRunTest(currentTest, false, true, raceDetector, 0)
return nil return nil
}); err != nil { }); err != nil {
@@ -284,12 +284,12 @@ func (self *app) wrapEditor(f func(v *gocui.View, key gocui.Key, ch rune, mod go
} }
} }
func suspendAndRunTest(test *components.IntegrationTest, sandbox bool, waitForDebugger bool, inputDelay int) { func suspendAndRunTest(test *components.IntegrationTest, sandbox bool, waitForDebugger bool, raceDetector bool, inputDelay int) {
if err := gocui.Screen.Suspend(); err != nil { if err := gocui.Screen.Suspend(); err != nil {
panic(err) panic(err)
} }
runTuiTest(test, sandbox, waitForDebugger, inputDelay) runTuiTest(test, sandbox, waitForDebugger, raceDetector, inputDelay)
fmt.Fprintf(os.Stdout, "\n%s", style.FgGreen.Sprint("press enter to return")) fmt.Fprintf(os.Stdout, "\n%s", style.FgGreen.Sprint("press enter to return"))
fmt.Scanln() // wait for enter press fmt.Scanln() // wait for enter press
@@ -384,7 +384,7 @@ func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit return gocui.ErrQuit
} }
func runTuiTest(test *components.IntegrationTest, sandbox bool, waitForDebugger bool, inputDelay int) { func runTuiTest(test *components.IntegrationTest, sandbox bool, waitForDebugger bool, raceDetector bool, inputDelay int) {
err := components.RunTests( err := components.RunTests(
[]*components.IntegrationTest{test}, []*components.IntegrationTest{test},
log.Printf, log.Printf,
@@ -392,6 +392,7 @@ func runTuiTest(test *components.IntegrationTest, sandbox bool, waitForDebugger
runAndPrintError, runAndPrintError,
sandbox, sandbox,
waitForDebugger, waitForDebugger,
raceDetector,
inputDelay, inputDelay,
1, 1,
) )

View File

@@ -26,10 +26,11 @@ const (
func RunTests( func RunTests(
tests []*IntegrationTest, tests []*IntegrationTest,
logf func(format string, formatArgs ...interface{}), logf func(format string, formatArgs ...interface{}),
runCmd func(cmd *exec.Cmd) error, runCmd func(cmd *exec.Cmd) (int, error),
testWrapper func(test *IntegrationTest, f func() error), testWrapper func(test *IntegrationTest, f func() error),
sandbox bool, sandbox bool,
waitForDebugger bool, waitForDebugger bool,
raceDetector bool,
inputDelay int, inputDelay int,
maxAttempts int, maxAttempts int,
) error { ) error {
@@ -41,7 +42,7 @@ func RunTests(
testDir := filepath.Join(projectRootDir, "test", "_results") testDir := filepath.Join(projectRootDir, "test", "_results")
if err := buildLazygit(); err != nil { if err := buildLazygit(raceDetector); err != nil {
return err return err
} }
@@ -59,7 +60,7 @@ func RunTests(
) )
for i := 0; i < maxAttempts; i++ { for i := 0; i < maxAttempts; i++ {
err := runTest(test, paths, projectRootDir, logf, runCmd, sandbox, waitForDebugger, inputDelay, gitVersion) err := runTest(test, paths, projectRootDir, logf, runCmd, sandbox, waitForDebugger, raceDetector, inputDelay, gitVersion)
if err != nil { if err != nil {
if i == maxAttempts-1 { if i == maxAttempts-1 {
return err return err
@@ -82,9 +83,10 @@ func runTest(
paths Paths, paths Paths,
projectRootDir string, projectRootDir string,
logf func(format string, formatArgs ...interface{}), logf func(format string, formatArgs ...interface{}),
runCmd func(cmd *exec.Cmd) error, runCmd func(cmd *exec.Cmd) (int, error),
sandbox bool, sandbox bool,
waitForDebugger bool, waitForDebugger bool,
raceDetector bool,
inputDelay int, inputDelay int,
gitVersion *git_commands.GitVersion, gitVersion *git_commands.GitVersion,
) error { ) error {
@@ -107,12 +109,17 @@ func runTest(
return err return err
} }
err = runCmd(cmd) pid, err := runCmd(cmd)
if err != nil {
return err // Print race detector log regardless of the command's exit status
if raceDetector {
logPath := fmt.Sprintf("%s.%d", raceDetectorLogsPath(), pid)
if bytes, err := os.ReadFile(logPath); err == nil {
logf("Race detector log:\n" + string(bytes))
}
} }
return nil return err
} }
func prepareTestDir( func prepareTestDir(
@@ -131,15 +138,18 @@ func prepareTestDir(
return createFixture(test, paths, rootDir) return createFixture(test, paths, rootDir)
} }
func buildLazygit() error { func buildLazygit(raceDetector bool) error {
// // TODO: remove this line! // // TODO: remove this line!
// // skipping this because I'm not making changes to the app code atm. // // skipping this because I'm not making changes to the app code atm.
// return nil // return nil
args := []string{"go", "build"}
if raceDetector {
args = append(args, "-race")
}
args = append(args, "-o", tempLazygitPath(), filepath.FromSlash("pkg/integration/clients/injector/main.go"))
osCommand := oscommands.NewDummyOSCommand() osCommand := oscommands.NewDummyOSCommand()
return osCommand.Cmd.New([]string{ return osCommand.Cmd.New(args).Run()
"go", "build", "-o", tempLazygitPath(), filepath.FromSlash("pkg/integration/clients/injector/main.go"),
}).Run()
} }
func createFixture(test *IntegrationTest, paths Paths, rootDir string) error { func createFixture(test *IntegrationTest, paths Paths, rootDir string) error {
@@ -202,6 +212,9 @@ func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string, sandb
if waitForDebugger { if waitForDebugger {
cmdObj.AddEnvVars("WAIT_FOR_DEBUGGER=true") cmdObj.AddEnvVars("WAIT_FOR_DEBUGGER=true")
} }
// Set a race detector log path only to avoid spamming the terminal with the
// logs. We are not showing this anywhere yet.
cmdObj.AddEnvVars(fmt.Sprintf("GORACE=log_path=%s", raceDetectorLogsPath()))
if test.ExtraEnvVars() != nil { if test.ExtraEnvVars() != nil {
for key, value := range test.ExtraEnvVars() { for key, value := range test.ExtraEnvVars() {
cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", key, value)) cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", key, value))
@@ -221,6 +234,10 @@ func tempLazygitPath() string {
return filepath.Join("/tmp", "lazygit", "test_lazygit") return filepath.Join("/tmp", "lazygit", "test_lazygit")
} }
func raceDetectorLogsPath() string {
return filepath.Join("/tmp", "lazygit", "race_log")
}
func findOrCreateDir(path string) { func findOrCreateDir(path string) {
_, err := os.Stat(path) _, err := os.Stat(path)
if err != nil { if err != nil {