master
  1const std = @import("std");
  2const mem = std.mem;
  3const testing = std.testing;
  4
  5const yaml_mod = @import("../yaml.zig");
  6const Yaml = yaml_mod.Yaml;
  7
  8test "simple list" {
  9    const source =
 10        \\- a
 11        \\- b
 12        \\- c
 13    ;
 14
 15    var yaml = try Yaml.load(testing.allocator, source);
 16    defer yaml.deinit();
 17
 18    try testing.expectEqual(yaml.docs.items.len, 1);
 19
 20    const list = yaml.docs.items[0].list;
 21    try testing.expectEqual(list.len, 3);
 22
 23    try testing.expectEqualStrings("a", list[0].string);
 24    try testing.expectEqualStrings("b", list[1].string);
 25    try testing.expectEqualStrings("c", list[2].string);
 26}
 27
 28test "simple list typed as array of strings" {
 29    const source =
 30        \\- a
 31        \\- b
 32        \\- c
 33    ;
 34
 35    var yaml = try Yaml.load(testing.allocator, source);
 36    defer yaml.deinit();
 37
 38    try testing.expectEqual(yaml.docs.items.len, 1);
 39
 40    const arr = try yaml.parse([3][]const u8);
 41    try testing.expectEqual(3, arr.len);
 42    try testing.expectEqualStrings("a", arr[0]);
 43    try testing.expectEqualStrings("b", arr[1]);
 44    try testing.expectEqualStrings("c", arr[2]);
 45}
 46
 47test "simple list typed as array of ints" {
 48    const source =
 49        \\- 0
 50        \\- 1
 51        \\- 2
 52    ;
 53
 54    var yaml = try Yaml.load(testing.allocator, source);
 55    defer yaml.deinit();
 56
 57    try testing.expectEqual(yaml.docs.items.len, 1);
 58
 59    const arr = try yaml.parse([3]u8);
 60    try testing.expectEqualSlices(u8, &[_]u8{ 0, 1, 2 }, &arr);
 61}
 62
 63test "list of mixed sign integer" {
 64    const source =
 65        \\- 0
 66        \\- -1
 67        \\- 2
 68    ;
 69
 70    var yaml = try Yaml.load(testing.allocator, source);
 71    defer yaml.deinit();
 72
 73    try testing.expectEqual(yaml.docs.items.len, 1);
 74
 75    const arr = try yaml.parse([3]i8);
 76    try testing.expectEqualSlices(i8, &[_]i8{ 0, -1, 2 }, &arr);
 77}
 78
 79test "simple map untyped" {
 80    const source =
 81        \\a: 0
 82    ;
 83
 84    var yaml = try Yaml.load(testing.allocator, source);
 85    defer yaml.deinit();
 86
 87    try testing.expectEqual(yaml.docs.items.len, 1);
 88
 89    const map = yaml.docs.items[0].map;
 90    try testing.expect(map.contains("a"));
 91    try testing.expectEqual(@as(i64, 0), map.get("a").?.int);
 92}
 93
 94test "simple map untyped with a list of maps" {
 95    const source =
 96        \\a: 0
 97        \\b:
 98        \\  - foo: 1
 99        \\    bar: 2
100        \\  - foo: 3
101        \\    bar: 4
102        \\c: 1
103    ;
104
105    var yaml = try Yaml.load(testing.allocator, source);
106    defer yaml.deinit();
107
108    try testing.expectEqual(yaml.docs.items.len, 1);
109
110    const map = yaml.docs.items[0].map;
111    try testing.expect(map.contains("a"));
112    try testing.expect(map.contains("b"));
113    try testing.expect(map.contains("c"));
114    try testing.expectEqual(@as(i64, 0), map.get("a").?.int);
115    try testing.expectEqual(@as(i64, 1), map.get("c").?.int);
116    try testing.expectEqual(@as(i64, 1), map.get("b").?.list[0].map.get("foo").?.int);
117    try testing.expectEqual(@as(i64, 2), map.get("b").?.list[0].map.get("bar").?.int);
118    try testing.expectEqual(@as(i64, 3), map.get("b").?.list[1].map.get("foo").?.int);
119    try testing.expectEqual(@as(i64, 4), map.get("b").?.list[1].map.get("bar").?.int);
120}
121
122test "simple map untyped with a list of maps. no indent" {
123    const source =
124        \\b:
125        \\- foo: 1
126        \\c: 1
127    ;
128
129    var yaml = try Yaml.load(testing.allocator, source);
130    defer yaml.deinit();
131
132    try testing.expectEqual(yaml.docs.items.len, 1);
133
134    const map = yaml.docs.items[0].map;
135    try testing.expect(map.contains("b"));
136    try testing.expect(map.contains("c"));
137    try testing.expectEqual(@as(i64, 1), map.get("c").?.int);
138    try testing.expectEqual(@as(i64, 1), map.get("b").?.list[0].map.get("foo").?.int);
139}
140
141test "simple map untyped with a list of maps. no indent 2" {
142    const source =
143        \\a: 0
144        \\b:
145        \\- foo: 1
146        \\  bar: 2
147        \\- foo: 3
148        \\  bar: 4
149        \\c: 1
150    ;
151
152    var yaml = try Yaml.load(testing.allocator, source);
153    defer yaml.deinit();
154
155    try testing.expectEqual(yaml.docs.items.len, 1);
156
157    const map = yaml.docs.items[0].map;
158    try testing.expect(map.contains("a"));
159    try testing.expect(map.contains("b"));
160    try testing.expect(map.contains("c"));
161    try testing.expectEqual(@as(i64, 0), map.get("a").?.int);
162    try testing.expectEqual(@as(i64, 1), map.get("c").?.int);
163    try testing.expectEqual(@as(i64, 1), map.get("b").?.list[0].map.get("foo").?.int);
164    try testing.expectEqual(@as(i64, 2), map.get("b").?.list[0].map.get("bar").?.int);
165    try testing.expectEqual(@as(i64, 3), map.get("b").?.list[1].map.get("foo").?.int);
166    try testing.expectEqual(@as(i64, 4), map.get("b").?.list[1].map.get("bar").?.int);
167}
168
169test "simple map typed" {
170    const source =
171        \\a: 0
172        \\b: hello there
173        \\c: 'wait, what?'
174    ;
175
176    var yaml = try Yaml.load(testing.allocator, source);
177    defer yaml.deinit();
178
179    const simple = try yaml.parse(struct { a: usize, b: []const u8, c: []const u8 });
180    try testing.expectEqual(@as(usize, 0), simple.a);
181    try testing.expectEqualStrings("hello there", simple.b);
182    try testing.expectEqualStrings("wait, what?", simple.c);
183}
184
185test "typed nested structs" {
186    const source =
187        \\a:
188        \\  b: hello there
189        \\  c: 'wait, what?'
190    ;
191
192    var yaml = try Yaml.load(testing.allocator, source);
193    defer yaml.deinit();
194
195    const simple = try yaml.parse(struct {
196        a: struct {
197            b: []const u8,
198            c: []const u8,
199        },
200    });
201    try testing.expectEqualStrings("hello there", simple.a.b);
202    try testing.expectEqualStrings("wait, what?", simple.a.c);
203}
204
205test "single quoted string" {
206    const source =
207        \\- 'hello'
208        \\- 'here''s an escaped quote'
209        \\- 'newlines and tabs\nare not\tsupported'
210    ;
211
212    var yaml = try Yaml.load(testing.allocator, source);
213    defer yaml.deinit();
214
215    const arr = try yaml.parse([3][]const u8);
216    try testing.expectEqual(arr.len, 3);
217    try testing.expectEqualStrings("hello", arr[0]);
218    try testing.expectEqualStrings("here's an escaped quote", arr[1]);
219    try testing.expectEqualStrings("newlines and tabs\\nare not\\tsupported", arr[2]);
220}
221
222test "double quoted string" {
223    const source =
224        \\- "hello"
225        \\- "\"here\" are some escaped quotes"
226        \\- "newlines and tabs\nare\tsupported"
227        \\- "let's have
228        \\some fun!"
229    ;
230
231    var yaml = try Yaml.load(testing.allocator, source);
232    defer yaml.deinit();
233
234    const arr = try yaml.parse([4][]const u8);
235    try testing.expectEqual(arr.len, 4);
236    try testing.expectEqualStrings("hello", arr[0]);
237    try testing.expectEqualStrings(
238        \\"here" are some escaped quotes
239    , arr[1]);
240    try testing.expectEqualStrings("newlines and tabs\nare\tsupported", arr[2]);
241    try testing.expectEqualStrings(
242        \\let's have
243        \\some fun!
244    , arr[3]);
245}
246
247test "multidoc typed as a slice of structs" {
248    const source =
249        \\---
250        \\a: 0
251        \\---
252        \\a: 1
253        \\...
254    ;
255
256    var yaml = try Yaml.load(testing.allocator, source);
257    defer yaml.deinit();
258
259    {
260        const result = try yaml.parse([2]struct { a: usize });
261        try testing.expectEqual(result.len, 2);
262        try testing.expectEqual(result[0].a, 0);
263        try testing.expectEqual(result[1].a, 1);
264    }
265
266    {
267        const result = try yaml.parse([]struct { a: usize });
268        try testing.expectEqual(result.len, 2);
269        try testing.expectEqual(result[0].a, 0);
270        try testing.expectEqual(result[1].a, 1);
271    }
272}
273
274test "multidoc typed as a struct is an error" {
275    const source =
276        \\---
277        \\a: 0
278        \\---
279        \\b: 1
280        \\...
281    ;
282
283    var yaml = try Yaml.load(testing.allocator, source);
284    defer yaml.deinit();
285
286    try testing.expectError(Yaml.Error.TypeMismatch, yaml.parse(struct { a: usize }));
287    try testing.expectError(Yaml.Error.TypeMismatch, yaml.parse(struct { b: usize }));
288    try testing.expectError(Yaml.Error.TypeMismatch, yaml.parse(struct { a: usize, b: usize }));
289}
290
291test "multidoc typed as a slice of structs with optionals" {
292    const source =
293        \\---
294        \\a: 0
295        \\c: 1.0
296        \\---
297        \\a: 1
298        \\b: different field
299        \\...
300    ;
301
302    var yaml = try Yaml.load(testing.allocator, source);
303    defer yaml.deinit();
304
305    const result = try yaml.parse([]struct { a: usize, b: ?[]const u8, c: ?f16 });
306    try testing.expectEqual(result.len, 2);
307
308    try testing.expectEqual(result[0].a, 0);
309    try testing.expect(result[0].b == null);
310    try testing.expect(result[0].c != null);
311    try testing.expectEqual(result[0].c.?, 1.0);
312
313    try testing.expectEqual(result[1].a, 1);
314    try testing.expect(result[1].b != null);
315    try testing.expectEqualStrings("different field", result[1].b.?);
316    try testing.expect(result[1].c == null);
317}
318
319test "empty yaml can be represented as void" {
320    const source = "";
321    var yaml = try Yaml.load(testing.allocator, source);
322    defer yaml.deinit();
323    const result = try yaml.parse(void);
324    try testing.expect(@TypeOf(result) == void);
325}
326
327test "nonempty yaml cannot be represented as void" {
328    const source =
329        \\a: b
330    ;
331
332    var yaml = try Yaml.load(testing.allocator, source);
333    defer yaml.deinit();
334
335    try testing.expectError(Yaml.Error.TypeMismatch, yaml.parse(void));
336}
337
338test "typed array size mismatch" {
339    const source =
340        \\- 0
341        \\- 0
342    ;
343
344    var yaml = try Yaml.load(testing.allocator, source);
345    defer yaml.deinit();
346
347    try testing.expectError(Yaml.Error.ArraySizeMismatch, yaml.parse([1]usize));
348    try testing.expectError(Yaml.Error.ArraySizeMismatch, yaml.parse([5]usize));
349}
350
351test "comments" {
352    const source =
353        \\
354        \\key: # this is the key
355        \\# first value
356        \\
357        \\- val1
358        \\
359        \\# second value
360        \\- val2
361    ;
362
363    var yaml = try Yaml.load(testing.allocator, source);
364    defer yaml.deinit();
365
366    const simple = try yaml.parse(struct {
367        key: []const []const u8,
368    });
369    try testing.expect(simple.key.len == 2);
370    try testing.expectEqualStrings("val1", simple.key[0]);
371    try testing.expectEqualStrings("val2", simple.key[1]);
372}
373
374test "promote ints to floats in a list mixed numeric types" {
375    const source =
376        \\a_list: [0, 1.0]
377    ;
378
379    var yaml = try Yaml.load(testing.allocator, source);
380    defer yaml.deinit();
381
382    const simple = try yaml.parse(struct {
383        a_list: []const f64,
384    });
385    try testing.expectEqualSlices(f64, &[_]f64{ 0.0, 1.0 }, simple.a_list);
386}
387
388test "demoting floats to ints in a list is an error" {
389    const source =
390        \\a_list: [0, 1.0]
391    ;
392
393    var yaml = try Yaml.load(testing.allocator, source);
394    defer yaml.deinit();
395
396    try testing.expectError(error.TypeMismatch, yaml.parse(struct {
397        a_list: []const u64,
398    }));
399}
400
401test "duplicate map keys" {
402    const source =
403        \\a: b
404        \\a: c
405    ;
406    try testing.expectError(error.DuplicateMapKey, Yaml.load(testing.allocator, source));
407}
408
409fn testStringify(expected: []const u8, input: anytype) !void {
410    var output = std.array_list.Managed(u8).init(testing.allocator);
411    defer output.deinit();
412
413    try yaml_mod.stringify(testing.allocator, input, output.writer());
414    try testing.expectEqualStrings(expected, output.items);
415}
416
417test "stringify an int" {
418    try testStringify("128", @as(u32, 128));
419}
420
421test "stringify a simple struct" {
422    try testStringify(
423        \\a: 1
424        \\b: 2
425        \\c: 2.5
426    , struct { a: i64, b: f64, c: f64 }{ .a = 1, .b = 2.0, .c = 2.5 });
427}
428
429test "stringify a struct with an optional" {
430    try testStringify(
431        \\a: 1
432        \\b: 2
433        \\c: 2.5
434    , struct { a: i64, b: ?f64, c: f64 }{ .a = 1, .b = 2.0, .c = 2.5 });
435
436    try testStringify(
437        \\a: 1
438        \\c: 2.5
439    , struct { a: i64, b: ?f64, c: f64 }{ .a = 1, .b = null, .c = 2.5 });
440}
441
442test "stringify a struct with all optionals" {
443    try testStringify("", struct { a: ?i64, b: ?f64 }{ .a = null, .b = null });
444}
445
446test "stringify an optional" {
447    try testStringify("", null);
448    try testStringify("", @as(?u64, null));
449}
450
451test "stringify a union" {
452    const Dummy = union(enum) {
453        x: u64,
454        y: f64,
455    };
456    try testStringify("a: 1", struct { a: Dummy }{ .a = .{ .x = 1 } });
457    try testStringify("a: 2.1", struct { a: Dummy }{ .a = .{ .y = 2.1 } });
458}
459
460test "stringify a string" {
461    try testStringify("a: name", struct { a: []const u8 }{ .a = "name" });
462    try testStringify("name", "name");
463}
464
465test "stringify a list" {
466    try testStringify("[ 1, 2, 3 ]", @as([]const u64, &.{ 1, 2, 3 }));
467    try testStringify("[ 1, 2, 3 ]", .{ @as(i64, 1), 2, 3 });
468    try testStringify("[ 1, name, 3 ]", .{ 1, "name", 3 });
469
470    const arr: [3]i64 = .{ 1, 2, 3 };
471    try testStringify("[ 1, 2, 3 ]", arr);
472}