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