From c18b9d58aa0c80748a4fe16e22d6cc41d149bd00 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 1 Nov 2021 09:38:42 -0700 Subject: [PATCH] tstest/archtest: add GOARCH-specific tests, run via qemu-user Updates #3233 Change-Id: Ia224c90490d41e50a1d547eeea709b0d9171c1f9 Signed-off-by: Brad Fitzpatrick --- .github/workflows/linux.yml | 9 ++++ tstest/archtest/archtest_test.go | 32 ++++++++++++++ tstest/archtest/qemu_test.go | 75 ++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 tstest/archtest/archtest_test.go create mode 100644 tstest/archtest/qemu_test.go diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index a57e47bb2..80f14615e 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -28,6 +28,15 @@ jobs: - name: Basic build run: go build ./cmd/... + - name: Get QEMU + run: | + # The qemu in Ubuntu 20.04 (Focal) is too old; we need 5.x something + # to run Go binaries. 5.2.0 (Debian bullseye) empirically works, and + # use this PPA which brings in a modern qemu. + sudo add-apt-repository -y ppa:jacob/virtualisation + sudo apt-get -y update + sudo apt-get -y install qemu-user + - name: Run tests on linux run: go test -bench=. -benchtime=1x ./... diff --git a/tstest/archtest/archtest_test.go b/tstest/archtest/archtest_test.go new file mode 100644 index 000000000..300e7fc51 --- /dev/null +++ b/tstest/archtest/archtest_test.go @@ -0,0 +1,32 @@ +// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package archtest + +import ( + "runtime" + "testing" + + "inet.af/netstack/atomicbitops" +) + +// tests netstack's AlignedAtomicInt64. +func TestAlignedAtomicInt64(t *testing.T) { + type T struct { + A atomicbitops.AlignedAtomicInt64 + x int32 + B atomicbitops.AlignedAtomicInt64 + } + + t.Logf("I am %v/%v\n", runtime.GOOS, runtime.GOARCH) + var x T + x.A.Store(1) + x.B.Store(2) + if got, want := x.A.Load(), int64(1); got != want { + t.Errorf("A = %v; want %v", got, want) + } + if got, want := x.B.Load(), int64(2); got != want { + t.Errorf("A = %v; want %v", got, want) + } +} diff --git a/tstest/archtest/qemu_test.go b/tstest/archtest/qemu_test.go new file mode 100644 index 000000000..758dfa894 --- /dev/null +++ b/tstest/archtest/qemu_test.go @@ -0,0 +1,75 @@ +// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && amd64 && !race +// +build linux,amd64,!race + +package archtest + +import ( + "bytes" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func TestInQemu(t *testing.T) { + t.Parallel() + type Arch struct { + Goarch string // GOARCH value + Qarch string // qemu name + } + arches := []Arch{ + {"arm", "arm"}, + {"arm64", "aarch64"}, + {"mips", "mips"}, + //{"mipsle", "mipsel"}, // Broken: https://github.com/tailscale/tailscale/issues/3233 + {"mips64", "mips64"}, + {"mips64le", "mips64el"}, + {"386", "386"}, + } + inCI := os.Getenv("CI") == "true" + for _, arch := range arches { + arch := arch + t.Run(arch.Goarch, func(t *testing.T) { + t.Parallel() + qemuUser := "qemu-" + arch.Qarch + execVia := qemuUser + if arch.Goarch == "386" { + execVia = "" // amd64 can run it fine + } else { + look, err := exec.LookPath(qemuUser) + if err != nil { + if inCI { + t.Fatalf("in CI and qemu not available: %v", err) + } + t.Skipf("%s not found; skipping test. error was: %v", qemuUser, err) + } + t.Logf("using %v", look) + } + cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), + "test", + "--exec="+execVia, + "-v", + "tailscale.com/tstest/archtest", + ) + cmd.Env = append(os.Environ(), "GOARCH="+arch.Goarch) + out, err := cmd.CombinedOutput() + if err != nil { + if strings.Contains(string(out), "fatal error: sigaction failed") && !inCI { + t.Skip("skipping; qemu too old. use 5.x.") + } + t.Errorf("failed: %s", out) + } + sub := fmt.Sprintf("I am linux/%s", arch.Goarch) + if !bytes.Contains(out, []byte(sub)) { + t.Errorf("output didn't contain %q: %s", sub, out) + } + }) + } +}