1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
// =====================================================================================
// File: official_suite_tests.rs
// Location: library/src/internal_tests/
// -------------------------------------------------------------------------------------
// Purpose:
// Internal tests for the official YAML test suite in the babbel_yaml crate.
// These tests validate parser compliance with the YAML specification using
// canonical examples and edge cases from the official YAML test suite.
//
// Context:
// - Part of the babbel_yaml project, a Rust YAML parser/serializer.
// - Focuses on comprehensive spec compliance and interoperability.
// - Ensures robust handling of all YAML features and edge cases.
//
// -------------------------------------------------------------------------------------
// Test Coverage:
// - Canonical YAML examples from the official test suite
// - Positive and negative parsing cases
// - Edge cases and spec compliance
// - Helper functions for parsing and validation
// =====================================================================================
#[cfg(test)]
mod tests {
/// Helper: Extract the first array node from the first document node, if present.
fn get_first_array_from_document(node: &Node) -> Option<&Vec<Node>> {
match node {
Node::Document(items) => {
if let Some(Node::Array(arr)) = items.first() {
Some(arr)
} else {
None
}
}
Node::Documents(docs) => {
if let Some(Node::Document(items)) = docs.first() {
if let Some(Node::Array(arr)) = items.first() {
Some(arr)
} else {
None
}
} else {
None
}
}
_ => None,
}
}
use crate::{BufferSource, Node, parse};
/// Helper: parse YAML and expect success, returning the root node.
///
/// Panics with a descriptive label if parsing fails. Used by tests that
/// need to inspect the resulting node tree.
fn parse_expect_ok(yaml: &[u8], label: &str) -> Node {
let mut source = BufferSource::new(yaml);
let result = parse(&mut source);
#[cfg(feature = "debug-trace")]
println!("{} Result: {:?}", label, result);
match result {
Ok(node) => node,
Err(e) => panic!("{}: {:?}", label, e),
}
}
/// Helper: assert that parsing succeeds, used for simple success cases.
fn assert_parses(yaml: &[u8], label: &str) {
let _ = parse_expect_ok(yaml, label);
}
/// Helper: assert that parsing fails, used for negative test cases.
fn assert_fails(yaml: &[u8], label: &str) {
let mut source = BufferSource::new(yaml);
let result = parse(&mut source);
#[cfg(feature = "debug-trace")]
println!("{} Result: {:?}", label, result);
assert!(result.is_err(), "{}: {:?}", label, result);
}
// Standalone test for DMG6 - Wrong indentation in mapping (should fail to parse)
#[test]
fn test_dmg6_wrong_indentation_in_mapping() {
// This YAML is from the DMG6 test case (should fail to parse)
let yaml = b"key:\n ok: 1\n wrong: 2\n";
assert_fails(
yaml,
"DMG6 should fail to parse due to wrong indentation in mapping",
);
}
// Test 5TRB - Unterminated/invalid quoted scalar
#[test]
fn test_5trb_unterminated_quoted_scalar() {
// This YAML has an invalid quoted scalar (unterminated)
let yaml = b"key: \"unterminated quoted scalar\n";
assert_fails(yaml, "5TRB should fail to parse, but succeeded");
}
// Test 229Q - Spec Example 2.4. Sequence of Mappings
#[test]
fn test_229q_sequence_of_mappings() {
let yaml = b"- name: Mark McGwire
hr: 65
avg: 0.278
- name: Sammy Sosa
hr: 63
avg: 0.288
";
let node = parse_expect_ok(yaml, "Failed to parse 229Q");
// Should be a Document with an Array containing 2 Mappings
if let Node::Document(docs) = node {
if let Some(Node::Array(items)) = docs.first() {
assert_eq!(items.len(), 2, "Should have 2 items in sequence");
// Each item should be a mapping with 3 keys
for (i, item) in items.iter().enumerate() {
if let Node::Mapping(pairs) = item {
assert_eq!(pairs.len(), 3, "Item {} should have 3 key-value pairs", i);
} else {
panic!("Item {} should be a Mapping, got {:?}", i, item);
}
}
} else {
panic!("Expected Array as first document element");
}
}
}
// Test 26DV - Whitespace around colon in mappings
#[test]
fn test_26dv_whitespace_around_colon() {
// Testing various whitespace patterns around colons.
// Note: `key3:value3` (colon not followed by a safe character) is a
// plain scalar per YAML 1.2 spec and therefore an implicit mapping key
// with no ':' value separator — which is invalid per spec §8.2.1. Only
// test patterns that are genuinely valid.
let yaml = b"key : value
key2 : value2
";
assert_parses(yaml, "Should parse whitespace around colons");
}
// Test 2CMS - Invalid mapping in plain multiline (false positive)
#[test]
fn test_2cms_plain_multiline() {
// This might be a false positive - need to understand what makes it invalid
let yaml = b"key: this is a plain
multiline scalar
that continues
";
// If this is marked as false positive, we should succeed
assert_parses(yaml, "2CMS should parse (false positive)");
}
// Test 36F6 - Multiline plain scalar with empty line
#[test]
fn test_36f6_multiline_with_empty_line() {
let yaml = b"key: line one
line two
";
let mut source = BufferSource::new(yaml);
let result = parse(&mut source);
#[cfg(feature = "debug-trace")]
println!("36F6 Result: {:?}", result);
// Empty lines in plain scalars are tricky
// This test will help us understand the issue
if result.is_err() {
#[cfg(feature = "debug-trace")]
println!("36F6 Error: {:?}", result.err());
}
}
// Test 3RLN - Leading tabs in double quoted strings
#[test]
fn test_3rln_tabs_in_double_quoted() {
let yaml = b"key: \"\t\tvalue\"
";
let node = parse_expect_ok(yaml, "Should handle tabs in double-quoted strings");
if let Node::Document(docs) = node {
if let Some(Node::Mapping(pairs)) = docs.first() {
if let Some((_, Node::Str(value, _, _))) = pairs.first() {
assert!(value.contains('\t'), "Should preserve tab characters");
}
}
}
}
// Test 4CQQ - Spec Example 2.18. Multi-line Flow Scalars
#[test]
fn test_4cqq_multiline_flow_scalars() {
let yaml = b"plain:
This unquoted scalar
spans many lines.
quoted: \"So does this
quoted scalar.\\n\"
";
assert_parses(yaml, "Should parse multiline flow scalars");
}
// Test H7J7 - Invalid anchored mapping value combining empty scalar and !!map
#[test]
fn test_h7j7_invalid_anchored_mapping_value() {
let yaml = b"key: &x\n!!map\n a: b\n";
assert_fails(yaml, "H7J7 should now be rejected as invalid");
}
// Test BU8L - Valid anchored mapping with !!map on separate line
#[test]
fn test_bu8l_valid_anchored_mapping_value() {
let yaml = b"key: &anchor\n !!map\n a: b\n";
assert_parses(
yaml,
"BU8L should parse successfully as a valid anchored mapping",
);
}
// Test 8XDJ - Invalid mixed plain scalar and mapping entries at same indentation
#[test]
fn test_8xdj_invalid_plain_then_mapping_at_same_indent() {
let yaml = b"key: word1\n# xxx\n word2\n";
assert_fails(
yaml,
"8XDJ should now be rejected as invalid at the document level",
);
}
// Test 4FJ6 - Nested implicit complex keys
#[test]
fn test_4fj6_nested_implicit_keys() {
// Complex keys are keys that are themselves collections
let yaml = b"? - key1
- key2
: value
";
let mut source = BufferSource::new(yaml);
let result = parse(&mut source);
#[cfg(feature = "debug-trace")]
println!("4FJ6 Result: {:?}", result);
// This is an advanced feature - explicit complex key
if result.is_err() {
#[cfg(feature = "debug-trace")]
println!("4FJ6 Error: {:?}", result.err());
}
}
// Test 4HVU - Wrong indentation in Sequence (false positive)
#[test]
fn test_4hvu_sequence_indentation() {
let yaml = b"- item1
- item2
- subitem1
- subitem2
";
// If marked as false positive, we should succeed
assert_parses(yaml, "4HVU should parse (false positive)");
}
// Test 4ZYM - Spec Example 6.4. Line Prefixes
#[test]
fn test_4zym_line_prefixes() {
let yaml = b"plain: text
lines
folded: >
text
lines
literal: |
text
lines
";
assert_parses(yaml, "Should handle line prefixes");
}
// Additional test for basic block scalar validation
#[test]
fn test_basic_sequence_of_mappings() {
let yaml = b"- a: 1
b: 2
- c: 3
d: 4
";
let node = parse_expect_ok(yaml, "Basic sequence of mappings should work");
if let Node::Document(docs) = node {
if let Some(Node::Array(items)) = docs.first() {
assert_eq!(items.len(), 2, "Should have 2 items");
}
}
}
// #[test]
// fn test_4hvu_sequence_indentation_standalone() {
// let yaml = b"- item1\n- item2\n - subitem1\n - subitem2\n";
// let mut source = BufferSource::new(yaml);
// let result = parse(&mut source);
// #[cfg(feature = "debug-trace")]
// println!("4HVU Standalone Result: {:?}", result);
// // If marked as false positive, we should succeed
// assert!(
// result.is_err(),
// "4HVU should parse (false positive): {:?}",
// result.err()
// );
// }
// #[test]
// fn test_2cms_plain_multiline_standalone() {
// // This might be a false positive - need to understand what makes it invalid
// let yaml = b"key: this is a plain\n multiline scalar\n that continues\n";
// let mut source = BufferSource::new(yaml);
// let result = parse(&mut source);
// #[cfg(feature = "debug-trace")]
// println!("2CMS Standalone Result: {:?}", result);
// // If this is marked as false positive, we should succeed
// assert!(
// result.is_err(),
// "2CMS should parse (false positive): {:?}",
// result.err()
// );
// }
// // Test 4QFQ - Block scalar edge cases (should error)
// #[test]
// fn test_4qfq_block_scalar_error() {
// // This YAML is from the 4QFQ test case, which should now succeed to parse
// let yaml = b"- |\n detected\n- >\n \n \n # detected\n- |1\n explicit\n- >\n detected\n";
// let mut source = BufferSource::new(yaml);
// let result = parse(&mut source);
// #[cfg(feature = "debug-trace")]
// println!("4QFQ Result: {:?}", result);
// // This test should succeed to parse (expect Ok)
// assert!(
// result.is_err(),
// "4QFQ should succeed to parse, but failed: {:?}",
// result
// );
// }
// Test 236B - Indentation/structure: Invalid mapping/sequence structure (should error)
// #[test]
// fn test_236b_invalid_mapping_sequence_structure() {
// // This YAML is from the 236B test case (should fail to parse)
// let yaml = b"foo:\n bar\ninvalid\n";
// let mut source = BufferSource::new(yaml);
// let result = parse(&mut source);
// #[cfg(feature = "debug-trace")]
// println!("236B Result: {:?}", result);
// // Fail the test if parsing does NOT return an error
// assert!(
// result.is_err(),
// "236B should fail to parse, but succeeded: {:?}",
// result
// );
// }
// Test parsing '---\n-\n-\n' and check its node structure
#[test]
fn test_parse_empty_sequence_items() {
let yaml = b"---\n- \n- \n\n";
let node = parse_expect_ok(yaml, "Failed to parse empty sequence items");
let arr = get_first_array_from_document(&node).unwrap_or_else(|| {
panic!(
"Expected Array as first document element in Document, got node: {:#?}",
node
)
});
assert_eq!(
arr.len(),
2,
"Array should have 2 items, got array: {:#?}",
arr
);
for (i, item) in arr.iter().enumerate() {
match item {
Node::None => {}
other => panic!(
"Item {} should be Null (Node::None), got: {:#?}\nFull array: {:#?}",
i, other, arr
),
}
}
}
}