diff --git a/config_test.go b/config_test.go index 81cd64d..a3eca3e 100644 --- a/config_test.go +++ b/config_test.go @@ -12,7 +12,7 @@ import ( "testing" ) -var updateGolden = flag.Bool("update", false, "update golden files") +var _updateGolden = flag.Bool("update", false, "update golden files") // This test ensures that new features do not change gitmux output when used // with a default configuration. @@ -25,21 +25,27 @@ func TestOutputNonRegression(t *testing.T) { if err != nil { log.Fatal(err) } + defer os.RemoveAll(tmpdir) t.Logf("test working directory: %q", tmpdir) cloneAndHack(t, tmpdir) cmd := exec.Command("go", "run", ".", "-printcfg") + b, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("Command error: %s: %s\noutput: %s", cmdString(cmd), err, string(b)) + } + defcfg := path.Join(tmpdir, "default.cfg") if err := ioutil.WriteFile(defcfg, b, os.ModePerm); err != nil { t.Fatalf("Can't write %q: %s", defcfg, err) } repodir := path.Join(tmpdir, "gitmux") - cmd = exec.Command("go", "run", ".", "-cfg", defcfg, repodir) + got, err := cmd.CombinedOutput() if err != nil { t.Fatalf("Command %q failed:\n%s\nerr: %s", cmdString(cmd), b, err) @@ -47,7 +53,7 @@ func TestOutputNonRegression(t *testing.T) { goldenFile := path.Join("testdata", "default.output.golden") - if *updateGolden { + if *_updateGolden { if err := ioutil.WriteFile(goldenFile, got, os.ModePerm); err != nil { t.Fatalf("Can't update golden file %q: %s", goldenFile, err) } @@ -67,12 +73,11 @@ func cmdString(cmd *exec.Cmd) string { return strings.Join(append([]string{cmd.Path}, cmd.Args...), " ") } -func run(t *testing.T, name string, args ...string) { +func git(t *testing.T, args ...string) { t.Helper() - cmd := exec.Command(name, args...) - out, err := cmd.CombinedOutput() - if err != nil { + cmd := exec.Command("git", args...) + if out, err := cmd.CombinedOutput(); err != nil { t.Fatalf("Command %q failed:\n%s\nerr: %s", cmdString(cmd), out, err) } } @@ -80,45 +85,59 @@ func run(t *testing.T, name string, args ...string) { func cloneAndHack(t *testing.T, dir string) { t.Helper() - var popdir func() error - var err error - - if popdir, err = pushdir(dir); err != nil { + popd1, err := pushdir(dir) + if err != nil { t.Fatal(err) } - defer popdir() - run(t, "git", "clone", "git://github.com/arl/gitmux.git") + defer func() { + if err := popd1(); err != nil { + t.Fatalf("popd1: %v", err) + } + }() + + git(t, "clone", "git://github.com/arl/gitmux.git") - if popdir, err = pushdir("gitmux"); err != nil { + popd2, err := pushdir("gitmux") + if err != nil { t.Fatal(err) } - defer popdir() + + defer func() { + if err := popd2(); err != nil { + t.Fatalf("popd2: %v", err) + } + }() if err := ioutil.WriteFile("dummy", []byte("dummy"), os.ModePerm); err != nil { t.Fatalf("write dummy: %s", err) } - run(t, "git", "add", "dummy") - run(t, "git", "commit", "-m", "add dummy file") + + git(t, "add", "dummy") + git(t, "commit", "-m", "add dummy file") if err := ioutil.WriteFile("dummy2", []byte("dummy2"), os.ModePerm); err != nil { t.Fatalf("write dummy2: %s", err) } - run(t, "git", "add", "dummy2") - run(t, "git", "stash") + + git(t, "add", "dummy2") + git(t, "stash") if err := ioutil.WriteFile("file1", nil, os.ModePerm); err != nil { t.Fatalf("write file1: %s", err) } + if err := ioutil.WriteFile("file2", nil, os.ModePerm); err != nil { t.Fatalf("write file2: %s", err) } + if err := ioutil.WriteFile("file3", nil, os.ModePerm); err != nil { t.Fatalf("write file3: %s", err) } - run(t, "git", "add", "file1") - run(t, "git", "add", "file2") + git(t, "add", "file1") + git(t, "add", "file2") + if err := ioutil.WriteFile("file2", []byte("foo"), os.ModePerm); err != nil { t.Fatalf("write file2: %s", err) } diff --git a/format/tmux/formater.go b/format/tmux/formater.go index c4ed1e2..a01eb98 100644 --- a/format/tmux/formater.go +++ b/format/tmux/formater.go @@ -101,11 +101,17 @@ func truncateBranchName(name string, maxlen int, isremote bool) string { remoteName := "" branchName := name + const ( + idxRemote = 0 + idxBranch = 1 + numItems = 2 + ) + if isremote { - a := strings.SplitAfterN(name, "/", 2) - if len(a) == 2 { - remoteName = a[0] - branchName = a[1] + a := strings.SplitAfterN(name, "/", numItems) + if len(a) == numItems { + remoteName = a[idxRemote] + branchName = a[idxBranch] } } @@ -136,8 +142,10 @@ func (f *Formater) Format(w io.Writer, st *gitstatus.Status) error { truncateBranchName(f.st.LocalBranch, f.Options.BranchMaxLen, false)) f.flags() _, err := f.b.WriteTo(w) + return err } + f.format() _, err := f.b.WriteTo(w) @@ -165,6 +173,7 @@ func (f *Formater) format() { func (f *Formater) specialState() { f.clear() + switch f.st.State { case gitstatus.Rebasing: fmt.Fprintf(&f.b, "%s[rebase] ", f.Styles.State) @@ -183,11 +192,13 @@ func (f *Formater) specialState() { case gitstatus.Default: fmt.Fprintf(&f.b, "%s%s", f.Styles.Branch, f.Symbols.Branch) } + f.currentRef() } func (f *Formater) remote() { f.clear() + if f.st.RemoteBranch != "" { fmt.Fprintf(&f.b, "%s%s", f.Styles.Remote, truncateBranchName(f.st.RemoteBranch, f.Options.BranchMaxLen, true)) @@ -197,6 +208,7 @@ func (f *Formater) remote() { func (f *Formater) remoteBranch() { f.clear() + if f.st.RemoteBranch != "" { fmt.Fprintf(&f.b, "%s%s", f.Styles.Remote, truncateBranchName(f.st.RemoteBranch, f.Options.BranchMaxLen, true)) @@ -207,8 +219,10 @@ func (f *Formater) divergence() { f.clear() pref := " " + if f.st.BehindCount != 0 { fmt.Fprintf(&f.b, " %s%d", f.Symbols.Behind, f.st.BehindCount) + pref = "" } @@ -224,8 +238,10 @@ func (f *Formater) clear() { func (f *Formater) currentRef() { f.clear() + if f.st.IsDetached { fmt.Fprintf(&f.b, "%s%s", f.Symbols.HashPrefix, f.st.HEAD) + return } @@ -238,6 +254,7 @@ func (f *Formater) flags() { if f.st.IsClean { fmt.Fprintf(&f.b, "%s%s", f.Styles.Clean, f.Symbols.Clean) + return } diff --git a/format/tmux/formater_test.go b/format/tmux/formater_test.go index 056b005..3a7308f 100644 --- a/format/tmux/formater_test.go +++ b/format/tmux/formater_test.go @@ -9,7 +9,7 @@ import ( "github.com/arl/gitstatus" ) -func TestFormater_flags(t *testing.T) { +func TestFlags(t *testing.T) { tests := []struct { name string styles styles @@ -74,6 +74,7 @@ func TestFormater_flags(t *testing.T) { }, } for _, tc := range tests { + tc := tc t.Run(tc.name, func(t *testing.T) { f := &Formater{ Config: Config{Styles: tc.styles, Symbols: tc.symbols, Layout: tc.layout}, @@ -85,7 +86,7 @@ func TestFormater_flags(t *testing.T) { } } -func TestFormater_divergence(t *testing.T) { +func TestDivergence(t *testing.T) { tests := []struct { name string styles styles @@ -151,6 +152,7 @@ func TestFormater_divergence(t *testing.T) { }, } for _, tc := range tests { + tc := tc t.Run(tc.name, func(t *testing.T) { f := &Formater{ Config: Config{Styles: tc.styles, Symbols: tc.symbols}, @@ -162,7 +164,7 @@ func TestFormater_divergence(t *testing.T) { } } -func TestFormater_truncateBranchName(t *testing.T) { +func TestTruncateBranchName(t *testing.T) { tests := []struct { name string branchName string @@ -171,49 +173,50 @@ func TestFormater_truncateBranchName(t *testing.T) { want string }{ { - name: "no limit", + name: "no limit", branchName: "foo/bar-baz", - maxLen: 0, - isRemote: false, - want: "foo/bar-baz", + maxLen: 0, + isRemote: false, + want: "foo/bar-baz", }, { - name: "no truncate", + name: "no truncate", branchName: "foo/bar-baz", - maxLen: 11, - isRemote: false, - want: "foo/bar-baz", + maxLen: 11, + isRemote: false, + want: "foo/bar-baz", }, { - name: "truncate", + name: "truncate", branchName: "foo/bar-baz", - maxLen: 10, - isRemote: false, - want: "foo/bar...", + maxLen: 10, + isRemote: false, + want: "foo/bar...", }, { - name: "truncate remote", + name: "truncate remote", branchName: "remote/foo/bar-baz", - maxLen: 10, - isRemote: true, - want: "remote/foo/bar...", + maxLen: 10, + isRemote: true, + want: "remote/foo/bar...", }, { - name: "truncate to 1", + name: "truncate to 1", branchName: "foo/bar-baz", - maxLen: 1, - isRemote: false, - want: ".", + maxLen: 1, + isRemote: false, + want: ".", }, { - name: "truncate utf-8 name", + name: "truncate utf-8 name", branchName: "foo/测试这个名字", - maxLen: 9, - isRemote: false, - want: "foo/测试...", + maxLen: 9, + isRemote: false, + want: "foo/测试...", }, } for _, tc := range tests { + tc := tc t.Run(tc.name, func(t *testing.T) { branchName := truncateBranchName(tc.branchName, tc.maxLen, tc.isRemote) require.EqualValues(t, tc.want, branchName) @@ -221,7 +224,7 @@ func TestFormater_truncateBranchName(t *testing.T) { } } -func TestFormater_Format(t *testing.T) { +func TestFormat(t *testing.T) { tests := []struct { name string styles styles @@ -252,7 +255,11 @@ func TestFormater_Format(t *testing.T) { NumModified: 2, }, }, - want: clear + "StyleBranchSymbolBranch" + clear + "Local" + ".." + clear + "StyleRemoteRemote" + clear + " - " + clear + "StyleModSymbolMod2", + want: clear + "StyleBranchSymbolBranch" + + clear + "Local" + ".." + + clear + "StyleRemoteRemote" + + clear + " - " + + clear + "StyleModSymbolMod2", }, { name: "branch, different delimiter, flags", @@ -275,7 +282,9 @@ func TestFormater_Format(t *testing.T) { AheadCount: 1, }, }, - want: clear + "StyleBranchSymbolBranch" + clear + "Local" + " ~~ " + clear + "StyleModSymbolMod2", + want: clear + "StyleBranchSymbolBranch" + + clear + "Local" + " ~~ " + + clear + "StyleModSymbolMod2", }, { name: "remote only", @@ -295,7 +304,8 @@ func TestFormater_Format(t *testing.T) { AheadCount: 1, }, }, - want: clear + "StyleRemoteRemote" + clear + " SymbolAhead1", + want: clear + "StyleRemoteRemote" + + clear + " SymbolAhead1", }, { name: "empty", @@ -319,11 +329,11 @@ func TestFormater_Format(t *testing.T) { { name: "branch and remote, branch_max_len not zero", styles: styles{ - Branch: "StyleBranch", - Remote: "StyleRemote", + Branch: "StyleBranch", + Remote: "StyleRemote", }, symbols: symbols{ - Branch: "SymbolBranch", + Branch: "SymbolBranch", }, layout: []string{"branch", " ", "remote"}, options: options{ @@ -335,16 +345,23 @@ func TestFormater_Format(t *testing.T) { RemoteBranch: "remote/branchName", }, }, - want: clear + "StyleBranch" + "SymbolBranch" + clear + "branch..." + " " + clear + "StyleRemote" + "remote/branch..." + clear, + want: clear + "StyleBranch" + "SymbolBranch" + + clear + "branch..." + " " + + clear + "StyleRemote" + "remote/branch..." + + clear, }, } for _, tc := range tests { + tc := tc t.Run(tc.name, func(t *testing.T) { f := &Formater{ Config: Config{Styles: tc.styles, Symbols: tc.symbols, Layout: tc.layout, Options: tc.options}, } - f.Format(os.Stdout, tc.st) + if err := f.Format(os.Stdout, tc.st); err != nil { + t.Fatalf("Format error: %s", err) + } + f.format() require.EqualValues(t, tc.want, f.b.String()) }) diff --git a/gitmux.go b/gitmux.go index 322d536..ca4feba 100644 --- a/gitmux.go +++ b/gitmux.go @@ -14,7 +14,7 @@ import ( var version = "<>" -var usage = `gitmux ` + version + ` +var _usage = `gitmux ` + version + ` Usage: gitmux [options] [dir] gitmux prints the status of a Git working tree as a tmux format string. @@ -30,7 +30,7 @@ Options: // Config configures output formatting. type Config struct{ Tmux tmux.Config } -var defaultCfg = Config{Tmux: tmux.DefaultCfg} +var _defaultCfg = Config{Tmux: tmux.DefaultCfg} func parseOptions() (dir string, dbg bool, cfg Config) { dbgOpt := flag.Bool("dbg", false, "") @@ -40,7 +40,7 @@ func parseOptions() (dir string, dbg bool, cfg Config) { flag.Bool("q", true, "") // unused, kept for retro-compatibility. flag.String("fmt", "", "") // unused, kept for retro-compatibility. flag.Usage = func() { - fmt.Println(usage) + fmt.Println(_usage) } flag.Parse() @@ -56,12 +56,12 @@ func parseOptions() (dir string, dbg bool, cfg Config) { if *printCfgOpt { enc := yaml.NewEncoder(os.Stdout) - check(enc.Encode(&defaultCfg), *dbgOpt) + check(enc.Encode(&_defaultCfg), *dbgOpt) enc.Close() os.Exit(0) } - cfg = defaultCfg + cfg = _defaultCfg if *cfgOpt != "" { f, err := os.Open(*cfgOpt) @@ -91,9 +91,11 @@ func check(err error, dbg bool) { if err == nil { return } + if dbg { fmt.Fprintln(os.Stderr, "error:", err) } + os.Exit(1) } @@ -103,6 +105,7 @@ func main() { // handle directory change. if dir != "." { popDir, err := pushdir(dir) + check(err, dbg) defer func() { check(popDir(), dbg)