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

191 lines
4.5 KiB
Go
Raw Normal View History

2021-12-30 13:11:58 +11:00
package oscommands
import (
"bufio"
"fmt"
2022-01-03 15:15:26 +11:00
"regexp"
"runtime"
2021-12-30 13:11:58 +11:00
"strings"
"sync"
2021-12-30 13:11:58 +11:00
"testing"
"github.com/go-errors/errors"
"github.com/samber/lo"
"golang.org/x/exp/slices"
2021-12-30 13:11:58 +11:00
)
2021-12-30 13:35:10 +11:00
// for use in testing
2021-12-30 13:11:58 +11:00
type FakeCmdObjRunner struct {
t *testing.T
// commands can be run in any order; mimicking the concurrent behaviour of
// production code.
expectedCmds []CmdObjMatcher
invokedCmdIndexes []int
mutex sync.Mutex
}
type CmdObjMatcher struct {
description string
// returns true if the matcher matches the command object
test func(ICmdObj) bool
// output of the command
output string
// error of the command
err error
2021-12-30 13:11:58 +11:00
}
var _ ICmdObjRunner = &FakeCmdObjRunner{}
2022-03-19 09:38:49 +11:00
func NewFakeRunner(t *testing.T) *FakeCmdObjRunner { //nolint:thelper
2021-12-30 13:11:58 +11:00
return &FakeCmdObjRunner{t: t}
}
func (self *FakeCmdObjRunner) remainingExpectedCmds() []CmdObjMatcher {
return lo.Filter(self.expectedCmds, func(_ CmdObjMatcher, i int) bool {
return !lo.Contains(self.invokedCmdIndexes, i)
})
}
2021-12-30 13:11:58 +11:00
func (self *FakeCmdObjRunner) Run(cmdObj ICmdObj) error {
_, err := self.RunWithOutput(cmdObj)
return err
}
func (self *FakeCmdObjRunner) RunWithOutput(cmdObj ICmdObj) (string, error) {
self.mutex.Lock()
defer self.mutex.Unlock()
if len(self.remainingExpectedCmds()) == 0 {
2021-12-30 13:11:58 +11:00
self.t.Errorf("ran too many commands. Unexpected command: `%s`", cmdObj.ToString())
return "", errors.New("ran too many commands")
}
for i := range self.expectedCmds {
if lo.Contains(self.invokedCmdIndexes, i) {
continue
}
expectedCmd := self.expectedCmds[i]
matched := expectedCmd.test(cmdObj)
if matched {
self.invokedCmdIndexes = append(self.invokedCmdIndexes, i)
return expectedCmd.output, expectedCmd.err
}
}
2021-12-30 13:11:58 +11:00
self.t.Errorf("Unexpected command: `%s`", cmdObj.ToString())
return "", nil
2021-12-30 13:11:58 +11:00
}
func (self *FakeCmdObjRunner) RunWithOutputs(cmdObj ICmdObj) (string, string, error) {
output, err := self.RunWithOutput(cmdObj)
return output, "", err
}
2021-12-30 13:11:58 +11:00
func (self *FakeCmdObjRunner) RunAndProcessLines(cmdObj ICmdObj, onLine func(line string) (bool, error)) error {
output, err := self.RunWithOutput(cmdObj)
if err != nil {
return err
}
scanner := bufio.NewScanner(strings.NewReader(output))
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
line := scanner.Text()
stop, err := onLine(line)
if err != nil {
return err
}
if stop {
break
}
}
return nil
}
func (self *FakeCmdObjRunner) ExpectFunc(description string, fn func(cmdObj ICmdObj) bool, output string, err error) *FakeCmdObjRunner {
self.mutex.Lock()
defer self.mutex.Unlock()
2021-12-30 17:19:01 +11:00
self.expectedCmds = append(self.expectedCmds, CmdObjMatcher{
test: fn,
output: output,
err: err,
description: description,
2021-12-30 17:19:01 +11:00
})
return self
}
func (self *FakeCmdObjRunner) ExpectArgs(expectedArgs []string, output string, err error) *FakeCmdObjRunner {
description := fmt.Sprintf("matches args %s", strings.Join(expectedArgs, " "))
self.ExpectFunc(description, func(cmdObj ICmdObj) bool {
2021-12-30 17:19:01 +11:00
args := cmdObj.GetCmd().Args
if runtime.GOOS == "windows" {
// thanks to the secureexec package, the first arg is something like
// '"C:\\Program Files\\Git\\mingw64\\bin\\<command>.exe"
// on windows so we'll just ensure it contains our program
if !strings.Contains(args[0], expectedArgs[0]) {
return false
}
} else {
// first arg is the program name
if expectedArgs[0] != args[0] {
return false
}
}
if !slices.Equal(expectedArgs[1:], args[1:]) {
return false
}
2021-12-30 13:11:58 +11:00
return true
}, output, err)
2021-12-30 13:11:58 +11:00
return self
}
2021-12-30 17:19:01 +11:00
2022-01-03 15:15:26 +11:00
func (self *FakeCmdObjRunner) ExpectGitArgs(expectedArgs []string, output string, err error) *FakeCmdObjRunner {
description := fmt.Sprintf("matches git args %s", strings.Join(expectedArgs, " "))
self.ExpectFunc(description, func(cmdObj ICmdObj) bool {
2022-01-03 15:15:26 +11:00
// first arg is 'git' on unix and something like '"C:\\Program Files\\Git\\mingw64\\bin\\git.exe" on windows so we'll just ensure it ends in either 'git' or 'git.exe'
re := regexp.MustCompile(`git(\.exe)?$`)
args := cmdObj.GetCmd().Args
if !re.MatchString(args[0]) {
return false
2022-01-03 15:15:26 +11:00
}
if !slices.Equal(expectedArgs, args[1:]) {
return false
}
return true
}, output, err)
2022-01-03 15:15:26 +11:00
return self
}
2021-12-30 17:19:01 +11:00
func (self *FakeCmdObjRunner) CheckForMissingCalls() {
self.mutex.Lock()
defer self.mutex.Unlock()
remaining := self.remainingExpectedCmds()
if len(remaining) > 0 {
self.t.Errorf(
"expected %d more command(s) to be run. Remaining commands:\n%s",
len(remaining),
strings.Join(
lo.Map(remaining, func(cmdObj CmdObjMatcher, _ int) string {
return cmdObj.description
}),
"\n",
),
)
2021-12-30 17:19:01 +11:00
}
}