use std::sync::Arc;
use noyalib::{Error, Location};
#[test]
fn location_new_and_accessors() {
let loc = Location::new(10, 5, 100);
assert_eq!(loc.line(), 10);
assert_eq!(loc.column(), 5);
assert_eq!(loc.index(), 100);
}
#[test]
fn location_default() {
let loc = Location::default();
assert_eq!(loc.line(), 0);
assert_eq!(loc.column(), 0);
assert_eq!(loc.index(), 0);
}
#[test]
fn location_display() {
let loc = Location::new(3, 7, 42);
let s = format!("{loc}");
assert_eq!(s, "line 3, column 7");
}
#[test]
fn location_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
let _ = set.insert(Location::new(1, 1, 0));
let _ = set.insert(Location::new(1, 1, 0)); let _ = set.insert(Location::new(2, 1, 5));
assert_eq!(set.len(), 2);
}
#[test]
fn location_clone_and_eq() {
let loc = Location::new(1, 2, 3);
let loc2 = loc;
assert_eq!(loc, loc2);
}
#[test]
fn location_from_index_start() {
let source = "hello\nworld\n";
let loc = Location::from_index(source, 0);
assert_eq!(loc.line(), 1);
assert_eq!(loc.column(), 1);
}
#[test]
fn location_from_index_second_line() {
let source = "hello\nworld\n";
let loc = Location::from_index(source, 6);
assert_eq!(loc.line(), 2);
assert_eq!(loc.column(), 1);
}
#[test]
fn location_from_index_middle_of_line() {
let source = "hello\nworld\n";
let loc = Location::from_index(source, 8);
assert_eq!(loc.line(), 2);
assert_eq!(loc.column(), 3);
}
#[test]
fn location_from_index_past_end() {
let source = "ab";
let loc = Location::from_index(source, 100);
assert_eq!(loc.line(), 1);
assert_eq!(loc.index(), 100);
}
#[test]
fn location_from_index_multibyte() {
let source = "héllo\nworld";
let loc = Location::from_index(source, 7); assert_eq!(loc.line(), 2);
assert_eq!(loc.column(), 1);
}
#[test]
fn error_parse_display() {
let e = Error::Parse("bad yaml".into());
assert_eq!(e.to_string(), "YAML parse error: bad yaml");
}
#[test]
fn error_parse_with_location_display() {
let e = Error::ParseWithLocation {
message: "unexpected token".into(),
location: Location::new(3, 5, 20),
};
let s = e.to_string();
assert!(s.contains("line 3, column 5"));
assert!(s.contains("unexpected token"));
}
#[test]
fn error_serialize_display() {
let e = Error::Serialize("cannot serialize".into());
assert!(e.to_string().contains("cannot serialize"));
}
#[test]
fn error_deserialize_display() {
let e = Error::Deserialize("cannot deserialize".into());
assert!(e.to_string().contains("cannot deserialize"));
}
#[test]
fn error_deserialize_with_location_display() {
let e = Error::DeserializeWithLocation {
message: "wrong type".into(),
location: Location::new(1, 1, 0),
};
let s = e.to_string();
assert!(s.contains("wrong type"));
assert!(s.contains("line 1, column 1"));
}
#[test]
fn error_io() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let e: Error = io_err.into();
assert!(e.to_string().contains("file not found"));
}
#[test]
fn error_invalid() {
let e = Error::Invalid("not valid yaml structure".into());
assert!(e.to_string().contains("not valid yaml structure"));
}
#[test]
fn error_type_mismatch() {
let e = Error::TypeMismatch {
expected: "string",
found: "integer".into(),
};
let s = e.to_string();
assert!(s.contains("string"));
assert!(s.contains("integer"));
}
#[test]
fn error_missing_field() {
let e = Error::MissingField("name".into());
assert!(e.to_string().contains("name"));
}
#[test]
fn error_unknown_field() {
let e = Error::UnknownField("extra".into());
assert!(e.to_string().contains("extra"));
}
#[test]
fn error_recursion_limit() {
let e = Error::RecursionLimitExceeded { depth: 42 };
assert!(e.to_string().contains("42"));
}
#[test]
fn error_repetition_limit() {
let e = Error::RepetitionLimitExceeded;
assert!(!e.to_string().is_empty());
}
#[test]
fn error_unknown_anchor() {
let e = Error::UnknownAnchor("myanchor".into());
assert!(e.to_string().contains("myanchor"));
}
#[test]
fn error_scalar_in_merge() {
let e = Error::ScalarInMerge;
assert!(e.to_string().contains("scalar"));
}
#[test]
fn error_tagged_in_merge() {
let e = Error::TaggedInMerge;
assert!(e.to_string().contains("tagged"));
}
#[test]
fn error_scalar_in_merge_element() {
let e = Error::ScalarInMergeElement;
assert!(e.to_string().contains("scalar"));
}
#[test]
fn error_sequence_in_merge_element() {
let e = Error::SequenceInMergeElement;
assert!(e.to_string().contains("sequence"));
}
#[test]
fn error_empty_tag() {
let e = Error::EmptyTag;
assert!(e.to_string().contains("tag"));
}
#[test]
fn error_failed_to_parse_number() {
let e = Error::FailedToParseNumber("abc".into());
assert!(e.to_string().contains("abc"));
}
#[test]
fn error_end_of_stream() {
let e = Error::EndOfStream;
assert!(!e.to_string().is_empty());
}
#[test]
fn error_more_than_one_document() {
let e = Error::MoreThanOneDocument;
assert!(e.to_string().contains("multiple"));
}
#[test]
fn error_duplicate_key() {
let e = Error::DuplicateKey("name".into());
assert!(e.to_string().contains("name"));
}
#[test]
fn error_custom() {
let e = Error::Custom("custom error".into());
assert_eq!(e.to_string(), "custom error");
}
#[test]
fn error_location_parse_with_location() {
let e = Error::ParseWithLocation {
message: "test".into(),
location: Location::new(5, 3, 20),
};
let loc = e.location().unwrap();
assert_eq!(loc.line(), 5);
assert_eq!(loc.column(), 3);
}
#[test]
fn error_location_deserialize_with_location() {
let e = Error::DeserializeWithLocation {
message: "test".into(),
location: Location::new(2, 1, 10),
};
assert!(e.location().is_some());
}
#[test]
fn error_location_none_for_plain_errors() {
assert!(Error::Parse("test".into()).location().is_none());
assert!(Error::Serialize("test".into()).location().is_none());
assert!(Error::Deserialize("test".into()).location().is_none());
assert!(Error::Custom("test".into()).location().is_none());
}
#[test]
fn error_parse_at() {
let source = "hello\nworld\n";
let e = Error::parse_at("bad token", source, 6);
let loc = e.location().unwrap();
assert_eq!(loc.line(), 2);
assert_eq!(loc.column(), 1);
}
#[test]
fn error_deserialize_at() {
let source = "hello\nworld\n";
let e = Error::deserialize_at("wrong type", source, 8);
let loc = e.location().unwrap();
assert_eq!(loc.line(), 2);
assert_eq!(loc.column(), 3);
}
#[test]
fn format_with_source_no_location() {
let e = Error::Parse("no loc".into());
let formatted = e.format_with_source("some source");
assert_eq!(formatted, e.to_string());
}
#[test]
fn format_with_source_with_location() {
let e = Error::ParseWithLocation {
message: "unexpected".into(),
location: Location::new(1, 3, 2),
};
let source = "hello world";
let formatted = e.format_with_source(source);
assert!(formatted.contains("error:"));
assert!(formatted.contains("-->"));
assert!(formatted.contains("hello world"));
assert!(formatted.contains("^"));
}
#[test]
fn format_with_source_line_out_of_range() {
let e = Error::ParseWithLocation {
message: "test".into(),
location: Location::new(100, 1, 0),
};
let source = "one line";
let formatted = e.format_with_source(source);
assert_eq!(formatted, e.to_string());
}
#[test]
fn format_with_source_column_zero() {
let e = Error::ParseWithLocation {
message: "test".into(),
location: Location::new(1, 0, 0),
};
let source = "hello";
let formatted = e.format_with_source(source);
assert!(formatted.contains("^"));
}
#[test]
fn shared_error_location_delegates() {
let e = Error::ParseWithLocation {
message: "inner".into(),
location: Location::new(5, 3, 20),
};
let shared = Error::from_shared(e.into_shared());
let loc = shared.location().unwrap();
assert_eq!(loc.line(), 5);
}
#[test]
fn shared_error_display() {
let e = Error::Parse("test".into());
let shared = Error::Shared(Arc::new(e));
assert_eq!(shared.to_string(), "YAML parse error: test");
}
#[test]
fn into_shared_already_shared_no_double_wrap() {
let e = Error::Parse("test".into());
let arc1 = e.into_shared();
let shared = Error::Shared(Arc::clone(&arc1));
let arc2 = shared.into_shared();
assert!(Arc::ptr_eq(&arc1, &arc2));
}
#[test]
fn is_shared_and_as_inner() {
let e = Error::Parse("test".into());
assert!(!e.is_shared());
assert!(e.as_inner().is_none());
let shared = Error::from_shared(e.into_shared());
assert!(shared.is_shared());
let inner = shared.as_inner().unwrap();
assert!(inner.to_string().contains("test"));
}
#[test]
fn serde_de_error_custom() {
use serde::de::Error as _;
let e = Error::custom("custom de error");
assert_eq!(e.to_string(), "custom de error");
}
#[test]
fn serde_ser_error_custom() {
use serde::ser::Error as _;
let e = Error::custom("custom ser error");
assert_eq!(e.to_string(), "custom ser error");
}