mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-23 12:18:51 +02:00
add new integration test pattern
This commit is contained in:
parent
c7f9d5801b
commit
77881a9c7d
.gitignoremain.gorecording.jsonsetup.shtest.json
pkg
test
integration
branchSuggestions
expected/repo
recording.jsonsetup.shtest.jsoncommit
expected/repo
.git_keep
myfile1myfile2myfile3myfile4myfile5commitsNewBranch
expected/repo
recording.jsonsetup.shtest.jsonintegration_new/branch/suggestions/expected/repo/.git_keep
8
.gitignore
vendored
8
.gitignore
vendored
@ -33,11 +33,19 @@ lazygit.exe
|
||||
!.gitmodules_keep
|
||||
|
||||
test/git_server/data
|
||||
|
||||
# we'll scrap these lines once we've fully moved over to the new integration test approach
|
||||
test/integration/*/actual/
|
||||
test/integration/*/used_config/
|
||||
# these sample hooks waste too much space
|
||||
test/integration/*/expected/**/hooks/
|
||||
test/integration/*/expected_remote/**/hooks/
|
||||
|
||||
test/integration_new/**/actual/
|
||||
test/integration_new/**/used_config/
|
||||
# these sample hooks waste too much space
|
||||
test/integration_new/**/expected/**/hooks/
|
||||
test/integration_new/**/expected_remote/**/hooks/
|
||||
|
||||
oryxBuildBinary
|
||||
__debug_bin
|
5
main.go
5
main.go
@ -16,6 +16,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/env"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration"
|
||||
"github.com/jesseduffield/lazygit/pkg/logs"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
yaml "github.com/jesseduffield/yaml"
|
||||
@ -150,6 +151,10 @@ func main() {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if test, ok := integration.CurrentIntegrationTest(); ok {
|
||||
test.SetupConfig(appConfig)
|
||||
}
|
||||
|
||||
common, err := app.NewCommon(appConfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
88
pkg/gui/assert.go
Normal file
88
pkg/gui/assert.go
Normal file
@ -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)
|
||||
}
|
@ -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
|
||||
|
@ -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))
|
||||
|
93
pkg/gui/input.go
Normal file
93
pkg/gui/input.go
Normal file
@ -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)
|
||||
}
|
76
pkg/gui/old_gui_test.go
Normal file
76
pkg/gui/old_gui_test.go
Normal file
@ -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)
|
||||
}
|
63
pkg/gui/test_mode.go
Normal file
63
pkg/gui/test_mode.go
Normal file
@ -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")
|
||||
})
|
||||
}
|
||||
}
|
46
pkg/integration/env.go
Normal file
46
pkg/integration/env.go
Normal file
@ -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")
|
||||
}
|
@ -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"`
|
||||
}
|
||||
// re-exporting this so that clients only need to import one package
|
||||
var Tests = integration_tests.Tests
|
||||
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
findOrCreateDir(testPath)
|
||||
prepareIntegrationTestDir(actualDir)
|
||||
findOrCreateDir(actualRepoDir)
|
||||
err := createFixture(testPath, actualRepoDir)
|
||||
configDir := filepath.Join(testPath, "used_config")
|
||||
|
||||
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)
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = runCmd(cmd)
|
||||
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 mode == UPDATE_SNAPSHOT || mode == RECORD {
|
||||
// create/update snapshot
|
||||
err = oscommands.CopyDir(actualDir, expectedDir)
|
||||
// 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 err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := renameSpecialPaths(expectedDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
func findOrCreateDir(path string) {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(path, 0o777)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note that we don't actually store this snapshot in the lazygit repo.
|
||||
// Instead we store the whole expected git repo of our test, so that
|
||||
// we can easily change what we want to compare without needing to regenerate
|
||||
// snapshots for each test.
|
||||
func generateSnapshot(dir string) (string, error) {
|
||||
osCommand := oscommands.NewDummyOSCommand()
|
||||
|
||||
_, err := os.Stat(filepath.Join(dir, ".git"))
|
||||
if err != nil {
|
||||
return "git directory not found", nil
|
||||
}
|
||||
|
||||
snapshot := ""
|
||||
|
||||
cmdStrs := []string{
|
||||
`remote show -n origin`, // remote branches
|
||||
// TODO: find a way to bring this back without breaking tests
|
||||
// `ls-remote origin`,
|
||||
`status`, // file tree
|
||||
`log --pretty=%B|%an|%ae -p -1`, // log
|
||||
`tag -n`, // tags
|
||||
`stash list`, // stash
|
||||
`submodule foreach 'git status'`, // submodule status
|
||||
`submodule foreach 'git log --pretty=%B -p -1'`, // submodule log
|
||||
`submodule foreach 'git tag -n'`, // submodule tags
|
||||
`submodule foreach 'git stash list'`, // submodule stash
|
||||
}
|
||||
|
||||
for _, cmdStr := range cmdStrs {
|
||||
// ignoring error for now. If there's an error it could be that there are no results
|
||||
output, _ := osCommand.Cmd.New(fmt.Sprintf("git -C %s %s", dir, cmdStr)).RunWithOutput()
|
||||
|
||||
snapshot += fmt.Sprintf("git %s:\n%s\n", cmdStr, output)
|
||||
}
|
||||
|
||||
snapshot += "files in repo:\n"
|
||||
err = filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.IsDir() {
|
||||
if f.Name() == ".git" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
bytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
relativePath, err := filepath.Rel(dir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
snapshot += fmt.Sprintf("path: %s\ncontent:\n%s\n", relativePath, string(bytes))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
func generateSnapshots(actualDir string, expectedDir string) (string, string, error) {
|
||||
actual, err := generateSnapshot(actualDir)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// there are a couple of reasons we're not generating the snapshot in expectedDir directly:
|
||||
// Firstly we don't want to have to revert our .git file back to .git_keep.
|
||||
// Secondly, the act of calling git commands like 'git status' actually changes the index
|
||||
// for some reason, and we don't want to leave your lazygit working tree dirty as a result.
|
||||
expectedDirCopyDir := filepath.Join(filepath.Dir(expectedDir), "expected_dir_test")
|
||||
err = oscommands.CopyDir(expectedDir, expectedDirCopyDir)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := os.RemoveAll(expectedDirCopyDir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := restoreSpecialPaths(expectedDirCopyDir); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
expected, err := generateSnapshot(expectedDirCopyDir)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return actual, expected, nil
|
||||
}
|
||||
|
||||
func getPathsToRename(dir string, needle string, contains string) []string {
|
||||
pathsToRename := []string{}
|
||||
|
||||
err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.Name() == needle && (contains == "" || strings.Contains(path, contains)) {
|
||||
pathsToRename = append(pathsToRename, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
// changing directory back to rootDir after the setup is done
|
||||
if err := os.Chdir(rootDir); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return pathsToRename
|
||||
}
|
||||
|
||||
var specialPathMappings = []struct{ original, new, contains string }{
|
||||
// git refuses to track .git or .gitmodules in subdirectories so we need to rename them
|
||||
{".git", ".git_keep", ""},
|
||||
{".gitmodules", ".gitmodules_keep", ""},
|
||||
// we also need git to ignore the contents of our test gitignore files so that
|
||||
// we actually commit files that are ignored within the test.
|
||||
{".gitignore", "lg_ignore_file", ""},
|
||||
// this is the .git/info/exclude file. We're being a little more specific here
|
||||
// so that we don't accidentally mess with some other file named 'exclude' in the test.
|
||||
{"exclude", "lg_exclude_file", ".git/info/exclude"},
|
||||
}
|
||||
|
||||
func renameSpecialPaths(dir string) error {
|
||||
for _, specialPath := range specialPathMappings {
|
||||
for _, path := range getPathsToRename(dir, specialPath.original, specialPath.contains) {
|
||||
err := os.Rename(path, filepath.Join(filepath.Dir(path), specialPath.new))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func restoreSpecialPaths(dir string) error {
|
||||
for _, specialPath := range specialPathMappings {
|
||||
for _, path := range getPathsToRename(dir, specialPath.new, specialPath.contains) {
|
||||
err := os.Rename(path, filepath.Join(filepath.Dir(path), specialPath.original))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLazygitCommand(testPath string, rootDir string, mode Mode, speed float64, extraCmdArgs string) (*exec.Cmd, error) {
|
||||
func getLazygitCommandNew(test types.Test, testPath string, rootDir string) (*exec.Cmd, error) {
|
||||
osCommand := oscommands.NewDummyOSCommand()
|
||||
|
||||
replayPath := filepath.Join(testPath, "recording.json")
|
||||
templateConfigDir := filepath.Join(rootDir, "test", "default_test_config")
|
||||
actualRepoDir := filepath.Join(testPath, "actual", "repo")
|
||||
|
||||
exists, err := osCommand.FileExists(filepath.Join(testPath, "config"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
templateConfigDir = filepath.Join(testPath, "config")
|
||||
}
|
||||
|
||||
configDir := filepath.Join(testPath, "used_config")
|
||||
|
||||
err = os.RemoveAll(configDir)
|
||||
err := os.RemoveAll(configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -546,17 +173,11 @@ func getLazygitCommand(testPath string, rootDir string, mode Mode, speed float64
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), configDir, actualRepoDir, extraCmdArgs)
|
||||
cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), configDir, actualRepoDir, test.ExtraCmdArgs())
|
||||
|
||||
cmdObj := osCommand.Cmd.New(cmdStr)
|
||||
cmdObj.AddEnvVars(fmt.Sprintf("SPEED=%f", speed))
|
||||
|
||||
switch mode {
|
||||
case RECORD:
|
||||
cmdObj.AddEnvVars(fmt.Sprintf("RECORD_EVENTS_TO=%s", replayPath))
|
||||
case TEST, UPDATE_SNAPSHOT:
|
||||
cmdObj.AddEnvVars(fmt.Sprintf("REPLAY_EVENTS_FROM=%s", replayPath))
|
||||
}
|
||||
cmdObj.AddEnvVars(fmt.Sprintf("LAZYGIT_TEST_NAME=%s", test.Name()))
|
||||
|
||||
return cmdObj.GetCmd(), nil
|
||||
}
|
||||
|
564
pkg/integration/integration_old.go
Normal file
564
pkg/integration/integration_old.go
Normal file
@ -0,0 +1,564 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// This package is for running our integration test suite. See docs/Integration_Tests.md for more info.
|
||||
|
||||
// Deprecated: This file is part of the old way of doing things. See integration.go for the new way
|
||||
|
||||
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, updateSnapshot, 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.
|
||||
func RunTests(
|
||||
logf func(format string, formatArgs ...interface{}),
|
||||
runCmd func(cmd *exec.Cmd) error,
|
||||
fnWrapper func(test *Test, f func(*testing.T) error),
|
||||
mode Mode,
|
||||
speedEnv string,
|
||||
onFail func(t *testing.T, expected string, actual string, prefix string),
|
||||
includeSkipped bool,
|
||||
) error {
|
||||
rootDir := GetRootDirectory()
|
||||
err := os.Chdir(rootDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
testDir := filepath.Join(rootDir, "test", "integration")
|
||||
|
||||
osCommand := oscommands.NewDummyOSCommand()
|
||||
err = osCommand.Cmd.New("go build -o " + tempLazygitPath()).Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tests, err := LoadTests(testDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
|
||||
speeds := getTestSpeeds(test.Speed, mode, speedEnv)
|
||||
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 := createFixture(testPath, actualRepoDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configDir := filepath.Join(testPath, "used_config")
|
||||
|
||||
cmd, err := getLazygitCommand(testPath, rootDir, mode, speed, test.ExtraCmdArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = runCmd(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mode == UPDATE_SNAPSHOT || mode == RECORD {
|
||||
// create/update snapshot
|
||||
err = oscommands.CopyDir(actualDir, expectedDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := renameSpecialPaths(expectedDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
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 err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for {
|
||||
_, err := os.Stat(filepath.Join(path, ".git"))
|
||||
|
||||
if err == nil {
|
||||
return path
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func findOrCreateDir(path string) {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(path, 0o777)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note that we don't actually store this snapshot in the lazygit repo.
|
||||
// Instead we store the whole expected git repo of our test, so that
|
||||
// we can easily change what we want to compare without needing to regenerate
|
||||
// snapshots for each test.
|
||||
func generateSnapshot(dir string) (string, error) {
|
||||
osCommand := oscommands.NewDummyOSCommand()
|
||||
|
||||
_, err := os.Stat(filepath.Join(dir, ".git"))
|
||||
if err != nil {
|
||||
return "git directory not found", nil
|
||||
}
|
||||
|
||||
snapshot := ""
|
||||
|
||||
cmdStrs := []string{
|
||||
`remote show -n origin`, // remote branches
|
||||
// TODO: find a way to bring this back without breaking tests
|
||||
// `ls-remote origin`,
|
||||
`status`, // file tree
|
||||
`log --pretty=%B|%an|%ae -p -1`, // log
|
||||
`tag -n`, // tags
|
||||
`stash list`, // stash
|
||||
`submodule foreach 'git status'`, // submodule status
|
||||
`submodule foreach 'git log --pretty=%B -p -1'`, // submodule log
|
||||
`submodule foreach 'git tag -n'`, // submodule tags
|
||||
`submodule foreach 'git stash list'`, // submodule stash
|
||||
}
|
||||
|
||||
for _, cmdStr := range cmdStrs {
|
||||
// ignoring error for now. If there's an error it could be that there are no results
|
||||
output, _ := osCommand.Cmd.New(fmt.Sprintf("git -C %s %s", dir, cmdStr)).RunWithOutput()
|
||||
|
||||
snapshot += fmt.Sprintf("git %s:\n%s\n", cmdStr, output)
|
||||
}
|
||||
|
||||
snapshot += "files in repo:\n"
|
||||
err = filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.IsDir() {
|
||||
if f.Name() == ".git" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
bytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
relativePath, err := filepath.Rel(dir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
snapshot += fmt.Sprintf("path: %s\ncontent:\n%s\n", relativePath, string(bytes))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
func generateSnapshots(actualDir string, expectedDir string) (string, string, error) {
|
||||
actual, err := generateSnapshot(actualDir)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// there are a couple of reasons we're not generating the snapshot in expectedDir directly:
|
||||
// Firstly we don't want to have to revert our .git file back to .git_keep.
|
||||
// Secondly, the act of calling git commands like 'git status' actually changes the index
|
||||
// for some reason, and we don't want to leave your lazygit working tree dirty as a result.
|
||||
expectedDirCopyDir := filepath.Join(filepath.Dir(expectedDir), "expected_dir_test")
|
||||
err = oscommands.CopyDir(expectedDir, expectedDirCopyDir)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := os.RemoveAll(expectedDirCopyDir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := restoreSpecialPaths(expectedDirCopyDir); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
expected, err := generateSnapshot(expectedDirCopyDir)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return actual, expected, nil
|
||||
}
|
||||
|
||||
func getPathsToRename(dir string, needle string, contains string) []string {
|
||||
pathsToRename := []string{}
|
||||
|
||||
err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.Name() == needle && (contains == "" || strings.Contains(path, contains)) {
|
||||
pathsToRename = append(pathsToRename, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return pathsToRename
|
||||
}
|
||||
|
||||
var specialPathMappings = []struct{ original, new, contains string }{
|
||||
// git refuses to track .git or .gitmodules in subdirectories so we need to rename them
|
||||
{".git", ".git_keep", ""},
|
||||
{".gitmodules", ".gitmodules_keep", ""},
|
||||
// we also need git to ignore the contents of our test gitignore files so that
|
||||
// we actually commit files that are ignored within the test.
|
||||
{".gitignore", "lg_ignore_file", ""},
|
||||
// this is the .git/info/exclude file. We're being a little more specific here
|
||||
// so that we don't accidentally mess with some other file named 'exclude' in the test.
|
||||
{"exclude", "lg_exclude_file", ".git/info/exclude"},
|
||||
}
|
||||
|
||||
func renameSpecialPaths(dir string) error {
|
||||
for _, specialPath := range specialPathMappings {
|
||||
for _, path := range getPathsToRename(dir, specialPath.original, specialPath.contains) {
|
||||
err := os.Rename(path, filepath.Join(filepath.Dir(path), specialPath.new))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func restoreSpecialPaths(dir string) error {
|
||||
for _, specialPath := range specialPathMappings {
|
||||
for _, path := range getPathsToRename(dir, specialPath.new, specialPath.contains) {
|
||||
err := os.Rename(path, filepath.Join(filepath.Dir(path), specialPath.original))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLazygitCommand(testPath string, rootDir string, mode Mode, speed float64, extraCmdArgs string) (*exec.Cmd, error) {
|
||||
osCommand := oscommands.NewDummyOSCommand()
|
||||
|
||||
replayPath := filepath.Join(testPath, "recording.json")
|
||||
templateConfigDir := filepath.Join(rootDir, "test", "default_test_config")
|
||||
actualRepoDir := filepath.Join(testPath, "actual", "repo")
|
||||
|
||||
exists, err := osCommand.FileExists(filepath.Join(testPath, "config"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
templateConfigDir = filepath.Join(testPath, "config")
|
||||
}
|
||||
|
||||
configDir := filepath.Join(testPath, "used_config")
|
||||
|
||||
err = os.RemoveAll(configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = oscommands.CopyDir(templateConfigDir, configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), configDir, actualRepoDir, extraCmdArgs)
|
||||
|
||||
cmdObj := osCommand.Cmd.New(cmdStr)
|
||||
cmdObj.AddEnvVars(fmt.Sprintf("SPEED=%f", speed))
|
||||
|
||||
switch mode {
|
||||
case RECORD:
|
||||
cmdObj.AddEnvVars(fmt.Sprintf("RECORD_EVENTS_TO=%s", replayPath))
|
||||
case TEST, UPDATE_SNAPSHOT:
|
||||
cmdObj.AddEnvVars(fmt.Sprintf("REPLAY_EVENTS_FROM=%s", replayPath))
|
||||
}
|
||||
|
||||
return cmdObj.GetCmd(), nil
|
||||
}
|
40
pkg/integration/integration_tests/branch/suggestions.go
Normal file
40
pkg/integration/integration_tests/branch/suggestions.go
Normal file
@ -0,0 +1,40 @@
|
||||
package branch
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/types"
|
||||
)
|
||||
|
||||
var Suggestions = types.NewTest(types.NewTestArgs{
|
||||
Description: "Checking out a branch with name suggestions",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell types.Shell) {
|
||||
shell.
|
||||
EmptyCommit("my commit message").
|
||||
NewBranch("new-branch").
|
||||
NewBranch("new-branch-2").
|
||||
NewBranch("new-branch-3").
|
||||
NewBranch("branch-to-checkout").
|
||||
NewBranch("other-new-branch-2").
|
||||
NewBranch("other-new-branch-3")
|
||||
},
|
||||
Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) {
|
||||
input.SwitchToBranchesWindow()
|
||||
|
||||
input.PushKeys(keys.Branches.CheckoutBranchByName)
|
||||
assert.CurrentViewName("confirmation")
|
||||
|
||||
input.Type("branch-to")
|
||||
|
||||
input.PushKeys(keys.Universal.TogglePanel)
|
||||
assert.CurrentViewName("suggestions")
|
||||
|
||||
// we expect the first suggestion to be the branch we want because it most
|
||||
// closely matches what we typed in
|
||||
input.Confirm()
|
||||
|
||||
assert.CurrentBranchName("branch-to-checkout")
|
||||
},
|
||||
})
|
32
pkg/integration/integration_tests/commit/commit.go
Normal file
32
pkg/integration/integration_tests/commit/commit.go
Normal file
@ -0,0 +1,32 @@
|
||||
package commit
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/types"
|
||||
)
|
||||
|
||||
var Commit = types.NewTest(types.NewTestArgs{
|
||||
Description: "Staging a couple files and committing",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell types.Shell) {
|
||||
shell.CreateFile("myfile", "myfile content")
|
||||
shell.CreateFile("myfile2", "myfile2 content")
|
||||
},
|
||||
Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) {
|
||||
assert.CommitCount(0)
|
||||
|
||||
input.Select()
|
||||
input.NextItem()
|
||||
input.Select()
|
||||
input.PushKeys(keys.Files.CommitChanges)
|
||||
|
||||
commitMessage := "my commit message"
|
||||
input.Type(commitMessage)
|
||||
input.Confirm()
|
||||
|
||||
assert.CommitCount(1)
|
||||
assert.HeadCommitMessage(commitMessage)
|
||||
},
|
||||
})
|
38
pkg/integration/integration_tests/commit/new_branch.go
Normal file
38
pkg/integration/integration_tests/commit/new_branch.go
Normal file
@ -0,0 +1,38 @@
|
||||
package commit
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/types"
|
||||
)
|
||||
|
||||
var NewBranch = types.NewTest(types.NewTestArgs{
|
||||
Description: "Creating a new branch from a commit",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell types.Shell) {
|
||||
shell.
|
||||
EmptyCommit("commit 1").
|
||||
EmptyCommit("commit 2").
|
||||
EmptyCommit("commit 3")
|
||||
},
|
||||
Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) {
|
||||
assert.CommitCount(3)
|
||||
|
||||
input.SwitchToCommitsWindow()
|
||||
assert.CurrentViewName("commits")
|
||||
input.NextItem()
|
||||
|
||||
input.PushKeys(keys.Universal.New)
|
||||
|
||||
assert.CurrentViewName("confirmation")
|
||||
|
||||
branchName := "my-branch-name"
|
||||
input.Type(branchName)
|
||||
input.Confirm()
|
||||
|
||||
assert.CommitCount(2)
|
||||
assert.HeadCommitMessage("commit 2")
|
||||
assert.CurrentBranchName(branchName)
|
||||
},
|
||||
})
|
16
pkg/integration/integration_tests/tests.go
Normal file
16
pkg/integration/integration_tests/tests.go
Normal file
@ -0,0 +1,16 @@
|
||||
package integration_tests
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/integration_tests/branch"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/integration_tests/commit"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/types"
|
||||
)
|
||||
|
||||
// Here is where we lists the actual tests that will run. When you create a new test,
|
||||
// be sure to add it to this list.
|
||||
|
||||
var Tests = []types.Test{
|
||||
commit.Commit,
|
||||
commit.NewBranch,
|
||||
branch.Suggestions,
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package gui
|
||||
package integration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -10,23 +10,10 @@ import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
)
|
||||
|
||||
func recordingEvents() bool {
|
||||
return recordEventsTo() != ""
|
||||
}
|
||||
// this all relates to the old way of doing integration tests where you record yourself
|
||||
// and then replay the events.
|
||||
|
||||
func recordEventsTo() string {
|
||||
return os.Getenv("RECORD_EVENTS_TO")
|
||||
}
|
||||
|
||||
func replaying() bool {
|
||||
return os.Getenv("REPLAY_EVENTS_FROM") != ""
|
||||
}
|
||||
|
||||
func headless() bool {
|
||||
return os.Getenv("HEADLESS") != ""
|
||||
}
|
||||
|
||||
func getRecordingSpeed() float64 {
|
||||
func GetRecordingSpeed() float64 {
|
||||
// humans are slow so this speeds things up.
|
||||
speed := 1.0
|
||||
envReplaySpeed := os.Getenv("SPEED")
|
||||
@ -40,7 +27,7 @@ func getRecordingSpeed() float64 {
|
||||
return speed
|
||||
}
|
||||
|
||||
func (gui *Gui) loadRecording() (*gocui.Recording, error) {
|
||||
func LoadRecording() (*gocui.Recording, error) {
|
||||
path := os.Getenv("REPLAY_EVENTS_FROM")
|
||||
|
||||
data, err := ioutil.ReadFile(path)
|
||||
@ -58,8 +45,8 @@ func (gui *Gui) loadRecording() (*gocui.Recording, error) {
|
||||
return recording, nil
|
||||
}
|
||||
|
||||
func (gui *Gui) saveRecording(recording *gocui.Recording) error {
|
||||
if !recordingEvents() {
|
||||
func SaveRecording(recording *gocui.Recording) error {
|
||||
if !RecordingEvents() {
|
||||
return nil
|
||||
}
|
||||
|
53
pkg/integration/shell.go
Normal file
53
pkg/integration/shell.go
Normal file
@ -0,0 +1,53 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
"github.com/mgutz/str"
|
||||
)
|
||||
|
||||
type ShellImpl struct{}
|
||||
|
||||
var _ types.Shell = &ShellImpl{}
|
||||
|
||||
func (s *ShellImpl) RunCommand(cmdStr string) types.Shell {
|
||||
args := str.ToArgv(cmdStr)
|
||||
cmd := secureexec.Command(args[0], args[1:]...)
|
||||
cmd.Env = os.Environ()
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error running command: %s\n%s", cmdStr, string(output)))
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *ShellImpl) CreateFile(path string, content string) types.Shell {
|
||||
err := ioutil.WriteFile(path, []byte(content), 0o644)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error creating file: %s\n%s", path, err))
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *ShellImpl) NewBranch(name string) types.Shell {
|
||||
return s.RunCommand("git checkout -b " + name)
|
||||
}
|
||||
|
||||
func (s *ShellImpl) GitAddAll() types.Shell {
|
||||
return s.RunCommand("git add -A")
|
||||
}
|
||||
|
||||
func (s *ShellImpl) Commit(message string) types.Shell {
|
||||
return s.RunCommand(fmt.Sprintf("git commit -m \"%s\"", message))
|
||||
}
|
||||
|
||||
func (s *ShellImpl) EmptyCommit(message string) types.Shell {
|
||||
return s.RunCommand(fmt.Sprintf("git commit --allow-empty -m \"%s\"", message))
|
||||
}
|
152
pkg/integration/types/types.go
Normal file
152
pkg/integration/types/types.go
Normal file
@ -0,0 +1,152 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type Test interface {
|
||||
Name() string
|
||||
Description() string
|
||||
// this is called before lazygit is run, for the sake of preparing the repo
|
||||
SetupRepo(Shell)
|
||||
// this gives you the default config and lets you set whatever values on it you like,
|
||||
// so that they appear when lazygit runs
|
||||
SetupConfig(config *config.AppConfig)
|
||||
// this is called upon lazygit starting
|
||||
Run(Shell, Input, Assert, config.KeybindingConfig)
|
||||
// e.g. '-debug'
|
||||
ExtraCmdArgs() string
|
||||
// for tests that are flakey and when we don't have time to fix them
|
||||
Skip() bool
|
||||
}
|
||||
|
||||
// this is for running shell commands, mostly for the sake of setting up the repo
|
||||
// but you can also run the commands from within lazygit to emulate things happening
|
||||
// in the background.
|
||||
// Implementation is at pkg/integration/shell.go
|
||||
type Shell interface {
|
||||
RunCommand(command string) Shell
|
||||
CreateFile(name string, content string) Shell
|
||||
NewBranch(branchName string) Shell
|
||||
GitAddAll() Shell
|
||||
Commit(message string) Shell
|
||||
EmptyCommit(message string) Shell
|
||||
}
|
||||
|
||||
// through this interface our test interacts with the lazygit gui
|
||||
// Implementation is at pkg/gui/input.go
|
||||
type Input interface {
|
||||
// key is something like 'w' or '<space>'. It's best not to pass a direct value,
|
||||
// but instead to go through the default user config to get a more meaningful key name
|
||||
PushKeys(keys ...string)
|
||||
// for typing into a popup prompt
|
||||
Type(content string)
|
||||
// for when you want to allow lazygit to process something before continuing
|
||||
Wait(milliseconds int)
|
||||
// going straight to a particular side window
|
||||
SwitchToStatusWindow()
|
||||
SwitchToFilesWindow()
|
||||
SwitchToBranchesWindow()
|
||||
SwitchToCommitsWindow()
|
||||
SwitchToStashWindow()
|
||||
// i.e. pressing enter
|
||||
Confirm()
|
||||
// i.e. pressing escape
|
||||
Cancel()
|
||||
// i.e. pressing space
|
||||
Select()
|
||||
// i.e. pressing down arrow
|
||||
NextItem()
|
||||
// i.e. pressing up arrow
|
||||
PreviousItem()
|
||||
}
|
||||
|
||||
// through this interface we assert on the state of the lazygit gui
|
||||
type Assert interface {
|
||||
WorkingTreeFileCount(int)
|
||||
CommitCount(int)
|
||||
HeadCommitMessage(string)
|
||||
CurrentViewName(expectedViewName string)
|
||||
CurrentBranchName(expectedBranchName string)
|
||||
}
|
||||
|
||||
type TestImpl struct {
|
||||
name string
|
||||
description string
|
||||
extraCmdArgs string
|
||||
skip bool
|
||||
setupRepo func(shell Shell)
|
||||
setupConfig func(config *config.AppConfig)
|
||||
run func(
|
||||
shell Shell,
|
||||
input Input,
|
||||
assert Assert,
|
||||
keys config.KeybindingConfig,
|
||||
)
|
||||
}
|
||||
|
||||
type NewTestArgs struct {
|
||||
Description string
|
||||
SetupRepo func(shell Shell)
|
||||
SetupConfig func(config *config.AppConfig)
|
||||
Run func(shell Shell, input Input, assert Assert, keys config.KeybindingConfig)
|
||||
ExtraCmdArgs string
|
||||
Skip bool
|
||||
}
|
||||
|
||||
func NewTest(args NewTestArgs) *TestImpl {
|
||||
return &TestImpl{
|
||||
name: testNameFromFilePath(),
|
||||
description: args.Description,
|
||||
extraCmdArgs: args.ExtraCmdArgs,
|
||||
skip: args.Skip,
|
||||
setupRepo: args.SetupRepo,
|
||||
setupConfig: args.SetupConfig,
|
||||
run: args.Run,
|
||||
}
|
||||
}
|
||||
|
||||
var _ Test = (*TestImpl)(nil)
|
||||
|
||||
func (self *TestImpl) Name() string {
|
||||
return self.name
|
||||
}
|
||||
|
||||
func (self *TestImpl) Description() string {
|
||||
return self.description
|
||||
}
|
||||
|
||||
func (self *TestImpl) ExtraCmdArgs() string {
|
||||
return self.extraCmdArgs
|
||||
}
|
||||
|
||||
func (self *TestImpl) Skip() bool {
|
||||
return self.skip
|
||||
}
|
||||
|
||||
func (self *TestImpl) SetupConfig(config *config.AppConfig) {
|
||||
self.setupConfig(config)
|
||||
}
|
||||
|
||||
func (self *TestImpl) SetupRepo(shell Shell) {
|
||||
self.setupRepo(shell)
|
||||
}
|
||||
|
||||
func (self *TestImpl) Run(
|
||||
shell Shell,
|
||||
input Input,
|
||||
assert Assert,
|
||||
keys config.KeybindingConfig,
|
||||
) {
|
||||
self.run(shell, input, assert, keys)
|
||||
}
|
||||
|
||||
func testNameFromFilePath() string {
|
||||
path := utils.FilePath(3)
|
||||
name := strings.Split(path, "integration/integration_tests/")[1]
|
||||
|
||||
return name[:len(name)-len(".go")]
|
||||
}
|
@ -128,3 +128,10 @@ func StackTrace() string {
|
||||
n := runtime.Stack(buf, false)
|
||||
return fmt.Sprintf("%s\n", buf[:n])
|
||||
}
|
||||
|
||||
// returns the path of the file that calls the function.
|
||||
// 'skip' is the number of stack frames to skip.
|
||||
func FilePath(skip int) string {
|
||||
_, path, _, _ := runtime.Caller(skip)
|
||||
return path
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
file0
|
@ -1 +0,0 @@
|
||||
ref: refs/heads/new-branch-3
|
Binary file not shown.
@ -1,8 +0,0 @@
|
||||
0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 commit (initial): file0
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 checkout: moving from master to new-branch
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 checkout: moving from new-branch to new-branch-2
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 checkout: moving from new-branch-2 to new-branch-3
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 checkout: moving from new-branch-3 to old-branch
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 checkout: moving from old-branch to old-branch-2
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 checkout: moving from old-branch-2 to old-branch-3
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675450 +1000 checkout: moving from old-branch-3 to new-branch-3
|
@ -1 +0,0 @@
|
||||
0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 commit (initial): file0
|
@ -1 +0,0 @@
|
||||
0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 branch: Created from HEAD
|
@ -1 +0,0 @@
|
||||
0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 branch: Created from HEAD
|
@ -1 +0,0 @@
|
||||
0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 branch: Created from HEAD
|
@ -1 +0,0 @@
|
||||
0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 branch: Created from HEAD
|
@ -1 +0,0 @@
|
||||
0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 branch: Created from HEAD
|
@ -1 +0,0 @@
|
||||
0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI <CI@example.com> 1617675445 +1000 branch: Created from HEAD
|
Binary file not shown.
Binary file not shown.
@ -1,2 +0,0 @@
|
||||
xŤÍA
|
||||
Â0Fa×9Ĺ왉“Ä€�ĐUŹ‘4°Đ)<ľ=‚ŰÇoé�Dő4v€Wř�cŚ%‹>ř–«V¶ąTˇQŐšôŻľÓ4Ó}šźř¦öŢpYz{�x >8UGgafsÔc2đ'7uÝŔćÜď+ö
|
@ -1 +0,0 @@
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8
|
@ -1 +0,0 @@
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8
|
@ -1 +0,0 @@
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8
|
@ -1 +0,0 @@
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8
|
@ -1 +0,0 @@
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8
|
@ -1 +0,0 @@
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8
|
@ -1 +0,0 @@
|
||||
75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8
|
@ -1 +0,0 @@
|
||||
test0
|
@ -1 +0,0 @@
|
||||
{"KeyEvents":[{"Timestamp":639,"Mod":0,"Key":259,"Ch":0},{"Timestamp":1752,"Mod":0,"Key":256,"Ch":99},{"Timestamp":2183,"Mod":0,"Key":256,"Ch":110},{"Timestamp":2271,"Mod":0,"Key":256,"Ch":101},{"Timestamp":2327,"Mod":0,"Key":256,"Ch":119},{"Timestamp":2599,"Mod":0,"Key":256,"Ch":45},{"Timestamp":3583,"Mod":0,"Key":9,"Ch":9},{"Timestamp":3880,"Mod":0,"Key":258,"Ch":0},{"Timestamp":4175,"Mod":0,"Key":13,"Ch":13},{"Timestamp":4815,"Mod":0,"Key":256,"Ch":113}],"ResizeEvents":[{"Timestamp":0,"Width":272,"Height":74}]}
|
@ -1,21 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
cd $1
|
||||
|
||||
git init
|
||||
|
||||
git config user.email "CI@example.com"
|
||||
git config user.name "CI"
|
||||
|
||||
echo test0 > file0
|
||||
git add .
|
||||
git commit -am file0
|
||||
|
||||
git checkout -b new-branch
|
||||
git checkout -b new-branch-2
|
||||
git checkout -b new-branch-3
|
||||
git checkout -b old-branch
|
||||
git checkout -b old-branch-2
|
||||
git checkout -b old-branch-3
|
@ -1 +0,0 @@
|
||||
{ "description": "Checking out a branch with name suggestions", "speed": 100 }
|
@ -1 +0,0 @@
|
||||
commit
|
Binary file not shown.
@ -1,5 +0,0 @@
|
||||
0000000000000000000000000000000000000000 3df3d8761bc0f0828596b11845aeac175b7b7393 CI <CI@example.com> 1617671339 +1000 commit (initial): myfile1
|
||||
3df3d8761bc0f0828596b11845aeac175b7b7393 a7d53cc21fd53100f955377be379423b0e386274 CI <CI@example.com> 1617671339 +1000 commit: myfile2
|
||||
a7d53cc21fd53100f955377be379423b0e386274 4ba4f1ed711a9081fab21bc222469aa5176a01f8 CI <CI@example.com> 1617671339 +1000 commit: myfile3
|
||||
4ba4f1ed711a9081fab21bc222469aa5176a01f8 1440bc6cc888a09dca2329d1060eec6de78d9d21 CI <CI@example.com> 1617671339 +1000 commit: myfile4
|
||||
1440bc6cc888a09dca2329d1060eec6de78d9d21 e7560e2cd4783a261ad32496cefed2d9f69a46e7 CI <CI@example.com> 1617671342 +1000 commit: commit
|
@ -1,5 +0,0 @@
|
||||
0000000000000000000000000000000000000000 3df3d8761bc0f0828596b11845aeac175b7b7393 CI <CI@example.com> 1617671339 +1000 commit (initial): myfile1
|
||||
3df3d8761bc0f0828596b11845aeac175b7b7393 a7d53cc21fd53100f955377be379423b0e386274 CI <CI@example.com> 1617671339 +1000 commit: myfile2
|
||||
a7d53cc21fd53100f955377be379423b0e386274 4ba4f1ed711a9081fab21bc222469aa5176a01f8 CI <CI@example.com> 1617671339 +1000 commit: myfile3
|
||||
4ba4f1ed711a9081fab21bc222469aa5176a01f8 1440bc6cc888a09dca2329d1060eec6de78d9d21 CI <CI@example.com> 1617671339 +1000 commit: myfile4
|
||||
1440bc6cc888a09dca2329d1060eec6de78d9d21 e7560e2cd4783a261ad32496cefed2d9f69a46e7 CI <CI@example.com> 1617671342 +1000 commit: commit
|
BIN
test/integration/commit/expected/repo/.git_keep/objects/0e/6cf0a6b79e8d44e186d812a1f74b43d64fac52
BIN
test/integration/commit/expected/repo/.git_keep/objects/0e/6cf0a6b79e8d44e186d812a1f74b43d64fac52
Binary file not shown.
BIN
test/integration/commit/expected/repo/.git_keep/objects/14/40bc6cc888a09dca2329d1060eec6de78d9d21
BIN
test/integration/commit/expected/repo/.git_keep/objects/14/40bc6cc888a09dca2329d1060eec6de78d9d21
Binary file not shown.
BIN
test/integration/commit/expected/repo/.git_keep/objects/18/0cf8328022becee9aaa2577a8f84ea2b9f3827
BIN
test/integration/commit/expected/repo/.git_keep/objects/18/0cf8328022becee9aaa2577a8f84ea2b9f3827
Binary file not shown.
BIN
test/integration/commit/expected/repo/.git_keep/objects/2b/173c861df433fa43ffad13f80c8b312c5c8bce
BIN
test/integration/commit/expected/repo/.git_keep/objects/2b/173c861df433fa43ffad13f80c8b312c5c8bce
Binary file not shown.
BIN
test/integration/commit/expected/repo/.git_keep/objects/2f/6174050380438f14b16658a356e762435ca591
BIN
test/integration/commit/expected/repo/.git_keep/objects/2f/6174050380438f14b16658a356e762435ca591
Binary file not shown.
BIN
test/integration/commit/expected/repo/.git_keep/objects/30/a1ca3481fdec3245b02aeacfb72ddfe2a433be
BIN
test/integration/commit/expected/repo/.git_keep/objects/30/a1ca3481fdec3245b02aeacfb72ddfe2a433be
Binary file not shown.
@ -1,3 +0,0 @@
|
||||
x�ÍA
|
||||
Â0@Q×9Åì™iÆI
|
||||
"BW=FšL°Ð!R"èííÜ~üÜÌÖÄrê»* J®˜d £ÆÂ¬¥DÕÀû"\S¾.½û³í0Íp›æ‡~’½6½äfw ¡ �¼áLˆèŽzLºþÉ�}ëº)¹2r,Ï
|
BIN
test/integration/commit/expected/repo/.git_keep/objects/4b/a4f1ed711a9081fab21bc222469aa5176a01f8
BIN
test/integration/commit/expected/repo/.git_keep/objects/4b/a4f1ed711a9081fab21bc222469aa5176a01f8
Binary file not shown.
BIN
test/integration/commit/expected/repo/.git_keep/objects/4f/346f1ad5ba2917da2109e2eaa2f2dfbb86f10f
BIN
test/integration/commit/expected/repo/.git_keep/objects/4f/346f1ad5ba2917da2109e2eaa2f2dfbb86f10f
Binary file not shown.
BIN
test/integration/commit/expected/repo/.git_keep/objects/a5/bce3fd2565d8f458555a0c6f42d0504a848bd5
BIN
test/integration/commit/expected/repo/.git_keep/objects/a5/bce3fd2565d8f458555a0c6f42d0504a848bd5
Binary file not shown.
BIN
test/integration/commit/expected/repo/.git_keep/objects/a7/341a59f0ddeef969e69fb6368266d22b0f2416
BIN
test/integration/commit/expected/repo/.git_keep/objects/a7/341a59f0ddeef969e69fb6368266d22b0f2416
Binary file not shown.
BIN
test/integration/commit/expected/repo/.git_keep/objects/a7/d53cc21fd53100f955377be379423b0e386274
BIN
test/integration/commit/expected/repo/.git_keep/objects/a7/d53cc21fd53100f955377be379423b0e386274
Binary file not shown.
BIN
test/integration/commit/expected/repo/.git_keep/objects/d2/34c5e057fe32c676ea67e8cb38f4625ddaeb54
BIN
test/integration/commit/expected/repo/.git_keep/objects/d2/34c5e057fe32c676ea67e8cb38f4625ddaeb54
Binary file not shown.
BIN
test/integration/commit/expected/repo/.git_keep/objects/df/6b0d2bcc76e6ec0fca20c227104a4f28bac41b
BIN
test/integration/commit/expected/repo/.git_keep/objects/df/6b0d2bcc76e6ec0fca20c227104a4f28bac41b
Binary file not shown.
BIN
test/integration/commit/expected/repo/.git_keep/objects/e7/560e2cd4783a261ad32496cefed2d9f69a46e7
BIN
test/integration/commit/expected/repo/.git_keep/objects/e7/560e2cd4783a261ad32496cefed2d9f69a46e7
Binary file not shown.
@ -1 +0,0 @@
|
||||
e7560e2cd4783a261ad32496cefed2d9f69a46e7
|
@ -1 +0,0 @@
|
||||
test1
|
@ -1 +0,0 @@
|
||||
test2
|
@ -1 +0,0 @@
|
||||
test3
|
@ -1 +0,0 @@
|
||||
test4
|
@ -1 +0,0 @@
|
||||
test5
|
@ -1 +0,0 @@
|
||||
{"KeyEvents":[{"Timestamp":527,"Mod":0,"Key":256,"Ch":32},{"Timestamp":830,"Mod":0,"Key":256,"Ch":99},{"Timestamp":1127,"Mod":0,"Key":256,"Ch":99},{"Timestamp":1190,"Mod":0,"Key":256,"Ch":111},{"Timestamp":1335,"Mod":0,"Key":256,"Ch":109},{"Timestamp":1447,"Mod":0,"Key":256,"Ch":109},{"Timestamp":1583,"Mod":0,"Key":256,"Ch":105},{"Timestamp":1606,"Mod":0,"Key":256,"Ch":116},{"Timestamp":1935,"Mod":0,"Key":13,"Ch":13},{"Timestamp":2353,"Mod":0,"Key":27,"Ch":0}],"ResizeEvents":[{"Timestamp":0,"Width":127,"Height":35}]}
|
@ -1,24 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
cd $1
|
||||
|
||||
git init
|
||||
|
||||
git config user.email "CI@example.com"
|
||||
git config user.name "CI"
|
||||
|
||||
echo test1 > myfile1
|
||||
git add .
|
||||
git commit -am "myfile1"
|
||||
echo test2 > myfile2
|
||||
git add .
|
||||
git commit -am "myfile2"
|
||||
echo test3 > myfile3
|
||||
git add .
|
||||
git commit -am "myfile3"
|
||||
echo test4 > myfile4
|
||||
git add .
|
||||
git commit -am "myfile4"
|
||||
echo test5 > myfile5
|
@ -1 +0,0 @@
|
||||
{ "description": "stage a file and commit the change", "speed": 15 }
|
@ -1 +0,0 @@
|
||||
file2
|
@ -1 +0,0 @@
|
||||
ref: refs/heads/lol
|
Binary file not shown.
@ -1,4 +0,0 @@
|
||||
0000000000000000000000000000000000000000 9901fd9b7766be600bed07f55f1794a759527a98 CI <CI@example.com> 1617674232 +1000 commit (initial): file0
|
||||
9901fd9b7766be600bed07f55f1794a759527a98 0029f9bf66e346d47ede6a501abb5b82bee60096 CI <CI@example.com> 1617674232 +1000 commit: file1
|
||||
0029f9bf66e346d47ede6a501abb5b82bee60096 e1cb250774fb8606d33062518d0ae03831130249 CI <CI@example.com> 1617674232 +1000 commit: file2
|
||||
e1cb250774fb8606d33062518d0ae03831130249 0029f9bf66e346d47ede6a501abb5b82bee60096 CI <CI@example.com> 1617674249 +1000 checkout: moving from master to lol
|
@ -1 +0,0 @@
|
||||
0000000000000000000000000000000000000000 0029f9bf66e346d47ede6a501abb5b82bee60096 CI <CI@example.com> 1617674249 +1000 branch: Created from 0029f9bf66e346d47ede6a501abb5b82bee60096
|
@ -1,3 +0,0 @@
|
||||
0000000000000000000000000000000000000000 9901fd9b7766be600bed07f55f1794a759527a98 CI <CI@example.com> 1617674232 +1000 commit (initial): file0
|
||||
9901fd9b7766be600bed07f55f1794a759527a98 0029f9bf66e346d47ede6a501abb5b82bee60096 CI <CI@example.com> 1617674232 +1000 commit: file1
|
||||
0029f9bf66e346d47ede6a501abb5b82bee60096 e1cb250774fb8606d33062518d0ae03831130249 CI <CI@example.com> 1617674232 +1000 commit: file2
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,2 +0,0 @@
|
||||
x+)JMU03c040031QHヒフI5`ーアコイ燹ヨカwチ�w.ス��モ[H
|
||||
矢y�5�来ミ(桍ァ^-ンW(x9
|
Binary file not shown.
@ -1 +0,0 @@
|
||||
0029f9bf66e346d47ede6a501abb5b82bee60096
|
@ -1 +0,0 @@
|
||||
e1cb250774fb8606d33062518d0ae03831130249
|
@ -1 +0,0 @@
|
||||
test0
|
@ -1 +0,0 @@
|
||||
test1
|
@ -1 +0,0 @@
|
||||
{"KeyEvents":[{"Timestamp":972,"Mod":0,"Key":259,"Ch":0},{"Timestamp":1243,"Mod":0,"Key":259,"Ch":0},{"Timestamp":1812,"Mod":0,"Key":256,"Ch":120},{"Timestamp":2683,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3018,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3033,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3050,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3067,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3084,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3100,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3363,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3499,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3628,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3771,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3908,"Mod":0,"Key":258,"Ch":0},{"Timestamp":4051,"Mod":0,"Key":258,"Ch":0},{"Timestamp":4259,"Mod":0,"Key":258,"Ch":0},{"Timestamp":4883,"Mod":0,"Key":258,"Ch":0},{"Timestamp":5124,"Mod":0,"Key":258,"Ch":0},{"Timestamp":5355,"Mod":0,"Key":258,"Ch":0},{"Timestamp":6083,"Mod":0,"Key":258,"Ch":0},{"Timestamp":6563,"Mod":0,"Key":258,"Ch":0},{"Timestamp":7210,"Mod":0,"Key":258,"Ch":0},{"Timestamp":9475,"Mod":0,"Key":258,"Ch":0},{"Timestamp":10395,"Mod":0,"Key":258,"Ch":0},{"Timestamp":11019,"Mod":0,"Key":258,"Ch":0},{"Timestamp":11346,"Mod":0,"Key":258,"Ch":0},{"Timestamp":11587,"Mod":0,"Key":258,"Ch":0},{"Timestamp":11771,"Mod":0,"Key":258,"Ch":0},{"Timestamp":11883,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12003,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12132,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12268,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12395,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12539,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12667,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12804,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12947,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13075,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13211,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13347,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13475,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13620,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13771,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13883,"Mod":0,"Key":258,"Ch":0},{"Timestamp":14027,"Mod":0,"Key":258,"Ch":0},{"Timestamp":14405,"Mod":0,"Key":27,"Ch":0},{"Timestamp":15540,"Mod":0,"Key":258,"Ch":0},{"Timestamp":15995,"Mod":0,"Key":256,"Ch":110},{"Timestamp":17267,"Mod":0,"Key":256,"Ch":108},{"Timestamp":17396,"Mod":0,"Key":256,"Ch":111},{"Timestamp":17547,"Mod":0,"Key":256,"Ch":108},{"Timestamp":17675,"Mod":0,"Key":13,"Ch":13},{"Timestamp":20195,"Mod":0,"Key":256,"Ch":113}],"ResizeEvents":[{"Timestamp":0,"Width":272,"Height":74}]}
|
@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
cd $1
|
||||
|
||||
git init
|
||||
|
||||
git config user.email "CI@example.com"
|
||||
git config user.name "CI"
|
||||
|
||||
echo test0 > file0
|
||||
git add .
|
||||
git commit -am file0
|
||||
|
||||
echo test1 > file1
|
||||
git add .
|
||||
git commit -am file1
|
||||
|
||||
echo test2 > file2
|
||||
git add .
|
||||
git commit -am file2
|
@ -1 +0,0 @@
|
||||
{ "description": "Reverting a commit. Note here that our snapshot test fails if the commit SHA is included in the message hence the renaming of the revert commit after creating it", "speed": 20 }
|
@ -0,0 +1 @@
|
||||
my commit message
|
@ -0,0 +1 @@
|
||||
ref: refs/heads/branch-to-checkout
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user