Source file src/internal/testenv/testenv_test.go

     1  // Copyright 2022 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 testenv_test
     6  
     7  import (
     8  	"internal/platform"
     9  	"internal/testenv"
    10  	"os"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  	"testing"
    15  )
    16  
    17  func TestGoToolLocation(t *testing.T) {
    18  	testenv.MustHaveGoBuild(t)
    19  
    20  	var exeSuffix string
    21  	if runtime.GOOS == "windows" {
    22  		exeSuffix = ".exe"
    23  	}
    24  
    25  	// Tests are defined to run within their package source directory,
    26  	// and this package's source directory is $GOROOT/src/internal/testenv.
    27  	// The 'go' command is installed at $GOROOT/bin/go, so if the environment
    28  	// is correct then testenv.GoTool() should be identical to ../../../bin/go.
    29  
    30  	relWant := "../../../bin/go" + exeSuffix
    31  	absWant, err := filepath.Abs(relWant)
    32  	if err != nil {
    33  		t.Fatal(err)
    34  	}
    35  
    36  	wantInfo, err := os.Stat(absWant)
    37  	if err != nil {
    38  		t.Fatal(err)
    39  	}
    40  	t.Logf("found go tool at %q (%q)", relWant, absWant)
    41  
    42  	goTool, err := testenv.GoTool()
    43  	if err != nil {
    44  		t.Fatalf("testenv.GoTool(): %v", err)
    45  	}
    46  	t.Logf("testenv.GoTool() = %q", goTool)
    47  
    48  	gotInfo, err := os.Stat(goTool)
    49  	if err != nil {
    50  		t.Fatal(err)
    51  	}
    52  	if !os.SameFile(wantInfo, gotInfo) {
    53  		t.Fatalf("%q is not the same file as %q", absWant, goTool)
    54  	}
    55  }
    56  
    57  func TestHasGoBuild(t *testing.T) {
    58  	if !testenv.HasGoBuild() {
    59  		switch runtime.GOOS {
    60  		case "js", "wasip1":
    61  			// No exec syscall, so these shouldn't be able to 'go build'.
    62  			t.Logf("HasGoBuild is false on %s", runtime.GOOS)
    63  			return
    64  		}
    65  
    66  		b := testenv.Builder()
    67  		if b == "" {
    68  			// We shouldn't make assumptions about what kind of sandbox or build
    69  			// environment external Go users may be running in.
    70  			t.Skipf("skipping: 'go build' unavailable")
    71  		}
    72  
    73  		// Since we control the Go builders, we know which ones ought
    74  		// to be able to run 'go build'. Check that they can.
    75  		//
    76  		// (Note that we don't verify that any builders *can't* run 'go build'.
    77  		// If a builder starts running 'go build' tests when it shouldn't,
    78  		// we will presumably find out about it when those tests fail.)
    79  		switch runtime.GOOS {
    80  		case "ios":
    81  			if isCorelliumBuilder(b) {
    82  				// The corellium environment is self-hosting, so it should be able
    83  				// to build even though real "ios" devices can't exec.
    84  			} else {
    85  				// The usual iOS sandbox does not allow the app to start another
    86  				// process. If we add builders on stock iOS devices, they presumably
    87  				// will not be able to exec, so we may as well allow that now.
    88  				t.Logf("HasGoBuild is false on %s", b)
    89  				return
    90  			}
    91  		case "android":
    92  			if isEmulatedBuilder(b) && platform.MustLinkExternal(runtime.GOOS, runtime.GOARCH, false) {
    93  				// As of 2023-05-02, the test environment on the emulated builders is
    94  				// missing a C linker.
    95  				t.Logf("HasGoBuild is false on %s", b)
    96  				return
    97  			}
    98  		}
    99  
   100  		if strings.Contains(b, "-noopt") ||
   101  			strings.Contains(b, "-spectre") {
   102  			// These builders set GO_GCFLAGS, which causes tests of 'go build' to
   103  			// be skipped.
   104  			t.Logf("HasGoBuild is false on %s", b)
   105  			return
   106  		}
   107  
   108  		t.Fatalf("HasGoBuild unexpectedly false on %s", b)
   109  	}
   110  
   111  	t.Logf("HasGoBuild is true; checking consistency with other functions")
   112  
   113  	hasExec := false
   114  	hasExecGo := false
   115  	t.Run("MustHaveExec", func(t *testing.T) {
   116  		testenv.MustHaveExec(t)
   117  		hasExec = true
   118  	})
   119  	t.Run("MustHaveExecPath", func(t *testing.T) {
   120  		testenv.MustHaveExecPath(t, "go")
   121  		hasExecGo = true
   122  	})
   123  	if !hasExec {
   124  		t.Errorf(`MustHaveExec(t) skipped unexpectedly`)
   125  	}
   126  	if !hasExecGo {
   127  		t.Errorf(`MustHaveExecPath(t, "go") skipped unexpectedly`)
   128  	}
   129  
   130  	dir := t.TempDir()
   131  	mainGo := filepath.Join(dir, "main.go")
   132  	if err := os.WriteFile(mainGo, []byte("package main\nfunc main() {}\n"), 0644); err != nil {
   133  		t.Fatal(err)
   134  	}
   135  	cmd := testenv.Command(t, "go", "build", "-o", os.DevNull, mainGo)
   136  	out, err := cmd.CombinedOutput()
   137  	if err != nil {
   138  		t.Fatalf("%v: %v\n%s", cmd, err, out)
   139  	}
   140  }
   141  
   142  func TestMustHaveExec(t *testing.T) {
   143  	hasExec := false
   144  	t.Run("MustHaveExec", func(t *testing.T) {
   145  		testenv.MustHaveExec(t)
   146  		t.Logf("MustHaveExec did not skip")
   147  		hasExec = true
   148  	})
   149  
   150  	switch runtime.GOOS {
   151  	case "js", "wasip1":
   152  		if hasExec {
   153  			// js and wasip1 lack an “exec” syscall.
   154  			t.Errorf("expected MustHaveExec to skip on %v", runtime.GOOS)
   155  		}
   156  	case "ios":
   157  		if b := testenv.Builder(); isCorelliumBuilder(b) && !hasExec {
   158  			// Most ios environments can't exec, but the corellium builder can.
   159  			t.Errorf("expected MustHaveExec not to skip on %v", b)
   160  		}
   161  	default:
   162  		if b := testenv.Builder(); b != "" && !hasExec {
   163  			t.Errorf("expected MustHaveExec not to skip on %v", b)
   164  		}
   165  	}
   166  }
   167  
   168  func TestCleanCmdEnvPWD(t *testing.T) {
   169  	// Test that CleanCmdEnv sets PWD if cmd.Dir is set.
   170  	switch runtime.GOOS {
   171  	case "plan9", "windows":
   172  		t.Skipf("PWD is not used on %s", runtime.GOOS)
   173  	}
   174  	dir := t.TempDir()
   175  	cmd := testenv.Command(t, testenv.GoToolPath(t), "help")
   176  	cmd.Dir = dir
   177  	cmd = testenv.CleanCmdEnv(cmd)
   178  
   179  	for _, env := range cmd.Env {
   180  		if strings.HasPrefix(env, "PWD=") {
   181  			pwd := strings.TrimPrefix(env, "PWD=")
   182  			if pwd != dir {
   183  				t.Errorf("unexpected PWD: want %s, got %s", dir, pwd)
   184  			}
   185  			return
   186  		}
   187  	}
   188  	t.Error("PWD not set in cmd.Env")
   189  }
   190  
   191  func isCorelliumBuilder(builderName string) bool {
   192  	// Support both the old infra's builder names and the LUCI builder names.
   193  	// The former's names are ad-hoc so we could maintain this invariant on
   194  	// the builder side. The latter's names are structured, and "corellium" will
   195  	// appear as a "host" suffix after the GOOS and GOARCH, which always begin
   196  	// with an underscore.
   197  	return strings.HasSuffix(builderName, "-corellium") || strings.Contains(builderName, "_corellium")
   198  }
   199  
   200  func isEmulatedBuilder(builderName string) bool {
   201  	// Support both the old infra's builder names and the LUCI builder names.
   202  	// The former's names are ad-hoc so we could maintain this invariant on
   203  	// the builder side. The latter's names are structured, and the signifier
   204  	// of emulation "emu" will appear as a "host" suffix after the GOOS and
   205  	// GOARCH because it modifies the run environment in such a way that it
   206  	// the target GOOS and GOARCH may not match the host. This suffix always
   207  	// begins with an underscore.
   208  	return strings.HasSuffix(builderName, "-emu") || strings.Contains(builderName, "_emu")
   209  }
   210  

View as plain text