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