Run the equality test only when named, on three stores at once, with its phases timed (issue 1384)
The live try took 1074 s for it. It now skips unless the tests pattern (-run) names it, which a try sets and a whole-suite merge check does not; its three independent pieces of work (both ways of Foreseeing on a fresh store, the controller's sends and readings, mesh-sdk's sends) run together; mesh-sdk's Input is read once for a phase of questions; and each phase logs its elapsed time, so the next run says where the time goes. A machine refused on either side is left out of the comparison of the send.
This commit is contained in:
@@ -4,11 +4,14 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/compose"
|
||||
snapshot "git.novox.be/novox/mesh-sdk/go/facts"
|
||||
@@ -20,6 +23,8 @@ import (
|
||||
// scrubbed as the snapshot is), else the snapshot of a small mesh of its own; raises it into throwaway stores as the merge
|
||||
// gate does; and composes every machine both ways.
|
||||
//
|
||||
// It runs only when named (a try with this test's name as the tests pattern); a whole-suite run skips it.
|
||||
//
|
||||
// (a) Foreseeing on the fresh store: the same error or none, the same validator problems, LeftOut and its reasons, Withheld,
|
||||
// Unbound, Unplaced, HandedOnly and the own secrets named foreseen; and the ports mesh-sdk names foreseen are the ones the
|
||||
// controller allocates when it sends.
|
||||
@@ -31,11 +36,19 @@ import (
|
||||
// field that differs; a machine both paths refuse alike is said, not failed. The test is deleted with the controller's
|
||||
// composition (issue 1389).
|
||||
func TestEveryMachineComposesAlikeBothWays(t *testing.T) {
|
||||
// It is slow (it composes every machine of the live mesh several times each way) and is deleted with the controller's
|
||||
// composition (issue 1389), so it runs only when asked for by name: a try or a `go test -run` that names it. A merge check
|
||||
// runs every test and does not.
|
||||
if run := flag.Lookup("test.run"); run == nil || !strings.Contains(run.Value.String(), "TestEveryMachineComposesAlikeBothWays") {
|
||||
t.Skip("runs only when named: a try with the tests pattern TestEveryMachineComposesAlikeBothWays")
|
||||
}
|
||||
ctx := t.Context()
|
||||
admin := os.Getenv("MESH_TEST_POSTGRES")
|
||||
if admin == "" {
|
||||
t.Skip("no throwaway PostgreSQL (MESH_TEST_POSTGRES) to raise the mesh in")
|
||||
}
|
||||
started := time.Now()
|
||||
phase := func(what string) { t.Logf("[%s] %s", time.Since(started).Round(time.Second), what) }
|
||||
f, source := equalityFacts(t)
|
||||
t.Logf("the facts of %s (%s): %d machine(s)", f.Taken.Format("2006-01-02 15:04:05"), source, len(f.Machines))
|
||||
shelf, err := shelfOfFacts(f)
|
||||
@@ -43,108 +56,149 @@ func TestEveryMachineComposesAlikeBothWays(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sources := sourcesOfFacts(f)
|
||||
var failures []string
|
||||
|
||||
// (a) on the fresh store. mesh-sdk's composition first: it writes nothing, where the controller's makes every pair
|
||||
// credential on the way.
|
||||
fresh, dropFresh, err := throwawayStores(ctx, admin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := raiseFromFacts(ctx, fresh, f, shelf, sources); err != nil {
|
||||
dropFresh()
|
||||
t.Fatal(err)
|
||||
}
|
||||
sdkForeseen := map[string]composedBoth{}
|
||||
foreseenPorts := map[string][]string{}
|
||||
for _, m := range f.Machines {
|
||||
in, err := readComposeInput(ctx, fresh)
|
||||
// Three stores, raised from the same snapshot, are three independent pieces of work and run together: (a) both ways on a
|
||||
// fresh store, (b) and (c) the controller's sends and readings, (b) mesh-sdk's sends.
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
oldForeseen, sdkForeseen = map[string]composedBoth{}, map[string]composedBoth{}
|
||||
foreseenPorts = map[string][]string{}
|
||||
readOld, readSDK = map[string]composedBoth{}, map[string]composedBoth{}
|
||||
oldMade, sdkMade made
|
||||
oldRefused = map[string]bool{}
|
||||
sendRefused = map[string]string{}
|
||||
errA, errOld, errSDK error
|
||||
)
|
||||
wg.Add(3)
|
||||
go func() { // (a) Foreseeing on the fresh store.
|
||||
defer wg.Done()
|
||||
fresh, drop, err := throwawayStores(ctx, admin)
|
||||
if err != nil {
|
||||
dropFresh()
|
||||
t.Fatalf("%s: the adapter cannot read the mesh: %v", m.Name, err)
|
||||
errA = err
|
||||
return
|
||||
}
|
||||
d, problems, cerr := compose.ComposeIn(in.Input, m.Name, compose.Foreseeing)
|
||||
if cerr == nil {
|
||||
foreseenPorts[m.Name] = append([]string(nil), d.ForeseenPorts...)
|
||||
sort.Strings(foreseenPorts[m.Name])
|
||||
defer drop()
|
||||
if _, err := raiseFromFacts(ctx, fresh, f, shelf, sources); err != nil {
|
||||
errA = err
|
||||
return
|
||||
}
|
||||
// mesh-sdk's composition first: it writes nothing, where the controller's makes every pair credential on the way. The
|
||||
// Input is read once for all of its machines.
|
||||
cached := withInputCache(ctx)
|
||||
for _, m := range f.Machines {
|
||||
in, err := inputFor(cached, fresh)
|
||||
if err != nil {
|
||||
errA = fmt.Errorf("%s: the adapter cannot read the mesh: %w", m.Name, err)
|
||||
return
|
||||
}
|
||||
d, problems, cerr := compose.ComposeIn(in.Input, m.Name, compose.Foreseeing)
|
||||
if cerr == nil {
|
||||
foreseenPorts[m.Name] = append([]string(nil), d.ForeseenPorts...)
|
||||
sort.Strings(foreseenPorts[m.Name])
|
||||
}
|
||||
sdkForeseen[m.Name] = capturedFrom(sendableOf(d), problems, cerr)
|
||||
}
|
||||
phase("(a) mesh-sdk's Foreseeing of every machine")
|
||||
gens, gensErr := generators(ctx, fresh)
|
||||
for _, m := range f.Machines {
|
||||
if gensErr != nil {
|
||||
oldForeseen[m.Name] = composedBoth{Err: gensErr.Error()}
|
||||
continue
|
||||
}
|
||||
d, problems, err := composedAndValidated(ctx, fresh, m.Name, gens, Foreseeing)
|
||||
oldForeseen[m.Name] = capturedFrom(d, problems, err)
|
||||
}
|
||||
phase("(a) the controller's Foreseeing of every machine")
|
||||
}()
|
||||
go func() { // (b) and (c): the controller's send of every machine, then both ways of reading.
|
||||
defer wg.Done()
|
||||
oldStore, drop, err := throwawayStores(ctx, admin)
|
||||
if err != nil {
|
||||
errOld = err
|
||||
return
|
||||
}
|
||||
defer drop()
|
||||
if _, err := raiseFromFacts(ctx, oldStore, f, shelf, sources); err != nil {
|
||||
errOld = err
|
||||
return
|
||||
}
|
||||
oldGens, oldGensErr := generators(ctx, oldStore)
|
||||
for _, m := range f.Machines {
|
||||
if oldGensErr != nil {
|
||||
break
|
||||
}
|
||||
if _, _, err := composedAndValidated(ctx, oldStore, m.Name, oldGens, Allocating); err != nil {
|
||||
oldRefused[m.Name] = true
|
||||
}
|
||||
}
|
||||
phase("(b) the controller's send of every machine")
|
||||
if oldMade, err = madeIn(ctx, oldStore); err != nil {
|
||||
errOld = err
|
||||
return
|
||||
}
|
||||
cached := withInputCache(ctx)
|
||||
for _, m := range f.Machines {
|
||||
if oldGensErr != nil {
|
||||
readOld[m.Name] = composedBoth{Err: oldGensErr.Error()}
|
||||
} else {
|
||||
d, problems, err := composedAndValidated(ctx, oldStore, m.Name, oldGens, Reading)
|
||||
readOld[m.Name] = capturedFrom(d, problems, err)
|
||||
}
|
||||
d, problems, err := composeQuestion(cached, oldStore, m.Name, compose.Reading, true)
|
||||
readSDK[m.Name] = capturedFrom(d, problems, err)
|
||||
}
|
||||
phase("(c) both ways of reading every machine after the controller's send")
|
||||
}()
|
||||
go func() { // (b) mesh-sdk's send of every machine.
|
||||
defer wg.Done()
|
||||
sdkStore, drop, err := throwawayStores(ctx, admin)
|
||||
if err != nil {
|
||||
errSDK = err
|
||||
return
|
||||
}
|
||||
defer drop()
|
||||
if _, err := raiseFromFacts(ctx, sdkStore, f, shelf, sources); err != nil {
|
||||
errSDK = err
|
||||
return
|
||||
}
|
||||
for _, m := range f.Machines {
|
||||
if _, err := composeSend(ctx, sdkStore, m.Name); err != nil {
|
||||
sendRefused[m.Name] = err.Error()
|
||||
}
|
||||
}
|
||||
phase("(b) mesh-sdk's send of every machine")
|
||||
sdkMade, errSDK = madeIn(ctx, sdkStore)
|
||||
}()
|
||||
wg.Wait()
|
||||
for _, err := range []error{errA, errOld, errSDK} {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sdkForeseen[m.Name] = capturedFrom(sendableOf(d), problems, cerr)
|
||||
}
|
||||
oldForeseen := map[string]composedBoth{}
|
||||
gens, gensErr := generators(ctx, fresh)
|
||||
for _, m := range f.Machines {
|
||||
if gensErr != nil {
|
||||
oldForeseen[m.Name] = composedBoth{Err: gensErr.Error()}
|
||||
continue
|
||||
}
|
||||
d, problems, err := composedAndValidated(ctx, fresh, m.Name, gens, Foreseeing)
|
||||
oldForeseen[m.Name] = capturedFrom(d, problems, err)
|
||||
}
|
||||
dropFresh()
|
||||
|
||||
var failures []string
|
||||
for _, m := range f.Machines {
|
||||
failures = append(failures, bothWaysDiffer(m.Name, "(a) Foreseeing", oldForeseen[m.Name], sdkForeseen[m.Name], false)...)
|
||||
}
|
||||
|
||||
// (b) and (c): the controller's send of every machine, then both ways of reading, on one store.
|
||||
oldStore, dropOld, err := throwawayStores(ctx, admin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := raiseFromFacts(ctx, oldStore, f, shelf, sources); err != nil {
|
||||
dropOld()
|
||||
t.Fatal(err)
|
||||
}
|
||||
oldGens, oldGensErr := generators(ctx, oldStore)
|
||||
for _, m := range f.Machines {
|
||||
if oldGensErr != nil {
|
||||
break
|
||||
}
|
||||
_, _, _ = composedAndValidated(ctx, oldStore, m.Name, oldGens, Allocating)
|
||||
}
|
||||
oldMade, err := madeIn(ctx, oldStore)
|
||||
if err != nil {
|
||||
dropOld()
|
||||
t.Fatal(err)
|
||||
}
|
||||
readOld, readSDK := map[string]composedBoth{}, map[string]composedBoth{}
|
||||
for _, m := range f.Machines {
|
||||
if oldGensErr != nil {
|
||||
readOld[m.Name] = composedBoth{Err: oldGensErr.Error()}
|
||||
} else {
|
||||
d, problems, err := composedAndValidated(ctx, oldStore, m.Name, oldGens, Reading)
|
||||
readOld[m.Name] = capturedFrom(d, problems, err)
|
||||
}
|
||||
d, problems, err := composeQuestion(ctx, oldStore, m.Name, compose.Reading, true)
|
||||
readSDK[m.Name] = capturedFrom(d, problems, err)
|
||||
}
|
||||
dropOld()
|
||||
for _, m := range f.Machines {
|
||||
failures = append(failures, bothWaysDiffer(m.Name, "(c) Reading after the send", readOld[m.Name], readSDK[m.Name], true)...)
|
||||
}
|
||||
|
||||
sdkStore, dropSDK, err := throwawayStores(ctx, admin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
// A machine refused on either side sends nothing, so what the controller made on the way to refusing it is no send's.
|
||||
refused := map[string]bool{}
|
||||
for m := range sendRefused {
|
||||
refused[m] = true
|
||||
}
|
||||
if _, err := raiseFromFacts(ctx, sdkStore, f, shelf, sources); err != nil {
|
||||
dropSDK()
|
||||
t.Fatal(err)
|
||||
for m := range oldRefused {
|
||||
refused[m] = true
|
||||
}
|
||||
sendRefused := map[string]string{}
|
||||
for _, m := range f.Machines {
|
||||
if _, err := composeSend(ctx, sdkStore, m.Name); err != nil {
|
||||
sendRefused[m.Name] = err.Error()
|
||||
}
|
||||
for m := range refused {
|
||||
t.Logf("%s: refused on one side or both, so its ports and secrets are left out of the comparison of the send", m)
|
||||
}
|
||||
sdkMade, err := madeIn(ctx, sdkStore)
|
||||
dropSDK()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
failures = append(failures, madeDiffer("(b) the send", oldMade, sdkMade)...)
|
||||
failures = append(failures, madeDiffer("(b) the send", oldMade, sdkMade, refused)...)
|
||||
|
||||
// (a)'s ports: those mesh-sdk names foreseen, for each machine, are the ones the controller's send allocated to it.
|
||||
for _, m := range f.Machines {
|
||||
if refused[m.Name] {
|
||||
continue
|
||||
}
|
||||
var allocated []string
|
||||
for _, p := range oldMade.Ports {
|
||||
if node, rest, ok := strings.Cut(p, " "); ok && node == m.Name {
|
||||
@@ -162,7 +216,7 @@ func TestEveryMachineComposesAlikeBothWays(t *testing.T) {
|
||||
}
|
||||
if !found {
|
||||
failures = append(failures, fmt.Sprintf("%s, (a) Foreseeing: mesh-sdk names the port %s foreseen and the "+
|
||||
"controller's send allocates none for it (it allocates %v)", m.Name, want, allocated))
|
||||
"controller's send allocates none for it", m.Name, want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user