diff --git a/gateway/qualification_contract_test.go b/gateway/qualification_contract_test.go index 7084711..5e10eab 100644 --- a/gateway/qualification_contract_test.go +++ b/gateway/qualification_contract_test.go @@ -212,26 +212,31 @@ func TestQualificationSixImpairmentProfilesTraverseProductionPath(t *testing.T) } } +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, 2*time.Second) + 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) != 6 || evidence.RawSamplesSHA256 == "" || evidence.JainIndex < 0.99 { + len(evidence.Series) != 10 || evidence.RawSamplesSHA256 == "" || evidence.JainIndex < 0.99 { t.Fatalf("pacer evidence = %#v", evidence) } -} - -func TestQualificationCapacityStepsConvergeWithinTenSeconds(t *testing.T) { - evidence, err := qualificationPacerEvidence(t, filepath.Join(t.TempDir(), "fairness.csv.gz"), 2*time.Second, 10*time.Second) - if err != nil { - t.Fatal(err) - } - if len(evidence.CapacitySteps) != 2 { - t.Fatalf("capacity steps = %#v", evidence.CapacitySteps) - } for _, step := range evidence.CapacitySteps { if step.Convergence > 10*time.Second || step.MaximumFiveSecond > step.FiveSecondCap*105/100 { t.Fatalf("capacity step = %#v", step) diff --git a/gateway/qualification_harness_test.go b/gateway/qualification_harness_test.go index 34d5f76..4a2a9f9 100644 --- a/gateway/qualification_harness_test.go +++ b/gateway/qualification_harness_test.go @@ -1187,8 +1187,17 @@ func runQualificationImpairment(t *testing.T, profile qualificationImpairmentPro observation.ObservedLossPercent = float64(observation.Dropped) * 100 / float64(packetCount) observation.ObservedReorderPercent = float64(observation.ObservedOutOfOrder) * 100 / float64(packetCount) if packetCount >= qualificationImpairmentPacketCount { - lowerThroughput := float64(media.BitrateKbps) * (1 - profile.LossPercent/100) * 0.90 - upperThroughput := float64(media.BitrateKbps) * 1.05 + capacityFactor := 1.0 + if len(profile.CapacitySteps) > 0 { + inverseRates := 1.0 + for _, reduction := range profile.CapacitySteps { + inverseRates += 100 / float64(100-reduction) + } + capacityFactor = float64(len(profile.CapacitySteps)+1) / inverseRates + } + expectedThroughput := float64(media.BitrateKbps) * (1 - profile.LossPercent/100) * capacityFactor + lowerThroughput := expectedThroughput * 0.90 + upperThroughput := expectedThroughput * 1.05 if observation.ObservedThroughputKbps < lowerThroughput || observation.ObservedThroughputKbps > upperThroughput { return qualificationImpairmentObservation{}, fmt.Errorf("observed throughput %.2f outside [%.2f,%.2f]", observation.ObservedThroughputKbps, lowerThroughput, upperThroughput) }