Merge render-bounds channel experiment: hand the window over on a switchable channel, and print a verdict that is proof only when no floored model fits
This commit is contained in:
@@ -111,18 +111,26 @@ static void testLabelsSeparateExactlyWhatTheRenderSeparates() {
|
||||
|
||||
// --- tail: TailMode -> RENDER_* mapping (docs/product/capture-tail.md) --------
|
||||
|
||||
// The tail assertions below are about the MODE's mapping; the one value that also
|
||||
// depends on the bounds channel has its own test, so they all pin the channel that
|
||||
// every shipped capture rendered on.
|
||||
static TailRenderSettings tailFor(TailMode mode, double manualTailMs) {
|
||||
return tailRenderSettingsFor(mode, manualTailMs,
|
||||
RenderBoundsChannel::CustomTimeBounds);
|
||||
}
|
||||
|
||||
static void testTailNoneIsExactBounds() {
|
||||
// None -> exact bounds, byte-identical to the pre-tail capture: tail flag clear,
|
||||
// 0 ms, disable-all normalize (the current default), no trim. Asserting the exact
|
||||
// bit values (not just "some value") pins the byte-identical contract: if the
|
||||
// mapping regressed to set a tail bit or a non-disable-all normalize, this fails.
|
||||
TailRenderSettings t = tailRenderSettingsFor(TailMode::None, 0.0);
|
||||
TailRenderSettings t = tailFor(TailMode::None, 0.0);
|
||||
CHECK(t.tailFlag == kTailFlagNone); // 0
|
||||
CHECK(t.tailMs == 0.0);
|
||||
CHECK(t.normalize == kNormalizeDisableAll); // 262144
|
||||
CHECK(t.trimEnd == 0.0);
|
||||
// manualTailMs must be ignored for None (a stray tail from a leftover ms is the bug).
|
||||
TailRenderSettings t2 = tailRenderSettingsFor(TailMode::None, 5000.0);
|
||||
TailRenderSettings t2 = tailFor(TailMode::None, 5000.0);
|
||||
CHECK(t2.tailFlag == kTailFlagNone);
|
||||
CHECK(t2.tailMs == 0.0);
|
||||
}
|
||||
@@ -131,7 +139,7 @@ static void testTailAutoIsSurgicalTrim() {
|
||||
// Auto -> custom-bounds tail bit, 8 s cap, SURGICAL normalize (ONLY &32768), and
|
||||
// the -72 dB TRIMEND ratio. The disable-all bit must NOT be set (it is semantically
|
||||
// opposed to trim — this assertion catches a regression to the None normalize).
|
||||
TailRenderSettings t = tailRenderSettingsFor(TailMode::Auto, 0.0);
|
||||
TailRenderSettings t = tailFor(TailMode::Auto, 0.0);
|
||||
CHECK(t.tailFlag == kTailFlagCustomBounds); // &1
|
||||
CHECK(t.tailMs == kMaxTailMs); // 8000
|
||||
CHECK(t.normalize == kNormalizeTrimEnd); // exactly 32768, nothing else
|
||||
@@ -139,7 +147,7 @@ static void testTailAutoIsSurgicalTrim() {
|
||||
// TRIMEND is the derived -72 dB ratio ~= 0.00025119 (the DAW-confirm value).
|
||||
CHECK(std::fabs(t.trimEnd - 0.00025119) < 1e-8);
|
||||
// manualTailMs is ignored for Auto (Auto always uses the 8 s cap).
|
||||
CHECK(tailRenderSettingsFor(TailMode::Auto, 3000.0).tailMs == kMaxTailMs);
|
||||
CHECK(tailFor(TailMode::Auto, 3000.0).tailMs == kMaxTailMs);
|
||||
}
|
||||
|
||||
static void testAutoTrimRatioDerivesFromDb() {
|
||||
@@ -147,7 +155,7 @@ static void testAutoTrimRatioDerivesFromDb() {
|
||||
// float — recompute it independently and require an exact match with the mapping.
|
||||
double expected = std::pow(10.0, kAutoTrimThresholdDb / 20.0);
|
||||
CHECK(autoTrimEndRatio() == expected);
|
||||
CHECK(tailRenderSettingsFor(TailMode::Auto, 0.0).trimEnd == expected);
|
||||
CHECK(tailFor(TailMode::Auto, 0.0).trimEnd == expected);
|
||||
// Sanity: -72 dB is well below unity but above zero.
|
||||
CHECK(expected > 0.0 && expected < 0.001);
|
||||
}
|
||||
@@ -156,7 +164,7 @@ static void testTailManualFixedNoTrim() {
|
||||
// Manual -> custom-bounds tail, the requested ms (within cap), disable-all
|
||||
// normalize (no trim). A Manual capture is a fixed tail, so it keeps today's
|
||||
// disable-all exactly like the no-tail path.
|
||||
TailRenderSettings t = tailRenderSettingsFor(TailMode::Manual, 2500.0);
|
||||
TailRenderSettings t = tailFor(TailMode::Manual, 2500.0);
|
||||
CHECK(t.tailFlag == kTailFlagCustomBounds);
|
||||
CHECK(t.tailMs == 2500.0);
|
||||
CHECK(t.normalize == kNormalizeDisableAll);
|
||||
@@ -165,12 +173,67 @@ static void testTailManualFixedNoTrim() {
|
||||
|
||||
static void testTailManualClampsToCap() {
|
||||
// The 8 s cap is a runaway guard that applies to Manual too: ms > 8000 -> 8000.
|
||||
CHECK(tailRenderSettingsFor(TailMode::Manual, 9000.0).tailMs == kMaxTailMs);
|
||||
CHECK(tailRenderSettingsFor(TailMode::Manual, 8000.0).tailMs == kMaxTailMs);
|
||||
CHECK(tailFor(TailMode::Manual, 9000.0).tailMs == kMaxTailMs);
|
||||
CHECK(tailFor(TailMode::Manual, 8000.0).tailMs == kMaxTailMs);
|
||||
// Below the cap is passed through unchanged.
|
||||
CHECK(tailRenderSettingsFor(TailMode::Manual, 100.0).tailMs == 100.0);
|
||||
CHECK(tailFor(TailMode::Manual, 100.0).tailMs == 100.0);
|
||||
// A negative request floors to 0 (no negative tail leaks into RENDER_TAILMS).
|
||||
CHECK(tailRenderSettingsFor(TailMode::Manual, -50.0).tailMs == 0.0);
|
||||
CHECK(tailFor(TailMode::Manual, -50.0).tailMs == 0.0);
|
||||
}
|
||||
|
||||
// --- bounds channel: RENDER_BOUNDSFLAG mode + the tail bit it drags along ------
|
||||
|
||||
static void testEachChannelNamesItsOwnBoundsFlagMode() {
|
||||
// The two RENDER_BOUNDSFLAG values, as literals from the SDK header — 0 = custom
|
||||
// time bounds, 2 = time selection. Pinned as numbers so a renumbering of the enum
|
||||
// cannot silently point a capture at "entire project" or "selected media items".
|
||||
CHECK(renderBoundsFlagFor(RenderBoundsChannel::CustomTimeBounds) == 0);
|
||||
CHECK(renderBoundsFlagFor(RenderBoundsChannel::TimeSelection) == 2);
|
||||
}
|
||||
|
||||
static void testTailBitFollowsTheBoundsChannel() {
|
||||
// RENDER_TAILFLAG's bits are per-bounds-mode: &1 covers custom time bounds, &4
|
||||
// covers the time selection. A tail set under the other channel's bit renders no
|
||||
// tail at all, which is why the mapping takes the channel rather than trusting a
|
||||
// caller to OR the right one in.
|
||||
CHECK(tailFlagBitFor(RenderBoundsChannel::CustomTimeBounds) == 1);
|
||||
CHECK(tailFlagBitFor(RenderBoundsChannel::TimeSelection) == 4);
|
||||
|
||||
// Both tail-bearing modes follow it — a fix applied to Auto alone would leave
|
||||
// Manual rendering under a bit the bounds mode does not read.
|
||||
for (TailMode mode : {TailMode::Auto, TailMode::Manual}) {
|
||||
CHECK(tailRenderSettingsFor(mode, 2500.0,
|
||||
RenderBoundsChannel::CustomTimeBounds)
|
||||
.tailFlag == kTailFlagCustomBounds);
|
||||
CHECK(tailRenderSettingsFor(mode, 2500.0,
|
||||
RenderBoundsChannel::TimeSelection)
|
||||
.tailFlag == kTailFlagTimeSelection);
|
||||
}
|
||||
}
|
||||
|
||||
static void testNoneSetsNoTailBitOnEitherChannel() {
|
||||
// None is exact bounds on every channel: no tail bit, so no channel's bit either.
|
||||
CHECK(tailRenderSettingsFor(TailMode::None, 5000.0,
|
||||
RenderBoundsChannel::CustomTimeBounds)
|
||||
.tailFlag == kTailFlagNone);
|
||||
CHECK(tailRenderSettingsFor(TailMode::None, 5000.0,
|
||||
RenderBoundsChannel::TimeSelection)
|
||||
.tailFlag == kTailFlagNone);
|
||||
}
|
||||
|
||||
static void testTheChannelLabelNamesTheModeAndItsStore() {
|
||||
// The console verdict is read by someone deciding which channel to keep, so the
|
||||
// label has to name both the mode number and where the window actually went.
|
||||
const std::string custom = renderBoundsChannelLabel(RenderBoundsChannel::CustomTimeBounds);
|
||||
CHECK(custom.find("RENDER_BOUNDSFLAG=0") != std::string::npos);
|
||||
CHECK(custom.find("RENDER_STARTPOS") != std::string::npos);
|
||||
|
||||
const std::string ts = renderBoundsChannelLabel(RenderBoundsChannel::TimeSelection);
|
||||
CHECK(ts.find("RENDER_BOUNDSFLAG=2") != std::string::npos);
|
||||
CHECK(ts.find("GetSet_LoopTimeRange") != std::string::npos);
|
||||
|
||||
// Two channels that read alike in the console would make the experiment unreadable.
|
||||
CHECK(custom != ts);
|
||||
}
|
||||
|
||||
// --- realtimeRecordWindowEnd: the T2 record-window extension -----------------
|
||||
@@ -321,6 +384,23 @@ static void testMultiTrackStemRenderIsNamedForRefusal() {
|
||||
CHECK(!isMultiTrackStemRender(sourceModeForScope(CaptureScope::Item, true), 2));
|
||||
}
|
||||
|
||||
static void testSourceBypassesBoundsChannelOnlyForContentDerivedSources() {
|
||||
// SelectedItems (&32) and RazorArea (&4096) derive their bounds from the
|
||||
// selected items'/areas' own extents -- RENDER_BOUNDSFLAG is never consulted, so
|
||||
// a bounds-channel verdict is not evidence for either (the regression this
|
||||
// predicate exists to catch: RunBatchCaptureItems always renders through
|
||||
// SelectedItems, so this false-EXACT would fire on every batch-item capture).
|
||||
CHECK(sourceBypassesBoundsChannel(SourceMode::SelectedItems));
|
||||
CHECK(sourceBypassesBoundsChannel(SourceMode::RazorArea));
|
||||
|
||||
// Every other source is genuinely time-bounded through RENDER_STARTPOS/ENDPOS or
|
||||
// the time selection, so the channel IS the evidence for these.
|
||||
CHECK(!sourceBypassesBoundsChannel(SourceMode::MasterMix));
|
||||
CHECK(!sourceBypassesBoundsChannel(SourceMode::TimeSelection));
|
||||
CHECK(!sourceBypassesBoundsChannel(SourceMode::SelectedTracks));
|
||||
CHECK(!sourceBypassesBoundsChannel(SourceMode::Realtime));
|
||||
}
|
||||
|
||||
static void testRefusalMessagesAreSiblingsWithDistinctExits() {
|
||||
const std::string item = multiTrackRefusalMessage(CaptureScope::Item);
|
||||
const std::string track = multiTrackRefusalMessage(CaptureScope::Track);
|
||||
@@ -448,6 +528,10 @@ int main() {
|
||||
testAutoTrimRatioDerivesFromDb();
|
||||
testTailManualFixedNoTrim();
|
||||
testTailManualClampsToCap();
|
||||
testEachChannelNamesItsOwnBoundsFlagMode();
|
||||
testTailBitFollowsTheBoundsChannel();
|
||||
testNoneSetsNoTailBitOnEitherChannel();
|
||||
testTheChannelLabelNamesTheModeAndItsStore();
|
||||
testRealtimeWindowNoneIsExact();
|
||||
testRealtimeWindowAutoAddsCap();
|
||||
testRealtimeWindowManualAddsClampedLength();
|
||||
@@ -459,6 +543,7 @@ int main() {
|
||||
testScopeSourceModes();
|
||||
testRangedItemScopeRendersTimeBounded();
|
||||
testMultiTrackStemRenderIsNamedForRefusal();
|
||||
testSourceBypassesBoundsChannelOnlyForContentDerivedSources();
|
||||
testRefusalMessagesAreSiblingsWithDistinctExits();
|
||||
testRefusalMessagesMatchGoldenLiterals();
|
||||
testRangeInference();
|
||||
|
||||
@@ -315,6 +315,297 @@ static void testASubMillisecondStartWouldNotHideItself() {
|
||||
frameCountFor(1.000, end, 48000)));
|
||||
}
|
||||
|
||||
static void testTheTwoLiveShortRendersPinnedAtFullPrecision() {
|
||||
// 1.6551724137931001 is the console's own %.17g read-back. 4.0677966101694913 is
|
||||
// the double nearest the six-decimal value (4.067797) the earlier refusal actually
|
||||
// printed -- that refusal predates the %.17g printer (git history has no commit
|
||||
// introducing this literal as a console value), so it is a reconstruction, not a
|
||||
// captured one. 240/145 and 240/59 (testTheSixDecimalDisplayDidNotCreateTheEffect)
|
||||
// produce the SAME counts as the literals here, so this test cannot distinguish the
|
||||
// real value from the reconstruction either -- it pins the count regression (full
|
||||
// precision or six-decimal input, the frame counts agree), not which double REAPER
|
||||
// was really handed.
|
||||
CHECK(frameCountFor(0.0, 1.6551724137931001, 48000) == 79448);
|
||||
CHECK(msFlooredEndFrameCount(0.0, 1.6551724137931001, 48000) == 79440);
|
||||
|
||||
CHECK(frameCountFor(0.0, 4.0677966101694913, 48000) == 195254);
|
||||
CHECK(msFlooredEndFrameCount(0.0, 4.0677966101694913, 48000) == 195216);
|
||||
|
||||
// And the counts REAPER produced are outside the gate's tolerance in both cases —
|
||||
// the refusals were correct, not an artifact of the one-frame slack.
|
||||
CHECK(!renderHonoredBounds(79448, 79440));
|
||||
CHECK(!renderHonoredBounds(195254, 195216));
|
||||
}
|
||||
|
||||
// --- isOnMillisecondGrid: whether an observation can speak to an edge ----------
|
||||
|
||||
static void testOnGridRecognizesWholeMillisecondsIncludingTheBinaryTrap() {
|
||||
CHECK(isOnMillisecondGrid(0.0));
|
||||
CHECK(isOnMillisecondGrid(2.0));
|
||||
CHECK(isOnMillisecondGrid(0.001));
|
||||
// 1.007 s does not multiply to exactly 1007.0 in double (pinned as the premise in
|
||||
// testWindowAlreadyOnTheMillisecondGridLosesNothing) and must still read as on-grid.
|
||||
CHECK(isOnMillisecondGrid(1.007));
|
||||
// A whole millisecond at 44.1 kHz is 44.1 frames — off the frame grid, on this one.
|
||||
CHECK(isOnMillisecondGrid(0.010));
|
||||
}
|
||||
|
||||
static void testOffGridRecognizesASubMillisecondRemainder() {
|
||||
CHECK(!isOnMillisecondGrid(1.6551724137931001));
|
||||
CHECK(!isOnMillisecondGrid(1.0001724));
|
||||
// One frame short of a whole second at 48 kHz is ~0.0208 ms off the grid — the
|
||||
// tightest remainder this predicate has to keep seeing.
|
||||
CHECK(!isOnMillisecondGrid(1.0 - 1.0 / 48000.0));
|
||||
}
|
||||
|
||||
// --- describeBoundsExperiment: the console verdict on a bounds channel --------
|
||||
|
||||
static void testAnExactRenderReadsExactAndNamesItsChannel() {
|
||||
const std::string s =
|
||||
describeBoundsExperiment("time selection (RENDER_BOUNDSFLAG=2)",
|
||||
0.0, 1.6551724137931001, 79448, 48000);
|
||||
CHECK(contains(s, "EXACT"));
|
||||
CHECK(!contains(s, "SHORT"));
|
||||
CHECK(contains(s, "time selection (RENDER_BOUNDSFLAG=2)"));
|
||||
CHECK(contains(s, "79448"));
|
||||
CHECK(contains(s, "48000 Hz"));
|
||||
// The END here carries a sub-millisecond remainder, so this run DID test it --
|
||||
// the END-untested caveat must not fire on a window it didn't apply to.
|
||||
CHECK(!contains(s, "END edge is UNTESTED"));
|
||||
}
|
||||
|
||||
static void testTheLiveShortfallReadsShortAndNamesTheMillisecondShape() {
|
||||
// The observation, replayed through the verdict: 79440 produced against 79448.
|
||||
const std::string s =
|
||||
describeBoundsExperiment("custom time bounds (RENDER_BOUNDSFLAG=0)",
|
||||
0.0, 1.6551724137931001, 79440, 48000);
|
||||
CHECK(contains(s, "SHORT"));
|
||||
CHECK(!contains(s, "EXACT"));
|
||||
CHECK(contains(s, "79440"));
|
||||
CHECK(contains(s, "79448"));
|
||||
// 79440 IS the ms-floored count, so the verdict has to say the floor did not move.
|
||||
CHECK(contains(s, "floored to the millisecond"));
|
||||
}
|
||||
|
||||
static void testAShortfallThatIsNotTheMillisecondShapeClaimsNothingAboutIt() {
|
||||
// A render 3 frames short is short, but 79445 is not the floored count — the
|
||||
// millisecond sentence must not appear, or it would assert a shape that is absent.
|
||||
CHECK(msFlooredEndFrameCount(0.0, 1.6551724137931001, 48000) != 79445);
|
||||
const std::string s =
|
||||
describeBoundsExperiment("custom time bounds", 0.0, 1.6551724137931001,
|
||||
79445, 48000);
|
||||
CHECK(contains(s, "SHORT"));
|
||||
CHECK(!contains(s, "floored to the millisecond"));
|
||||
}
|
||||
|
||||
static void testARenderPastTheWindowReadsLong() {
|
||||
// The whole-item widening, through the verdict: 30 s printed for a 1 s window.
|
||||
const std::string s =
|
||||
describeBoundsExperiment("custom time bounds", 5.0, 6.0, 30 * 48000, 48000);
|
||||
CHECK(contains(s, "LONG"));
|
||||
CHECK(contains(s, "1440000 frames"));
|
||||
CHECK(contains(s, "the 48000 the window asks for"));
|
||||
}
|
||||
|
||||
static void testAWindowAlreadyOnTheGridIsUnaffectedByTheChannelSwitch() {
|
||||
// A window whose end is a whole millisecond has nothing for a floor to take: the
|
||||
// exact count and the floored count are the same number, so an exact render reads
|
||||
// EXACT and the millisecond sentence never fires.
|
||||
CHECK(frameCountFor(0.0, 2.0, 48000) == msFlooredEndFrameCount(0.0, 2.0, 48000));
|
||||
const std::string s =
|
||||
describeBoundsExperiment("time selection", 0.0, 2.0, 96000, 48000);
|
||||
CHECK(contains(s, "EXACT"));
|
||||
CHECK(contains(s, "96000"));
|
||||
CHECK(!contains(s, "floored to the millisecond"));
|
||||
// The false positive this window is the shape of: a render that floored either edge
|
||||
// alone, or both together, would have printed this identical EXACT count (every
|
||||
// edge here is on the grid) -- the line has to say this run cannot rule any of them
|
||||
// out rather than reading EXACT as settled.
|
||||
CHECK(contains(s, "EXACT here is not proof"));
|
||||
CHECK(contains(s, "floors the START edge alone"));
|
||||
CHECK(contains(s, "floors the END edge alone"));
|
||||
CHECK(contains(s, "floors START and END together"));
|
||||
}
|
||||
|
||||
static void testEqualRemaindersCancelUnderAFullFloorEvenOffGrid() {
|
||||
// C1: a dragged, fixed-length time selection reproduces this. Neither edge sits on
|
||||
// the millisecond grid (isOnMillisecondGrid is false for both), but the START and
|
||||
// END frame-rounding remainders are EQUAL (rs == re == 8 frames), so a render that
|
||||
// floors both edges together lands on the identical count -- the grid predicate on
|
||||
// either edge alone would have missed this collision entirely.
|
||||
const double start = 1.0001724, end = 2.0001724;
|
||||
CHECK(!isOnMillisecondGrid(start));
|
||||
CHECK(!isOnMillisecondGrid(end));
|
||||
const long long expected = frameCountFor(start, end, 48000);
|
||||
CHECK(expected == 48000);
|
||||
// The both-edges-floored render lands on the SAME count as the exact one.
|
||||
CHECK(frameCountFor(1.000, 2.000, 48000) == expected);
|
||||
// Neither edge floored ALONE reproduces it -- only the combined floor does.
|
||||
CHECK(frameCountFor(1.000, end, 48000) != expected);
|
||||
CHECK(frameCountFor(start, 2.000, 48000) != expected);
|
||||
|
||||
const std::string s =
|
||||
describeBoundsExperiment("time selection", start, end, expected, 48000);
|
||||
CHECK(contains(s, "EXACT"));
|
||||
CHECK(contains(s, "EXACT here is not proof"));
|
||||
CHECK(contains(s, "floors START and END together"));
|
||||
CHECK(!contains(s, "floors the START edge alone"));
|
||||
CHECK(!contains(s, "floors the END edge alone"));
|
||||
}
|
||||
|
||||
static void testEndOffGridByUnderHalfAFrameStillCollidesWithAFlooredEnd() {
|
||||
// C1's second live shape: isOnMillisecondGrid reads this END as off-grid, but the
|
||||
// remainder is under half a frame at 48 kHz, so flooring it doesn't move its frame
|
||||
// index -- a grid test on the edge alone would still miss this collision.
|
||||
const double start = 0.0, end = 1.000005;
|
||||
CHECK(!isOnMillisecondGrid(end));
|
||||
const long long expected = frameCountFor(start, end, 48000);
|
||||
CHECK(expected == 48000);
|
||||
CHECK(frameCountFor(start, 1.000, 48000) == expected); // the floored-end model matches
|
||||
|
||||
const std::string s =
|
||||
describeBoundsExperiment("time selection", start, end, expected, 48000);
|
||||
CHECK(contains(s, "EXACT"));
|
||||
CHECK(contains(s, "EXACT here is not proof"));
|
||||
CHECK(contains(s, "floors the END edge alone"));
|
||||
}
|
||||
|
||||
static void testALongVerdictNeverCarriesTheFloorSentence() {
|
||||
// A floor only removes frames, so LONG can never be its signature -- the sentence
|
||||
// must not appear even though the delta here is a "clean" one-frame LONG.
|
||||
const std::string s =
|
||||
describeBoundsExperiment("time selection", 5.0, 6.0, 48001, 48000);
|
||||
CHECK(contains(s, "LONG"));
|
||||
CHECK(!contains(s, "floored to the millisecond"));
|
||||
}
|
||||
|
||||
static void testASubFrameWindowIsNotJudgedNotExact() {
|
||||
// A window under one frame at this rate rounds to 0 expected frames. A 0-frame
|
||||
// render against that is a 0-vs-0 coincidence of degenerate inputs, not a match --
|
||||
// it must read NOT JUDGED, never EXACT.
|
||||
const double oneTenthOfAFrame = 1.0 / (48000.0 * 10.0);
|
||||
const long long expected = frameCountFor(0.0, oneTenthOfAFrame, 48000);
|
||||
CHECK(expected == 0);
|
||||
const std::string s =
|
||||
describeBoundsExperiment("time selection", 0.0, oneTenthOfAFrame, 0, 48000);
|
||||
CHECK(contains(s, "NOT JUDGED"));
|
||||
CHECK(!contains(s, "EXACT"));
|
||||
}
|
||||
|
||||
static void testAWithinToleranceDeltaIsTaggedNotFloorShaped() {
|
||||
// One frame off frameCountFor is the gate's own edge-convention slack
|
||||
// (render_window.h), not the millisecond floor -- the verdict must say so rather
|
||||
// than reading like a genuine miss or like the floor was escaped.
|
||||
const std::string shortByOne =
|
||||
describeBoundsExperiment("time selection", 0.0, 4.067797, 195253, 48000);
|
||||
CHECK(contains(shortByOne, "SHORT"));
|
||||
CHECK(contains(shortByOne, "WITHIN TOLERANCE"));
|
||||
CHECK(!contains(shortByOne, "floored to the millisecond"));
|
||||
|
||||
const std::string longByOne =
|
||||
describeBoundsExperiment("time selection", 0.0, 4.067797, 195255, 48000);
|
||||
CHECK(contains(longByOne, "LONG"));
|
||||
CHECK(contains(longByOne, "WITHIN TOLERANCE"));
|
||||
|
||||
// A genuine miss (outside the tolerance) carries no such tag.
|
||||
const std::string shortByThree =
|
||||
describeBoundsExperiment("time selection", 0.0, 4.067797, 195251, 48000);
|
||||
CHECK(contains(shortByThree, "SHORT"));
|
||||
CHECK(!contains(shortByThree, "WITHIN TOLERANCE"));
|
||||
}
|
||||
|
||||
static void testABypassingSourceReadsNotJudgedAndNamesTheSourceNotTheChannel() {
|
||||
// SelectedItems/RazorArea derive their own bounds from content -- the channel
|
||||
// named by channelLabel was never consulted, so a matching frame count here would
|
||||
// be a coincidence, not evidence the channel escaped the floor.
|
||||
const std::string s =
|
||||
describeBoundsExperiment("time selection", 0.0, 1.6551724137931001,
|
||||
79448, 48000, "selected media items");
|
||||
CHECK(contains(s, "NOT JUDGED"));
|
||||
CHECK(contains(s, "selected media items"));
|
||||
CHECK(!contains(s, "EXACT"));
|
||||
// The channel is still named at the top of the line -- only the verdict changes.
|
||||
CHECK(contains(s, "time selection"));
|
||||
}
|
||||
|
||||
static void testANullOrEmptyBypassLabelFallsBackToTheOrdinaryVerdict() {
|
||||
// Off-grid, non-cancelling edges (see testEqualRemaindersCancelUnderAFullFloorEvenOffGrid
|
||||
// for the window shape that WOULD trip the collision caveat, whose own text also
|
||||
// contains "EXACT") so this assertion is pinned to the verdict word itself, not to a
|
||||
// caveat sentence that happens to contain the same substring.
|
||||
const double start = 1.0001724, end = 2.0009724;
|
||||
const long long expected = frameCountFor(start, end, 48000);
|
||||
const std::string withNull =
|
||||
describeBoundsExperiment("time selection", start, end, expected, 48000, nullptr);
|
||||
CHECK(contains(withNull, "EXACT"));
|
||||
CHECK(!contains(withNull, "EXACT here is not proof"));
|
||||
CHECK(contains(describeBoundsExperiment("time selection", start, end, expected, 48000,
|
||||
""),
|
||||
"EXACT"));
|
||||
}
|
||||
|
||||
static void testAnOnGridStartSaysTheStartEdgeIsUntested() {
|
||||
// Both live observations started at 0 s — the value that hides a start-side floor.
|
||||
const std::string s =
|
||||
describeBoundsExperiment("time selection", 0.0, 1.6551724137931001, 79448, 48000);
|
||||
CHECK(contains(s, "UNTESTED"));
|
||||
CHECK(contains(s, "millisecond grid"));
|
||||
}
|
||||
|
||||
static void testAnOffGridStartSaysTheStartEdgeIsTested() {
|
||||
// The run that would genuinely settle the start question: a start carrying its own
|
||||
// remainder, paired with an end whose remainder does NOT cancel it (unlike
|
||||
// testEqualRemaindersCancelUnderAFullFloorEvenOffGrid's window, where the same shape
|
||||
// of start value pairs with an end that cancels it and the collision caveat fires
|
||||
// instead). No floored model reproduces this count, so EXACT here is unqualified.
|
||||
const double start = 1.0001724, end = 2.0009724;
|
||||
const long long expected = frameCountFor(start, end, 48000);
|
||||
const std::string s =
|
||||
describeBoundsExperiment("time selection", start, end, expected, 48000);
|
||||
CHECK(contains(s, "IS tested"));
|
||||
CHECK(!contains(s, "UNTESTED"));
|
||||
CHECK(contains(s, "EXACT"));
|
||||
CHECK(!contains(s, "EXACT here is not proof"));
|
||||
// A start-floored-alone render would have printed a DIFFERENT count here, so a
|
||||
// mismatch against `expected` on a re-run is real evidence, not ambiguous.
|
||||
CHECK(frameCountFor(1.000, end, 48000) != expected);
|
||||
CHECK(contains(describeBoundsExperiment("time selection", start, end,
|
||||
frameCountFor(1.000, end, 48000), 48000),
|
||||
"LONG"));
|
||||
}
|
||||
|
||||
static void testAt44100WhereAMillisecondIsNotAWholeNumberOfFrames() {
|
||||
// 44.1 kHz: the window is 463 frames, the ms-floored one 441 (both pinned in
|
||||
// testMillisecondFloorAt44100WhereAMillisecondIsNotWholeFrames). The verdict has to
|
||||
// reach the same two numbers at a rate where a millisecond is 44.1 frames.
|
||||
const std::string exact =
|
||||
describeBoundsExperiment("time selection", 0.0, 0.0105, 463, 44100);
|
||||
CHECK(contains(exact, "EXACT"));
|
||||
CHECK(contains(exact, "44100 Hz"));
|
||||
|
||||
const std::string floored =
|
||||
describeBoundsExperiment("custom time bounds", 0.0, 0.0105, 441, 44100);
|
||||
CHECK(contains(floored, "SHORT"));
|
||||
CHECK(contains(floored, "floored to the millisecond"));
|
||||
}
|
||||
|
||||
static void testAnUnmeasuredRenderAnswersNothingRatherThanPassing() {
|
||||
// Auto/Manual are not judged against a frame count, and an empty render has none.
|
||||
// The line must still print and must not read as a pass — its silence would.
|
||||
const std::string s =
|
||||
describeBoundsExperiment("time selection", 0.0, 1.6551724137931001, 0, 0);
|
||||
CHECK(!s.empty());
|
||||
CHECK(contains(s, "NOT JUDGED"));
|
||||
CHECK(!contains(s, "EXACT"));
|
||||
CHECK(contains(s, "time selection"));
|
||||
}
|
||||
|
||||
static void testAnUnnamedChannelStillProducesAReadableLine() {
|
||||
CHECK(contains(describeBoundsExperiment(nullptr, 0.0, 1.0, 48000, 48000),
|
||||
"unnamed"));
|
||||
CHECK(contains(describeBoundsExperiment("", 0.0, 1.0, 48000, 48000), "unnamed"));
|
||||
}
|
||||
|
||||
// --- describeBoundsDrift: the read-back's verdict ------------------------------
|
||||
|
||||
static void testBoundsThatReadBackUnchangedDescribeNothing() {
|
||||
@@ -395,6 +686,26 @@ int main() {
|
||||
testOneFrameOfRemainderStillFloors();
|
||||
testMillisecondFloorAt44100WhereAMillisecondIsNotWholeFrames();
|
||||
testASubMillisecondStartWouldNotHideItself();
|
||||
testTheTwoLiveShortRendersPinnedAtFullPrecision();
|
||||
testOnGridRecognizesWholeMillisecondsIncludingTheBinaryTrap();
|
||||
testOffGridRecognizesASubMillisecondRemainder();
|
||||
testAnExactRenderReadsExactAndNamesItsChannel();
|
||||
testTheLiveShortfallReadsShortAndNamesTheMillisecondShape();
|
||||
testAShortfallThatIsNotTheMillisecondShapeClaimsNothingAboutIt();
|
||||
testARenderPastTheWindowReadsLong();
|
||||
testAWindowAlreadyOnTheGridIsUnaffectedByTheChannelSwitch();
|
||||
testEqualRemaindersCancelUnderAFullFloorEvenOffGrid();
|
||||
testEndOffGridByUnderHalfAFrameStillCollidesWithAFlooredEnd();
|
||||
testALongVerdictNeverCarriesTheFloorSentence();
|
||||
testASubFrameWindowIsNotJudgedNotExact();
|
||||
testAWithinToleranceDeltaIsTaggedNotFloorShaped();
|
||||
testABypassingSourceReadsNotJudgedAndNamesTheSourceNotTheChannel();
|
||||
testANullOrEmptyBypassLabelFallsBackToTheOrdinaryVerdict();
|
||||
testAnOnGridStartSaysTheStartEdgeIsUntested();
|
||||
testAnOffGridStartSaysTheStartEdgeIsTested();
|
||||
testAt44100WhereAMillisecondIsNotAWholeNumberOfFrames();
|
||||
testAnUnmeasuredRenderAnswersNothingRatherThanPassing();
|
||||
testAnUnnamedChannelStillProducesAReadableLine();
|
||||
testBoundsThatReadBackUnchangedDescribeNothing();
|
||||
testADriftedEndNamesBothWindowsAndBothCounts();
|
||||
testTheReportPrintsEnoughDigitsToShowTheDrift();
|
||||
|
||||
Reference in New Issue
Block a user