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