Files
VerseVDI-Data-Plane/gateway/qualification_contract_test.go
T

413 lines
15 KiB
Go

package gateway
import (
"compress/gzip"
"encoding/binary"
"io"
"os"
"os/exec"
"path/filepath"
"reflect"
"strconv"
"strings"
"testing"
"time"
)
func TestQualificationCatalogMatchesSection7(t *testing.T) {
media := qualificationMediaProfiles()
if len(media) != 3 {
t.Fatalf("media profile count = %d, want 3", len(media))
}
wantMedia := []qualificationMediaProfile{
{Name: "1080p60-h264", Codec: "h264", BitrateKbps: 20000, Duration: 10 * time.Minute, Warmup: time.Second, PacketBytes: 1179},
{Name: "1440p120-hevc", Codec: "hevc", BitrateKbps: 50000, Duration: 10 * time.Minute, Warmup: time.Second, PacketBytes: 1179},
{Name: "4k60-hevc", Codec: "hevc", BitrateKbps: 80000, Duration: 10 * time.Minute, Warmup: time.Second, PacketBytes: 1179},
}
if !reflect.DeepEqual(media, wantMedia) {
t.Fatalf("media profiles = %#v, want %#v", media, wantMedia)
}
impairments := qualificationImpairmentProfiles()
wantImpairments := []qualificationImpairmentProfile{
{Name: "baseline", RTT: 20 * time.Millisecond},
{Name: "latency", RTT: 150 * time.Millisecond},
{Name: "jitter", RTT: 50 * time.Millisecond, Jitter: 30 * time.Millisecond},
{Name: "loss", RTT: 50 * time.Millisecond, Jitter: 10 * time.Millisecond, LossPercent: 5},
{Name: "reorder", RTT: 100 * time.Millisecond, Jitter: 10 * time.Millisecond, LossPercent: 1, Reorder: true},
{Name: "constrained", RTT: 50 * time.Millisecond, Jitter: 10 * time.Millisecond, LossPercent: 2, Reorder: true, CapacitySteps: []int{25, 50}},
}
if !reflect.DeepEqual(impairments, wantImpairments) {
t.Fatalf("impairment profiles = %#v, want %#v", impairments, wantImpairments)
}
}
func TestQualificationProtocolVersionIsExplicitAndImmutable(t *testing.T) {
const version = "v1.0.0-phase3c-gateway-rc.8"
t.Setenv("VERSEVDI_QUALIFICATION_PROTOCOL_VERSION", version)
got, err := qualificationProtocolVersion()
if err != nil || got != version {
t.Fatalf("qualificationProtocolVersion() = %q, want %q", got, version)
}
for _, invalid := range []string{"", "unknown", "v1", " v1.0.0", "v1.0.0+mutable"} {
t.Setenv("VERSEVDI_QUALIFICATION_PROTOCOL_VERSION", invalid)
if _, err := qualificationProtocolVersion(); err == nil {
t.Fatalf("qualificationProtocolVersion() accepted %q", invalid)
}
}
}
func TestQualificationRecordsLinkedToolVersions(t *testing.T) {
versions, err := qualificationToolVersions()
if err != nil || versions["qualification"] != qualificationToolVersion ||
versions["go"] == "" || versions["quic-go"] == "" {
t.Fatalf("qualification tool versions = %#v, %v", versions, err)
}
}
func TestQualificationOutputAndStatisticsFailClosed(t *testing.T) {
if err := validateQualificationOutputDir("relative/evidence"); err == nil {
t.Fatal("relative evidence directory was accepted")
}
if err := validateQualificationOutputDir(filepath.Join(t.TempDir(), "evidence")); err != nil {
t.Fatalf("absolute evidence directory rejected: %v", err)
}
summary, err := summarizeQualificationSamples([]time.Duration{
time.Millisecond, 2 * time.Millisecond, 3 * time.Millisecond,
4 * time.Millisecond, 5 * time.Millisecond,
})
if err != nil {
t.Fatal(err)
}
if summary.Count != 5 || summary.Min != time.Millisecond || summary.Median != 3*time.Millisecond ||
summary.P90 != 5*time.Millisecond || summary.P95 != 5*time.Millisecond ||
summary.P99 != 5*time.Millisecond || summary.Max != 5*time.Millisecond ||
summary.Mean != 3*time.Millisecond || summary.Histogram["le_5ms"] != 5 {
t.Fatalf("summary = %#v", summary)
}
if err := enforceQualificationProcessingGate(summary); err != nil {
t.Fatalf("5 ms p95 rejected: %v", err)
}
summary.P95++
if err := enforceQualificationProcessingGate(summary); err == nil {
t.Fatal("p95 above 5 ms was accepted")
}
}
func TestQualificationShortProcessingWritesRawArtifact(t *testing.T) {
profile := qualificationMediaProfile{
Name: "smoke", Codec: "h264", BitrateKbps: 20000,
Duration: time.Second, Warmup: time.Millisecond, PacketBytes: 1000,
}
rawPath := filepath.Join(t.TempDir(), "processing.csv.gz")
summary, err := runQualificationProcessing(t, profile, rawPath)
if err != nil {
t.Fatal(err)
}
if summary.Count < 1 || summary.RawSamplesSHA256 == "" || summary.RawSamplesBytes < 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)
if err != nil {
t.Fatal(err)
}
defer file.Close()
reader, err := gzip.NewReader(file)
if err != nil {
t.Fatal(err)
}
raw, err := io.ReadAll(reader)
if err != nil {
t.Fatal(err)
}
if err := reader.Close(); err != nil {
t.Fatal(err)
}
if !strings.HasPrefix(string(raw), "elapsed_ns,processing_ns\n") || strings.Count(string(raw), "\n") != int(summary.Count)+1 {
t.Fatalf("raw sample rows do not match summary count: %q", raw)
}
}
func TestQualificationProcessingPreservesPayload(t *testing.T) {
profile := qualificationMediaProfiles()[0]
payload := qualificationPayload(profile)
trace, elapsed, err := newQualificationPath(t, profile, profile.BitrateKbps).traverse(t, payload)
if err != nil {
t.Fatal(err)
}
if !trace.PayloadPreserved || !trace.ApolloRecovered || !trace.VerseQUIC {
t.Fatalf("production path trace = %#v", trace)
}
if elapsed <= 0 {
t.Fatalf("processing duration = %s", elapsed)
}
}
func TestQualificationRepeatedTraversalTracksEveryProductionStage(t *testing.T) {
profile := qualificationMediaProfiles()[1]
pacerKbps := (profile.BitrateKbps*int64(profile.PacketBytes+frameHeaderSize) + int64(profile.PacketBytes) - 1) / int64(profile.PacketBytes)
path := newQualificationPath(t, profile, pacerKbps)
payload := qualificationPayload(profile)
if err := runQualificationWarmup(t, path, profile, payload); err != nil {
t.Fatal(err)
}
for index := 0; index < 25_000; index++ {
current := append([]byte(nil), payload...)
binary.BigEndian.PutUint32(current[len(current)-4:], uint32(index))
trace, _, err := path.traverse(t, current)
if err != nil {
t.Fatal(err)
}
if !trace.NativeUDPIngress || !trace.ApolloRecovered || !trace.ProductionQueue ||
!trace.ProductionMediaLoop || !trace.ProductionPacer || !trace.VerseQUIC ||
!trace.PublicClientDecode || !trace.PayloadPreserved {
t.Fatalf("traversal %d missed a production stage: %#v", index, trace)
}
}
}
func TestQualificationImpairmentIsDeterministicAndBounded(t *testing.T) {
profile := qualificationImpairmentProfiles()[3]
first, err := runQualificationImpairment(t, profile, qualificationMediaProfiles()[0], 1000, filepath.Join(t.TempDir(), "first.csv.gz"))
if err != nil {
t.Fatal(err)
}
second, err := runQualificationImpairment(t, profile, qualificationMediaProfiles()[0], 1000, filepath.Join(t.TempDir(), "second.csv.gz"))
if err != nil {
t.Fatal(err)
}
if first.Dropped != second.Dropped || first.InjectedReordered != second.InjectedReordered {
t.Fatalf("deterministic impairment selection differs:\n%#v\n%#v", first, second)
}
if first.Sent != 1000 || first.Delivered+first.Dropped != first.Sent ||
first.ObservedLossPercent < 3.5 || first.ObservedLossPercent > 6.5 ||
first.MaxQueuePackets > qualificationImpairmentQueuePackets || first.RawSamplesSHA256 == "" {
t.Fatalf("impairment observation = %#v", first)
}
if _, err := runQualificationImpairment(t, profile, qualificationMediaProfiles()[0], qualificationImpairmentMaxPackets+1, filepath.Join(t.TempDir(), "invalid.csv.gz")); err == nil {
t.Fatal("unbounded impairment packet count was accepted")
}
unknown := profile
unknown.Name = "private-simulator"
if _, err := runQualificationImpairment(t, unknown, qualificationMediaProfiles()[0], 1, filepath.Join(t.TempDir(), "unknown.csv.gz")); err == nil {
t.Fatal("unregistered impairment profile was accepted")
}
}
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 {
t.Fatalf("impairment profile count = %d, want exactly 6", len(profiles))
}
for _, profile := range profiles {
observation, err := runQualificationImpairment(t, profile, qualificationMediaProfiles()[0], 40,
filepath.Join(t.TempDir(), profile.Name+".csv.gz"))
if err != nil {
t.Fatalf("%s: %v", profile.Name, err)
}
if observation.Profile != profile.Name || observation.Delivered+observation.Dropped != 40 ||
observation.RawSamplesSHA256 == "" || observation.MaxQueuePackets > qualificationImpairmentQueuePackets {
t.Fatalf("%s observation = %#v", profile.Name, observation)
}
}
}
func TestQualificationLossAndSteppedThroughputBounds(t *testing.T) {
profiles := qualificationImpairmentProfiles()
for _, profile := range profiles[3:] {
observation, err := runQualificationImpairment(t, profile, qualificationMediaProfiles()[0],
qualificationImpairmentPacketCount, filepath.Join(t.TempDir(), profile.Name+".csv.gz"))
if err != nil {
t.Fatalf("%s: %v", profile.Name, err)
}
if observation.ObservedThroughputKbps <= 0 ||
profile.Name == "constrained" && len(observation.CapacityStepObservations) != 2 {
t.Fatalf("%s observation = %#v", profile.Name, observation)
}
}
}
func TestQualificationUsesPublicQUICAndProductionPacer(t *testing.T) {
qualificationTraverseProfiles(t, qualificationMediaProfiles())
evidence, err := qualificationPacerEvidence(t, filepath.Join(t.TempDir(), "fairness.csv.gz"), 2*time.Second, 4*time.Second)
if err != nil {
t.Fatal(err)
}
if len(evidence.PerFlowBytes) != 8 || len(evidence.CapacitySteps) != 2 ||
len(evidence.Series) != 10 || evidence.RawSamplesSHA256 == "" || evidence.JainIndex < 0.99 {
t.Fatalf("pacer evidence = %#v", evidence)
}
for _, step := range evidence.CapacitySteps {
if step.Convergence > 10*time.Second || step.MaximumFiveSecond > step.FiveSecondCap*105/100 {
t.Fatalf("capacity step = %#v", step)
}
}
}
func TestQualificationSmokeTraversesNativeApolloRecoveryQueuePacerAndQUIC(t *testing.T) {
trace := qualificationProductionPathSmoke(t, qualificationMediaProfiles()[0])
if !trace.ApolloRecovered || !trace.ProductionQueue || !trace.ProductionPacer ||
!trace.VerseQUIC || !trace.PayloadPreserved {
t.Fatalf("qualification production-path trace = %#v", trace)
}
}