|
8 | 8 | "os/exec" |
9 | 9 | "path/filepath" |
10 | 10 | "reflect" |
| 11 | + "runtime" |
11 | 12 | "strings" |
12 | 13 | "testing" |
13 | 14 | "time" |
@@ -197,30 +198,69 @@ func TestCommandContext(t *testing.T) { |
197 | 198 | } |
198 | 199 | } |
199 | 200 |
|
200 | | -func TestNaiveSelf(t *testing.T) { |
201 | | - if os.Getenv("TEST_CHECK") == "1" { |
202 | | - os.Exit(2) |
203 | | - } |
204 | | - cmd := exec.Command(naiveSelf(), "-test.run=TestNaiveSelf") |
205 | | - cmd.Env = append(cmd.Environ(), "TEST_CHECK=1") |
206 | | - err := cmd.Start() |
| 201 | +// TestRunNaiveSelf verifies that reexec.Self() (and thus CommandContext) |
| 202 | +// can resolve a path that can be used to re-execute the current test binary |
| 203 | +// when it falls back to the argv[0]-based implementation. |
| 204 | +// |
| 205 | +// It invokes the binary via naiveSelf (intentionally bypassing the Linux |
| 206 | +// /proc/self/exe fast-path) so the fallback logic is exercised consistently |
| 207 | +// across platforms. |
| 208 | +func TestRunNaiveSelf(t *testing.T) { |
| 209 | + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) |
| 210 | + defer cancel() |
| 211 | + |
| 212 | + // Similar to [rexec.CommandContext], but using naiveSelf to skip the |
| 213 | + // optimized "/proc/self/exe" on Linux. |
| 214 | + cmd := exec.CommandContext(ctx, naiveSelf(os.Args[0]), testReExec2) |
| 215 | + cmd.Args = cmd.Args[1:] |
| 216 | + |
| 217 | + out, err := cmd.CombinedOutput() |
207 | 218 | if err != nil { |
208 | 219 | t.Fatalf("Unable to start command: %v", err) |
209 | 220 | } |
210 | | - err = cmd.Wait() |
211 | | - |
212 | | - var expError *exec.ExitError |
213 | | - if !errors.As(err, &expError) { |
214 | | - t.Fatalf("got %T, want %T", err, expError) |
215 | | - } |
216 | 221 |
|
217 | | - const expected = "exit status 2" |
218 | | - if err.Error() != expected { |
219 | | - t.Fatalf("got %v, want %v", err, expected) |
| 222 | + expOut := "Hello test-reexec2" |
| 223 | + actual := strings.TrimSpace(string(out)) |
| 224 | + if actual != expOut { |
| 225 | + t.Errorf("got %v, want %v", actual, expOut) |
220 | 226 | } |
| 227 | +} |
221 | 228 |
|
222 | | - os.Args[0] = "mkdir" |
223 | | - if naiveSelf() == os.Args[0] { |
224 | | - t.Fatalf("Expected naiveSelf to resolve the location of mkdir") |
225 | | - } |
| 229 | +func TestNaiveSelfResolve(t *testing.T) { |
| 230 | + t.Run("resolve in PATH", func(t *testing.T) { |
| 231 | + executable := "sh" |
| 232 | + if runtime.GOOS == "windows" { |
| 233 | + executable = "cmd" |
| 234 | + } |
| 235 | + resolved := naiveSelf(executable) |
| 236 | + if resolved == executable { |
| 237 | + t.Errorf("did not resolve via PATH; got %q", resolved) |
| 238 | + } |
| 239 | + if !filepath.IsAbs(resolved) { |
| 240 | + t.Errorf("expected absolute path; got %q", resolved) |
| 241 | + } |
| 242 | + }) |
| 243 | + t.Run("not in PATH", func(t *testing.T) { |
| 244 | + const executable = "some-nonexistent-executable" |
| 245 | + resolved := naiveSelf(executable) |
| 246 | + want, _ := filepath.Abs(executable) |
| 247 | + if resolved != want { |
| 248 | + t.Errorf("expected absolute path; got %q, want %q", resolved, want) |
| 249 | + } |
| 250 | + }) |
| 251 | + t.Run("relative path", func(t *testing.T) { |
| 252 | + executable := filepath.Join(".", "some-executable") |
| 253 | + resolved := naiveSelf(executable) |
| 254 | + want, _ := filepath.Abs(executable) |
| 255 | + if resolved != want { |
| 256 | + t.Errorf("expected absolute path; got %q, want %q", resolved, want) |
| 257 | + } |
| 258 | + }) |
| 259 | + t.Run("absolute path unchanged", func(t *testing.T) { |
| 260 | + executable := filepath.Join(os.TempDir(), "some-executable") |
| 261 | + resolved := naiveSelf(executable) |
| 262 | + if resolved != executable { |
| 263 | + t.Errorf("should not modify absolute paths; got %q, want %q", resolved, executable) |
| 264 | + } |
| 265 | + }) |
226 | 266 | } |
0 commit comments