package shell import ( "context" "runtime" "strings" "testing" "time" ) // Benchmark to measure CPU efficiency func BenchmarkShellQuickCommands(b *testing.B) { shell := NewShell(&Options{WorkingDir: b.TempDir()}) b.ReportAllocs() for b.Loop() { _, _, err := shell.Exec(context.Background(), "echo test") exitCode := ExitCode(err) if err != nil || exitCode != 0 { b.Fatalf("Command failed: %v, exit code: %d", err, exitCode) } } } func TestTestTimeout(t *testing.T) { ctx, cancel := context.WithTimeout(t.Context(), time.Millisecond) t.Cleanup(cancel) shell := NewShell(&Options{WorkingDir: t.TempDir()}) _, _, err := shell.Exec(ctx, "sleep 10") if status := ExitCode(err); status == 0 { t.Fatalf("Expected non-zero exit status, got %d", status) } if !IsInterrupt(err) { t.Fatalf("Expected command to be interrupted, but it was not") } if err == nil { t.Fatalf("Expected an error due to timeout, but got none") } } func TestTestCancel(t *testing.T) { ctx, cancel := context.WithCancel(t.Context()) cancel() // immediately cancel the context shell := NewShell(&Options{WorkingDir: t.TempDir()}) _, _, err := shell.Exec(ctx, "sleep 10") if status := ExitCode(err); status == 0 { t.Fatalf("Expected non-zero exit status, got %d", status) } if !IsInterrupt(err) { t.Fatalf("Expected command to be interrupted, but it was not") } if err == nil { t.Fatalf("Expected an error due to cancel, but got none") } } func TestRunCommandError(t *testing.T) { shell := NewShell(&Options{WorkingDir: t.TempDir()}) _, _, err := shell.Exec(t.Context(), "nopenopenope") if status := ExitCode(err); status == 0 { t.Fatalf("Expected non-zero exit status, got %d", status) } if IsInterrupt(err) { t.Fatalf("Expected command to not be interrupted, but it was") } if err == nil { t.Fatalf("Expected an error, got nil") } } func TestRunContinuity(t *testing.T) { shell := NewShell(&Options{WorkingDir: t.TempDir()}) shell.Exec(t.Context(), "export FOO=bar") dst := t.TempDir() shell.Exec(t.Context(), "cd "+dst) out, _, _ := shell.Exec(t.Context(), "echo $FOO ; pwd") expect := "bar\n" + dst + "\n" if out != expect { t.Fatalf("Expected output %q, got %q", expect, out) } } // New tests for Windows shell support func TestShellTypeDetection(t *testing.T) { shell := &PersistentShell{} tests := []struct { command string expected ShellType windowsOnly bool }{ // Windows-specific commands {"dir", ShellTypeCmd, true}, {"type file.txt", ShellTypeCmd, true}, {"copy file1.txt file2.txt", ShellTypeCmd, true}, {"del file.txt", ShellTypeCmd, true}, {"md newdir", ShellTypeCmd, true}, {"tasklist", ShellTypeCmd, true}, // PowerShell commands {"Get-Process", ShellTypePowerShell, true}, {"Get-ChildItem", ShellTypePowerShell, true}, {"Set-Location C:\\", ShellTypePowerShell, true}, {"Get-Content file.txt | Where-Object {$_ -match 'pattern'}", ShellTypePowerShell, true}, {"$files = Get-ChildItem", ShellTypePowerShell, true}, // Unix/cross-platform commands {"ls -la", ShellTypePOSIX, false}, {"cat file.txt", ShellTypePOSIX, false}, {"grep pattern file.txt", ShellTypePOSIX, false}, {"echo hello", ShellTypePOSIX, false}, {"git status", ShellTypePOSIX, false}, {"go build", ShellTypePOSIX, false}, } for _, test := range tests { t.Run(test.command, func(t *testing.T) { result := shell.determineShellType(test.command) if test.windowsOnly && runtime.GOOS != "windows" { // On non-Windows systems, everything should use POSIX if result != ShellTypePOSIX { t.Errorf("On non-Windows, command %q should use POSIX shell, got %v", test.command, result) } } else if runtime.GOOS == "windows" { // On Windows, check the expected shell type if result != test.expected { t.Errorf("Command %q should use %v shell, got %v", test.command, test.expected, result) } } }) } } func TestWindowsCDHandling(t *testing.T) { if runtime.GOOS != "windows" { t.Skip("Windows CD handling test only runs on Windows") } shell := NewShell(&Options{ WorkingDir: "C:\\Users", }) tests := []struct { command string expectedCwd string shouldError bool }{ {"cd ..", "C:\\", false}, {"cd Documents", "C:\\Users\\Documents", false}, {"cd C:\\Windows", "C:\\Windows", false}, {"cd", "", true}, // Missing argument } for _, test := range tests { t.Run(test.command, func(t *testing.T) { originalCwd := shell.GetWorkingDir() stdout, stderr, err := shell.handleWindowsCD(test.command) if test.shouldError { if err == nil { t.Errorf("Command %q should have failed", test.command) } } else { if err != nil { t.Errorf("Command %q failed: %v", test.command, err) } if shell.GetWorkingDir() != test.expectedCwd { t.Errorf("Command %q: expected cwd %q, got %q", test.command, test.expectedCwd, shell.GetWorkingDir()) } } // Reset for next test shell.SetWorkingDir(originalCwd) _ = stdout _ = stderr }) } } func TestCrossPlatformExecution(t *testing.T) { shell := NewShell(&Options{WorkingDir: "."}) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // Test a simple command that should work on all platforms stdout, stderr, err := shell.Exec(ctx, "echo hello") if err != nil { t.Fatalf("Echo command failed: %v, stderr: %s", err, stderr) } if stdout == "" { t.Error("Echo command produced no output") } // The output should contain "hello" regardless of platform if !strings.Contains(strings.ToLower(stdout), "hello") { t.Errorf("Echo output should contain 'hello', got: %q", stdout) } } func TestWindowsNativeCommands(t *testing.T) { if runtime.GOOS != "windows" { t.Skip("Windows native command test only runs on Windows") } shell := NewShell(&Options{WorkingDir: "."}) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // Test Windows dir command stdout, stderr, err := shell.Exec(ctx, "dir") if err != nil { t.Fatalf("Dir command failed: %v, stderr: %s", err, stderr) } if stdout == "" { t.Error("Dir command produced no output") } }