github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/testutil/testutil.go (about) 1 // Copyright 2017 the u-root 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 testutil 6 7 import ( 8 "fmt" 9 "log" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "strings" 14 "testing" 15 "time" 16 17 "github.com/u-root/gobusybox/src/pkg/golang" 18 "github.com/mvdan/u-root-coreutils/pkg/cmdline" 19 ) 20 21 // CheckError is a helper function for tests 22 // It is common to check if an err is expected in the form of errStr, then 23 // there should be an actual error reported. This is an if and only if condition 24 // that needs to be verified. 25 func CheckError(err error, errStr string) error { 26 if err != nil && errStr == "" { 27 return fmt.Errorf("no error expected, got: \n%v", err) 28 } else if err == nil && errStr != "" { 29 return fmt.Errorf("error \n%v\nexpected, got nil error", errStr) 30 } else if err != nil && err.Error() != errStr { 31 return fmt.Errorf("error \n%v\nexpected, got: \n%v", errStr, err) 32 } 33 return nil 34 } 35 36 // NowLog returns the current time formatted like the standard log package's 37 // timestamp. 38 func NowLog() string { 39 return time.Now().Format("2006/01/02 15:04:05") 40 } 41 42 var binary string 43 44 // Command returns an exec.Cmd appropriate for testing the u-root command. 45 // 46 // Command decides which executable to call based on environment variables: 47 // - EXECPATH="executable args" overrides any other test subject. 48 // - UROOT_TEST_BUILD=1 will force compiling the u-root command in question. 49 func Command(t testing.TB, args ...string) *exec.Cmd { 50 // If EXECPATH is set, just use that. 51 execPath := os.Getenv("EXECPATH") 52 if len(execPath) > 0 { 53 exe := strings.Split(os.Getenv("EXECPATH"), " ") 54 return exec.Command(exe[0], append(exe[1:], args...)...) 55 } 56 57 // Should be cached by Run if os.Executable is going to fail. 58 if len(binary) > 0 { 59 t.Logf("binary: %v", binary) 60 return exec.Command(binary, args...) 61 } 62 63 execPath, err := os.Executable() 64 if err != nil { 65 // Run should have prevented this case by caching something in 66 // `binary`. 67 t.Fatal("You must call testutil.Run() in your TestMain.") 68 } 69 70 c := exec.Command(execPath, args...) 71 c.Env = append(c.Env, append(os.Environ(), "UROOT_CALL_MAIN=1")...) 72 return c 73 } 74 75 // IsExitCode takes err and checks whether it represents the given process exit 76 // code. 77 // 78 // IsExitCode assumes that `err` is the return value of a successful call to 79 // exec.Cmd.Run/Output/CombinedOutput and hence an *exec.ExitError. 80 func IsExitCode(err error, exitCode int) error { 81 if err == nil { 82 if exitCode != 0 { 83 return fmt.Errorf("got code 0, want %d", exitCode) 84 } 85 return nil 86 } 87 88 exitErr, ok := err.(*exec.ExitError) 89 if !ok { 90 return fmt.Errorf("encountered error other than ExitError: %#v", err) 91 } 92 es, err := exitStatus(exitErr) 93 if err != nil { 94 return err 95 } 96 if es != exitCode { 97 return fmt.Errorf("got exit status %d, want %d", es, exitCode) 98 } 99 return nil 100 } 101 102 func run(m *testing.M, mainFn func()) int { 103 // UROOT_CALL_MAIN=1 /proc/self/exe should be the same as just running 104 // the command we are testing. 105 if len(os.Getenv("UROOT_CALL_MAIN")) > 0 { 106 mainFn() 107 return 0 108 } 109 110 // Normally, /proc/self/exe (and equivalents) are used to test u-root 111 // commands. 112 // 113 // Such a symlink isn't available on Plan 9, OS X, or FreeBSD. On these 114 // systems, we compile the u-root command in question on the fly 115 // instead. 116 // 117 // Here, we decide whether to compile or not and cache the executable. 118 // Do this here, so that when m.Run() returns, we can remove the 119 // executable using the functor returned. 120 _, err := os.Executable() 121 if err != nil || len(os.Getenv("UROOT_TEST_BUILD")) > 0 { 122 // We can't find ourselves? Probably FreeBSD or something. Try to go 123 // build the command. 124 // 125 // This is NOT build-system-independent, and hence the fallback. 126 tmpDir, err := os.MkdirTemp("", "uroot-build") 127 if err != nil { 128 log.Fatal(err) 129 } 130 defer os.RemoveAll(tmpDir) 131 132 wd, err := os.Getwd() 133 if err != nil { 134 log.Fatal(err) 135 } 136 137 execPath := filepath.Join(tmpDir, "binary") 138 // Build the stuff. 139 if err := golang.Default().BuildDir(wd, execPath, nil); err != nil { 140 log.Fatal(err) 141 } 142 143 // Cache dat. 144 binary = execPath 145 } 146 147 return m.Run() 148 } 149 150 // Run sets up necessary commands to be compiled, if necessary, and calls 151 // m.Run. 152 func Run(m *testing.M, mainFn func()) { 153 os.Exit(run(m, mainFn)) 154 } 155 156 // SkipIfInVMTest skips a test if it's being executed in a u-root test VM. 157 // 158 // See pkg/vmtest/integration.go which starts the VM with the uroot.vmtest in 159 // the kernel cmdline. 160 func SkipIfInVMTest(t *testing.T) { 161 if cmdline.ContainsFlag("uroot.vmtest") { 162 t.Skipf("Skipping test since we are in a u-root test VM") 163 } 164 } 165 166 // SkipIfNotRoot skips the calling test if uid != 0. 167 func SkipIfNotRoot(t *testing.T) { 168 if os.Getuid() != 0 { 169 t.Skipf("Skipping test since we are not root") 170 } 171 }