1package shell
2
3import (
4 "context"
5 "path/filepath"
6 "runtime"
7 "strings"
8 "testing"
9 "time"
10)
11
12// Benchmark to measure CPU efficiency
13func BenchmarkShellQuickCommands(b *testing.B) {
14 shell := NewShell(&Options{WorkingDir: b.TempDir()})
15
16 b.ReportAllocs()
17
18 for b.Loop() {
19 _, _, err := shell.Exec(context.Background(), "echo test")
20 exitCode := ExitCode(err)
21 if err != nil || exitCode != 0 {
22 b.Fatalf("Command failed: %v, exit code: %d", err, exitCode)
23 }
24 }
25}
26
27func TestTestTimeout(t *testing.T) {
28 // XXX(@andreynering): This fails on Windows. Address once possible.
29 if runtime.GOOS == "windows" {
30 t.Skip("Skipping test on Windows")
31 }
32
33 ctx, cancel := context.WithTimeout(t.Context(), time.Millisecond)
34 t.Cleanup(cancel)
35
36 shell := NewShell(&Options{WorkingDir: t.TempDir()})
37 _, _, err := shell.Exec(ctx, "sleep 10")
38 if status := ExitCode(err); status == 0 {
39 t.Fatalf("Expected non-zero exit status, got %d", status)
40 }
41 if !IsInterrupt(err) {
42 t.Fatalf("Expected command to be interrupted, but it was not")
43 }
44 if err == nil {
45 t.Fatalf("Expected an error due to timeout, but got none")
46 }
47}
48
49func TestTestCancel(t *testing.T) {
50 ctx, cancel := context.WithCancel(t.Context())
51 cancel() // immediately cancel the context
52
53 shell := NewShell(&Options{WorkingDir: t.TempDir()})
54 _, _, err := shell.Exec(ctx, "sleep 10")
55 if status := ExitCode(err); status == 0 {
56 t.Fatalf("Expected non-zero exit status, got %d", status)
57 }
58 if !IsInterrupt(err) {
59 t.Fatalf("Expected command to be interrupted, but it was not")
60 }
61 if err == nil {
62 t.Fatalf("Expected an error due to cancel, but got none")
63 }
64}
65
66func TestRunCommandError(t *testing.T) {
67 shell := NewShell(&Options{WorkingDir: t.TempDir()})
68 _, _, err := shell.Exec(t.Context(), "nopenopenope")
69 if status := ExitCode(err); status == 0 {
70 t.Fatalf("Expected non-zero exit status, got %d", status)
71 }
72 if IsInterrupt(err) {
73 t.Fatalf("Expected command to not be interrupted, but it was")
74 }
75 if err == nil {
76 t.Fatalf("Expected an error, got nil")
77 }
78}
79
80func TestRunContinuity(t *testing.T) {
81 tempDir1 := t.TempDir()
82 tempDir2 := t.TempDir()
83
84 shell := NewShell(&Options{WorkingDir: tempDir1})
85 if _, _, err := shell.Exec(t.Context(), "export FOO=bar"); err != nil {
86 t.Fatalf("failed to set env: %v", err)
87 }
88 if _, _, err := shell.Exec(t.Context(), "cd "+filepath.ToSlash(tempDir2)); err != nil {
89 t.Fatalf("failed to change directory: %v", err)
90 }
91 out, _, err := shell.Exec(t.Context(), "echo $FOO ; pwd")
92 if err != nil {
93 t.Fatalf("failed to echo: %v", err)
94 }
95 expect := "bar\n" + tempDir2 + "\n"
96 if out != expect {
97 t.Fatalf("expected output %q, got %q", expect, out)
98 }
99}
100
101func TestCrossPlatformExecution(t *testing.T) {
102 shell := NewShell(&Options{WorkingDir: "."})
103 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
104 defer cancel()
105
106 // Test a simple command that should work on all platforms
107 stdout, stderr, err := shell.Exec(ctx, "echo hello")
108 if err != nil {
109 t.Fatalf("Echo command failed: %v, stderr: %s", err, stderr)
110 }
111
112 if stdout == "" {
113 t.Error("Echo command produced no output")
114 }
115
116 // The output should contain "hello" regardless of platform
117 if !strings.Contains(strings.ToLower(stdout), "hello") {
118 t.Errorf("Echo output should contain 'hello', got: %q", stdout)
119 }
120}