-
Notifications
You must be signed in to change notification settings - Fork 1.1k
refactor: streamline date64 tests #9165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…d introducing utility functions
|
re-iterating here, that I used claude to refactor those tests, I read the changes and we are keeping the same test cases. |
|
@scovich can you review ? |
arrow-arith/src/numeric.rs
Outdated
| Date64Type::to_naive_date_opt(i64::MIN).is_none(), | ||
| "i64::MIN should return None" | ||
| ); | ||
| fn date_to_millis(year: i32, month: u32, day: u32) -> i64 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel these can be made constants as well; for example:
const MAX_VALID_MILLIS: i64 = MAX_VALID_DATE
.signed_duration_since(EPOCH)
.num_milliseconds();
const MIN_VALID_MILLIS: i64 = MIN_VALID_DATE
.signed_duration_since(EPOCH)
.num_milliseconds();
const YEAR_2000_MILLIS: i64 = date_to_millis(2000, 1, 1);
const fn date_to_millis(year: i32, month: u32, day: u32) -> i64 {
let date = NaiveDate::from_ymd_opt(year, month, day).unwrap();
date.signed_duration_since(EPOCH).num_milliseconds()
}Then can simplify their usages in the functions by removing the function call to a variable and just use constant directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The approach looks very nice.
⚠️ FYI, I used claude to refactor those tests, I read the changes and we are keeping the same test cases.
Great use for an LLM IMO!
One issue tho, which I also failed to notice when eyeballing the changes, but which claude pointed out when I asked it to check the before/after tests for equivalence:
The original tests for
subtract_day_time_optandsubtract_month_day_nano_optspecifically tested:
- Subtracting a positive interval from i64::MIN (should fail going more negative)
- Subtracting a negative interval from i64::MAX (should fail going more positive)
The refactored versions test:
- Subtracting a positive interval from i64::MAX (mathematically valid direction but extreme input)
- Subtracting a negative interval from i64::MIN (mathematically valid direction but extreme input)
It suggested three possible ways to address this (favoring 2/):
- Update the test logic to be add/sub aware and test the original edge cases
- Expand the test logic to test both the original and new edge cases
- Accept that the negative test "succeeds" (both BEFORE and AFTER) because we can't create a date from i64::MIN/MAX in the first place -- so it doesn't matter what interval we pass because the test will never get that far.
However, 3/ raised red flags for me: There are already tests to exercise physical (integer) overflow, and based on the comments here I suspect that the original test author intended to test logical overflow, where the interval shifts a valid date in a "bad" direction that takes it out of gamut. If so, the original tests were broken and the refactored ones should actually be working with MIN/MAX_VALID_MILLIS instead of ia64::MIN/MAX. At which point it would be important to also apply 1/ from the list above.
It also raises the question of whether we should use ? for intermediate operations like the date creation, or if unwrap is more appropriate to be sure the test failed/succeeded for the reason we think it did?
Hi, sorry for the intrusion, i approved this PR because the refactoring itself is clean and the core test coverage is preserved. Regarding scovich's observation about the subtle difference in edge case testing (i64::MIN/MAX with positive/negative intervals): since neither i64::MIN nor i64::MAX can be converted to a valid date in the first place, the test fails at date creation before the interval logic is even reached, the behavior is effectively the same (?) That said, if the original intent was to test logical overflow (a valid date shifted out of range), then both the original and refactored tests miss that case. Testing with MIN_VALID_MILLIS/MAX_VALID_MILLIS instead would be a nice improvement i super agree, even in separate pr. This would also be a great "good first issue" for newcomers to the repo (like myself 😄) who want to improve test coverage. |
|
@scovich this part here is testing the close to limit -> going over limit cases |
| // Large intervals that would overflow - behavior differs for add vs subtract | ||
| // For add: near_max + huge_days overflows, near_min + huge_neg_days overflows | ||
| // For subtract: near_min - huge_days overflows, near_max - huge_neg_days overflows | ||
| if is_subtract { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here @scovich (linking is behaving weirdly for me)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah nice. So we do have the coverage. Is the other test in question even useful then? Or do we just leave it as-is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed, you're right it wasn't useful.
alamb
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
||
| // Test with extreme input values that would cause overflow | ||
| assert!( | ||
| Date64Type::add_year_months_opt(i64::MAX, 1).is_none(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Which issue does this PR close? - Closes #N/A (internal refactoring - no issue) # Rationale for this change Noticed while writing tests in apache#9144, that the current tests could be re-written to be easier to read/re-use.⚠️ FIY, I used claude to refactor those tests, I read the changes and we are keeping the same test cases. The Date64 boundary tests in `arrow-arith/src/numeric.rs` had significant code duplication. Each test function for Date64 operations (`to_naive_date_opt`, `add_year_months_opt`, `subtract_year_months_opt`, `add_day_time_opt`, `subtract_day_time_opt`, `add_month_day_nano_opt`, `subtract_month_day_nano_opt`) repeated similar setup code and boundary checks, making the test suite harder to maintain and extend. # What changes are included in this PR? This PR refactors the Date64 boundary tests by: 1. **Introducing shared constants** for commonly used values: - `MAX_VALID_DATE`, `MIN_VALID_DATE`, `EPOCH` - NaiveDate constants for chrono's valid date range 2. **Adding utility functions** to reduce repetition: - `date_to_millis(year, month, day)` - converts a date to milliseconds from epoch - `max_valid_millis()`, `min_valid_millis()`, `year_2000_millis()` - common millisecond values 3. **Consolidating similar test patterns** into parameterized helper functions: - `test_year_month_op()` - tests `add_year_months_opt` and `subtract_year_months_opt` - `test_day_time_op()` - tests `add_day_time_opt` and `subtract_day_time_opt` - `test_month_day_nano_op()` - tests `add_month_day_nano_opt` and `subtract_month_day_nano_opt` 4. **Reducing 8 separate test functions to 4** while maintaining the same test coverage Net result: **-297 lines** (163 added, 460 removed) with equivalent functionality. # Are these changes tested? Yes - this is a refactoring of existing tests. The same boundary conditions and edge cases are still tested, just organized more efficiently. Running `cargo test` confirms all tests pass. # Are there any user-facing changes? No. This is an internal test refactoring with no changes to public APIs or functionality. --------- Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
Which issue does this PR close?
Rationale for this change
Noticed while writing tests in #9144, that the current tests could be re-written to be easier to read/re-use.
The Date64 boundary tests in
arrow-arith/src/numeric.rshad significant code duplication. Each test function for Date64 operations (to_naive_date_opt,add_year_months_opt,subtract_year_months_opt,add_day_time_opt,subtract_day_time_opt,add_month_day_nano_opt,subtract_month_day_nano_opt) repeated similar setup code and boundary checks, making the test suite harder to maintain and extend.What changes are included in this PR?
This PR refactors the Date64 boundary tests by:
Introducing shared constants for commonly used values:
MAX_VALID_DATE,MIN_VALID_DATE,EPOCH- NaiveDate constants for chrono's valid date rangeAdding utility functions to reduce repetition:
date_to_millis(year, month, day)- converts a date to milliseconds from epochmax_valid_millis(),min_valid_millis(),year_2000_millis()- common millisecond valuesConsolidating similar test patterns into parameterized helper functions:
test_year_month_op()- testsadd_year_months_optandsubtract_year_months_opttest_day_time_op()- testsadd_day_time_optandsubtract_day_time_opttest_month_day_nano_op()- testsadd_month_day_nano_optandsubtract_month_day_nano_optReducing 8 separate test functions to 4 while maintaining the same test coverage
Net result: -297 lines (163 added, 460 removed) with equivalent functionality.
Are these changes tested?
Yes - this is a refactoring of existing tests. The same boundary conditions and edge cases are still tested, just organized more efficiently. Running
cargo testconfirms all tests pass.Are there any user-facing changes?
No. This is an internal test refactoring with no changes to public APIs or functionality.