github.com/shaardie/u-root@v4.0.1-0.20190127173353-f24a1c26aa2e+incompatible/integration/gotest_test.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 integration
     6  
     7  import (
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"os/exec"
    12  	"path"
    13  	"path/filepath"
    14  	"regexp"
    15  	"sort"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  )
    20  
    21  // testPkgs returns a slice of tests to run.
    22  func testPkgs(t *testing.T) []string {
    23  	// Packages which do not contain tests (or do not contain tests for the
    24  	// build target) will still compile a test binary which vacuously pass.
    25  	cmd := exec.Command("go", "list",
    26  		"github.com/u-root/u-root/cmds/...",
    27  		// TODO: only running tests in cmds because tests in pkg have
    28  		// duplicate names which confuses the test runner. This should
    29  		// get fixed.
    30  		// "github.com/u-root/u-root/xcmds/...",
    31  		// "github.com/u-root/u-root/pkg/...",
    32  	)
    33  	cmd.Env = append(os.Environ(), "GOARCH="+TestArch())
    34  	out, err := cmd.CombinedOutput()
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	pkgs := strings.Fields(strings.TrimSpace(string(out)))
    39  
    40  	// TODO: Some tests do not run properly in QEMU at the moment. They are
    41  	// blacklisted. These tests fail for mostly two reasons:
    42  	// 1. either it requires networking (not enabled in the kernel)
    43  	// 2. or it depends on some test files (for example /bin/sleep)
    44  	blacklist := []string{
    45  		"github.com/u-root/u-root/cmds/bzimage",
    46  		"github.com/u-root/u-root/cmds/cmp",
    47  		"github.com/u-root/u-root/cmds/dd",
    48  		"github.com/u-root/u-root/cmds/dhclient",
    49  		"github.com/u-root/u-root/cmds/elvish/eval",
    50  		"github.com/u-root/u-root/cmds/fmap",
    51  		"github.com/u-root/u-root/cmds/gpt",
    52  		"github.com/u-root/u-root/cmds/kill",
    53  		"github.com/u-root/u-root/cmds/mount",
    54  		"github.com/u-root/u-root/cmds/tail",
    55  		"github.com/u-root/u-root/cmds/wget",
    56  		"github.com/u-root/u-root/cmds/which",
    57  		"github.com/u-root/u-root/cmds/wifi",
    58  	}
    59  	for i := 0; i < len(pkgs); i++ {
    60  		for _, b := range blacklist {
    61  			if pkgs[i] == b {
    62  				pkgs = append(pkgs[:i], pkgs[i+1:]...)
    63  			}
    64  		}
    65  	}
    66  
    67  	return pkgs
    68  }
    69  
    70  // TestGoTest effectively runs "go test ./..." inside a QEMU instance. The
    71  // tests run as root and can do all sorts of things not possible otherwise.
    72  func TestGoTest(t *testing.T) {
    73  	SkipWithoutQEMU(t)
    74  
    75  	// TODO: support arm
    76  	if TestArch() != "amd64" {
    77  		t.Skipf("test not supported on %s", TestArch())
    78  	}
    79  
    80  	// Create a temporary directory.
    81  	tmpDir, err := ioutil.TempDir("", "uroot-integration")
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  
    86  	// Statically build tests and add them to the temporary directory.
    87  	pkgs := testPkgs(t)
    88  	tests := []string{}
    89  	os.Setenv("CGO_ENABLED", "0")
    90  	testDir := filepath.Join(tmpDir, "tests")
    91  	for _, pkg := range pkgs {
    92  		testFile := filepath.Join(testDir, path.Base(pkg))
    93  		cmd := exec.Command("go", "test", "-ldflags", "-s -w", "-c", pkg, "-o", testFile)
    94  		if err := cmd.Run(); err != nil {
    95  			t.Fatalf("could not build %s: %v", pkg, err)
    96  		}
    97  
    98  		// When a package does not contain any tests, the test
    99  		// executable is not generated, so it is not included in the
   100  		// `tests` list.
   101  		if _, err := os.Stat(testFile); !os.IsNotExist(err) {
   102  			tests = append(tests, pkg)
   103  		}
   104  	}
   105  
   106  	// Create the CPIO and start QEMU.
   107  	q, cleanup := QEMUTest(t, &Options{
   108  		Cmds: []string{
   109  			"github.com/u-root/u-root/integration/testcmd/gotest/uinit",
   110  			"github.com/u-root/u-root/cmds/init",
   111  			// Used by gotest/uinit.
   112  			"github.com/u-root/u-root/cmds/mkdir",
   113  			"github.com/u-root/u-root/cmds/mount",
   114  			// Used by an elvish test.
   115  			"github.com/u-root/u-root/cmds/ls",
   116  		},
   117  		TmpDir: tmpDir,
   118  	})
   119  	defer cleanup()
   120  
   121  	// Tests are run and checked in sorted order.
   122  	bases := []string{}
   123  	for _, test := range tests {
   124  		bases = append(bases, path.Base(test))
   125  	}
   126  	sort.Strings(bases)
   127  
   128  	// Check that the test runner is running inside QEMU.
   129  	headerMsg := fmt.Sprintf("TAP: 1..%d", len(bases))
   130  	if err := q.ExpectTimeout(headerMsg, 30*time.Second); err != nil {
   131  		t.Fatalf("cannot communicate with test runner: %v", err)
   132  	}
   133  	t.Log(headerMsg)
   134  
   135  	// Check that each test passed.
   136  	for i, base := range bases {
   137  		runMsg := fmt.Sprintf("TAP: # running %d - %s", i, base)
   138  		passMsg := fmt.Sprintf("TAP: ok %d - %s", i, base)
   139  		failMsg := fmt.Sprintf("TAP: not ok %d - %s", i, base)
   140  		passOrFailMsg := regexp.MustCompile(fmt.Sprintf("TAP: (not )?ok %d - %s", i, base))
   141  
   142  		t.Log(runMsg)
   143  		str, err := q.ExpectRETimeout(passOrFailMsg, 30*time.Second)
   144  		if err != nil {
   145  			// If we can neither find the "ok" nor the "not ok" message, the
   146  			// test runner inside QEMU is misbehaving and we fatal early
   147  			// instead of wasting time.
   148  			t.Logf(failMsg)
   149  			t.Fatal("TAP: Bail out! QEMU test runner stopped printing.")
   150  		}
   151  
   152  		if strings.Contains(str, passMsg) {
   153  			t.Log(passMsg)
   154  		} else {
   155  			t.Error(failMsg)
   156  		}
   157  	}
   158  }