1package shell
  2
  3import (
  4	"context"
  5	"runtime"
  6	"strings"
  7	"testing"
  8	"time"
  9)
 10
 11// Benchmark to measure CPU efficiency
 12func BenchmarkShellQuickCommands(b *testing.B) {
 13	shell := newPersistentShell(b.TempDir())
 14
 15	b.ReportAllocs()
 16
 17	for b.Loop() {
 18		_, _, err := shell.Exec(context.Background(), "echo test")
 19		exitCode := ExitCode(err)
 20		if err != nil || exitCode != 0 {
 21			b.Fatalf("Command failed: %v, exit code: %d", err, exitCode)
 22		}
 23	}
 24}
 25
 26func TestTestTimeout(t *testing.T) {
 27	ctx, cancel := context.WithTimeout(t.Context(), time.Millisecond)
 28	t.Cleanup(cancel)
 29
 30	shell := newPersistentShell(t.TempDir())
 31	_, _, err := shell.Exec(ctx, "sleep 10")
 32	if status := ExitCode(err); status == 0 {
 33		t.Fatalf("Expected non-zero exit status, got %d", status)
 34	}
 35	if !IsInterrupt(err) {
 36		t.Fatalf("Expected command to be interrupted, but it was not")
 37	}
 38	if err == nil {
 39		t.Fatalf("Expected an error due to timeout, but got none")
 40	}
 41}
 42
 43func TestTestCancel(t *testing.T) {
 44	ctx, cancel := context.WithCancel(t.Context())
 45	cancel() // immediately cancel the context
 46
 47	shell := newPersistentShell(t.TempDir())
 48	_, _, err := shell.Exec(ctx, "sleep 10")
 49	if status := ExitCode(err); status == 0 {
 50		t.Fatalf("Expected non-zero exit status, got %d", status)
 51	}
 52	if !IsInterrupt(err) {
 53		t.Fatalf("Expected command to be interrupted, but it was not")
 54	}
 55	if err == nil {
 56		t.Fatalf("Expected an error due to cancel, but got none")
 57	}
 58}
 59
 60func TestRunCommandError(t *testing.T) {
 61	shell := newPersistentShell(t.TempDir())
 62	_, _, err := shell.Exec(t.Context(), "nopenopenope")
 63	if status := ExitCode(err); status == 0 {
 64		t.Fatalf("Expected non-zero exit status, got %d", status)
 65	}
 66	if IsInterrupt(err) {
 67		t.Fatalf("Expected command to not be interrupted, but it was")
 68	}
 69	if err == nil {
 70		t.Fatalf("Expected an error, got nil")
 71	}
 72}
 73
 74func TestRunContinuity(t *testing.T) {
 75	shell := newPersistentShell(t.TempDir())
 76	shell.Exec(t.Context(), "export FOO=bar")
 77	dst := t.TempDir()
 78	shell.Exec(t.Context(), "cd "+dst)
 79	out, _, _ := shell.Exec(t.Context(), "echo $FOO ; pwd")
 80	expect := "bar\n" + dst + "\n"
 81	if out != expect {
 82		t.Fatalf("Expected output %q, got %q", expect, out)
 83	}
 84}
 85
 86// New tests for Windows shell support
 87
 88func TestShellTypeDetection(t *testing.T) {
 89	shell := &PersistentShell{}
 90
 91	tests := []struct {
 92		command     string
 93		expected    ShellType
 94		windowsOnly bool
 95	}{
 96		// Windows-specific commands
 97		{"dir", ShellTypeCmd, true},
 98		{"type file.txt", ShellTypeCmd, true},
 99		{"copy file1.txt file2.txt", ShellTypeCmd, true},
100		{"del file.txt", ShellTypeCmd, true},
101		{"md newdir", ShellTypeCmd, true},
102		{"tasklist", ShellTypeCmd, true},
103
104		// PowerShell commands
105		{"Get-Process", ShellTypePowerShell, true},
106		{"Get-ChildItem", ShellTypePowerShell, true},
107		{"Set-Location C:\\", ShellTypePowerShell, true},
108		{"Get-Content file.txt | Where-Object {$_ -match 'pattern'}", ShellTypePowerShell, true},
109		{"$files = Get-ChildItem", ShellTypePowerShell, true},
110
111		// Unix/cross-platform commands
112		{"ls -la", ShellTypePOSIX, false},
113		{"cat file.txt", ShellTypePOSIX, false},
114		{"grep pattern file.txt", ShellTypePOSIX, false},
115		{"echo hello", ShellTypePOSIX, false},
116		{"git status", ShellTypePOSIX, false},
117		{"go build", ShellTypePOSIX, false},
118	}
119
120	for _, test := range tests {
121		t.Run(test.command, func(t *testing.T) {
122			result := shell.determineShellType(test.command)
123
124			if test.windowsOnly && runtime.GOOS != "windows" {
125				// On non-Windows systems, everything should use POSIX
126				if result != ShellTypePOSIX {
127					t.Errorf("On non-Windows, command %q should use POSIX shell, got %v", test.command, result)
128				}
129			} else if runtime.GOOS == "windows" {
130				// On Windows, check the expected shell type
131				if result != test.expected {
132					t.Errorf("Command %q should use %v shell, got %v", test.command, test.expected, result)
133				}
134			}
135		})
136	}
137}
138
139func TestWindowsCDHandling(t *testing.T) {
140	if runtime.GOOS != "windows" {
141		t.Skip("Windows CD handling test only runs on Windows")
142	}
143
144	shell := &PersistentShell{
145		cwd: "C:\\Users",
146		env: []string{},
147	}
148
149	tests := []struct {
150		command     string
151		expectedCwd string
152		shouldError bool
153	}{
154		{"cd ..", "C:\\", false},
155		{"cd Documents", "C:\\Users\\Documents", false},
156		{"cd C:\\Windows", "C:\\Windows", false},
157		{"cd", "", true}, // Missing argument
158	}
159
160	for _, test := range tests {
161		t.Run(test.command, func(t *testing.T) {
162			originalCwd := shell.cwd
163			stdout, stderr, err := shell.handleWindowsCD(test.command)
164
165			if test.shouldError {
166				if err == nil {
167					t.Errorf("Command %q should have failed", test.command)
168				}
169			} else {
170				if err != nil {
171					t.Errorf("Command %q failed: %v", test.command, err)
172				}
173				if shell.cwd != test.expectedCwd {
174					t.Errorf("Command %q: expected cwd %q, got %q", test.command, test.expectedCwd, shell.cwd)
175				}
176			}
177
178			// Reset for next test
179			shell.cwd = originalCwd
180			_ = stdout
181			_ = stderr
182		})
183	}
184}
185
186func TestCrossPlatformExecution(t *testing.T) {
187	shell := newPersistentShell(".")
188	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
189	defer cancel()
190
191	// Test a simple command that should work on all platforms
192	stdout, stderr, err := shell.Exec(ctx, "echo hello")
193	if err != nil {
194		t.Fatalf("Echo command failed: %v, stderr: %s", err, stderr)
195	}
196
197	if stdout == "" {
198		t.Error("Echo command produced no output")
199	}
200
201	// The output should contain "hello" regardless of platform
202	if !strings.Contains(strings.ToLower(stdout), "hello") {
203		t.Errorf("Echo output should contain 'hello', got: %q", stdout)
204	}
205}
206
207func TestWindowsNativeCommands(t *testing.T) {
208	if runtime.GOOS != "windows" {
209		t.Skip("Windows native command test only runs on Windows")
210	}
211
212	shell := newPersistentShell(".")
213	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
214	defer cancel()
215
216	// Test Windows dir command
217	stdout, stderr, err := shell.Exec(ctx, "dir")
218	if err != nil {
219		t.Fatalf("Dir command failed: %v, stderr: %s", err, stderr)
220	}
221
222	if stdout == "" {
223		t.Error("Dir command produced no output")
224	}
225}