summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2022-01-15 20:24:19 +1100
committerJesse Duffield <jessedduffield@gmail.com>2022-01-17 19:14:59 +1100
commit2691477aff8505275488e35af7611fb78b53b328 (patch)
treefae3722004872d57ee652e54656c8f0f01bb3e96
parent8ca71eeb362aebbb8b148f0ef2fc58d33fc05ee0 (diff)
allow sandbox mode with integration tests
-rw-r--r--docs/Integration_Tests.md45
-rw-r--r--pkg/gui/gui_test.go6
-rw-r--r--pkg/integration/integration.go114
-rw-r--r--test/lazyintegration/main.go22
-rw-r--r--test/runner/main.go14
5 files changed, 136 insertions, 65 deletions
diff --git a/docs/Integration_Tests.md b/docs/Integration_Tests.md
index 814e593ca..1bb797b3a 100644
--- a/docs/Integration_Tests.md
+++ b/docs/Integration_Tests.md
@@ -33,17 +33,32 @@ git commit -am "myfile1"
## Running tests
+### From a TUI
+
+You can run/record/sandbox tests via a TUI with the following command:
+
+```
+go run test/lazyintegration/main.go
+```
+
+This TUI makes much of the following documentation redundant, but feel free to read through anyway!
+
+### From command line
+
To run all tests - assuming you're at the project root:
+
```
go test ./pkg/gui/
```
To run them in parallel
+
```
PARALLEL=true go test ./pkg/gui
```
To run a single test
+
```
go test ./pkg/gui -run /<test name>
# For example, to run the `tags` test:
@@ -51,29 +66,35 @@ go test ./pkg/gui -run /tags
```
To run a test at a certain speed
+
```
SPEED=2 go test ./pkg/gui -run /<test name>
```
To update a snapshot
+
```
-UPDATE_SNAPSHOTS=true go test ./pkg/gui -run /<test name>
+MODE=updateSnapshot go test ./pkg/gui -run /<test name>
```
## Creating a new test
To create a new test:
-1) Copy and paste an existing test directory and rename the new directory to whatever you want the test name to be. Update the test.json file's description to describe your test.
-2) Update the `setup.sh` any way you like
-3) If you want to have a config folder for just that test, create a `config` directory to contain a `config.yml` and optionally a `state.yml` file. Otherwise, the `test/default_test_config` directory will be used.
-4) From the lazygit root directory, run:
+
+1. Copy and paste an existing test directory and rename the new directory to whatever you want the test name to be. Update the test.json file's description to describe your test.
+2. Update the `setup.sh` any way you like
+3. If you want to have a config folder for just that test, create a `config` directory to contain a `config.yml` and optionally a `state.yml` file. Otherwise, the `test/default_test_config` directory will be used.
+4. From the lazygit root directory, run:
+
```
-RECORD_EVENTS=true go test ./pkg/gui -run /<test name>
+MODE=record go test ./pkg/gui -run /<test name>
```
-5) Feel free to re-attempt recording as many times as you like. In the absence of a proper testing framework, the more deliberate your keypresses, the better!
-6) Once satisfied with the recording, stage all the newly created files: `test.json`, `setup.sh`, `recording.json` and the `expected` directory that contains a copy of the repo you created.
+
+5. Feel free to re-attempt recording as many times as you like. In the absence of a proper testing framework, the more deliberate your keypresses, the better!
+6. Once satisfied with the recording, stage all the newly created files: `test.json`, `setup.sh`, `recording.json` and the `expected` directory that contains a copy of the repo you created.
The resulting directory will look like:
+
```
actual/ (the resulting repo after running the test, ignored by git)
expected/ (the 'snapshot' repo)
@@ -85,6 +106,14 @@ recording.json
Feel free to create a hierarchy of directories in the `test/integration` directory to group tests by feature.
+## Sandboxing
+
+The integration tests serve a secondary purpose of providing a setup for easy sandboxing. If you want to run a test in sandbox mode (meaning the session won't be recorded and we won't create/update snapshots), go:
+
+```
+MODE=sandbox go test ./pkg/gui -run /<test name>
+```
+
## Feedback
If you think this process can be improved, let me know! It shouldn't be too hard to change things.
diff --git a/pkg/gui/gui_test.go b/pkg/gui/gui_test.go
index 6e7a26b79..e35ab1896 100644
--- a/pkg/gui/gui_test.go
+++ b/pkg/gui/gui_test.go
@@ -39,8 +39,7 @@ import (
// original playback speed. Speed may be a decimal.
func Test(t *testing.T) {
- record := false
- updateSnapshots := os.Getenv("UPDATE_SNAPSHOTS") != ""
+ mode := integration.GetModeFromEnv()
speedEnv := os.Getenv("SPEED")
includeSkipped := os.Getenv("INCLUDE_SKIPPED") != ""
@@ -53,8 +52,7 @@ func Test(t *testing.T) {
assert.NoError(t, err)
})
},
- updateSnapshots,
- record,
+ mode,
speedEnv,
func(t *testing.T, expected string, actual string, prefix string) {
assert.Equal(t, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual))
diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go
index 9c498e849..3ac6f0c8c 100644
--- a/pkg/integration/integration.go
+++ b/pkg/integration/integration.go
@@ -24,6 +24,36 @@ type Test struct {
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")
+ }
+}
+
// 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.
@@ -31,8 +61,7 @@ func RunTests(
logf func(format string, formatArgs ...interface{}),
runCmd func(cmd *exec.Cmd) error,
fnWrapper func(test *Test, f func(*testing.T) error),
- updateSnapshots bool,
- record bool,
+ mode Mode,
speedEnv string,
onFail func(t *testing.T, expected string, actual string, prefix string),
includeSkipped bool,
@@ -65,7 +94,7 @@ func RunTests(
}
fnWrapper(test, func(t *testing.T) error {
- speeds := getTestSpeeds(test.Speed, updateSnapshots, speedEnv)
+ speeds := getTestSpeeds(test.Speed, mode, speedEnv)
testPath := filepath.Join(testDir, test.Name)
actualRepoDir := filepath.Join(testPath, "actual")
expectedRepoDir := filepath.Join(testPath, "expected")
@@ -73,10 +102,10 @@ func RunTests(
expectedRemoteDir := filepath.Join(testPath, "expected_remote")
logf("path: %s", testPath)
- // three retries at normal speed for the sake of flakey tests
- speeds = append(speeds, 1)
for i, speed := range speeds {
- logf("%s: attempting test at speed %f\n", test.Name, speed)
+ if mode != SANDBOX && mode != RECORD {
+ logf("%s: attempting test at speed %f\n", test.Name, speed)
+ }
findOrCreateDir(testPath)
prepareIntegrationTestDir(actualRepoDir)
@@ -88,7 +117,7 @@ func RunTests(
configDir := filepath.Join(testPath, "used_config")
- cmd, err := getLazygitCommand(testPath, rootDir, record, speed, test.ExtraCmdArgs)
+ cmd, err := getLazygitCommand(testPath, rootDir, mode, speed, test.ExtraCmdArgs)
if err != nil {
return err
}
@@ -98,7 +127,7 @@ func RunTests(
return err
}
- if updateSnapshots {
+ if mode == UPDATE_SNAPSHOT || mode == RECORD {
err = oscommands.CopyDir(actualRepoDir, expectedRepoDir)
if err != nil {
return err
@@ -122,39 +151,41 @@ func RunTests(
}
}
- actualRepo, expectedRepo, err := generateSnapshots(actualRepoDir, expectedRepoDir)
- if err != nil {
- return err
- }
-
- actualRemote := "remote folder does not exist"
- expectedRemote := "remote folder does not exist"
- if folderExists(expectedRemoteDir) {
- actualRemote, expectedRemote, err = generateSnapshotsForRemote(actualRemoteDir, expectedRemoteDir)
+ if mode != SANDBOX {
+ actualRepo, expectedRepo, err := generateSnapshots(actualRepoDir, expectedRepoDir)
if err != nil {
return err
}
- } else if folderExists(actualRemoteDir) {
- actualRemote = "remote folder exists"
- }
- if expectedRepo == actualRepo && expectedRemote == actualRemote {
- logf("%s: success at speed %f\n", test.Name, speed)
- break
- }
+ actualRemote := "remote folder does not exist"
+ expectedRemote := "remote folder does not exist"
+ if folderExists(expectedRemoteDir) {
+ actualRemote, expectedRemote, err = generateSnapshotsForRemote(actualRemoteDir, expectedRemoteDir)
+ if err != nil {
+ return err
+ }
+ } else if folderExists(actualRemoteDir) {
+ actualRemote = "remote folder exists"
+ }
- // if the snapshots and we haven't tried all playback speeds different we'll retry at a slower speed
- if i == len(speeds)-1 {
- // get the log file and print that
- bytes, err := ioutil.ReadFile(filepath.Join(configDir, "development.log"))
- if err != nil {
- return err
+ if expectedRepo == actualRepo && expectedRemote == actualRemote {
+ logf("%s: success at speed %f\n", test.Name, speed)
+ break
}
- logf("%s", string(bytes))
- if expectedRepo != actualRepo {
- onFail(t, expectedRepo, actualRepo, "repo")
- } else {
- onFail(t, expectedRemote, actualRemote, "remote")
+
+ // if the snapshots and we haven't tried all playback speeds different we'll retry at a slower speed
+ if i == len(speeds)-1 {
+ // get the log file and print that
+ bytes, err := ioutil.ReadFile(filepath.Join(configDir, "development.log"))
+ if err != nil {
+ return err
+ }
+ logf("%s", string(bytes))
+ if expectedRepo != actualRepo {
+ onFail(t, expectedRepo, actualRepo, "repo")
+ } else {
+ onFail(t, expectedRemote, actualRemote, "remote")
+ }
}
}
}
@@ -231,8 +262,8 @@ func tempLazygitPath() string {
return filepath.Join("/tmp", "lazygit", "test_lazygit")
}
-func getTestSpeeds(testStartSpeed float64, updateSnapshots bool, speedStr string) []float64 {
- if updateSnapshots {
+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}
}
@@ -254,7 +285,7 @@ func getTestSpeeds(testStartSpeed float64, updateSnapshots bool, speedStr string
if startSpeed > 5 {
speeds = append(speeds, 5)
}
- speeds = append(speeds, 1)
+ speeds = append(speeds, 1, 1)
return speeds
}
@@ -400,7 +431,7 @@ func generateSnapshotsForRemote(actualDir string, expectedDir string) (string, s
return actual, expected, nil
}
-func getLazygitCommand(testPath string, rootDir string, record bool, speed float64, extraCmdArgs string) (*exec.Cmd, error) {
+func getLazygitCommand(testPath string, rootDir string, mode Mode, speed float64, extraCmdArgs string) (*exec.Cmd, error) {
osCommand := oscommands.NewDummyOSCommand()
replayPath := filepath.Join(testPath, "recording.json")
@@ -432,9 +463,10 @@ func getLazygitCommand(testPath string, rootDir string, record bool, speed float
cmdObj := osCommand.Cmd.New(cmdStr)
cmdObj.AddEnvVars(fmt.Sprintf("SPEED=%f", speed))
- if record {
+ switch mode {
+ case RECORD:
cmdObj.AddEnvVars(fmt.Sprintf("RECORD_EVENTS_TO=%s", replayPath))
- } else {
+ case TEST, UPDATE_SNAPSHOT:
cmdObj.AddEnvVars(fmt.Sprintf("REPLAY_EVENTS_FROM=%s", replayPath))
}
diff --git a/test/lazyintegration/main.go b/test/lazyintegration/main.go
index 3bd299abf..6940333ed 100644
--- a/test/lazyintegration/main.go
+++ b/test/lazyintegration/main.go
@@ -106,7 +106,21 @@ func main() {
return nil
}
- cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true RECORD_EVENTS=true go run test/runner/main.go %s", currentTest.Name))
+ cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=record go run test/runner/main.go %s", currentTest.Name))
+ app.runSubprocess(cmd)
+
+ return nil
+ }); err != nil {
+ log.Panicln(err)
+ }
+
+ if err := g.SetKeybinding("list", nil, 's', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
+ currentTest := app.getCurrentTest()
+ if currentTest == nil {
+ return nil
+ }
+
+ cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run test/runner/main.go %s", currentTest.Name))
app.runSubprocess(cmd)
return nil
@@ -128,13 +142,13 @@ func main() {
log.Panicln(err)
}
- if err := g.SetKeybinding("list", nil, 's', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
+ if err := g.SetKeybinding("list", nil, 'u', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
currentTest := app.getCurrentTest()
if currentTest == nil {
return nil
}
- cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true UPDATE_SNAPSHOTS=true go run test/runner/main.go %s", currentTest.Name))
+ cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=updateSnapshot go run test/runner/main.go %s", currentTest.Name))
app.runSubprocess(cmd)
return nil
@@ -347,7 +361,7 @@ func (app *App) layout(g *gocui.Gui) error {
keybindingsView.Title = "Keybindings"
keybindingsView.Wrap = true
keybindingsView.FgColor = gocui.ColorDefault
- fmt.Fprintln(keybindingsView, "up/down: navigate, enter: run test, s: run test and update snapshots, r: record test, o: open test config, n: duplicate test, m: rename test, d: delete test")
+ fmt.Fprintln(keybindingsView, "up/down: navigate, enter: run test, u: run test and update snapshots, r: record test, s: sandbox, o: open test config, n: duplicate test, m: rename test, d: delete test")
}
editorView, err := g.SetViewBeneath("editor", "keybindings", editorViewHeight)
diff --git a/test/runner/main.go b/test/runner/main.go
index b122568fe..af6195cbc 100644
--- a/test/runner/main.go
+++ b/test/runner/main.go
@@ -11,19 +11,18 @@ import (
"github.com/stretchr/testify/assert"
)
+// see https://github.com/jesseduffield/lazygit/blob/master/docs/Integration_Tests.md
// This file can be invoked directly, but you might find it easier to go through
-// test/lazyintegration/main.go, which provides a convenient gui wrapper to integration
-// tests.
+// test/lazyintegration/main.go, which provides a convenient gui wrapper to integration tests.
//
// If invoked directly, you can specify a test by passing it as the first argument.
-// You can also specify that you want to record a test by passing RECORD_EVENTS=true
+// You can also specify that you want to record a test by passing MODE=record
// as an env var.
func main() {
- record := os.Getenv("RECORD_EVENTS") != ""
- updateSnapshots := record || os.Getenv("UPDATE_SNAPSHOTS") != ""
+ mode := integration.GetModeFromEnv()
speedEnv := os.Getenv("SPEED")
- includeSkipped := os.Getenv("INCLUDE_SKIPPED") != ""
+ includeSkipped := os.Getenv("INCLUDE_SKIPPED") == "true"
selectedTestName := os.Args[1]
err := integration.RunTests(
@@ -37,8 +36,7 @@ func main() {
log.Print(err.Error())
}
},
- updateSnapshots,
- record,
+ mode,
speedEnv,
func(_t *testing.T, expected string, actual string, prefix string) {
assert.Equal(MockTestingT{}, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual))