github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/vmtest/gotest.go (about) 1 // Copyright 2018 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 vmtest 6 7 import ( 8 "fmt" 9 "io" 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path" 14 "path/filepath" 15 "testing" 16 17 "github.com/u-root/u-root/pkg/golang" 18 "github.com/u-root/u-root/pkg/uio" 19 "github.com/u-root/u-root/pkg/uroot" 20 "github.com/u-root/u-root/pkg/vmtest/internal/json2test" 21 ) 22 23 // GolangTest compiles the unit tests found in pkgs and runs them in a QEMU VM. 24 func GolangTest(t *testing.T, pkgs []string, o *Options) { 25 SkipWithoutQEMU(t) 26 // TODO: support arm 27 if TestArch() != "amd64" && TestArch() != "arm64" { 28 t.Skipf("test not supported on %s", TestArch()) 29 } 30 31 if o == nil { 32 o = &Options{} 33 } 34 35 // Create a temporary directory. 36 if len(o.TmpDir) == 0 { 37 tmpDir, err := ioutil.TempDir("", "uroot-integration") 38 if err != nil { 39 t.Fatal(err) 40 } 41 o.TmpDir = tmpDir 42 } 43 44 // Set up u-root build options. 45 env := golang.Default() 46 env.CgoEnabled = false 47 env.GOARCH = TestArch() 48 o.BuildOpts.Env = env 49 50 // Statically build tests and add them to the temporary directory. 51 var tests []string 52 os.Setenv("CGO_ENABLED", "0") 53 os.Setenv("GOARCH", TestArch()) 54 testDir := filepath.Join(o.TmpDir, "tests") 55 for _, pkg := range pkgs { 56 pkgDir := filepath.Join(testDir, pkg) 57 if err := os.MkdirAll(pkgDir, 0755); err != nil { 58 t.Fatal(err) 59 } 60 61 testFile := filepath.Join(pkgDir, fmt.Sprintf("%s.test", path.Base(pkg))) 62 63 cmd := exec.Command("go", "test", 64 "-gcflags=all=-l", 65 "-ldflags", "-s -w", 66 "-c", pkg, 67 "-o", testFile, 68 ) 69 if stderr, err := cmd.CombinedOutput(); err != nil { 70 t.Fatalf("could not build %s: %v\n%s", pkg, err, string(stderr)) 71 } 72 73 // When a package does not contain any tests, the test 74 // executable is not generated, so it is not included in the 75 // `tests` list. 76 if _, err := os.Stat(testFile); !os.IsNotExist(err) { 77 tests = append(tests, pkg) 78 79 p, err := o.BuildOpts.Env.Package(pkg) 80 if err != nil { 81 t.Fatal(err) 82 } 83 // Optimistically copy any files in the pkg's 84 // directory, in case e.g. a testdata dir is there. 85 if err := copyRelativeFiles(p.Dir, filepath.Join(testDir, pkg)); err != nil { 86 t.Fatal(err) 87 } 88 } 89 } 90 91 // Create the CPIO and start QEMU. 92 o.BuildOpts.AddBusyBoxCommands("github.com/u-root/u-root/cmds/core/*") 93 o.BuildOpts.AddCommands(uroot.BinaryCmds("cmd/test2json")...) 94 95 // Specify the custom gotest uinit. 96 o.Uinit = "github.com/u-root/u-root/integration/testcmd/gotest/uinit" 97 98 tc := json2test.NewTestCollector() 99 serial := []io.Writer{ 100 // Collect JSON test events in tc. 101 json2test.EventParser(tc), 102 // Write non-JSON output to log. 103 JSONLessTestLineWriter(t, "serial"), 104 } 105 if o.QEMUOpts.SerialOutput != nil { 106 serial = append(serial, o.QEMUOpts.SerialOutput) 107 } 108 o.QEMUOpts.SerialOutput = uio.MultiWriteCloser(serial...) 109 110 q, cleanup := QEMUTest(t, o) 111 defer cleanup() 112 113 if err := q.Expect("GoTest Done"); err != nil { 114 t.Errorf("Waiting for GoTest Done: %v", err) 115 } 116 117 // TODO: check that tc.Tests == tests 118 for pkg, test := range tc.Tests { 119 switch test.State { 120 case json2test.StateFail: 121 t.Errorf("Test %v failed:\n%v", pkg, test.FullOutput) 122 case json2test.StateSkip: 123 t.Logf("Test %v skipped", pkg) 124 case json2test.StatePass: 125 // Nothing. 126 default: 127 t.Errorf("Test %v left in state %v:\n%v", pkg, test.State, test.FullOutput) 128 } 129 } 130 } 131 132 func copyRelativeFiles(src string, dst string) error { 133 return filepath.Walk(src, func(path string, fi os.FileInfo, err error) error { 134 if err != nil { 135 return err 136 } 137 138 rel, err := filepath.Rel(src, path) 139 if err != nil { 140 return err 141 } 142 143 if fi.Mode().IsDir() { 144 return os.MkdirAll(filepath.Join(dst, rel), fi.Mode().Perm()) 145 } else if fi.Mode().IsRegular() { 146 srcf, err := os.Open(path) 147 if err != nil { 148 return err 149 } 150 defer srcf.Close() 151 dstf, err := os.Create(filepath.Join(dst, rel)) 152 if err != nil { 153 return err 154 } 155 defer dstf.Close() 156 _, err = io.Copy(dstf, srcf) 157 return err 158 } 159 return nil 160 }) 161 }