diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/gui/assert.go | 88 | ||||
-rw-r--r-- | pkg/gui/gui.go | 31 | ||||
-rw-r--r-- | pkg/gui/gui_test.go | 34 | ||||
-rw-r--r-- | pkg/gui/input.go | 93 | ||||
-rw-r--r-- | pkg/gui/old_gui_test.go | 76 | ||||
-rw-r--r-- | pkg/gui/test_mode.go | 63 | ||||
-rw-r--r-- | pkg/integration/env.go | 46 | ||||
-rw-r--r-- | pkg/integration/integration.go | 519 | ||||
-rw-r--r-- | pkg/integration/integration_old.go | 564 | ||||
-rw-r--r-- | pkg/integration/integration_tests/branch/suggestions.go | 40 | ||||
-rw-r--r-- | pkg/integration/integration_tests/commit/commit.go | 32 | ||||
-rw-r--r-- | pkg/integration/integration_tests/commit/new_branch.go | 38 | ||||
-rw-r--r-- | pkg/integration/integration_tests/tests.go | 16 | ||||
-rw-r--r-- | pkg/integration/recording.go (renamed from pkg/gui/recording.go) | 27 | ||||
-rw-r--r-- | pkg/integration/shell.go | 53 | ||||
-rw-r--r-- | pkg/integration/types/types.go | 152 | ||||
-rw-r--r-- | pkg/utils/utils.go | 7 |
17 files changed, 1361 insertions, 518 deletions
diff --git a/pkg/gui/assert.go b/pkg/gui/assert.go new file mode 100644 index 000000000..43ecc26d8 --- /dev/null +++ b/pkg/gui/assert.go @@ -0,0 +1,88 @@ +package gui + +import ( + "fmt" + "time" + + "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +type AssertImpl struct { + gui *Gui +} + +var _ types.Assert = &AssertImpl{} + +func (self *AssertImpl) WorkingTreeFileCount(expectedCount int) { + self.assertWithRetries(func() (bool, string) { + actualCount := len(self.gui.State.Model.Files) + + return actualCount == expectedCount, fmt.Sprintf( + "Expected %d changed working tree files, but got %d", + expectedCount, actualCount, + ) + }) +} + +func (self *AssertImpl) CommitCount(expectedCount int) { + self.assertWithRetries(func() (bool, string) { + actualCount := len(self.gui.State.Model.Commits) + + return actualCount == expectedCount, fmt.Sprintf( + "Expected %d commits present, but got %d", + expectedCount, actualCount, + ) + }) +} + +func (self *AssertImpl) HeadCommitMessage(expectedMessage string) { + self.assertWithRetries(func() (bool, string) { + if len(self.gui.State.Model.Commits) == 0 { + return false, "Expected at least one commit to be present" + } + + headCommit := self.gui.State.Model.Commits[0] + if headCommit.Name != expectedMessage { + return false, fmt.Sprintf( + "Expected commit message to be '%s', but got '%s'", + expectedMessage, headCommit.Name, + ) + } + + return true, "" + }) +} + +func (self *AssertImpl) CurrentViewName(expectedViewName string) { + self.assertWithRetries(func() (bool, string) { + actual := self.gui.currentViewName() + return actual == expectedViewName, fmt.Sprintf("Expected current view name to be '%s', but got '%s'", expectedViewName, actual) + }) +} + +func (self *AssertImpl) CurrentBranchName(expectedViewName string) { + self.assertWithRetries(func() (bool, string) { + actual := self.gui.helpers.Refs.GetCheckedOutRef().Name + return actual == expectedViewName, fmt.Sprintf("Expected current branch name to be '%s', but got '%s'", expectedViewName, actual) + }) +} + +func (self *AssertImpl) assertWithRetries(test func() (bool, string)) { + waitTimes := []int{0, 100, 200, 400, 800, 1600} + + var message string + for _, waitTime := range waitTimes { + time.Sleep(time.Duration(waitTime) * time.Millisecond) + + var ok bool + ok, message = test() + if ok { + return + } + } + + self.gui.g.Close() + // need to give the gui time to close + time.Sleep(time.Millisecond * 100) + panic(message) +} diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index bb5821a57..f7c8926f5 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -31,6 +31,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/services/custom_commands" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/jesseduffield/lazygit/pkg/integration" "github.com/jesseduffield/lazygit/pkg/tasks" "github.com/jesseduffield/lazygit/pkg/theme" "github.com/jesseduffield/lazygit/pkg/updates" @@ -418,12 +419,14 @@ var RuneReplacements = map[rune]string{ } func (gui *Gui) initGocui(headless bool) (*gocui.Gui, error) { - recordEvents := recordingEvents() + recordEvents := integration.RecordingEvents() playMode := gocui.NORMAL if recordEvents { playMode = gocui.RECORDING - } else if replaying() { + } else if integration.Replaying() { playMode = gocui.REPLAYING + } else if integration.IntegrationTestName() != "" { + playMode = gocui.REPLAYING_NEW } g, err := gocui.NewGui(gocui.OutputTrue, OverlappingEdges, playMode, headless, RuneReplacements) @@ -475,7 +478,7 @@ func (gui *Gui) viewTabMap() map[string][]context.TabView { // Run: setup the gui with keybindings and start the mainloop func (gui *Gui) Run(startArgs types.StartArgs) error { - g, err := gui.initGocui(headless()) + g, err := gui.initGocui(integration.Headless()) if err != nil { return err } @@ -490,23 +493,7 @@ func (gui *Gui) Run(startArgs types.StartArgs) error { }) deadlock.Opts.Disable = !gui.Debug - if replaying() { - gui.g.RecordingConfig = gocui.RecordingConfig{ - Speed: getRecordingSpeed(), - Leeway: 100, - } - - var err error - gui.g.Recording, err = gui.loadRecording() - if err != nil { - return err - } - - go utils.Safe(func() { - time.Sleep(time.Second * 40) - log.Fatal("40 seconds is up, lazygit recording took too long to complete") - }) - } + gui.handleTestMode() gui.g.OnSearchEscape = gui.onSearchEscape if err := gui.Config.ReloadUserConfig(); err != nil { @@ -593,7 +580,7 @@ func (gui *Gui) RunAndHandleError(startArgs types.StartArgs) error { } } - if err := gui.saveRecording(gui.g.Recording); err != nil { + if err := integration.SaveRecording(gui.g.Recording); err != nil { return err } @@ -627,7 +614,7 @@ func (gui *Gui) runSubprocessWithSuspense(subprocess oscommands.ICmdObj) (bool, gui.Mutexes.SubprocessMutex.Lock() defer gui.Mutexes.SubprocessMutex.Unlock() - if replaying() { + if integration.Replaying() { // we do not yet support running subprocesses within integration tests. So if // we're replaying an integration test and we're inside this method, something // has gone wrong, so we should fail diff --git a/pkg/gui/gui_test.go b/pkg/gui/gui_test.go index d2345d5d0..2565392e6 100644 --- a/pkg/gui/gui_test.go +++ b/pkg/gui/gui_test.go @@ -3,6 +3,9 @@ package gui +// this is the new way of running tests. See pkg/integration/integration_tests/commit.go +// for an example + import ( "fmt" "io" @@ -14,60 +17,37 @@ import ( "github.com/creack/pty" "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/stretchr/testify/assert" ) -// This file is quite similar to integration/main.go. The main difference is that this file is -// run via `go test` whereas the other is run via `test/lazyintegration/main.go` which provides -// a convenient gui wrapper around our integration tests. The `go test` approach is better -// for CI and for running locally in the background to ensure you haven't broken -// anything while making changes. If you want to visually see what's happening when a test is run, -// you'll need to take the other approach -// -// As for this file, to run an integration test, e.g. for test 'commit', go: -// go test pkg/gui/gui_test.go -run /commit -// -// To update a snapshot for an integration test, pass UPDATE_SNAPSHOTS=true -// UPDATE_SNAPSHOTS=true go test pkg/gui/gui_test.go -run /commit -// -// integration tests are run in test/integration/<test_name>/actual and the final test does -// not clean up that directory so you can cd into it to see for yourself what -// happened when a test fails. -// -// To override speed, pass e.g. `SPEED=1` as an env var. Otherwise we start each test -// at a high speed and then drop down to lower speeds upon each failure until finally -// trying at the original playback speed (speed 1). A speed of 2 represents twice the -// original playback speed. Speed may be a decimal. - func Test(t *testing.T) { if testing.Short() { t.Skip("Skipping integration tests in short mode") } mode := integration.GetModeFromEnv() - speedEnv := os.Getenv("SPEED") includeSkipped := os.Getenv("INCLUDE_SKIPPED") != "" parallelTotal := tryConvert(os.Getenv("PARALLEL_TOTAL"), 1) parallelIndex := tryConvert(os.Getenv("PARALLEL_INDEX"), 0) testNumber := 0 - err := integration.RunTests( + err := integration.RunTestsNew( t.Logf, runCmdHeadless, - func(test *integration.Test, f func(*testing.T) error) { + func(test types.Test, f func(*testing.T) error) { defer func() { testNumber += 1 }() if testNumber%parallelTotal != parallelIndex { return } - t.Run(test.Name, func(t *testing.T) { + t.Run(test.Name(), func(t *testing.T) { err := f(t) assert.NoError(t, err) }) }, mode, - speedEnv, func(t *testing.T, expected string, actual string, prefix string) { t.Helper() assert.Equal(t, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual)) diff --git a/pkg/gui/input.go b/pkg/gui/input.go new file mode 100644 index 000000000..c6424c077 --- /dev/null +++ b/pkg/gui/input.go @@ -0,0 +1,93 @@ +package gui + +import ( + "time" + + "github.com/gdamore/tcell/v2" + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/gui/keybindings" + "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +type InputImpl struct { + g *gocui.Gui + keys config.KeybindingConfig +} + +var _ types.Input = &InputImpl{} + +func (self *InputImpl) PushKeys(keyStrs ...string) { + for _, keyStr := range keyStrs { + self.pushKey(keyStr) + } +} + +func (self *InputImpl) pushKey(keyStr string) { + key := keybindings.GetKey(keyStr) + + var r rune + var tcellKey tcell.Key + switch v := key.(type) { + case rune: + r = v + tcellKey = tcell.KeyRune + case gocui.Key: + tcellKey = tcell.Key(v) + } + + self.g.ReplayedEvents.Keys <- gocui.NewTcellKeyEventWrapper( + tcell.NewEventKey(tcellKey, r, tcell.ModNone), + 0, + ) +} + +func (self *InputImpl) SwitchToStatusWindow() { + self.pushKey(self.keys.Universal.JumpToBlock[0]) +} + +func (self *InputImpl) SwitchToFilesWindow() { + self.pushKey(self.keys.Universal.JumpToBlock[1]) +} + +func (self *InputImpl) SwitchToBranchesWindow() { + self.pushKey(self.keys.Universal.JumpToBlock[2]) +} + +func (self *InputImpl) SwitchToCommitsWindow() { + self.pushKey(self.keys.Universal.JumpToBlock[3]) +} + +func (self *InputImpl) SwitchToStashWindow() { + self.pushKey(self.keys.Universal.JumpToBlock[4]) +} + +func (self *InputImpl) Type(content string) { + for _, char := range content { + self.pushKey(string(char)) + } +} + +func (self *InputImpl) Confirm() { + self.pushKey(self.keys.Universal.Confirm) +} + +func (self *InputImpl) Cancel() { + self.pushKey(self.keys.Universal.Return) +} + +func (self *InputImpl) Select() { + self.pushKey(self.keys.Universal.Select) +} + +func (self *InputImpl) NextItem() { + self.pushKey(self.keys.Universal.NextItem) +} + +func (self *InputImpl) PreviousItem() { + self.pushKey(self.keys.Universal.PrevItem) +} + +func (self *InputImpl) Wait(milliseconds int) { + time.Sleep(time.Duration(milliseconds) * time.Millisecond) +} diff --git a/pkg/gui/old_gui_test.go b/pkg/gui/old_gui_test.go new file mode 100644 index 000000000..12e33432d --- /dev/null +++ b/pkg/gui/old_gui_test.go @@ -0,0 +1,76 @@ +//go:build !windows +// +build !windows + +package gui + +import ( + "fmt" + "os" + "testing" + + "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/stretchr/testify/assert" +) + +// Deprecated: this is the old way of running tests. See pkg/gui/gui_test.go for the new way. + +// This file is quite similar to integration/main.go. The main difference is that this file is +// run via `go test` whereas the other is run via `test/lazyintegration/main.go` which provides +// a convenient gui wrapper around our integration tests. The `go test` approach is better +// for CI and for running locally in the background to ensure you haven't broken +// anything while making changes. If you want to visually see what's happening when a test is run, +// you'll need to take the other approach +// +// As for this file, to run an integration test, e.g. for test 'commit', go: +// go test pkg/gui/old_gui_test.go -run /commit +// +// To update a snapshot for an integration test, pass UPDATE_SNAPSHOTS=true +// UPDATE_SNAPSHOTS=true go test pkg/gui/old_gui_test.go -run /commit +// +// integration tests are run in test/integration/<test_name>/actual and the final test does +// not clean up that directory so you can cd into it to see for yourself what +// happened when a test fails. +// +// To override speed, pass e.g. `SPEED=1` as an env var. Otherwise we start each test +// at a high speed and then drop down to lower speeds upon each failure until finally +// trying at the original playback speed (speed 1). A speed of 2 represents twice the +// original playback speed. Speed may be a decimal. + +func TestOld(t *testing.T) { + if testing.Short() { + t.Skip("Skipping integration tests in short mode") + } + + mode := integration.GetModeFromEnv() + speedEnv := os.Getenv("SPEED") + includeSkipped := os.Getenv("INCLUDE_SKIPPED") != "" + + parallelTotal := tryConvert(os.Getenv("PARALLEL_TOTAL"), 1) + parallelIndex := tryConvert(os.Getenv("PARALLEL_INDEX"), 0) + testNumber := 0 + + err := integration.RunTests( + t.Logf, + runCmdHeadless, + func(test *integration.Test, f func(*testing.T) error) { + defer func() { testNumber += 1 }() + if testNumber%parallelTotal != parallelIndex { + return + } + + t.Run(test.Name, func(t *testing.T) { + err := f(t) + assert.NoError(t, err) + }) + }, + mode, + speedEnv, + func(t *testing.T, expected string, actual string, prefix string) { + t.Helper() + assert.Equal(t, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual)) + }, + includeSkipped, + ) + + assert.NoError(t, err) +} diff --git a/pkg/gui/test_mode.go b/pkg/gui/test_mode.go new file mode 100644 index 000000000..88e42e440 --- /dev/null +++ b/pkg/gui/test_mode.go @@ -0,0 +1,63 @@ +package gui + +import ( + "fmt" + "log" + "time" + + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/utils" +) + +func (gui *Gui) handleTestMode() { + if integration.PlayingIntegrationTest() { + test, ok := integration.CurrentIntegrationTest() + + if !ok { + panic(fmt.Sprintf("test %s not found", integration.IntegrationTestName())) + } + + go func() { + time.Sleep(time.Millisecond * 100) + + test.Run( + &integration.ShellImpl{}, + &InputImpl{g: gui.g, keys: gui.Config.GetUserConfig().Keybinding}, + &AssertImpl{gui: gui}, + gui.c.UserConfig.Keybinding, + ) + + gui.g.Update(func(*gocui.Gui) error { + return gocui.ErrQuit + }) + + time.Sleep(time.Second * 1) + + log.Fatal("gocui should have already exited") + }() + + go utils.Safe(func() { + time.Sleep(time.Second * 40) + log.Fatal("40 seconds is up, lazygit recording took too long to complete") + }) + } + + if integration.Replaying() { + gui.g.RecordingConfig = gocui.RecordingConfig{ + Speed: integration.GetRecordingSpeed(), + Leeway: 100, + } + + var err error + gui.g.Recording, err = integration.LoadRecording() + if err != nil { + panic(err) + } + + go utils.Safe(func() { + time.Sleep(time.Second * 40) + log.Fatal("40 seconds is up, lazygit recording took too long to complete") + }) + } +} diff --git a/pkg/integration/env.go b/pkg/integration/env.go new file mode 100644 index 000000000..7cdc72267 --- /dev/null +++ b/pkg/integration/env.go @@ -0,0 +1,46 @@ +package integration + +import ( + "os" + + "github.com/jesseduffield/generics/slices" + "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +func Headless() bool { + return os.Getenv("HEADLESS") != "" +} + +// NEW integration test format stuff + +func IntegrationTestName() string { + return os.Getenv("LAZYGIT_TEST_NAME") +} + +func CurrentIntegrationTest() (types.Test, bool) { + if !PlayingIntegrationTest() { + return nil, false + } + + return slices.Find(Tests, func(test types.Test) bool { + return test.Name() == IntegrationTestName() + }) +} + +func PlayingIntegrationTest() bool { + return IntegrationTestName() != "" +} + +// OLD integration test format stuff + +func Replaying() bool { + return os.Getenv("REPLAY_EVENTS_FROM") != "" +} + +func RecordingEvents() bool { + return recordEventsTo() != "" +} + +func recordEventsTo() string { + return os.Getenv("RECORD_EVENTS_TO") +} diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index 4172f3e49..853dae0d9 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -1,72 +1,29 @@ package integration import ( - "encoding/json" "errors" "fmt" "io/ioutil" - "log" "os" "os/exec" "path/filepath" - "strconv" - "strings" "testing" - "github.com/jesseduffield/generics/slices" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" - "github.com/jesseduffield/lazygit/pkg/secureexec" + "github.com/jesseduffield/lazygit/pkg/integration/integration_tests" + "github.com/jesseduffield/lazygit/pkg/integration/types" ) -// This package is for running our integration test suite. See docs/Integration_Tests.md for more info +// this is the integration runner for the new and improved integration interface -type Test struct { - Name string `json:"name"` - Speed float64 `json:"speed"` - Description string `json:"description"` - ExtraCmdArgs string `json:"extraCmdArgs"` - Skip bool `json:"skip"` -} - -type Mode int - -const ( - // default: for when we're just running a test and comparing to the snapshot - TEST = iota - // for when we want to record a test and set the snapshot based on the result - RECORD - // when we just want to use the setup of the test for our own sandboxing purposes. - // This does not record the session and does not create/update snapshots - SANDBOX - // running a test but updating the snapshot - UPDATE_SNAPSHOT -) - -func GetModeFromEnv() Mode { - switch os.Getenv("MODE") { - case "record": - return RECORD - case "", "test": - return TEST - case "updateSnapshot": - return UPDATE_SNAPSHOT - case "sandbox": - return SANDBOX - default: - log.Fatalf("unknown test mode: %s, must be one of [test, record, update, sandbox]", os.Getenv("MODE")) - panic("unreachable") - } -} +// re-exporting this so that clients only need to import one package +var Tests = integration_tests.Tests -// this function is used by both `go test` and from our lazyintegration gui, but -// errors need to be handled differently in each (for example go test is always -// working with *testing.T) so we pass in any differences as args here. -func RunTests( +func RunTestsNew( logf func(format string, formatArgs ...interface{}), runCmd func(cmd *exec.Cmd) error, - fnWrapper func(test *Test, f func(*testing.T) error), + fnWrapper func(test types.Test, f func(*testing.T) error), mode Mode, - speedEnv string, onFail func(t *testing.T, expected string, actual string, prefix string), includeSkipped bool, ) error { @@ -76,7 +33,7 @@ func RunTests( return err } - testDir := filepath.Join(rootDir, "test", "integration") + testDir := filepath.Join(rootDir, "test", "integration_new") osCommand := oscommands.NewDummyOSCommand() err = osCommand.Cmd.New("go build -o " + tempLazygitPath()).Run() @@ -84,115 +41,94 @@ func RunTests( return err } - tests, err := LoadTests(testDir) - if err != nil { - return err - } - - for _, test := range tests { + for _, test := range Tests { test := test fnWrapper(test, func(t *testing.T) error { //nolint: thelper - if test.Skip && !includeSkipped { - logf("skipping test: %s", test.Name) + if test.Skip() && !includeSkipped { + logf("skipping test: %s", test.Name()) return nil } - speeds := getTestSpeeds(test.Speed, mode, speedEnv) - testPath := filepath.Join(testDir, test.Name) + testPath := filepath.Join(testDir, test.Name()) + actualDir := filepath.Join(testPath, "actual") expectedDir := filepath.Join(testPath, "expected") actualRepoDir := filepath.Join(actualDir, "repo") logf("path: %s", testPath) - for i, speed := range speeds { - if mode != SANDBOX && mode != RECORD { - logf("%s: attempting test at speed %f\n", test.Name, speed) - } + findOrCreateDir(testPath) + prepareIntegrationTestDir(actualDir) + findOrCreateDir(actualRepoDir) + err := createFixtureNew(test, actualRepoDir, rootDir) + if err != nil { + return err + } + + configDir := filepath.Join(testPath, "used_config") - findOrCreateDir(testPath) - prepareIntegrationTestDir(actualDir) - findOrCreateDir(actualRepoDir) - err := createFixture(testPath, actualRepoDir) + cmd, err := getLazygitCommandNew(test, testPath, rootDir) + if err != nil { + return err + } + + err = runCmd(cmd) + if err != nil { + return err + } + + if mode == UPDATE_SNAPSHOT { + // create/update snapshot + err = oscommands.CopyDir(actualDir, expectedDir) if err != nil { return err } - configDir := filepath.Join(testPath, "used_config") + if err := renameSpecialPaths(expectedDir); err != nil { + return err + } - cmd, err := getLazygitCommand(testPath, rootDir, mode, speed, test.ExtraCmdArgs) - if err != nil { + logf("%s", "updated snapshot") + } else { + if err := validateSameRepos(expectedDir, actualDir); err != nil { return err } - err = runCmd(cmd) + // iterate through each repo in the expected dir and comparet to the corresponding repo in the actual dir + expectedFiles, err := ioutil.ReadDir(expectedDir) if err != nil { return err } - if mode == UPDATE_SNAPSHOT || mode == RECORD { - // create/update snapshot - err = oscommands.CopyDir(actualDir, expectedDir) - if err != nil { - return err + for _, f := range expectedFiles { + if !f.IsDir() { + return errors.New("unexpected file (as opposed to directory) in integration test 'expected' directory") } - if err := renameSpecialPaths(expectedDir); err != nil { - return err - } + // get corresponding file name from actual dir + actualRepoPath := filepath.Join(actualDir, f.Name()) + expectedRepoPath := filepath.Join(expectedDir, f.Name()) - logf("%s", "updated snapshot") - } else { - if err := validateSameRepos(expectedDir, actualDir); err != nil { - return err - } - - // iterate through each repo in the expected dir and comparet to the corresponding repo in the actual dir - expectedFiles, err := ioutil.ReadDir(expectedDir) + actualRepo, expectedRepo, err := generateSnapshots(actualRepoPath, expectedRepoPath) if err != nil { return err } - success := true - for _, f := range expectedFiles { - if !f.IsDir() { - return errors.New("unexpected file (as opposed to directory) in integration test 'expected' directory") - } - - // get corresponding file name from actual dir - actualRepoPath := filepath.Join(actualDir, f.Name()) - expectedRepoPath := filepath.Join(expectedDir, f.Name()) - - actualRepo, expectedRepo, err := generateSnapshots(actualRepoPath, expectedRepoPath) + if expectedRepo != actualRepo { + // get the log file and print it + bytes, err := ioutil.ReadFile(filepath.Join(configDir, "development.log")) if err != nil { return err } + logf("%s", string(bytes)) - if expectedRepo != actualRepo { - success = false - // if the snapshot doesn't match and we haven't tried all playback speeds different we'll retry at a slower speed - if i < len(speeds)-1 { - break - } - - // get the log file and print it - bytes, err := ioutil.ReadFile(filepath.Join(configDir, "development.log")) - if err != nil { - return err - } - logf("%s", string(bytes)) - - onFail(t, expectedRepo, actualRepo, f.Name()) - } - } - - if success { - logf("%s: success at speed %f\n", test.Name, speed) - break + onFail(t, expectedRepo, actualRepo, f.Name()) } } } + logf("test passed: %s", test.Name()) + return nil }) } @@ -200,344 +136,35 @@ func RunTests( return nil } -// validates that the actual and expected dirs have the same repo names (doesn't actually check the contents of the repos) -func validateSameRepos(expectedDir string, actualDir string) error { - // iterate through each repo in the expected dir and compare to the corresponding repo in the actual dir - expectedFiles, err := ioutil.ReadDir(expectedDir) - if err != nil { - return err - } - - var actualFiles []os.FileInfo - actualFiles, err = ioutil.ReadDir(actualDir) - if err != nil { - return err - } - - expectedFileNames := slices.Map(expectedFiles, getFileName) - actualFileNames := slices.Map(actualFiles, getFileName) - if !slices.Equal(expectedFileNames, actualFileNames) { - return fmt.Errorf("expected and actual repo dirs do not match: expected: %s, actual: %s", expectedFileNames, actualFileNames) - } - - return nil -} - -func getFileName(f os.FileInfo) string { - return f.Name() -} - -func prepareIntegrationTestDir(actualDir string) { - // remove contents of integration test directory - dir, err := ioutil.ReadDir(actualDir) - if err != nil { - if os.IsNotExist(err) { - err = os.Mkdir(actualDir, 0o777) - if err != nil { - panic(err) - } - } else { - panic(err) - } - } - for _, d := range dir { - os.RemoveAll(filepath.Join(actualDir, d.Name())) - } -} - -func GetRootDirectory() string { - path, err := os.Getwd() - if err != nil { +func createFixtureNew(test types.Test, actualDir string, rootDir string) error { + if err := os.Chdir(actualDir); err != nil { panic(err) } - for { - _, err := os.Stat(filepath.Join(path, ".git")) + shell := &ShellImpl{} + shell.RunCommand("git init") + shell.RunCommand(`git config user.email "CI@example.com"`) + shell.RunCommand(`git config user.name "CI"`) - if err == nil { - return path - } + test.SetupRepo(shell) - if !os.IsNotExist(err) { - panic(err) - } - - path = filepath.Dir(path) - - if path == "/" { - log.Fatal("must run in lazygit folder or child folder") - } - } -} - -func createFixture(testPath, actualDir string) error { - bashScriptPath := filepath.Join(testPath, "setup.sh") - cmd := secureexec.Command("bash", bashScriptPath, actualDir) - - if output, err := cmd.CombinedOutput(); err != nil { - return errors.New(string(output)) - } - - return nil -} - -func tempLazygitPath() string { - return filepath.Join("/tmp", "lazygit", "test_lazygit") -} - -func getTestSpeeds(testStartSpeed float64, mode Mode, speedStr string) []float64 { - if mode != TEST { - // have to go at original speed if updating snapshots in case we go to fast and create a junk snapshot - return []float64{1.0} - } - - if speedStr != "" { - speed, err := strconv.ParseFloat(speedStr, 64) - if err != nil { - panic(err) - } - return []float64{speed} - } - - // default is 10, 5, 1 - startSpeed := 10.0 - if testStartSpeed != 0 { - startSpeed = testStartSpeed - } - speeds := []float64{startSpeed} - if startSpeed > 5 { - speeds = append(speeds, 5) - } - speeds = append(speeds, 1, 1) - - return speeds -} - -func LoadTests(testDir string) ([]*Test, error) { - paths, err := filepath.Glob(filepath.Join(testDir, "/*/test.json")) - if err != nil { - return nil, err - } - - tests := make([]*Test, len(paths)) - - for i, path := range paths { - data, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - - test := &Test{} - - err = json.Unmarshal(data, test) - if err != nil { - return nil, err - } - - test.Name = strings.TrimPrefix(filepath.Dir(path), testDir+"/") - - tests[i] = test - } - - return tests, nil -} |