Skip to content

Add geotiff edge-case tests: IFD loops, header parsing, descending-y, non-georeferenced (#1482)#1492

Merged
brendancol merged 1 commit intomainfrom
issue-1482
May 5, 2026
Merged

Add geotiff edge-case tests: IFD loops, header parsing, descending-y, non-georeferenced (#1482)#1492
brendancol merged 1 commit intomainfrom
issue-1482

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Closes #1482.

Summary

21 new tests in the geotiff module, plus one small reader fix.

Tests cover:

  • IFD chain loop detection. parse_all_ifds already had a seen set; this adds a test that builds a malicious buffer where IFD2 points back at IFD1, so the guard can't regress silently.
  • _read_value edge cases: RATIONAL/SRATIONAL with denominator zero, RATIONAL count overflow, truncated IFD entry buffer.
  • BigTIFF malformations: magic 43 with offset_size != 8, truncated 8-byte BigTIFF header.
  • Classic TIFF with first-IFD offset past the buffer end.
  • Descending-y (south-up) write/read round-trip. Pixel data orientation survives. The y sign does not, because ModelPixelScale stores absolute scale, so the test documents that as a known limitation.
  • Non-georeferenced TIFF read returns a DataArray with integer pixel coords (0..N-1) and no crs attr, instead of fractional coords from the default unit transform.

Code change

_extract_transform now returns (transform, has_georef). GeoInfo carries a new has_georef flag. _geo_to_coords falls back to integer pixel coords when has_georef is False. Existing georeferenced reads behave identically.

Test plan

  • pytest xrspatial/geotiff/tests/test_header.py xrspatial/geotiff/tests/test_georef_edges.py
  • Full geotiff suite passes. Three pre-existing matplotlib-deepcopy failures in test_features.py::TestPalette are unrelated and present on main.

@github-actions github-actions Bot added the performance PR touches performance-sensitive code label May 5, 2026
brendancol added a commit that referenced this pull request May 5, 2026
…1495)

test_merge_dask_different_crs_matches_eager intermittently SIGABRTs
on macOS CI runners. The merge() dask graph creates a fresh
pyproj.Transformer per chunk, and PROJ's first-time CRS-database load
is not safe under concurrent threaded workers — multiple threads racing
to init the SQLite DB on macOS triggers an abort.

The test exercises dask graph correctness, not dask threading. Pin it
to the synchronous scheduler and pre-warm pyproj.CRS objects so the
parity assertion runs without the threading dimension. CRS
thread-safety in the production reproject path is its own concern and
is not regressed here.

Refs the same crash on PR #1492 macOS-3.13 and on main run e253900
(macOS-3.12).
Tests:
- IFD chain loop / cycle detection in parse_all_ifds
- RATIONAL/SRATIONAL with denominator=0 returning 0.0
- RATIONAL with count overflowing the buffer
- Truncated IFD entry buffer
- BigTIFF malformations (offset_size != 8, truncated header)
- Classic TIFF first-IFD offset past buffer
- Descending-y (south-up) write/read round-trip
- Non-georeferenced TIFF read produces integer pixel coords

Code:
- _extract_transform now returns (transform, has_georef) so callers
  can tell "no GeoTIFF tags" apart from "tags present with default
  values".  GeoInfo carries the new has_georef flag.
- _geo_to_coords falls back to integer pixel coords (0..N-1) when
  has_georef is False, instead of producing y values like
  -0.5, -1.5, ... from the default unit transform.
@brendancol brendancol merged commit 6ece00d into main May 5, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add geotiff edge-case tests: IFD chain loops, header parsing, descending-y, non-georeferenced

1 participant