github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+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" { 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 testDir := filepath.Join(o.TmpDir, "tests") 54 for _, pkg := range pkgs { 55 pkgDir := filepath.Join(testDir, pkg) 56 if err := os.MkdirAll(pkgDir, 0755); err != nil { 57 t.Fatal(err) 58 } 59 60 testFile := filepath.Join(pkgDir, fmt.Sprintf("%s.test", path.Base(pkg))) 61 62 cmd := exec.Command("go", "test", 63 "-gcflags=all=-l", 64 "-ldflags", "-s -w", 65 "-c", pkg, 66 "-o", testFile, 67 ) 68 if stderr, err := cmd.CombinedOutput(); err != nil { 69 t.Fatalf("could not build %s: %v\n%s", pkg, err, string(stderr)) 70 } 71 72 // When a package does not contain any tests, the test 73 // executable is not generated, so it is not included in the 74 // `tests` list. 75 if _, err := os.Stat(testFile); !os.IsNotExist(err) { 76 tests = append(tests, pkg) 77 78 p, err := o.BuildOpts.Env.Package(pkg) 79 if err != nil { 80 t.Fatal(err) 81 } 82 // Optimistically copy any files in the pkg's 83 // directory, in case e.g. a testdata dir is there. 84 if err := copyRelativeFiles(p.Dir, filepath.Join(testDir, pkg)); err != nil { 85 t.Fatal(err) 86 } 87 } 88 } 89 90 // Create the CPIO and start QEMU. 91 o.BuildOpts.AddBusyBoxCommands("github.com/u-root/u-root/cmds/core/*") 92 o.BuildOpts.AddCommands(uroot.BinaryCmds("cmd/test2json")...) 93 94 // Specify the custom gotest uinit. 95 o.Uinit = "github.com/u-root/u-root/integration/testcmd/gotest/uinit" 96 97 tc := json2test.NewTestCollector() 98 serial := []io.Writer{ 99 // Collect JSON test events in tc. 100 json2test.EventParser(tc), 101 // Write non-JSON output to log. 102 JSONLessTestLineWriter(t, "serial"), 103 } 104 if o.QEMUOpts.SerialOutput != nil { 105 serial = append(serial, o.QEMUOpts.SerialOutput) 106 } 107 o.QEMUOpts.SerialOutput = uio.MultiWriteCloser(serial...) 108 109 q, cleanup := QEMUTest(t, o) 110 defer cleanup() 111 112 if err := q.Expect("GoTest Done"); err != nil { 113 t.Errorf("Waiting for GoTest Done: %v", err) 114 } 115 116 // TODO: check that tc.Tests == tests 117 for pkg, test := range tc.Tests { 118 switch test.State { 119 case json2test.StateFail: 120 t.Errorf("Test %v failed:\n%v", pkg, test.FullOutput) 121 case json2test.StateSkip: 122 t.Logf("Test %v skipped", pkg) 123 case json2test.StatePass: 124 // Nothing. 125 default: 126 t.Errorf("Test %v left in state %v:\n%v", pkg, test.State, test.FullOutput) 127 } 128 } 129 } 130 131 func copyRelativeFiles(src string, dst string) error { 132 return filepath.Walk(src, func(path string, fi os.FileInfo, err error) error { 133 if err != nil { 134 return err 135 } 136 137 rel, err := filepath.Rel(src, path) 138 if err != nil { 139 return err 140 } 141 142 if fi.Mode().IsDir() { 143 return os.MkdirAll(filepath.Join(dst, rel), fi.Mode().Perm()) 144 } else if fi.Mode().IsRegular() { 145 srcf, err := os.Open(path) 146 if err != nil { 147 return err 148 } 149 defer srcf.Close() 150 dstf, err := os.Create(filepath.Join(dst, rel)) 151 if err != nil { 152 return err 153 } 154 defer dstf.Close() 155 _, err = io.Copy(dstf, srcf) 156 return err 157 } 158 return nil 159 }) 160 }