summaryrefslogtreecommitdiffstats
path: root/src/os/executable_test.go
blob: 98b72d7d5e42d032b799618c797276aec53f1011 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package os_test

import (
	"fmt"
	"internal/testenv"
	"os"
	"path/filepath"
	"runtime"
	"testing"
)

const executable_EnvVar = "OSTEST_OUTPUT_EXECPATH"

func TestExecutable(t *testing.T) {
	testenv.MustHaveExec(t)
	t.Parallel()

	ep, err := os.Executable()
	if err != nil {
		t.Fatalf("Executable failed: %v", err)
	}
	// we want fn to be of the form "dir/prog"
	dir := filepath.Dir(filepath.Dir(ep))
	fn, err := filepath.Rel(dir, ep)
	if err != nil {
		t.Fatalf("filepath.Rel: %v", err)
	}

	cmd := testenv.Command(t, fn, "-test.run=^$")
	// make child start with a relative program path
	cmd.Dir = dir
	cmd.Path = fn
	if runtime.GOOS == "openbsd" || runtime.GOOS == "aix" {
		// OpenBSD and AIX rely on argv[0]
	} else {
		// forge argv[0] for child, so that we can verify we could correctly
		// get real path of the executable without influenced by argv[0].
		cmd.Args[0] = "-"
	}
	cmd.Env = append(cmd.Environ(), fmt.Sprintf("%s=1", executable_EnvVar))
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("exec(self) failed: %v", err)
	}
	outs := string(out)
	if !filepath.IsAbs(outs) {
		t.Fatalf("Child returned %q, want an absolute path", out)
	}
	if !sameFile(outs, ep) {
		t.Fatalf("Child returned %q, not the same file as %q", out, ep)
	}
}

func sameFile(fn1, fn2 string) bool {
	fi1, err := os.Stat(fn1)
	if err != nil {
		return false
	}
	fi2, err := os.Stat(fn2)
	if err != nil {
		return false
	}
	return os.SameFile(fi1, fi2)
}

func init() {
	if e := os.Getenv(executable_EnvVar); e != "" {
		// first chdir to another path
		dir := "/"
		if runtime.GOOS == "windows" {
			cwd, err := os.Getwd()
			if err != nil {
				panic(err)
			}
			dir = filepath.VolumeName(cwd)
		}
		os.Chdir(dir)
		if ep, err := os.Executable(); err != nil {
			fmt.Fprint(os.Stderr, "ERROR: ", err)
		} else {
			fmt.Fprint(os.Stderr, ep)
		}
		os.Exit(0)
	}
}

func TestExecutableDeleted(t *testing.T) {
	testenv.MustHaveGoBuild(t)
	switch runtime.GOOS {
	case "windows", "plan9":
		t.Skipf("%v does not support deleting running binary", runtime.GOOS)
	case "openbsd", "freebsd", "aix":
		t.Skipf("%v does not support reading deleted binary name", runtime.GOOS)
	}
	t.Parallel()

	dir := t.TempDir()

	src := filepath.Join(dir, "testdel.go")
	exe := filepath.Join(dir, "testdel.exe")

	err := os.WriteFile(src, []byte(testExecutableDeletion), 0666)
	if err != nil {
		t.Fatal(err)
	}

	out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src).CombinedOutput()
	t.Logf("build output:\n%s", out)
	if err != nil {
		t.Fatal(err)
	}

	out, err = testenv.Command(t, exe).CombinedOutput()
	t.Logf("exec output:\n%s", out)
	if err != nil {
		t.Fatal(err)
	}
}

const testExecutableDeletion = `package main

import (
	"fmt"
	"os"
)

func main() {
	before, err := os.Executable()
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to read executable name before deletion: %v\n", err)
		os.Exit(1)
	}

	err = os.Remove(before)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to remove executable: %v\n", err)
		os.Exit(1)
	}

	after, err := os.Executable()
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to read executable name after deletion: %v\n", err)
		os.Exit(1)
	}

	if before != after {
		fmt.Fprintf(os.Stderr, "before and after do not match: %v != %v\n", before, after)
		os.Exit(1)
	}
}
`