From 43ad9a81c282022203e45ae3088b93763320bccc Mon Sep 17 00:00:00 2001 From: Anthony HAMON Date: Sun, 2 Sep 2018 17:15:27 +0200 Subject: [PATCH] merge setup in function that create a new git command --- main.go | 6 -- pkg/commands/git.go | 127 ++++++++++++++-------------- pkg/commands/git_test.go | 177 ++++++++++++++++++++++++++++++++------- 3 files changed, 211 insertions(+), 99 deletions(-) diff --git a/main.go b/main.go index 6193f43d3..25e55d364 100644 --- a/main.go +++ b/main.go @@ -49,11 +49,5 @@ func main() { panic(err) } - if err := app.GitCommand.SetupGit(); err != nil { - app.Log.Error(err.Error()) - fmt.Println(err) - os.Exit(1) - } - app.Gui.RunWithSubprocesses() } diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 48d51e900..6ba5f8a84 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -19,98 +19,95 @@ import ( // to check if we have a valid git repository and we get an error instead var ErrGitRepositoryInvalid = fmt.Errorf("can't find a valid git repository in current directory") -func openGitRepositoryAndWorktree() (*gogit.Repository, *gogit.Worktree, error) { - r, err := gogit.PlainOpen(".") - - if err != nil { - return nil, nil, err - } - - w, err := r.Worktree() - - if err != nil { - return nil, nil, err - } - - return r, w, nil -} - -// GitCommand is our main git interface -type GitCommand struct { - Log *logrus.Entry - OSCommand *OSCommand - Worktree *gogit.Worktree - Repo *gogit.Repository - Tr *i18n.Localizer - openGitRepositoryAndWorktree func() (*gogit.Repository, *gogit.Worktree, error) -} - -// NewGitCommand it runs git commands -func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer) (*GitCommand, error) { - gitCommand := &GitCommand{ - Log: log, - OSCommand: osCommand, - Tr: tr, - openGitRepositoryAndWorktree: openGitRepositoryAndWorktree, - } - return gitCommand, nil -} - -// SetupGit sets git repo up -func (c *GitCommand) SetupGit() error { - fs := []func() error{ - c.verifyInGitRepo, - c.navigateToRepoRootDirectory, - c.setupRepositoryAndWorktree, - } - - for _, f := range fs { - if err := f(); err != nil { - return err - } - } - - return nil -} - -func (c *GitCommand) verifyInGitRepo() error { - if _, err := c.OSCommand.RunCommandWithOutput("git status"); err != nil { +func verifyInGitRepo(runCmdWithOutput func(string) (string, error)) error { + if _, err := runCmdWithOutput("git status"); err != nil { return ErrGitRepositoryInvalid } return nil } -func (c *GitCommand) navigateToRepoRootDirectory() error { +func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir func(string) error) error { for { - f, err := os.Stat(".git") + f, err := stat(".git") if err == nil && f.IsDir() { return nil } - c.Log.Debug("going up a directory to find the root") + if !os.IsNotExist(err) { + return err + } - if err = os.Chdir(".."); err != nil { + if err = chdir(".."); err != nil { return err } } } -func (c *GitCommand) setupRepositoryAndWorktree() (err error) { - c.Repo, c.Worktree, err = c.openGitRepositoryAndWorktree() +func setupRepositoryAndWorktree(openGitRepository func(string) (*gogit.Repository, error), sLocalize func(string) string) (repository *gogit.Repository, worktree *gogit.Worktree, err error) { + repository, err = openGitRepository(".") + + if err != nil { + if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) { + return nil, nil, errors.New(sLocalize("GitconfigParseErr")) + } - if err == nil { return } - if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) { - return errors.New(c.Tr.SLocalize("GitconfigParseErr")) + worktree, err = repository.Worktree() + + if err != nil { + return } return } +// GitCommand is our main git interface +type GitCommand struct { + Log *logrus.Entry + OSCommand *OSCommand + Worktree *gogit.Worktree + Repo *gogit.Repository + Tr *i18n.Localizer +} + +// NewGitCommand it runs git commands +func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer) (*GitCommand, error) { + var worktree *gogit.Worktree + var repo *gogit.Repository + + fs := []func() error{ + func() error { + return verifyInGitRepo(osCommand.RunCommandWithOutput) + }, + func() error { + return navigateToRepoRootDirectory(os.Stat, os.Chdir) + }, + func() error { + var err error + repo, worktree, err = setupRepositoryAndWorktree(gogit.PlainOpen, tr.SLocalize) + return err + }, + } + + for _, f := range fs { + if err := f(); err != nil { + return nil, err + } + } + + return &GitCommand{ + Log: log, + OSCommand: osCommand, + Tr: tr, + Worktree: worktree, + Repo: repo, + }, nil +} + // GetStashEntries stash entryies func (c *GitCommand) GetStashEntries() []StashEntry { rawString, _ := c.OSCommand.RunCommandWithOutput("git stash list --pretty='%gs'") diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 062e8ee3d..ebf4fd43d 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -3,8 +3,10 @@ package commands import ( "fmt" "io/ioutil" + "os" "os/exec" "testing" + "time" "github.com/jesseduffield/lazygit/pkg/i18n" "github.com/jesseduffield/lazygit/pkg/test" @@ -13,6 +15,39 @@ import ( gogit "gopkg.in/src-d/go-git.v4" ) +type fileInfoMock struct { + name string + size int64 + fileMode os.FileMode + fileModTime time.Time + isDir bool + sys interface{} +} + +func (f fileInfoMock) Name() string { + return f.name +} + +func (f fileInfoMock) Size() int64 { + return f.size +} + +func (f fileInfoMock) Mode() os.FileMode { + return f.fileMode +} + +func (f fileInfoMock) ModTime() time.Time { + return f.fileModTime +} + +func (f fileInfoMock) IsDir() bool { + return f.isDir +} + +func (f fileInfoMock) Sys() interface{} { + return f.sys +} + func newDummyLog() *logrus.Entry { log := logrus.New() log.Out = ioutil.Discard @@ -27,69 +62,155 @@ func newDummyGitCommand() *GitCommand { } } -func TestGitCommandSetupGit(t *testing.T) { +func TestVerifyInGitRepo(t *testing.T) { type scenario struct { - command func(string, ...string) *exec.Cmd - openGitRepositoryAndWorktree func() (*gogit.Repository, *gogit.Worktree, error) - test func(error) + runCmdWithOutput func(string) (string, error) + test func(error) } scenarios := []scenario{ { - func(string, ...string) *exec.Cmd { - return exec.Command("exit", "1") + func(string) (string, error) { + return "", nil }, - func() (*gogit.Repository, *gogit.Worktree, error) { - return nil, nil, nil + func(err error) { + assert.NoError(t, err) + }, + }, + { + func(string) (string, error) { + return "", ErrGitRepositoryInvalid }, func(err error) { assert.Error(t, err) assert.Equal(t, ErrGitRepositoryInvalid, err) }, }, + } + + for _, s := range scenarios { + s.test(verifyInGitRepo(s.runCmdWithOutput)) + } +} + +func TestNavigateToRepoRootDirectory(t *testing.T) { + type scenario struct { + stat func(string) (os.FileInfo, error) + chdir func(string) error + test func(error) + } + + scenarios := []scenario{ { - func(string, ...string) *exec.Cmd { - return exec.Command("echo") + func(string) (os.FileInfo, error) { + return fileInfoMock{isDir: true}, nil }, - func() (*gogit.Repository, *gogit.Worktree, error) { - return nil, nil, fmt.Errorf(`unquoted '\' must be followed by new line`) + func(string) error { + return nil }, func(err error) { - assert.Error(t, err) - assert.Contains(t, err.Error(), "gitconfig") + assert.NoError(t, err) }, }, { - func(string, ...string) *exec.Cmd { - return exec.Command("echo") + func(string) (os.FileInfo, error) { + return nil, fmt.Errorf("An error occurred") }, - func() (*gogit.Repository, *gogit.Worktree, error) { - return nil, nil, fmt.Errorf("Error from inside gogit") + func(string) error { + return nil }, func(err error) { + assert.Error(t, err) + assert.EqualError(t, err, "An error occurred") + }, + }, + { + func(string) (os.FileInfo, error) { + return nil, os.ErrNotExist + }, + func(string) error { + return fmt.Errorf("An error occurred") + }, + func(err error) { + assert.Error(t, err) + assert.EqualError(t, err, "An error occurred") + }, + }, + { + func(string) (os.FileInfo, error) { + return nil, os.ErrNotExist + }, + func(string) error { + return fmt.Errorf("An error occurred") + }, + func(err error) { + assert.Error(t, err) + assert.EqualError(t, err, "An error occurred") + }, + }, + } + + for _, s := range scenarios { + s.test(navigateToRepoRootDirectory(s.stat, s.chdir)) + } +} + +func TestSetupRepositoryAndWorktree(t *testing.T) { + type scenario struct { + openGitRepository func(string) (*gogit.Repository, error) + sLocalize func(string) string + test func(*gogit.Repository, *gogit.Worktree, error) + } + + scenarios := []scenario{ + { + func(string) (*gogit.Repository, error) { + return nil, fmt.Errorf(`unquoted '\' must be followed by new line`) + }, + func(string) string { + return "error translated" + }, + func(r *gogit.Repository, w *gogit.Worktree, err error) { + assert.Error(t, err) + assert.EqualError(t, err, "error translated") + }, + }, + { + func(string) (*gogit.Repository, error) { + return nil, fmt.Errorf("Error from inside gogit") + }, + func(string) string { return "" }, + func(r *gogit.Repository, w *gogit.Worktree, err error) { assert.Error(t, err) assert.EqualError(t, err, "Error from inside gogit") }, }, { - func(string, ...string) *exec.Cmd { - return exec.Command("echo") + func(string) (*gogit.Repository, error) { + return &gogit.Repository{}, nil }, - func() (*gogit.Repository, *gogit.Worktree, error) { - return &gogit.Repository{}, &gogit.Worktree{}, nil + func(string) string { return "" }, + func(r *gogit.Repository, w *gogit.Worktree, err error) { + assert.Error(t, err) + assert.Equal(t, gogit.ErrIsBareRepository, err) }, - func(err error) { + }, + { + func(string) (*gogit.Repository, error) { + assert.NoError(t, os.RemoveAll("/tmp/lazygit-test")) + r, err := gogit.PlainInit("/tmp/lazygit-test", false) + assert.NoError(t, err) + return r, nil + }, + func(string) string { return "" }, + func(r *gogit.Repository, w *gogit.Worktree, err error) { assert.NoError(t, err) }, }, } for _, s := range scenarios { - gitCmd := newDummyGitCommand() - gitCmd.OSCommand.command = s.command - gitCmd.openGitRepositoryAndWorktree = s.openGitRepositoryAndWorktree - - s.test(gitCmd.SetupGit()) + s.test(setupRepositoryAndWorktree(s.openGitRepository, s.sLocalize)) } }