package tools import ( "strings" "testing" ) func TestACommandWithNoPlaceholdersAsksForNothing(t *testing.T) { // The common case, and the one that must stay instant: an ordinary tool // runs without a box opening in front of it. if got := (Tool{Command: "go test ./..."}).Placeholders(); got != nil { t.Errorf("Placeholders() = %v, want none", got) } } func TestAPlaceholderIsAskedForByItsLabel(t *testing.T) { got := Tool{Command: "go mod init {{module path}}"}.Placeholders() if len(got) != 1 { t.Fatalf("Placeholders() = %v, want one", got) } if got[0].Label != "module path" { t.Errorf("Label = %q, want %q", got[0].Label, "module path") } if got[0].Raw { t.Error("Raw = true; a plain placeholder is shell-quoted") } } func TestPlaceholdersComeBackInTheOrderTheyAppear(t *testing.T) { got := Tool{Command: "cp {{source}} {{destination}}"}.Placeholders() if len(got) != 2 || got[0].Label != "source" || got[1].Label != "destination" { t.Errorf("Placeholders() = %v, want source then destination", got) } } func TestALabelUsedTwiceIsAskedForOnce(t *testing.T) { // One field, and both occurrences get what is typed into it. got := Tool{Command: "mkdir {{name}} && cd {{name}}"}.Placeholders() if len(got) != 1 || got[0].Label != "name" { t.Fatalf("Placeholders() = %v, want just name", got) } if filled := (Tool{Command: "mkdir {{name}} && cd {{name}}"}).Fill(map[string]string{"name": "src"}); filled != "mkdir 'src' && cd 'src'" { t.Errorf("Fill() = %q", filled) } } func TestATrailingEllipsisAsksForTheValueVerbatim(t *testing.T) { got := Tool{Command: "go test {{extra flags...}} ./..."}.Placeholders() if len(got) != 1 { t.Fatalf("Placeholders() = %v, want one", got) } if got[0].Label != "extra flags" { t.Errorf("Label = %q, want the ... stripped off", got[0].Label) } if !got[0].Raw { t.Error("Raw = false; a trailing ... asks for the value verbatim") } } func TestTheEllipsisDoesNotSwallowTheRestOfTheCommand(t *testing.T) { // `./...` is in nearly every Go tools file, and it must not be read as a // placeholder's raw marker leaking past the closing braces. filled := Tool{Command: "go test {{extra flags...}} ./..."}.Fill(map[string]string{"extra flags": "-run TestX -v"}) if want := "go test -run TestX -v ./..."; filled != want { t.Errorf("Fill() = %q, want %q", filled, want) } } func TestSingleBracesAreNotPlaceholders(t *testing.T) { // Both of these are ordinary things to put in a tools file, and reading // either as a placeholder would turn a working command into a dialog // asking for "print $1". tests := map[string]string{ "awk": `git log --oneline | awk '{print $1}'`, "find": `find . -name '*.tmp' -exec rm {} +`, } for name, command := range tests { t.Run(name, func(t *testing.T) { if got := (Tool{Command: command}).Placeholders(); got != nil { t.Errorf("Placeholders() = %v, want none", got) } if filled := (Tool{Command: command}).Fill(nil); filled != command { t.Errorf("Fill() changed the command:\n got %q\nwant %q", filled, command) } }) } } func TestAValueIsShellQuotedSoASpaceStaysOneArgument(t *testing.T) { filled := Tool{Command: "go build -o {{output}}"}.Fill(map[string]string{"output": "my binary"}) if want := "go build -o 'my binary'"; filled != want { t.Errorf("Fill() = %q, want %q", filled, want) } } func TestAQuoteInsideAValueDoesNotEscapeTheQuoting(t *testing.T) { // The one case single-quoting has to handle, and the one that would let a // typed value run as a command if it were got wrong. filled := Tool{Command: "echo {{message}}"}.Fill(map[string]string{"message": "it's here"}) if want := `echo 'it'\''s here'`; filled != want { t.Errorf("Fill() = %q, want %q", filled, want) } } func TestShellQuoteSurvivesWhatAShellWouldOtherwiseRead(t *testing.T) { for _, value := range []string{"a b", "$HOME", "`whoami`", "a; rm -rf /", "*", "it's", ""} { quoted := ShellQuote(value) if !strings.HasPrefix(quoted, "'") || !strings.HasSuffix(quoted, "'") { t.Errorf("ShellQuote(%q) = %q, want it wrapped in single quotes", value, quoted) } } } func TestARawPlaceholderIsNotQuoted(t *testing.T) { filled := Tool{Command: "cargo test {{args...}}"}.Fill(map[string]string{"args": "--release --all-features"}) if want := "cargo test --release --all-features"; filled != want { t.Errorf("Fill() = %q, want %q", filled, want) } } func TestALabelWithNoValueBecomesNothing(t *testing.T) { // The command then reports its own complaint, which says more than the // editor guessing what an empty field was supposed to mean. filled := Tool{Command: "go mod init {{module}}"}.Fill(map[string]string{}) if want := "go mod init ''"; filled != want { t.Errorf("Fill() = %q, want %q", filled, want) } } func TestFillLeavesACommandWithNoPlaceholdersAlone(t *testing.T) { const command = "go test ./..." if filled := (Tool{Command: command}).Fill(map[string]string{"unused": "x"}); filled != command { t.Errorf("Fill() = %q, want it unchanged", filled) } } func TestALabelUsedBothWaysIsSubstitutedPerOccurrence(t *testing.T) { // Asked for once; each occurrence then honours its own braces. filled := Tool{Command: "echo {{x}} && echo {{x...}}"}.Fill(map[string]string{"x": "a b"}) if want := "echo 'a b' && echo a b"; filled != want { t.Errorf("Fill() = %q, want %q", filled, want) } } func TestAHalfTypedPlaceholderIsRefusedRatherThanRun(t *testing.T) { // Left alone it would reach the shell with its braces still in it, which // fails in a way that names neither the tool nor the file. dir := project(t, "[[tool]]\nname = \"Init\"\ncommand = \"go mod init {{module\"\n") _, err := Load(testProfile(), dir) if err == nil { t.Fatal("Load() accepted a placeholder that is never closed") } for _, want := range []string{"Init", "never closed"} { if !strings.Contains(err.Error(), want) { t.Errorf("the error never mentions %q: %v", want, err) } } } func TestAPlaceholderWithNoLabelIsRefused(t *testing.T) { // It would open a box asking for nothing, under a blank heading. for _, command := range []string{"go mod init {{}}", "go mod init {{ }}", "go test {{...}}"} { t.Run(command, func(t *testing.T) { dir := project(t, "[[tool]]\nname = \"X\"\ncommand = \""+command+"\"\n") if _, err := Load(testProfile(), dir); err == nil { t.Errorf("Load() accepted %q", command) } }) } } func TestAWellFormedPlaceholderLoadsFine(t *testing.T) { dir := project(t, "[[tool]]\nname = \"Init\"\ncommand = \"go mod init {{module path}}\"\n") list, err := Load(testProfile(), dir) if err != nil { t.Fatalf("Load() error = %v", err) } got := list.Tools()[0].Placeholders() if len(got) != 1 || got[0].Label != "module path" { t.Errorf("Placeholders() = %v, want one called %q", got, "module path") } }