1package zli 2 3import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net/mail" 10 "os" 11 "reflect" 12 "runtime" 13 "strings" 14 "testing" 15) 16 17func TestFatal(t *testing.T) { 18 tests := []struct { 19 in interface{} 20 args []interface{} 21 want string 22 }{ 23 {"", nil, "zli.test: \n"}, 24 {nil, nil, "zli.test: <nil>\n"}, 25 {nil, nil, "zli.test: <nil>\n"}, 26 {42, nil, "zli.test: 42\n"}, 27 28 {"oh noes", nil, "zli.test: oh noes\n"}, 29 {"oh noes: %d", []interface{}{42}, "zli.test: oh noes: 42\n"}, 30 {"oh noes: %d %d", []interface{}{42, 666}, "zli.test: oh noes: 42 666\n"}, 31 {[]byte("oh noes: %d %d"), []interface{}{42, 666}, "zli.test: oh noes: 42 666\n"}, 32 33 {errors.New("oh noes"), nil, "zli.test: oh noes\n"}, 34 {errors.New("oh noes"), []interface{}{"data", 666}, "zli.test: oh noes [data 666]\n"}, 35 36 {mail.Address{Name: "asd", Address: "qwe"}, nil, "zli.test: {asd qwe}\n"}, 37 {mail.Address{Name: "asd", Address: "qwe"}, []interface{}{"data", 666}, "zli.test: {asd qwe} [data 666]\n"}, 38 } 39 40 for i, tt := range tests { 41 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 42 exit, _, out := Test(t) 43 44 func() { 45 defer exit.Recover() 46 Fatalf(tt.in, tt.args...) 47 }() 48 49 if *exit != 1 { 50 t.Errorf("wrong exit: %d", *exit) 51 } 52 got := out.String() 53 if got != tt.want { 54 t.Errorf("\ngot: %q\nwant: %q", got, tt.want) 55 } 56 }) 57 } 58} 59 60func TestF(t *testing.T) { 61 t.Run("nil", func(t *testing.T) { 62 _, _, out := Test(t) 63 64 var err error 65 F(err) // Will panic if exit is set. 66 67 if out.String() != "" { 68 t.Errorf("out has data: %q", out.String()) 69 } 70 }) 71 72 t.Run("nil", func(t *testing.T) { 73 exit, _, out := Test(t) 74 75 func() { 76 defer exit.Recover() 77 F(errors.New("oh noes")) 78 }() 79 80 if *exit != 1 { 81 t.Errorf("wrong exit: %d", *exit) 82 } 83 if out.String() != "zli.test: oh noes\n" { 84 t.Errorf("wrong out: %q", out.String()) 85 } 86 }) 87} 88 89func TestInputOrFile(t *testing.T) { 90 tests := []struct { 91 in string 92 stdin io.Reader 93 want, wantErr string 94 }{ 95 {"/nonexistent", nil, "", "no such file or directory"}, 96 {"zli_test.go", strings.NewReader("xx"), "package zli", ""}, 97 {"-", strings.NewReader("xx yy\nzz"), "xx yy\nzz", ""}, 98 {"", strings.NewReader("xx yy\nzz"), "xx yy\nzz", ""}, 99 } 100 101 for _, tt := range tests { 102 t.Run(tt.in, func(t *testing.T) { 103 Stdin = tt.stdin 104 defer func() { Stdin = os.Stdin }() 105 106 fp, err := InputOrFile(tt.in, true) 107 if !errorContains(err, tt.wantErr) { 108 t.Errorf("wrong error\ngot: %s\nwant: %s", err, tt.wantErr) 109 } 110 111 // No need to test the test if there's an error. 112 if err != nil { 113 return 114 } 115 116 if fp == nil { 117 t.Fatal("fp is nil") 118 } 119 120 got, err := ioutil.ReadAll(fp) 121 if err != nil { 122 t.Errorf("error reading fp: %s", err) 123 } 124 125 err = fp.Close() 126 if err != nil { 127 t.Errorf("error closing fp: %s", err) 128 } 129 130 g := string(got) 131 if len(g) > len(tt.want) { 132 g = g[:len(tt.want)] 133 } 134 if !strings.HasPrefix(g, tt.want) { 135 t.Errorf("wrong output\ngot: %q\nwant: %q", g, tt.want) 136 } 137 }) 138 } 139} 140 141func TestInputOrArgs(t *testing.T) { 142 tests := []struct { 143 in []string 144 sep string 145 stdin io.Reader 146 want []string 147 }{ 148 {[]string{"arg"}, "", nil, []string{"arg"}}, 149 {nil, "", strings.NewReader(""), []string{}}, 150 {nil, "\n ", strings.NewReader(""), []string{}}, 151 152 {nil, "", strings.NewReader("a"), []string{"a"}}, 153 {[]string{}, "", strings.NewReader("a"), []string{"a"}}, 154 {[]string{}, "", strings.NewReader("a\nb c"), []string{"a\nb c"}}, 155 156 {[]string{}, " ", strings.NewReader(" a b c "), []string{"a", "b", "c"}}, 157 {[]string{}, "\x00", strings.NewReader("aa\x00bb"), []string{"aa", "bb"}}, 158 } 159 160 for _, tt := range tests { 161 t.Run(fmt.Sprintf("%s", tt.in), func(t *testing.T) { 162 Stdin = tt.stdin 163 defer func() { Stdin = os.Stdin }() 164 165 got, err := InputOrArgs(tt.in, tt.sep, true) 166 if err != nil { 167 t.Fatal(err) 168 } 169 170 if !reflect.DeepEqual(got, tt.want) { 171 t.Errorf("\ngot: %#v\nwant: %#v", got, tt.want) 172 } 173 }) 174 } 175} 176 177func TestPager(t *testing.T) { 178 set := func(term bool) (*bytes.Buffer, func()) { 179 buf := new(bytes.Buffer) 180 Stdout = buf 181 Stderr = buf 182 183 save := IsTerminal 184 if term { 185 IsTerminal = func(uintptr) bool { return true } 186 } 187 188 return buf, func() { 189 Stdout = os.Stdout 190 Stderr = os.Stderr 191 IsTerminal = save 192 } 193 } 194 195 t.Run("not a terminal", func(t *testing.T) { 196 buf, c := set(false) 197 defer c() 198 199 Pager(strings.NewReader("buffy")) 200 if buf.String() != "buffy" { 201 t.Error(buf.String()) 202 } 203 }) 204 205 t.Run("no PAGER", func(t *testing.T) { 206 buf, c := set(true) 207 defer c() 208 209 os.Unsetenv("PAGER") 210 Pager(strings.NewReader("buffy")) 211 if buf.String() != "buffy" { 212 t.Errorf("out: %q", buf.String()) 213 } 214 }) 215 216 t.Run("PAGER doesn't exist", func(t *testing.T) { 217 buf, c := set(true) 218 defer c() 219 220 os.Setenv("PAGER", "doesntexistasdad") 221 Pager(strings.NewReader("buffy")) 222 223 want := "zli.test: zli.Pager: running $PAGER: exec: \"doesntexistasdad\": executable file not found in $PATH\nbuffy" 224 if buf.String() != want { 225 t.Errorf("out: %q", buf.String()) 226 } 227 }) 228 229 t.Run("PAGER doesn't exist w/ args", func(t *testing.T) { 230 buf, c := set(true) 231 defer c() 232 233 os.Setenv("PAGER", "doesntexistasdad -r -f") 234 Pager(strings.NewReader("buffy")) 235 236 want := "zli.test: zli.Pager: running $PAGER: exec: \"doesntexistasdad\": executable file not found in $PATH\nbuffy" 237 if buf.String() != want { 238 t.Errorf("out: %q", buf.String()) 239 } 240 }) 241 242 t.Run("error", func(t *testing.T) { 243 buf, c := set(true) 244 defer c() 245 246 // zli.Pager: running $PAGER: exit status 1 247 os.Setenv("PAGER", "false") 248 Pager(strings.NewReader("buffy")) 249 250 want := "zli.test: zli.Pager: running $PAGER: exit status 1\n" 251 if buf.String() != want { 252 t.Errorf("out: %q", buf.String()) 253 } 254 }) 255 256 t.Run("run it", func(t *testing.T) { 257 if runtime.GOOS == "windows" || runtime.GOOS == "js" { 258 t.Skip("requires cat shell tool") 259 } 260 261 buf, c := set(true) 262 defer c() 263 264 os.Setenv("PAGER", "cat") 265 Pager(strings.NewReader("buffy")) 266 267 if buf.String() != "buffy" { 268 t.Errorf("out: %q", buf.String()) 269 } 270 }) 271 272 t.Run("pagestdout", func(t *testing.T) { 273 buf, c := set(true) 274 defer c() 275 276 func() { 277 defer PagerStdout()() 278 fmt.Fprintf(Stdout, "buffy") 279 }() 280 281 if buf.String() != "buffy" { 282 t.Errorf("out: %q", buf.String()) 283 } 284 285 }) 286} 287 288func errorContains(out error, want string) bool { 289 if out == nil { 290 return want == "" 291 } 292 if want == "" { 293 return false 294 } 295 return strings.Contains(out.Error(), want) 296} 297