summaryrefslogtreecommitdiffhomepage
path: root/test/runner
diff options
context:
space:
mode:
Diffstat (limited to 'test/runner')
-rw-r--r--test/runner/defs.bzl105
-rw-r--r--test/runner/gtest/gtest.go50
-rw-r--r--test/runner/runner.go211
3 files changed, 161 insertions, 205 deletions
diff --git a/test/runner/defs.bzl b/test/runner/defs.bzl
index 829247657..2a0ef2cec 100644
--- a/test/runner/defs.bzl
+++ b/test/runner/defs.bzl
@@ -4,7 +4,7 @@ load("//tools:defs.bzl", "default_platform", "platforms")
def _runner_test_impl(ctx):
# Generate a runner binary.
- runner = ctx.actions.declare_file("%s-runner" % ctx.label.name)
+ runner = ctx.actions.declare_file(ctx.label.name)
runner_content = "\n".join([
"#!/bin/bash",
"set -euf -x -o pipefail",
@@ -85,18 +85,9 @@ def _syscall_test(
# Add the full_platform and file access in a tag to make it easier to run
# all the tests on a specific flavor. Use --test_tag_filters=ptrace,file_shared.
+ tags = list(tags)
tags += [full_platform, "file_" + file_access]
- # Hash this target into one of 15 buckets. This can be used to
- # randomly split targets between different workflows.
- hash15 = hash(native.package_name() + name) % 15
- tags.append("hash15:" + str(hash15))
-
- # TODO(b/139838000): Tests using hostinet must be disabled on Guitar until
- # we figure out how to request ipv4 sockets on Guitar machines.
- if network == "host":
- tags.append("noguitar")
-
# Disable off-host networking.
tags.append("requires-net:loopback")
tags.append("requires-net:ipv4")
@@ -157,116 +148,82 @@ def syscall_test(
if not tags:
tags = []
- vfs2_tags = list(tags)
- if vfs2:
- # Add tag to easily run VFS2 tests with --test_tag_filters=vfs2
- vfs2_tags.append("vfs2")
- if fuse:
- vfs2_tags.append("fuse")
-
- else:
- # Don't automatically run tests tests not yet passing.
- vfs2_tags.append("manual")
- vfs2_tags.append("noguitar")
- vfs2_tags.append("notap")
-
- _syscall_test(
- test = test,
- platform = default_platform,
- use_tmpfs = use_tmpfs,
- add_uds_tree = add_uds_tree,
- tags = platforms[default_platform] + vfs2_tags,
- debug = debug,
- vfs2 = True,
- fuse = fuse,
- **kwargs
- )
- if fuse:
- # Only generate *_vfs2_fuse target if fuse parameter is enabled.
- return
-
- _syscall_test(
- test = test,
- platform = "native",
- use_tmpfs = False,
- add_uds_tree = add_uds_tree,
- tags = list(tags),
- debug = debug,
- **kwargs
- )
-
- for (platform, platform_tags) in platforms.items():
+ if vfs2 and not fuse:
+ # Generate a vfs1 plain test. Most testing will now be
+ # biased towards vfs2, with only a single vfs1 case.
_syscall_test(
test = test,
- platform = platform,
+ platform = default_platform,
use_tmpfs = use_tmpfs,
add_uds_tree = add_uds_tree,
- tags = platform_tags + tags,
+ tags = tags + platforms[default_platform],
debug = debug,
+ vfs2 = False,
**kwargs
)
- if add_overlay:
+ if not fuse:
+ # Generate a native test if fuse is not required.
_syscall_test(
test = test,
- platform = default_platform,
- use_tmpfs = use_tmpfs,
+ platform = "native",
+ use_tmpfs = False,
add_uds_tree = add_uds_tree,
- tags = platforms[default_platform] + tags,
+ tags = tags,
debug = debug,
- overlay = True,
**kwargs
)
- # TODO(gvisor.dev/issue/4407): Remove tags to enable VFS2 overlay tests.
- overlay_vfs2_tags = list(vfs2_tags)
- overlay_vfs2_tags.append("manual")
- overlay_vfs2_tags.append("noguitar")
- overlay_vfs2_tags.append("notap")
+ for (platform, platform_tags) in platforms.items():
_syscall_test(
test = test,
- platform = default_platform,
+ platform = platform,
use_tmpfs = use_tmpfs,
add_uds_tree = add_uds_tree,
- tags = platforms[default_platform] + overlay_vfs2_tags,
+ tags = platform_tags + tags,
+ fuse = fuse,
+ vfs2 = vfs2,
debug = debug,
- overlay = True,
- vfs2 = True,
**kwargs
)
- if add_hostinet:
+ if add_overlay:
_syscall_test(
test = test,
platform = default_platform,
use_tmpfs = use_tmpfs,
- network = "host",
add_uds_tree = add_uds_tree,
tags = platforms[default_platform] + tags,
debug = debug,
+ fuse = fuse,
+ vfs2 = vfs2,
+ overlay = True,
**kwargs
)
-
- if not use_tmpfs:
- # Also test shared gofer access.
+ if add_hostinet:
_syscall_test(
test = test,
platform = default_platform,
use_tmpfs = use_tmpfs,
+ network = "host",
add_uds_tree = add_uds_tree,
tags = platforms[default_platform] + tags,
debug = debug,
- file_access = "shared",
+ fuse = fuse,
+ vfs2 = vfs2,
**kwargs
)
+ if not use_tmpfs:
+ # Also test shared gofer access.
_syscall_test(
test = test,
platform = default_platform,
use_tmpfs = use_tmpfs,
add_uds_tree = add_uds_tree,
- tags = platforms[default_platform] + vfs2_tags,
+ tags = platforms[default_platform] + tags,
debug = debug,
file_access = "shared",
- vfs2 = True,
+ fuse = fuse,
+ vfs2 = vfs2,
**kwargs
)
diff --git a/test/runner/gtest/gtest.go b/test/runner/gtest/gtest.go
index 2ad5f58ef..38e57d62f 100644
--- a/test/runner/gtest/gtest.go
+++ b/test/runner/gtest/gtest.go
@@ -35,39 +35,6 @@ var (
filterBenchmarkFlag = "--benchmark_filter"
)
-// BuildTestArgs builds arguments to be passed to the test binary to execute
-// only the test cases in `indices`.
-func BuildTestArgs(indices []int, testCases []TestCase) []string {
- var testFilter, benchFilter string
- for _, tci := range indices {
- tc := testCases[tci]
- if tc.all {
- // No argument will make all tests run.
- return nil
- }
- if tc.benchmark {
- if len(benchFilter) > 0 {
- benchFilter += "|"
- }
- benchFilter += "^" + tc.Name + "$"
- } else {
- if len(testFilter) > 0 {
- testFilter += ":"
- }
- testFilter += tc.FullName()
- }
- }
-
- var args []string
- if len(testFilter) > 0 {
- args = append(args, fmt.Sprintf("%s=%s", filterTestFlag, testFilter))
- }
- if len(benchFilter) > 0 {
- args = append(args, fmt.Sprintf("%s=%s", filterBenchmarkFlag, benchFilter))
- }
- return args
-}
-
// TestCase is a single gtest test case.
type TestCase struct {
// Suite is the suite for this test.
@@ -92,6 +59,22 @@ func (tc TestCase) FullName() string {
return fmt.Sprintf("%s.%s", tc.Suite, tc.Name)
}
+// Args returns arguments to be passed when invoking the test.
+func (tc TestCase) Args() []string {
+ if tc.all {
+ return []string{} // No arguments.
+ }
+ if tc.benchmark {
+ return []string{
+ fmt.Sprintf("%s=^%s$", filterBenchmarkFlag, tc.Name),
+ fmt.Sprintf("%s=", filterTestFlag),
+ }
+ }
+ return []string{
+ fmt.Sprintf("%s=%s", filterTestFlag, tc.FullName()),
+ }
+}
+
// ParseTestCases calls a gtest test binary to list its test and returns a
// slice with the name and suite of each test.
//
@@ -107,7 +90,6 @@ func ParseTestCases(testBin string, benchmarks bool, extraArgs ...string) ([]Tes
// We failed to list tests with the given flags. Just
// return something that will run the binary with no
// flags, which should execute all tests.
- fmt.Printf("failed to get test list: %v\n", err)
return []TestCase{
{
Suite: "Default",
diff --git a/test/runner/runner.go b/test/runner/runner.go
index a8a134fe2..7e8e88ba2 100644
--- a/test/runner/runner.go
+++ b/test/runner/runner.go
@@ -26,6 +26,7 @@ import (
"path/filepath"
"strings"
"syscall"
+ "testing"
"time"
specs "github.com/opencontainers/runtime-spec/specs-go"
@@ -56,82 +57,13 @@ var (
leakCheck = flag.Bool("leak-check", false, "check for reference leaks")
)
-func main() {
- flag.Parse()
- if flag.NArg() != 1 {
- fatalf("test must be provided")
- }
-
- log.SetLevel(log.Info)
- if *debug {
- log.SetLevel(log.Debug)
- }
-
- if *platform != "native" && *runscPath == "" {
- if err := testutil.ConfigureExePath(); err != nil {
- panic(err.Error())
- }
- *runscPath = specutils.ExePath
- }
-
- // Make sure stdout and stderr are opened with O_APPEND, otherwise logs
- // from outside the sandbox can (and will) stomp on logs from inside
- // the sandbox.
- for _, f := range []*os.File{os.Stdout, os.Stderr} {
- flags, err := unix.FcntlInt(f.Fd(), unix.F_GETFL, 0)
- if err != nil {
- fatalf("error getting file flags for %v: %v", f, err)
- }
- if flags&unix.O_APPEND == 0 {
- flags |= unix.O_APPEND
- if _, err := unix.FcntlInt(f.Fd(), unix.F_SETFL, flags); err != nil {
- fatalf("error setting file flags for %v: %v", f, err)
- }
- }
- }
-
- // Resolve the absolute path for the binary.
- testBin, err := filepath.Abs(flag.Args()[0])
- if err != nil {
- fatalf("Abs(%q) failed: %v", flag.Args()[0], err)
- }
-
- // Get all test cases in each binary.
- testCases, err := gtest.ParseTestCases(testBin, true)
- if err != nil {
- fatalf("ParseTestCases(%q) failed: %v", testBin, err)
- }
-
- // Get subset of tests corresponding to shard.
- indices, err := testutil.TestIndicesForShard(len(testCases))
- if err != nil {
- fatalf("TestsForShard() failed: %v", err)
- }
- if len(indices) == 0 {
- log.Warningf("No tests to run in this shard")
- return
- }
- args := gtest.BuildTestArgs(indices, testCases)
-
- switch *platform {
- case "native":
- if err := runTestCaseNative(testBin, args); err != nil {
- fatalf(err.Error())
- }
- default:
- if err := runTestCaseRunsc(testBin, args); err != nil {
- fatalf(err.Error())
- }
- }
-}
-
// runTestCaseNative runs the test case directly on the host machine.
-func runTestCaseNative(testBin string, args []string) error {
+func runTestCaseNative(testBin string, tc gtest.TestCase, t *testing.T) {
// These tests might be running in parallel, so make sure they have a
// unique test temp dir.
tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "")
if err != nil {
- return fmt.Errorf("could not create temp dir: %v", err)
+ t.Fatalf("could not create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
@@ -152,12 +84,12 @@ func runTestCaseNative(testBin string, args []string) error {
}
// Remove shard env variables so that the gunit binary does not try to
// interpret them.
- env = filterEnv(env, "TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS", "GTEST_SHARD_INDEX", "GTEST_TOTAL_SHARDS")
+ env = filterEnv(env, []string{"TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS", "GTEST_SHARD_INDEX", "GTEST_TOTAL_SHARDS"})
if *addUDSTree {
socketDir, cleanup, err := uds.CreateSocketTree("/tmp")
if err != nil {
- return fmt.Errorf("failed to create socket tree: %v", err)
+ t.Fatalf("failed to create socket tree: %v", err)
}
defer cleanup()
@@ -167,7 +99,7 @@ func runTestCaseNative(testBin string, args []string) error {
env = append(env, "TEST_UDS_ATTACH_TREE="+socketDir)
}
- cmd := exec.Command(testBin, args...)
+ cmd := exec.Command(testBin, tc.Args()...)
cmd.Env = env
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -183,9 +115,8 @@ func runTestCaseNative(testBin string, args []string) error {
if err := cmd.Run(); err != nil {
ws := err.(*exec.ExitError).Sys().(syscall.WaitStatus)
- return fmt.Errorf("test exited with status %d, want 0", ws.ExitStatus())
+ t.Errorf("test %q exited with status %d, want 0", tc.FullName(), ws.ExitStatus())
}
- return nil
}
// runRunsc runs spec in runsc in a standard test configuration.
@@ -193,7 +124,7 @@ func runTestCaseNative(testBin string, args []string) error {
// runsc logs will be saved to a path in TEST_UNDECLARED_OUTPUTS_DIR.
//
// Returns an error if the sandboxed application exits non-zero.
-func runRunsc(spec *specs.Spec) error {
+func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
bundleDir, cleanup, err := testutil.SetupBundleDir(spec)
if err != nil {
return fmt.Errorf("SetupBundleDir failed: %v", err)
@@ -206,8 +137,9 @@ func runRunsc(spec *specs.Spec) error {
}
defer cleanup()
+ name := tc.FullName()
id := testutil.RandomContainerID()
- log.Infof("Running test in container %q", id)
+ log.Infof("Running test %q in container %q", name, id)
specutils.LogSpec(spec)
args := []string{
@@ -243,8 +175,13 @@ func runRunsc(spec *specs.Spec) error {
args = append(args, "-ref-leak-mode=log-names")
}
- testLogDir := os.Getenv("TEST_UNDECLARED_OUTPUTS_DIR")
- if len(testLogDir) > 0 {
+ testLogDir := ""
+ if undeclaredOutputsDir, ok := unix.Getenv("TEST_UNDECLARED_OUTPUTS_DIR"); ok {
+ // Create log directory dedicated for this test.
+ testLogDir = filepath.Join(undeclaredOutputsDir, strings.Replace(name, "/", "_", -1))
+ if err := os.MkdirAll(testLogDir, 0755); err != nil {
+ return fmt.Errorf("could not create test dir: %v", err)
+ }
debugLogDir, err := ioutil.TempDir(testLogDir, "runsc")
if err != nil {
return fmt.Errorf("could not create temp dir: %v", err)
@@ -252,6 +189,7 @@ func runRunsc(spec *specs.Spec) error {
debugLogDir += "/"
log.Infof("runsc logs: %s", debugLogDir)
args = append(args, "-debug-log", debugLogDir)
+ args = append(args, "-coverage-report", debugLogDir)
// Default -log sends messages to stderr which makes reading the test log
// difficult. Instead, drop them when debug log is enabled given it's a
@@ -289,7 +227,7 @@ func runRunsc(spec *specs.Spec) error {
if !ok {
return
}
- log.Warningf("Got signal: %v", s)
+ log.Warningf("%s: Got signal: %v", name, s)
done := make(chan bool, 1)
dArgs := append([]string{}, args...)
dArgs = append(dArgs, "-alsologtostderr=true", "debug", "--stacks", id)
@@ -322,7 +260,7 @@ func runRunsc(spec *specs.Spec) error {
if err == nil && len(testLogDir) > 0 {
// If the test passed, then we erase the log directory. This speeds up
// uploading logs in continuous integration & saves on disk space.
- _ = os.RemoveAll(testLogDir)
+ os.RemoveAll(testLogDir)
}
return err
@@ -377,10 +315,10 @@ func setupUDSTree(spec *specs.Spec) (cleanup func(), err error) {
}
// runsTestCaseRunsc runs the test case in runsc.
-func runTestCaseRunsc(testBin string, args []string) error {
+func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) {
// Run a new container with the test executable and filter for the
// given test suite and name.
- spec := testutil.NewSpecWithArgs(append([]string{testBin}, args...)...)
+ spec := testutil.NewSpecWithArgs(append([]string{testBin}, tc.Args()...)...)
// Mark the root as writeable, as some tests attempt to
// write to the rootfs, and expect EACCES, not EROFS.
@@ -406,12 +344,12 @@ func runTestCaseRunsc(testBin string, args []string) error {
// users, so make sure it is world-accessible.
tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "")
if err != nil {
- return fmt.Errorf("could not create temp dir: %v", err)
+ t.Fatalf("could not create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
if err := os.Chmod(tmpDir, 0777); err != nil {
- return fmt.Errorf("could not chmod temp dir: %v", err)
+ t.Fatalf("could not chmod temp dir: %v", err)
}
// "/tmp" is not replaced with a tmpfs mount inside the sandbox
@@ -431,12 +369,13 @@ func runTestCaseRunsc(testBin string, args []string) error {
// Set environment variables that indicate we are running in gVisor with
// the given platform, network, and filesystem stack.
- env := []string{"TEST_ON_GVISOR=" + *platform, "GVISOR_NETWORK=" + *network}
- env = append(env, os.Environ()...)
- const vfsVar = "GVISOR_VFS"
+ platformVar := "TEST_ON_GVISOR"
+ networkVar := "GVISOR_NETWORK"
+ env := append(os.Environ(), platformVar+"="+*platform, networkVar+"="+*network)
+ vfsVar := "GVISOR_VFS"
if *vfs2 {
env = append(env, vfsVar+"=VFS2")
- const fuseVar = "FUSE_ENABLED"
+ fuseVar := "FUSE_ENABLED"
if *fuse {
env = append(env, fuseVar+"=TRUE")
} else {
@@ -448,11 +387,11 @@ func runTestCaseRunsc(testBin string, args []string) error {
// Remove shard env variables so that the gunit binary does not try to
// interpret them.
- env = filterEnv(env, "TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS", "GTEST_SHARD_INDEX", "GTEST_TOTAL_SHARDS")
+ env = filterEnv(env, []string{"TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS", "GTEST_SHARD_INDEX", "GTEST_TOTAL_SHARDS"})
// Set TEST_TMPDIR to /tmp, as some of the syscall tests require it to
// be backed by tmpfs.
- env = filterEnv(env, "TEST_TMPDIR")
+ env = filterEnv(env, []string{"TEST_TMPDIR"})
env = append(env, fmt.Sprintf("TEST_TMPDIR=%s", testTmpDir))
spec.Process.Env = env
@@ -460,19 +399,18 @@ func runTestCaseRunsc(testBin string, args []string) error {
if *addUDSTree {
cleanup, err := setupUDSTree(spec)
if err != nil {
- return fmt.Errorf("error creating UDS tree: %v", err)
+ t.Fatalf("error creating UDS tree: %v", err)
}
defer cleanup()
}
- if err := runRunsc(spec); err != nil {
- return fmt.Errorf("test failed with error %v, want nil", err)
+ if err := runRunsc(tc, spec); err != nil {
+ t.Errorf("test %q failed with error %v, want nil", tc.FullName(), err)
}
- return nil
}
// filterEnv returns an environment with the excluded variables removed.
-func filterEnv(env []string, exclude ...string) []string {
+func filterEnv(env, exclude []string) []string {
var out []string
for _, kv := range env {
ok := true
@@ -493,3 +431,82 @@ func fatalf(s string, args ...interface{}) {
fmt.Fprintf(os.Stderr, s+"\n", args...)
os.Exit(1)
}
+
+func matchString(a, b string) (bool, error) {
+ return a == b, nil
+}
+
+func main() {
+ flag.Parse()
+ if flag.NArg() != 1 {
+ fatalf("test must be provided")
+ }
+ testBin := flag.Args()[0] // Only argument.
+
+ log.SetLevel(log.Info)
+ if *debug {
+ log.SetLevel(log.Debug)
+ }
+
+ if *platform != "native" && *runscPath == "" {
+ if err := testutil.ConfigureExePath(); err != nil {
+ panic(err.Error())
+ }
+ *runscPath = specutils.ExePath
+ }
+
+ // Make sure stdout and stderr are opened with O_APPEND, otherwise logs
+ // from outside the sandbox can (and will) stomp on logs from inside
+ // the sandbox.
+ for _, f := range []*os.File{os.Stdout, os.Stderr} {
+ flags, err := unix.FcntlInt(f.Fd(), unix.F_GETFL, 0)
+ if err != nil {
+ fatalf("error getting file flags for %v: %v", f, err)
+ }
+ if flags&unix.O_APPEND == 0 {
+ flags |= unix.O_APPEND
+ if _, err := unix.FcntlInt(f.Fd(), unix.F_SETFL, flags); err != nil {
+ fatalf("error setting file flags for %v: %v", f, err)
+ }
+ }
+ }
+
+ // Get all test cases in each binary.
+ testCases, err := gtest.ParseTestCases(testBin, true)
+ if err != nil {
+ fatalf("ParseTestCases(%q) failed: %v", testBin, err)
+ }
+
+ // Get subset of tests corresponding to shard.
+ indices, err := testutil.TestIndicesForShard(len(testCases))
+ if err != nil {
+ fatalf("TestsForShard() failed: %v", err)
+ }
+
+ // Resolve the absolute path for the binary.
+ testBin, err = filepath.Abs(testBin)
+ if err != nil {
+ fatalf("Abs() failed: %v", err)
+ }
+
+ // Run the tests.
+ var tests []testing.InternalTest
+ for _, tci := range indices {
+ // Capture tc.
+ tc := testCases[tci]
+ tests = append(tests, testing.InternalTest{
+ Name: fmt.Sprintf("%s_%s", tc.Suite, tc.Name),
+ F: func(t *testing.T) {
+ if *platform == "native" {
+ // Run the test case on host.
+ runTestCaseNative(testBin, tc, t)
+ } else {
+ // Run the test case in runsc.
+ runTestCaseRunsc(testBin, tc, t)
+ }
+ },
+ })
+ }
+
+ testing.Main(matchString, tests, nil, nil)
+}