-
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
362 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,242 @@ | ||
// 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) | ||
} | ||
|
||
// UserRoot will skip the test if the test is being run as the root user. | ||
// | ||
// Uses the effective UID value to determine user. | ||
func UserRoot(t *testing.T) { | ||
euid := os.Geteuid() | ||
if euid == 0 { | ||
t.Skip("test must not run as root") | ||
} | ||
} | ||
|
||
// NotUserRoot will skip the test if the test is not being run as the root user. | ||
// | ||
// Uses the effective UID value to determine user. | ||
func NotUserRoot(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] | ||
} | ||
|
||
// EnvironmentVariableSet will skip the test if the given environment variable | ||
// is set to any value. | ||
func EnvironmentVariableSet(t *testing.T, name string) { | ||
if name == "" { | ||
t.Fatal("environment variable name must be set") | ||
} | ||
|
||
_, exists := os.LookupEnv(name) | ||
if exists { | ||
t.Skipf("environment variable %q is set", name) | ||
} | ||
} | ||
|
||
// EnvironmentVariableNotSet will skip the test if the given environment | ||
// variable is not set in the system environment. | ||
func EnvironmentVariableNotSet(t *testing.T, name string) { | ||
if name == "" { | ||
t.Fatal("environment variable name must be set") | ||
} | ||
_, exists := os.LookupEnv(name) | ||
if !exists { | ||
t.Skipf("environment variable %q is not set", name) | ||
} | ||
} | ||
|
||
// EnvironmentVariableMatches will skip the test if the system environment | ||
// matches one of the given environment variable values. | ||
func EnvironmentVariableMatches(t *testing.T, name string, values ...string) { | ||
if len(values) == 0 { | ||
t.Fatal("no possible environment variable values given") | ||
} | ||
|
||
// if not set in the system, then it must not match | ||
actual, exists := os.LookupEnv(name) | ||
if !exists { | ||
return | ||
} | ||
|
||
for _, value := range values { | ||
if value == actual { | ||
t.Skipf("environment variable %q matches %q", name, value) | ||
} | ||
} | ||
} | ||
|
||
// EnvironmentVariableNotMatches will skip the test if the system environment | ||
// does not match one of the given environment variable values. | ||
func EnvironmentVariableNotMatches(t *testing.T, name string, values ...string) { | ||
if len(values) == 0 { | ||
t.Fatal("no possible environment variable values given") | ||
} | ||
|
||
actual, exists := os.LookupEnv(name) | ||
if !exists { | ||
t.Skipf("environment variable %q not set", name) | ||
} | ||
|
||
for _, value := range values { | ||
if actual == value { | ||
return | ||
} | ||
} | ||
|
||
t.Skipf("environment variable %q does not match values (is %q)", name, actual) | ||
} |
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,99 @@ | ||
// Copyright (c) The Test Authors | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
//go:build linux || darwin | ||
|
||
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_UserRoot(t *testing.T) { | ||
t.Skip("requires root") | ||
UserRoot(t) | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_NotUserRoot(t *testing.T) { | ||
NotUserRoot(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") | ||
} | ||
|
||
func TestSkip_EnvironmentVariableSet(t *testing.T) { | ||
t.Setenv("EXAMPLE", "value") | ||
|
||
EnvironmentVariableSet(t, "EXAMPLE") | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_EnvironmentVariableNotSet(t *testing.T) { | ||
EnvironmentVariableNotSet(t, "DOESNOTEXIST") | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_EnvironmentVariableMatches(t *testing.T) { | ||
t.Setenv("EXAMPLE", "foo") | ||
|
||
EnvironmentVariableMatches(t, "EXAMPLE", "bar", "foo", "baz") | ||
t.Fatal("expected to skip test") | ||
} | ||
|
||
func TestSkip_EnvironmentVariableNotMatches(t *testing.T) { | ||
t.Setenv("EXAMPLE", "other") | ||
|
||
EnvironmentVariableNotMatches(t, "EXAMPLE", "bar", "foo", "baz") | ||
t.Fatal("expected to skip test") | ||
} |