diff --git a/.circleci/config.yml b/.circleci/config.yml index 98b50a9260..038645c377 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -287,6 +287,12 @@ jobs: go vet ./... go vet -C tools ./... + # The third-party notices link the licence text this bundle copies into + # docs/assets/landing, which is generated rather than tracked. + - run: + name: Build the landing-page diagram + command: make docs-landing-assets + - run: name: Check documentation links command: python3 scripts/check-doc-links.py @@ -416,7 +422,10 @@ jobs: - install-fmpy # One scripts/race-shard.sh shard per node: internal/exec/runtime alone peaks near - # 6.5 GB under -race, so it shares its 8 GB node only sequentially (tests/wasm, tools). + # 8.5 GB under -race, so it shares its 8 GB node only sequentially (tests/wasm, tools). + # The race detector's memory is per process and the corpus-wide exploration alone + # accounts for half of that peak, so the package runs as two processes: the + # exploration, then everything else (4 GB each). - run: name: Run Go race tests command: | @@ -429,10 +438,14 @@ jobs: shard=${shards[$CIRCLE_NODE_INDEX]} pkgs=$(scripts/race-shard.sh "$shard") echo "shard $shard:"; echo "$pkgs" - go test -v -race -pgo=off -timeout 30m $pkgs if [ "$shard" = runtime ]; then + exploration='^TestExploreWithIsExploreOverTheConformanceCorpus$' + go test -v -race -pgo=off -timeout 30m -run "$exploration" $pkgs + go test -v -race -pgo=off -timeout 30m -skip "$exploration" $pkgs go test -v -race -pgo=off -timeout 30m ./tests/wasm go test -C tools -v -race -pgo=off -timeout 30m ./... + else + go test -v -race -pgo=off -timeout 30m $pkgs fi no_output_timeout: 30m environment: diff --git a/internal/exec/runtime/sweep_queue_test.go b/internal/exec/runtime/sweep_queue_test.go index cbe618a584..f56384b2fb 100644 --- a/internal/exec/runtime/sweep_queue_test.go +++ b/internal/exec/runtime/sweep_queue_test.go @@ -210,11 +210,32 @@ func TestRunSweepWithKeepsEachRowsWritesToItself(t *testing.T) { } } +// metDeadline is a context whose deadline is met when the test says so rather than when a +// clock does, so a slow machine cannot meet it before every job has taken its first row. +type metDeadline struct { + context.Context + done chan struct{} + once sync.Once +} + +func (d *metDeadline) Done() <-chan struct{} { return d.done } + +func (d *metDeadline) Err() error { + select { + case <-d.done: + return context.DeadlineExceeded + default: + return nil + } +} + +func (d *metDeadline) meet() { d.once.Do(func() { close(d.done) }) } + // A deadline met mid-sweep ends the sweep with context.DeadlineExceeded and no table: no row // starts once the deadline is visible, the rows in flight finish and are discarded — an // absence, never a partial answer — as one job reports when it meets the deadline between -// two rows. The rows the jobs take first wait out the deadline, so on any job count the -// rows started are exactly the jobs' first ones. +// two rows. The rows the jobs take first wait out the deadline, which the last of them to +// start meets, so on any job count the rows started are exactly the jobs' first ones. func TestRunSweepWithStopsAtTheDeadline(t *testing.T) { fresh, scope := sweepJobsFixture(t) first := firstOf(t, fresh) @@ -225,13 +246,15 @@ func TestRunSweepWithStopsAtTheDeadline(t *testing.T) { } double := sweepCalcRun(sym, scope) for _, jobs := range []int{1, sweepJobs} { - stop, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond) + stop := &metDeadline{Context: context.Background(), done: make(chan struct{})} var started, finished, late atomic.Int64 waiting := func(ctx *Context, bindings []SweepBinding) (SweepRunResult, error) { - started.Add(1) if stop.Err() != nil { late.Add(1) } + if started.Add(1) == int64(jobs) { + stop.meet() + } if bindings[0].Value.Const.Int <= int64(sweepJobs) { <-stop.Done() } @@ -240,7 +263,6 @@ func TestRunSweepWithStopsAtTheDeadline(t *testing.T) { return result, err } table, err := RunSweepWith(stop, SweepWorkers{First: firstOf(t, fresh), Jobs: jobs, Fresh: fresh}, "test::Fib", plan, 0, waiting) - cancel() if !errors.Is(err, context.DeadlineExceeded) || len(table.Rows) != 0 || table.Target != "" { t.Fatalf("on %d jobs: table of %d rows and %v, want no table and context.DeadlineExceeded", jobs, len(table.Rows), err) }