1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-03-19 21:28:28 +02:00

support integration testing

WIP
This commit is contained in:
Jesse Duffield 2020-10-04 18:41:33 +11:00
parent ece93e5eef
commit f76196937a
7 changed files with 521 additions and 5 deletions

3
.gitignore vendored
View File

@ -25,4 +25,5 @@ lazygit
!.circleci/
!.github/
test/git_server/data
test/git_server/data
test/integration_test/

224
pkg/gui/gui_test.go Normal file
View File

@ -0,0 +1,224 @@
package gui
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"testing"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/stretchr/testify/assert"
)
// To run an integration test, e.g. for test 'commit', go:
// go test pkg/gui/gui_test.go -run /commit
//
// To record keypresses for an integration test, pass RECORD_EVENTS=true like so:
// RECORD_EVENTS=true go test pkg/gui/gui_test.go -run /commit
//
// To update a snapshot for an integration test, pass UPDATE_SNAPSHOT=true
// UPDATE_SNAPSHOT=true go test pkg/gui/gui_test.go -run /commit
//
// When RECORD_EVENTS is true, updates will be updated automatically
//
// integration tests are run in test/integration_test 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 failed.
//
// TODO: support passing an env var for playback speed, given it's currently pretty fast
type integrationTest struct {
name string
prepare func() error
}
func generateSnapshot(t *testing.T) string {
osCommand := oscommands.NewDummyOSCommand()
cmd := `sh -c "git status; cat ./*; git log --pretty=%B -p"`
snapshot, err := osCommand.RunCommandWithOutput(cmd)
assert.NoError(t, err)
return snapshot
}
func findOrCreateDir(path string) {
_, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
err = os.MkdirAll(path, 0777)
if err != nil {
panic(err)
}
} else {
panic(err)
}
}
}
func Test(t *testing.T) {
tests := []integrationTest{
{
name: "commit",
prepare: createFixture1,
},
{
name: "squash",
prepare: createFixture2,
},
}
gotoRootDirectory()
rootDir, err := os.Getwd()
if err != nil {
panic(err)
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
testPath := filepath.Join(rootDir, "test", "integration", test.name)
findOrCreateDir(testPath)
replayPath := filepath.Join(testPath, "recording.json")
snapshotPath := filepath.Join(testPath, "snapshot.txt")
err := os.Chdir(rootDir)
assert.NoError(t, err)
prepareIntegrationTestDir()
err = test.prepare()
assert.NoError(t, err)
record := os.Getenv("RECORD_EVENTS") != ""
runLazygit(t, replayPath, record)
updateSnapshot := os.Getenv("UPDATE_SNAPSHOT") != ""
actual := generateSnapshot(t)
if updateSnapshot {
err := ioutil.WriteFile(snapshotPath, []byte(actual), 0600)
assert.NoError(t, err)
}
expectedBytes, err := ioutil.ReadFile(snapshotPath)
assert.NoError(t, err)
expected := string(expectedBytes)
assert.Equal(t, expected, actual, fmt.Sprintf("expected:\n%s\nactual:\n%s\n", expected, actual))
})
}
}
func createFixture1() error {
cmds := []string{
"git init",
`sh -c "echo test > myfile"`,
}
return runCommands(cmds)
}
func createFixture2() error {
cmds := []string{
"git init",
`sh -c "echo test1 > myfile1"`,
`git add .`,
`git commit -am "myfile1"`,
`sh -c "echo test2 > myfile2"`,
`git add .`,
`git commit -am "myfile2"`,
`sh -c "echo test3 > myfile3"`,
`git add .`,
`git commit -am "myfile3"`,
`sh -c "echo test4 > myfile4"`,
`git add .`,
`git commit -am "myfile4"`,
`sh -c "echo test5 > myfile5"`,
`git add .`,
`git commit -am "myfile5"`,
}
return runCommands(cmds)
}
func runCommands(cmds []string) error {
osCommand := oscommands.NewDummyOSCommand()
for _, cmd := range cmds {
if err := osCommand.RunCommand(cmd); err != nil {
return errors.New(fmt.Sprintf("error running command `%s`: %v", cmd, err))
}
}
return nil
}
func gotoRootDirectory() {
for {
_, err := os.Stat(".git")
if err == nil {
return
}
if !os.IsNotExist(err) {
panic(err)
}
if err = os.Chdir(".."); err != nil {
panic(err)
}
}
}
func runLazygit(t *testing.T, replayPath string, record bool) {
osCommand := oscommands.NewDummyOSCommand()
var cmd *exec.Cmd
if record {
cmd = osCommand.ExecutableFromString("lazygit")
cmd.Env = append(
cmd.Env,
fmt.Sprintf("RECORD_EVENTS_TO=%s", replayPath),
)
} else {
cmd = osCommand.ExecutableFromString("lazygit")
cmd.Env = append(
cmd.Env,
fmt.Sprintf("REPLAY_EVENTS_FROM=%s", replayPath),
)
}
err := osCommand.RunExecutable(cmd)
assert.NoError(t, err)
}
func prepareIntegrationTestDir() {
path := filepath.Join("test", "integration_test")
// remove contents of integration test directory
dir, err := ioutil.ReadDir(path)
if err != nil {
if os.IsNotExist(err) {
err = os.Mkdir(path, 0777)
if err != nil {
panic(err)
}
} else {
panic(err)
}
}
for _, d := range dir {
os.RemoveAll(filepath.Join(path, d.Name()))
}
if err := os.Chdir(path); err != nil {
panic(err)
}
}

View File

@ -9,7 +9,11 @@ import (
)
func recordingEvents() bool {
return os.Getenv("RECORD_EVENTS") == "true"
return recordEventsTo() != ""
}
func recordEventsTo() string {
return os.Getenv("RECORD_EVENTS_TO")
}
func (gui *Gui) timeSinceStart() int64 {
@ -29,11 +33,14 @@ func (gui *Gui) replayRecordedEvents() {
ticker := time.NewTicker(time.Millisecond)
defer ticker.Stop()
var leeway int64 = 1000
// might need to add leeway if this ends up flakey
var leeway int64 = 0
// humans are slow so this speeds things up.
var speed int64 = 5
for _, event := range events {
for range ticker.C {
now := gui.timeSinceStart() - leeway
now := gui.timeSinceStart()*speed - leeway
if gui.g != nil && now >= event.Timestamp {
gui.g.ReplayedEvents <- *event.Event
break
@ -70,7 +77,9 @@ func (gui *Gui) saveRecordedEvents() error {
return err
}
return ioutil.WriteFile("recorded_events.json", jsonEvents, 0600)
path := recordEventsTo()
return ioutil.WriteFile(path, jsonEvents, 0600)
}
func (gui *Gui) recordEvents() {

View File

@ -0,0 +1,226 @@
[
{
"Timestamp": 41,
"Event": {
"Type": 1,
"Mod": 0,
"Key": 0,
"Ch": 0,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 0,
"Bytes": null
}
},
{
"Timestamp": 1042,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 32,
"Ch": 0,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "IA=="
}
},
{
"Timestamp": 1602,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 0,
"Ch": 99,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "Yw=="
}
},
{
"Timestamp": 2010,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 0,
"Ch": 109,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "bQ=="
}
},
{
"Timestamp": 2170,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 0,
"Ch": 121,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "eQ=="
}
},
{
"Timestamp": 2234,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 32,
"Ch": 0,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "IA=="
}
},
{
"Timestamp": 2354,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 0,
"Ch": 99,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "Yw=="
}
},
{
"Timestamp": 2410,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 0,
"Ch": 111,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "bw=="
}
},
{
"Timestamp": 2578,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 0,
"Ch": 109,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "bQ=="
}
},
{
"Timestamp": 2690,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 0,
"Ch": 109,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "bQ=="
}
},
{
"Timestamp": 2730,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 0,
"Ch": 105,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "aQ=="
}
},
{
"Timestamp": 2850,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 0,
"Ch": 116,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "dA=="
}
},
{
"Timestamp": 2954,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 13,
"Ch": 0,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "DQ=="
}
},
{
"Timestamp": 3625,
"Event": {
"Type": 0,
"Mod": 0,
"Key": 0,
"Ch": 113,
"Width": 0,
"Height": 0,
"Err": null,
"MouseX": 0,
"MouseY": 0,
"N": 1,
"Bytes": "cQ=="
}
}
]

View File

@ -0,0 +1,13 @@
On branch master
nothing to commit, working tree clean
test
my commit
diff --git a/myfile b/myfile
new file mode 100644
index 0000000..9daeafb
--- /dev/null
+++ b/myfile
@@ -0,0 +1 @@
+test

View File

@ -0,0 +1 @@
[{"Timestamp":14,"Event":{"Type":1,"Mod":0,"Key":0,"Ch":0,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":0,"Bytes":null}},{"Timestamp":482,"Event":{"Type":0,"Mod":0,"Key":65514,"Ch":0,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":3,"Bytes":"G09D"}},{"Timestamp":626,"Event":{"Type":0,"Mod":0,"Key":65514,"Ch":0,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":3,"Bytes":"G09D"}},{"Timestamp":810,"Event":{"Type":0,"Mod":0,"Key":65516,"Ch":0,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":3,"Bytes":"G09C"}},{"Timestamp":937,"Event":{"Type":0,"Mod":0,"Key":65516,"Ch":0,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":3,"Bytes":"G09C"}},{"Timestamp":1065,"Event":{"Type":0,"Mod":0,"Key":65516,"Ch":0,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":3,"Bytes":"G09C"}},{"Timestamp":1591,"Event":{"Type":0,"Mod":0,"Key":0,"Ch":101,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":1,"Bytes":"ZQ=="}},{"Timestamp":2034,"Event":{"Type":0,"Mod":0,"Key":65517,"Ch":0,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":3,"Bytes":"G09B"}},{"Timestamp":2243,"Event":{"Type":0,"Mod":0,"Key":0,"Ch":115,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":1,"Bytes":"cw=="}},{"Timestamp":2554,"Event":{"Type":0,"Mod":0,"Key":65517,"Ch":0,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":3,"Bytes":"G09B"}},{"Timestamp":2803,"Event":{"Type":0,"Mod":0,"Key":0,"Ch":100,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":1,"Bytes":"ZA=="}},{"Timestamp":3209,"Event":{"Type":0,"Mod":0,"Key":65517,"Ch":0,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":3,"Bytes":"G09B"}},{"Timestamp":3522,"Event":{"Type":0,"Mod":0,"Key":0,"Ch":102,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":1,"Bytes":"Zg=="}},{"Timestamp":4066,"Event":{"Type":0,"Mod":0,"Key":0,"Ch":109,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":1,"Bytes":"bQ=="}},{"Timestamp":5091,"Event":{"Type":0,"Mod":0,"Key":13,"Ch":0,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":1,"Bytes":"DQ=="}},{"Timestamp":5834,"Event":{"Type":0,"Mod":0,"Key":0,"Ch":113,"Width":0,"Height":0,"Err":null,"MouseX":0,"MouseY":0,"N":1,"Bytes":"cQ=="}}]

View File

@ -0,0 +1,42 @@
On branch master
nothing to commit, working tree clean
test1
test2
test3
test5
myfile2
myfile3
diff --git a/myfile2 b/myfile2
new file mode 100644
index 0000000..180cf83
--- /dev/null
+++ b/myfile2
@@ -0,0 +1 @@
+test2
diff --git a/myfile3 b/myfile3
new file mode 100644
index 0000000..df6b0d2
--- /dev/null
+++ b/myfile3
@@ -0,0 +1 @@
+test3
diff --git a/myfile5 b/myfile5
new file mode 100644
index 0000000..4f346f1
--- /dev/null
+++ b/myfile5
@@ -0,0 +1 @@
+test5
myfile1
diff --git a/myfile1 b/myfile1
new file mode 100644
index 0000000..a5bce3f
--- /dev/null
+++ b/myfile1
@@ -0,0 +1 @@
+test1