package terminal import ( "testing" "unicode/utf16" ) // These run on every platform: they check the shapes Windows wants things in, // not Windows itself. The API calls that consume them are compiled with // GOOS=windows and have never been run by this project's authors — see // README.md. func TestWindowsShellIsComspecOrCmd(t *testing.T) { if got := windowsShell(`C:\Windows\system32\cmd.exe`); got != `C:\Windows\system32\cmd.exe` { t.Errorf("windowsShell(COMSPEC) = %q, want COMSPEC itself", got) } if got := windowsShell(""); got != "cmd.exe" { t.Errorf("windowsShell(\"\") = %q, want the fallback cmd.exe", got) } } func TestTheEnvironmentBlockIsNulSeparatedAndDoubleNulEnded(t *testing.T) { block := environmentBlock([]string{"A=1", "TERM=xterm-256color"}) want := append(utf16.Encode([]rune("A=1")), 0) want = append(want, utf16.Encode([]rune("TERM=xterm-256color"))...) want = append(want, 0, 0) if len(block) != len(want) { t.Fatalf("block has %d code units, want %d", len(block), len(want)) } for i := range want { if block[i] != want[i] { t.Fatalf("block differs at %d: %v, want %v", i, block, want) } } } func TestTheEnvironmentBlockKeepsNonASCII(t *testing.T) { // UTF-16, not bytes: a variable holding an accent must survive. block := environmentBlock([]string{"NAME=élan"}) if got := string(utf16.Decode(block[:len(block)-2])); got != "NAME=élan" { t.Errorf("the block decodes to %q, want NAME=élan", got) } } func TestAnEmptyEnvironmentIsTwoNuls(t *testing.T) { // A single NUL would be a block whose first entry is empty and which then // runs off the end; nil would mean "inherit", which the caller never // wants because TERM is set. Two NULs is the documented empty block. if got := environmentBlock(nil); len(got) != 2 || got[0] != 0 || got[1] != 0 { t.Errorf("environmentBlock(nil) = %v, want two NULs", got) } } func TestCmdExeGetsItsCommandVerbatimInsideOnePairOfQuotes(t *testing.T) { // cmd.exe /S /C strips the first and last quote and parses the rest by // its own rules, so a quote inside the command must reach it unescaped. got := windowsCommandLine(`C:\Windows\system32\cmd.exe`, []string{"/S", "/C", `echo "hello world" && node --test`}) want := `"C:\Windows\system32\cmd.exe" /S /C "echo "hello world" && node --test"` if got != want { t.Errorf("windowsCommandLine() = %s, want %s", got, want) } } func TestCmdExeIsRecognisedWhateverItsCaseOrPath(t *testing.T) { for _, program := range []string{"cmd.exe", "CMD.EXE", `C:\WINDOWS\System32\Cmd.exe`, "cmd"} { if !isCmdExe(program) { t.Errorf("isCmdExe(%q) = false, want true", program) } } for _, program := range []string{"pwsh.exe", `C:\Program Files\PowerShell\7\pwsh.exe`, "bash"} { if isCmdExe(program) { t.Errorf("isCmdExe(%q) = true, want false", program) } } } func TestAnInteractiveCmdExeHasNoSwitchesToSpecialCase(t *testing.T) { // No arguments: the ordinary composition, which is just the program. if got := windowsCommandLine("cmd.exe", nil); got != "cmd.exe" { t.Errorf("windowsCommandLine(cmd.exe) = %q, want cmd.exe", got) } } func TestOtherProgramsGetCRuntimeQuoting(t *testing.T) { // The rule syscall.EscapeArg follows: quotes around an argument with a // space, \" for a quote, backslashes doubled only before a quote. tests := []struct { program string args []string want string }{ {"pwsh.exe", []string{"-NoLogo"}, `pwsh.exe -NoLogo`}, {`C:\Program Files\PowerShell\7\pwsh.exe`, []string{"-c", "echo hi"}, `"C:\Program Files\PowerShell\7\pwsh.exe" -c "echo hi"`}, {"node", []string{"-e", `console.log("x")`}, `node -e "console.log(\"x\")"`}, {"node", []string{`C:\path with space\`}, `node "C:\path with space\\"`}, {"node", []string{`a\"b`}, `node "a\\\"b"`}, {"node", []string{""}, `node ""`}, } for _, test := range tests { if got := windowsCommandLine(test.program, test.args); got != test.want { t.Errorf("windowsCommandLine(%q, %q) = %s, want %s", test.program, test.args, got, test.want) } } } func TestProgramNameDropsWindowsAndUnixPaths(t *testing.T) { for program, want := range map[string]string{ `C:\Windows\system32\cmd.exe`: "cmd.exe", "/bin/sh": "sh", "pwsh.exe": "pwsh.exe", } { if got := programName(program); got != want { t.Errorf("programName(%q) = %q, want %q", program, got, want) } } }