diff --git a/.circleci/config.yml b/.circleci/config.yml index d60eadaab..16bafdb58 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,7 +9,7 @@ jobs: - checkout - restore_cache: keys: - - v1-pkg-cache + - pkg-cache-{{ checksum "Gopkg.lock" }} - run: name: Run gofmt -s command: | @@ -31,7 +31,7 @@ jobs: command: | bash <(curl -s https://codecov.io/bash) - save_cache: - key: v1-pkg-cache + key: pkg-cache-{{ checksum "Gopkg.lock" }} paths: - "/go/pkg" diff --git a/pkg/commands/git.go b/pkg/commands/git.go index b56650e97..619db05e7 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -46,8 +46,8 @@ func (c *GitCommand) SetupGit() { // GetStashEntries stash entryies func (c *GitCommand) GetStashEntries() []StashEntry { - stashEntries := make([]StashEntry, 0) rawString, _ := c.OSCommand.RunCommandWithOutput("git stash list --pretty='%gs'") + stashEntries := []StashEntry{} for i, line := range utils.SplitLines(rawString) { stashEntries = append(stashEntries, stashEntryFromLine(line, i)) } @@ -80,7 +80,7 @@ func includes(array []string, str string) bool { func (c *GitCommand) GetStatusFiles() []File { statusOutput, _ := c.GitStatus() statusStrings := utils.SplitLines(statusOutput) - files := make([]File, 0) + files := []File{} for _, statusString := range statusStrings { change := statusString[0:2] @@ -106,13 +106,13 @@ func (c *GitCommand) GetStatusFiles() []File { // StashDo modify stash func (c *GitCommand) StashDo(index int, method string) error { - return c.OSCommand.RunCommand("git stash " + method + " stash@{" + fmt.Sprint(index) + "}") + return c.OSCommand.RunCommand(fmt.Sprintf("git stash %s stash@{%d}", method, index)) } // StashSave save stash // TODO: before calling this, check if there is anything to save func (c *GitCommand) StashSave(message string) error { - return c.OSCommand.RunCommand("git stash save " + c.OSCommand.Quote(message)) + return c.OSCommand.RunCommand(fmt.Sprintf("git stash save %s", c.OSCommand.Quote(message))) } // MergeStatusFiles merge status files @@ -121,28 +121,28 @@ func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []File) []File { return newFiles } - appendedIndexes := make([]int, 0) + headResults := []File{} + tailResults := []File{} - // retain position of files we already could see - result := make([]File, 0) - for _, oldFile := range oldFiles { - for newIndex, newFile := range newFiles { + for _, newFile := range newFiles { + var isHeadResult bool + + for _, oldFile := range oldFiles { if oldFile.Name == newFile.Name { - result = append(result, newFile) - appendedIndexes = append(appendedIndexes, newIndex) + isHeadResult = true break } } - } - // append any new files to the end - for index, newFile := range newFiles { - if !includesInt(appendedIndexes, index) { - result = append(result, newFile) + if isHeadResult { + headResults = append(headResults, newFile) + continue } + + tailResults = append(tailResults, newFile) } - return result + return append(headResults, tailResults...) } func (c *GitCommand) verifyInGitRepo() { @@ -447,17 +447,6 @@ func includesString(list []string, a string) bool { return false } -// not sure how to genericise this because []interface{} doesn't accept e.g. -// []int arguments -func includesInt(list []int, a int) bool { - for _, b := range list { - if b == a { - return true - } - } - return false -} - // GetCommits obtains the commits of the current branch func (c *GitCommand) GetCommits() []Commit { pushables := c.GetCommitsToPush() diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 9c6a17b2c..8117ed1e5 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -2,11 +2,12 @@ package commands import ( "io/ioutil" - "strings" + "os/exec" "testing" "github.com/jesseduffield/lazygit/pkg/test" "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" ) func newDummyLog() *logrus.Entry { @@ -22,11 +23,251 @@ func newDummyGitCommand() *GitCommand { } } -func TestDiff(t *testing.T) { - gitCommand := newDummyGitCommand() - if err := test.GenerateRepo("lots_of_diffs.sh"); err != nil { - t.Error(err.Error()) +func TestGitCommandGetStashEntries(t *testing.T) { + type scenario struct { + command func(string, ...string) *exec.Cmd + test func([]StashEntry) } + + scenarios := []scenario{ + { + func(string, ...string) *exec.Cmd { + return exec.Command("echo") + }, + func(entries []StashEntry) { + assert.Len(t, entries, 0) + }, + }, + { + func(string, ...string) *exec.Cmd { + return exec.Command("echo", "WIP on add-pkg-commands-test: 55c6af2 increase parallel build\nWIP on master: bb86a3f update github template") + }, + func(entries []StashEntry) { + expected := []StashEntry{ + { + 0, + "WIP on add-pkg-commands-test: 55c6af2 increase parallel build", + "WIP on add-pkg-commands-test: 55c6af2 increase parallel build", + }, + { + 1, + "WIP on master: bb86a3f update github template", + "WIP on master: bb86a3f update github template", + }, + } + + assert.Len(t, entries, 2) + assert.EqualValues(t, expected, entries) + }, + }, + } + + for _, s := range scenarios { + gitCmd := newDummyGitCommand() + gitCmd.OSCommand.command = s.command + + s.test(gitCmd.GetStashEntries()) + } +} + +func TestGetStashEntryDiff(t *testing.T) { + gitCmd := newDummyGitCommand() + gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"stash", "show", "-p", "--color", "stash@{1}"}, args) + + return exec.Command("echo") + } + + _, err := gitCmd.GetStashEntryDiff(1) + + assert.NoError(t, err) +} + +func TestGetStatusFiles(t *testing.T) { + type scenario struct { + command func(string, ...string) *exec.Cmd + test func([]File) + } + + scenarios := []scenario{ + { + func(cmd string, args ...string) *exec.Cmd { + return exec.Command("echo") + }, + func(files []File) { + assert.Len(t, files, 0) + }, + }, + { + func(cmd string, args ...string) *exec.Cmd { + return exec.Command( + "echo", + "MM file1.txt\nA file3.txt\nAM file2.txt\n?? file4.txt", + ) + }, + func(files []File) { + assert.Len(t, files, 4) + + expected := []File{ + { + Name: "file1.txt", + HasStagedChanges: true, + HasUnstagedChanges: true, + Tracked: true, + Deleted: false, + HasMergeConflicts: false, + DisplayString: "MM file1.txt", + Type: "other", + }, + { + Name: "file3.txt", + HasStagedChanges: true, + HasUnstagedChanges: false, + Tracked: false, + Deleted: false, + HasMergeConflicts: false, + DisplayString: "A file3.txt", + Type: "other", + }, + { + Name: "file2.txt", + HasStagedChanges: true, + HasUnstagedChanges: true, + Tracked: false, + Deleted: false, + HasMergeConflicts: false, + DisplayString: "AM file2.txt", + Type: "other", + }, + { + Name: "file4.txt", + HasStagedChanges: false, + HasUnstagedChanges: true, + Tracked: false, + Deleted: false, + HasMergeConflicts: false, + DisplayString: "?? file4.txt", + Type: "other", + }, + } + + assert.EqualValues(t, expected, files) + }, + }, + } + + for _, s := range scenarios { + gitCmd := newDummyGitCommand() + gitCmd.OSCommand.command = s.command + + s.test(gitCmd.GetStatusFiles()) + } +} + +func TestGitCommandStashDo(t *testing.T) { + gitCmd := newDummyGitCommand() + gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"stash", "drop", "stash@{1}"}, args) + + return exec.Command("echo") + } + + assert.NoError(t, gitCmd.StashDo(1, "drop")) +} + +func TestGitCommandStashSave(t *testing.T) { + gitCmd := newDummyGitCommand() + gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"stash", "save", "A stash message"}, args) + + return exec.Command("echo") + } + + assert.NoError(t, gitCmd.StashSave("A stash message")) +} + +func TestGitCommandMergeStatusFiles(t *testing.T) { + type scenario struct { + oldFiles []File + newFiles []File + test func([]File) + } + + scenarios := []scenario{ + { + []File{}, + []File{ + { + Name: "new_file.txt", + }, + }, + func(files []File) { + expected := []File{ + { + Name: "new_file.txt", + }, + } + + assert.Len(t, files, 1) + assert.EqualValues(t, expected, files) + }, + }, + { + []File{ + { + Name: "new_file1.txt", + }, + { + Name: "new_file2.txt", + }, + { + Name: "new_file3.txt", + }, + }, + []File{ + { + Name: "new_file4.txt", + }, + { + Name: "new_file5.txt", + }, + { + Name: "new_file1.txt", + }, + }, + func(files []File) { + expected := []File{ + { + Name: "new_file1.txt", + }, + { + Name: "new_file4.txt", + }, + { + Name: "new_file5.txt", + }, + } + + assert.Len(t, files, 3) + assert.EqualValues(t, expected, files) + }, + }, + } + + for _, s := range scenarios { + gitCmd := newDummyGitCommand() + + s.test(gitCmd.MergeStatusFiles(s.oldFiles, s.newFiles)) + } +} + +func TestGitCommandDiff(t *testing.T) { + gitCommand := newDummyGitCommand() + assert.NoError(t, test.GenerateRepo("lots_of_diffs.sh")) + files := []File{ { Name: "deleted_staged", @@ -110,10 +351,8 @@ func TestDiff(t *testing.T) { DisplayString: "?? master", }, } + for _, file := range files { - content := gitCommand.Diff(file) - if strings.Contains(content, "error") { - t.Error("Error: diff test failed. File: " + file.Name + ", " + content) - } + assert.NotContains(t, gitCommand.Diff(file), "error") } } diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 3e384d80f..3c272e520 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -192,6 +192,7 @@ func (c *AppConfig) SaveAppState() error { return ioutil.WriteFile(filepath, marshalledAppState, 0644) } +// LoadAppState loads recorded AppState from file func (c *AppConfig) LoadAppState() error { filepath, err := prepareConfigFile("state.yml") if err != nil { @@ -207,6 +208,7 @@ func (c *AppConfig) LoadAppState() error { return yaml.Unmarshal(appStateBytes, c.AppState) } +// GetDefaultConfig returns the application default configuration func GetDefaultConfig() []byte { return []byte( `gui: diff --git a/pkg/updates/updates.go b/pkg/updates/updates.go index ad5dd57b6..5f533e5a0 100644 --- a/pkg/updates/updates.go +++ b/pkg/updates/updates.go @@ -22,7 +22,7 @@ import ( "github.com/sirupsen/logrus" ) -// Update checks for updates and does updates +// Updater checks for updates and does updates type Updater struct { Log *logrus.Entry Config config.AppConfigurer @@ -30,7 +30,7 @@ type Updater struct { Tr *i18n.Localizer } -// Updater implements the check and update methods +// Updaterer implements the check and update methods type Updaterer interface { CheckForNewUpdate() Update() @@ -78,6 +78,7 @@ func (u *Updater) getLatestVersionNumber() (string, error) { return dat["tag_name"].(string), nil } +// RecordLastUpdateCheck records last time an update check was performed func (u *Updater) RecordLastUpdateCheck() error { u.Config.GetAppState().LastUpdateCheck = time.Now().Unix() return u.Config.SaveAppState() diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 1d5f0f273..e28ab1824 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -83,6 +83,7 @@ func GetProjectRoot() string { return strings.Split(dir, "lazygit")[0] + "lazygit" } +// Loader dumps a string to be displayed as a loader func Loader() string { characters := "|/-\\" now := time.Now()