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