test(verify): compare what the two paths produced - #8359
Conversation
Two frames agree when they hold the same rows in the same order with the same values, and the comparison says which of those failed rather than that they differ. A float is compared within a tolerance, since the two paths reach the same number by different arithmetic. A visualization has no frame to compare. What it emits is a figure, and the meaning of a figure is in the numbers behind it, so the figures are compared number by number; a plot that renders as an image is compared as the HTML it emits instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Automated Reviewer SuggestionsBased on the
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8359 +/- ##
============================================
- Coverage 94.11% 94.00% -0.11%
- Complexity 4811 4862 +51
============================================
Files 1197 1204 +7
Lines 48813 49076 +263
Branches 5906 5948 +42
============================================
+ Hits 45939 46135 +196
- Misses 1420 1465 +45
- Partials 1454 1476 +22
*This pull request uses carry forward flags. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
`read_json` infers a type per file, so a column one side wrote as "6" and the other wrote as "6.0" both arrive as the number 6, and a null beside the text "nan" both arrive as NaN. Two different answers compared as one, on every string column of every operator. The engine writes a schema beside its output naming which columns are strings, and both sides are now read that way. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`astype(str)` gets three things wrong against `toString`: an empty cell renders as the text "nan", a column holding one has become a float by then so 6 reads "6.0", and a boolean capitalises where the executor writes it lower. The comparison could not see any of it until apache#8359 stopped inferring a type per file, which is why a cast to STRING has been passing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three kinds of comment came out. A drawing of the string the code below assembles. A restatement of a branch the reader can see. And the word MVP, which dated the scope to a moment rather than stating it. What replaces them says the same thing shorter, or says what the code cannot: which cases the harness does not drive and why none of them has an operator asking for it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
carloea2
left a comment
There was a problem hiding this comment.
The result comparison looks good.
It ignores column order. Row order is settled by --unordered, which lex-sorts both frames unless the operator declares itself order-sensitive. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
carloea2
left a comment
There was a problem hiding this comment.
Reviewed alongside the related export and verification PRs. These findings are based on code inspection and focused Python checks, not a full Scala suite run.
A requested column is one the engine declared as a model, so a side that never emitted it is the divergence. Skipping it hid that: the column was dropped from both frames afterwards and a path that produced no model at all compared equal. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
carloea2
left a comment
There was a problem hiding this comment.
Follow-up review of the series. Runtime findings were checked locally against the current code; deployment routing was checked from configuration.
The standalone script writes its page through Python's text mode, so on Windows the file holds CRLF while the runtime path's JSONL carries the same markup with LF. Five operators reported a mismatch that was only that. The line ending is the platform writing the file rather than anything the operator chose, so it is normalized away like the Styler uuid already is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
carloea2
left a comment
There was a problem hiding this comment.
Further review with local regression tests and connected execution checks.
The comparison gave every numeric column the tolerance a double needs, so two whole numbers a workflow would never call the same passed it: at rtol=1e-5, LONG 100000 and 100001 compare equal. The declared integer columns are split out and compared exactly; the rest keep the tolerance. Reading them again is the other half. `read_json` parses a column holding a null through float64, so 9007199254740993 is already 9007199254740992 before anything compares it, and pinning the dtype does not help: the rounding happens on the way in. Python's json reads the integer exactly. A value that is not whole in a column the engine declared integral is itself the divergence, so it is reported. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It restated its own signature and listed a codec table that lives in TupleIO. The reasons a reader cannot derive stay. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@carloea2 all three are fixed: a missing model column now fails, line endings are normalized, and integer columns are compared exactly. Would you take another look when you have a moment? |
The two chart comparators go to a change of their own. They answer a different question and share no code with this one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
What changes were proposed in this PR?
Two frames agree when they hold the same rows in the same order with the same
values, and the comparison says which of those failed rather than that they
differ. A float is compared within a tolerance, since the two paths reach the
same number by different arithmetic.
A visualization has no frame to compare at all. The two comparators that read
what one draws are in #8509.
Any related issues, documentation, discussions?
Part of #8325, 6 of 27; that issue lists the set in order.
Closes #8411, the task this change is the whole of.
How was this PR tested?
The tests in this change cover it. The whole set is exercised together once the last piece lands: every operator run through the engine and through its generated script, and the two answers compared.
Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 5)