"""Cascade mask math — EoMT primary, incumbent arbitrating sidewalk/earth only. Pure-function tests (no model, no network) over one pixel per scenario. """ from __future__ import annotations import numpy as np from lawn_estimator.config import Config from lawn_estimator.segmentation import combine_cascade def test_cascade_scenarios(): # columns: grass sidewalk+open earth+open earth+roof road+open tree primary = np.array([[9, 11, 13, 13, 6, 4]]) arbiter = np.array([[7, 1, 1, 7, 1, 4]]) lawn, canopy = combine_cascade(primary, arbiter) assert lawn[0, 0] # EoMT grass counts regardless of arbiter assert lawn[0, 1] # parkway strip: EoMT sidewalk, incumbent open → reclaimed assert lawn[0, 2] # dormant/shadowed turf: EoMT earth, incumbent open → reclaimed assert not lawn[0, 3] # EoMT earth but incumbent says roof → stays out assert not lawn[0, 4] # road is NOT arbitrated even if incumbent said open assert lawn[0, 5] # EoMT tree = canopy counts assert canopy.tolist() == [[False, False, False, False, False, True]] def test_cascade_flag_defaults_off(monkeypatch): monkeypatch.delenv("LAWN_CASCADE", raising=False) assert Config().lawn_cascade is False monkeypatch.setenv("LAWN_CASCADE", "1") assert Config().lawn_cascade is True