Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/znippy-zoomies

osm-katana/tests/facett_ladder_fixture.rs

//! **The other half of facett's cross-repo taxonomy guard.**
//!
//! `facett_map::style::classify_with` / `classify_label` and this crate's
//! [`osm_katana::shared::class::RULES`] are two TEXTS of one taxonomy — an arm-for-arm
//! transcription, in two repos, with nothing enforcing that they stay in step. facett
//! checks itself against a CHECKED-IN copy of this crate's ladder
//! (`facett-map/osm-katana-ladder.txt`, produced by `examples/ladder_spelling.rs`), and
//! its own guard is `facett_map::style::osm_katana_ladder`.
//!
//! A copy can go stale, and facett cannot see into this repo. **This file is what makes
//! the stale side notice.** It pins the generator's output against a literal, so the one
//! edit that would strand facett's copy — a change to `RULES`, `RUNGS`,
//! `UNCLASSIFIED_MIN_ZOOM` or `TIER_CAMERAS` — turns a test red HERE, in the repo where
//! the edit happened, with the regeneration command in the failure message.
//!
//! # This is a change detector, and that is the whole job
//!
//! It is not a correctness check and it does not pretend to be one: it says nothing
//! about whether a rung is RIGHT. It says that the ladder moved and that a copy of it
//! exists elsewhere. The literal below is not derived from `rules_spelling()` at
//! runtime — that would be the identity-value trap LAW 2 names, a value compared with
//! itself through one function, and it could never go red.
//!
//! Run: `cargo test -p osm-katana --test facett_ladder_fixture`

use osm_katana::shared::class::{RULES, RUNGS, Tier, UNCLASSIFIED_MIN_ZOOM, rules_spelling};

/// The regeneration command, spelled once, quoted into every failure message.
const REGENERATE: &str = "cargo run -p osm-katana --example ladder_spelling \
     > ../facett/facett-map/osm-katana-ladder.txt";

/// **The tier ladder facett holds a copy of.** A literal, one line per tier.
const TIERS: &[&str] = &[
    "Country@0>6",
    "Region@6>6",
    "District@9>9",
    "City@12>12",
    "Detail@15>15",
];

/// **The rules facett holds a copy of.** A literal, one line per rule, in order.
const RULE_LINES: &[&str] = &[
    // FIRST since `islet-1` (2026-08-31): `RULES` is order-sensitive and the `place`
    // block ends in a catch-all, so a shoreline way tagged `place=islet` matched `place`
    // and was written min_zoom 12 while facett paints coastline at 6 — 32 049 of 79 084
    // coastline ways on the Sweden cut carry a `place` tag.
    "natural∈[coastline]>6",
    // The boundary block (2026-09-01): `admin_level` decides the rung, asked before the
    // `boundary` key so a way segment tagged with the level alone lands with its relation.
    "admin_level∈[1|2]>6",
    "admin_level∈[3|4]>6",
    "admin_level∈[5|6|7]>9",
    "admin_level?>12",
    "boundary∈[national_park|protected_area]>6",
    "boundary?>12",
    "place∈[country|state|province|region]>6",
    "place∈[city]>6",
    "place∈[town]>6",
    "place∈[village]>9",
    "place∈[municipality|borough]>9",
    "place?>12",
    "piste:type?!highway∈[motorway|motorway_link|trunk|trunk_link|primary|primary_link|secondary|secondary_link|tertiary|tertiary_link]>9",
    "snowmobile?!highway∈[motorway|motorway_link|trunk|trunk_link|primary|primary_link|secondary|secondary_link|tertiary|tertiary_link]>12",
    "route?!highway∈[motorway|motorway_link|trunk|trunk_link|primary|primary_link|secondary|secondary_link|tertiary|tertiary_link]>9",
    "sac_scale?!highway∈[motorway|motorway_link|trunk|trunk_link|primary|primary_link|secondary|secondary_link|tertiary|tertiary_link]>9",
    "trail_visibility?!highway∈[motorway|motorway_link|trunk|trunk_link|primary|primary_link|secondary|secondary_link|tertiary|tertiary_link]>9",
    "mtb:scale?!highway∈[motorway|motorway_link|trunk|trunk_link|primary|primary_link|secondary|secondary_link|tertiary|tertiary_link]>12",
    "highway∈[motorway|trunk|primary]>6",
    "highway∈[motorway_link|trunk_link|primary_link|secondary]>6",
    "highway∈[secondary_link|tertiary|tertiary_link]>9",
    "highway∈[residential|unclassified|living_street]>12",
    "highway∈[service|track]>15",
    "highway∈[footway|cycleway|path|pedestrian|steps]>15",
    "highway∈[road|busway|bus_guideway]>9",
    "highway?>12",
    "railway∈[rail|narrow_gauge]>6",
    "railway∈[light_rail|subway|tram|funicular|preserved|miniature]>9",
    "railway?>12",
    "natural∈[water|lake|bay|wetland]>6",
    "water?>6",
    "waterway∈[river|riverbank|dock|basin]>6",
    "waterway∈[canal]>9",
    "waterway∈[ditch|drain]>15",
    "waterway?>12",
    "natural∈[wood|forest|scrub|grassland|heath]>6",
    "leisure∈[park|garden|nature_reserve|recreation_ground]>6",
    "landuse?>6",
    "building?>12",
    "amenity?>12",
];

/// ★ **RED-when-broken: the ladder moved and facett's copy did not.**
///
/// **RED ARM** (seen): moved `place=town` from `Region` to `District` in `RULES` ⇒
/// *"rule 3 is now `place∈[town]>9`, the copy facett holds says `place∈[town]>6`"*.
#[test]
fn the_ladder_facett_copied_is_still_this_ladder() {
    assert_eq!(
        RUNGS.to_vec(),
        vec![6u8, 9, 12, 15],
        "the RUNGS moved. facett projects its `Lod` onto this list; regenerate its copy:\n    {REGENERATE}"
    );
    assert_eq!(
        UNCLASSIFIED_MIN_ZOOM, 12,
        "the unclassified rung moved. Regenerate facett's copy:\n    {REGENERATE}"
    );

    let tiers: Vec<String> = Tier::ladder_spelling().split(',').map(str::to_string).collect();
    assert_eq!(
        tiers.len(),
        TIERS.len(),
        "the tier ladder has {} entries and facett's copy has {}. Regenerate it:\n    {REGENERATE}",
        tiers.len(),
        TIERS.len()
    );
    for (i, (got, want)) in tiers.iter().zip(TIERS).enumerate() {
        assert_eq!(
            got, want,
            "tier {i} is now `{got}`, the copy facett holds says `{want}`. Regenerate \
             it:\n    {REGENERATE}"
        );
    }

    let rules: Vec<String> = rules_spelling().split(',').map(str::to_string).collect();
    assert_eq!(
        rules.len(),
        RULE_LINES.len(),
        "the ladder has {} rules and facett's copy has {}. A rule was added or removed; \
         regenerate it:\n    {REGENERATE}",
        rules.len(),
        RULE_LINES.len()
    );
    for (i, (got, want)) in rules.iter().zip(RULE_LINES).enumerate() {
        assert_eq!(
            got, want,
            "rule {i} is now `{got}`, the copy facett holds says `{want}`. Regenerate \
             it:\n    {REGENERATE}"
        );
    }
}

/// ★ **RED-when-broken: a pin that pins nothing.**
///
/// The literals above are only worth anything if they are the whole ladder and if they
/// could disagree with it. Three arms: the pin covers every rule in `RULES`, the
/// spelling is non-trivial (a table of empty strings would compare equal to itself), and
/// a DELIBERATELY wrong expectation is detected — the arm that proves the comparison has
/// teeth without needing a source edit to see red.
#[test]
fn the_pin_could_actually_disagree() {
    assert_eq!(
        RULE_LINES.len(),
        RULES.len(),
        "the pin covers {} of {} rules — the uncovered ones can drift silently",
        RULE_LINES.len(),
        RULES.len()
    );
    assert!(
        RULE_LINES.iter().all(|r| r.contains('>') && r.len() > 4),
        "a rule spelling with no `>rung` is not a spelling; the pin would be comparing \
         placeholders with placeholders"
    );
    let real: Vec<String> = rules_spelling().split(',').map(str::to_string).collect();
    let mut wrong = RULE_LINES.to_vec();
    wrong[0] = "place∈[country|state|province|region]>15";
    assert_ne!(
        real,
        wrong.iter().map(|s| (*s).to_string()).collect::<Vec<String>>(),
        "a deliberately wrong expectation compares EQUAL to the real spelling — the \
         comparison has no teeth"
    );
}