diff options
author | Brian Geffon <bgeffon@google.com> | 2018-12-10 14:41:40 -0800 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-12-10 14:42:34 -0800 |
commit | d3bc79bc8438206ac6a14fde4eaa288fc07eee82 (patch) | |
tree | e820398591bfd1503456e877fa0c2bdd0f994959 /test/syscalls/syscall_test.go | |
parent | 833edbd10b49db1f934dcb2495dcb41c1310eea4 (diff) |
Open source system call tests.
PiperOrigin-RevId: 224886231
Change-Id: I0fccb4d994601739d8b16b1d4e6b31f40297fb22
Diffstat (limited to 'test/syscalls/syscall_test.go')
-rw-r--r-- | test/syscalls/syscall_test.go | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/test/syscalls/syscall_test.go b/test/syscalls/syscall_test.go new file mode 100644 index 000000000..8463289fe --- /dev/null +++ b/test/syscalls/syscall_test.go @@ -0,0 +1,245 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package syscall_test runs the syscall test suites in gVisor containers. It +// is meant to be run with "go test", and will panic if run on its own. +package syscall_test + +import ( + "flag" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "syscall" + "testing" + + "golang.org/x/sys/unix" + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/container" + "gvisor.googlesource.com/gvisor/runsc/specutils" + "gvisor.googlesource.com/gvisor/runsc/test/testutil" + "gvisor.googlesource.com/gvisor/test/syscalls/gtest" +) + +// Location of syscall tests, relative to the repo root. +const testDir = "test/syscalls/linux" + +var ( + testName = flag.String("test-name", "", "name of test binary to run") + debug = flag.Bool("debug", false, "enable debug logs") + strace = flag.Bool("strace", false, "enable strace logs") + platform = flag.String("platform", "ptrace", "platform to run on") + parallel = flag.Bool("parallel", false, "run tests in parallel") +) + +func TestSyscalls(t *testing.T) { + if *testName == "" { + t.Fatalf("test-name flag must be provided") + } + + // Get path to test binary. + fullTestName := filepath.Join(testDir, *testName) + testBin, err := testutil.FindFile(fullTestName) + if err != nil { + t.Fatalf("FindFile(%q) failed: %v", fullTestName, err) + } + + // Get all test cases in each binary. + testCases, err := gtest.ParseTestCases(testBin) + if err != nil { + t.Fatalf("ParseTestCases(%q) failed: %v", testBin, err) + } + + // 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 { + t.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 { + t.Fatalf("error setting file flags for %v: %v", f, err) + } + } + } + + for _, tc := range testCases { + // Capture tc. + tc := tc + + testName := fmt.Sprintf("%s_%s", tc.Suite, tc.Name) + t.Run(testName, func(t *testing.T) { + if *parallel { + t.Parallel() + } + + if *platform == "native" { + // Run the test case on host. + runTestCaseNative(testBin, tc, t) + return + } + + // Run the test case in runsc. + runTestCaseRunsc(testBin, tc, t) + }) + } +} + +// runTestCaseNative runs the test case directly on the host machine. +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 { + t.Fatalf("could not create temp dir: %v", err) + } + defer os.RemoveAll(tmpDir) + + // Replace TEST_TMPDIR in the current environment with something + // unique. + env := os.Environ() + newEnvVar := "TEST_TMPDIR=" + tmpDir + var found bool + for i, kv := range env { + if strings.HasPrefix(kv, "TEST_TMPDIR=") { + env[i] = newEnvVar + found = true + break + } + } + if !found { + env = append(env, newEnvVar) + } + // Remove the TEST_PREMATURE_EXIT_FILE variable and XML_OUTPUT_FILE + // from the environment. + env = filterEnv(env, []string{"TEST_PREMATURE_EXIT_FILE", "XML_OUTPUT_FILE"}) + + cmd := exec.Command(testBin, gtest.FilterTestFlag+"="+tc.FullName()) + cmd.Env = env + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + ws := err.(*exec.ExitError).Sys().(syscall.WaitStatus) + t.Errorf("test %q exited with status %d, want 0", tc.FullName(), ws.ExitStatus()) + } +} + +// runsTestCaseRunsc runs the test case in runsc. +func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) { + rootDir, err := testutil.SetupRootDir() + if err != nil { + t.Fatalf("SetupRootDir failed: %v", err) + } + defer os.RemoveAll(rootDir) + + conf := testutil.TestConfig() + conf.RootDir = rootDir + conf.Debug = *debug + conf.Strace = *strace + p, err := boot.MakePlatformType(*platform) + if err != nil { + t.Fatalf("error getting platform %q: %v", *platform, err) + } + conf.Platform = p + + // Run a new container with the test executable and filter for the + // given test suite and name. + spec := testutil.NewSpecWithArgs(testBin, gtest.FilterTestFlag+"="+tc.FullName()) + + // Mark the root as writeable, as some tests attempt to + // write to the rootfs, and expect EACCES, not EROFS. + spec.Root.Readonly = false + + // Set environment variable that indicates we are + // running in gVisor and with the given platform. + platformVar := "TEST_ON_GVISOR" + env := append(os.Environ(), platformVar+"="+*platform) + + // Remove the TEST_PREMATURE_EXIT_FILE variable and XML_OUTPUT_FILE + // from the environment. + env = filterEnv(env, []string{"TEST_PREMATURE_EXIT_FILE", "XML_OUTPUT_FILE"}) + + // Set TEST_TMPDIR to /tmp, as some of the syscall tests require it to + // be backed by tmpfs. + for i, kv := range env { + if strings.HasPrefix(kv, "TEST_TMPDIR=") { + env[i] = "TEST_TMPDIR=/tmp" + break + } + } + + spec.Process.Env = env + + bundleDir, err := testutil.SetupBundleDir(spec) + if err != nil { + t.Fatalf("SetupBundleDir failed: %v", err) + } + defer os.RemoveAll(bundleDir) + + id := testutil.UniqueContainerID() + log.Infof("Running test %q in container %q", tc.FullName(), id) + specutils.LogSpec(spec) + ws, err := container.Run(id, spec, conf, bundleDir, "", "", "") + if err != nil { + t.Fatalf("container.Run failed: %v", err) + } + if got := ws.ExitStatus(); got != 0 { + t.Errorf("test %q exited with status %d, want 0", tc.FullName(), ws.ExitStatus()) + } +} + +// filterEnv returns an environment with the blacklisted variables removed. +func filterEnv(env, blacklist []string) []string { + var out []string + for _, kv := range env { + ok := true + for _, k := range blacklist { + if strings.HasPrefix(kv, k+"=") { + ok = false + break + } + } + if ok { + out = append(out, kv) + } + } + return out +} + +func TestMain(m *testing.M) { + flag.Parse() + + log.SetLevel(log.Warning) + if *debug { + log.SetLevel(log.Debug) + } + if err := testutil.ConfigureExePath(); err != nil { + panic(err.Error()) + } + + if *platform != "native" { + // The native tests don't expect to be running as root, but + // runsc requires it. + testutil.RunAsRoot() + } + + os.Exit(m.Run()) +} |