mod common;
use common::*;
#[test]
fn chart_valid_bar_no_diagnostics() {
let src = r##"zenith version=1 {
project id="proj.vb" name="ValidBar"
tokens format="zenith-token-v1" {
token id="color.bg" type="color" value="#ffffff"
}
styles {
}
document id="doc.vb" title="ValidBar" {
page id="page.vb" w=(px)800 h=(px)600 {
chart id="c.bar" kind="bar" x=(px)50 y=(px)50 w=(px)600 h=(px)400 {
series 10.0 20.0 30.0 label="A"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
let chart_diags: Vec<&str> = codes(&report)
.into_iter()
.filter(|c| c.starts_with("chart."))
.collect();
assert!(
chart_diags.is_empty(),
"valid bar chart must produce no chart.* diagnostics; got: {:?}",
chart_diags
);
}
#[test]
fn chart_invalid_kind_fires_diagnostic() {
let src = r##"zenith version=1 {
project id="proj.ik" name="InvalidKind"
styles {
}
document id="doc.ik" title="InvalidKind" {
page id="page.ik" w=(px)800 h=(px)600 {
chart id="c.bad" kind="histogram" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {
series 1.0 2.0 label="X"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
has_code(&report, "chart.invalid_kind"),
"bogus chart kind must fire chart.invalid_kind; got: {:?}",
codes(&report)
);
}
#[test]
fn chart_all_valid_kinds() {
for kind in ["bar", "line", "sparkline", "pie", "donut"] {
let src = format!(
r##"zenith version=1 {{
project id="proj.vk" name="ValidKind"
styles {{
}}
document id="doc.vk" title="ValidKind" {{
page id="page.vk" w=(px)800 h=(px)600 {{
chart id="c.kind" kind="{kind}" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {{
series 1.0 2.0 label="S"
}}
}}
}}
}}
"##
);
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
!has_code(&report, "chart.invalid_kind"),
"kind={kind:?} must not fire chart.invalid_kind; got: {:?}",
codes(&report)
);
}
}
#[test]
fn chart_duplicate_id_fires_id_duplicate() {
let src = r##"zenith version=1 {
project id="proj.dup" name="Dup"
styles {
}
document id="doc.dup" title="Dup" {
page id="page.dup" w=(px)800 h=(px)600 {
rect id="node.dup" x=(px)0 y=(px)0 w=(px)10 h=(px)10
chart id="node.dup" kind="bar" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {
series 1.0 label="A"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
has_code(&report, "id.duplicate"),
"chart id duplicate must fire id.duplicate; got: {:?}",
codes(&report)
);
}
#[test]
fn chart_valid_bar_mode_no_diagnostic() {
for bar_mode in ["grouped", "stacked"] {
let src = format!(
r##"zenith version=1 {{
project id="proj.bm" name="BarMode"
styles {{
}}
document id="doc.bm" title="BarMode" {{
page id="page.bm" w=(px)800 h=(px)600 {{
chart id="c.bm" kind="bar" bar-mode="{bar_mode}" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {{
series 1.0 2.0 label="S"
}}
}}
}}
}}
"##
);
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
!has_code(&report, "chart.invalid_bar_mode"),
"bar-mode={bar_mode:?} must not fire chart.invalid_bar_mode; got: {:?}",
codes(&report)
);
}
}
#[test]
fn chart_invalid_bar_mode_fires_diagnostic() {
let src = r##"zenith version=1 {
project id="proj.ibm" name="InvalidBarMode"
styles {
}
document id="doc.ibm" title="InvalidBarMode" {
page id="page.ibm" w=(px)800 h=(px)600 {
chart id="c.ibm" kind="bar" bar-mode="clustered" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {
series 1.0 2.0 label="S"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
has_code(&report, "chart.invalid_bar_mode"),
"bogus bar-mode must fire chart.invalid_bar_mode; got: {:?}",
codes(&report)
);
}
#[test]
fn chart_matching_categories_no_mismatch() {
let src = r##"zenith version=1 {
project id="proj.mc" name="MatchCat"
styles {
}
document id="doc.mc" title="MatchCat" {
page id="page.mc" w=(px)800 h=(px)600 {
chart id="c.mc" kind="bar" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {
categories "Q1" "Q2" "Q3"
series 10.0 20.0 30.0 label="S1"
series 5.0 15.0 25.0 label="S2"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
!has_code(&report, "chart.category_count_mismatch"),
"matching categories must not fire chart.category_count_mismatch; got: {:?}",
codes(&report)
);
}
#[test]
fn chart_category_count_mismatch_fires_diagnostic() {
let src = r##"zenith version=1 {
project id="proj.cm" name="CatMismatch"
styles {
}
document id="doc.cm" title="CatMismatch" {
page id="page.cm" w=(px)800 h=(px)600 {
chart id="c.cm" kind="bar" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {
categories "Q1" "Q2" "Q3"
series 10.0 20.0 label="S"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
has_code(&report, "chart.category_count_mismatch"),
"category/series count mismatch must fire chart.category_count_mismatch; got: {:?}",
codes(&report)
);
}
#[test]
fn chart_no_categories_no_mismatch() {
let src = r##"zenith version=1 {
project id="proj.nc2" name="NoCatMismatch"
styles {
}
document id="doc.nc2" title="NoCatMismatch" {
page id="page.nc2" w=(px)800 h=(px)600 {
chart id="c.nc2" kind="bar" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {
series 1.0 2.0 3.0 label="S1"
series 10.0 20.0 label="S2"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
!has_code(&report, "chart.category_count_mismatch"),
"absent categories must not fire chart.category_count_mismatch; got: {:?}",
codes(&report)
);
}
#[test]
fn chart_valid_point_placement_no_diagnostic() {
for value in ["edge", "center"] {
let src = format!(
r##"zenith version=1 {{
project id="proj.pp" name="PointPlacement"
styles {{
}}
document id="doc.pp" title="PointPlacement" {{
page id="page.pp" w=(px)800 h=(px)600 {{
chart id="c.pp" kind="line" point-placement="{value}" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {{
series 1.0 2.0 label="S"
}}
}}
}}
}}
"##
);
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
!has_code(&report, "chart.invalid_point_placement"),
"point-placement={value:?} must not fire chart.invalid_point_placement; got: {:?}",
codes(&report)
);
}
}
#[test]
fn chart_invalid_point_placement_fires_diagnostic() {
let src = r##"zenith version=1 {
project id="proj.ipp" name="InvalidPointPlacement"
styles {
}
document id="doc.ipp" title="InvalidPointPlacement" {
page id="page.ipp" w=(px)800 h=(px)600 {
chart id="c.ipp" kind="line" point-placement="left" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {
series 1.0 2.0 label="S"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
has_code(&report, "chart.invalid_point_placement"),
"bogus point-placement must fire chart.invalid_point_placement; got: {:?}",
codes(&report)
);
}
#[test]
fn chart_valid_value_labels_no_diagnostic() {
for value in ["auto", "none", "top", "center"] {
let src = format!(
r##"zenith version=1 {{
project id="proj.vl" name="ValueLabels"
styles {{
}}
document id="doc.vl" title="ValueLabels" {{
page id="page.vl" w=(px)800 h=(px)600 {{
chart id="c.vl" kind="bar" value-labels="{value}" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {{
series 1.0 2.0 label="S"
}}
}}
}}
}}
"##
);
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
!has_code(&report, "chart.invalid_value_labels"),
"value-labels={value:?} must not fire chart.invalid_value_labels; got: {:?}",
codes(&report)
);
}
}
#[test]
fn chart_invalid_value_labels_fires_diagnostic() {
let src = r##"zenith version=1 {
project id="proj.ivl" name="InvalidValueLabels"
styles {
}
document id="doc.ivl" title="InvalidValueLabels" {
page id="page.ivl" w=(px)800 h=(px)600 {
chart id="c.ivl" kind="bar" value-labels="outside" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {
series 1.0 2.0 label="S"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
has_code(&report, "chart.invalid_value_labels"),
"bogus value-labels must fire chart.invalid_value_labels; got: {:?}",
codes(&report)
);
}
#[test]
fn chart_valid_legend_position_no_diagnostic() {
for value in ["right", "left", "top", "bottom"] {
let src = format!(
r##"zenith version=1 {{
project id="proj.lp" name="LegendPosition"
styles {{
}}
document id="doc.lp" title="LegendPosition" {{
page id="page.lp" w=(px)800 h=(px)600 {{
chart id="c.lp" kind="bar" legend-position="{value}" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {{
series 1.0 2.0 label="S"
}}
}}
}}
}}
"##
);
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
!has_code(&report, "chart.invalid_legend_position"),
"legend-position={value:?} must not fire chart.invalid_legend_position; got: {:?}",
codes(&report)
);
}
}
#[test]
fn chart_invalid_legend_position_fires_diagnostic() {
let src = r##"zenith version=1 {
project id="proj.ilp" name="InvalidLegendPosition"
styles {
}
document id="doc.ilp" title="InvalidLegendPosition" {
page id="page.ilp" w=(px)800 h=(px)600 {
chart id="c.ilp" kind="bar" legend-position="center" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {
series 1.0 2.0 label="S"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
has_code(&report, "chart.invalid_legend_position"),
"bogus legend-position must fire chart.invalid_legend_position; got: {:?}",
codes(&report)
);
}
#[test]
fn chart_valid_legend_layout_no_diagnostic() {
for value in ["wrapped", "list"] {
let src = format!(
r##"zenith version=1 {{
project id="proj.ll" name="LegendLayout"
styles {{
}}
document id="doc.ll" title="LegendLayout" {{
page id="page.ll" w=(px)800 h=(px)600 {{
chart id="c.ll" kind="bar" legend-layout="{value}" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {{
series 1.0 2.0 label="S"
}}
}}
}}
}}
"##
);
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
!has_code(&report, "chart.invalid_legend_layout"),
"legend-layout={value:?} must not fire chart.invalid_legend_layout; got: {:?}",
codes(&report)
);
}
}
#[test]
fn chart_invalid_legend_layout_fires_diagnostic() {
let src = r##"zenith version=1 {
project id="proj.ill" name="InvalidLegendLayout"
styles {
}
document id="doc.ill" title="InvalidLegendLayout" {
page id="page.ill" w=(px)800 h=(px)600 {
chart id="c.ill" kind="bar" legend-layout="grid" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {
series 1.0 2.0 label="S"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
has_code(&report, "chart.invalid_legend_layout"),
"bogus legend-layout must fire chart.invalid_legend_layout; got: {:?}",
codes(&report)
);
}
#[test]
fn chart_valid_legend_align_no_diagnostic() {
for value in ["center", "left", "right"] {
let src = format!(
r##"zenith version=1 {{
project id="proj.la" name="LegendAlign"
styles {{
}}
document id="doc.la" title="LegendAlign" {{
page id="page.la" w=(px)800 h=(px)600 {{
chart id="c.la" kind="bar" legend-align="{value}" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {{
series 1.0 2.0 label="S"
}}
}}
}}
}}
"##
);
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
!has_code(&report, "chart.invalid_legend_align"),
"legend-align={value:?} must not fire chart.invalid_legend_align; got: {:?}",
codes(&report)
);
}
}
#[test]
fn chart_invalid_legend_align_fires_diagnostic() {
let src = r##"zenith version=1 {
project id="proj.ila" name="InvalidLegendAlign"
styles {
}
document id="doc.ila" title="InvalidLegendAlign" {
page id="page.ila" w=(px)800 h=(px)600 {
chart id="c.ila" kind="bar" legend-align="justify" x=(px)0 y=(px)0 w=(px)400 h=(px)300 {
series 1.0 2.0 label="S"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
has_code(&report, "chart.invalid_legend_align"),
"bogus legend-align must fire chart.invalid_legend_align; got: {:?}",
codes(&report)
);
}
#[test]
fn chart_missing_geometry_fires_geometry_diagnostic() {
let src = r##"zenith version=1 {
project id="proj.mg" name="MissingGeom"
styles {
}
document id="doc.mg" title="MissingGeom" {
page id="page.mg" w=(px)800 h=(px)600 {
chart id="c.nogeom" kind="line" {
series 1.0 2.0 label="S"
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
let geom_diags: Vec<&str> = codes(&report)
.into_iter()
.filter(|c| *c == "node.missing_geometry")
.collect();
assert!(
!geom_diags.is_empty(),
"chart missing geometry must fire node.missing_geometry; got: {:?}",
codes(&report)
);
}
#[test]
fn chart_label_colors_token_refs_are_collected() {
let src = r##"zenith version=1 {
project id="proj.lct" name="LabelColorTokens"
tokens format="zenith-token-v1" {
token id="c.a" type="color" value="#aa0000"
token id="c.b" type="color" value="#00aa00"
token id="c.s" type="color" value="#0000aa"
}
styles {
}
document id="doc.lct" title="LabelColorTokens" {
page id="page.lct" w=(px)800 h=(px)600 {
chart id="c.lct" kind="pie" x=(px)0 y=(px)0 w=(px)400 h=(px)400 {
label-colors (token)"c.a" (token)"c.b"
series label="S" label-color=(token)"c.s" 60.0 40.0
}
}
}
}
"##;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
let unused_diags: Vec<&str> = codes(&report)
.into_iter()
.filter(|c| c.starts_with("token."))
.collect();
assert!(
unused_diags.is_empty(),
"label-colors and series label-color tokens must be counted as referenced \
(no token.* diagnostics); got: {:?}",
unused_diags
);
}
#[test]
fn chart_orientation_valid_values_no_diagnostic() {
for value in ["vertical", "horizontal"] {
let src = format!(
r#"zenith version=1 {{
project id="proj.ov" name="OrientationValid"
styles {{
}}
document id="doc.ov" title="OrientationValid" {{
page id="page.ov" w=(px)800 h=(px)600 {{
chart id="c.ov" kind="bar" x=(px)0 y=(px)0 w=(px)400 h=(px)300 orientation="{value}" {{
series 1.0 2.0 label="S"
}}
}}
}}
}}
"#
);
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
!has_code(&report, "chart.invalid_orientation"),
"orientation=\"{value}\" must not fire chart.invalid_orientation; got: {:?}",
codes(&report)
);
}
}
#[test]
fn chart_invalid_orientation_fires_diagnostic() {
let src = r#"zenith version=1 {
project id="proj.io" name="InvalidOrientation"
styles {
}
document id="doc.io" title="InvalidOrientation" {
page id="page.io" w=(px)800 h=(px)600 {
chart id="c.bad" kind="bar" x=(px)0 y=(px)0 w=(px)400 h=(px)300 orientation="diagonal" {
series 1.0 2.0 label="S"
}
}
}
}
"#;
let adapter = KdlAdapter;
let doc = adapter.parse(src.as_bytes()).expect("parse must succeed");
let report = validate(&doc);
assert!(
has_code(&report, "chart.invalid_orientation"),
"bogus orientation must fire chart.invalid_orientation; got: {:?}",
codes(&report)
);
}