// Standalone tests for reasampler::batch_capture — no REAPER, no test framework. // Same fast loop as the sibling pure tests (render_settings et al.): assert the batch // planning (selected-set -> ordered capture units) and result aggregation directly. // // Covers (M11 brief §test cases): // * selected-set -> capture-request-list planning: items; razor areas; empty set; // multiple razor areas across tracks (order preserved, ordinals contiguous); // invalid (empty/inverted) ranges dropped. // * batch result aggregation: all-success; partial-failure ORDERING (failed // ordinals reported in unit order); all-failed; single-unit noun singular. #include "../src/batch_capture.h" #include #include #include using namespace reasampler; static int g_fail = 0; #define CHECK(cond) do { if(!(cond)) { \ std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0) // --- planCaptureUnits: items ------------------------------------------------- // Three selected items -> three units, order preserved, ordinals 1,2,3, bounds exact. static void testPlanItemsPreservesOrderAndBounds() { std::vector in = {{0.0, 1.5}, {2.0, 2.25}, {10.0, 12.0}}; auto units = planCaptureUnits(in); CHECK(units.size() == 3); CHECK(units[0].ordinal == 1 && units[0].startSeconds == 0.0 && units[0].endSeconds == 1.5); CHECK(units[1].ordinal == 2 && units[1].startSeconds == 2.0 && units[1].endSeconds == 2.25); CHECK(units[2].ordinal == 3 && units[2].startSeconds == 10.0 && units[2].endSeconds == 12.0); } // --- planCaptureUnits: empty set --------------------------------------------- // No selected item / no razor area -> empty plan (shell reports nothing-to-batch). static void testPlanEmptySet() { CHECK(planCaptureUnits({}).empty()); } // --- planCaptureUnits: invalid ranges dropped, ordinals stay contiguous ------ // An empty (end==start) and an inverted (end in = { {0.0, 1.0}, // kept -> #1 {5.0, 5.0}, // empty -> dropped {3.0, 2.0}, // inverted -> dropped {8.0, 9.0}, // kept -> #2 }; auto units = planCaptureUnits(in); CHECK(units.size() == 2); CHECK(units[0].ordinal == 1 && units[0].startSeconds == 0.0 && units[0].endSeconds == 1.0); CHECK(units[1].ordinal == 2 && units[1].startSeconds == 8.0 && units[1].endSeconds == 9.0); } // --- planCaptureUnits: multiple razor areas across tracks -------------------- // The shell concatenates every track's parsed razor areas in track order; planning // preserves that concatenation order and assigns contiguous ordinals. Two areas on // track A followed by one on track B -> ordinals 1,2,3 in that exact order. static void testPlanRazorAreasAcrossTracksOrdered() { std::vector in = { {0.0, 0.5}, // track A, area 1 {1.0, 1.5}, // track A, area 2 {4.0, 6.0}, // track B, area 1 }; auto units = planCaptureUnits(in); CHECK(units.size() == 3); CHECK(units[0].startSeconds == 0.0 && units[0].endSeconds == 0.5); CHECK(units[1].startSeconds == 1.0 && units[1].endSeconds == 1.5); CHECK(units[2].startSeconds == 4.0 && units[2].endSeconds == 6.0); CHECK(units[0].ordinal == 1 && units[1].ordinal == 2 && units[2].ordinal == 3); } // --- BatchOutcome: all-success ----------------------------------------------- static void testOutcomeAllSuccess() { BatchOutcome o; o.record(1, true); o.record(2, true); o.record(3, true); CHECK(o.total() == 3); CHECK(o.succeeded() == 3); CHECK(o.failed() == 0); CHECK(o.failures().empty()); CHECK(o.summaryLine("item") == "ReaSampler batch capture: 3 items captured."); } // --- BatchOutcome: partial-failure ordering ---------------------------------- // Failures are reported in UNIT ORDER (#2, #4), not record-count order or sorted — // the summary must let the user retry exactly the right units. This assertion fails // if the aggregation reordered, deduped, or dropped a failure. static void testOutcomePartialFailureOrdering() { BatchOutcome o; o.record(1, true); o.record(2, false, "render produced no output file"); o.record(3, true); o.record(4, false, "capture range is empty"); o.record(5, true); CHECK(o.total() == 5); CHECK(o.succeeded() == 3); CHECK(o.failed() == 2); auto fails = o.failures(); CHECK(fails.size() == 2); CHECK(fails[0].ordinal == 2 && !fails[0].ok); CHECK(fails[0].detail == "render produced no output file"); CHECK(fails[1].ordinal == 4); CHECK(o.summaryLine("item") == "ReaSampler batch capture: 3 of 5 items captured (2 failed: #2, #4)."); } // --- BatchOutcome: all-failed ------------------------------------------------ static void testOutcomeAllFailed() { BatchOutcome o; o.record(1, false, "x"); o.record(2, false, "y"); CHECK(o.succeeded() == 0); CHECK(o.failed() == 2); CHECK(o.summaryLine("razor area") == "ReaSampler batch capture: 0 of 2 razor areas captured (2 failed: #1, #2)."); } // --- BatchOutcome: single unit uses singular noun ---------------------------- static void testOutcomeSingularNoun() { BatchOutcome o; o.record(1, true); CHECK(o.summaryLine("item") == "ReaSampler batch capture: 1 item captured."); } // --- BatchOutcome: empty (no units planned) ---------------------------------- static void testOutcomeEmpty() { BatchOutcome o; CHECK(o.total() == 0); CHECK(o.summaryLine("item") == "ReaSampler batch capture: nothing to capture."); } int main() { testPlanItemsPreservesOrderAndBounds(); testPlanEmptySet(); testPlanDropsInvalidAndRenumbers(); testPlanRazorAreasAcrossTracksOrdered(); testOutcomeAllSuccess(); testOutcomePartialFailureOrdering(); testOutcomeAllFailed(); testOutcomeSingularNoun(); testOutcomeEmpty(); if (g_fail == 0) std::printf("All tests passed.\n"); return g_fail ? 1 : 0; }