vize_l3 0.608.0

L3 - the Davinci reactivity IR (codename Impeto): flat scheduled ops with explicit state and effect edges
Documentation
//! TS-16 laws for the L3 placement page.

use vize_l0::cstr;
use vize_l0::dump::{Dump, Error as DumpError, Mode as DumpMode};
use vize_l3::op::OpId;
use vize_l3::placement::dump::{Page as PlacementPage, Record as DumpPlacement};
use vize_l3::placement::{Placement, PlacementRecord, PlacementSet};

const CANONICAL: &str = "\
[s3-placement-folio]

[s3-placement-folio.placements]
op=1 alternatives=inline,cache leader=- chosen=cache
op=3 alternatives=inline,group leader=2 chosen=inline
op=5 alternatives=inline,hoist leader=- chosen=hoist

";

fn folio() -> PlacementPage {
    let row = |op, alternative, leader: Option<u32>, chosen| {
        DumpPlacement(
            PlacementRecord::new(
                OpId::new(op),
                PlacementSet::INLINE.with(alternative),
                leader.map(OpId::new),
            )
            .with_chosen(chosen),
        )
    };
    PlacementPage {
        placements: vec![
            row(1, Placement::Cache, None, Placement::Cache),
            row(3, Placement::Group, Some(2), Placement::Inline),
            row(5, Placement::Hoist, None, Placement::Hoist),
        ],
    }
}

#[test]
fn full_print_is_identity_on_canonical_text() {
    let parsed = PlacementPage::parse(CANONICAL).expect("canonical text parses");
    assert_eq!(parsed.print_to_string(DumpMode::Full).as_str(), CANONICAL);
    assert_eq!(parsed, folio());
}

#[test]
fn parse_print_is_structural_identity() {
    let printed = folio().print_to_string(DumpMode::Full);
    assert_eq!(printed.as_str(), CANONICAL);
    assert_eq!(PlacementPage::parse(printed.as_str()), Ok(folio()));
    assert_eq!(
        folio().print_to_string(DumpMode::Display),
        folio().print_to_string(DumpMode::Full)
    );
}

#[test]
fn every_set_round_trips_including_the_empty_one() {
    for bits in 0..16u8 {
        let set = PlacementSet::from_bits(bits).expect("known bits");
        let page = PlacementPage {
            placements: vec![DumpPlacement(PlacementRecord::new(
                OpId::new(bits.into()),
                set,
                None,
            ))],
        };
        let printed = page.print_to_string(DumpMode::Full);
        assert_eq!(PlacementPage::parse(printed.as_str()), Ok(page));
    }
    assert_eq!(PlacementSet::from_bits(16), None);
}

#[test]
fn parse_rejects_malformed_rows_exactly() {
    let page =
        |row: &str| cstr!("[s3-placement-folio]\n\n[s3-placement-folio.placements]\n{row}\n");
    let cases = [
        (
            "op=1 alternatives=cache,inline leader=- chosen=inline",
            "placement alternatives `cache,inline` are not in canonical order",
        ),
        (
            "op=1 alternatives=inline,inline leader=- chosen=inline",
            "placement alternatives `inline,inline` are not in canonical order",
        ),
        (
            "op=1 alternatives=inline,spill leader=- chosen=inline",
            "unknown placement `spill`",
        ),
        (
            "op=1 alternatives=inline leader=x chosen=inline",
            "invalid `leader` integer `x`",
        ),
        (
            "op=1 alternatives=inline leader=-",
            "missing `chosen` field",
        ),
        (
            "op=1 alternatives=inline chosen=inline leader=-",
            "expected `leader=...`, got `chosen=inline`",
        ),
        (
            "op=1 alternatives=inline leader=- chosen=inline extra=1",
            "unexpected field `extra=1`",
        ),
    ];
    for (row, message) in cases {
        assert_eq!(
            PlacementPage::parse(page(row).as_str()),
            Err(DumpError::new(4, cstr!("{message}"))),
            "{row}"
        );
    }
}