1- package reexec
1+ package reexec_test
22
33import (
44 "context"
55 "errors"
66 "fmt"
77 "os"
8- "os/exec"
98 "path/filepath"
109 "reflect"
1110 "runtime"
1211 "strings"
1312 "testing"
1413 "time"
14+
15+ "github.com/moby/sys/reexec"
16+ "github.com/moby/sys/reexec/internal/reexecoverride"
1517)
1618
1719const (
@@ -21,23 +23,23 @@ const (
2123)
2224
2325func init () {
24- Register (testReExec , func () {
26+ reexec . Register (testReExec , func () {
2527 panic ("Return Error" )
2628 })
27- Register (testReExec2 , func () {
29+ reexec . Register (testReExec2 , func () {
2830 var args string
2931 if len (os .Args ) > 1 {
3032 args = fmt .Sprintf ("(args: %#v)" , os .Args [1 :])
3133 }
3234 fmt .Println ("Hello" , testReExec2 , args )
3335 os .Exit (0 )
3436 })
35- Register (testReExec3 , func () {
37+ reexec . Register (testReExec3 , func () {
3638 fmt .Println ("Hello " + testReExec3 )
3739 time .Sleep (1 * time .Second )
3840 os .Exit (0 )
3941 })
40- if Init () {
42+ if reexec . Init () {
4143 // Make sure we exit in case re-exec didn't os.Exit on its own.
4244 os .Exit (0 )
4345 }
@@ -73,7 +75,7 @@ func TestRegister(t *testing.T) {
7375 t .Errorf ("got %q, want %q" , r , tc .expectedErr )
7476 }
7577 }()
76- Register (tc .name , func () {})
78+ reexec . Register (tc .name , func () {})
7779 })
7880 }
7981}
@@ -102,7 +104,7 @@ func TestCommand(t *testing.T) {
102104 }
103105 for _ , tc := range tests {
104106 t .Run (tc .doc , func (t * testing.T ) {
105- cmd := Command (tc .cmdAndArgs ... )
107+ cmd := reexec . Command (tc .cmdAndArgs ... )
106108 if ! reflect .DeepEqual (cmd .Args , tc .cmdAndArgs ) {
107109 t .Fatalf ("got %+v, want %+v" , cmd .Args , tc .cmdAndArgs )
108110 }
@@ -169,7 +171,7 @@ func TestCommandContext(t *testing.T) {
169171 ctx , cancel := context .WithTimeout (context .Background (), 100 * time .Millisecond )
170172 defer cancel ()
171173
172- cmd := CommandContext (ctx , tc .cmdAndArgs ... )
174+ cmd := reexec . CommandContext (ctx , tc .cmdAndArgs ... )
173175 if ! reflect .DeepEqual (cmd .Args , tc .cmdAndArgs ) {
174176 t .Fatalf ("got %+v, want %+v" , cmd .Args , tc .cmdAndArgs )
175177 }
@@ -202,18 +204,17 @@ func TestCommandContext(t *testing.T) {
202204// can resolve a path that can be used to re-execute the current test binary
203205// when it falls back to the argv[0]-based implementation.
204206//
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.
207+ // It forces Self() to bypass the Linux /proc/self/exe fast-path via
208+ // [reexecoverride.OverrideArgv0] so that the fallback logic is exercised
209+ // consistently across platforms.
208210func TestRunNaiveSelf (t * testing.T ) {
209211 ctx , cancel := context .WithTimeout (context .Background (), 100 * time .Millisecond )
210212 defer cancel ()
211213
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 :]
214+ // Force Self() to use naiveSelf(os.Args[0]), instead of "/proc/self/exe" on Linux.
215+ reexecoverride .OverrideArgv0 (t , os .Args [0 ])
216216
217+ cmd := reexec .CommandContext (ctx , testReExec2 )
217218 out , err := cmd .CombinedOutput ()
218219 if err != nil {
219220 t .Fatalf ("Unable to start command: %v" , err )
@@ -227,12 +228,23 @@ func TestRunNaiveSelf(t *testing.T) {
227228}
228229
229230func TestNaiveSelfResolve (t * testing.T ) {
231+ t .Run ("fast path on Linux" , func (t * testing.T ) {
232+ if runtime .GOOS != "linux" {
233+ t .Skip ("only supported on Linux" )
234+ }
235+ resolved := reexec .Self ()
236+ expected := "/proc/self/exe"
237+ if resolved != expected {
238+ t .Errorf ("got %v, want %v" , resolved , expected )
239+ }
240+ })
230241 t .Run ("resolve in PATH" , func (t * testing.T ) {
231242 executable := "sh"
232243 if runtime .GOOS == "windows" {
233244 executable = "cmd"
234245 }
235- resolved := naiveSelf (executable )
246+ reexecoverride .OverrideArgv0 (t , executable )
247+ resolved := reexec .Self ()
236248 if resolved == executable {
237249 t .Errorf ("did not resolve via PATH; got %q" , resolved )
238250 }
@@ -242,23 +254,26 @@ func TestNaiveSelfResolve(t *testing.T) {
242254 })
243255 t .Run ("not in PATH" , func (t * testing.T ) {
244256 const executable = "some-nonexistent-executable"
245- resolved := naiveSelf (executable )
257+ reexecoverride .OverrideArgv0 (t , executable )
258+ resolved := reexec .Self ()
246259 want , _ := filepath .Abs (executable )
247260 if resolved != want {
248261 t .Errorf ("expected absolute path; got %q, want %q" , resolved , want )
249262 }
250263 })
251264 t .Run ("relative path" , func (t * testing.T ) {
252265 executable := filepath .Join ("." , "some-executable" )
253- resolved := naiveSelf (executable )
266+ reexecoverride .OverrideArgv0 (t , executable )
267+ resolved := reexec .Self ()
254268 want , _ := filepath .Abs (executable )
255269 if resolved != want {
256270 t .Errorf ("expected absolute path; got %q, want %q" , resolved , want )
257271 }
258272 })
259273 t .Run ("absolute path unchanged" , func (t * testing.T ) {
260274 executable := filepath .Join (os .TempDir (), "some-executable" )
261- resolved := naiveSelf (executable )
275+ reexecoverride .OverrideArgv0 (t , executable )
276+ resolved := reexec .Self ()
262277 if resolved != executable {
263278 t .Errorf ("should not modify absolute paths; got %q, want %q" , resolved , executable )
264279 }
0 commit comments