diff --git a/Makefile b/Makefile index 721bb524..9636d6e0 100644 --- a/Makefile +++ b/Makefile @@ -79,26 +79,6 @@ redis-provisioner-image: @echo @docker image inspect $(REDIS_PROVISIONER_IMAGE) --format 'built {{.RepoTags}} {{.Size}} bytes' -# The whole check. Raises a database, runs the repository's own check against it -- merge-check.sh, -# the very script `mesh/repo-check` runs -- and takes the database down again, including when the -# check fails, which is why the teardown is not conditional and the script's exit status is the -# target's. -# -# **It calls the script rather than restating it** (novox/hq issue 431): this target used to list its -# own steps, its formatting step walked vendor/ where the script's does not, and it failed on -# third-party code no change could fix -- so a developer's check and the gate disagreed, and the steps -# after formatting never ran through it. The script is the one list of steps; this target only gives it -# a database (MESH_TEST_POSTGRES, exported above). -# -# **Two levels** (issue 895): the unit level, `go test ./...` untagged, timed and held to a bound; and the integration -# level, `-tags integration`, the tests that start a bus, a store or another process. Each is said with its time. -# -# **Packages in parallel, under the race detector, each test on a bus of its own** (internal/testbus), -# as the script runs them: a bus per test, of the release the mesh runs, makes the suite the same in any -# order, and the timeout bounds a hang to a failure with a stack, never a stalled gate. -check: postgres - @sh merge-check.sh ; status=$$? ; docker rm -f -v $(PG_CONTAINER) >/dev/null 2>&1 || true ; exit $$status - # The unit level alone (untagged: no bus, no store, no other process), which a developer runs continuously. The tests # that need a bus or the store carry `//go:build integration` and run in `check`, not here; without a database the # store's tests would skip rather than fail anyway. @@ -108,8 +88,7 @@ test: vet: go vet ./... -# Quick steps for a developer, not part of `check`. The directories gofmt reads must be the ones -# merge-check.sh lists, never `.`, which walks vendor/ (novox/hq issue 431). +# A quick step for a developer: the checker runs the build module's own formatting and vet (ADR 0501 §5, D7). fmt: @unformatted=$$(gofmt -l cmd internal examples) ; \ if [ -n "$$unformatted" ] ; then echo "not gofmt'd:" ; echo "$$unformatted" ; exit 1 ; fi diff --git a/RELEASE-57.md b/RELEASE-57.md deleted file mode 100644 index 140ccff6..00000000 --- a/RELEASE-57.md +++ /dev/null @@ -1,6 +0,0 @@ -# Release 57: config and provisioning - -This branch carries the work of hq design 57 (ADR 0348, ADR 0349) in this repository. Each package -merges here through its own reviewed pull request; the branch reaches main as one pull request, in one -delivery group with the branch of the same name in mesh-controller, mesh-host, mesh-sdk, mesh-catalog -and hq, after a rehearsal in the lab. This file is deleted by the last commit before that pull request. diff --git a/check-nested-modules.sh b/check-nested-modules.sh deleted file mode 100644 index ee4b8905..00000000 --- a/check-nested-modules.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -# The unit tests of every Go module of its own below this repository's root, run from the root, for merge-check.sh -# and for the test that tries it on a tree (merge_check_nested_test.go). Its own file so that the test needs no -# particular merge-check.sh: the check that judges a change to merge-check.sh is main's. -set -eu -cgo="${cgo:-0}" -race="${race:-}" - -# nested_modules vets and tests every Go module of its own below the root, `modules//go.mod` (novox/hq ADR 0420 -# §5, issue 1025): `./...` stops at a nested go.mod, so a module that lives whole in this repository would have its -# unit tests run by nobody. None is a pass. Each runs in its own directory, outside the vendor directory this -# repository's own packages are built from; a nested module vendors what the forge holds (its own `vendor/`, so the -# check runs offline), as this repository's root module does (issue 1019). A failing module fails the check; the others are still run. -nested_modules() { - nested=0 - for gomod in modules/*/go.mod; do - [ -e "$gomod" ] || continue - dir=$(dirname "$gomod") - echo "nested module $dir:" - if ! (cd "$dir" && GOFLAGS= CGO_ENABLED=0 go vet ./... && - GOFLAGS= CGO_ENABLED="${cgo:-0}" go test ${race:-} -count=1 -timeout 30m ./...); then - echo "nested module $dir FAILED" - nested=1 - fi - done - return $nested -} - -nested_modules diff --git a/cmd/mesh-controller/merge_check_nested_test.go b/cmd/mesh-controller/merge_check_nested_test.go deleted file mode 100644 index 7dd78da6..00000000 --- a/cmd/mesh-controller/merge_check_nested_test.go +++ /dev/null @@ -1,72 +0,0 @@ -//go:build integration - -package main - -import ( - "os" - "os/exec" - "path/filepath" - "strings" - "testing" -) - -// **A repository's merge check runs every module's unit tests, a Go module of its own below the root included** -// (novox/hq ADR 0420 §5, issue 1025): `./...` stops at a nested go.mod. This runs the script -// check-nested-modules.sh in a tree with a `go` that only records where it was run and fails for a module named bad. -func TestTheMergeCheckVetsAndTestsEveryNestedGoModule(t *testing.T) { - script, err := filepath.Abs("../../check-nested-modules.sh") - if err != nil { - t.Fatal(err) - } - bin, tree := t.TempDir(), t.TempDir() - log := filepath.Join(t.TempDir(), "go.log") - fake := "#!/bin/sh\necho \"$(basename \"$PWD\") $1 GOFLAGS=[$GOFLAGS]\" >>\"$LOG\"\n" + - "[ \"$(basename \"$PWD\")\" = bad ] && [ \"$1\" = test ] && exit 1\nexit 0\n" - if err := os.WriteFile(filepath.Join(bin, "go"), []byte(fake), 0o755); err != nil { - t.Fatal(err) - } - run := func() (string, error) { - cmd := exec.Command("sh", script) - cmd.Dir = tree - cmd.Env = append(os.Environ(), "PATH="+bin+":"+os.Getenv("PATH"), "LOG="+log, "GOFLAGS=-mod=vendor") - out, err := cmd.CombinedOutput() - return string(out), err - } - // No modules directory: a pass, nothing run. - if out, err := run(); err != nil { - t.Fatalf("a repository with no nested module failed: %v\n%s", err, out) - } - if _, err := os.Stat(log); err == nil { - t.Fatal("go ran with no nested module") - } - for _, m := range []string{"good", "bad", "other"} { - dir := filepath.Join(tree, "modules", m) - if err := os.MkdirAll(dir, 0o755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module x\n"), 0o644); err != nil { - t.Fatal(err) - } - } - // A directory with no go.mod is no Go module of its own. - if err := os.MkdirAll(filepath.Join(tree, "modules", "typescript"), 0o755); err != nil { - t.Fatal(err) - } - out, err := run() - if err == nil { - t.Fatalf("a nested module whose tests fail passed the check:\n%s", out) - } - raw, _ := os.ReadFile(log) - ran := string(raw) - for _, want := range []string{"good vet GOFLAGS=[]", "good test GOFLAGS=[]", "other test GOFLAGS=[]", "bad test GOFLAGS=[]"} { - if !strings.Contains(ran, want) { - t.Errorf("%q was not run, nor outside the vendor directory:\n%s", want, ran) - } - } - if strings.Contains(ran, "typescript") { - t.Errorf("a directory with no go.mod was run:\n%s", ran) - } - if !strings.Contains(out, "nested module modules/bad FAILED") { - t.Errorf("the failing module is not named:\n%s", out) - } -} diff --git a/internal/lint/mergecheck_unit_level_test.go b/internal/lint/mergecheck_unit_level_test.go deleted file mode 100644 index b019ce54..00000000 --- a/internal/lint/mergecheck_unit_level_test.go +++ /dev/null @@ -1,76 +0,0 @@ -//go:build integration - -package lint - -import ( - "context" - "os" - "os/exec" - "path/filepath" - "strings" - "testing" - "time" -) - -// judgeUnitRun runs merge-check.sh's judging of the unit level on a recorded `go test -v` output (issue 895), as it -// would judge the output of the untagged run: it starts the shell, so it is an integration test. -func judgeUnitRun(t *testing.T, recorded string, bound string) (string, error) { - t.Helper() - out := filepath.Join(t.TempDir(), "unit.out") - if err := os.WriteFile(out, []byte(recorded), 0o600); err != nil { - t.Fatal(err) - } - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - cmd := exec.CommandContext(ctx, "sh", "merge-check.sh", "judge-unit-run", out, bound) - cmd.Dir = filepath.Join("..", "..") - said, err := cmd.CombinedOutput() - return string(said), err -} - -const recordedUnitRun = `=== RUN TestQuick ---- PASS: TestQuick (0.01s) -=== RUN TestSlowOne ---- PASS: TestSlowOne (2.50s) -=== RUN TestSlowTwo - --- PASS: TestSlowTwo/sub (1.00s) ---- PASS: TestSlowTwo (1.20s) ---- SKIP: TestNeedsAStore (0.00s) -PASS -ok example.test/internal/one 2.80s -=== RUN TestElsewhere ---- PASS: TestElsewhere (0.30s) -PASS -ok example.test/internal/two 0.35s -? example.test/internal/three [no test files] -` - -// **The unit level over its bound fails the check and names the slowest tests** (issue 895). -func TestAUnitRunOverItsBoundFailsAndNamesTheSlowestTests(t *testing.T) { - said, err := judgeUnitRun(t, recordedUnitRun, "1") - if err == nil { - t.Fatalf("a unit run of 2.80 s passed a bound of 1 s: %s", said) - } - for _, want := range []string{"5 tests (1 skipped)", "2.80 s (bound 1 s)", "TestSlowOne", "TestSlowTwo", "example.test/internal/one"} { - if !strings.Contains(said, want) { - t.Errorf("the verdict does not say %q: %s", want, said) - } - } - if strings.Index(said, "TestSlowOne") > strings.Index(said, "TestSlowTwo") { - t.Errorf("the slowest test is not named first: %s", said) - } - if strings.Contains(said, "TestSlowTwo/sub") { - t.Errorf("a subtest is named as if it were a test of its own: %s", said) - } -} - -// A unit run within its bound passes and says its count and time. -func TestAUnitRunWithinItsBoundPasses(t *testing.T) { - said, err := judgeUnitRun(t, recordedUnitRun, "3") - if err != nil { - t.Fatalf("a unit run of 2.80 s failed a bound of 3 s: %v: %s", err, said) - } - if !strings.Contains(said, "unit level: 5 tests (1 skipped), 2.80 s (bound 3 s)") { - t.Errorf("the level's count and time are not said: %s", said) - } -} diff --git a/internal/lint/removed_test.go b/internal/lint/removed_test.go new file mode 100644 index 00000000..0c5b1b03 --- /dev/null +++ b/internal/lint/removed_test.go @@ -0,0 +1,22 @@ +package lint + +import ( + "os" + "path/filepath" + "testing" +) + +// What this repository no longer holds (R4g, design 47 acceptance test 24): the checker runs the build module's own steps +// and the repository's tests, so no script of the repository stands in for them. A file named here came back. + +// root is this repository's root, from the package's directory. +const root = "../.." + +func TestTheRepositoryHoldsNoMergeCheckScript(t *testing.T) { + for _, name := range []string{"merge-check.sh", "check-nested-modules.sh", "RELEASE-57.md", "testdata/store-tests"} { + if _, err := os.Stat(filepath.Join(root, name)); err == nil { + t.Errorf("%s is in the repository: the checker (ADR 0501 §5) runs its steps, and no script of the repository "+ + "stands in for it", name) + } + } +} diff --git a/merge-check.sh b/merge-check.sh deleted file mode 100755 index 4c5f0a52..00000000 --- a/merge-check.sh +++ /dev/null @@ -1,348 +0,0 @@ -#!/bin/sh -# mesh-check-toolchain: go -# -# The controller's own check (novox/hq ADR 0237 as amended): the second layer of a pull request's merge -# check, `mesh/repo-check`, run by the build seat in the mesh's Go toolchain — and by hand: -# `MESH_TEST_POSTGRES=… sh merge-check.sh`. -# -# The gate — every machine composed with this change's controller, which judges itself — is the build -# seat's first layer (`mesh/merge-gate`), run because the mesh's module graph builds `mesh-controller` -# from this repository; it is not repeated here. This is the code's own quality and its suite: -# -# 1. formatted and vetted, with and without the build tag `integration`; -# 2. the unit level: `go test ./...` untagged and without the race detector, timed and held to a bound -# (unit_bound below): a test with no database, bus, running service or other process (novox/hq ADR 0391, -# how-we-build section 5). With a bound set, over it the check fails and names the slowest tests (issue 895); -# 3. the integration level: the whole suite with `-tags integration`, packages in parallel, against the -# throwaway store the build seat raised at the release the mesh runs; every live test on a bus of its own -# (internal/testbus), so nothing is serialised. Under the race detector when the toolchain has a C -# compiler, and said when it has not. -# -# **A test that starts a bus, a store or another process, or composes the whole mesh, carries -# `//go:build integration`** as the first line of its file; a file that held both kinds is two files -# (`_integration_test.go` beside `_test.go`). Each level says its test count and its time. -# -# Exit 0 is a pass; anything else fails `mesh/repo-check` with the last line printed. -set -eu -export GOFLAGS=-mod=vendor GOPROXY=off - -# **The unit level's bound, in seconds** (issue 895): the slowest package's own test time of the untagged run, the -# compile not counted. A run over it fails the check and names the slowest tests. It is 5 s since the tests carry the -# `integration` tag (design 47 milestone 3): the slowest packages, cmd/mesh-controller and internal/catalogue, measured -# 2.6-2.8 s on a quiet machine in about 2000 small tests each, and the build node runs the store tests beside this run -# and other work besides, so twice the measure. How-we-build section 5 asks 1 s for the whole unit suite: issue 1055 -# (a catalogue loaded once per package run, fewer source-scanning tests, the timed run started alone). -unit_bound=5 - -# judge_store_tests says each listed test of one package as `go test -v` reported it, and counts in $missing each -# that did not pass: $1 the package, $2 go test's exit status, $3 the file holding its output, then the names. -# A result is found anywhere in a line, not only at its start: when a test's last output leaves a line open, -# `go test -v` writes `--- PASS: (…)` on that same line (novox/hq issue 515). A package whose go test -# exits non-zero fails even when each listed test said PASS — a TestMain or a leak checker can fail after them. -judge_store_tests() { - pkg=$1 status=$2 out=$3 - shift 3 - for name in "$@"; do - line=$(grep -oE -- "--- (PASS|FAIL|SKIP): $name \([^)]*\)" "$out" | tail -n 1 || true) - echo " $pkg ${line:-"--- NOT RUN: $name"}" - case "$line" in "--- PASS: "*) ;; *) missing=$((missing + 1)) ;; esac - done - if [ "$status" -ne 0 ]; then - echo " $pkg FAIL: go test exited $status" - missing=$((missing + 1)) - fi -} -# judge_unit_run says the unit level as `go test -v` reported it and fails when it was over the bound: $1 the file -# holding the output of the untagged `go test -v` of every package (packages one after another, each ending in its -# `ok