github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/shellwords/shellwords_test.go (about) 1 package shellwords 2 3 import ( 4 "go/build" 5 "os" 6 "path" 7 "reflect" 8 "testing" 9 10 "github.com/spf13/pflag" 11 "github.com/stretchr/testify/assert" 12 ) 13 14 func TestParseMySQLCli(t *testing.T) { 15 p := NewParser() 16 p.ParseEnv = true 17 s := `--MYSQL_PWD="! 8BE4" --mysql --host="127.0.0.1" -P 9633 -u 'root'` 18 args, err := p.Parse(s) 19 20 assert.Nil(t, err) 21 assert.Equal(t, []string{"--MYSQL_PWD=! 8BE4", "--mysql", "--host=127.0.0.1", "-P", "9633", "-u", "root"}, args) 22 23 pf := pflag.NewFlagSet("ds", pflag.ExitOnError) 24 25 pf.BoolP("mysql", "", false, "mysql command") 26 pf.StringP("MYSQL_PWD", "", "", "MYSQL_PWD env password") 27 pf.StringP("database", "D", "", "Database to use") 28 pf.StringP("host", "h", "", "Connect to host") 29 pf.IntP("port", "P", 3306, "Port number to use") 30 pf.StringP("user", "u", "", "User for login if not current user") 31 pf.StringP("password", "p", "", "Password to use when connecting to serve") 32 33 err = pf.Parse(args) 34 assert.Nil(t, err) 35 36 host, _ := pf.GetString("host") 37 assert.Equal(t, "127.0.0.1", host) 38 39 port, _ := pf.GetInt("port") 40 assert.Equal(t, 9633, port) 41 42 user, _ := pf.GetString("user") 43 assert.Equal(t, "root", user) 44 45 db, _ := pf.GetString("database") 46 assert.Equal(t, "", db) 47 48 password, _ := pf.GetString("password") 49 if password == "" { 50 password, _ = pf.GetString("MYSQL_PWD") 51 } 52 53 assert.Equal(t, "! 8BE4", password) 54 } 55 56 // nolint gochecknoglobals 57 var testcases = []struct { 58 line string 59 expected []string 60 }{ 61 {``, []string{}}, 62 {`""`, []string{``}}, 63 {`''`, []string{``}}, 64 {`var --bar=baz`, []string{`var`, `--bar=baz`}}, 65 {`var --bar="baz"`, []string{`var`, `--bar=baz`}}, 66 {`var "--bar=baz"`, []string{`var`, `--bar=baz`}}, 67 {`var "--bar='baz'"`, []string{`var`, `--bar='baz'`}}, 68 {"var --bar=`baz`", []string{`var`, "--bar=`baz`"}}, 69 {`var "--bar=\"baz'"`, []string{`var`, `--bar="baz'`}}, 70 {`var "--bar=\'baz\'"`, []string{`var`, `--bar='baz'`}}, 71 {`var --bar='\'`, []string{`var`, `--bar=\`}}, 72 {`var "--bar baz"`, []string{`var`, `--bar baz`}}, 73 {`var --"bar baz"`, []string{`var`, `--bar baz`}}, 74 {`var --"bar baz"`, []string{`var`, `--bar baz`}}, 75 {`a "b"`, []string{`a`, `b`}}, 76 {`a " b "`, []string{`a`, ` b `}}, 77 {`a " "`, []string{`a`, ` `}}, 78 {`a 'b'`, []string{`a`, `b`}}, 79 {`a ' b '`, []string{`a`, ` b `}}, 80 {`a ' '`, []string{`a`, ` `}}, 81 {"foo bar\\ ", []string{`foo`, `bar `}}, 82 {`foo "" bar ''`, []string{`foo`, ``, `bar`, ``}}, 83 } 84 85 func TestSimple(t *testing.T) { 86 for _, testcase := range testcases { 87 args, err := Parse(testcase.line) 88 if err != nil { 89 t.Fatal(err) 90 } 91 92 if !reflect.DeepEqual(args, testcase.expected) { 93 t.Fatalf("Expected %#v for %q, but %#v:", testcase.expected, testcase.line, args) 94 } 95 } 96 } 97 98 func TestError(t *testing.T) { 99 _, err := Parse("foo '") 100 if err == nil { 101 t.Fatal("Should be an error") 102 } 103 104 _, err = Parse(`foo "`) 105 106 if err == nil { 107 t.Fatal("Should be an error") 108 } 109 110 _, err = Parse("foo `") 111 if err == nil { 112 t.Fatal("Should be an error") 113 } 114 } 115 116 func TestShellRun(t *testing.T) { 117 dir, err := os.Getwd() 118 if err != nil { 119 t.Fatal(err) 120 } 121 122 pwd, err := shellRun("pwd", "") 123 if err != nil { 124 t.Fatal(err) 125 } 126 127 pwd2, err := shellRun("pwd", path.Join(dir, "/testdata")) 128 if err != nil { 129 t.Fatal(err) 130 } 131 132 if pwd == pwd2 { 133 t.Fatal("`pwd` should be changed") 134 } 135 } 136 137 func TestShellRunNoEnv(t *testing.T) { 138 old := os.Getenv("SHELL") 139 defer os.Setenv("SHELL", old) 140 os.Unsetenv("SHELL") 141 142 dir, err := os.Getwd() 143 if err != nil { 144 t.Fatal(err) 145 } 146 147 pwd, err := shellRun("pwd", "") 148 if err != nil { 149 t.Fatal(err) 150 } 151 152 pwd2, err := shellRun("pwd", path.Join(dir, "/testdata")) 153 if err != nil { 154 t.Fatal(err) 155 } 156 157 if pwd == pwd2 { 158 t.Fatal("`pwd` should be changed") 159 } 160 } 161 162 func TestBacktick(t *testing.T) { 163 goversion, err := shellRun("go version", "") 164 if err != nil { 165 t.Fatal(err) 166 } 167 168 parser := NewParser() 169 parser.ParseBacktick = true 170 args, err := parser.Parse("echo `go version`") 171 if err != nil { 172 t.Fatal(err) 173 } 174 175 expected := []string{"echo", goversion} 176 177 if !reflect.DeepEqual(args, expected) { 178 t.Fatalf("Expected %#v, but %#v:", expected, args) 179 } 180 181 args, err = parser.Parse(`echo $(echo foo)`) 182 if err != nil { 183 t.Fatal(err) 184 } 185 186 expected = []string{"echo", "foo"} 187 if !reflect.DeepEqual(args, expected) { 188 t.Fatalf("Expected %#v, but %#v:", expected, args) 189 } 190 191 args, err = parser.Parse(`echo bar=$(echo 200)cm`) 192 if err != nil { 193 t.Fatal(err) 194 } 195 196 expected = []string{"echo", "bar=200cm"} 197 198 if !reflect.DeepEqual(args, expected) { 199 t.Fatalf("Expected %#v, but %#v:", expected, args) 200 } 201 202 parser.ParseBacktick = false 203 args, _ = parser.Parse(`echo $(echo "foo")`) 204 expected = []string{"echo", `$(echo "foo")`} 205 206 if !reflect.DeepEqual(args, expected) { 207 t.Fatalf("Expected %#v, but %#v:", expected, args) 208 } 209 210 args, err = parser.Parse("echo $(`echo1)") 211 212 if err != nil { 213 t.Fatal(err) 214 } 215 216 expected = []string{"echo", "$(`echo1)"} 217 if !reflect.DeepEqual(args, expected) { 218 t.Fatalf("Expected %#v, but %#v:", expected, args) 219 } 220 } 221 222 func TestBacktickMulti(t *testing.T) { 223 parser := NewParser() 224 parser.ParseBacktick = true 225 args, err := parser.Parse(`echo $(go env GOPATH && go env GOROOT)`) 226 if err != nil { 227 t.Fatal(err) 228 } 229 230 expected := []string{"echo", build.Default.GOPATH + "\n" + build.Default.GOROOT} 231 232 if !reflect.DeepEqual(args, expected) { 233 t.Fatalf("Expected %#v, but %#v:", expected, args) 234 } 235 } 236 237 func TestBacktickError(t *testing.T) { 238 parser := NewParser() 239 parser.ParseBacktick = true 240 _, err := parser.Parse("echo `go Version`") 241 242 if err == nil { 243 t.Fatal("Should be an error") 244 } 245 246 expected := "exit status 2:go Version: unknown command\nRun 'go help' for usage.\n" 247 if expected != err.Error() { 248 t.Fatalf("Expected %q, but %q", expected, err.Error()) 249 } 250 251 _, err = parser.Parse(`echo $(echo1)`) 252 if err == nil { 253 t.Fatal("Should be an error") 254 } 255 256 _, err = parser.Parse(`echo FOO=$(echo1)`) 257 258 if err == nil { 259 t.Fatal("Should be an error") 260 } 261 262 _, err = parser.Parse(`echo $(echo1`) 263 264 if err == nil { 265 t.Fatal("Should be an error") 266 } 267 268 _, err = parser.Parse(`echo $ (echo1`) 269 270 if err == nil { 271 t.Fatal("Should be an error") 272 } 273 274 _, err = parser.Parse(`echo (echo1`) 275 276 if err == nil { 277 t.Fatal("Should be an error") 278 } 279 280 _, err = parser.Parse(`echo )echo1`) 281 if err == nil { 282 t.Fatal("Should be an error") 283 } 284 } 285 286 func TestEnv(t *testing.T) { 287 os.Setenv("FOO", "bar") 288 289 parser := NewParser() 290 parser.ParseEnv = true 291 args, err := parser.Parse("echo $FOO") 292 if err != nil { 293 t.Fatal(err) 294 } 295 296 expected := []string{"echo", "bar"} 297 if !reflect.DeepEqual(args, expected) { 298 t.Fatalf("Expected %#v, but %#v:", expected, args) 299 } 300 } 301 302 func TestCustomEnv(t *testing.T) { 303 parser := NewParser() 304 parser.ParseEnv = true 305 parser.Getenv = func(k string) string { return map[string]string{"FOO": "baz"}[k] } 306 args, err := parser.Parse("echo $FOO") 307 if err != nil { 308 t.Fatal(err) 309 } 310 311 expected := []string{"echo", "baz"} 312 313 if !reflect.DeepEqual(args, expected) { 314 t.Fatalf("Expected %#v, but %#v:", expected, args) 315 } 316 } 317 318 func TestNoEnv(t *testing.T) { 319 parser := NewParser() 320 parser.ParseEnv = true 321 args, err := parser.Parse("echo $BAR") 322 if err != nil { 323 t.Fatal(err) 324 } 325 326 expected := []string{"echo"} 327 if !reflect.DeepEqual(args, expected) { 328 t.Fatalf("Expected %#v, but %#v:", expected, args) 329 } 330 } 331 332 func TestEnvArguments(t *testing.T) { 333 os.Setenv("FOO", "bar baz") 334 335 parser := NewParser() 336 parser.ParseEnv = true 337 args, err := parser.Parse("echo $FOO") 338 if err != nil { 339 t.Fatal(err) 340 } 341 342 expected := []string{"echo", "bar baz"} 343 if !reflect.DeepEqual(args, expected) { 344 t.Fatalf("Expected %#v, but %#v:", expected, args) 345 } 346 } 347 348 func TestEnvArgumentsFail(t *testing.T) { 349 os.Setenv("FOO", "bar '") 350 351 parser := NewParser() 352 parser.ParseEnv = true 353 _, err := parser.Parse("echo $FOO") 354 if err != nil { 355 t.Fatal("Should not be an error") 356 } 357 358 _, err = parser.Parse("$FOO") 359 360 if err != nil { 361 t.Fatal("Should not be an error") 362 } 363 364 _, err = parser.Parse("echo $FOO") 365 if err != nil { 366 t.Fatal("Should be an error") 367 } 368 369 os.Setenv("FOO", "bar `") 370 371 _, err = parser.Parse("$FOO ") 372 if err != nil { 373 t.Fatal("Should be an error") 374 } 375 } 376 377 func TestDupEnv(t *testing.T) { 378 os.Setenv("FOO", "bar") 379 os.Setenv("FOO_BAR", "baz") 380 381 parser := NewParser() 382 parser.ParseEnv = true 383 args, err := parser.Parse("echo $$FOO$") 384 if err != nil { 385 t.Fatal(err) 386 } 387 388 expected := []string{"echo", "$bar$"} 389 390 if !reflect.DeepEqual(args, expected) { 391 t.Fatalf("Expected %#v, but %#v:", expected, args) 392 } 393 394 args, err = parser.Parse("echo $${FOO_BAR}$") 395 if err != nil { 396 t.Fatal(err) 397 } 398 399 expected = []string{"echo", "$baz$"} 400 401 if !reflect.DeepEqual(args, expected) { 402 t.Fatalf("Expected %#v, but %#v:", expected, args) 403 } 404 } 405 406 func TestHaveMore(t *testing.T) { 407 parser := NewParser() 408 parser.ParseEnv = true 409 410 line := "echo foo; seq 1 10" 411 args, err := parser.Parse(line) 412 if err != nil { 413 t.Fatalf(err.Error()) 414 } 415 416 expected := []string{"echo", "foo"} 417 if !reflect.DeepEqual(args, expected) { 418 t.Fatalf("Expected %#v, but %#v:", expected, args) 419 } 420 421 if parser.Position == 0 { 422 t.Fatalf("Commands should be remaining") 423 } 424 425 line = string([]rune(line)[parser.Position+1:]) 426 args, err = parser.Parse(line) 427 428 if err != nil { 429 t.Fatalf(err.Error()) 430 } 431 432 expected = []string{"seq", "1", "10"} 433 434 if !reflect.DeepEqual(args, expected) { 435 t.Fatalf("Expected %#v, but %#v:", expected, args) 436 } 437 438 if parser.Position > 0 { 439 t.Fatalf("Commands should not be remaining") 440 } 441 } 442 443 func TestHaveRedirect(t *testing.T) { 444 parser := NewParser() 445 parser.ParseEnv = true 446 447 line := "ls -la 2>foo" 448 args, err := parser.Parse(line) 449 if err != nil { 450 t.Fatalf(err.Error()) 451 } 452 453 expected := []string{"ls", "-la"} 454 if !reflect.DeepEqual(args, expected) { 455 t.Fatalf("Expected %#v, but %#v:", expected, args) 456 } 457 458 if parser.Position == 0 { 459 t.Fatalf("Commands should be remaining") 460 } 461 } 462 463 func TestBackquoteInFlag(t *testing.T) { 464 parser := NewParser() 465 parser.ParseBacktick = true 466 args, err := parser.Parse("cmd -flag=`echo val1` -flag=val2") 467 if err != nil { 468 panic(err) 469 } 470 471 expected := []string{"cmd", "-flag=val1", "-flag=val2"} 472 if !reflect.DeepEqual(args, expected) { 473 t.Fatalf("Expected %#v, but %#v:", expected, args) 474 } 475 }