summaryrefslogtreecommitdiff
path: root/Magefile.go
diff options
context:
space:
mode:
Diffstat (limited to 'Magefile.go')
-rw-r--r--Magefile.go249
1 files changed, 44 insertions, 205 deletions
diff --git a/Magefile.go b/Magefile.go
index a708fb7..25b1a97 100644
--- a/Magefile.go
+++ b/Magefile.go
@@ -5,7 +5,6 @@ package main
import (
"bufio"
- "encoding/json"
"errors"
"fmt"
"go/format"
@@ -203,13 +202,15 @@ func TestWithName() error {
fmt.Println("Running integration test", testName, "(requires root)...")
env := goEnv()
forwardEnv(env, "HOME", "GOPATH", "GOMODCACHE", "PATH", "GOTOOLCHAIN")
- return runGoTestWithProgress(env,
- "./integrationtests/...",
- "-run", "^"+testName+"$",
- "-failfast",
- "-timeout=30m",
- "-count=1",
- "-json",
+ if err := compileIntegrationTestBinary(env); err != nil {
+ return err
+ }
+ return runIntegrationTestBinary(env,
+ "-test.run", "^"+testName+"$",
+ "-test.failfast",
+ "-test.timeout=30m",
+ "-test.count=1",
+ "-test.v",
)
}
return sh.RunWithV(goEnv(), "go", "test", "./...", "-run", "^"+testName+"$", "-v", "-failfast")
@@ -519,6 +520,29 @@ func IntegrationTestSerial() error {
return runIntegrationTests(false)
}
+func compileIntegrationTestBinary(env map[string]string) error {
+ return sh.RunWithV(env, "go", "test", "-c", "./integrationtests/...", "-o", "integrationtests.test")
+}
+
+func runIntegrationTestBinary(env map[string]string, args ...string) error {
+ envList := make([]string, 0, len(env))
+ for k, v := range env {
+ envList = append(envList, k+"="+v)
+ }
+ slices.Sort(envList)
+
+ cmd := exec.Command("./integrationtests.test", args...)
+ cmd.Dir = "integrationtests"
+ cmd.Env = append(os.Environ(), envList...)
+ if os.Geteuid() == 0 {
+ return cmd.Run()
+ }
+ sudoCmd := exec.Command("sudo", append([]string{"-n", "-E", "./integrationtests.test"}, args...)...)
+ sudoCmd.Dir = "integrationtests"
+ sudoCmd.Env = cmd.Env
+ return sudoCmd.Run()
+}
+
func runIntegrationTests(parallel bool) error {
mg.SerialDeps(All)
if err := buildWorkloadBinary(); err != nil {
@@ -528,16 +552,19 @@ func runIntegrationTests(parallel bool) error {
env := goEnv()
forwardEnv(env, "HOME", "GOPATH", "GOMODCACHE", "GOTOOLCHAIN")
+ if err := compileIntegrationTestBinary(env); err != nil {
+ return err
+ }
+
timeout := "30m"
if !parallel {
timeout = "90m"
}
args := []string{
- "./integrationtests/...",
- "-failfast",
- "-timeout=" + timeout,
- "-count=1",
+ "-test.failfast",
+ "-test.timeout=" + timeout,
+ "-test.count=1",
}
if parallel {
@@ -547,13 +574,12 @@ func runIntegrationTests(parallel bool) error {
}
env[integrationParallelE] = "1"
fmt.Printf("Running integration tests in parallel (requires root, parallel=%d)...\n", parallelism)
- args = append(args, "-parallel", strconv.Itoa(parallelism))
+ args = append(args, "-test.parallel", strconv.Itoa(parallelism))
} else {
fmt.Println("Running integration tests serially (requires root)...")
}
- args = append(args, "-json")
- return runGoTestWithProgress(env, args...)
+ return runIntegrationTestBinary(env, args...)
}
func resolveIntegrationParallelism() (int, error) {
@@ -810,7 +836,7 @@ func sudoOutput(cmd string, args ...string) (string, error) {
if os.Geteuid() == 0 {
return sh.Output(cmd, args...)
}
- return sh.Output("sudo", append([]string{cmd}, args...)...)
+ return sh.Output("sudo", append([]string{"-n", cmd}, args...)...)
}
func sudoRunWithEnv(env map[string]string, cmd string, args ...string) error {
@@ -822,8 +848,8 @@ func sudoRunWithEnv(env map[string]string, cmd string, args ...string) error {
keys = append(keys, k)
}
slices.Sort(keys)
- sudoArgs := make([]string, 0, 1+len(keys)+1+len(args))
- sudoArgs = append(sudoArgs, "env")
+ sudoArgs := make([]string, 0, 2+len(keys)+1+len(args))
+ sudoArgs = append(sudoArgs, "-n", "env")
for _, k := range keys {
sudoArgs = append(sudoArgs, k+"="+env[k])
}
@@ -897,193 +923,6 @@ func sortLinesWithLocale(lines []string) (string, error) {
return string(output), nil
}
-type goTestEvent struct {
- Action string `json:"Action"`
- Package string `json:"Package"`
- Test string `json:"Test"`
- Output string `json:"Output"`
-}
-
-// buildGoTestCmd constructs the exec.Cmd for running `go test -json` with the
-// given env vars. When not root it wraps the command with `sudo env KEY=VAL …`
-// so elevated integration tests inherit the correct CGO/LIBBPFGO environment.
-func buildGoTestCmd(env map[string]string, cmdArgs []string) *exec.Cmd {
- if os.Geteuid() == 0 {
- cmd := exec.Command("go", cmdArgs...)
- cmd.Env = append(os.Environ(), envToList(env)...)
- return cmd
- }
- keys := make([]string, 0, len(env))
- for k := range env {
- keys = append(keys, k)
- }
- slices.Sort(keys)
- sudoArgs := make([]string, 0, 1+len(keys)+1+len(cmdArgs))
- sudoArgs = append(sudoArgs, "env")
- for _, k := range keys {
- sudoArgs = append(sudoArgs, k+"="+env[k])
- }
- sudoArgs = append(sudoArgs, "go")
- sudoArgs = append(sudoArgs, cmdArgs...)
- return exec.Command("sudo", sudoArgs...)
-}
-
-// startProgressTicker prints the set of currently-running tests every 15 s.
-// Call close(done) to stop the ticker goroutine.
-func startProgressTicker(running map[string]time.Time, done <-chan struct{}) {
- ticker := time.NewTicker(15 * time.Second)
- go func() {
- defer ticker.Stop()
- for {
- select {
- case <-done:
- return
- case <-ticker.C:
- if len(running) == 0 {
- fmt.Println("Integration tests still running... waiting for next test event")
- continue
- }
- names := make([]string, 0, len(running))
- for k := range running {
- names = append(names, k)
- }
- slices.Sort(names)
- fmt.Println("Integration tests running:", strings.Join(names, ", "))
- }
- }
- }()
-}
-
-// drainTestEvents reads JSON test events from scanner, updates the running map,
-// and prints human-readable RUN/PASS/FAIL/SKIP/LOG lines.
-func drainTestEvents(scanner *bufio.Scanner, running map[string]time.Time) {
- for scanner.Scan() {
- line := scanner.Bytes()
- var ev goTestEvent
- if err := json.Unmarshal(line, &ev); err != nil {
- fmt.Println(string(line))
- continue
- }
- if ev.Test == "" {
- continue
- }
- key := ev.Package + "/" + ev.Test
- switch ev.Action {
- case "run":
- running[key] = time.Now()
- fmt.Println("RUN ", key)
- case "pass":
- delete(running, key)
- fmt.Println("PASS", key)
- case "fail":
- delete(running, key)
- fmt.Println("FAIL", key)
- case "skip":
- delete(running, key)
- fmt.Println("SKIP", key)
- case "output":
- msg := strings.TrimSpace(ev.Output)
- if msg != "" && shouldPrintTestLog(msg) {
- fmt.Println("LOG ", key, "-", msg)
- }
- }
- }
-}
-
-// runGoTestWithProgress runs `go test -json` (via sudo when not root), streams
-// progress to stdout every 15 s, and returns a non-nil error on test failure.
-func runGoTestWithProgress(env map[string]string, args ...string) error {
- cmd := buildGoTestCmd(env, append([]string{"test"}, args...))
-
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- return err
- }
- stderr, err := cmd.StderrPipe()
- if err != nil {
- return err
- }
- if err := cmd.Start(); err != nil {
- return err
- }
-
- // Forward stderr from the test binary so build errors are always visible.
- go func() { _, _ = io.Copy(os.Stderr, stderr) }()
-
- scanner := bufio.NewScanner(stdout)
- scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024)
-
- running := map[string]time.Time{}
- done := make(chan struct{})
- startProgressTicker(running, done)
-
- drainTestEvents(scanner, running)
- close(done)
-
- if err := scanner.Err(); err != nil {
- return err
- }
- return cmd.Wait()
-}
-
-func envToList(env map[string]string) []string {
- if len(env) == 0 {
- return nil
- }
- out := make([]string, 0, len(env))
- for k, v := range env {
- out = append(out, k+"="+v)
- }
- slices.Sort(out)
- return out
-}
-
-func shouldPrintTestLog(msg string) bool {
- // Always keep error/failure lines.
- if strings.Contains(msg, "--- FAIL:") ||
- strings.Contains(msg, " FAIL ") ||
- strings.Contains(msg, "panic:") ||
- strings.Contains(strings.ToLower(msg), "error") ||
- strings.Contains(strings.ToLower(msg), "expected event not found") {
- return true
- }
-
- // Drop high-volume attach/debug noise from ior startup in integration tests.
- noisePrefixes := []string{
- "=== RUN",
- "___",
- "|_ _|",
- "| |",
- "|___",
- "v0.0.0",
- "libbpf:",
- "Attaching tracepoint ",
- "Attached prog handle_ ",
- "Attached tracepoint",
- "Attaching sys_",
- "Not attaching sys_",
- "Collecting flame graph stats",
- "Starting flamegraph worker",
- "Waiting for stats to be ready",
- "Stopping event loop",
- "Waiting for flamegraph",
- "Worker ",
- "Writing ",
- "Good bye...",
- "Statistics:",
- "duration:",
- "tracepoints:",
- "syscalls:",
- "syscalls after filter:",
- }
- for _, p := range noisePrefixes {
- if strings.HasPrefix(msg, p) {
- return false
- }
- }
- return true
-}
-
func isIntegrationTest(testName string) (bool, error) {
out, err := sh.OutputWith(goEnv(), "go", "test", "./integrationtests/...", "-list", ".")
if err != nil {