Source file src/os/executable_test.go

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

View as plain text