mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-21 12:16:54 +02:00
support running integration tests in parallel
This commit is contained in:
parent
2724f3888a
commit
2657060aa2
@ -32,27 +32,31 @@ import (
|
|||||||
// TODO: support passing an env var for playback speed, given it's currently pretty fast
|
// TODO: support passing an env var for playback speed, given it's currently pretty fast
|
||||||
|
|
||||||
type integrationTest struct {
|
type integrationTest struct {
|
||||||
name string
|
name string
|
||||||
fixture string
|
fixture string
|
||||||
|
startSpeed int
|
||||||
}
|
}
|
||||||
|
|
||||||
func tests() []integrationTest {
|
func tests() []integrationTest {
|
||||||
return []integrationTest{
|
return []integrationTest{
|
||||||
{
|
{
|
||||||
name: "commit",
|
name: "commit",
|
||||||
fixture: "newFile",
|
fixture: "newFile",
|
||||||
|
startSpeed: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "squash",
|
name: "squash",
|
||||||
fixture: "manyCommits",
|
fixture: "manyCommits",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "patchBuilding",
|
name: "patchBuilding",
|
||||||
fixture: "updatedFile",
|
fixture: "updatedFile",
|
||||||
|
startSpeed: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "patchBuilding2",
|
name: "patchBuilding2",
|
||||||
fixture: "updatedFile",
|
fixture: "updatedFile",
|
||||||
|
startSpeed: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "mergeConflicts",
|
name: "mergeConflicts",
|
||||||
@ -69,9 +73,11 @@ func tests() []integrationTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateSnapshot(t *testing.T) string {
|
func generateSnapshot(t *testing.T, actualDir string) string {
|
||||||
osCommand := oscommands.NewDummyOSCommand()
|
osCommand := oscommands.NewDummyOSCommand()
|
||||||
cmd := `bash -c "git status; cat ./*; git log --pretty=%B -p"`
|
cmd := fmt.Sprintf(`bash -c "cd %s && git status; cat ./*; git log --pretty=%%B -p"`, actualDir)
|
||||||
|
|
||||||
|
// need to copy from current directory to
|
||||||
|
|
||||||
snapshot, err := osCommand.RunCommandWithOutput(cmd)
|
snapshot, err := osCommand.RunCommandWithOutput(cmd)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -96,12 +102,7 @@ func findOrCreateDir(path string) {
|
|||||||
func Test(t *testing.T) {
|
func Test(t *testing.T) {
|
||||||
tests := tests()
|
tests := tests()
|
||||||
|
|
||||||
gotoRootDirectory()
|
rootDir := getRootDirectory()
|
||||||
|
|
||||||
rootDir, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
record := os.Getenv("RECORD_EVENTS") != ""
|
record := os.Getenv("RECORD_EVENTS") != ""
|
||||||
updateSnapshots := record || os.Getenv("UPDATE_SNAPSHOTS") != ""
|
updateSnapshots := record || os.Getenv("UPDATE_SNAPSHOTS") != ""
|
||||||
@ -110,7 +111,15 @@ func Test(t *testing.T) {
|
|||||||
test := test
|
test := test
|
||||||
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
speeds := []int{10, 5, 1}
|
if usePty() {
|
||||||
|
t.Parallel()
|
||||||
|
}
|
||||||
|
|
||||||
|
startSpeed := 10
|
||||||
|
if test.startSpeed != 0 {
|
||||||
|
startSpeed = test.startSpeed
|
||||||
|
}
|
||||||
|
speeds := []int{startSpeed, 5, 1}
|
||||||
if updateSnapshots {
|
if updateSnapshots {
|
||||||
// have to go at original speed if updating snapshots in case we go to fast and create a junk snapshot
|
// have to go at original speed if updating snapshots in case we go to fast and create a junk snapshot
|
||||||
speeds = []int{1}
|
speeds = []int{1}
|
||||||
@ -120,21 +129,19 @@ func Test(t *testing.T) {
|
|||||||
t.Logf("%s: attempting test at speed %d\n", test.name, speed)
|
t.Logf("%s: attempting test at speed %d\n", test.name, speed)
|
||||||
|
|
||||||
testPath := filepath.Join(rootDir, "test", "integration", test.name)
|
testPath := filepath.Join(rootDir, "test", "integration", test.name)
|
||||||
|
actualDir := filepath.Join(testPath, "actual")
|
||||||
findOrCreateDir(testPath)
|
findOrCreateDir(testPath)
|
||||||
|
|
||||||
snapshotPath := filepath.Join(testPath, "snapshot.txt")
|
snapshotPath := filepath.Join(testPath, "snapshot.txt")
|
||||||
|
|
||||||
err := os.Chdir(rootDir)
|
prepareIntegrationTestDir(testPath)
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
prepareIntegrationTestDir()
|
err := createFixture(rootDir, test.fixture, actualDir)
|
||||||
|
|
||||||
err = createFixture(rootDir, test.fixture)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
runLazygit(t, testPath, rootDir, record, speed)
|
runLazygit(t, testPath, rootDir, record, speed)
|
||||||
|
|
||||||
actual := generateSnapshot(t)
|
actual := generateSnapshot(t, actualDir)
|
||||||
|
|
||||||
if updateSnapshots {
|
if updateSnapshots {
|
||||||
err := ioutil.WriteFile(snapshotPath, []byte(actual), 0600)
|
err := ioutil.WriteFile(snapshotPath, []byte(actual), 0600)
|
||||||
@ -146,6 +153,7 @@ func Test(t *testing.T) {
|
|||||||
expected := string(expectedBytes)
|
expected := string(expectedBytes)
|
||||||
|
|
||||||
if expected == actual {
|
if expected == actual {
|
||||||
|
t.Logf("%s: success at speed %d\n", test.name, speed)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,9 +166,9 @@ func Test(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFixture(rootDir string, name string) error {
|
func createFixture(rootDir string, name string, actualDir string) error {
|
||||||
osCommand := oscommands.NewDummyOSCommand()
|
osCommand := oscommands.NewDummyOSCommand()
|
||||||
cmd := exec.Command("bash", filepath.Join(rootDir, "test", "fixtures", fmt.Sprintf("%s.sh", name)))
|
cmd := exec.Command("bash", filepath.Join(rootDir, "test", "fixtures", fmt.Sprintf("%s.sh", name)), actualDir)
|
||||||
|
|
||||||
if err := osCommand.RunExecutable(cmd); err != nil {
|
if err := osCommand.RunExecutable(cmd); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -169,20 +177,27 @@ func createFixture(rootDir string, name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gotoRootDirectory() {
|
func getRootDirectory() string {
|
||||||
|
path, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_, err := os.Stat(".git")
|
_, err := os.Stat(filepath.Join(path, ".git"))
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = os.Chdir(".."); err != nil {
|
path = filepath.Dir(path)
|
||||||
panic(err)
|
|
||||||
|
if path == "/" {
|
||||||
|
panic("must run in lazygit folder or child folder")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,6 +208,7 @@ func runLazygit(t *testing.T, testPath string, rootDir string, record bool, spee
|
|||||||
replayPath := filepath.Join(testPath, "recording.json")
|
replayPath := filepath.Join(testPath, "recording.json")
|
||||||
cmdStr := fmt.Sprintf("go run %s", filepath.Join(rootDir, "main.go"))
|
cmdStr := fmt.Sprintf("go run %s", filepath.Join(rootDir, "main.go"))
|
||||||
templateConfigDir := filepath.Join(rootDir, "test", "default_test_config")
|
templateConfigDir := filepath.Join(rootDir, "test", "default_test_config")
|
||||||
|
actualDir := filepath.Join(testPath, "actual")
|
||||||
|
|
||||||
exists, err := osCommand.FileExists(filepath.Join(testPath, "config"))
|
exists, err := osCommand.FileExists(filepath.Join(testPath, "config"))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -201,14 +217,14 @@ func runLazygit(t *testing.T, testPath string, rootDir string, record bool, spee
|
|||||||
templateConfigDir = filepath.Join(testPath, "config")
|
templateConfigDir = filepath.Join(testPath, "config")
|
||||||
}
|
}
|
||||||
|
|
||||||
configDir := filepath.Join(rootDir, "test", "integration_test_config")
|
configDir := filepath.Join(testPath, "used_config")
|
||||||
|
|
||||||
err = os.RemoveAll(configDir)
|
err = os.RemoveAll(configDir)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = oscommands.CopyDir(templateConfigDir, configDir)
|
err = oscommands.CopyDir(templateConfigDir, configDir)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
cmdStr = fmt.Sprintf("%s --use-config-dir=%s", cmdStr, configDir)
|
cmdStr = fmt.Sprintf("%s --use-config-dir=%s --path=%s", cmdStr, configDir, actualDir)
|
||||||
|
|
||||||
cmd := osCommand.ExecutableFromString(cmdStr)
|
cmd := osCommand.ExecutableFromString(cmdStr)
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("REPLAY_SPEED=%d", speed))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("REPLAY_SPEED=%d", speed))
|
||||||
@ -226,7 +242,7 @@ func runLazygit(t *testing.T, testPath string, rootDir string, record bool, spee
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if we're on CI we'll need to use a PTY. We can work that out by seeing if the 'TERM' env is defined.
|
// if we're on CI we'll need to use a PTY. We can work that out by seeing if the 'TERM' env is defined.
|
||||||
if os.Getenv("TERM") == "" {
|
if usePty() {
|
||||||
cmd.Env = append(cmd.Env, "TERM=xterm")
|
cmd.Env = append(cmd.Env, "TERM=xterm")
|
||||||
|
|
||||||
f, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: 100, Cols: 100})
|
f, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: 100, Cols: 100})
|
||||||
@ -243,8 +259,13 @@ func runLazygit(t *testing.T, testPath string, rootDir string, record bool, spee
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareIntegrationTestDir() {
|
func usePty() bool {
|
||||||
path := filepath.Join("test", "integration_test")
|
return true
|
||||||
|
return os.Getenv("TERM") == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareIntegrationTestDir(testPath string) {
|
||||||
|
path := filepath.Join(testPath, "actual")
|
||||||
|
|
||||||
// remove contents of integration test directory
|
// remove contents of integration test directory
|
||||||
dir, err := ioutil.ReadDir(path)
|
dir, err := ioutil.ReadDir(path)
|
||||||
@ -261,8 +282,4 @@ func prepareIntegrationTestDir() {
|
|||||||
for _, d := range dir {
|
for _, d := range dir {
|
||||||
os.RemoveAll(filepath.Join(path, d.Name()))
|
os.RemoveAll(filepath.Join(path, d.Name()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Chdir(path); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,11 @@ func (gui *Gui) replayRecordedEvents() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Second * 20)
|
||||||
|
log.Fatal("20 seconds is up, lazygit recording took too long to complete")
|
||||||
|
}()
|
||||||
|
|
||||||
events, err := gui.loadRecordedEvents()
|
events, err := gui.loadRecordedEvents()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@ -49,13 +54,26 @@ func (gui *Gui) replayRecordedEvents() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, event := range events {
|
// The playback could be paused at any time because integration tests run concurrently.
|
||||||
|
// Therefore we can't just check for a given event whether we've passed its timestamp,
|
||||||
|
// or else we'll have an explosion of keypresses after the test is resumed.
|
||||||
|
// We need to check if we've waited long enough since the last event was replayed.
|
||||||
|
for i, event := range events {
|
||||||
|
var prevEventTimestamp int64 = 0
|
||||||
|
if i > 0 {
|
||||||
|
prevEventTimestamp = events[i-1].Timestamp
|
||||||
|
}
|
||||||
|
timeToWait := (event.Timestamp - prevEventTimestamp) / int64(speed)
|
||||||
|
if i == 0 {
|
||||||
|
timeToWait += leeway
|
||||||
|
}
|
||||||
|
var timeWaited int64 = 0
|
||||||
middle:
|
middle:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
now := gui.timeSinceStart()*int64(speed) - leeway
|
timeWaited += 1
|
||||||
if gui.g != nil && now >= event.Timestamp {
|
if gui.g != nil && timeWaited >= timeToWait {
|
||||||
gui.g.ReplayedEvents <- *event.Event
|
gui.g.ReplayedEvents <- *event.Event
|
||||||
break middle
|
break middle
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/manyCommits.sh
vendored
2
test/fixtures/manyCommits.sh
vendored
@ -1,5 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd $1
|
||||||
|
|
||||||
git init
|
git init
|
||||||
|
|
||||||
git config user.email "CI@example.com"
|
git config user.email "CI@example.com"
|
||||||
|
2
test/fixtures/mergeConflicts.sh
vendored
2
test/fixtures/mergeConflicts.sh
vendored
@ -1,5 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd $1
|
||||||
|
|
||||||
git init
|
git init
|
||||||
git config user.email "CI@example.com"
|
git config user.email "CI@example.com"
|
||||||
git config user.name "CI"
|
git config user.name "CI"
|
||||||
|
2
test/fixtures/newFile.sh
vendored
2
test/fixtures/newFile.sh
vendored
@ -1,5 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd $1
|
||||||
|
|
||||||
git init
|
git init
|
||||||
|
|
||||||
git config user.email "CI@example.com"
|
git config user.email "CI@example.com"
|
||||||
|
2
test/fixtures/newFile2.sh
vendored
2
test/fixtures/newFile2.sh
vendored
@ -1,5 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd $1
|
||||||
|
|
||||||
git init
|
git init
|
||||||
|
|
||||||
git config user.email "CI@example.com"
|
git config user.email "CI@example.com"
|
||||||
|
2
test/fixtures/unstagedFiles.sh
vendored
2
test/fixtures/unstagedFiles.sh
vendored
@ -1,5 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd $1
|
||||||
|
|
||||||
git init
|
git init
|
||||||
|
|
||||||
git config user.email "CI@example.com"
|
git config user.email "CI@example.com"
|
||||||
|
2
test/fixtures/updatedFile.sh
vendored
2
test/fixtures/updatedFile.sh
vendored
@ -1,5 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd $1
|
||||||
|
|
||||||
git init
|
git init
|
||||||
|
|
||||||
git config user.email "CI@example.com"
|
git config user.email "CI@example.com"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user