Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions mortie/tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,56 @@ def test_order2res_decreasing(self):
assert all(resolutions[i] > resolutions[i+1] for i in range(len(resolutions)-1))


class TestRes2Display:
"""Test the resolution-ladder printer"""

def test_prints_all_orders_through_max(self, capsys):
"""One line per order 0..MAX_ORDER -- no silent drop above 19"""
tools.res2display()
lines = capsys.readouterr().out.strip().split('\n')
assert len(lines) == tools.MAX_ORDER + 1
# every order 0..29 appears with its trailing phrasing
for order in range(tools.MAX_ORDER + 1):
assert any(
line.endswith('at tessellation order ' + str(order))
for line in lines
)

def test_max_order_argument(self, capsys):
"""max_order bounds the printed range inclusively"""
tools.res2display(max_order=5)
lines = capsys.readouterr().out.strip().split('\n')
assert len(lines) == 6
assert lines[-1].endswith('at tessellation order 5')

def test_unit_ladder_km_m_cm(self, capsys):
"""Coarse orders read in km, sub-km in m, sub-m in cm"""
tools.res2display()
lines = capsys.readouterr().out.strip().split('\n')
by_order = {int(line.rsplit(' ', 1)[1]): line for line in lines}
# order 12 = 1.589 km, order 13 = 794.456 m (the issue's examples)
assert by_order[12] == '1.589 km at tessellation order 12'
assert by_order[13] == '794.456 m at tessellation order 13'
# finest orders drop to cm rather than tiny km/m fractions
assert by_order[25] == '19.396 cm at tessellation order 25'
assert by_order[29] == '1.212 cm at tessellation order 29'

def test_rounds_within_bracket(self, capsys):
"""Values are rounded to three decimals inside the chosen unit"""
tools.res2display()
lines = capsys.readouterr().out.strip().split('\n')
for line in lines:
number = line.split(' ', 1)[0]
# at most three digits after the decimal point
assert '.' in number and len(number.split('.')[1]) <= 3

def test_out_of_range_raises(self):
"""max_order outside 0..MAX_ORDER is rejected"""
for bad in (-1, tools.MAX_ORDER + 1):
with pytest.raises(ValueError, match="max_order must be"):
tools.res2display(max_order=bad)


class TestUnique2Parent:
"""Test UNIQ to parent cell conversion"""

Expand Down
31 changes: 27 additions & 4 deletions mortie/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,33 @@ def order2res(order):
return res


def res2display():
'''prints resolution levels'''
for res in range(20):
print(str(order2res(res)) + ' km at tessellation order ' + str(res))
def res2display(max_order=MAX_ORDER):
'''prints resolution levels

Prints one line per tessellation order from 0 through ``max_order``
(default MAX_ORDER = 29, the finest order the packed-u64 kernel reaches).
Each resolution is rendered in the largest sensible unit -- km at coarse
orders, m once it drops below 1 km, cm once it drops below 1 m -- and
rounded to three decimals within that bracket, so fine orders read
naturally (e.g. order 12 -> ``1.589 km``, order 13 -> ``794.456 m``)
rather than as tiny km fractions.

``max_order`` must lie in 0..MAX_ORDER, the order range the packed-u64
kernel encodes.
'''
if not 0 <= max_order <= MAX_ORDER:
raise ValueError(
f"max_order must be between 0 and {MAX_ORDER}, got {max_order!r}")
for res in range(max_order + 1):

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 from Claude (review)

max_order is unvalidated at both ends. Two edge cases neither guarded nor tested:

  • Negative max_orderrange(n<1) is empty, so the function silently prints nothing (e.g. res2display(-1) returns with no output, no error). Silent no-op on bad input is easy to misread as "nothing to display".
  • max_order > 29 → happily prints beyond the kernel's MAX_ORDER, contradicting the docstring's "29 = the finest order the kernel reaches". Worse, past order ~32 the cm bracket itself drops below 1 (0.152 cm, 0.019 cm, …) — there's no mm rung, so it degrades to sub-1 cm fractions, the exact readability problem this PR set out to fix.

Suggest clamping/validating to 0..MAX_ORDER (e.g. raise ValueError on out-of-range, or min(max_order, MAX_ORDER)), and add a test pinning the chosen behavior. Not a blocker for the sanctioned scope, but the docstring currently over-promises vs. the code.


Generated by Claude Code

km = order2res(res)
if km >= 1.0:
value, unit = km, 'km'
elif km >= 1e-3:
value, unit = km * 1e3, 'm'
else:
value, unit = km * 1e5, 'cm'
print(str(round(value, 3)) + ' ' + unit +

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 from Claude (review)

Nit / latent: bracket selection uses the raw km, but the printed number is round(value, 3) after conversion. A value just under a boundary can round up across it and print in the smaller unit — e.g. km = 0.9999996 selects the m bracket and prints 1000.0 m instead of 1.0 km (same for 1000.0 cm). No order in 0..29 triggers this, so it's harmless today, but it's a real inconsistency if the range ever widens or order2res constants change. Worth a one-line comment or a post-round bracket re-check.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 from Claude

Left standing intentionally. As the review notes, no order in the sanctioned 0..MAX_ORDER range lands within rounding distance of a bracket boundary (the closest is order 12 at 1.589 km, nowhere near 1.000), so this can't fire today. A post-round bracket re-check would add branching to a print helper for a case that only becomes reachable if order2res's constants change or the range widens past 29 — both of which are now guarded against by the phase-2 ValueError. Flagging it here rather than folding it in; happy to add the re-check if you'd prefer the belt-and-suspenders version.


Generated by Claude Code

' at tessellation order ' + str(res))


def unique2parent(unique):
Expand Down
Loading