Improve dtest mismatch handling, TABLE compare, and progress UX - #372
Improve dtest mismatch handling, TABLE compare, and progress UX#372tameware wants to merge 10 commits into
Conversation
Loops return false after the first API fault or expected-result difference, and dtest propagates that as a non-zero process status instead of always exiting 0. Co-authored-by: Cursor <cursoragent@cursor.com>
Golden TABLE lines already include all DDS_STRAINS; skipping NT let NT-only mismatches pass. Co-authored-by: Cursor <cursoragent@cursor.com>
Updates overwrite in place and finish_running clears the line so a leftover 100% row does not sit above the timer summary. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
🟢 Approval recommended
The remaining requested integration coverage is a non-blocking nit.
Pull request overview
Improves dtest failure handling, complete TABLE comparisons, and terminal progress output.
Changes:
- Propagates failures as non-zero exits.
- Compares all five strains, including NT.
- Adds progress rendering and cleanup tests.
File summaries
| File | Description |
|---|---|
library/tests/TestTimer.hpp |
Adds progress-line state and cleanup API. |
library/tests/TestTimer.cpp |
Implements ANSI progress rendering and clearing. |
library/tests/testcommon.cpp |
Propagates failure statuses. |
library/tests/test_timer_test.cpp |
Tests progress behavior. |
library/tests/loop.hpp |
Updates loop return contracts. |
library/tests/loop.cpp |
Stops on errors or mismatches. |
library/tests/loop_par_test.cpp |
Tests fail-fast mismatch handling. |
library/tests/dtest.cpp |
Returns the process status. |
library/tests/compare.cpp |
Compares all five strains. |
library/tests/compare_test.cpp |
Tests NT and suit comparisons. |
library/tests/BUILD.bazel |
Registers the comparison test target. |
Review details
Suppressed comments (1)
library/tests/testcommon.cpp:197
- The new exit-status propagation is not covered end to end:
loop_par_testonly checks the helper's boolean, while the existing dtest integration test covers a successful run. Please add a failing dtest invocation with an intentionally wrong TABLE/PAR result and assert a non-zero process status (and only the first mismatch report), so regressions in thereal_main/mainwiring at this change are detected.
return ok ? 0 : 1;
- Files reviewed: 11/11 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Exercises real_main/main wiring so a deliberate PAR golden mismatch exits non-zero and stops after the first reported difference. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Resolve timer finalization on failure paths and provide safe non-TTY progress output.
Review details
Suppressed comments (5)
library/tests/TestTimer.cpp:132
- This now emits VT control bytes unconditionally. When stdout is redirected (for example into CI logs) the escape sequences and carriage returns are recorded literally, and Windows consoles without virtual-terminal processing render them instead of clearing the line. Gate the in-place mode on a TTY/VT-capable stream and keep a newline-based fallback for other outputs.
cout << "\033[2K\r" << setw(8) << reached << " (" <<
library/tests/loop.cpp:216
- This return bypasses the only
timer.end()for the whole PAR batch (line 230). Becausereal_mainnow continues totimer.print_handsafter a false result, an API fault after any work leaves the batch open and reports zero completed hands; close the timing scope with an accurate processed count before returning (or restructure the timing scope).
return false;
library/tests/loop.cpp:228
- This mismatch return also skips the only
timer.end()for the PAR run (line 230). The new non-exiting path therefore prints a timer summary with zero hands even when earlier iterations were processed; record the elapsed work with an accurate count before returning, or move the timing boundary so failure paths are closed.
return false;
library/tests/loop.cpp:265
- This return bypasses the only
timer.end()for the whole dealer-PAR batch (line 279). Sincereal_mainnow proceeds totimer.print_handsafter a false result, an API fault after work has been done leaves the timing batch open and reports zero hands; close it with an accurate processed count before returning or restructure the timing scope.
return false;
library/tests/loop.cpp:277
- This mismatch return also skips the only
timer.end()for the dealer-PAR run (line 279). The new non-exiting path can therefore print a zero-hand timer summary after earlier iterations were processed; record the elapsed work with an accurate count before returning, or move the timing boundary so failure paths are closed.
return false;
- Files reviewed: 12/12 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
Add failure-path coverage for the remaining loop modes, including calc/TABLE mismatches.
Review details
Suppressed comments (2)
library/tests/loop.cpp:93
- The new loop-level regression coverage only exercises
loop_par; the return/cleanup branches added forloop_solve,loop_calc,loop_dealerpar, andloop_playare not executed by either new test. Those paths differ in batching and in whether a progress line is already active, so a regression could still make one of those modes continue after the first mismatch/API fault or leave stale progress while all current tests pass. Please add focused failure tests for the remaining loops (or a shared parameterized equivalent).
timer.finish_running();
report_dds_error("loop_solve", ret);
cout << "loop_solve: i " << i << "\n";
return false;
library/tests/loop.hpp:40
- The new failure-return contract is applied to all five loops, but the added regression coverage exercises only
loop_par.loop_solve,loop_calc,loop_dealerpar, andloop_playstill have no tests for an API fault or an early expected-result mismatch, so those paths can regress without detecting the promised non-zero dtest behavior. Add focused failure-path tests for the remaining loops (including a calc/TABLE mismatch case).
/// @return false on DDS API fault or first expected-result mismatch
auto loop_solve(
BoardsPBN * bop,
SolvedBoards * solvedbdp,
DealPBN * deal_list,
FutureTricks * fut_list,
const int number,
const int stepsize,
std::vector<std::pair<int, int>>* board_times = nullptr) -> bool;
- Files reviewed: 12/12 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🟡 Changes recommended
Two moderate findings remain, and broader loop failure coverage is also needed.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (2)
library/tests/TestTimer.cpp:136
- This now emits ANSI control sequences unconditionally, including when stdout is redirected to CI/log files or on native Windows consoles without virtual-terminal processing. Those consumers will see literal
\033[2K/carriage returns and a single concatenated progress line instead of readable progress. Please gate the in-place path on a TTY/ANSI-capable stream and retain a newline fallback (or enable VT on Windows).
cout << "\033[2K\r" << setw(8) << reached << " (" <<
setw(6) << setprecision(1) << right << fixed <<
100. * reached /
static_cast<float>(divisor) << "%)" <<
setw(15) << right << fixed << setprecision(0) <<
library/tests/testcommon.cpp:197
- The added integration test only exercises an expected PAR mismatch; no test drives a DDS call to return a non-
RETURN_NO_FAULTcode. Because this line now promises the same non-zero propagation for API faults, add a regression fixture (for example, an overflowing PAR table) and assert the CLI exits non-zero without processing a later deal.
return ok ? 0 : 1;
- Files reviewed: 12/12 changed files
- Comments generated: 1
- Review effort level: Lite
Add unit and integration coverage for solve/calc/play/dealerpar early returns, and close PAR timers on mismatch/API faults so summaries stay accurate. Co-authored-by: Cursor <cursoragent@cursor.com>
Run CalcAllTablesPBNX in MAXNOOFBOARDS-sized chunks (same as solve) and print progress between batches instead of only at the end. Co-authored-by: Cursor <cursoragent@cursor.com>
Ensure one-line-per-deal GIB inputs use the same batched progress path as NUMBER list files. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved timer/progress cleanup paths and a potential dangling ownership bug remain.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (4)
library/tests/loop.cpp:90
- This branch returns while the
timer.start(count)at line 77 is still open. Sincereal_mainnow continues totimer.print_handsafter a failure, the failed batch is omitted from the timing summary and the timer remains logically open until the next reset. End the timer before clearing/reporting the error, as the PAR loops do.
timer.finish_running();
library/tests/loop.cpp:172
- This branch returns while the
timer.start(count)at line 165 is still open.real_mainnow prints the timer summary after this return, so an API-fault batch is not recorded and the timing scope is left unfinished. Close the started batch before reporting the error, consistently with the PAR loops.
timer.finish_running();
library/tests/loop.cpp:364
- This branch returns while the
timer.start(count)at line 347 is still open. With the new non-zero-return path,real_mainstill prints timing data, but this failed batch is omitted and the timer scope is left unfinished. Calltimer.end()before clearing/reporting the error.
timer.finish_running();
library/tests/loop.cpp:285
- This has the same stale-progress problem as the PAR loop: after an earlier successful deal,
print_runninghas activated the line, but this API-fault branch never callsfinish_running. The error output can therefore be written after the old progress text instead of replacing it cleanly; clear the line before reporting the fault.
timer.end();
- Files reviewed: 13/13 changed files
- Comments generated: 2
- Review effort level: Lite
Make HandLists move-only to avoid dangling frees, clear the progress line on PAR/dealer-PAR API faults, and end open timer batches on solve/calc/play faults before reporting. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Two unresolved moderate findings remain regarding large-input parallelism and moved-from temporary cleanup.
Review details
Suppressed comments (2)
library/tests/loop.cpp:155
- This turns the unbounded X API back into repeated
MAXNOOFBOARDS-sized jobs. The repository'sLargeBatchIsSingleParallelJobtest documents thatCalcAllTablesX/CalcAllTablesPBNXmust dispatch a larger input as one job (library/tests/system/calc_all_tables_x_test.cpp:204-230); withsol100000.txtthis loop now performs about 500 conversions, allocations, and scheduler launches instead of one. Please provide progress without forfeiting that whole-input parallelism, or otherwise measure and justify this regression before adopting the fixed batch size.
for (int i = 0; i < number; i += batch)
{
const int count = (i + batch > number ? number - i : batch);
library/tests/loop_failure_test.cpp:79
- The moved-from object's
pathis left in an unspecified state afterstd::move. Its destructor still callsrelease(), which removespathwhenever it is non-empty, so a move that leaves the source string populated can delete the same temporary file now owned by the returned object. Clear the source path along with the raw pointers before the moved-from destructor runs.
path = std::move(other.path);
- Files reviewed: 13/13 changed files
- Comments generated: 0 new
- Review effort level: Lite
std::move leaves the source string unspecified; an explicit clear prevents the donor destructor from unlinking the assignee's temp file. Co-authored-by: Cursor <cursoragent@cursor.com>
|
library/tests/loop_failure_test.cpp:79 fixed in 38f710f. |
This is not a regression. Batches of 200 versus unbounded batch size have little effect on performance, and batching is the only good way to show progress. Also, dtest is not performance-critical.
With batching: Without batching: 2% difference. |
There was a problem hiding this comment.
🔵 Needs a closer look
loop.cpp caps CalcAllTablesPBNX calls at MAXNOOFBOARDS, regressing the single-job API contract and large-run performance.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
library/tests/loop.cpp:155
- This now caps every
CalcAllTablesPBNXcall atMAXNOOFBOARDS, so inputs with more than 200 deals are split into multiple solver jobs. That regresses the X-API contract exercised bylibrary/tests/system/calc_all_tables_x_test.cpp:204-230and documented inlibrary/src/calc_tables.cpp:612-614, where all deal×strain boards are intentionally dispatched in one job; largesolXXX.txtruns will pay repeated conversion, preflight, fan-out, and scheduling overhead. Please preserve the single-job path and obtain progress without chunking the API call, or explicitly revisit that API performance contract if chunking is required for the UX.
- Files reviewed: 13/13 changed files
- Comments generated: 0 new
- Review effort level: Lite
Document that loop_calc chunks CalcAllTablesPBNX for progress UX without changing the X-API single-job-per-call contract. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The calc batching change can undermine intended large-batch behavior for larger inputs.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
library/tests/loop.cpp:143
- This changes
-s calcfrom the X API's intended single large-batch dispatch into oneCalcAllTablesPBNXjob per 200 deals. For inputs larger thanMAXNOOFBOARDS, that repeats conversion, allocation, scheduling, and worker setup and forfeits the large-batch performance contract documented inlibrary/src/calc_tables.cpp:612-616and asserted bylibrary/tests/system/calc_all_tables_x_test.cpp:204-230. Preserve the single X-API call for large inputs, or add a solver-side progress mechanism rather than chunking the workload in the harness.
- Files reviewed: 13/13 changed files
- Comments generated: 0 new
- Review effort level: Lite
|
Per Cursor: Copilot is mixing up two layers:
Batching in |
Summary
compare_TABLEverify allDDS_STRAINS, including NT.-s calcruns, including GIB-producedsolXXX.txtfiles.Test plan
bazelisk test //library/tests:loop_par_test //library/tests:compare_test //library/tests:test_timer_testbazelisk run //library/tests:dtest -- -f hands/list100.txt -s calcsucceeds and leaves no leftover 100% progress row above the timer summaryAddresses Issue #334
Made with Cursor