Fix render-bounds EXACT verdict: enumerate floored models instead of trusting grid membership

Grid-ness of an edge was a proxy for "no floor could explain this count," not
the test itself — equal remainders on both edges cancel under a full floor. Now
checks all three floored models directly and corrects the SHORT/LONG
floor-signature docs.
This commit is contained in:
2026-08-02 16:21:46 -04:00
parent 5f971e60cd
commit bcdf97d6c4
6 changed files with 188 additions and 51 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ Checks for Θ, Ξ, and Ψ work that no unit test can close. Build **Release**, i
- [ ] One razor-union case (two disjoint areas, one track) — lands the requested window, no `ReaSampler capture failed:` line (`PLAN.md:2137`) - [ ] One razor-union case (two disjoint areas, one track) — lands the requested window, no `ReaSampler capture failed:` line (`PLAN.md:2137`)
- [ ] Capture an item whose extent already equals the window — still lands, unchanged (the byte-identity regression floor) (`docs/COMPLETED.md:829`) - [ ] Capture an item whose extent already equals the window — still lands, unchanged (the byte-identity regression floor) (`docs/COMPLETED.md:829`)
- [ ] **The millisecond floor — what to expect.** A custom-bounds render is known to floor its window's END to the millisecond and write the floored value back over `RENDER_ENDPOS`. Mechanism, why two `RENDER_BOUNDSFLAG` channels exist, and the two live observations behind this: `src/core/capture/render_settings.h`'s `RenderBoundsChannel` and `docs/TODO.md` "An offline capture can be refused...". **Both live observations started at `0s`, on the grid, so nothing is known about the START edge** - [ ] **The millisecond floor — what to expect.** A custom-bounds render is known to floor its window's END to the millisecond and write the floored value back over `RENDER_ENDPOS`. Mechanism, why two `RENDER_BOUNDSFLAG` channels exist, and the two live observations behind this: `src/core/capture/render_settings.h`'s `RenderBoundsChannel` and `docs/TODO.md` "An offline capture can be refused...". **Both live observations started at `0s`, on the grid, so nothing is known about the START edge**
- [ ] **The one experiment — does another bounds mode escape the floor?** This build renders on the TIME SELECTION channel (`RENDER_BOUNDSFLAG=2`, window handed over via `GetSet_LoopTimeRange`) instead of custom time bounds. Every capture prints one line beginning `ReaSampler capture -- bounds channel:` — including the two paths that answer before any bounds are judged (unsupported format, no output file), which print `NOT JUDGED` rather than staying silent. Read the verdict: a bare **SHORT**/**LONG** (no tag), or one naming "floored to the millisecond", is the floor's signature — it did not escape this channel. **(WITHIN TOLERANCE)** on a SHORT/LONG is the gate's ordinary ±1-frame edge-convention slack (`render_window.h`), not the floor — don't read it as either result. **EXACT on a window whose END is off the millisecond grid** is the fix — the floor did not reach this channel. **EXACT on a window whose END lands on the grid is NOT conclusive**: the line adds "The END edge is UNTESTED here too" — a floored render prints the identical count by coincidence, so re-run with an off-grid end before trusting EXACT. **NOT JUDGED naming a bounds channel** means that capture answered nothing (tail mode was not None, the render was empty, the render never even reached a bounds check) OR the render source itself derives its own bounds and never consulted the channel — selected-items/razor captures always read this way, so pick a window narrower than the selected item(s) to route through the time-bounded source instead - [ ] **The one experiment — does another bounds mode escape the floor?** This build renders on the TIME SELECTION channel (`RENDER_BOUNDSFLAG=2`, window handed over via `GetSet_LoopTimeRange`) instead of custom time bounds. On every return past the point a bounds channel is chosen (the format/mode/empty-range/no-project refusals answer earlier and print nothing), one line beginning `ReaSampler capture -- bounds channel:` prints — including the two paths that answer before any bounds are judged (unsupported format, no output file), which print `NOT JUDGED` rather than staying silent. Read the verdict: **the floor's signature is ONLY a sentence naming "floored to the millisecond"** — a bare SHORT with no such sentence means the shortfall's cause is unestablished, and LONG can never be the floor's signature (a floor only removes frames, never adds them). **(WITHIN TOLERANCE)** on a SHORT/LONG is the gate's ordinary ±1-frame edge-convention slack (`render_window.h`), not the floor — don't read it as either result, and it can mask a floor: a window whose start and end sit in the same millisecond bucket makes the floored count equal the exact one, so a real one-frame floor there reads as a bare SHORT (WITHIN TOLERANCE) with no floor sentence at all. **EXACT is the fix only when the line carries no further caveat.** The verdict checks the observed count against every millisecond-floored model of the window (start floored alone, end floored alone, both together) and names any that reproduce it — grid membership on either edge is a proxy for that collision, not the test itself, so the caveat can fire even when NEITHER edge sits on the millisecond grid (sub-millisecond remainders on the two edges can cancel under a full floor — a dragged, fixed-length time selection is the reproducible case). Re-run with a window the caveat doesn't name before trusting EXACT. **NOT JUDGED naming a bounds channel** means that capture answered nothing (tail mode was not None, the render was empty, the render never even reached a bounds check, or the window rounds to 0 frames at this rate) OR the render source itself is INFERRED (not SDK-confirmed) to derive its own bounds and never consult the channel — selected-items captures always read this way; razor edits never do today, because no offline capture path assigns `SourceMode::RazorArea` (razor is a range source resolved through track/item scope, not a render source of its own) — so pick a window narrower than the selected item(s) to route through the time-bounded source instead
- [ ] **Same run, the START edge.** The verdict line also says whether the run tested the start. Capture a range whose start is NOT a whole millisecond (set View → time unit to Samples, then nudge the selection start off the grid) so the line reads `The START edge IS tested here`. Report that line verbatim — it is the only evidence available for whether the start floors too, and a start floor is the case that would break the null test silently rather than loudly - [ ] **Same run, the START edge.** The verdict line also says whether the run tested the start. Capture a range whose start is NOT a whole millisecond (set View → time unit to Samples, then nudge the selection start off the grid) so the line reads `The START edge IS tested here`. Report that line verbatim — it is the only evidence available for whether the start floors too, and a start floor is the case that would break the null test silently rather than loudly
- [ ] **Auto and Manual tail.** Repeat the off-grid-start/off-grid-end capture once with the panel tail toggle at **Auto** and once at **Manual**. Neither is judged against a frame count, so the evidence is the `after render` drift lines: report whether either channel's bounds read back changed. `[verify — DAW]` A tail is assumed to render PAST the window end — the SDK header (`:3048`) confirms only that `RENDER_TAILMS` is a length in ms, not that it extends past the end. If that assumption is wrong, an end floor could be costing Auto/Manual real content with no detector (`checkRenderedBounds` returns immediately for `tailMode != None`) — so also report by ear/measurement whether either tail capture comes up short against the source, not only whether the bounds fields drifted - [ ] **Auto and Manual tail.** Repeat the off-grid-start/off-grid-end capture once with the panel tail toggle at **Auto** and once at **Manual**. Neither is judged against a frame count, so the evidence is the `after render` drift lines: report whether either channel's bounds read back changed. `[verify — DAW]` A tail is assumed to render PAST the window end — the SDK header (`:3048`) confirms only that `RENDER_TAILMS` is a length in ms, not that it extends past the end. If that assumption is wrong, an end floor could be costing Auto/Manual real content with no detector (`checkRenderedBounds` returns immediately for `tailMode != None`) — so also report by ear/measurement whether either tail capture comes up short against the source, not only whether the bounds fields drifted
- [ ] Whichever way the experiment lands, the refused render is still kept for diagnosis at `<project folder>/reasampler_refused/` (the refusal line names the path; a failed move leaves it unindexed in the bank folder and says so). Delete the folder when done — nothing in the bank references it - [ ] Whichever way the experiment lands, the refused render is still kept for diagnosis at `<project folder>/reasampler_refused/` (the refusal line names the path; a failed move leaves it unindexed in the bank folder and says so). Delete the folder when done — nothing in the bank references it
+10 -7
View File
@@ -145,13 +145,16 @@ RenderSettingsChoice renderSettingsFor(SourceMode mode, double wetDry);
// naming them apart would assert a render distinction that does not exist. // naming them apart would assert a render distinction that does not exist.
const char* renderSourceLabel(SourceMode mode); const char* renderSourceLabel(SourceMode mode);
// True for a render source that derives its bounds from content rather than from // True for a render source INFERRED (not SDK-confirmed) to derive its bounds from
// RENDER_BOUNDSFLAG at all: SelectedItems (&32) and RazorArea (&4096) bound // content rather than RENDER_BOUNDSFLAG: SelectedItems (&32), per the observed-defect
// themselves to the selected items'/areas' own extents (see the &32 inference in // inference in src/core/capture/CLAUDE.md §Gotchas, and RazorArea (&4096) by analogy to
// src/core/capture/CLAUDE.md §Gotchas; RazorArea is read the same way, sharing the // it — the SDK header (~3042) actually separates bounds (RENDER_BOUNDSFLAG, its own
// single-file bit for the same reason). A capture on one of these never consults // value 4 = selected media items) from source (&32), which leans the other way.
// RenderBoundsChannel, so describeBoundsExperiment's verdict must not be read as // RazorArea stays in the set on that inference even though no offline capture path
// evidence about the channel for it — the caller names the source instead. // assigns it today — razor is a RANGE source (capture_batch.cpp's razor units render
// through track scope), not a render source of its own. A capture on either never
// consults RenderBoundsChannel, so describeBoundsExperiment's verdict must not be read
// as evidence about the channel — the caller names the source instead.
bool sourceBypassesBoundsChannel(SourceMode mode); bool sourceBypassesBoundsChannel(SourceMode mode);
// --- Capture scope: the FX-scope invariant ------------------------------------ // --- Capture scope: the FX-scope invariant ------------------------------------
+50 -16
View File
@@ -76,20 +76,30 @@ std::string describeBoundsExperiment(const char* channelLabel,
s += ". "; s += ". ";
if (bypassingSourceLabel && bypassingSourceLabel[0]) { if (bypassingSourceLabel && bypassingSourceLabel[0]) {
s += "NOT JUDGED -- rendered from " + std::string(bypassingSourceLabel) + s += "NOT JUDGED -- this capture's render source is " +
", which derives its bounds from content and never consulted this channel;" std::string(bypassingSourceLabel) +
" this capture is not evidence either way about it."; ", INFERRED (not SDK-confirmed) to derive its bounds from content rather"
" than consult this channel; this capture is not evidence either way about it.";
return s; return s;
} }
if (sampleRate <= 0) { if (sampleRate <= 0) {
s += "NOT JUDGED -- the landed render's frames were never counted against the " s += "NOT JUDGED -- this capture's frames were never counted against the "
"window, so this capture is not evidence either way about the channel."; "window, so this capture is not evidence either way about the channel.";
return s; return s;
} }
const long long expected = frameCountFor(reqStart, reqEnd, sampleRate); const long long expected = frameCountFor(reqStart, reqEnd, sampleRate);
const long long delta = actualFrames - expected; const long long delta = actualFrames - expected;
// A window under a frame at this rate has nothing to compare: a 0-frame render
// against a 0-frame window is a coincidence of degenerate inputs, not a match.
if (expected == 0 && actualFrames == 0) {
s += "NOT JUDGED -- the requested window rounds to 0 frames at this rate, so a "
"0-frame render is not evidence either way about the channel.";
return s;
}
// Within the gate's own edge-convention slack (render_window.h): its normal // Within the gate's own edge-convention slack (render_window.h): its normal
// tolerance, not evidence the millisecond floor was escaped or hit. // tolerance, not evidence the millisecond floor was escaped or hit.
const bool withinTolerance = const bool withinTolerance =
@@ -101,13 +111,14 @@ std::string describeBoundsExperiment(const char* channelLabel,
" the window asks for at " + std::to_string(sampleRate) + " Hz."; " the window asks for at " + std::to_string(sampleRate) + " Hz.";
// The shape both live short renders matched to the frame. A match says this channel // The shape both live short renders matched to the frame. A match says this channel
// produced a floored window; it does not locate where inside REAPER the floor is. // produced a floored window; it does not locate where inside REAPER the floor is. A
if (delta != 0) { // floor only removes frames, so this can only ever match a SHORT, never a LONG.
if (delta < 0) {
const long long msFloored = const long long msFloored =
msFlooredEndFrameCount(reqStart, reqEnd, sampleRate); msFlooredEndFrameCount(reqStart, reqEnd, sampleRate);
if (msFloored > 0 && actualFrames == msFloored) if (msFloored > 0 && actualFrames == msFloored)
s += " That is exactly the count this window holds with its end floored to" s += " That is exactly the count this window holds with its end floored to"
" the millisecond -- this channel did not escape the floor."; " the millisecond -- the shape REAPER's render was measured producing.";
} }
s += isOnMillisecondGrid(reqStart) s += isOnMillisecondGrid(reqStart)
@@ -117,15 +128,38 @@ std::string describeBoundsExperiment(const char* channelLabel,
: " The START edge IS tested here: " + exactly(reqStart) + : " The START edge IS tested here: " + exactly(reqStart) +
"s carries a sub-millisecond remainder."; "s carries a sub-millisecond remainder.";
// An EXACT verdict on an on-grid END is not proof: a channel that floors the end // EXACT is proof only when no millisecond-floored model of this window could have
// would have printed this same count, since floor/ceil/round all leave a grid point // produced the same count. Grid membership on an edge is a PROXY for that collision,
// alone. Without this, EXACT reads as settled when this run could not have told the // not the test itself: sub-millisecond remainders on the two edges can cancel under
// two apart. // a full floor even when neither edge is on the grid (a dragged, fixed-length time
if (delta == 0 && isOnMillisecondGrid(reqEnd)) { // selection reproduces this), and a remainder under half a frame collides with a
s += " The END edge is UNTESTED here too: " + exactly(reqEnd) + // floored edge without ever registering as off-grid. Enumerate every floored model
"s is already on the millisecond grid, so a channel that floors the end" // directly rather than inferring from grid membership.
" would have printed this same EXACT count -- re-run over a window whose" if (delta == 0) {
" end is off the grid before reading EXACT as the fix."; const double flooredStart = floorToMilliseconds(reqStart);
const double flooredEnd = floorToMilliseconds(reqEnd);
const bool startAlone =
actualFrames == frameCountFor(flooredStart, reqEnd, sampleRate);
const bool endAlone =
actualFrames == frameCountFor(reqStart, flooredEnd, sampleRate);
const bool bothTogether =
actualFrames == frameCountFor(flooredStart, flooredEnd, sampleRate);
if (startAlone || endAlone || bothTogether) {
std::string models;
auto addModel = [&](const char* label) {
if (!models.empty()) models += ", or ";
models += label;
};
if (startAlone) addModel("floors the START edge alone");
if (endAlone) addModel("floors the END edge alone");
if (bothTogether) addModel("floors START and END together");
s += " EXACT here is not proof: a render that " + models +
" to the millisecond would print this identical count -- re-run over a"
" window where a floored edge would show a different count before"
" reading EXACT as the fix.";
}
} }
return s; return s;
} }
+19 -7
View File
@@ -85,20 +85,32 @@ bool isOnMillisecondGrid(double seconds);
// at all. Always non-empty — a capture that answered nothing has to say so, or its // at all. Always non-empty — a capture that answered nothing has to say so, or its
// silence reads as a pass. // silence reads as a pass.
// //
// EXACT never stands alone as proof: an END that sits on the millisecond grid prints // EXACT never stands alone as proof: the observed count is checked against every
// the SAME EXACT count whether the channel honored the window or floored it and landed // millisecond-floored model of the same window (start floored alone, end floored alone,
// back on the grid by coincidence, so that case is called out in the sentence rather // both together), and any model that reproduces it is named in the sentence. Grid
// than left to read as settled — same principle as the existing START-edge caveat. // membership on an edge is a PROXY for that collision, not the test itself — remainders
// on the two edges can cancel under a full floor even when NEITHER edge sits on the
// grid, and a remainder under half a frame collides with a floored edge without ever
// registering as off-grid at all. Checking the models directly is what a grid test on
// either edge alone cannot do.
// //
// A non-zero delta that still falls inside the gate's own tolerance (renderHonoredBounds) // A non-zero delta that still falls inside the gate's own tolerance (renderHonoredBounds)
// is tagged "(WITHIN TOLERANCE)" — that is the gate's ordinary edge-convention slack, not // is tagged "(WITHIN TOLERANCE)" — that is the gate's ordinary edge-convention slack, not
// evidence of the millisecond floor; a bare SHORT/LONG, or a delta matching // evidence of the millisecond floor. The floor's signature is ONLY the "floored to the
// msFlooredEndFrameCount exactly, is the floor's signature. // millisecond" sentence (a delta matching msFlooredEndFrameCount exactly); a bare
// SHORT with no such sentence means the shortfall's cause is unestablished, and LONG can
// never be the floor's signature — a floor only removes frames, never adds them. A
// window whose start and end sit in the same millisecond bucket makes msFlooredEndFrameCount
// equal the exact count, so a real one-frame floor there reads as a bare SHORT (WITHIN
// TOLERANCE) with no floor sentence at all — that combination is not evidence the floor
// didn't happen, just a case this diagnostic can't see into.
// //
// `sampleRate <= 0` means the landed file was never measured: a tail mode adds frames by // `sampleRate <= 0` means the landed file was never measured: a tail mode adds frames by
// design and is not judged, an empty render has none, and a render whose layout failed to // design and is not judged, an empty render has none, and a render whose layout failed to
// parse or declared no sample rate is refused before it can be judged either — the // parse or declared no sample rate is refused before it can be judged either — the
// sentence then says the run answered nothing rather than inventing a comparison. // sentence then says the run answered nothing rather than inventing a comparison. A
// window that rounds to 0 frames at this rate reads NOT JUDGED the same way: a 0-frame
// render against a 0-frame window is not a comparison either.
// //
// `bypassingSourceLabel`, when non-null and non-empty, means the render source itself // `bypassingSourceLabel`, when non-null and non-empty, means the render source itself
// defined the window (render_settings::sourceBypassesBoundsChannel) — `channelLabel` was // defined the window (render_settings::sourceBypassesBoundsChannel) — `channelLabel` was
+5 -4
View File
@@ -563,10 +563,11 @@ CaptureResult OfflineRenderBackend::capture(const CaptureRequest& request) {
// because a verdict that appeared only on some outcomes would read its own absence // because a verdict that appeared only on some outcomes would read its own absence
// on the rest as a pass. Auto/Manual are not judged against a frame count (they add // on the rest as a pass. Auto/Manual are not judged against a frame count (they add
// frames by design) and the sentence says so rather than comparing anyway. // frames by design) and the sentence says so rather than comparing anyway.
// SelectedItems/RazorArea derive their bounds from content and never consult the // SelectedItems/RazorArea are INFERRED (not SDK-confirmed) to derive their bounds
// channel at all (render_settings::sourceBypassesBoundsChannel) — the batch-item // from content and never consult the channel at all
// path renders through SelectedItems on every capture, so without this the verdict // (render_settings::sourceBypassesBoundsChannel) — the batch-item path renders
// would print an EXACT/SHORT/LONG claim about a channel that was never in play. // through SelectedItems on every capture, so without this the verdict would print an
// EXACT/SHORT/LONG claim about a channel that was never in play.
const char* boundsBypassLabel = sourceBypassesBoundsChannel(request.sourceMode) const char* boundsBypassLabel = sourceBypassesBoundsChannel(request.sourceMode)
? renderSourceLabel(request.sourceMode) ? renderSourceLabel(request.sourceMode)
: nullptr; : nullptr;
+103 -16
View File
@@ -417,10 +417,79 @@ static void testAWindowAlreadyOnTheGridIsUnaffectedByTheChannelSwitch() {
CHECK(contains(s, "EXACT")); CHECK(contains(s, "EXACT"));
CHECK(contains(s, "96000")); CHECK(contains(s, "96000"));
CHECK(!contains(s, "floored to the millisecond")); CHECK(!contains(s, "floored to the millisecond"));
// The false positive this window is the shape of: an end-floored render would have // The false positive this window is the shape of: a render that floored either edge
// printed this identical EXACT count, so the line must say this run cannot tell the // alone, or both together, would have printed this identical EXACT count (every
// two apart rather than reading EXACT as settled. // edge here is on the grid) -- the line has to say this run cannot rule any of them
CHECK(contains(s, "END edge is UNTESTED")); // 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() { static void testAWithinToleranceDeltaIsTaggedNotFloorShaped() {
@@ -460,10 +529,18 @@ static void testABypassingSourceReadsNotJudgedAndNamesTheSourceNotTheChannel() {
} }
static void testANullOrEmptyBypassLabelFallsBackToTheOrdinaryVerdict() { static void testANullOrEmptyBypassLabelFallsBackToTheOrdinaryVerdict() {
CHECK(contains(describeBoundsExperiment("time selection", 0.0, 1.0, 48000, 48000, // Off-grid, non-cancelling edges (see testEqualRemaindersCancelUnderAFullFloorEvenOffGrid
nullptr), // for the window shape that WOULD trip the collision caveat, whose own text also
"EXACT")); // contains "EXACT") so this assertion is pinned to the verdict word itself, not to a
CHECK(contains(describeBoundsExperiment("time selection", 0.0, 1.0, 48000, 48000, ""), // 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")); "EXACT"));
} }
@@ -476,18 +553,24 @@ static void testAnOnGridStartSaysTheStartEdgeIsUntested() {
} }
static void testAnOffGridStartSaysTheStartEdgeIsTested() { static void testAnOffGridStartSaysTheStartEdgeIsTested() {
// The run that would settle the start question: a start carrying its own remainder. // The run that would genuinely settle the start question: a start carrying its own
// Whether REAPER floors the start or not, THIS run is the one that shows it. // 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 = const std::string s =
describeBoundsExperiment("time selection", 1.0001724, 2.0001724, 48000, 48000); describeBoundsExperiment("time selection", start, end, expected, 48000);
CHECK(contains(s, "IS tested")); CHECK(contains(s, "IS tested"));
CHECK(!contains(s, "UNTESTED")); CHECK(!contains(s, "UNTESTED"));
// A floored start would have printed 48008 frames, not 48000 — so the same line
// reads EXACT here and SHORT/LONG on the floored outcome.
CHECK(frameCountFor(1.000, 2.0001724, 48000) == 48008);
CHECK(contains(s, "EXACT")); CHECK(contains(s, "EXACT"));
CHECK(contains(describeBoundsExperiment("time selection", 1.0001724, 2.0001724, CHECK(!contains(s, "EXACT here is not proof"));
48008, 48000), // 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")); "LONG"));
} }
@@ -611,6 +694,10 @@ int main() {
testAShortfallThatIsNotTheMillisecondShapeClaimsNothingAboutIt(); testAShortfallThatIsNotTheMillisecondShapeClaimsNothingAboutIt();
testARenderPastTheWindowReadsLong(); testARenderPastTheWindowReadsLong();
testAWindowAlreadyOnTheGridIsUnaffectedByTheChannelSwitch(); testAWindowAlreadyOnTheGridIsUnaffectedByTheChannelSwitch();
testEqualRemaindersCancelUnderAFullFloorEvenOffGrid();
testEndOffGridByUnderHalfAFrameStillCollidesWithAFlooredEnd();
testALongVerdictNeverCarriesTheFloorSentence();
testASubFrameWindowIsNotJudgedNotExact();
testAWithinToleranceDeltaIsTaggedNotFloorShaped(); testAWithinToleranceDeltaIsTaggedNotFloorShaped();
testABypassingSourceReadsNotJudgedAndNamesTheSourceNotTheChannel(); testABypassingSourceReadsNotJudgedAndNamesTheSourceNotTheChannel();
testANullOrEmptyBypassLabelFallsBackToTheOrdinaryVerdict(); testANullOrEmptyBypassLabelFallsBackToTheOrdinaryVerdict();