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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/compose"
|
"git.novox.be/novox/mesh-sdk/go/compose"
|
||||||
snapshot "git.novox.be/novox/mesh-sdk/go/facts"
|
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
|
// 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.
|
// 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,
|
// (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
|
// 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.
|
// 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
|
// field that differs; a machine both paths refuse alike is said, not failed. The test is deleted with the controller's
|
||||||
// composition (issue 1389).
|
// composition (issue 1389).
|
||||||
func TestEveryMachineComposesAlikeBothWays(t *testing.T) {
|
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()
|
ctx := t.Context()
|
||||||
admin := os.Getenv("MESH_TEST_POSTGRES")
|
admin := os.Getenv("MESH_TEST_POSTGRES")
|
||||||
if admin == "" {
|
if admin == "" {
|
||||||
t.Skip("no throwaway PostgreSQL (MESH_TEST_POSTGRES) to raise the mesh in")
|
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)
|
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))
|
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)
|
shelf, err := shelfOfFacts(f)
|
||||||
@@ -43,108 +56,149 @@ func TestEveryMachineComposesAlikeBothWays(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
sources := sourcesOfFacts(f)
|
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
|
// Three stores, raised from the same snapshot, are three independent pieces of work and run together: (a) both ways on a
|
||||||
// credential on the way.
|
// fresh store, (b) and (c) the controller's sends and readings, (b) mesh-sdk's sends.
|
||||||
fresh, dropFresh, err := throwawayStores(ctx, admin)
|
var (
|
||||||
if err != nil {
|
wg sync.WaitGroup
|
||||||
t.Fatal(err)
|
oldForeseen, sdkForeseen = map[string]composedBoth{}, map[string]composedBoth{}
|
||||||
}
|
foreseenPorts = map[string][]string{}
|
||||||
if _, err := raiseFromFacts(ctx, fresh, f, shelf, sources); err != nil {
|
readOld, readSDK = map[string]composedBoth{}, map[string]composedBoth{}
|
||||||
dropFresh()
|
oldMade, sdkMade made
|
||||||
t.Fatal(err)
|
oldRefused = map[string]bool{}
|
||||||
}
|
sendRefused = map[string]string{}
|
||||||
sdkForeseen := map[string]composedBoth{}
|
errA, errOld, errSDK error
|
||||||
foreseenPorts := map[string][]string{}
|
)
|
||||||
for _, m := range f.Machines {
|
wg.Add(3)
|
||||||
in, err := readComposeInput(ctx, fresh)
|
go func() { // (a) Foreseeing on the fresh store.
|
||||||
|
defer wg.Done()
|
||||||
|
fresh, drop, err := throwawayStores(ctx, admin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dropFresh()
|
errA = err
|
||||||
t.Fatalf("%s: the adapter cannot read the mesh: %v", m.Name, err)
|
return
|
||||||
}
|
}
|
||||||
d, problems, cerr := compose.ComposeIn(in.Input, m.Name, compose.Foreseeing)
|
defer drop()
|
||||||
if cerr == nil {
|
if _, err := raiseFromFacts(ctx, fresh, f, shelf, sources); err != nil {
|
||||||
foreseenPorts[m.Name] = append([]string(nil), d.ForeseenPorts...)
|
errA = err
|
||||||
sort.Strings(foreseenPorts[m.Name])
|
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)
|
var failures []string
|
||||||
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()
|
|
||||||
for _, m := range f.Machines {
|
for _, m := range f.Machines {
|
||||||
failures = append(failures, bothWaysDiffer(m.Name, "(a) Foreseeing", oldForeseen[m.Name], sdkForeseen[m.Name], false)...)
|
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)...)
|
failures = append(failures, bothWaysDiffer(m.Name, "(c) Reading after the send", readOld[m.Name], readSDK[m.Name], true)...)
|
||||||
}
|
}
|
||||||
|
// A machine refused on either side sends nothing, so what the controller made on the way to refusing it is no send's.
|
||||||
sdkStore, dropSDK, err := throwawayStores(ctx, admin)
|
refused := map[string]bool{}
|
||||||
if err != nil {
|
for m := range sendRefused {
|
||||||
t.Fatal(err)
|
refused[m] = true
|
||||||
}
|
}
|
||||||
if _, err := raiseFromFacts(ctx, sdkStore, f, shelf, sources); err != nil {
|
for m := range oldRefused {
|
||||||
dropSDK()
|
refused[m] = true
|
||||||
t.Fatal(err)
|
|
||||||
}
|
}
|
||||||
sendRefused := map[string]string{}
|
for m := range refused {
|
||||||
for _, m := range f.Machines {
|
t.Logf("%s: refused on one side or both, so its ports and secrets are left out of the comparison of the send", m)
|
||||||
if _, err := composeSend(ctx, sdkStore, m.Name); err != nil {
|
|
||||||
sendRefused[m.Name] = err.Error()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sdkMade, err := madeIn(ctx, sdkStore)
|
failures = append(failures, madeDiffer("(b) the send", oldMade, sdkMade, refused)...)
|
||||||
dropSDK()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
failures = append(failures, madeDiffer("(b) the send", oldMade, sdkMade)...)
|
|
||||||
|
|
||||||
// (a)'s ports: those mesh-sdk names foreseen, for each machine, are the ones the controller's send allocated to it.
|
// (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 {
|
for _, m := range f.Machines {
|
||||||
|
if refused[m.Name] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
var allocated []string
|
var allocated []string
|
||||||
for _, p := range oldMade.Ports {
|
for _, p := range oldMade.Ports {
|
||||||
if node, rest, ok := strings.Cut(p, " "); ok && node == m.Name {
|
if node, rest, ok := strings.Cut(p, " "); ok && node == m.Name {
|
||||||
@@ -162,7 +216,7 @@ func TestEveryMachineComposesAlikeBothWays(t *testing.T) {
|
|||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
failures = append(failures, fmt.Sprintf("%s, (a) Foreseeing: mesh-sdk names the port %s foreseen and the "+
|
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