github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/os/executable_test.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package os_test 6 7 import ( 8 "fmt" 9 "internal/testenv" 10 "os" 11 osexec "os/exec" 12 "path/filepath" 13 "runtime" 14 "testing" 15 ) 16 17 const executable_EnvVar = "OSTEST_OUTPUT_EXECPATH" 18 19 func TestExecutable(t *testing.T) { 20 testenv.MustHaveExec(t) 21 ep, err := os.Executable() 22 if err != nil { 23 t.Fatalf("Executable failed: %v", err) 24 } 25 // we want fn to be of the form "dir/prog" 26 dir := filepath.Dir(filepath.Dir(ep)) 27 fn, err := filepath.Rel(dir, ep) 28 if err != nil { 29 t.Fatalf("filepath.Rel: %v", err) 30 } 31 32 cmd := &osexec.Cmd{} 33 // make child start with a relative program path 34 cmd.Dir = dir 35 cmd.Path = fn 36 // forge argv[0] for child, so that we can verify we could correctly 37 // get real path of the executable without influenced by argv[0]. 38 cmd.Args = []string{"-", "-test.run=XXXX"} 39 if runtime.GOOS == "openbsd" || runtime.GOOS == "aix" { 40 // OpenBSD and AIX rely on argv[0] 41 cmd.Args[0] = fn 42 } 43 cmd.Env = append(os.Environ(), fmt.Sprintf("%s=1", executable_EnvVar)) 44 out, err := cmd.CombinedOutput() 45 if err != nil { 46 t.Fatalf("exec(self) failed: %v", err) 47 } 48 outs := string(out) 49 if !filepath.IsAbs(outs) { 50 t.Fatalf("Child returned %q, want an absolute path", out) 51 } 52 if !sameFile(outs, ep) { 53 t.Fatalf("Child returned %q, not the same file as %q", out, ep) 54 } 55 } 56 57 func sameFile(fn1, fn2 string) bool { 58 fi1, err := os.Stat(fn1) 59 if err != nil { 60 return false 61 } 62 fi2, err := os.Stat(fn2) 63 if err != nil { 64 return false 65 } 66 return os.SameFile(fi1, fi2) 67 } 68 69 func init() { 70 if e := os.Getenv(executable_EnvVar); e != "" { 71 // first chdir to another path 72 dir := "/" 73 if runtime.GOOS == "windows" { 74 cwd, err := os.Getwd() 75 if err != nil { 76 panic(err) 77 } 78 dir = filepath.VolumeName(cwd) 79 } 80 os.Chdir(dir) 81 if ep, err := os.Executable(); err != nil { 82 fmt.Fprint(os.Stderr, "ERROR: ", err) 83 } else { 84 fmt.Fprint(os.Stderr, ep) 85 } 86 os.Exit(0) 87 } 88 } 89 90 func TestExecutableDeleted(t *testing.T) { 91 testenv.MustHaveExec(t) 92 switch runtime.GOOS { 93 case "windows", "plan9": 94 t.Skipf("%v does not support deleting running binary", runtime.GOOS) 95 case "openbsd", "freebsd", "aix": 96 t.Skipf("%v does not support reading deleted binary name", runtime.GOOS) 97 } 98 99 dir := t.TempDir() 100 101 src := filepath.Join(dir, "testdel.go") 102 exe := filepath.Join(dir, "testdel.exe") 103 104 err := os.WriteFile(src, []byte(testExecutableDeletion), 0666) 105 if err != nil { 106 t.Fatal(err) 107 } 108 109 out, err := osexec.Command(testenv.GoToolPath(t), "build", "-o", exe, src).CombinedOutput() 110 t.Logf("build output:\n%s", out) 111 if err != nil { 112 t.Fatal(err) 113 } 114 115 out, err = osexec.Command(exe).CombinedOutput() 116 t.Logf("exec output:\n%s", out) 117 if err != nil { 118 t.Fatal(err) 119 } 120 } 121 122 const testExecutableDeletion = `package main 123 124 import ( 125 "fmt" 126 "os" 127 ) 128 129 func main() { 130 before, err := os.Executable() 131 if err != nil { 132 fmt.Fprintf(os.Stderr, "failed to read executable name before deletion: %v\n", err) 133 os.Exit(1) 134 } 135 136 err = os.Remove(before) 137 if err != nil { 138 fmt.Fprintf(os.Stderr, "failed to remove executable: %v\n", err) 139 os.Exit(1) 140 } 141 142 after, err := os.Executable() 143 if err != nil { 144 fmt.Fprintf(os.Stderr, "failed to read executable name after deletion: %v\n", err) 145 os.Exit(1) 146 } 147 148 if before != after { 149 fmt.Fprintf(os.Stderr, "before and after do not match: %v != %v\n", before, after) 150 os.Exit(1) 151 } 152 } 153 `