Skip to content

test(integration): compare() accepts an optional expected anchor - #1218

Merged
james-willis merged 10 commits into
apache:mainfrom
james-willis:jw/compare-expected
Sep 2, 2026
Merged

test(integration): compare() accepts an optional expected anchor#1218
james-willis merged 10 commits into
apache:mainfrom
james-willis:jw/compare-expected

Conversation

@james-willis

@james-willis james-willis commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Stacked on #1211#1211 has merged; main is merged in and the diff now shows only this PR's changes. One post-merge addition: main's trinary override semantics (#1198) changed SedonaDB's RS_SetBandNoDataValue(rast, band, NULL) from propagating the NULL to clearing that band's nodata, while Sedona Spark still returns a NULL raster — the combined NULL-args test is split so NULL band index stays a passing agreement and NULL value is xfail-cataloged.

Implements @paleolimbot's post-merge suggestion on #1203: an optional parameter on compare() that lets the reference value be specified, guarding the parity claim against vacuous agreement — every engine returning the same wrong thing passes a pure engine-vs-engine comparison.

The API

compare(sql, sedona, spark, expected=200.0)        # table path: anything assert_result accepts
compare(sql, sedona, spark, expected=[(None,)])    # e.g. anchoring a NULL
compare(sql, sedona, spark, expected=decoded)      # raster path: a DecodedRaster

expected is keyword-only and optional. The subject is asserted against the anchor first (clearest failure when all engines agree on a wrong value), then against each reference as before — so the references are anchored transitively. Parity-only remains the default for cases where the reference is hard to state.

Coverage added

Anchors on every test whose expected value is trivially known:

  • test_rs_band_nodata: the written nodata for both bands, and [(None,)] for the no-nodata fixture.
  • All four passing setter tests (test_rs_setbandnodata, _band2, _overwrite, _two_arg_single_band): the full DecodedRaster — the exact seeded pixels (reconstructible because random_raster_data is seeded), the grid (stated explicitly so the anchor and the fixture can't drift apart), and the per-band nodata.

The anchored tests place their grid with bbox=, and the anchors state the same BBOX via DecodedRaster's bbox construction (added to #1211) — the instance still carries the derived gdal_transform, which is what assert_decoded_equal compares.

The setter anchors close a real hole: with parity only, both engines no-opping identically (source nodata unchanged) would have passed every setter test.

Verification

  • Suite: 21 passed, 14 xfailed locally (pyspark 4.0.4, Sedona 1.9.1) after merging main, plus test_raster_testing.py (5 passed) in-branch.
  • Negative-tested both paths: a wrong scalar anchor and a wrong raster-nodata anchor each raise AssertionError; the correct anchors pass.

This suite is not yet wired into CI (#1206 does that), so the counts above are from a local run.

Engines agreeing with each other is not proof either is right — every
engine returning the same wrong thing passes the parity claim. compare()
now takes an optional keyword-only expected= asserted on the subject
first (references are anchored transitively): anything assert_result
accepts for table results, a DecodedRaster for raster results.

Anchor the tests whose expected value is trivially known: the getter
test (written nodata and NULL) and all four passing setter tests (the
exact seeded pixels, grid, and per-band nodata) — the setter tests would
previously have passed if both engines no-opped identically.
Includes the commits of apache#1211 (bbox placement in the raster fixture
writers) and converts the anchored tests' explicit grid placement to
bbox=. The anchors keep their GDAL transform tuples — that is the
decoded representation assert_decoded_equal compares, not a placement
choice.
Same exactly-one bbox/gdal_transform surface as the write_* helpers, so
anchors can state a north-up grid readably. The instance always carries a
gdal_transform afterwards (decoders produce transforms and
assert_decoded_equal compares them); bbox is construction sugar, derived
from the pixel shape with rasterio's from_bounds.
DecodedRaster now takes the same exactly-one bbox/gdal_transform surface
as the writers (apache#1211), so the anchors state the same BBOX the fixtures
are placed with and the duplicate transform constant goes away.
Comment on lines +47 to +64
# The grid the anchored tests write and reconstruct. Anchors must state the
# exact raster they expect, so the placement is passed to
# create_random_raster_view explicitly rather than relying on its defaults
# staying in sync — and the anchors state the same BBOX.
BANDS, HEIGHT, WIDTH = 2, 6, 7
BBOX = (100.0, 482.0, 114.0, 500.0)


def _anchor(dtype, nodata, *, bands=BANDS, plants=None):
"""The `DecodedRaster` a pixel-preserving setter must return on the
standard grid: the seeded pixels unchanged, with `nodata` per band."""
return DecodedRaster(
random_raster_data(
dtype, bands=bands, height=HEIGHT, width=WIDTH, plants=plants
),
nodata=nodata,
bbox=BBOX,
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this the best place for this? Are the bands / bbox duplicated from the default for random_raster_data()? Maybe DecodedRaster.random() is better?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ill consider this as a followup in #1219

main's trinary override semantics (apache#1198) changed SedonaDB's
RS_SetBandNoDataValue(rast, band, NULL) from propagating the NULL to
clearing that band's nodata (probe: a band holding 7.0 comes back
cleared while band 2 keeps its 7.0); Sedona Spark still returns a NULL
raster. Split the combined NULL-args test: NULL band index still agrees
(both NULL), NULL value is xfail-cataloged.
Comment thread integration/spark-parity/test_rs_raster_out.py
@james-willis
james-willis marked this pull request as ready for review September 2, 2026 19:42
@james-willis
james-willis merged commit 4949176 into apache:main Sep 2, 2026
5 checks passed
james-willis added a commit to james-willis/sedona-db that referenced this pull request Sep 2, 2026
Addresses the _anchor review comment on apache#1218: the standard random raster
now has one definition. DecodedRaster.random builds it (seeded pixels on
the RANDOM_GRID_* defaults, scalar nodata broadcast per band),
DecodedRaster.write_geotiff writes it (GeoTIFF nodata is file-wide, so
per-band anchors refuse to write), and write_random_geotiff and
DBEngine.create_random_raster_view delegate to them. Anchors are the same
object the fixture registers, so they cannot drift apart — the duplicated
BANDS/HEIGHT/WIDTH/BBOX constants and the module-local _anchor helper in
the parity suites go away.
@james-willis
james-willis deleted the jw/compare-expected branch September 4, 2026 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants