1
0
mirror of https://github.com/openshift/installer.git synced 2026-02-05 15:47:14 +01:00
Files
Patrick Dillon 89d9849658 vendor: capi v1.11 & openshift/api
go mod vendor
2025-11-11 16:19:45 -05:00

152 lines
5.0 KiB
Go

// Copyright 2018 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 testscript
import (
"io"
"log"
"os"
"path/filepath"
"runtime"
"strings"
)
// TestingM is implemented by *testing.M. It's defined as an interface
// to allow testscript to co-exist with other testing frameworks
// that might also wish to call M.Run.
type TestingM interface {
Run() int
}
// Deprecated: this option is no longer used.
func IgnoreMissedCoverage() {}
// Main should be called within a TestMain function to allow
// subcommands to be run in the testscript context.
// Main always calls [os.Exit], so it does not return back to the caller.
//
// The commands map holds the set of command names, each
// with an associated run function which may call os.Exit.
//
// When Run is called, these commands are installed as regular commands in the shell
// path, so can be invoked with "exec" or via any other command (for example a shell script).
//
// For backwards compatibility, the commands declared in the map can be run
// without "exec" - that is, "foo" will behave like "exec foo".
// This can be disabled with Params.RequireExplicitExec to keep consistency
// across test scripts, and to keep separate process executions explicit.
func Main(m TestingM, commands map[string]func()) {
// Depending on os.Args[0], this is either the top-level execution of
// the test binary by "go test", or the execution of one of the provided
// commands via "foo" or "exec foo".
cmdName := filepath.Base(os.Args[0])
if runtime.GOOS == "windows" {
cmdName = strings.TrimSuffix(cmdName, ".exe")
}
mainf := commands[cmdName]
if mainf == nil {
// Unknown command; this is just the top-level execution of the
// test binary by "go test".
os.Exit(testingMRun(m, commands))
}
// The command being registered is being invoked, so run it, then exit.
os.Args[0] = cmdName
mainf()
os.Exit(0)
}
// testingMRun exists just so that we can use `defer`, given that [Main] above uses [os.Exit].
func testingMRun(m TestingM, commands map[string]func()) int {
// Set up all commands in a directory, added in $PATH.
tmpdir, err := os.MkdirTemp("", "testscript-main")
if err != nil {
log.Fatalf("could not set up temporary directory: %v", err)
}
defer func() {
if err := os.RemoveAll(tmpdir); err != nil {
log.Fatalf("cannot delete temporary directory: %v", err)
}
}()
bindir := filepath.Join(tmpdir, "bin")
if err := os.MkdirAll(bindir, 0o777); err != nil {
log.Fatalf("could not set up PATH binary directory: %v", err)
}
os.Setenv("PATH", bindir+string(filepath.ListSeparator)+os.Getenv("PATH"))
// We're not in a subcommand.
for name := range commands {
// Set up this command in the directory we added to $PATH.
binfile := filepath.Join(bindir, name)
if runtime.GOOS == "windows" {
binfile += ".exe"
}
binpath, err := os.Executable()
if err == nil {
err = copyBinary(binpath, binfile)
}
if err != nil {
log.Fatalf("could not set up %s in $PATH: %v", name, err)
}
scriptCmds[name] = func(ts *TestScript, neg bool, args []string) {
if ts.params.RequireExplicitExec {
ts.Fatalf("use 'exec %s' rather than '%s' (because RequireExplicitExec is enabled)", name, name)
}
ts.cmdExec(neg, append([]string{name}, args...))
}
}
return m.Run()
}
// Deprecated: use [Main], as the only reason for returning exit codes
// was to collect full code coverage, which Go does automatically now:
// https://go.dev/blog/integration-test-coverage
func RunMain(m TestingM, commands map[string]func() int) (exitCode int) {
commands2 := make(map[string]func(), len(commands))
for name, fn := range commands {
commands2[name] = func() { os.Exit(fn()) }
}
Main(m, commands2)
// Main always calls os.Exit; we assume that all users of RunMain would have simply
// called os.Exit with the returned exitCode as well, following the documentation.
panic("unreachable")
}
// copyBinary makes a copy of a binary to a new location. It is used as part of
// setting up top-level commands in $PATH.
//
// It does not attempt to use symlinks for two reasons:
//
// First, some tools like cmd/go's -toolexec will be clever enough to realise
// when they're given a symlink, and they will use the symlink target for
// executing the program. This breaks testscript, as we depend on os.Args[0] to
// know what command to run.
//
// Second, symlinks might not be available on some environments, so we have to
// implement a "full copy" fallback anyway.
//
// However, we do try to use cloneFile, since that will probably work on most
// unix-like setups. Note that "go test" also places test binaries in the
// system's temporary directory, like we do.
func copyBinary(from, to string) error {
if err := cloneFile(from, to); err == nil {
return nil
}
writer, err := os.OpenFile(to, os.O_WRONLY|os.O_CREATE, 0o777)
if err != nil {
return err
}
defer writer.Close()
reader, err := os.Open(from)
if err != nil {
return err
}
defer reader.Close()
_, err = io.Copy(writer, reader)
return err
}