* lint: cosmetics

* lint: rename global variables

* gofmt

* lint: unchecked error

* lint: errcheck

* lint: cosmetics

* Specialize 'run' function to run 'git'
This commit is contained in:
Aurélien Rainone 2020-09-01 23:01:30 +02:00 committed by GitHub
parent 59b3b290a9
commit 247481f189
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 122 additions and 66 deletions

View file

@ -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)
}
}()
if popdir, err = pushdir("gitmux"); err != nil {
git(t, "clone", "git://github.com/arl/gitmux.git")
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)
}

View file

@ -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
}

View file

@ -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())
})

View file

@ -14,7 +14,7 @@ import (
var version = "<<development 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)