Skip to content

Cover write_geotiff_gpu compression modes (zstd default + jpeg) (#1646)#1647

Merged
brendancol merged 2 commits into
mainfrom
deep-sweep-test-coverage-geotiff-2026-05-11-run2
May 12, 2026
Merged

Cover write_geotiff_gpu compression modes (zstd default + jpeg) (#1646)#1647
brendancol merged 2 commits into
mainfrom
deep-sweep-test-coverage-geotiff-2026-05-11-run2

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Summary

  • Add test_gpu_writer_compression_modes_2026_05_11.py with 11 tests covering the documented write_geotiff_gpu compression= modes (zstd, deflate, jpeg, none).
  • Default compression='zstd' and 'jpeg' had no targeted round-trip coverage before this PR; only 'deflate' and 'none' did. The new tests exercise the live nvJPEG GPU encoder and the nvCOMP zstd path on a CUDA host.
  • Update .claude/sweep-test-coverage-state.csv (pass 7).

Closes #1646.

Test plan

  • pytest xrspatial/geotiff/tests/test_gpu_writer_compression_modes_2026_05_11.py -> 11 passed locally on a CUDA host.
  • nvJPEG path verified to actually run (not just the Pillow fallback) via a wrapper that counted calls into _nvjpeg_batch_encode.
  • TIFF compression tag (259) check confirms the written file advertises the right codec (1 / 7 / 8 / 50000).
  • CI runs the suite end-to-end.

Cover the documented compression= modes that had no targeted
round-trip tests:

- zstd (the default, "fastest on GPU"): pixel-exact round-trip on
  int32 plus default-codec pinning via TIFF tag 259.
- jpeg (nvJPEG with Pillow fallback): round-trip for 3-band uint8 RGB
  and single-band uint8 with mean-abs-diff bounds; pin compression
  tag 7. Exercises the live nvJPEG encoder on a GPU host, not just
  the Pillow fallback.
- deflate + none: plain round-trips outside the COG / nodata-sentinel
  paths so a regression in the basic tiled assembly is visible.
- Cross-codec parity: zstd, deflate, none must produce pixel-identical
  read-backs for the same input (catches predictor / codec mis-wiring).

11 tests, all passing on the GPU host.

Update .claude/sweep-test-coverage-state.csv to record pass 7.
@github-actions github-actions Bot added the performance PR touches performance-sensitive code label May 12, 2026
@brendancol brendancol requested a review from Copilot May 12, 2026 00:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds targeted GPU-only test coverage for xrspatial.geotiff.write_geotiff_gpu(..., compression=...) to close a parameter-coverage gap around the documented compression modes (including the default zstd and GPU JPEG), plus updates the local coverage-sweep tracking file.

Changes:

  • Add a new GPU-only pytest module with round-trip and TIFF Compression-tag assertions for zstd, deflate, jpeg, and none.
  • Add cross-codec parity checks for lossless codecs and additional explicit round-trip coverage outside COG/sentinel-focused paths.
  • Update .claude/sweep-test-coverage-state.csv to record this coverage pass.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
xrspatial/geotiff/tests/test_gpu_writer_compression_modes_2026_05_11.py Adds GPU-only tests covering write_geotiff_gpu compression modes, round-trips, and TIFF Compression tag checks.
.claude/sweep-test-coverage-state.csv Records the coverage sweep “Pass 7” status and notes for the geotiff module.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +129 to +134
"""Omitting ``compression=`` defaults to zstd; bytes must match an
explicit ``compression='zstd'`` call.

Pins the default so a silent change to the default codec (eg. to
'deflate') would fail this test.
"""
Comment on lines +155 to +171
JPEG is lossy so we tolerate a moderate per-pixel error budget but
require the mean error to stay within typical JPEG quality bounds
(well under 50 for default-quality 8-bit).
"""
da, arr = _make_rgb_uint8_da()
path = str(tmp_path / "jpeg_rgb.tif")

write_geotiff_gpu(da, path, compression='jpeg')

out = open_geotiff(path)
assert out.shape == arr.shape
assert out.dtype == arr.dtype
diff = np.abs(out.values.astype(np.int32) - arr.astype(np.int32))
# Random uint8 is the worst case for JPEG; we just want to catch a
# codec that emits all-zero or all-255 output rather than measure
# quality. Mean-abs-diff below 50 is comfortable for default quality.
assert diff.mean() < 50, (
Comment on lines +151 to +173
@_gpu_only
def test_write_geotiff_gpu_jpeg_rgb_roundtrip(tmp_path):
"""``compression='jpeg'`` round-trips a 3-band uint8 RGB raster.

JPEG is lossy so we tolerate a moderate per-pixel error budget but
require the mean error to stay within typical JPEG quality bounds
(well under 50 for default-quality 8-bit).
"""
da, arr = _make_rgb_uint8_da()
path = str(tmp_path / "jpeg_rgb.tif")

write_geotiff_gpu(da, path, compression='jpeg')

out = open_geotiff(path)
assert out.shape == arr.shape
assert out.dtype == arr.dtype
diff = np.abs(out.values.astype(np.int32) - arr.astype(np.int32))
# Random uint8 is the worst case for JPEG; we just want to catch a
# codec that emits all-zero or all-255 output rather than measure
# quality. Mean-abs-diff below 50 is comfortable for default quality.
assert diff.mean() < 50, (
f"JPEG round-trip mean diff {diff.mean()} suggests encoder/decoder break"
)
- Rewrite test_write_geotiff_gpu_zstd_default_matches_explicit so the
  docstring matches what it asserts: pin the compression tag and the
  decoded-array equality, not byte-for-byte file equality (the writer
  may legitimately vary tile padding/ordering between runs).
- Swap the JPEG RGB test input from random uint8 noise to a deterministic
  smooth gradient (mirroring test_jpeg.py::_gradient_rgb). Tighten the
  mean-abs-diff bound from 50 to 8 for RGB and to 5 for the monochrome
  variant; the looser bound only existed because random noise is the
  worst case for JPEG.
- Add test_write_geotiff_gpu_jpeg_uses_nvjpeg_when_available: spy on
  _gpu_decode._nvjpeg_batch_encode via monkeypatch and assert it fires
  at least once when libnvjpeg is loadable. Without this spy a
  regression breaking nvJPEG would silently fall through to the Pillow
  fallback and the round-trip tests would still pass. The new test is
  guarded by _nvjpeg_only so it only runs on hosts where libnvjpeg is
  actually loadable.
@brendancol brendancol merged commit 46f567f into main May 12, 2026
10 of 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.

Test coverage: write_geotiff_gpu compression modes (zstd default + jpeg) untested

2 participants