mod common;
use common::init_test_logging;
use rich_rust::prelude::*;
use rich_rust::style::StyleParseError;
#[test]
fn test_style_parse_empty_string() {
init_test_logging();
let style = Style::parse("").unwrap();
assert!(style.is_null(), "Empty string should produce null style");
}
#[test]
fn test_style_parse_none_keyword() {
init_test_logging();
let style = Style::parse("none").unwrap();
assert!(style.is_null(), "'none' should produce null style");
}
#[test]
fn test_style_parse_bold_only() {
init_test_logging();
let style = Style::parse("bold").unwrap();
assert!(
style.attributes.contains(Attributes::BOLD),
"Should have bold attribute"
);
assert!(style.color.is_none(), "Should have no foreground color");
assert!(style.bgcolor.is_none(), "Should have no background color");
}
#[test]
fn test_style_parse_italic_only() {
init_test_logging();
let style = Style::parse("italic").unwrap();
assert!(
style.attributes.contains(Attributes::ITALIC),
"Should have italic attribute"
);
}
#[test]
fn test_style_parse_underline_only() {
init_test_logging();
let style = Style::parse("underline").unwrap();
assert!(
style.attributes.contains(Attributes::UNDERLINE),
"Should have underline attribute"
);
}
#[test]
fn test_style_parse_red_foreground() {
init_test_logging();
let style = Style::parse("red").unwrap();
assert!(style.color.is_some(), "Should have foreground color");
assert!(style.bgcolor.is_none(), "Should have no background color");
assert!(style.attributes.is_empty(), "Should have no attributes");
}
#[test]
fn test_style_parse_on_blue_background() {
init_test_logging();
let style = Style::parse("on blue").unwrap();
assert!(style.bgcolor.is_some(), "Should have background color");
assert!(style.color.is_none(), "Should have no foreground color");
}
#[test]
fn test_style_parse_combined() {
init_test_logging();
let style = Style::parse("bold italic red on blue").unwrap();
assert!(
style.attributes.contains(Attributes::BOLD),
"Should have bold"
);
assert!(
style.attributes.contains(Attributes::ITALIC),
"Should have italic"
);
assert!(style.color.is_some(), "Should have foreground color");
assert!(style.bgcolor.is_some(), "Should have background color");
}
#[test]
fn test_style_parse_not_bold() {
init_test_logging();
let style = Style::parse("not bold").unwrap();
assert!(
!style.attributes.contains(Attributes::BOLD),
"Bold should not be set"
);
assert!(
style.set_attributes.contains(Attributes::BOLD),
"Bold should be in set_attributes"
);
}
#[test]
fn test_style_parse_hex_color() {
init_test_logging();
let style = Style::parse("#ff0000").unwrap();
assert!(style.color.is_some(), "Should have foreground color");
let color = style.color.unwrap();
let triplet = color.triplet.expect("Should have triplet");
assert_eq!(triplet.red, 255, "Red component should be 255");
assert_eq!(triplet.green, 0, "Green component should be 0");
assert_eq!(triplet.blue, 0, "Blue component should be 0");
}
#[test]
fn test_style_parse_rgb_color() {
init_test_logging();
let style = Style::parse("rgb(0,255,0)").unwrap();
assert!(style.color.is_some(), "Should have foreground color");
let color = style.color.unwrap();
let triplet = color.triplet.expect("Should have triplet");
assert_eq!(triplet.red, 0, "Red component should be 0");
assert_eq!(triplet.green, 255, "Green component should be 255");
assert_eq!(triplet.blue, 0, "Blue component should be 0");
}
#[test]
fn test_style_parse_256_color() {
init_test_logging();
let style = Style::parse("color(128)").unwrap();
assert!(style.color.is_some(), "Should have foreground color");
let color = style.color.unwrap();
assert_eq!(color.number, Some(128), "Color number should be 128");
}
#[test]
fn test_style_parse_hyperlink() {
init_test_logging();
let style = Style::parse("link https://example.com").unwrap();
assert_eq!(
style.link,
Some("https://example.com".to_string()),
"Should have hyperlink"
);
}
#[test]
fn test_style_parse_short_aliases() {
init_test_logging();
let style = Style::parse("b").unwrap();
assert!(
style.attributes.contains(Attributes::BOLD),
"'b' should map to bold"
);
let style = Style::parse("i").unwrap();
assert!(
style.attributes.contains(Attributes::ITALIC),
"'i' should map to italic"
);
let style = Style::parse("u").unwrap();
assert!(
style.attributes.contains(Attributes::UNDERLINE),
"'u' should map to underline"
);
let style = Style::parse("d").unwrap();
assert!(
style.attributes.contains(Attributes::DIM),
"'d' should map to dim"
);
let style = Style::parse("s").unwrap();
assert!(
style.attributes.contains(Attributes::STRIKE),
"'s' should map to strike"
);
let style = Style::parse("o").unwrap();
assert!(
style.attributes.contains(Attributes::OVERLINE),
"'o' should map to overline"
);
let style = Style::parse("r").unwrap();
assert!(
style.attributes.contains(Attributes::REVERSE),
"'r' should map to reverse"
);
}
#[test]
fn test_style_parse_all_attributes() {
init_test_logging();
let attributes = [
("bold", Attributes::BOLD),
("dim", Attributes::DIM),
("italic", Attributes::ITALIC),
("underline", Attributes::UNDERLINE),
("blink", Attributes::BLINK),
("blink2", Attributes::BLINK2),
("reverse", Attributes::REVERSE),
("conceal", Attributes::CONCEAL),
("strike", Attributes::STRIKE),
("underline2", Attributes::UNDERLINE2),
("frame", Attributes::FRAME),
("encircle", Attributes::ENCIRCLE),
("overline", Attributes::OVERLINE),
];
for (name, expected) in attributes {
let style = Style::parse(name).unwrap_or_else(|_| panic!("Should parse '{}'", name));
assert!(
style.attributes.contains(expected),
"'{}' should set {:?} attribute",
name,
expected
);
}
}
#[test]
fn test_style_parse_multiple_attributes() {
init_test_logging();
let style = Style::parse("bold italic underline strike").unwrap();
assert!(style.attributes.contains(Attributes::BOLD));
assert!(style.attributes.contains(Attributes::ITALIC));
assert!(style.attributes.contains(Attributes::UNDERLINE));
assert!(style.attributes.contains(Attributes::STRIKE));
}
#[test]
fn test_style_parse_on_hex_color() {
init_test_logging();
let style = Style::parse("on #0000ff").unwrap();
assert!(style.bgcolor.is_some(), "Should have background color");
let color = style.bgcolor.unwrap();
let triplet = color.triplet.expect("Should have triplet");
assert_eq!(triplet.blue, 255, "Blue component should be 255");
}
#[test]
fn test_style_parse_case_insensitive() {
init_test_logging();
let style1 = Style::parse("BOLD RED").unwrap();
let style2 = Style::parse("Bold Red").unwrap();
let style3 = Style::parse("bold red").unwrap();
assert!(style1.attributes.contains(Attributes::BOLD));
assert!(style2.attributes.contains(Attributes::BOLD));
assert!(style3.attributes.contains(Attributes::BOLD));
assert!(style1.color.is_some());
assert!(style2.color.is_some());
assert!(style3.color.is_some());
}
#[test]
fn test_style_parse_extra_whitespace() {
init_test_logging();
let style = Style::parse(" bold red on blue ").unwrap();
assert!(style.attributes.contains(Attributes::BOLD));
assert!(style.color.is_some());
assert!(style.bgcolor.is_some());
}
#[test]
fn test_style_parse_invalid_keyword() {
init_test_logging();
let result = Style::parse("invalid");
assert!(result.is_err(), "'invalid' should produce an error");
match result {
Err(StyleParseError::UnknownToken(token)) => {
assert_eq!(token, "invalid", "Error should contain the unknown token");
}
Err(other) => panic!("Expected UnknownToken error, got {:?}", other),
Ok(_) => panic!("Expected error for 'invalid'"),
}
}
#[test]
fn test_style_parse_invalid_color() {
init_test_logging();
let result = Style::parse("notacolor");
assert!(result.is_err(), "'notacolor' should produce an error");
}
#[test]
fn test_style_parse_incomplete_on() {
init_test_logging();
let result = Style::parse("on");
assert!(result.is_err(), "'on' alone should produce an error");
match result {
Err(StyleParseError::InvalidFormat(msg)) => {
assert!(
msg.contains("requires a color"),
"Error should mention 'requires a color'"
);
}
Err(other) => panic!("Expected InvalidFormat error, got {:?}", other),
Ok(_) => panic!("Expected error for 'on' alone"),
}
}
#[test]
fn test_style_parse_incomplete_not() {
init_test_logging();
let result = Style::parse("not");
assert!(result.is_err(), "'not' alone should produce an error");
match result {
Err(StyleParseError::InvalidFormat(msg)) => {
assert!(
msg.contains("requires an attribute"),
"Error should mention 'requires an attribute'"
);
}
Err(other) => panic!("Expected InvalidFormat error, got {:?}", other),
Ok(_) => panic!("Expected error for 'not' alone"),
}
}
#[test]
fn test_style_parse_incomplete_link() {
init_test_logging();
let result = Style::parse("link");
assert!(result.is_err(), "'link' alone should produce an error");
match result {
Err(StyleParseError::InvalidFormat(msg)) => {
assert!(
msg.contains("requires a URL"),
"Error should mention 'requires a URL'"
);
}
Err(other) => panic!("Expected InvalidFormat error, got {:?}", other),
Ok(_) => panic!("Expected error for 'link' alone"),
}
}
#[test]
fn test_style_parse_not_invalid_attribute() {
init_test_logging();
let result = Style::parse("not invalid");
assert!(result.is_err(), "'not invalid' should produce an error");
match result {
Err(StyleParseError::UnknownAttribute(attr)) => {
assert_eq!(
attr, "invalid",
"Error should contain the unknown attribute"
);
}
Err(other) => panic!("Expected UnknownAttribute error, got {:?}", other),
Ok(_) => panic!("Expected error for 'not invalid'"),
}
}
#[test]
fn test_style_parse_invalid_hex() {
init_test_logging();
let result = Style::parse("#ff");
assert!(result.is_err(), "'#ff' should produce an error");
let result = Style::parse("#gggggg");
assert!(result.is_err(), "'#gggggg' should produce an error");
}
#[test]
fn test_style_parse_invalid_rgb() {
init_test_logging();
let result = Style::parse("rgb(255)");
assert!(result.is_err(), "'rgb(255)' should produce an error");
let result = Style::parse("rgb(256,0,0)");
assert!(result.is_err(), "'rgb(256,0,0)' should produce an error");
}
#[test]
fn test_style_parse_invalid_color_number() {
init_test_logging();
let result = Style::parse("color(256)");
assert!(result.is_err(), "'color(256)' should produce an error");
let result = Style::parse("color(-1)");
assert!(result.is_err(), "'color(-1)' should produce an error");
}
#[test]
fn test_style_parse_duplicate_attribute() {
init_test_logging();
let result = Style::parse("bold bold");
if let Ok(style) = result {
assert!(style.attributes.contains(Attributes::BOLD));
}
}
#[test]
fn test_style_parse_two_foreground_colors() {
init_test_logging();
let style = Style::parse("red blue").unwrap();
assert!(style.color.is_some());
let color = style.color.unwrap();
assert!(color.number.is_some() || color.triplet.is_some());
}
#[test]
fn test_style_not_then_set() {
init_test_logging();
let style = Style::parse("not bold bold").unwrap();
assert!(
style.attributes.contains(Attributes::BOLD),
"Second 'bold' should set the attribute"
);
}
#[test]
fn test_style_set_then_not() {
init_test_logging();
let style = Style::parse("bold not bold").unwrap();
assert!(
!style.attributes.contains(Attributes::BOLD),
"'not bold' should unset the attribute"
);
assert!(
style.set_attributes.contains(Attributes::BOLD),
"Bold should still be in set_attributes"
);
}
#[test]
fn test_style_combine_preserves_explicit() {
init_test_logging();
let style1 = Style::new().bold().color(Color::parse("red").unwrap());
let style2 = Style::parse("not bold").unwrap();
let combined = style1.combine(&style2);
assert!(!combined.attributes.contains(Attributes::BOLD));
assert!(combined.set_attributes.contains(Attributes::BOLD));
assert!(combined.color.is_some());
}
#[test]
fn test_style_combine_null_identity() {
init_test_logging();
let style = Style::parse("bold red").unwrap();
let null = Style::null();
let combined1 = style.combine(&null);
let combined2 = null.combine(&style);
assert_eq!(combined1.attributes, style.attributes);
assert_eq!(combined2.attributes, style.attributes);
}
#[test]
fn test_style_display_roundtrip() {
init_test_logging();
let original = Style::parse("bold red on blue").unwrap();
let display = original.to_string();
let reparsed = Style::parse(&display).unwrap();
assert_eq!(original.attributes, reparsed.attributes);
assert!(original.color.is_some() && reparsed.color.is_some());
assert!(original.bgcolor.is_some() && reparsed.bgcolor.is_some());
}
#[test]
fn test_style_null_display() {
init_test_logging();
let null = Style::null();
assert_eq!(null.to_string(), "none");
}
#[test]
fn test_style_parse_cached() {
init_test_logging();
let style1 = Style::parse("bold red").unwrap();
let style2 = Style::parse("bold red").unwrap();
assert_eq!(style1, style2, "Cached results should be equal");
}
#[test]
fn test_style_parse_case_normalized_cache() {
init_test_logging();
let style1 = Style::parse("BOLD RED").unwrap();
let style2 = Style::parse("bold red").unwrap();
assert_eq!(style1, style2, "Case-normalized styles should be equal");
}
#[test]
fn test_style_from_str() {
init_test_logging();
let style: Style = "bold red".parse().unwrap();
assert!(style.attributes.contains(Attributes::BOLD));
assert!(style.color.is_some());
}
#[test]
fn test_style_try_from_str() {
init_test_logging();
let style: Style = Style::try_from("bold red").unwrap();
assert!(style.attributes.contains(Attributes::BOLD));
}
#[test]
fn test_style_try_from_string() {
init_test_logging();
let style: Style = Style::try_from("bold red".to_string()).unwrap();
assert!(style.attributes.contains(Attributes::BOLD));
}
#[test]
fn test_style_from_color() {
init_test_logging();
let color = Color::parse("red").unwrap();
let style: Style = color.into();
assert!(style.color.is_some());
assert!(style.attributes.is_empty());
}
#[test]
fn test_style_from_tuple() {
init_test_logging();
let style: Style = (255u8, 0u8, 0u8).into();
assert!(style.color.is_some());
let triplet = style.color.unwrap().triplet.unwrap();
assert_eq!(triplet.red, 255);
assert_eq!(triplet.green, 0);
assert_eq!(triplet.blue, 0);
}
#[test]
fn test_style_from_array() {
init_test_logging();
let style: Style = [255u8, 0u8, 0u8].into();
assert!(style.color.is_some());
}
#[test]
fn test_style_add_operator() {
init_test_logging();
let s1 = Style::new().bold();
let s2 = Style::new().italic();
let combined = s1 + s2;
assert!(combined.attributes.contains(Attributes::BOLD));
assert!(combined.attributes.contains(Attributes::ITALIC));
}
#[test]
fn test_style_add_operator_ref() {
init_test_logging();
let s1 = Style::new().bold();
let s2 = Style::new().italic();
let combined = s1 + &s2;
assert!(combined.attributes.contains(Attributes::BOLD));
assert!(combined.attributes.contains(Attributes::ITALIC));
}
#[test]
fn test_style_ref_add_operator() {
init_test_logging();
let s1 = Style::new().bold();
let s2 = Style::new().italic();
let combined = &s1 + s2;
assert!(combined.attributes.contains(Attributes::BOLD));
assert!(combined.attributes.contains(Attributes::ITALIC));
}
#[test]
fn test_style_ref_add_ref_operator() {
init_test_logging();
let s1 = Style::new().bold();
let s2 = Style::new().italic();
let combined = &s1 + &s2;
assert!(combined.attributes.contains(Attributes::BOLD));
assert!(combined.attributes.contains(Attributes::ITALIC));
}