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