fix: report human-readable, calendar-aware elapsed time in memleak message - #5012
Open
jasonb5 wants to merge 6 commits into
Open
fix: report human-readable, calendar-aware elapsed time in memleak message#5012jasonb5 wants to merge 6 commits into
jasonb5 wants to merge 6 commits into
Conversation
rljacob
approved these changes
Jul 16, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes the MEMLEAK failure message to report human-readable, calendar-aware elapsed model time rather than subtracting raw YYYYMMDD stamps (which previously produced nonsensical “days” values). This improves diagnostic accuracy without changing the leak pass/fail criteria.
Changes:
- Add
_days_in_monthand_format_elapsed_model_timehelpers to decodeYYYYMMDDstamps and compute elapsed years/months/days withNO_LEAPvsGREGORIANFebruary handling. - Update
perf_check_for_memory_leakto use the caseCALENDARwhen formatting the elapsed time in the MEMLEAK message. - Expand unit tests to cover formatting and calendar edge cases, and update the existing memleak message assertion.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| CIME/SystemTests/system_tests_common.py | Adds calendar-aware elapsed-time formatting and uses it in the memleak message. |
| CIME/tests/test_unit_system_tests.py | Converts tests to pytest-style classes and adds/updates unit tests for the new helpers and updated message. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+1196
to
+1210
| years = y1 - y0 | ||
| months = m1 - m0 | ||
| days = d1 - d0 | ||
|
|
||
| if days < 0: | ||
| # Borrow one month, using the true length of the month immediately | ||
| # preceding the end month for the case's calendar. | ||
| borrow_year, borrow_month = y1, m1 - 1 | ||
| if borrow_month == 0: | ||
| borrow_year, borrow_month = y1 - 1, 12 | ||
| days += _days_in_month(borrow_year, borrow_month, calendar_type) | ||
| months -= 1 | ||
| if months < 0: | ||
| months += 12 | ||
| years -= 1 |
| # raw: years=0, months=1, days=-4 | ||
| # borrow Feb (NO_LEAP → 28): days = -4 + 28 = 24, months = 0 | ||
| assert _format_elapsed_model_time(10205, 10301) == "24 days" | ||
|
|
Closes #5007. perf_check_for_memory_leak was computing elapsed time by subtracting two raw YYYYMMDD-encoded model-date stamps, producing a nonsensical result (e.g. 41129 "days" for a 5-year run). Add _yyyymmdd_elapsed_str helper that decodes each YYYYMMDD stamp into (year, month, day) components, applies 30-day borrow arithmetic (calendar is unavailable at this call site), and returns a human-readable string that omits zero components and uses correct singular/plural forms. Update perf_check_for_memory_leak to use the helper in the diagnostic message. The pass/fail logic (memory ratio) is unchanged. Update existing memleak tests to use realistic YYYYMMDD stamps so they actually exercise the YYYYMMDD decode path.
Replace the fixed 30-day month-borrow approximation in the memleak elapsed-time message with calendar-aware arithmetic. The case CALENDAR variable (NO_LEAP or GREGORIAN) is now threaded through so month/year borrows use the true length of the preceding month, making the reported elapsed time exact rather than approximate. Add _days_in_month helper and rename _yyyymmdd_elapsed_str to _format_elapsed_model_time to describe intent rather than input encoding.
jasonb5
force-pushed
the
jasonb5/system-tests/fix-memleak-elapsed-days
branch
from
July 22, 2026 17:35
a4c896c to
3d1e708
Compare
A single month-borrow was insufficient for spans that cross multiple short months (e.g. Jan 31 → Mar 1 on NO_LEAP). The day-borrow block now loops, walking borrow_month backwards each iteration, until days is non-negative. The months-borrow block is likewise a while loop for defensive correctness. Regression tests added for NO_LEAP and GREGORIAN (leap and non-leap) variants of Jan 31 → Mar 1, and a cross-year-boundary multi-borrow case.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes #5007.
The memory-leak check in
CIME/SystemTests/system_tests_common.pyreported elapsed model time in "days" using arithmetic that was not actually days. It subtracted twoYYYYMMDD-encoded model-date stamps directly, so a 5-year run reported nonsense like41129 days(the message mixed years ×10000, months ×100, and day-of-month).Root cause
perf_get_memory_liststores the model date asfloat(YYYYMMDD)(e.g.51231.0), andperf_check_for_memory_leakdifferenced the raw stamps:This is cosmetic only — the pass/fail decision uses
finalmem/originalmemand the tolerance ratio, both unaffected — but the message is misleading and can send developers debugging in the wrong direction.Fix
YYYYMMDDstamp into(year, month, day)and report elapsed time in years/months/days, dropping zero components, e.g.memleak detected, memory went from 1499.740000 to 1578.320000 in 4 years, 11 months, 29 days.CALENDARvariable (NO_LEAPorGREGORIAN) is threaded through so a day-borrow uses the true length of the preceding month (including the Gregorian leap-year rule) rather than a flat 30-day approximation._days_in_month(year, month, calendar_type)._format_elapsed_model_time(describes intent rather than input encoding).Tests
TestFormatElapsedModelTime: singular/plural units, every component combination, day/month/year borrows, float stamps, and century-scale runs.NO_LEAPFebruary, Gregorian leap / non-leap / century / 400-year February, and case-insensitive calendar names.TestDaysInMonthclass covering 30/31-day months and all February edge cases.CALENDARand assert the new message.All
test_unit_system_tests.pytests pass (the 2 pre-existingtest_generate_baselinefailures are unrelated — they spawn a real subprocess). Doctests pass;black(22.3.0) andpylint(4.0.4) clean.Checklist