use noyalib::{Mapping, Value, from_str, from_value, to_string};
use serde::{Deserialize, Serialize};
#[test]
fn scanner_double_quoted_hex_escape_x_produces_char() {
let yaml = r#""\x41""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "A");
}
#[test]
fn scanner_double_quoted_hex_escape_u_produces_unicode() {
let yaml = r#""\u0041""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "A");
}
#[test]
fn scanner_double_quoted_hex_escape_upper_u_produces_unicode() {
let yaml = r#""\U00000041""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "A");
}
#[test]
fn scanner_double_quoted_nel_escape() {
let yaml = r#""\N""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "\u{0085}");
}
#[test]
fn scanner_double_quoted_nbsp_escape() {
let yaml = r#""\_""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "\u{00A0}");
}
#[test]
fn scanner_double_quoted_line_sep_escape() {
let yaml = r#""\L""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "\u{2028}");
}
#[test]
fn scanner_double_quoted_para_sep_escape() {
let yaml = r#""\P""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "\u{2029}");
}
#[test]
fn scanner_double_quoted_null_escape() {
let yaml = r#""\0""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "\0");
}
#[test]
fn scanner_double_quoted_bell_escape() {
let yaml = r#""\a""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "\x07");
}
#[test]
fn scanner_double_quoted_backspace_escape() {
let yaml = r#""\b""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "\x08");
}
#[test]
fn scanner_double_quoted_vtab_escape() {
let yaml = r#""\v""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "\x0B");
}
#[test]
fn scanner_double_quoted_formfeed_escape() {
let yaml = r#""\f""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "\x0C");
}
#[test]
fn scanner_double_quoted_esc_escape() {
let yaml = r#""\e""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "\x1B");
}
#[test]
fn scanner_double_quoted_slash_escape() {
let yaml = r#""\/""#;
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "/");
}
#[test]
fn scanner_double_quoted_space_escape() {
let yaml = "\"\\ \"";
let v: String = from_str(yaml).unwrap();
assert!(v.contains(' '));
}
#[test]
fn scanner_double_quoted_unknown_escape_returns_error() {
let yaml = r#""\q""#;
let result: Result<String, _> = from_str(yaml);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("unknown escape"));
}
#[test]
fn scanner_double_quoted_line_break_escape_folds() {
let yaml = "\"hello\\\n world\"";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "helloworld");
}
#[test]
fn scanner_double_quoted_multiline_folding() {
let yaml = "\"first\n second\"";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "first second");
}
#[test]
fn scanner_double_quoted_multiline_with_blank_lines() {
let yaml = "\"first\n\n second\"";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "first\nsecond");
}
#[test]
fn scanner_single_quoted_escaped_quote() {
let yaml = "'it''s'";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "it's");
}
#[test]
fn scanner_single_quoted_multiline_folding() {
let yaml = "'first\n second'";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "first second");
}
#[test]
fn scanner_single_quoted_multiline_blank_lines() {
let yaml = "'first\n\n second'";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "first\nsecond");
}
#[test]
fn scanner_unterminated_single_quoted_returns_error() {
let yaml = "'unterminated";
let result: Result<String, _> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn scanner_unterminated_double_quoted_returns_error() {
let yaml = "\"unterminated";
let result: Result<String, _> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn scanner_tab_as_indentation_returns_error() {
let yaml = "key:\n\tvalue";
let result: Result<Value, _> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn scanner_comment_skipping_works() {
let yaml = "# comment\nkey: value # inline comment\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"], Value::String("value".to_string()));
}
#[test]
fn scanner_plain_scalar_multiline_in_block() {
let yaml = "key: first\n second\n third\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"], Value::String("first second third".to_string()));
}
#[test]
fn scanner_block_scalar_literal_strip() {
let yaml = "|\n hello\n world\n";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "hello\nworld\n");
}
#[test]
fn scanner_block_scalar_literal_strip_chomping() {
let yaml = "|-\n hello\n world\n\n";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "hello\nworld");
}
#[test]
fn scanner_block_scalar_literal_keep_chomping() {
let yaml = "|+\n hello\n world\n\n";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "hello\nworld\n\n");
}
#[test]
fn scanner_block_scalar_folded_basic() {
let yaml = ">\n hello\n world\n";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "hello world\n");
}
#[test]
fn scanner_block_scalar_folded_strip_chomping() {
let yaml = ">-\n hello\n world\n\n";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "hello world");
}
#[test]
fn scanner_block_scalar_folded_keep_chomping() {
let yaml = ">+\n hello\n world\n\n";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "hello world\n\n");
}
#[test]
fn scanner_block_scalar_folded_multiple_breaks_preserved() {
let yaml = ">\n first\n\n second\n";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "first\nsecond\n");
}
#[test]
fn scanner_block_scalar_folded_leading_blank_preserves_break() {
let yaml = ">\n normal\n indented\n back\n";
let v: String = from_str(yaml).unwrap();
assert!(v.contains('\n'));
}
#[test]
fn scanner_block_scalar_explicit_indent() {
let yaml = "|2\n hello\n world\n";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "hello\nworld\n");
}
#[test]
fn scanner_block_scalar_empty_content() {
let yaml = "|-\n";
let v: String = from_str(yaml).unwrap();
assert_eq!(v, "");
}
#[test]
fn scanner_primary_tag_handle() {
let yaml = "!custom value";
let v: Value = from_str(yaml).unwrap();
match v {
Value::Tagged(t) => assert_eq!(t.tag().as_str(), "!custom"),
Value::String(s) => assert_eq!(s, "value"),
other => panic!("expected tagged or string, got {other:?}"),
}
}
#[test]
fn scanner_complex_mapping_key() {
let yaml = "? key\n: value\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"], Value::String("value".to_string()));
}
#[test]
fn scanner_mapping_value_not_allowed_error() {
let yaml = ": value";
let result: Result<Value, _> = from_str(yaml);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn scanner_flow_key_in_block_context() {
let yaml = "{a: 1}: value\n";
let result: Result<Value, _> = from_str(yaml);
let _ = result;
}
#[test]
fn scanner_block_entry_in_flow_context() {
let yaml = "[- item]";
let result: Result<Value, _> = from_str(yaml);
let _ = result;
}
#[test]
fn parser_flow_sequence_entry_mapping() {
let yaml = "[{a: 1}, {b: 2}]";
let v: Value = from_str(yaml).unwrap();
let seq = v.as_sequence().unwrap();
assert_eq!(seq.len(), 2);
}
#[test]
fn parser_flow_sequence_implicit_key() {
let yaml = "[a: 1, b: 2]";
let v: Value = from_str(yaml).unwrap();
let seq = v.as_sequence().unwrap();
assert_eq!(seq.len(), 2);
}
#[test]
fn parser_flow_mapping_explicit_key() {
let yaml = "{a: b, c: d}";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"], Value::String("b".to_string()));
assert_eq!(v["c"], Value::String("d".to_string()));
}
#[test]
fn parser_flow_mapping_empty_value() {
let yaml = "{a: , b: }";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["a"], Value::Null);
assert_eq!(v["b"], Value::Null);
}
#[test]
fn parser_indentless_sequence() {
let yaml = "key:\n - a\n - b\n";
let v: Value = from_str(yaml).unwrap();
let seq = v["key"].as_sequence().unwrap();
assert_eq!(seq.len(), 2);
}
#[test]
fn parser_block_mapping_value_state() {
let yaml = "a: 1\nb: 2\nc: 3\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["c"], Value::from(3));
}
#[test]
fn parser_empty_document_content() {
let yaml = "---\n...\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v, Value::Null);
}
#[test]
fn parser_anchor_with_tag() {
let yaml = "a: &anchor !tag value\nb: *anchor\n";
let v: Value = from_str(yaml).unwrap();
match &v["a"] {
Value::Tagged(_) | Value::String(_) => {}
other => panic!("expected tagged or string, got {other:?}"),
}
}
#[test]
fn parser_tag_then_anchor() {
let yaml = "a: !tag &anchor value\nb: *anchor\n";
let v: Value = from_str(yaml).unwrap();
match &v["a"] {
Value::Tagged(_) | Value::String(_) => {}
other => panic!("expected tagged or string, got {other:?}"),
}
}
#[test]
fn parser_anchor_only_implicit_scalar() {
let yaml = "key: &anchor\nother: *anchor\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"], Value::Null);
}
#[test]
fn parser_block_sequence_empty_entries() {
let yaml = "-\n-\n-\n";
let v: Value = from_str(yaml).unwrap();
let seq = v.as_sequence().unwrap();
assert_eq!(seq.len(), 3);
assert!(seq.iter().all(|v| *v == Value::Null));
}
#[test]
fn loader_recursion_depth_exceeded_returns_error() {
let mut yaml = String::new();
for i in 0..200 {
for _ in 0..i {
yaml.push(' ');
yaml.push(' ');
}
yaml.push_str("a:\n");
}
let result: Result<Value, _> = from_str(&yaml);
assert!(result.is_err());
}
#[test]
fn loader_alias_expansion_limit_exceeded_returns_error() {
let mut yaml = String::from("anchor: &a big_value\nseq:\n");
for _ in 0..200 {
yaml.push_str(" - *a\n");
}
let result: Result<Value, _> = from_str(&yaml);
let _ = result;
}
#[test]
fn loader_unknown_anchor_returns_error() {
let yaml = "value: *nonexistent\n";
let result: Result<Value, _> = from_str(yaml);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("unknown anchor"));
}
#[test]
fn loader_unexpected_sequence_end_returns_error() {
let yaml = "]\n";
let result: Result<Value, _> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn loader_merge_key_with_mapping() {
let yaml = "defaults: &defs\n a: 1\n b: 2\nresult:\n <<: *defs\n c: 3\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["result"]["a"], Value::from(1));
assert_eq!(v["result"]["c"], Value::from(3));
}
#[test]
fn loader_merge_key_with_sequence_of_mappings() {
let yaml = "d1: &d1\n a: 1\nd2: &d2\n b: 2\nresult:\n <<: [*d1, *d2]\n c: 3\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["result"]["a"], Value::from(1));
assert_eq!(v["result"]["b"], Value::from(2));
assert_eq!(v["result"]["c"], Value::from(3));
}
#[test]
fn loader_merge_key_scalar_returns_error() {
let yaml = "result:\n <<: scalar_value\n";
let result: Result<Value, _> = from_str(yaml);
assert!(result.is_err());
}
#[test]
fn loader_duplicate_key_last_wins_by_default() {
let yaml = "key: first\nkey: second\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["key"], Value::String("second".to_string()));
}
#[test]
fn loader_large_integer_overflow_to_float() {
let yaml = "99999999999999999999999999999999";
let v: Value = from_str(yaml).unwrap();
match v {
Value::Number(_) => {} _ => panic!("expected number"),
}
}
#[test]
fn loader_anchored_sequence() {
let yaml = "seq: &s\n - 1\n - 2\nalias: *s\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["seq"], v["alias"]);
}
#[test]
fn loader_anchored_mapping() {
let yaml = "map: &m\n a: 1\nalias: *m\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["map"], v["alias"]);
}
#[test]
fn de_deserialize_ignored_any_returns_unit() {
#[derive(Deserialize, Debug)]
struct Simple {
name: String,
}
let yaml = "name: test\nextra: ignored\n";
let v: Simple = from_str(yaml).unwrap();
assert_eq!(v.name, "test");
}
#[test]
fn de_spanned_field_deserialization() {
use noyalib::Spanned;
let yaml = "hello";
let v: Spanned<String> = from_str(yaml).unwrap();
assert_eq!(v.value, "hello");
assert!(v.start.line() >= 1);
}
#[test]
fn de_spanned_map_access_all_fields() {
use noyalib::Spanned;
let yaml = "key: value";
let v: Spanned<Value> = from_str(yaml).unwrap();
assert!(v.start.line() >= 1);
let _ = v.start.column();
let _ = v.start.index();
assert!(v.end.line() >= 1);
let _ = v.end.column();
let _ = v.end.index();
}
#[test]
fn de_type_mismatch_on_enum_from_non_map_non_string() {
#[derive(Deserialize, Debug)]
enum MyEnum {
A,
B,
}
let v = Value::from(42);
let result: Result<MyEnum, _> = from_value(&v);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("expected") || err.contains("mismatch"));
}
#[test]
fn ser_flow_map_tag_with_non_mapping_falls_through() {
use noyalib::fmt::FlowMap;
let v = FlowMap(42);
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("42"));
}
#[test]
fn ser_flow_seq_tag_with_non_sequence_falls_through() {
use noyalib::fmt::FlowSeq;
let v = FlowSeq("hello");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("hello"));
}
#[test]
fn ser_literal_block_tag_with_string() {
use noyalib::fmt::LitStr;
let v = LitStr("hello\nworld");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("hello"));
}
#[test]
fn ser_folded_block_tag_with_string() {
use noyalib::fmt::FoldStr;
let v = FoldStr("hello\nworld");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("hello"));
}
#[test]
fn ser_commented_wrapper_serializes() {
use noyalib::fmt::Commented;
let v = Commented::new(42, "this is a comment");
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("42"));
assert!(yaml.contains("# this is a comment"));
}
#[test]
fn ser_commented_non_sequence_falls_through() {
use noyalib::{Tag, TaggedValue};
let tag = Tag::new("__noya_commented");
let inner = Value::from(42);
let tagged = Value::Tagged(Box::new(TaggedValue::new(tag, inner)));
let yaml = to_string(&tagged).unwrap();
assert!(yaml.contains("42"));
}
#[test]
fn ser_space_after_tag() {
use noyalib::fmt::SpaceAfter;
let v = SpaceAfter(42);
let yaml = to_string(&v).unwrap();
assert!(yaml.contains("42"));
}
#[test]
fn ser_tagged_value_serialization() {
use noyalib::{Tag, TaggedValue};
let tag = Tag::new("!custom");
let inner = Value::String("hello".to_string());
let tagged = Value::Tagged(Box::new(TaggedValue::new(tag, inner)));
let yaml = to_string(&tagged).unwrap();
assert!(yaml.contains("!custom"));
assert!(yaml.contains("hello"));
}
#[test]
fn ser_looks_like_number_edge_cases() {
let v = Value::String("1.0".to_string());
let yaml = to_string(&v).unwrap();
assert!(yaml.contains('\'') || yaml.contains('"'));
let v = Value::String("+1".to_string());
let yaml = to_string(&v).unwrap();
assert!(yaml.contains('\'') || yaml.contains('"'));
let v = Value::String("+".to_string());
let yaml = to_string(&v).unwrap();
assert!(yaml.contains('+'));
}
#[test]
fn value_index_usize_out_of_bounds_panics() {
let v = Value::Sequence(vec![Value::from(1)]);
let result = std::panic::catch_unwind(|| {
let _ = &v[5];
});
assert!(result.is_err());
}
#[test]
fn value_index_usize_on_non_sequence_panics() {
let v = Value::from(42);
let result = std::panic::catch_unwind(|| {
let _ = &v[0];
});
assert!(result.is_err());
}
#[test]
fn value_index_mut_usize_on_tagged() {
let mut v = Value::Tagged(Box::new(noyalib::TaggedValue::new(
noyalib::Tag::new("!t"),
Value::Sequence(vec![Value::from(1), Value::from(2)]),
)));
v[0] = Value::from(99);
if let Value::Tagged(t) = &v {
if let Value::Sequence(s) = t.value() {
assert_eq!(s[0], Value::from(99));
}
}
}
#[test]
fn value_type_name_for_error_messages() {
let v = Value::Null;
let result: Result<Vec<i32>, _> = from_value(&v);
assert!(result.is_err());
let v = Value::Bool(true);
let result: Result<Vec<i32>, _> = from_value(&v);
assert!(result.is_err());
}
#[test]
fn value_deserialize_visit_string() {
let yaml = "hello world";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v, Value::String("hello world".to_string()));
}
#[test]
fn value_deserialize_via_ref() {
let v = Value::Sequence(vec![Value::from(1), Value::from(2)]);
let result: Vec<i64> = from_value(&v).unwrap();
assert_eq!(result, vec![1, 2]);
}
#[test]
fn value_deserialize_enum_from_ref() {
#[derive(Deserialize, Debug, PartialEq)]
enum Color {
Red,
Blue,
}
let v = Value::String("Red".to_string());
let result: Color = from_value(&v).unwrap();
assert_eq!(result, Color::Red);
}
#[test]
fn value_mapping_deserialize() {
let yaml = "a: 1\nb: 2\n";
let m: Mapping = from_str(yaml).unwrap();
assert_eq!(m.len(), 2);
}
#[test]
fn value_mapping_any_deserialize() {
use noyalib::MappingAny;
let yaml = "1: a\n2: b\n";
let m: MappingAny = from_str(yaml).unwrap();
assert_eq!(m.len(), 2);
}
#[test]
fn value_tagged_value_deserialize() {
let yaml = "!tag value";
let v: Value = from_str(yaml).unwrap();
if let Value::Tagged(t) = v {
let parts = t.into_parts();
assert_eq!(parts.0.as_str(), "!tag");
}
}
#[test]
fn spanned_unknown_field_skipped() {
use noyalib::Spanned;
let yaml = "42";
let v: Spanned<i32> = from_str(yaml).unwrap();
assert_eq!(v.value, 42);
}
#[test]
fn singleton_map_recursive_tagged_value_transforms() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Status {
Active,
Inactive,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Container {
#[serde(with = "noyalib::with::singleton_map_recursive")]
status: Status,
}
let c = Container {
status: Status::Active,
};
let yaml = to_string(&c).unwrap();
let parsed: Container = from_str(&yaml).unwrap();
assert_eq!(parsed, c);
}
#[test]
fn singleton_map_recursive_nested_sequence() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Item {
A,
B { x: i32 },
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Wrapper {
#[serde(with = "noyalib::with::singleton_map_recursive")]
items: Vec<Item>,
}
let w = Wrapper {
items: vec![Item::A, Item::B { x: 1 }],
};
let yaml = to_string(&w).unwrap();
let parsed: Wrapper = from_str(&yaml).unwrap();
assert_eq!(parsed, w);
}
#[test]
fn singleton_map_with_transform_key_functions() {
use noyalib::with::singleton_map_with;
assert_eq!(singleton_map_with::to_lowercase("Hello"), "hello");
assert_eq!(singleton_map_with::to_uppercase("Hello"), "HELLO");
assert_eq!(
singleton_map_with::to_snake_case("HelloWorld"),
"hello_world"
);
assert_eq!(
singleton_map_with::to_pascal_case("hello_world"),
"HelloWorld"
);
assert_eq!(
singleton_map_with::to_kebab_case("GetRequest"),
"get-request"
);
assert_eq!(
singleton_map_with::from_kebab_case("get-request"),
"GetRequest"
);
}
#[test]
fn singleton_map_with_tagged_value_in_nested_mapping() {
use noyalib::{Tag, TaggedValue};
let tagged = Value::Tagged(Box::new(TaggedValue::new(
Tag::new("!custom"),
Value::Mapping({
let mut m = Mapping::new();
let _ = m.insert("KEY".to_string(), Value::from(1));
m
}),
)));
let yaml = to_string(&tagged).unwrap();
assert!(yaml.contains("!custom"));
}
#[test]
fn multi_document_parsing() {
let yaml = "---\na: 1\n---\nb: 2\n";
let docs: Vec<Value> = noyalib::load_all_as(yaml).unwrap();
assert_eq!(docs.len(), 2);
}
#[test]
fn multi_document_with_document_end() {
let yaml = "---\na: 1\n...\n---\nb: 2\n...\n";
let docs: Vec<Value> = noyalib::load_all_as(yaml).unwrap();
assert_eq!(docs.len(), 2);
}
#[test]
fn yaml_inf_and_nan_parsing() {
let yaml = "inf: .inf\nninf: -.inf\nnan: .nan\n";
let v: Value = from_str(yaml).unwrap();
if let Value::Number(n) = &v["inf"] {
assert!(n.as_f64().is_infinite());
}
if let Value::Number(n) = &v["nan"] {
assert!(n.as_f64().is_nan());
}
}
#[test]
fn yaml_octal_and_hex_integers() {
let yaml = "hex: 0xFF\noctal: 0o77\n";
let v: Value = from_str(yaml).unwrap();
assert_eq!(v["hex"], Value::from(255));
assert_eq!(v["octal"], Value::from(63));
}
#[test]
fn error_format_with_source_shows_location() {
let yaml = "key: [invalid\n";
let err = from_str::<Value>(yaml).unwrap_err();
let display = err.format_with_source(yaml);
assert!(!display.is_empty());
}
#[test]
fn roundtrip_complex_document() {
let yaml = r#"
string: hello
int: 42
float: 3.14
bool: true
null_val: ~
sequence:
- 1
- 2
mapping:
nested: value
"#;
let v: Value = from_str(yaml).unwrap();
let output = to_string(&v).unwrap();
let v2: Value = from_str(&output).unwrap();
assert_eq!(v, v2);
}