test(gateway): qualify production path artifacts
This commit is contained in:
@@ -5,8 +5,10 @@ import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -41,7 +43,7 @@ func TestQualificationCatalogMatchesSection7(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestQualificationProtocolVersionIsExplicitAndImmutable(t *testing.T) {
|
||||
const version = "v1.0.0-phase3c-gateway-rc.6"
|
||||
const version = "v1.0.0-phase3c-gateway-rc.8"
|
||||
t.Setenv("VERSEVDI_QUALIFICATION_PROTOCOL_VERSION", version)
|
||||
got, err := qualificationProtocolVersion()
|
||||
if err != nil || got != version {
|
||||
@@ -104,7 +106,8 @@ func TestQualificationShortProcessingWritesRawArtifact(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if summary.Count < 1 || summary.RawSamplesSHA256 == "" || summary.RawSamplesBytes < 1 ||
|
||||
summary.ResourceSamples < 2 || summary.RawResourcesSHA256 == "" || summary.RawResourcesBytes < 1 {
|
||||
summary.ResourceSamples < 2 || summary.RawResourcesSHA256 == "" || summary.RawResourcesBytes < 1 ||
|
||||
summary.CPUScope != "isolated gateway qualification process (gateway plus bounded fixture/client driver)" {
|
||||
t.Fatalf("processing summary = %#v", summary)
|
||||
}
|
||||
file, err := os.Open(rawPath)
|
||||
@@ -194,6 +197,162 @@ func TestQualificationImpairmentIsDeterministicAndBounded(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestQualificationRTTIsNotSyntheticDoubleOneWayCompletion(t *testing.T) {
|
||||
rawPath := filepath.Join(t.TempDir(), "latency.csv.gz")
|
||||
observation, err := runQualificationImpairment(
|
||||
t,
|
||||
qualificationImpairmentProfiles()[1],
|
||||
qualificationMediaProfiles()[0],
|
||||
40,
|
||||
rawPath,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
meanLatency := qualificationRawMeanLatency(t, rawPath)
|
||||
delta := observation.ObservedRTT - 2*meanLatency
|
||||
if delta < 0 {
|
||||
delta = -delta
|
||||
}
|
||||
if delta < 5*time.Millisecond {
|
||||
t.Fatalf("RTT %s was synthesized as twice one-way completion %s", observation.ObservedRTT, meanLatency)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQualificationFixedSeedJitterIsObservableOnTraversedTraffic(t *testing.T) {
|
||||
profile := qualificationImpairmentProfiles()[2]
|
||||
observation, err := runQualificationImpairment(
|
||||
t,
|
||||
profile,
|
||||
qualificationMediaProfiles()[0],
|
||||
200,
|
||||
filepath.Join(t.TempDir(), "jitter.csv.gz"),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if observation.RTTSource != "apollo_enet_acknowledge" || observation.ObservedRTT <= 0 {
|
||||
t.Fatalf("RTT observation is not transport-acknowledged: %#v", observation)
|
||||
}
|
||||
if observation.ObservedLatency < 5*time.Millisecond || observation.ObservedLatency > 100*time.Millisecond {
|
||||
t.Fatalf("observed one-way latency %s does not reflect configured traversal", observation.ObservedLatency)
|
||||
}
|
||||
if observation.ObservedJitter < 5*time.Millisecond || observation.ObservedJitter > 80*time.Millisecond {
|
||||
t.Fatalf("observed jitter %s is outside reviewed fixed-seed tolerance", observation.ObservedJitter)
|
||||
}
|
||||
if observation.ObservedOutOfOrder == 0 {
|
||||
t.Fatal("fixed-seed jitter was serialized away before production traversal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestQualificationCPUTracksConsumedWorkNotIdleCapacity(t *testing.T) {
|
||||
started := time.Now()
|
||||
before := qualificationRuntimeSample(started)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
idle := qualificationRuntimeSample(started).CPUSeconds - before.CPUSeconds
|
||||
if idle > 50*time.Millisecond.Seconds() {
|
||||
t.Fatalf("idle CPU consumption = %.6fs, want at most 0.05s", idle)
|
||||
}
|
||||
|
||||
workBefore := qualificationRuntimeSample(started).CPUSeconds
|
||||
deadline := time.Now().Add(75 * time.Millisecond)
|
||||
var value uint64 = 1
|
||||
for time.Now().Before(deadline) {
|
||||
value = value*6364136223846793005 + 1
|
||||
}
|
||||
if value == 0 {
|
||||
t.Fatal("bounded CPU work was optimized away")
|
||||
}
|
||||
work := qualificationRuntimeSample(started).CPUSeconds - workBefore
|
||||
if work <= idle || work < 20*time.Millisecond.Seconds() {
|
||||
t.Fatalf("bounded work CPU = %.6fs, idle = %.6fs", work, idle)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQualificationCPUIsolationExcludesParentTestWork(t *testing.T) {
|
||||
output := filepath.Join(t.TempDir(), "cpu.txt")
|
||||
command := exec.Command(os.Args[0], "-test.run=^TestQualificationCPUChild$", "-test.count=1")
|
||||
command.Env = append(os.Environ(), "VERSEVDI_QUALIFICATION_CPU_CHILD="+output)
|
||||
if err := command.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deadline := time.Now().Add(150 * time.Millisecond)
|
||||
var value uint64 = 1
|
||||
for time.Now().Before(deadline) {
|
||||
value = value*2862933555777941757 + 3037000493
|
||||
}
|
||||
if value == 0 {
|
||||
t.Fatal("parent CPU work was optimized away")
|
||||
}
|
||||
if err := command.Wait(); err != nil {
|
||||
t.Fatalf("CPU child: %v", err)
|
||||
}
|
||||
raw, err := os.ReadFile(output)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
consumed, err := strconv.ParseFloat(string(raw), 64)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if consumed > 50*time.Millisecond.Seconds() {
|
||||
t.Fatalf("isolated idle qualification process consumed %.6fs while parent test was busy", consumed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQualificationCPUChild(t *testing.T) {
|
||||
output := os.Getenv("VERSEVDI_QUALIFICATION_CPU_CHILD")
|
||||
if output == "" {
|
||||
return
|
||||
}
|
||||
started := time.Now()
|
||||
before := qualificationRuntimeSample(started)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
consumed := qualificationRuntimeSample(started).CPUSeconds - before.CPUSeconds
|
||||
if err := os.WriteFile(output, []byte(strconv.FormatFloat(consumed, 'f', 9, 64)), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func qualificationRawMeanLatency(t *testing.T, path string) time.Duration {
|
||||
t.Helper()
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
compressed, err := gzip.NewReader(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw, err := io.ReadAll(compressed)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := compressed.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var total time.Duration
|
||||
var count int
|
||||
for _, line := range strings.Split(string(raw), "\n")[1:] {
|
||||
fields := strings.Split(line, ",")
|
||||
if len(fields) < 5 || fields[4] != "delivered" {
|
||||
continue
|
||||
}
|
||||
sent, sentErr := strconv.ParseInt(fields[1], 10, 64)
|
||||
delivered, deliveredErr := strconv.ParseInt(fields[2], 10, 64)
|
||||
if sentErr != nil || deliveredErr != nil || delivered < sent {
|
||||
t.Fatalf("invalid raw latency row %q", line)
|
||||
}
|
||||
total += time.Duration(delivered - sent)
|
||||
count++
|
||||
}
|
||||
if count == 0 {
|
||||
t.Fatal("no delivered raw latency rows")
|
||||
}
|
||||
return total / time.Duration(count)
|
||||
}
|
||||
|
||||
func TestQualificationSixImpairmentProfilesTraverseProductionPath(t *testing.T) {
|
||||
profiles := qualificationImpairmentProfiles()
|
||||
if len(profiles) != 6 {
|
||||
|
||||
Reference in New Issue
Block a user