11 files changed,
251 insertions(+),
185 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-07-30 23:38:41 +0300
Authored at:
2026-07-30 22:11:01 +0300
Change ID:
kvmtqyoqyqzqxzmpxlswvlqnyrmlmnmx
Parent:
aac4846
jump to
A
go.sum
··· 1 +olexsmir.xyz/x v0.2.0 h1:F1Wyt8J49pKawPKAdrlOk8rnm0vNYb/Ax/N2FsO2EUs= 2 +olexsmir.xyz/x v0.2.0/go.mod h1:MNHD3lrnOpxNKG7je8/dKgtZg5V8zPiow+U0QISz+Yc= 3 +olexsmir.xyz/x v0.3.1 h1:wlJjZG98duc2ZX3z1Hqx48DKUWwOFObE9dNPZqg3TK4= 4 +olexsmir.xyz/x v0.3.1/go.mod h1:79Z6oaW+ZCKZahm5r4Cgxbv1Ey8IQuAi1uXMhyJFJto=
M
internal/config/config_test.go
··· 4 4 "os" 5 5 "path/filepath" 6 6 "testing" 7 + 8 + "olexsmir.xyz/x/is" 7 9 ) 8 10 9 11 func TestLoad(t *testing.T) { 10 12 t.Run("creates default config when file missing", func(t *testing.T) { 11 13 want := Config{cfg: make(map[string]string)} 12 - if err := want.read(defaultConfig); err != nil { 13 - t.Fatal(err) 14 - } 14 + is.Err(t, want.read(defaultConfig), nil) 15 15 16 16 dir := t.TempDir() 17 17 t.Setenv("XDG_CONFIG_HOME", dir) 18 18 19 19 cfg, err := Load() 20 - if err != nil { 21 - t.Fatalf("Load() = _, %v", err) 22 - } 20 + is.Err(t, err, nil) 23 21 for key, val := range want.cfg { 24 - if cfg.Get(key) != val { 25 - t.Fatalf("got %s=%q, want %q", key, cfg.Get(key), val) 26 - } 22 + is.Equal(t, val, cfg.Get(key)) 27 23 } 28 24 }) 29 25 ··· 32 28 t.Setenv("XDG_CONFIG_HOME", dir) 33 29 34 30 cfgDir := filepath.Join(dir, "viye.conf") 35 - if err := os.WriteFile(cfgDir, []byte("custom = value\n"), 0o644); err != nil { 36 - t.Fatal(err) 37 - } 31 + is.Err(t, os.WriteFile(cfgDir, []byte("custom = value\n"), 0o644), nil) 38 32 39 33 cfg, err := Load() 40 - if err != nil { 41 - t.Fatalf("Load() = _, %v", err) 42 - } 43 - if cfg.Get("custom") != "value" { 44 - t.Fatalf("got custom=%q, want %q", cfg.Get("custom"), "value") 45 - } 34 + is.Err(t, err, nil) 35 + is.Equal(t, "value", cfg.Get("custom")) 46 36 }) 47 37 48 38 t.Run("invalid config file returns error", func(t *testing.T) { ··· 50 40 t.Setenv("XDG_CONFIG_HOME", dir) 51 41 52 42 cfgDir := filepath.Join(dir, "viye.conf") 53 - if err := os.WriteFile(cfgDir, []byte("bad line\n"), 0o644); err != nil { 54 - t.Fatal(err) 55 - } 43 + is.Err(t, os.WriteFile(cfgDir, []byte("bad line\n"), 0o644), nil) 56 44 57 45 _, err := Load() 58 - if err != ErrInvalidConfig { 59 - t.Fatalf("got %v, want ErrInvalidConfig", err) 60 - } 46 + is.Err(t, err, ErrInvalidConfig) 61 47 }) 62 48 } 63 49 ··· 69 55 cfg: map[string]string{}, 70 56 } 71 57 72 - if err := c.Set("key", "val"); err != nil { 73 - t.Fatal(err) 74 - } 75 - if c.cfg["key"] != "val" { 76 - t.Fatalf("in-memory: got %v, want key=val", c.cfg) 77 - } 58 + is.Err(t, c.Set("key", "val"), nil) 59 + is.Equal(t, "val", c.cfg["key"]) 78 60 79 61 b, err := os.ReadFile(path) 80 - if err != nil { 81 - t.Fatal(err) 82 - } 83 - if string(b) != "key = val\n" { 84 - t.Fatalf("on disk: got %q, want %q", string(b), "key = val\n") 85 - } 62 + is.Err(t, err, nil) 63 + is.Equal(t, "key = val\n", string(b)) 86 64 } 87 65 88 66 func TestRead(t *testing.T) { 89 67 t.Run("valid config", func(t *testing.T) { 90 68 c := Config{cfg: make(map[string]string)} 91 - err := c.read([]byte("key1 = value1\nkey2 = value2\n")) 92 - if err != nil { 93 - t.Fatalf("unexpected error: %v", err) 94 - } 95 - if c.cfg["key1"] != "value1" || c.cfg["key2"] != "value2" { 96 - t.Fatalf("got %v, want key1=value1 key2=value2", c.cfg) 97 - } 69 + is.Err(t, c.read([]byte("key1 = value1\nkey2 = value2\n")), nil) 70 + is.Equal(t, "value1", c.cfg["key1"]) 71 + is.Equal(t, "value2", c.cfg["key2"]) 98 72 }) 99 73 100 74 t.Run("trailing newline", func(t *testing.T) { 101 75 c := Config{cfg: make(map[string]string)} 102 - err := c.read([]byte("key = val\n")) 103 - if err != nil { 104 - t.Fatalf("unexpected error: %v", err) 105 - } 106 - if c.cfg["key"] != "val" { 107 - t.Fatalf("got %v, want key=val", c.cfg) 108 - } 76 + is.Err(t, c.read([]byte("key = val\n")), nil) 77 + is.Equal(t, "val", c.cfg["key"]) 109 78 }) 110 79 111 80 t.Run("empty lines", func(t *testing.T) { 112 81 c := Config{cfg: make(map[string]string)} 113 - err := c.read([]byte("\nkey = val\n\n")) 114 - if err != nil { 115 - t.Fatalf("unexpected error: %v", err) 116 - } 117 - if c.cfg["key"] != "val" { 118 - t.Fatalf("got %v, want key=val", c.cfg) 119 - } 82 + is.Err(t, c.read([]byte("\nkey = val\n\n")), nil) 83 + is.Equal(t, "val", c.cfg["key"]) 120 84 }) 121 85 122 86 t.Run("multiple equals signs", func(t *testing.T) { 123 87 c := Config{cfg: make(map[string]string)} 124 - err := c.read([]byte("key = val = extra")) 125 - if err != ErrInvalidConfig { 126 - t.Fatalf("got %v, want ErrInvalidConfig", err) 127 - } 88 + is.Err(t, c.read([]byte("key = val = extra")), ErrInvalidConfig) 128 89 }) 129 90 130 91 t.Run("no separator", func(t *testing.T) { 131 92 c := Config{cfg: make(map[string]string)} 132 - err := c.read([]byte("justtext")) 133 - if err != ErrInvalidConfig { 134 - t.Fatalf("got %v, want ErrInvalidConfig", err) 135 - } 93 + is.Err(t, c.read([]byte("justtext")), ErrInvalidConfig) 136 94 }) 137 95 }
A
internal/plumbing/config/config_test.go
··· 1 +package config 2 + 3 +import ( 4 + "os" 5 + "path/filepath" 6 + "testing" 7 + 8 + "github.com/olexsmir/viye/internal/viye" 9 + 10 + "olexsmir.xyz/x/is" 11 +) 12 + 13 +func TestMatch(t *testing.T) { 14 + got := (Tool{}).Match(&viye.Context{Cmd: "config"}) 15 + is.Equal(t, true, got) 16 +} 17 + 18 +func TestExecute(t *testing.T) { 19 + t.Run("no body empty config", func(t *testing.T) { 20 + dir := t.TempDir() 21 + t.Setenv("XDG_CONFIG_HOME", dir) 22 + 23 + got, err := (Tool{}).Execute(&viye.Context{Path: []string{"config"}}) 24 + is.Err(t, err, nil) 25 + is.Equal(t, "\n", got) 26 + }) 27 + 28 + t.Run("no body with existing key", func(t *testing.T) { 29 + dir := t.TempDir() 30 + t.Setenv("XDG_CONFIG_HOME", dir) 31 + 32 + cfgPath := filepath.Join(dir, "viye.conf") 33 + os.WriteFile(cfgPath, []byte("k = v\n"), 0o644) 34 + 35 + got, err := (Tool{}).Execute(&viye.Context{Path: []string{"config"}}) 36 + is.Err(t, err, nil) 37 + is.Equal(t, ": k = v\n\n", got) 38 + }) 39 + 40 + t.Run("set key", func(t *testing.T) { 41 + dir := t.TempDir() 42 + t.Setenv("XDG_CONFIG_HOME", dir) 43 + 44 + got, err := (Tool{}).Execute(&viye.Context{ 45 + Path: []string{"config"}, 46 + Body: []string{": name = olex"}, 47 + }) 48 + is.Err(t, err, nil) 49 + is.Equal(t, ": name = olex\n\n", got) 50 + }) 51 + 52 + t.Run("update and delete", func(t *testing.T) { 53 + dir := t.TempDir() 54 + t.Setenv("XDG_CONFIG_HOME", dir) 55 + 56 + cfgPath := filepath.Join(dir, "viye.conf") 57 + os.WriteFile(cfgPath, []byte("a = 1\nb = 2\n"), 0o644) 58 + 59 + got, err := (Tool{}).Execute(&viye.Context{ 60 + Path: []string{"config"}, 61 + Body: []string{": a = updated"}, 62 + }) 63 + is.Err(t, err, nil) 64 + is.Equal(t, ": a = updated\n\n", got) 65 + }) 66 + 67 + t.Run("key with spaces", func(t *testing.T) { 68 + dir := t.TempDir() 69 + t.Setenv("XDG_CONFIG_HOME", dir) 70 + 71 + _, err := (Tool{}).Execute(&viye.Context{ 72 + Path: []string{"config"}, 73 + Body: []string{": my key = val"}, 74 + }) 75 + is.Err(t, err, `contains spaces`) 76 + }) 77 +}
M
internal/plumbing/files/files_test.go
··· 7 7 "testing" 8 8 9 9 "github.com/olexsmir/viye/internal/viye" 10 + 11 + "olexsmir.xyz/x/is" 10 12 ) 11 13 12 14 func TestMatch(t *testing.T) { 13 15 tests := []struct { 14 - p string 15 - m bool 16 + p string 17 + want bool 16 18 }{ 17 19 {"~/foo", true}, 18 20 {"/tmp", true}, 19 21 {"/tmp/foo", true}, 20 22 {"./foo", true}, 21 23 {"./foo/bar", true}, 22 - 23 24 {"nope", false}, 24 25 {"$ ls", false}, 25 26 {"~", false}, ··· 28 29 } 29 30 for _, tt := range tests { 30 31 got := (&Tool{}).Match(&viye.Context{Path: []string{tt.p}}) 31 - if got != tt.m { 32 - t.Errorf("Match(%q) = %v; want %v", tt.p, got, tt.m) 33 - } 32 + is.Equal(t, tt.want, got) 34 33 } 35 34 } 36 35 ··· 40 39 os.WriteFile(filepath.Join(dir, "x"), nil, 0o644) 41 40 ctx := &viye.Context{Path: []string{dir}, Dir: "."} 42 41 got, err := (&Tool{}).Execute(ctx) 43 - if err != nil { 44 - t.Fatal(err) 45 - } 46 - want := "| x\n" 47 - if got != want { 48 - t.Errorf("Execute = %q; want %q", got, want) 49 - } 42 + is.Err(t, err, nil) 43 + is.Equal(t, "| x\n", got) 50 44 }) 51 45 52 46 t.Run("navigate into subdir via full path", func(t *testing.T) { ··· 58 52 v := viye.New() 59 53 v.Register(&Tool{}) 60 54 var out strings.Builder 61 - if err := v.Run(&out, []string{"viye", sub}); err != nil { 62 - t.Fatal(err) 63 - } 64 - want := "| y\n" 65 - if out.String() != want { 66 - t.Errorf("Run = %q; want %q", out.String(), want) 67 - } 55 + err := v.Run(&out, []string{"viye", sub}) 56 + is.Err(t, err, nil) 57 + is.Equal(t, "| y\n", out.String()) 68 58 }) 69 59 70 60 t.Run("non existent path", func(t *testing.T) { 71 61 ctx := &viye.Context{Path: []string{"/nonexistent_foobar"}, Dir: "."} 72 - if _, err := (&Tool{}).Execute(ctx); err == nil { 73 - t.Error("expected error") 74 - } 62 + _, err := (&Tool{}).Execute(ctx) 63 + is.NotEqual(t, nil, err) 75 64 }) 76 65 } 77 66 78 67 func TestResolve(t *testing.T) { 79 68 home, _ := os.UserHomeDir() 80 - tests := []struct { 81 - p, dir string 82 - want string 83 - }{ 69 + tests := []struct{ p, dir, want string }{ 84 70 {"~", ".", home}, 85 71 {"~/foo", ".", filepath.Join(home, "foo")}, 86 72 {"/abs/path", ".", "/abs/path"}, ··· 90 76 } 91 77 for _, tt := range tests { 92 78 got := resolve(tt.p, tt.dir) 93 - if got != tt.want { 94 - t.Errorf("resolve(%q, %q) = %q; want %q", tt.p, tt.dir, got, tt.want) 95 - } 79 + is.Equal(t, tt.want, got) 96 80 } 97 81 } 98 82 ··· 102 86 os.WriteFile(filepath.Join(dir, "a.txt"), nil, 0o644) 103 87 104 88 got, err := listDir(dir) 105 - if err != nil { 106 - t.Fatal(err) 107 - } 108 - if got != "a.txt\nsub/\n" { 109 - t.Errorf("listDir = %q; want %q", got, "a.txt\\nsub/\\n") 110 - } 89 + is.Err(t, err, nil) 90 + is.Equal(t, "a.txt\nsub/\n", got) 111 91 }
M
internal/plumbing/gobin/gobin_test.go
··· 4 4 "testing" 5 5 6 6 "github.com/olexsmir/viye/internal/viye" 7 + 8 + "olexsmir.xyz/x/is" 7 9 ) 8 10 9 11 func TestMatch(t *testing.T) { ··· 19 21 } 20 22 for _, tt := range tests { 21 23 got := (Tool{}).Match(&viye.Context{Path: tt.path}) 22 - if got != tt.want { 23 - t.Errorf("Match(%v) = %v; want %v", tt.path, got, tt.want) 24 - } 24 + is.Equal(t, tt.want, got) 25 25 } 26 26 } 27 27
M
internal/plumbing/json/json.go
··· 13 13 func (Tool) Name() string { return "json" } 14 14 func (Tool) Match(ctx *viye.Context) bool { return ctx.Cmd == "json" } 15 15 func (Tool) Execute(ctx *viye.Context) (string, error) { 16 - // Phase 1: no body → return editable template 17 16 if len(ctx.Body) == 0 { 18 - return ": name: your name\n: age: 0\n", nil 17 + return ": name: your name\n" + 18 + ": age: 0\n", nil 19 19 } 20 20 21 - // Phase 2: body present → parse and produce JSON 22 21 m := make(map[string]string) 23 22 for _, line := range ctx.Body { 24 - // Strip ": " prefix 25 23 line = strings.TrimPrefix(line, ": ") 26 24 col := strings.Index(line, ":") 27 25 if col < 0 { 28 26 continue 29 27 } 28 + 30 29 key := strings.TrimSpace(line[:col]) 31 30 val := strings.TrimSpace(line[col+1:]) 32 31 if key != "" {
A
internal/plumbing/json/json_test.go
··· 1 +package json 2 + 3 +import ( 4 + "testing" 5 + 6 + "github.com/olexsmir/viye/internal/viye" 7 + 8 + "olexsmir.xyz/x/is" 9 +) 10 + 11 +func TestMatch(t *testing.T) { 12 + got := (Tool{}).Match(&viye.Context{Cmd: "json"}) 13 + is.Equal(t, true, got) 14 +} 15 + 16 +func TestExecute(t *testing.T) { 17 + tests := map[string]struct { 18 + body []string 19 + want string 20 + }{ 21 + "no body": {want: ": name: your name\n: age: 0\n"}, 22 + "empty body slice": {want: ": name: your name\n: age: 0\n"}, 23 + "single key": { 24 + body: []string{": name: olex"}, 25 + want: `| {"name":"olex"}` + "\n", 26 + }, 27 + "multiple keys": { 28 + body: []string{": name: olex", ": age: 30"}, 29 + want: `| {"age":"30","name":"olex"}` + "\n", 30 + }, 31 + "values with spaces": { 32 + body: []string{": title: hello world"}, 33 + want: `| {"title":"hello world"}` + "\n", 34 + }, 35 + "keys with spaces": { 36 + body: []string{": my key: value"}, 37 + want: `| {"my key":"value"}` + "\n", 38 + }, 39 + "line without colon separator": { 40 + body: []string{": justtext"}, 41 + want: `| {}` + "\n", 42 + }, 43 + "line without : prefix": { 44 + body: []string{"name: olex"}, 45 + want: `| {"name":"olex"}` + "\n", 46 + }, 47 + "mixed valid and invalid": { 48 + body: []string{": name: olex", "garbage", ": age: 30"}, 49 + want: `| {"age":"30","name":"olex"}` + "\n", 50 + }, 51 + "empty key": { 52 + body: []string{": : value"}, 53 + want: `| {}` + "\n", 54 + }, 55 + "trailing whitespace in key": { 56 + body: []string{": name : olex"}, 57 + want: `| {"name":"olex"}` + "\n", 58 + }, 59 + "trailing whitespace in value": { 60 + body: []string{": name: olex "}, 61 + want: `| {"name":"olex"}` + "\n", 62 + }, 63 + "empty value": { 64 + body: []string{": name: "}, 65 + want: `| {"name":""}` + "\n", 66 + }, 67 + } 68 + for name, tt := range tests { 69 + t.Run(name, func(t *testing.T) { 70 + got, err := (Tool{}).Execute(&viye.Context{Cmd: "json", Body: tt.body}) 71 + is.Err(t, err, nil) 72 + is.Equal(t, tt.want, got) 73 + }) 74 + } 75 +}
M
internal/plumbing/shell/shell.go
··· 21 21 return isCmd(ctx) || isBGCmd(ctx) || isKill(ctx) || isMkdir(ctx) 22 22 } 23 23 24 -func (Tool) Execute(ctx *viye.Context) (string, error) { 24 +func (Tool) Execute(c *viye.Context) (string, error) { 25 25 switch { 26 - case isCmd(ctx): 27 - ctxTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) 26 + case isCmd(c): 27 + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 28 28 defer cancel() 29 29 30 - cmd := exec.CommandContext(ctxTimeout, "sh", "-c", strings.Join(ctx.Args, " ")) 31 - cmd.Dir = ctx.Dir 30 + cmd := exec.CommandContext(ctx, "sh", "-c", strings.Join(c.Args, " ")) 31 + cmd.Dir = c.Dir 32 32 out, err := cmd.CombinedOutput() 33 33 if err != nil { 34 - if ctxTimeout.Err() == context.DeadlineExceeded { 34 + if ctx.Err() == context.DeadlineExceeded { 35 35 return "", fmt.Errorf("timeout: command exceeded 5s") 36 36 } 37 37 } 38 38 return viye.FormatOutput(string(out)), nil 39 39 40 - case isBGCmd(ctx): 41 - cmd := exec.Command("sh", "-c", strings.Join(ctx.Args, " ")) 40 + case isBGCmd(c): 41 + cmd := exec.Command("sh", "-c", strings.Join(c.Args, " ")) 42 42 cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} 43 - cmd.Dir = ctx.Dir 43 + cmd.Dir = c.Dir 44 44 if err := cmd.Start(); err != nil { 45 45 return "", err 46 46 } 47 47 return fmt.Sprintf("| pid: %d", cmd.Process.Pid), nil 48 48 49 - case isKill(ctx): 50 - pid, err := strconv.Atoi(ctx.Args[0]) 49 + case isKill(c): 50 + pid, err := strconv.Atoi(c.Args[0]) 51 51 if err != nil { 52 - return "", fmt.Errorf("kill: invalid pid %q", ctx.Args[0]) 52 + return "", fmt.Errorf("kill: invalid pid %q", c.Args[0]) 53 53 } 54 54 p, err := os.FindProcess(pid) 55 55 if err != nil { ··· 60 60 } 61 61 return "| done\n", nil 62 62 63 - case isMkdir(ctx): 64 - path := filepath.Join(append([]string{ctx.Dir}, ctx.Args...)...) 63 + case isMkdir(c): 64 + path := filepath.Join(append([]string{c.Dir}, c.Args...)...) 65 65 if err := os.MkdirAll(path, 0o755); err != nil { 66 66 return "", fmt.Errorf("mkdir: %w", err) 67 67 }
M
internal/plumbing/shell/shell_test.go
··· 6 6 "testing" 7 7 8 8 "github.com/olexsmir/viye/internal/viye" 9 + 10 + "olexsmir.xyz/x/is" 9 11 ) 10 12 11 13 func TestMatch(t *testing.T) { 12 14 tests := []struct { 13 - path string 14 - cmd string 15 - args []string 16 - want bool 15 + path, cmd string 16 + args []string 17 + want bool 17 18 }{ 18 19 {"$", "$", nil, true}, 19 20 {"$ go run .", "$", []string{"go", "run", "."}, true}, ··· 35 36 Cmd: tt.cmd, 36 37 Args: tt.args, 37 38 }) 38 - if got != tt.want { 39 - t.Errorf("Match(%q) = %v; want %v", tt.path, got, tt.want) 40 - } 39 + is.Equal(t, tt.want, got) 41 40 } 42 41 } 43 42 ··· 49 48 Args: []string{"echo", "hello"}, 50 49 Dir: ".", 51 50 }) 52 - if err != nil { 53 - t.Fatal(err) 54 - } 55 - if got != "| hello\n" { 56 - t.Errorf("Execute($ echo hello) = %q; want %q", got, "| hello\\n") 57 - } 51 + is.Err(t, err, nil) 52 + is.Equal(t, "| hello\n", got) 58 53 }) 59 54 60 55 t.Run("bg cmd", func(t *testing.T) { ··· 64 59 Args: []string{"sleep", "0"}, 65 60 Dir: ".", 66 61 }) 67 - if err != nil { 68 - t.Fatal(err) 69 - } 70 - if got != "| pid: " && len(got) < 10 { 71 - t.Errorf("Execute($$ sleep 0) = %q; want pid string", got) 62 + is.Err(t, err, nil) 63 + if len(got) < 10 { 64 + t.Fatalf("got short output %q, expected pid string", got) 72 65 } 73 66 }) 74 67 ··· 80 73 Args: []string{"sub"}, 81 74 Dir: dir, 82 75 }) 83 - if err != nil { 84 - t.Fatal(err) 85 - } 86 - if _, err := os.Stat(filepath.Join(dir, "sub")); err != nil { 87 - t.Errorf("dir not created: %v", err) 88 - } 89 - if got != "| created "+filepath.Join(dir, "sub")+"\n" { 90 - t.Errorf("output = %q; want %q", got, "| created "+filepath.Join(dir, "sub")+"\n") 91 - } 76 + is.Err(t, err, nil) 77 + 78 + _, err = os.Stat(filepath.Join(dir, "sub")) 79 + is.Err(t, err, nil) 80 + is.Equal(t, "| created "+filepath.Join(dir, "sub")+"\n", got) 92 81 }) 93 82 94 83 t.Run("kill", func(t *testing.T) { 95 - // no reliable way to test kill without a real process, 96 - // just verify it doesn't panic with valid args structure 97 - if _, err := (&Tool{}).Execute(&viye.Context{ 84 + _, err := (&Tool{}).Execute(&viye.Context{ 98 85 Path: []string{"kill 999999"}, 99 86 Cmd: "kill", 100 87 Args: []string{"999999"}, 101 88 Dir: ".", 102 - }); err == nil { 103 - t.Log("kill on bogus pid returned nil (expected error or success)") 104 - } 89 + }) 90 + is.NotEqual(t, nil, err) 105 91 }) 106 92 }
M
internal/viye/viye_test.go
··· 2 2 3 3 import ( 4 4 "testing" 5 + 6 + "olexsmir.xyz/x/is" 5 7 ) 6 8 7 9 func TestSplitLeaf(t *testing.T) { ··· 25 27 } 26 28 for _, tt := range tests { 27 29 cmd, args := splitLeaf(tt.leaf) 28 - if cmd != tt.cmd { 29 - t.Errorf("splitLeaf(%q) cmd = %q; want %q", tt.leaf, cmd, tt.cmd) 30 - } 31 - if len(args) != len(tt.args) { 32 - t.Errorf("splitLeaf(%q) len(args) = %d; want %d", tt.leaf, len(args), len(tt.args)) 33 - } else { 30 + is.Equal(t, tt.cmd, cmd) 31 + is.Equal(t, len(tt.args), len(args)) 32 + if len(args) == len(tt.args) { 34 33 for i := range args { 35 - if args[i] != tt.args[i] { 36 - t.Errorf("splitLeaf(%q) args[%d] = %q; want %q", tt.leaf, i, args[i], tt.args[i]) 37 - } 34 + is.Equal(t, tt.args[i], args[i]) 38 35 } 39 36 } 40 37 } 41 38 } 42 39 43 40 func TestSplitArgs(t *testing.T) { 44 - tests := []struct { 45 - args []string 46 - want []string 47 - wantB []string 48 - }{ 41 + tests := []struct{ args, want, wantB []string }{ 49 42 {[]string{"/tmp/foo", "bar/baz"}, []string{"/tmp/foo", "bar/baz"}, nil}, 50 43 {[]string{"./foo", "bar"}, []string{"./foo", "bar"}, nil}, 51 44 {[]string{"./"}, []string{"./"}, nil}, ··· 57 50 } 58 51 for _, tt := range tests { 59 52 got, gotB := splitArgs(tt.args) 60 - if len(got) != len(tt.want) { 61 - t.Errorf("splitArgs(%v) path = %v; want %v", tt.args, got, tt.want) 62 - continue 63 - } 64 - for i := range got { 65 - if got[i] != tt.want[i] { 66 - t.Errorf("splitArgs(%v) path = %v; want %v", tt.args, got, tt.want) 67 - break 53 + is.Equal(t, len(tt.want), len(got)) 54 + if len(got) == len(tt.want) { 55 + for i := range got { 56 + is.Equal(t, tt.want[i], got[i]) 68 57 } 69 58 } 70 - if len(gotB) != len(tt.wantB) { 71 - t.Errorf("splitArgs(%v) body = %v; want %v", tt.args, gotB, tt.wantB) 72 - continue 73 - } 74 - for i := range gotB { 75 - if gotB[i] != tt.wantB[i] { 76 - t.Errorf("splitArgs(%v) body = %v; want %v", tt.args, gotB, tt.wantB) 77 - break 59 + is.Equal(t, len(tt.wantB), len(gotB)) 60 + if len(gotB) == len(tt.wantB) { 61 + for i := range gotB { 62 + is.Equal(t, tt.wantB[i], gotB[i]) 78 63 } 79 64 } 80 65 }