| 🛟 Updated. 28d5985 k33g 21h ago | 1 | package terminal |
| 2 | |
| 3 | import ( |
| 4 | "testing" |
| 5 | "unicode/utf16" |
| 6 | ) |
| 7 | |
| 8 | // These run on every platform: they check the shapes Windows wants things in, |
| 9 | // not Windows itself. The API calls that consume them are compiled with |
| 10 | // GOOS=windows and have never been run by this project's authors — see |
| 11 | // README.md. |
| 12 | |
| 13 | func TestWindowsShellIsComspecOrCmd(t *testing.T) { |
| 14 | if got := windowsShell(`C:\Windows\system32\cmd.exe`); got != `C:\Windows\system32\cmd.exe` { |
| 15 | t.Errorf("windowsShell(COMSPEC) = %q, want COMSPEC itself", got) |
| 16 | } |
| 17 | if got := windowsShell(""); got != "cmd.exe" { |
| 18 | t.Errorf("windowsShell(\"\") = %q, want the fallback cmd.exe", got) |
| 19 | } |
| 20 | } |
| 21 | |
| 22 | func TestTheEnvironmentBlockIsNulSeparatedAndDoubleNulEnded(t *testing.T) { |
| 23 | block := environmentBlock([]string{"A=1", "TERM=xterm-256color"}) |
| 24 | |
| 25 | want := append(utf16.Encode([]rune("A=1")), 0) |
| 26 | want = append(want, utf16.Encode([]rune("TERM=xterm-256color"))...) |
| 27 | want = append(want, 0, 0) |
| 28 | |
| 29 | if len(block) != len(want) { |
| 30 | t.Fatalf("block has %d code units, want %d", len(block), len(want)) |
| 31 | } |
| 32 | for i := range want { |
| 33 | if block[i] != want[i] { |
| 34 | t.Fatalf("block differs at %d: %v, want %v", i, block, want) |
| 35 | } |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | func TestTheEnvironmentBlockKeepsNonASCII(t *testing.T) { |
| 40 | // UTF-16, not bytes: a variable holding an accent must survive. |
| 41 | block := environmentBlock([]string{"NAME=élan"}) |
| 42 | |
| 43 | if got := string(utf16.Decode(block[:len(block)-2])); got != "NAME=élan" { |
| 44 | t.Errorf("the block decodes to %q, want NAME=élan", got) |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | func TestAnEmptyEnvironmentIsTwoNuls(t *testing.T) { |
| 49 | // A single NUL would be a block whose first entry is empty and which then |
| 50 | // runs off the end; nil would mean "inherit", which the caller never |
| 51 | // wants because TERM is set. Two NULs is the documented empty block. |
| 52 | if got := environmentBlock(nil); len(got) != 2 || got[0] != 0 || got[1] != 0 { |
| 53 | t.Errorf("environmentBlock(nil) = %v, want two NULs", got) |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | func TestCmdExeGetsItsCommandVerbatimInsideOnePairOfQuotes(t *testing.T) { |
| 58 | // cmd.exe /S /C strips the first and last quote and parses the rest by |
| 59 | // its own rules, so a quote inside the command must reach it unescaped. |
| 60 | got := windowsCommandLine(`C:\Windows\system32\cmd.exe`, []string{"/S", "/C", `echo "hello world" && node --test`}) |
| 61 | |
| 62 | want := `"C:\Windows\system32\cmd.exe" /S /C "echo "hello world" && node --test"` |
| 63 | if got != want { |
| 64 | t.Errorf("windowsCommandLine() = %s, want %s", got, want) |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | func TestCmdExeIsRecognisedWhateverItsCaseOrPath(t *testing.T) { |
| 69 | for _, program := range []string{"cmd.exe", "CMD.EXE", `C:\WINDOWS\System32\Cmd.exe`, "cmd"} { |
| 70 | if !isCmdExe(program) { |
| 71 | t.Errorf("isCmdExe(%q) = false, want true", program) |
| 72 | } |
| 73 | } |
| 74 | for _, program := range []string{"pwsh.exe", `C:\Program Files\PowerShell\7\pwsh.exe`, "bash"} { |
| 75 | if isCmdExe(program) { |
| 76 | t.Errorf("isCmdExe(%q) = true, want false", program) |
| 77 | } |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | func TestAnInteractiveCmdExeHasNoSwitchesToSpecialCase(t *testing.T) { |
| 82 | // No arguments: the ordinary composition, which is just the program. |
| 83 | if got := windowsCommandLine("cmd.exe", nil); got != "cmd.exe" { |
| 84 | t.Errorf("windowsCommandLine(cmd.exe) = %q, want cmd.exe", got) |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | func TestOtherProgramsGetCRuntimeQuoting(t *testing.T) { |
| 89 | // The rule syscall.EscapeArg follows: quotes around an argument with a |
| 90 | // space, \" for a quote, backslashes doubled only before a quote. |
| 91 | tests := []struct { |
| 92 | program string |
| 93 | args []string |
| 94 | want string |
| 95 | }{ |
| 96 | {"pwsh.exe", []string{"-NoLogo"}, `pwsh.exe -NoLogo`}, |
| 97 | {`C:\Program Files\PowerShell\7\pwsh.exe`, []string{"-c", "echo hi"}, `"C:\Program Files\PowerShell\7\pwsh.exe" -c "echo hi"`}, |
| 98 | {"node", []string{"-e", `console.log("x")`}, `node -e "console.log(\"x\")"`}, |
| 99 | {"node", []string{`C:\path with space\`}, `node "C:\path with space\\"`}, |
| 100 | {"node", []string{`a\"b`}, `node "a\\\"b"`}, |
| 101 | {"node", []string{""}, `node ""`}, |
| 102 | } |
| 103 | |
| 104 | for _, test := range tests { |
| 105 | if got := windowsCommandLine(test.program, test.args); got != test.want { |
| 106 | t.Errorf("windowsCommandLine(%q, %q) = %s, want %s", test.program, test.args, got, test.want) |
| 107 | } |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | func TestProgramNameDropsWindowsAndUnixPaths(t *testing.T) { |
| 112 | for program, want := range map[string]string{ |
| 113 | `C:\Windows\system32\cmd.exe`: "cmd.exe", |
| 114 | "/bin/sh": "sh", |
| 115 | "pwsh.exe": "pwsh.exe", |
| 116 | } { |
| 117 | if got := programName(program); got != want { |
| 118 | t.Errorf("programName(%q) = %q, want %q", program, got, want) |
| 119 | } |
| 120 | } |
| 121 | } |