v / vlib / yaml / yaml_conformance_test.v
145 lines · 141 sloc · 4.54 KB · d96fe54c0a96fd6d651e9cb3c61b137ccc5c7dbe
Raw
1module yaml
2
3// Conformance-style coverage. Each case mirrors a pattern from the YAML 1.2
4// spec (or the public yaml-test-suite) that's within the subset V's parser
5// implements: plain mappings, sequences, scalars, flow style, JSON-superset
6// documents. Anchors, aliases, tags, merge keys, multi-document streams and
7// the chomp/indent indicators on block scalars are intentionally NOT covered:
8// V's parser does not support them today.
9//
10// Each case is `name | yaml_input | expected_json` and is verified by parsing
11// `yaml_input`, serializing to JSON, and comparing against the expected JSON
12// via `json_logically_eq` (see `test_helpers.v`).
13//
14// Scalar variants (booleans, nulls, numeric underscores), quoted-string
15// escapes, block scalars, `[]`/`{}`, and the empty document are exercised by
16// `yaml_edge_cases_test.v` with stricter, typed assertions; not duplicated here.
17
18struct ConformanceCase {
19 name string
20 src string
21 expected string
22}
23
24const cases = [
25 ConformanceCase{
26 name: 'single scalar mapping'
27 src: 'key: value'
28 expected: '{"key":"value"}'
29 },
30 ConformanceCase{
31 name: 'integer values'
32 src: 'a: 1\nb: -2\nc: 0\nd: 9223372036854775807'
33 expected: '{"a":1,"b":-2,"c":0,"d":9223372036854775807}'
34 },
35 ConformanceCase{
36 name: 'float values'
37 src: 'a: 1.5\nb: -0.001\nc: 1.0e10\nd: -1.5e-3'
38 expected: '{"a":1.5,"b":-0.001,"c":10000000000,"d":-0.0015}'
39 },
40 ConformanceCase{
41 name: 'simple sequence'
42 src: 'items:\n - one\n - two\n - three'
43 expected: '{"items":["one","two","three"]}'
44 },
45 ConformanceCase{
46 name: 'nested mapping under sequence item'
47 src: 'servers:\n - host: a\n port: 1\n - host: b\n port: 2'
48 expected: '{"servers":[{"host":"a","port":1},{"host":"b","port":2}]}'
49 },
50 ConformanceCase{
51 name: 'flow sequence inline'
52 src: 'tags: [web, api, db]'
53 expected: '{"tags":["web","api","db"]}'
54 },
55 ConformanceCase{
56 name: 'flow mapping inline'
57 src: 'config: {host: a, port: 8080}'
58 expected: '{"config":{"host":"a","port":8080}}'
59 },
60 ConformanceCase{
61 name: 'mixed flow and block'
62 src: 'top:\n inner:\n - {k: v, n: 1}\n - [a, b, c]'
63 expected: '{"top":{"inner":[{"k":"v","n":1},["a","b","c"]]}}'
64 },
65 ConformanceCase{
66 name: 'deeply nested mapping'
67 src: 'a:\n b:\n c:\n d:\n e: end'
68 expected: '{"a":{"b":{"c":{"d":{"e":"end"}}}}}'
69 },
70 ConformanceCase{
71 name: 'sequence of sequences'
72 src: 'matrix:\n - [1, 2, 3]\n - [4, 5, 6]\n - [7, 8, 9]'
73 expected: '{"matrix":[[1,2,3],[4,5,6],[7,8,9]]}'
74 },
75 ConformanceCase{
76 name: 'unicode in string values'
77 src: 'a: "café"\nb: "日本語"\nc: plain café'
78 expected: '{"a":"café","b":"日本語","c":"plain café"}'
79 },
80 ConformanceCase{
81 name: 'comment is stripped'
82 src: '# leading comment\nkey: value # trailing comment'
83 expected: '{"key":"value"}'
84 },
85 ConformanceCase{
86 name: 'document separator markers are tolerated'
87 src: '---\na: 1\n...'
88 expected: '{"a":1}'
89 },
90 ConformanceCase{
91 name: 'JSON-superset input (object)'
92 src: '{"a": 1, "b": [2, 3]}'
93 expected: '{"a":1,"b":[2,3]}'
94 },
95 ConformanceCase{
96 name: 'JSON-superset input (array root)'
97 src: '[1, 2, {"three": 3}]'
98 expected: '[1,2,{"three":3}]'
99 },
100 ConformanceCase{
101 name: 'sequence containing null'
102 src: 'a:\n - one\n - ~\n - three'
103 expected: '{"a":["one",null,"three"]}'
104 },
105 ConformanceCase{
106 name: 'mapping with quoted keys containing dots'
107 src: 'plain: 1\n"a.b": 2\n"c.d.e": 3'
108 expected: '{"plain":1,"a.b":2,"c.d.e":3}'
109 },
110 ConformanceCase{
111 name: 'mixed types in flow sequence'
112 src: 'mix: [1, "two", true, null, 3.14]'
113 expected: '{"mix":[1,"two",true,null,3.14]}'
114 },
115 ConformanceCase{
116 name: 'nested flow inside block sequence'
117 src: 'items:\n - {a: 1, b: [x, y]}\n - {a: 2, b: [z]}'
118 expected: '{"items":[{"a":1,"b":["x","y"]},{"a":2,"b":["z"]}]}'
119 },
120]!
121
122fn test_yaml_conformance_cases() ! {
123 mut failed := []string{}
124 for c in cases {
125 doc := parse_text(c.src) or {
126 failed << '${c.name}: parse error: ${err}'
127 continue
128 }
129 got := doc.to_json()
130 matched := json_logically_eq(got, c.expected) or {
131 failed << '${c.name}: invalid JSON produced: ${got} (vs expected ${c.expected}): ${err}'
132 continue
133 }
134 if !matched {
135 failed << '${c.name}: got ${got}, want ${c.expected}'
136 }
137 }
138 if failed.len > 0 {
139 eprintln('YAML conformance failures (${failed.len}/${cases.len}):')
140 for f in failed {
141 eprintln(' - ${f}')
142 }
143 assert false, '${failed.len} conformance case(s) failed'
144 }
145}
146