Source file src/cmd/go/scriptconds_test.go

     1  // Copyright 2018 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 main_test
     6  
     7  import (
     8  	"cmd/go/internal/cfg"
     9  	"cmd/internal/script"
    10  	"cmd/internal/script/scripttest"
    11  	"errors"
    12  	"fmt"
    13  	"internal/testenv"
    14  	"os"
    15  	"os/exec"
    16  	"path/filepath"
    17  	"runtime"
    18  	"runtime/debug"
    19  	"sync"
    20  	"testing"
    21  )
    22  
    23  func scriptConditions(t *testing.T) map[string]script.Cond {
    24  	conds := scripttest.DefaultConds()
    25  
    26  	scripttest.AddToolChainScriptConditions(t, conds, goHostOS, goHostArch)
    27  
    28  	add := func(name string, cond script.Cond) {
    29  		if _, ok := conds[name]; ok {
    30  			panic(fmt.Sprintf("condition %q is already registered", name))
    31  		}
    32  		conds[name] = cond
    33  	}
    34  
    35  	lazyBool := func(summary string, f func() bool) script.Cond {
    36  		return script.OnceCondition(summary, func() (bool, error) { return f(), nil })
    37  	}
    38  
    39  	add("abscc", script.Condition("default $CC path is absolute and exists", defaultCCIsAbsolute))
    40  	add("case-sensitive", script.OnceCondition("$WORK filesystem is case-sensitive", isCaseSensitive))
    41  	add("cc", script.PrefixCondition("go env CC = <suffix> (ignoring the go/env file)", ccIs))
    42  	add("git", lazyBool("the 'git' executable exists and provides the standard CLI", hasWorkingGit))
    43  	add("net", script.PrefixCondition("can connect to external network host <suffix>", hasNet))
    44  	add("trimpath", script.OnceCondition("test binary was built with -trimpath", isTrimpath))
    45  
    46  	return conds
    47  }
    48  
    49  func defaultCCIsAbsolute(s *script.State) (bool, error) {
    50  	GOOS, _ := s.LookupEnv("GOOS")
    51  	GOARCH, _ := s.LookupEnv("GOARCH")
    52  	defaultCC := cfg.DefaultCC(GOOS, GOARCH)
    53  	if filepath.IsAbs(defaultCC) {
    54  		if _, err := exec.LookPath(defaultCC); err == nil {
    55  			return true, nil
    56  		}
    57  	}
    58  	return false, nil
    59  }
    60  
    61  func ccIs(s *script.State, want string) (bool, error) {
    62  	CC, _ := s.LookupEnv("CC")
    63  	if CC != "" {
    64  		return CC == want, nil
    65  	}
    66  	GOOS, _ := s.LookupEnv("GOOS")
    67  	GOARCH, _ := s.LookupEnv("GOARCH")
    68  	return cfg.DefaultCC(GOOS, GOARCH) == want, nil
    69  }
    70  
    71  var scriptNetEnabled sync.Map // testing.TB → already enabled
    72  
    73  func hasNet(s *script.State, host string) (bool, error) {
    74  	if !testenv.HasExternalNetwork() {
    75  		return false, nil
    76  	}
    77  
    78  	// TODO(bcmills): Add a flag or environment variable to allow skipping tests
    79  	// for specific hosts and/or skipping all net tests except for specific hosts.
    80  
    81  	t, ok := tbFromContext(s.Context())
    82  	if !ok {
    83  		return false, errors.New("script Context unexpectedly missing testing.TB key")
    84  	}
    85  
    86  	if netTestSem != nil {
    87  		// When the number of external network connections is limited, we limit the
    88  		// number of net tests that can run concurrently so that the overall number
    89  		// of network connections won't exceed the limit.
    90  		_, dup := scriptNetEnabled.LoadOrStore(t, true)
    91  		if !dup {
    92  			// Acquire a net token for this test until the test completes.
    93  			netTestSem <- struct{}{}
    94  			t.Cleanup(func() {
    95  				<-netTestSem
    96  				scriptNetEnabled.Delete(t)
    97  			})
    98  		}
    99  	}
   100  
   101  	// Since we have confirmed that the network is available,
   102  	// allow cmd/go to use it.
   103  	s.Setenv("TESTGONETWORK", "")
   104  	return true, nil
   105  }
   106  
   107  func isCaseSensitive() (bool, error) {
   108  	tmpdir, err := os.MkdirTemp(testTmpDir, "case-sensitive")
   109  	if err != nil {
   110  		return false, fmt.Errorf("failed to create directory to determine case-sensitivity: %w", err)
   111  	}
   112  	defer os.RemoveAll(tmpdir)
   113  
   114  	fcap := filepath.Join(tmpdir, "FILE")
   115  	if err := os.WriteFile(fcap, []byte{}, 0644); err != nil {
   116  		return false, fmt.Errorf("error writing file to determine case-sensitivity: %w", err)
   117  	}
   118  
   119  	flow := filepath.Join(tmpdir, "file")
   120  	_, err = os.ReadFile(flow)
   121  	switch {
   122  	case err == nil:
   123  		return false, nil
   124  	case os.IsNotExist(err):
   125  		return true, nil
   126  	default:
   127  		return false, fmt.Errorf("unexpected error reading file when determining case-sensitivity: %w", err)
   128  	}
   129  }
   130  
   131  func isTrimpath() (bool, error) {
   132  	info, _ := debug.ReadBuildInfo()
   133  	if info == nil {
   134  		return false, errors.New("missing build info")
   135  	}
   136  
   137  	for _, s := range info.Settings {
   138  		if s.Key == "-trimpath" && s.Value == "true" {
   139  			return true, nil
   140  		}
   141  	}
   142  	return false, nil
   143  }
   144  
   145  func hasWorkingGit() bool {
   146  	if runtime.GOOS == "plan9" {
   147  		// The Git command is usually not the real Git on Plan 9.
   148  		// See https://golang.org/issues/29640.
   149  		return false
   150  	}
   151  	_, err := exec.LookPath("git")
   152  	return err == nil
   153  }
   154  

View as plain text