All sections

Conversion Conformance Testing

Machine-readable fixtures and property-based tests catch the conversions that only fail on pentagons, antimeridian cells, or near-polar geometry rather than on the common case.

stableh35 min read

Unit tests written against a handful of convenient cells — a mid-latitude hexagon, a simple square polygon — will pass for years and still hide bugs that only surface on the twelve pentagons per resolution, cells that straddle the antimeridian, or cells near the poles where the icosahedron projection distorts most. Conformance testing in this knowledge base has two layers: machine-readable fixtures that pin exact expected numeric outputs with explicit tolerances, and property-based tests that check invariants which must hold across every input, not just the ones a fixture happens to cover.

Fixture shape

A fixture is a JSON object validated against ConformanceFixtureSchema: id, an optional description and method name, an input record, a parameters record, an expected record, a tolerance record (per-field absolute tolerances), and free-text notes. Fixtures are re-derived on every test run by calling the real library function with input and parameters and comparing the result to expected within tolerance — they are not just stored answers, they are executable regression checks against the exact functions documented elsewhere in this KB (inscribedCircle, circumscribedCircle, polygonToH3, and so on).

{
  "id": "h3-inscribed-pentagon-r5",
  "description": "inscribed circle for cell 85080003fffffff",
  "method": "h3-to-inscribed-circle",
  "input": {
    "cell": "85080003fffffff"
  },
  "parameters": {
    "edgeSamples": 64
  },
  "expected": {
    "center": [64.700000128, 10.536199075],
    "radiusMeters": 6050.9753,
    "cellAreaM2": 127785582.61,
    "isPentagon": true
  },
  "tolerance": {
    "radiusMeters": 0.05,
    "cellAreaM2": 1
  },
  "notes": [
    "Radii are spherical (haversine) metres.",
    "inscribed cell and cell circumscribed hold within SAFETY_MARGIN."
  ]
}

The tolerance on radiusMeters here (0.05 m on a radius of roughly 6 km) is tight enough to catch a regression in the edge-densification sample count or the safety-margin constant, but loose enough to absorb floating-point differences between test runs and library versions.

Key properties

Property-based tests do not assert one expected number; they assert a relationship that must hold for every cell, polygon, or cell set fed into the function under test.

  1. Every sampled point in an inscribed circle lies inside the source cell, within tolerance. For a densified sample of the cell boundary (a great-circle-interpolated set of points along every edge, not just the original vertices), no boundary sample may fall strictly inside the inscribed disk — if one does, the disk extends past the true boundary and the subset guarantee is broken.
  2. Every sampled source-cell boundary point lies inside the circumscribed circle, within tolerance. Every point on the densified boundary must be at or within the circumscribed radius from the cell center; a violation means the circle fails to fully contain the cell it claims to bound.
  3. Fully-contained cell results do not extend outside the source polygon. For full-mode polyfilling, every returned cell's own boundary, not just its center, must lie within the source polygon — a center-contained check is not sufficient evidence for a full-mode guarantee, and the test must verify the stronger claim the mode name makes.
  4. Compact then uncompact preserves the normalized set. uncompact(compact(cells), resolution) must return exactly the same set of cells (as a set, not an ordered list) that uncompact produced from the original mixed or uniform input at that resolution — compaction is a lossless re-encoding of a cell set, and any input/output mismatch is a correctness bug, not an approximation.
  5. Weighted overlap fractions sum consistently. For a cell fully partitioned by a set of regions (no gaps, no overlaps in the source regions), the sum of cellCoverageFraction across all (cell, region) links for a given cell must equal 1 within numerical tolerance — a sum below 1 indicates a missed region overlap, and a sum above 1 indicates double-counted area.

Normal and pathological fixtures

Fixtures are organized to cover both the common case and the failure modes that only appear geometrically:

Normal hexagon
A mid-latitude, non-pentagon, non-boundary-crossing cell — the baseline case every function must get right before anything else matters.
Pentagon
One of the twelve pentagon cells per resolution, which break the six-neighbor and regular-hexagon-ratio assumptions several algorithms silently rely on.
Antimeridian
A cell whose boundary or center sits at or crosses plus-or-minus 180 degrees longitude, where naive planar longitude arithmetic produces a world-spanning artifact.
Near-polar
A cell at high latitude where icosahedron face distortion is largest and small-angle approximations in some geometry libraries break down.
Hole
A polygon with an interior ring, verifying that cells inside the hole are correctly excluded rather than treated as covered.
Narrow
A polygon thinner than a cell's width at the target resolution, which can legitimately return zero center-contained cells despite having positive area.
Tiny
A polygon much smaller than a single cell, testing that intersect-mode still returns the enclosing cell rather than an empty set.

Each pathological category maps back to a named edge case elsewhere in this KB — pentagons to h3-pentagons, antimeridian cells to antimeridian-handling, and so on — so a failing fixture points directly at the conceptual page explaining why the input is hard, not just at a numeric mismatch.

Running the checks

Fixtures live as one JSON file per case under a conformance directory, indexed by an index.json manifest; the test harness reads every fixture, re-derives the result with the real library function named in method, and asserts the numeric fields fall within their declared tolerance while boolean fields (such as isPentagon) match exactly. Property tests are separate, hand-written test suites that iterate representative cells — typically an ordinary hexagon, a pentagon, a near-polar cell, and an antimeridian cell in the same suite — and assert the relationships in the Key Properties section above hold for every one of them, not just for whichever fixture happens to be checked in.

Tip

A new geometry conversion function is not conformance-tested until it has at least one fixture per pathological category above, plus a property test for the invariant the function is supposed to guarantee. A function with only normal-case fixtures has not been tested against the inputs most likely to break it.

Edge cases affecting this page
  • - 12 pentagon cells per resolution sit at icosahedron vertices; they break the 6-neighbour and regular-shape assumptions and have lower inscribed/circumscribed ratios.
  • - Geometries crossing ±180° longitude wrap incorrectly, producing world-spanning artifacts when treated as planar.
  • - Polygons much smaller than a cell may be missed or over-represented by a single cell.
  • - Polygons thinner than a cell can yield zero center-contained cells.
  • - Interior rings (donuts) must be respected so cells inside a hole are excluded.