| package shell |
| |
| import ( |
| "reflect" |
| "testing" |
| ) |
| |
| func TestQuote(t *testing.T) { |
| tests := []struct { |
| in, want string |
| }{ |
| {"", "''"}, // empty is special |
| {"abc", "abc"}, // nothing to quote |
| {"--flag", "--flag"}, // " |
| {"'abc", `\'abc`}, // single quote only |
| {"abc'", `abc\'`}, // " |
| {`shan't`, `shan\'t`}, // " |
| {"--flag=value", `'--flag=value'`}, |
| {"a b\tc", "'a b\tc'"}, |
| {`a"b"c`, `'a"b"c'`}, |
| {`'''`, `\'\'\'`}, |
| {`\`, `'\'`}, |
| {`'a=b`, `\''a=b'`}, // quotes and other stuff |
| {`a='b`, `'a='\''b'`}, // " |
| {`a=b'`, `'a=b'\'`}, // " |
| } |
| for _, test := range tests { |
| got := Quote(test.in) |
| if got != test.want { |
| t.Errorf("Quote %q: got %q, want %q", test.in, got, test.want) |
| } |
| } |
| } |
| |
| func TestSplit(t *testing.T) { |
| tests := []struct { |
| in string |
| want []string |
| ok bool |
| }{ |
| // Variations of empty input yield an empty split. |
| {"", nil, true}, |
| {" ", nil, true}, |
| {"\t", nil, true}, |
| {"\n ", nil, true}, |
| |
| // Leading and trailing whitespace are discarded. |
| {"a", []string{"a"}, true}, |
| {" a", []string{"a"}, true}, |
| {"a\n", []string{"a"}, true}, |
| |
| // Escaped newlines are magic in the correct ways. |
| {"a\\\nb", []string{"ab"}, true}, |
| {"a \\\n b\tc", []string{"a", "b", "c"}, true}, |
| |
| // Various splits with and without quotes. Quoted whitespace is |
| // preserved. |
| {"a b c", []string{"a", "b", "c"}, true}, |
| {`a 'b c'`, []string{"a", "b c"}, true}, |
| {"\"a\nb\"cd e'f'", []string{"a\nbcd", "ef"}, true}, |
| {"'\n \t '", []string{"\n \t "}, true}, |
| |
| // Quoted empty strings are preserved in various places. |
| {"''", []string{""}, true}, |
| {"a ''", []string{"a", ""}, true}, |
| {" a \"\" b ", []string{"a", "", "b"}, true}, |
| |
| // Unbalanced quotation marks and escapes are detected. |
| {"\\", []string{""}, false}, |
| {"'", []string{""}, false}, |
| {`"`, []string{""}, false}, |
| {"a 'b c", []string{"a", "b c"}, false}, |
| {`a "b c`, []string{"a", "b c"}, false}, |
| {`a "b \"`, []string{"a", `b "`}, false}, |
| } |
| for _, test := range tests { |
| got, ok := Split(test.in) |
| if ok != test.ok { |
| t.Errorf("Split %#q: got valid=%v, want %v", test.in, ok, test.ok) |
| } |
| if !reflect.DeepEqual(got, test.want) { |
| t.Errorf("Split %#q: got %+q, want %+q", test.in, got, test.want) |
| } |
| } |
| } |
| |
| func TestRoundTrip(t *testing.T) { |
| tests := [][]string{ |
| nil, |
| {"a"}, |
| {"a "}, |
| {"a", "b", "c"}, |
| {"a", "b c"}, |
| {"--flag=value"}, |
| {"m='$USER'", "nop+", "$$"}, |
| {`"a" b `, "c"}, |
| {"a=b", "--foo", "${bar}", `\$`}, |
| {"cat", "a${b}.txt", "|", "tee", "capture", "2>", "/dev/null"}, |
| } |
| for _, test := range tests { |
| s := Join(test) |
| t.Logf("Join %#q = %v", test, s) |
| got, ok := Split(s) |
| if !ok { |
| t.Errorf("Split %+q: should be valid, but is not", s) |
| } |
| if !reflect.DeepEqual(got, test) { |
| t.Errorf("Split %+q: got %q, want %q", s, got, test) |
| } |
| } |
| } |