#![cfg(test)]
use super::*;
use crate::color::rgb;
#[test]
fn theme_color_resolves_palette_anchors() {
let p = Palette {
paper: rgb(1.0, 0.0, 0.0),
ink: rgb(0.0, 1.0, 0.0),
accent: rgb(0.0, 0.0, 1.0),
};
assert_eq!(ThemeColor::Paper.resolve(&p), p.paper);
assert_eq!(ThemeColor::Ink.resolve(&p), p.ink);
assert_eq!(ThemeColor::Accent.resolve(&p), p.accent);
}
#[test]
fn theme_color_mix_is_linear_in_t() {
let p = Palette {
paper: rgb(0.0, 0.0, 0.0),
ink: rgb(1.0, 1.0, 1.0),
accent: rgb(0.0, 0.0, 0.0),
};
let mid = ThemeColor::mix(ThemeColor::Paper, ThemeColor::Ink, 0.5).resolve(&p);
let [r, g, b, _] = mid.components;
assert!((r - 0.5).abs() < 1e-6);
assert!((g - 0.5).abs() < 1e-6);
assert!((b - 0.5).abs() < 1e-6);
}
#[test]
fn theme_invert_swaps_paper_and_ink() {
let t = Theme::default();
let paper = t.palette.paper;
let ink = t.palette.ink;
let inv = t.invert();
assert_eq!(inv.palette.paper, ink);
assert_eq!(inv.palette.ink, paper);
}
#[test]
fn length_resolve_handles_abs_and_rel() {
assert_eq!(Length::Abs(8.0).resolve(11.0), 8.0);
assert_eq!(Length::Rel(1.5).resolve(11.0), 16.5);
assert_eq!(Length::Rel(0.5).resolve(20.0), 10.0);
}
#[test]
fn element_cascade_walks_inherit_to_parent() {
let parent = TextElement::default();
let leaf: Element<TextElement> = Element::Inherit;
assert!(std::ptr::eq(leaf.cascade(Some(&parent)).unwrap(), &parent));
}
#[test]
fn element_cascade_blank_shortcircuits_to_none() {
let parent = TextElement::default();
let leaf: Element<TextElement> = Element::Blank;
assert!(leaf.cascade(Some(&parent)).is_none());
}
#[test]
fn element_cascade_set_wins_over_parent() {
let parent = TextElement::default();
let mine = TextElement::default();
let leaf: Element<TextElement> = Element::Set(mine.clone());
let resolved = leaf.cascade(Some(&parent)).unwrap();
assert!(!std::ptr::eq(resolved, &parent));
}
#[test]
fn per_channel_resolves_by_channel_then_root() {
let mut pc = PerChannel::new(LineElement {
linewidth_pt: Some(Length::Abs(1.0)),
..LineElement::default()
});
let override_line = LineElement {
linewidth_pt: Some(Length::Abs(2.0)),
..LineElement::default()
};
pc.by_channel[1] = Element::Set(override_line.clone());
let ch0 = pc.resolve(0).unwrap();
let ch1 = pc.resolve(1).unwrap();
assert_eq!(ch0.linewidth_pt, Some(Length::Abs(1.0))); assert_eq!(ch1.linewidth_pt, Some(Length::Abs(2.0))); }
#[test]
fn per_channel_blank_overrides_root() {
let mut pc = PerChannel::new(LineElement {
linewidth_pt: Some(Length::Abs(1.0)),
..LineElement::default()
});
pc.by_channel[0] = Element::Blank;
assert!(pc.resolve(0).is_none()); assert!(pc.resolve(1).is_some()); }
#[test]
fn sided_resolves_most_specific_first() {
let mut s = Sided::new(RectElement {
linewidth_pt: Some(Length::Abs(1.0)),
..RectElement::default()
});
let ch_override = RectElement {
linewidth_pt: Some(Length::Abs(2.0)),
..RectElement::default()
};
let side_override = RectElement {
linewidth_pt: Some(Length::Abs(3.0)),
..RectElement::default()
};
s.by_channel[0] = Element::Set(ch_override);
s.by_channel_side[0][1] = Element::Set(side_override);
let r00 = s.resolve(0, 0).unwrap();
let r01 = s.resolve(0, 1).unwrap();
let r10 = s.resolve(1, 0).unwrap();
assert_eq!(r00.linewidth_pt, Some(Length::Abs(2.0))); assert_eq!(r01.linewidth_pt, Some(Length::Abs(3.0))); assert_eq!(r10.linewidth_pt, Some(Length::Abs(1.0))); }
#[test]
fn per_axis_resolves_per_field() {
let mut axis = PerAxis::new(axis_concrete_defaults());
axis.by_channel[0].tick_length = Some(Length::Abs(8.0));
axis.by_channel_side[1][1].tick_gap = Some(Length::Abs(5.0));
let r00 = axis.resolve(0, 0);
let r11 = axis.resolve(1, 1);
assert_eq!(r00.tick_length, Length::Abs(8.0)); assert_eq!(r00.tick_gap, axis.all.tick_gap.unwrap()); assert_eq!(r11.tick_gap, Length::Abs(5.0)); assert_eq!(r11.tick_length, axis.all.tick_length.unwrap()); }
#[test]
fn theme_part_merge_replaces_set_fields() {
let base = Theme::default();
let part = ThemePart {
legend_spacing: Some(Length::Abs(20.0)),
..ThemePart::default()
};
let merged = base.merge(&part);
assert_eq!(merged.legend_spacing, Length::Abs(20.0));
assert_eq!(merged.palette, base.palette);
}
#[test]
fn theme_part_merges_legend_variants_additively() {
let base = Theme::default().with_legend_variant("a", LegendTheme::default());
let mut variants = std::collections::HashMap::new();
variants.insert("b".to_string(), LegendTheme::default());
let part = ThemePart {
legend_variants: variants,
..ThemePart::default()
};
let merged = base.merge(&part);
assert!(merged.legend_variants.contains_key("a"));
assert!(merged.legend_variants.contains_key("b"));
}
#[test]
fn font_spec_cascade_per_field() {
let parent = FontSpec {
family: Some(FontFamily::SansSerif),
weight: Some(FontWeight::REGULAR),
..FontSpec::default()
};
let child = FontSpec {
weight: Some(FontWeight::BOLD),
..FontSpec::default()
};
let merged = parent.cascade(&child);
assert!(matches!(merged.family, Some(FontFamily::SansSerif)));
assert_eq!(merged.weight, Some(FontWeight::BOLD));
}
#[test]
fn rotation_along_across_resolve_against_baseline() {
assert_eq!(Rotation::Along.resolve(0.0), 0.0);
assert_eq!(Rotation::Across.resolve(0.0), 90.0);
assert_eq!(Rotation::Along.resolve(90.0), 90.0);
assert_eq!(Rotation::Across.resolve(90.0), 180.0);
assert_eq!(Rotation::Along.resolve(45.0), 45.0);
assert_eq!(Rotation::Across.resolve(45.0), 135.0);
assert_eq!(Rotation::Degrees(30.0).resolve(0.0), 30.0);
assert_eq!(Rotation::Degrees(30.0).resolve(90.0), 30.0);
}
#[test]
fn direction_auto_resolves_from_side() {
use crate::scales::chrome::{Anchor, LegendSide};
assert_eq!(
Direction::Auto.resolve(LegendSide::Top),
ResolvedDirection::Horizontal
);
assert_eq!(
Direction::Auto.resolve(LegendSide::Bottom),
ResolvedDirection::Horizontal
);
assert_eq!(
Direction::Auto.resolve(LegendSide::Left),
ResolvedDirection::Vertical
);
assert_eq!(
Direction::Auto.resolve(LegendSide::Right),
ResolvedDirection::Vertical
);
assert_eq!(
Direction::Auto.resolve(LegendSide::InPanel {
anchor: Anchor::TopRight,
inset_pt: 4.0
}),
ResolvedDirection::Vertical
);
assert_eq!(
Direction::Horizontal.resolve(LegendSide::Right),
ResolvedDirection::Horizontal
);
assert_eq!(
Direction::Vertical.resolve(LegendSide::Top),
ResolvedDirection::Vertical
);
}
#[test]
fn themed_grid_dash_pattern_reaches_stroke() {
use crate::plot::chrome::linear_axis::stroke_from_line_element;
use crate::scales::value::LinetypeStep;
use std::sync::Arc;
let element = LineElement {
linewidth_pt: Some(Length::Abs(1.0)),
linetype: Some(Arc::from([LinetypeStep::Dash(4.0), LinetypeStep::Gap(2.0)])),
..LineElement::default()
};
let stroke = stroke_from_line_element(&element, 96.0);
assert!(
!stroke.dash_pattern.is_empty(),
"dashed LineElement must produce a non-empty stroke dash pattern"
);
assert_eq!(stroke.dash_pattern.len(), 2);
}
#[test]
fn font_features_merge_by_tag() {
let parent = FontSpec {
features: vec![FontFeature::new(*b"liga", 1), FontFeature::new(*b"kern", 1)],
..FontSpec::default()
};
let child = FontSpec {
features: vec![
FontFeature::new(*b"kern", 0), FontFeature::new(*b"ss01", 1), ],
..FontSpec::default()
};
let merged = parent.cascade(&child);
let kern = merged.features.iter().find(|f| f.tag == *b"kern").unwrap();
assert_eq!(kern.value, 0); assert!(merged.features.iter().any(|f| f.tag == *b"liga")); assert!(merged.features.iter().any(|f| f.tag == *b"ss01")); }
#[test]
fn text_element_cascade_merges_outline_fields_independently() {
let parent = TextElement {
text_stroke: Some(ThemeColor::Ink),
text_linewidth_pt: Some(Length::Abs(2.0)),
..TextElement::default()
};
let child_width = TextElement {
text_linewidth_pt: Some(Length::Abs(0.5)),
..TextElement::default()
};
let merged = child_width.cascade(&parent);
assert_eq!(merged.text_stroke, Some(ThemeColor::Ink));
assert_eq!(merged.text_linewidth_pt, Some(Length::Abs(0.5)));
let child_color = TextElement {
text_stroke: Some(ThemeColor::Accent),
..TextElement::default()
};
let merged = child_color.cascade(&parent);
assert_eq!(merged.text_stroke, Some(ThemeColor::Accent));
assert_eq!(merged.text_linewidth_pt, Some(Length::Abs(2.0)));
}
#[test]
fn text_concrete_defaults_leaves_text_stroke_unset() {
let d = text_concrete_defaults();
assert!(d.text_stroke.is_none());
assert_eq!(d.text_linewidth_pt, Some(Length::Abs(DEFAULT_LINEWIDTH_PT)));
}