From 694de014c679ddc2332db3ae8c21930b0ba570e1 Mon Sep 17 00:00:00 2001 From: Qiheng He Date: Thu, 1 Oct 2026 02:09:53 +0800 Subject: [PATCH] test(e2e): rewrite Linux matrix runner with testcontainers-go --- .github/workflows/e2e.yml | 5 +- docs/e2e.md | 25 ++++- tests/e2e/linux/Dockerfile | 28 ++--- tests/e2e/linux/compose.yaml | 44 ++++++++ tests/e2e/linux/config.sh | 54 ---------- tests/e2e/linux/e2e.sh | 70 ------------- tests/e2e/linux/e2e_test.go | 183 +++++++++++++++++++++++++++++++++ tests/e2e/linux/exec.go | 88 ++++++++++++++++ tests/e2e/linux/go.mod | 58 +++++++++++ tests/e2e/linux/go.sum | 122 ++++++++++++++++++++++ tests/e2e/linux/install.go | 18 ++++ tests/e2e/linux/install.sh | 23 ----- tests/e2e/linux/lib.sh | 110 -------------------- tests/e2e/linux/matrix.go | 146 ++++++++++++++++++++++++++ tests/e2e/linux/matrix_test.go | 172 +++++++++++++++++++++++++++++++ tests/e2e/linux/run.sh | 81 --------------- tests/e2e/linux/setup.go | 120 +++++++++++++++++++++ tests/e2e/linux/setup.sh | 54 ---------- tests/e2e/linux/verify.go | 130 +++++++++++++++++++++++ tests/e2e/linux/verify.sh | 128 ----------------------- tests/e2e/windows/lib.ps1 | 4 +- 21 files changed, 1122 insertions(+), 541 deletions(-) create mode 100644 tests/e2e/linux/compose.yaml delete mode 100644 tests/e2e/linux/config.sh delete mode 100755 tests/e2e/linux/e2e.sh create mode 100644 tests/e2e/linux/e2e_test.go create mode 100644 tests/e2e/linux/exec.go create mode 100644 tests/e2e/linux/go.mod create mode 100644 tests/e2e/linux/go.sum create mode 100644 tests/e2e/linux/install.go delete mode 100755 tests/e2e/linux/install.sh delete mode 100644 tests/e2e/linux/lib.sh create mode 100644 tests/e2e/linux/matrix.go create mode 100644 tests/e2e/linux/matrix_test.go delete mode 100755 tests/e2e/linux/run.sh create mode 100644 tests/e2e/linux/setup.go delete mode 100644 tests/e2e/linux/setup.sh create mode 100644 tests/e2e/linux/verify.go delete mode 100755 tests/e2e/linux/verify.sh diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 93e5259..91bb08d 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -24,7 +24,10 @@ jobs: timeout-minutes: 60 steps: - uses: actions/checkout@v7 - - run: bash tests/e2e/linux/e2e.sh + # No host Go anywhere: the suite runs from the Compose runner service + # driving the runner daemon through the mounted socket (same invocation + # as docs/e2e.md), so CI dogfoods the documented setup. + - run: docker compose -f tests/e2e/linux/compose.yaml run --rm e2e e2e-windows: if: github.repository == 'version-fox/vfox-flutter' diff --git a/docs/e2e.md b/docs/e2e.md index 4a1276c..2df99d4 100644 --- a/docs/e2e.md +++ b/docs/e2e.md @@ -6,15 +6,29 @@ each `vfox` x `flavor` x `mirror` combination gets its own throwaway container. ## Ubuntu 26.04.1 -Assume that Docker Engine (in either rootful or rootless mode) is already installed. - -Execute in Bash, +Only Docker Engine (in either rootful or rootless mode) is required on the +host. Execute in Bash, ```bash sudo apt install --assume-yes git git clone git@github.com:version-fox/vfox-flutter.git cd ./vfox-flutter/ -bash tests/e2e/linux/e2e.sh +docker compose -f tests/e2e/linux/compose.yaml run --rm e2e +``` + +With rootless Docker the daemon socket lives elsewhere; point `DOCKER_SOCK` +at it instead: + +```bash +DOCKER_SOCK="$XDG_RUNTIME_DIR/docker.sock" \ + docker compose -f tests/e2e/linux/compose.yaml run --rm e2e +``` + +To check the `vfox` x `flavor` x `mirror` matrix expansion without +starting containers, override the service command: + +```bash +docker compose -f tests/e2e/linux/compose.yaml run --rm e2e go test -short ./... ``` On ARM64 it runs the official flavor only: OpenHarmony publishes no `linux-arm64` Dart SDK (see `docs/ohos.md`), so the @@ -28,7 +42,8 @@ override the architecture: ```bash docker run --privileged --rm tonistiigi/binfmt --install all -ARCH=arm64 bash tests/e2e/linux/e2e.sh +cd ./vfox-flutter/ +ARCH=arm64 docker compose -f tests/e2e/linux/compose.yaml run --rm e2e ``` ## Windows 11 Pro diff --git a/tests/e2e/linux/Dockerfile b/tests/e2e/linux/Dockerfile index 569f50d..ac6e013 100644 --- a/tests/e2e/linux/Dockerfile +++ b/tests/e2e/linux/Dockerfile @@ -2,21 +2,23 @@ FROM ubuntu:26.04 ENV DEBIAN_FRONTEND=noninteractive -RUN </dev/null || exit 0 -fi -VFOX_E2E_CONFIG_SH=1 - -DEFAULT_OFFICIAL_VERSION="3.47.4" -DEFAULT_OHOS_VERSION="3.41.10-ohos-1.0.0" -DEFAULT_MIRROR_CN="https://storage.flutter-io.cn" - -# resolve_flutter_version honours $FLUTTER_VERSION / $OHOS_VERSION. -resolve_flutter_version() { - local flavor="$1" - local version="" - case "$flavor" in - official) version="${FLUTTER_VERSION:-$DEFAULT_OFFICIAL_VERSION}" ;; - ohos) version="${OHOS_VERSION:-$DEFAULT_OHOS_VERSION}" ;; - *) - echo "FAIL unknown flavor $flavor" >&2 - return 1 - ;; - esac - printf '%s' "$version" -} - -# default_flavours : ohos has no arm64 artefacts, so arm64 runs -# official only (same rule as the Windows matrix). -default_flavours() { - if [[ "$1" == "arm64" ]]; then - echo "official" - else - echo "official ohos" - fi -} - -default_mirrors() { - if [[ "$1" == "arm64" ]]; then - echo "default" - else - echo "default $DEFAULT_MIRROR_CN" - fi -} - -# releases_index_for_mirror -releases_index_for_mirror() { - printf '%s/flutter_infra_release/releases/releases_%s.json' "${1%/}" "$2" -} diff --git a/tests/e2e/linux/e2e.sh b/tests/e2e/linux/e2e.sh deleted file mode 100755 index 48febb5..0000000 --- a/tests/e2e/linux/e2e.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bash -# Host-side matrix runner: build the image once, then fan out one container -# per (vfox version x flavor x mirror) combination, at most $max_jobs at a -# time. Logs are prefixed so parallel runs stay readable. -set -o errexit -o nounset -o pipefail - -here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# shellcheck source=lib.sh -source "$here/lib.sh" -# shellcheck source=config.sh -source "$here/config.sh" - -run_one() { - local image="$1" - local platform="$2" - local vfox="$3" - local flavor="$4" - local mirror="$5" - local prefix="vfox $vfox, $flavor, mirror $mirror, $platform" - local env=(--env "VFOX_VERSION=$vfox" --env "FLAVOR=$flavor") - if [[ "$mirror" != "default" ]]; then - env+=(--env "FLUTTER_STORAGE_BASE_URL=$mirror") - fi - docker run --rm --platform "$platform" "${env[@]}" "$image" 2>&1 | sed --expression "s|^|[$prefix] |" -} - -main() { - local repo arch platform image - repo="$(cd "$here/../../.." && pwd)" - arch="$(normalize_arch "${ARCH:-$(uname --machine)}")" - platform="linux/$arch" - image="vfox-flutter-e2e:linux-$arch" - - docker build --pull --platform "$platform" --file "$here/Dockerfile" --tag "$image" "$repo" - - # Space-separated env lists (e.g. FLAVOR="official ohos") are split - # into arrays up front, so the loops below need no word splitting. - # (read is a bash builtin with no long-option form for -r/-a.) - local -a foxes flavours mirrors - read -ra foxes <<< "${VFOX_VERSION:-latest main}" - read -ra flavours <<< "${FLAVOR:-$(default_flavours "$arch")}" - read -ra mirrors <<< "${MIRROR:-$(default_mirrors "$arch")}" - local mirror_explicit="${MIRROR:-}" - local max_jobs="${E2E_MAX_JOBS:-3}" - local failed=0 - - for vfox in "${foxes[@]}"; do - for flavor in "${flavours[@]}"; do - for mirror in "${mirrors[@]}"; do - # The mirror matrix only runs against latest vfox unless the - # caller explicitly pins MIRROR, to keep CI time bounded. - if [[ -z "$mirror_explicit" && "$mirror" != "default" && "$vfox" != "latest" ]]; then - continue - fi - run_one "$image" "$platform" "$vfox" "$flavor" "$mirror" & - while [[ "$(jobs -p | wc --lines)" -ge "$max_jobs" ]]; do - wait -n || failed=1 - done - done - done - done - - while [[ "$(jobs -p | wc --lines)" -gt 0 ]]; do - wait -n || failed=1 - done - - return "$failed" -} - -main "$@" diff --git a/tests/e2e/linux/e2e_test.go b/tests/e2e/linux/e2e_test.go new file mode 100644 index 0000000..e208804 --- /dev/null +++ b/tests/e2e/linux/e2e_test.go @@ -0,0 +1,183 @@ +package e2e + +import ( + "context" + "crypto/rand" + "encoding/hex" + "fmt" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/containerd/platforms" + specs "github.com/opencontainers/image-spec/specs-go/v1" + tc "github.com/testcontainers/testcontainers-go" + + client "github.com/moby/moby/client" +) + +// passThroughEnvs are host env knobs the in-container scripts (run.sh and +// friends) honour. Forwarded only when set, so unset stays default. +var passThroughEnvs = []string{ + "FLUTTER_VERSION", + "OHOS_VERSION", + "VFOX_FLUTTER_GITHUB_MIRROR", + "E2E_RETRY_ATTEMPTS", +} + +// TestE2E builds the image once, then runs one container per matrix combo. +// Bounded parallelism via t.Parallel + semaphore (like E2E_MAX_JOBS). +// +// Full matrix (~45min) runs in CI; locally scope it down, e.g.: +// VFOX_VERSION=latest FLAVOR=official MIRROR=default E2E_MAX_JOBS=1 go test -v +func TestE2E(t *testing.T) { + if testing.Short() { + t.Skip("skip container E2E in short mode") + } + + arch, err := detectArch() + if err != nil { + t.Fatal(err) + } + platform := "linux/" + arch + image := "vfox-flutter-e2e:linux-" + arch + + here, err := filepath.Abs(".") + if err != nil { + t.Fatal(err) + } + repoRoot := filepath.Join(here, "..", "..", "..") + + ctx := context.Background() + buildImage(t, ctx, repoRoot, image, platform) + + combos, err := matrix() + if err != nil { + t.Fatal(err) + } + if len(combos) == 0 { + t.Fatal("empty matrix: nothing to run") + } + + sem := make(chan struct{}, maxJobs()) + for _, c := range combos { + c := c + t.Run(c.slug(), func(t *testing.T) { + t.Parallel() + sem <- struct{}{} + defer func() { <-sem }() + runOne(t, ctx, image, platform, c) + }) + } +} + +// buildImage replicates `docker build --pull --platform $platform +// --file tests/e2e/linux/Dockerfile --tag $image $repoRoot`. +func buildImage(t *testing.T, ctx context.Context, repoRoot, image, platform string) { + t.Helper() + + provider, err := tc.NewDockerProvider() + if err != nil { + t.Fatalf("new docker provider: %v", err) + } + t.Cleanup(func() { _ = provider.Close() }) + + pf, err := platforms.Parse(platform) + if err != nil { + t.Fatalf("invalid platform %s: %v", platform, err) + } + + tag, err := provider.BuildImage(ctx, &tc.ContainerRequest{ + FromDockerfile: tc.FromDockerfile{ + Context: repoRoot, + Dockerfile: "tests/e2e/linux/Dockerfile", + Repo: "vfox-flutter-e2e", + Tag: image[strings.LastIndex(image, ":")+1:], + KeepImage: true, + BuildLogWriter: os.Stdout, + BuildOptionsModifier: func(opts *client.ImageBuildOptions) { + opts.Platforms = []specs.Platform{pf} + opts.PullParent = true + }, + }, + }) + if err != nil { + t.Fatalf("build image %s: %v", image, err) + } + if tag != image { + t.Fatalf("built tag %s, want %s", tag, image) + } +} + +// containerName returns a unique container name per run: the combo slug stays +// greppable in `docker ps`, the random suffix avoids clashes with leftovers +// from killed runs (the old bash runner used anonymous names). +func containerName(c combo) string { + var b [4]byte + if _, err := rand.Read(b[:]); err != nil { + return fmt.Sprintf("vfox-flutter-e2e-%s-%d", c.slug(), time.Now().UnixNano()) + } + return "vfox-flutter-e2e-" + c.slug() + "-" + hex.EncodeToString(b[:]) +} + +// runOne starts one long-lived container per combo and drives the whole +// E2E flow (setup -> install -> verify) through exec calls, asserting in Go. +// A combo passes when every phase exits 0 and the outputs carry the expected +// markers (same bar as the old run.sh and the Windows matrix). +func runOne(t *testing.T, ctx context.Context, image, platform string, c combo) { + t.Helper() + prefix := c.prefix(platform) + + env := map[string]string{ + "VFOX_VERSION": c.vfox, + "FLAVOR": c.flavor, + } + if c.mirror != "default" { + env["FLUTTER_STORAGE_BASE_URL"] = c.mirror + } + for _, name := range passThroughEnvs { + if v, ok := os.LookupEnv(name); ok && v != "" { + env[name] = v + } + } + + ctr, err := tc.GenericContainer(ctx, tc.GenericContainerRequest{ + ContainerRequest: tc.ContainerRequest{ + Image: image, + ImagePlatform: platform, + Name: containerName(c), + Env: env, + }, + Started: true, + }) + if err != nil { + t.Fatalf("[%s] start container: %v", prefix, err) + } + t.Cleanup(func() { + _ = ctr.Terminate(context.Background()) + }) + + // One overall budget per combo; the CI job timeout stays the outer bound. + ctx, cancel := context.WithTimeout(ctx, 45*time.Minute) + defer cancel() + + flavor := c.flavor + mirror := c.mirror + version := resolveFlutterVersion(t, flavor) + machine := strings.TrimSpace(execOK(ctx, t, ctr, "uname --machine")) + t.Logf("=== vfox %s, flutter %s, %s, mirror %s, %s ===", c.vfox, version, flavor, mirror, machine) + + checkMirrorReachable(ctx, t, ctr, flavor, mirror) + setupVfox(ctx, t, ctr, c.vfox) + checkBogusMirrorRejected(ctx, t, ctr, flavor, version) + checkBogusGithubMirrorRejected(ctx, t, ctr, flavor, version) + + installFlutterVersion(ctx, t, ctr, version) + + sdk := sdkDir(ctx, t, ctr) + verifySdkLayout(ctx, t, ctr, flavor, sdk) + verifyManifestDrift(ctx, t, ctr, version, sdk) + verifyToolchain(ctx, t, ctr, sdk) +} diff --git a/tests/e2e/linux/exec.go b/tests/e2e/linux/exec.go new file mode 100644 index 0000000..d0213de --- /dev/null +++ b/tests/e2e/linux/exec.go @@ -0,0 +1,88 @@ +package e2e + +import ( + "context" + "io" + "os" + "strconv" + "strings" + "testing" + "time" + + tc "github.com/testcontainers/testcontainers-go" + tcexec "github.com/testcontainers/testcontainers-go/exec" +) + +// retryAttempts mirrors lib.sh: up to $E2E_RETRY_ATTEMPTS attempts (default 3). +func retryAttempts() int { + if v, ok := os.LookupEnv("E2E_RETRY_ATTEMPTS"); ok && strings.TrimSpace(v) != "" { + if n, err := strconv.Atoi(strings.TrimSpace(v)); err == nil { + return n + } + } + return 3 +} + +// execRun runs script via `bash -c` in the container and returns its exit +// code with the combined stdout+stderr output. extraEnv entries ("K=V") +// override single variables; the rest of the container env is inherited. +func execRun(ctx context.Context, t *testing.T, ctr tc.Container, script string, extraEnv ...string) (int, string) { + t.Helper() + opts := []tcexec.ProcessOption{tcexec.Multiplexed()} + if len(extraEnv) > 0 { + opts = append(opts, tcexec.WithEnv(extraEnv)) + } + code, r, err := ctr.Exec(ctx, []string{"bash", "-c", script}, opts...) + if err != nil { + t.Fatalf("exec: %v\n--- script ---\n%s", err, script) + } + out, err := io.ReadAll(r) + if err != nil { + t.Fatalf("exec read output: %v\n--- script ---\n%s", err, script) + } + return code, string(out) +} + +// execOK runs script and fatals unless it exits 0, returning the output. +func execOK(ctx context.Context, t *testing.T, ctr tc.Container, script string, extraEnv ...string) string { + t.Helper() + code, out := execRun(ctx, t, ctr, script, extraEnv...) + if code != 0 { + t.Fatalf("exec exited with code %d\n--- script ---\n%s\n--- output ---\n%s", code, script, out) + } + return out +} + +// execRetry keeps the old lib.sh retry semantics (linear 10s*attempt backoff, +// per-attempt logging) with the standard library only: the established retry +// libraries (cenkalti/backoff, avast/retry-go) have no release in the last +// 6 months, so no third-party candidate qualifies. +func execRetry(ctx context.Context, t *testing.T, ctr tc.Container, label, script string, extraEnv ...string) string { + t.Helper() + max := retryAttempts() + var out string + for attempt := 1; ; attempt++ { + code, o := execRun(ctx, t, ctr, script, extraEnv...) + out = o + if code == 0 { + return out + } + if attempt >= max { + t.Fatalf("%s failed after %d attempt(s) (exit %d)\n--- output ---\n%s", label, attempt, code, out) + } + t.Logf("retrying %s, attempt %d exited %d", label, attempt, code) + t.Logf("--- output ---\n%s", out) + select { + case <-ctx.Done(): + t.Fatalf("%s: %v", label, ctx.Err()) + case <-time.After(time.Duration(10*attempt) * time.Second): + } + } +} + +// activated prefixes script with the vfox env setup, the equivalent of the +// old lib.sh activate_vfox (`eval "$(vfox activate bash)"`). Every exec +// starts a fresh shell, so activation cannot persist and must wrap each call. +func activated(script string) string { + return `eval "$(vfox activate bash)"; ` + script +} diff --git a/tests/e2e/linux/go.mod b/tests/e2e/linux/go.mod new file mode 100644 index 0000000..b9391bc --- /dev/null +++ b/tests/e2e/linux/go.mod @@ -0,0 +1,58 @@ +module github.com/version-fox/vfox-flutter/tests/e2e/linux + +go 1.25.0 + +require ( + github.com/containerd/platforms v0.2.1 + github.com/moby/moby/client v0.5.0 + github.com/opencontainers/image-spec v1.1.1 + github.com/stretchr/testify v1.12.1 + github.com/testcontainers/testcontainers-go v0.44.0 +) + +require ( + dario.cat/mergo v1.0.2 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/go-connections v0.7.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/ebitengine/purego v0.10.1 // indirect + github.com/felixge/httpsnoop v1.1.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/klauspost/compress v1.18.6 // indirect + github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e // indirect + github.com/magiconair/properties v1.8.10 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/go-archive v0.2.0 // indirect + github.com/moby/moby/api v1.55.0 // indirect + github.com/moby/patternmatcher v0.6.1 // indirect + github.com/moby/sys/sequential v0.7.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.2 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/shirou/gopsutil/v4 v4.26.6 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect + github.com/tklauser/go-sysconf v0.4.0 // indirect + github.com/tklauser/numcpus v0.12.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect + go.opentelemetry.io/otel v1.44.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect + go.opentelemetry.io/otel/trace v1.44.0 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect + golang.org/x/crypto v0.54.0 // indirect + golang.org/x/sys v0.47.0 // indirect +) diff --git a/tests/e2e/linux/go.sum b/tests/e2e/linux/go.sum new file mode 100644 index 0000000..5001fb9 --- /dev/null +++ b/tests/e2e/linux/go.sum @@ -0,0 +1,122 @@ +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= +github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY= +github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc= +github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= +github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= +github.com/moby/moby/api v1.55.0 h1:2/sexvQyqIWS8pRSCFddBfpW2qE7vR7FCL+vN8pxwMc= +github.com/moby/moby/api v1.55.0/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.5.0 h1:5XhyPk2fuOWf6RlSFa3MkIIgDZkF25xToXW8Q/BH7cc= +github.com/moby/moby/client v0.5.0/go.mod h1:rcVpF8ncl9vo5gaIBdol6CnbEtSj1uxMvEV/UrykF/s= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.7.0 h1:ASQNGNROJSuOO6LL6bPHbKvuZu6NU8P4ldPWk31zj/8= +github.com/moby/sys/sequential v0.7.0/go.mod h1:NfSTAp6V3fw4tmkD62PEcOKeZKquXT8VKCkf7aVR79o= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs= +github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +github.com/testcontainers/testcontainers-go v0.44.0 h1:/Fwh6HY1mIikhnm9e7HwoxGycx0lzRAE0f5VQpjFxzI= +github.com/testcontainers/testcontainers-go v0.44.0/go.mod h1:IcnwQrYTO86xHXu5bvMaBH7ATlbS3Qn1M1QWW3c66rE= +github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= +github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= +github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= +github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= +pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/tests/e2e/linux/install.go b/tests/e2e/linux/install.go new file mode 100644 index 0000000..20fcddd --- /dev/null +++ b/tests/e2e/linux/install.go @@ -0,0 +1,18 @@ +package e2e + +import ( + "context" + "fmt" + "testing" + + tc "github.com/testcontainers/testcontainers-go" +) + +// installFlutterVersion installs one Flutter version and selects it globally +// (was install.sh). +func installFlutterVersion(ctx context.Context, t *testing.T, ctr tc.Container, version string) { + t.Helper() + execRetry(ctx, t, ctr, fmt.Sprintf("vfox install flutter@%s", version), + activated(fmt.Sprintf("vfox install flutter@%s", version))) + execOK(ctx, t, ctr, activated(fmt.Sprintf("vfox use --global flutter@%s", version))) +} diff --git a/tests/e2e/linux/install.sh b/tests/e2e/linux/install.sh deleted file mode 100755 index 0e3d5dd..0000000 --- a/tests/e2e/linux/install.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash -# Install and activate one Flutter version: `bash install.sh `. -# Runs standalone (called as a subprocess by run.sh, like install.ps1). -set -o errexit -o nounset -o pipefail - -here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# shellcheck source=lib.sh -source "$here/lib.sh" - -install_flutter_version() { - local version="$1" - activate_vfox - if ! retry "vfox install flutter@${version}" vfox install flutter@"$version"; then - die "vfox install flutter@${version} failed after ${E2E_RETRY_ATTEMPTS:-3} attempts" - fi - vfox use --global flutter@"$version" - activate_vfox -} - -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - version="${1:?usage: install.sh }" - install_flutter_version "$version" -fi diff --git a/tests/e2e/linux/lib.sh b/tests/e2e/linux/lib.sh deleted file mode 100644 index c9a60cc..0000000 --- a/tests/e2e/linux/lib.sh +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env bash -# Shared helpers for the Linux container E2E suite. Sourced, not executed: -# source "$here/lib.sh" -# -# Layout (mirrors tests/e2e/windows): -# lib.sh - generic helpers (this file; no side effects when sourced) -# config.sh - default versions / flavours / mirrors -# setup.sh - install vfox and register the local flutter plugin -# install.sh - install one Flutter version with retries -# verify.sh - assert on the installed SDK (layout, drift, toolchain) -# run.sh - thin in-container orchestrator: setup -> install -> verify -# e2e.sh - host-side matrix runner (builds the image, fans out containers) - -if [[ -n "${VFOX_E2E_LIB_SH:-}" ]]; then - return 0 2>/dev/null || exit 0 -fi -VFOX_E2E_LIB_SH=1 - -die() { - echo "FAIL $*" >&2 - exit 1 -} - -pass() { - echo "PASS $*" -} - -# assert_contains