| 1 | //go:build windows |
| 2 | |
| 3 | package main |
| 4 | |
| 5 | import ( |
| 6 | "os" |
| 7 | "path/filepath" |
| 8 | "strings" |
| 9 | "testing" |
| 10 | ) |
| 11 | |
| 12 | func TestWindowsExternalOpenerIconUsesShellIcon(t *testing.T) { |
| 13 | explorer := filepath.Join(os.Getenv("WINDIR"), "explorer.exe") |
| 14 | if info, err := os.Stat(explorer); err != nil || info.IsDir() { |
| 15 | t.Skip("Windows Explorer executable is unavailable") |
| 16 | } |
| 17 | got := platformExternalOpenerIconDataURL(externalOpenerSpec{IconSource: explorer}) |
| 18 | if !strings.HasPrefix(got, "data:image/png;base64,") { |
| 19 | t.Fatalf("native Explorer icon = %q, want PNG data URL", got) |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | func TestWindowsAppPathExecutableEmptyName(t *testing.T) { |
| 24 | if got := windowsAppPathExecutable(""); got != "" { |
| 25 | t.Fatalf("empty name = %q, want empty", got) |
| 26 | } |
| 27 | if got := windowsAppPathExecutable(" "); got != "" { |
| 28 | t.Fatalf("blank name = %q, want empty", got) |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | func TestWindowsTerminalIconSourceSkipsZeroByteAliasForPowershell(t *testing.T) { |
| 33 | // When only a zero-byte execution alias is visible (package dir unreadable), |
| 34 | // IconSource must not stick on the alias — PowerShell is a known renderable |
| 35 | // PE that SHGetFileInfo can extract. |
| 36 | dir := t.TempDir() |
| 37 | alias := filepath.Join(dir, "wt.exe") |
| 38 | if err := os.WriteFile(alias, nil, 0o644); err != nil { |
| 39 | t.Fatalf("write zero-byte alias: %v", err) |
| 40 | } |
| 41 | // Point candidate resolution at our temp alias only by calling pick through |
| 42 | // the real resolver with overridden env via windowsTerminalIconSource on a |
| 43 | // path that will not find package globs, then assert via pick contract. |
| 44 | // Direct integration: windowsTerminalIconSource(alias) should return a |
| 45 | // non-zero file when powershell is on PATH / System32. |
| 46 | got := windowsTerminalIconSource(alias) |
| 47 | if got == "" { |
| 48 | t.Fatal("icon source empty") |
| 49 | } |
| 50 | if got == alias { |
| 51 | info, err := os.Stat(got) |
| 52 | if err != nil { |
| 53 | t.Fatalf("stat result: %v", err) |
| 54 | } |
| 55 | if info.Size() == 0 { |
| 56 | t.Fatalf("icon source stayed on zero-byte alias %q; want renderable fallback", got) |
| 57 | } |
| 58 | } |
| 59 | if info, err := os.Stat(got); err != nil || info.IsDir() || info.Size() == 0 { |
| 60 | t.Fatalf("icon source %q is not a non-zero file (err=%v)", got, err) |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | func TestWindowsConsoleLaunchDoesNotInvokeCmdStart(t *testing.T) { |
| 65 | // Integration-free: planWindowsConsoleLaunch + ShellExecute path must never |
| 66 | // build cmd /c start. The pure helpers test covers special-character dirs; |
| 67 | // here we only assert the platform launcher uses that plan shape. |
| 68 | ps := firstWindowsExecutable([]string{"powershell.exe"}, |
| 69 | joinWindowsInstallPath(os.Getenv("WINDIR"), "System32", "WindowsPowerShell", "v1.0", "powershell.exe")) |
| 70 | if ps == "" { |
| 71 | t.Skip("powershell.exe unavailable") |
| 72 | } |
| 73 | workdir := filepath.Join(t.TempDir(), "repo&calc") |
| 74 | if err := os.MkdirAll(workdir, 0o755); err != nil { |
| 75 | t.Fatalf("mkdir workdir: %v", err) |
| 76 | } |
| 77 | plan := planWindowsConsoleLaunch(ps, workdir) |
| 78 | if !windowsConsoleLaunchIsDirect(plan) { |
| 79 | t.Fatalf("console plan must be a direct ShellExecute open: %+v", plan) |
| 80 | } |
| 81 | if plan.File != ps { |
| 82 | t.Fatalf("File = %q, want powershell target %q", plan.File, ps) |
| 83 | } |
| 84 | if plan.Dir != workdir { |
| 85 | t.Fatalf("Dir = %q, want %q", plan.Dir, workdir) |
| 86 | } |
| 87 | // Do not call launchPlatformExternalOpener here: it would open a real |
| 88 | // interactive console window in CI. ShellExecute wiring is covered by |
| 89 | // openWorkspacePath and compile-time type checks. |
| 90 | } |
| 91 |