-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
skip: introduce a skip package with utilities for skipping test cases
- Loading branch information
Showing
3 changed files
with
273 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
// Copyright (c) The Test Authors | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
// Package skip provides helper functions for skipping test cases in | ||
// environments that meet certain conditions. | ||
package skip | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"os" | ||
"os/exec" | ||
"regexp" | ||
"runtime" | ||
"strings" | ||
"testing" | ||
"time" | ||
) | ||
|
||
// OperatingSystem will skip the test if the Go runtime detects the operating system | ||
// matches one of the given names. | ||
func OperatingSystem(t *testing.T, names ...string) { | ||
os := runtime.GOOS | ||
for _, name := range names { | ||
if os == strings.ToLower(name) { | ||
t.Skipf("operating system excluded from tests %q", os) | ||
} | ||
} | ||
} | ||
|
||
// NotOperatingSystem will skip the test if the Go runtime detects the operating | ||
// system does not match one of the given names. | ||
func NotOperatingSystem(t *testing.T, names ...string) { | ||
os := runtime.GOOS | ||
for _, name := range names { | ||
if os == strings.ToLower(name) { | ||
return | ||
} | ||
} | ||
t.Skipf("operating excluded from tests %q", os) | ||
} | ||
|
||
// RootUser will skip the test if the test is being run as the root user. | ||
// | ||
// Uses the effective UID value to determine user. | ||
func RootUser(t *testing.T) { | ||
euid := os.Geteuid() | ||
if euid == 0 { | ||
t.Skip("test must not run as root") | ||
} | ||
} | ||
|
||
// NotRootUser will skip the test if the test is not being run as the root user. | ||
// | ||
// Uses the effective UID value to determine user. | ||
func NotRootUser(t *testing.T) { | ||
euid := os.Geteuid() | ||
if euid != 0 { | ||
t.Skip("test must run as root") | ||
} | ||
} | ||
|
||
// Architecture will skip the test if the Go runtime detects the system | ||
// architecture matches one of the given names. | ||
func Architecture(t *testing.T, names ...string) { | ||
arch := runtime.GOARCH | ||
for _, name := range names { | ||
if arch == strings.ToLower(name) { | ||
t.Skipf("arch excluded from tests %q", arch) | ||
} | ||
} | ||
} | ||
|
||
// NotArchitecture will skip the test if the Go runtime the system architecture | ||
// does not match one of the given names. | ||
func NotArchitecture(t *testing.T, names ...string) { | ||
arch := runtime.GOARCH | ||
for _, name := range names { | ||
if arch == strings.ToLower(name) { | ||
return | ||
} | ||
} | ||
t.Skipf("arch excluded from tests %q", arch) | ||
} | ||
|
||
func cmdAvailable(name string) bool { | ||
_, err := exec.LookPath(name) | ||
return !errors.Is(err, exec.ErrNotFound) | ||
} | ||
|
||
// CommandNotFound will skip the test if the given command cannot be found on | ||
// the system. | ||
func CommandNotFound(t *testing.T, command string) { | ||
if !cmdAvailable(command) { | ||
t.Skipf("command %q not detected on system", command) | ||
} | ||
} | ||
|
||
// DockerNotFound will skip the test if the docker command cannot be found on | ||
// the system path. | ||
func DockerNotFound(t *testing.T) { | ||
if !cmdAvailable("docker") { | ||
t.Skip("docker not detected on system") | ||
} | ||
} | ||
|
||
// PodmanNotFound will skip the test if the podman command cannot be found on | ||
// the system path. | ||
func PodmanNotFound(t *testing.T) { | ||
if !cmdAvailable("podman") { | ||
t.Skip("podman not detected on system") | ||
} | ||
} | ||
|
||
// MinimumCores will skip the test if the system does not meet the minimum | ||
// number of CPU cores. | ||
func MinimumCores(t *testing.T, num int) { | ||
cpus := runtime.NumCPU() | ||
if cpus < num { | ||
t.Skip("system does not meet minimum cpu cores") | ||
} | ||
} | ||
|
||
// MaximumCores will skip the test if the system does not meet the maximum | ||
// number of cpu cores. | ||
func MaximumCores(t *testing.T, num int) { | ||
cpus := runtime.NumCPU() | ||
if cpus > num { | ||
t.Skip("system exceeds maximum cpu cores") | ||
} | ||
} | ||
|
||
// CgroupsVersion will skip the test if the system does not match the given | ||
// cgroups version. | ||
func CgroupsVersion(t *testing.T, version int) { | ||
if runtime.GOOS != "linux" { | ||
t.Skip("cgroups requires linux") | ||
} | ||
|
||
mType := mountType(t, "/sys/fs/cgroup") | ||
|
||
switch mType { | ||
case "tmpfs": | ||
// this is a cgroups v1 system | ||
if version == 2 { | ||
t.Skip("system does not match cgroups version 2") | ||
} | ||
case "cgroup2": | ||
// this is a cgroups v2 system | ||
if version == 1 { | ||
t.Skip("system does not match cgroups version 1") | ||
} | ||
default: | ||
t.Fatalf("unknown cgroups mount type %q", mType) | ||
} | ||
} | ||
|
||
func mountType(t *testing.T, path string) string { | ||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) | ||
defer cancel() | ||
|
||
cmd := exec.CommandContext(ctx, "df", "-T", path) | ||
b, err := cmd.CombinedOutput() | ||
if err != nil { | ||
t.Fatalf("unable to run df command: %v", err) | ||
} | ||
|
||
// need the first token of the second line | ||
output := string(b) | ||
tokenRe := regexp.MustCompile(`on\s+([\w]+)\s+`) | ||
results := tokenRe.FindStringSubmatch(output) | ||
if len(results) != 2 { | ||
t.Fatal("no mount type for path") | ||
} | ||
return results[1] | ||
} | ||
|
||
// TODO | ||
// - landlock detection |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Copyright (c) The Test Authors | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
//go:build linux || macos | ||
|
||
package skip | ||
|
||
import "testing" | ||
|
||
func TestSkip_OperatingSystem(t *testing.T) { | ||
OperatingSystem(t, "darwin", "linux", "windows") | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_NotOperatingSystem(t *testing.T) { | ||
NotOperatingSystem(t, "windows") | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_RootUser(t *testing.T) { | ||
t.Skip("requires root") | ||
RootUser(t) | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_NotRootUser(t *testing.T) { | ||
NotRootUser(t) | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_Architecture(t *testing.T) { | ||
Architecture(t, "arm64", "amd64") | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_NotArchitecture(t *testing.T) { | ||
NotArchitecture(t, "itanium", "mips") | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_DockerNotFound(t *testing.T) { | ||
t.Skip("skip docker test") // gha runner | ||
|
||
DockerNotFound(t) | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_PodmanNotFound(t *testing.T) { | ||
t.Skip("skip podman test") // gha runner | ||
|
||
PodmanNotFound(t) | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_CommandNotFound(t *testing.T) { | ||
CommandNotFound(t, "doesnotexist") | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_MinimumCores(t *testing.T) { | ||
MinimumCores(t, 200) | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_MaximumCores(t *testing.T) { | ||
MaximumCores(t, 2) | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_CgroupsVersion(t *testing.T) { | ||
CgroupsVersion(t, 1) | ||
t.Fatal("expected to skip test") | ||
} |