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

View as plain text