|
3 | 3 | #include <catch2/catch_test_macros.hpp> |
4 | 4 |
|
5 | 5 | using autoConfig::probePolicy::YoutubeRampEvidence; |
| 6 | +using autoConfig::probePolicy::YoutubeBaselineAssessment; |
| 7 | +using autoConfig::probePolicy::YoutubeBaselineDecision; |
| 8 | +using autoConfig::probePolicy::YoutubeConfirmationDecision; |
| 9 | +using autoConfig::probePolicy::YoutubeProbeSampleClass; |
| 10 | +using autoConfig::probePolicy::YoutubeProbeSampleMetrics; |
| 11 | +using autoConfig::probePolicy::assessYoutubeBaseline; |
| 12 | +using autoConfig::probePolicy::classifyYoutubeProbeSample; |
6 | 13 | using autoConfig::probePolicy::clampEstimateToObservedSafe; |
| 14 | +using autoConfig::probePolicy::decideYoutubeConfirmation; |
7 | 15 | using autoConfig::probePolicy::effectiveProbeCeilingKbps; |
8 | 16 | using autoConfig::probePolicy::hasProbeThroughputMetrics; |
| 17 | +using autoConfig::probePolicy::makeYoutubeProbeSampleMetrics; |
9 | 18 | using autoConfig::probePolicy::probeSubstepProgress; |
10 | 19 | using autoConfig::probePolicy::reachedEffectiveProbeCeiling; |
| 20 | +using autoConfig::probePolicy::resolveYoutubeBaseline; |
11 | 21 | using autoConfig::probePolicy::safeVideoKbps; |
| 22 | +using autoConfig::probePolicy::youtubeLowControlRecovered; |
| 23 | +using autoConfig::probePolicy::youtubeSampleAccepted; |
| 24 | + |
| 25 | +TEST_CASE("YouTube probe metrics use deterministic basis-point ratios") |
| 26 | +{ |
| 27 | + const YoutubeProbeSampleMetrics sample = makeYoutubeProbeSampleMetrics(900, 1000, 3, 150, 10, 2, 100); |
| 28 | + |
| 29 | + CHECK(sample.throughputBasisPoints == 9000); |
| 30 | + CHECK(sample.dropBasisPoints == 200); |
| 31 | + CHECK(sample.congestionHighBasisPoints == 1000); |
| 32 | + CHECK(sample.congestionSevereBasisPoints == 200); |
| 33 | + CHECK(classifyYoutubeProbeSample(sample) == YoutubeProbeSampleClass::Clean); |
| 34 | +} |
| 35 | + |
| 36 | +TEST_CASE("YouTube probe sample classification honors clean and hard boundaries") |
| 37 | +{ |
| 38 | + CHECK(classifyYoutubeProbeSample({9000, 200, 1000, 200}) == YoutubeProbeSampleClass::Clean); |
| 39 | + CHECK(classifyYoutubeProbeSample({8999, 200, 1000, 200}) == YoutubeProbeSampleClass::Marginal); |
| 40 | + CHECK(classifyYoutubeProbeSample({7500, 500, 3000, 1000}) == YoutubeProbeSampleClass::Marginal); |
| 41 | + CHECK(classifyYoutubeProbeSample({7499, 0, 0, 0}) == YoutubeProbeSampleClass::Hard); |
| 42 | + CHECK(classifyYoutubeProbeSample({10000, 501, 0, 0}) == YoutubeProbeSampleClass::Hard); |
| 43 | + CHECK(classifyYoutubeProbeSample({10000, 0, 3001, 0}) == YoutubeProbeSampleClass::Hard); |
| 44 | + CHECK(classifyYoutubeProbeSample({10000, 0, 0, 1001}) == YoutubeProbeSampleClass::Hard); |
| 45 | +} |
| 46 | + |
| 47 | +TEST_CASE("YouTube probe ignores isolated congestion but rejects sustained congestion") |
| 48 | +{ |
| 49 | + const YoutubeProbeSampleMetrics isolatedSpike = makeYoutubeProbeSampleMetrics(950, 1000, 0, 150, 1, 0, 100); |
| 50 | + const YoutubeProbeSampleMetrics sustainedCongestion = makeYoutubeProbeSampleMetrics(950, 1000, 0, 150, 31, 0, 100); |
| 51 | + |
| 52 | + CHECK(classifyYoutubeProbeSample(isolatedSpike) == YoutubeProbeSampleClass::Clean); |
| 53 | + CHECK(classifyYoutubeProbeSample(sustainedCongestion) == YoutubeProbeSampleClass::Hard); |
| 54 | +} |
| 55 | + |
| 56 | +TEST_CASE("Two YouTube baseline samples select every initial decision") |
| 57 | +{ |
| 58 | + const YoutubeProbeSampleMetrics cleanA{9500, 100, 500, 100}; |
| 59 | + const YoutubeProbeSampleMetrics cleanB{9300, 150, 700, 150}; |
| 60 | + const YoutubeProbeSampleMetrics marginalA{8500, 300, 1500, 300}; |
| 61 | + const YoutubeProbeSampleMetrics marginalB{8200, 350, 2000, 400}; |
| 62 | + const YoutubeProbeSampleMetrics hardA{7000, 300, 1500, 300}; |
| 63 | + const YoutubeProbeSampleMetrics hardB{8000, 600, 1500, 300}; |
| 64 | + |
| 65 | + CHECK(assessYoutubeBaseline(cleanA, cleanB).decision == YoutubeBaselineDecision::Clean); |
| 66 | + CHECK(assessYoutubeBaseline(cleanA, {10500, 200, 1000, 200}).decision == YoutubeBaselineDecision::NeedsThird); |
| 67 | + CHECK(assessYoutubeBaseline(marginalA, marginalB).decision == YoutubeBaselineDecision::Impaired); |
| 68 | + CHECK(assessYoutubeBaseline(cleanA, marginalA).decision == YoutubeBaselineDecision::NeedsThird); |
| 69 | + CHECK(assessYoutubeBaseline(hardA, hardB).decision == YoutubeBaselineDecision::Unstable); |
| 70 | +} |
| 71 | + |
| 72 | +TEST_CASE("A third YouTube baseline sample resolves from component medians") |
| 73 | +{ |
| 74 | + const YoutubeProbeSampleMetrics clean{9500, 100, 500, 100}; |
| 75 | + const YoutubeProbeSampleMetrics marginal{8500, 300, 1500, 300}; |
| 76 | + const YoutubeProbeSampleMetrics hard{6000, 700, 4000, 1500}; |
| 77 | + |
| 78 | + const YoutubeBaselineAssessment impaired = resolveYoutubeBaseline(clean, hard, marginal); |
| 79 | + CHECK(impaired.decision == YoutubeBaselineDecision::Impaired); |
| 80 | + CHECK(impaired.reference.throughputBasisPoints == marginal.throughputBasisPoints); |
| 81 | + CHECK(impaired.reference.dropBasisPoints == marginal.dropBasisPoints); |
| 82 | + CHECK(impaired.reference.congestionHighBasisPoints == marginal.congestionHighBasisPoints); |
| 83 | + CHECK(impaired.reference.congestionSevereBasisPoints == marginal.congestionSevereBasisPoints); |
| 84 | + |
| 85 | + CHECK(resolveYoutubeBaseline(clean, hard, clean).decision == YoutubeBaselineDecision::Clean); |
| 86 | + CHECK(resolveYoutubeBaseline(hard, clean, hard).decision == YoutubeBaselineDecision::Unstable); |
| 87 | + CHECK(resolveYoutubeBaseline({7000, 100, 500, 100}, clean, {9500, 600, 500, 100}).decision == YoutubeBaselineDecision::Unstable); |
| 88 | +} |
| 89 | + |
| 90 | +TEST_CASE("Impaired YouTube baseline accepts only bounded relative degradation") |
| 91 | +{ |
| 92 | + const YoutubeBaselineAssessment baseline{YoutubeBaselineDecision::Impaired, {8500, 300, 1500, 300}}; |
| 93 | + |
| 94 | + CHECK(youtubeSampleAccepted({8000, 400, 2500, 800}, baseline)); |
| 95 | + CHECK_FALSE(youtubeSampleAccepted({7999, 400, 2500, 800}, baseline)); |
| 96 | + CHECK_FALSE(youtubeSampleAccepted({8000, 401, 2500, 800}, baseline)); |
| 97 | + CHECK_FALSE(youtubeSampleAccepted({8000, 400, 2501, 800}, baseline)); |
| 98 | + CHECK_FALSE(youtubeSampleAccepted({8000, 400, 2500, 801}, baseline)); |
| 99 | + |
| 100 | + const YoutubeBaselineAssessment cleanBaseline{YoutubeBaselineDecision::Clean, {9500, 100, 500, 100}}; |
| 101 | + CHECK(youtubeSampleAccepted({9200, 100, 500, 100}, cleanBaseline)); |
| 102 | + CHECK_FALSE(youtubeSampleAccepted({8500, 100, 500, 100}, cleanBaseline)); |
| 103 | +} |
| 104 | + |
| 105 | +TEST_CASE("YouTube low control must recover acceptance and the previous rung") |
| 106 | +{ |
| 107 | + const YoutubeBaselineAssessment baseline{YoutubeBaselineDecision::Impaired, {8500, 300, 1500, 300}}; |
| 108 | + const YoutubeProbeSampleMetrics original{8800, 300, 1000, 200}; |
| 109 | + |
| 110 | + CHECK(youtubeLowControlRecovered({8300, 400, 2000, 700}, original, baseline)); |
| 111 | + CHECK_FALSE(youtubeLowControlRecovered({8299, 400, 2000, 700}, original, baseline)); |
| 112 | + CHECK_FALSE(youtubeLowControlRecovered({8300, 401, 2000, 700}, original, baseline)); |
| 113 | + CHECK_FALSE(youtubeLowControlRecovered({8300, 400, 2001, 700}, original, baseline)); |
| 114 | + CHECK_FALSE(youtubeLowControlRecovered({8300, 400, 2000, 701}, original, baseline)); |
| 115 | +} |
| 116 | + |
| 117 | +TEST_CASE("YouTube high-low-high confirmation distinguishes all four outcomes") |
| 118 | +{ |
| 119 | + CHECK(decideYoutubeConfirmation(true, false) == YoutubeConfirmationDecision::CapacityKnee); |
| 120 | + CHECK(decideYoutubeConfirmation(true, true) == YoutubeConfirmationDecision::TransientRecovered); |
| 121 | + CHECK(decideYoutubeConfirmation(false, false) == YoutubeConfirmationDecision::PathUnstable); |
| 122 | + CHECK(decideYoutubeConfirmation(false, true) == YoutubeConfirmationDecision::Inconsistent); |
| 123 | +} |
12 | 124 |
|
13 | 125 | TEST_CASE("YouTube first-rung failure retains a conservative observed cap") |
14 | 126 | { |
|
0 commit comments