1 
2 //          Copyright Ferdinand Majerech 2011-2014.
3 // Distributed under the Boost Software License, Version 1.0.
4 //    (See accompanying file LICENSE_1_0.txt or copy at
5 //          http://www.boost.org/LICENSE_1_0.txt)
6 
7 module dyaml.test.emitter;
8 
9 
10 version(unittest)
11 {
12 
13 import std.algorithm;
14 import std.file;
15 import std.range;
16 import std.typecons;
17 
18 import dyaml.emitter;
19 import dyaml.event;
20 import dyaml.test.common;
21 import dyaml.token;
22 
23 // Try to emit an event range.
24 void emitTestCommon(T)(ref Appender!string emitStream, T events, bool canonical = false) @safe
25     if (isInputRange!T && is(ElementType!T == Event))
26 {
27     auto emitter = Emitter!(typeof(emitStream), char)(emitStream, canonical, 2, 80, LineBreak.unix);
28     foreach(ref event; events)
29     {
30         emitter.emit(event);
31     }
32 }
33 
34 /// Determine if events in events1 are equivalent to events in events2.
35 ///
36 /// Params:  events1 = First event array to compare.
37 ///          events2 = Second event array to compare.
38 ///
39 /// Returns: true if the events are equivalent, false otherwise.
40 bool compareEvents(T, U)(T events1, U events2)
41     if (isInputRange!T && isInputRange!U && is(ElementType!T == Event) && is(ElementType!U == Event))
42 {
43     foreach (e1, e2; zip(events1, events2))
44     {
45         //Different event types.
46         if(e1.id != e2.id){return false;}
47         //Different anchor (if applicable).
48         if(e1.id.among!(EventID.sequenceStart,
49             EventID.mappingStart,
50             EventID.alias_,
51             EventID.scalar)
52             && e1.anchor != e2.anchor)
53         {
54             return false;
55         }
56         //Different collection tag (if applicable).
57         if(e1.id.among!(EventID.sequenceStart, EventID.mappingStart) && e1.tag != e2.tag)
58         {
59             return false;
60         }
61         if(e1.id == EventID.scalar)
62         {
63             //Different scalar tag (if applicable).
64             if(!(e1.implicit || e2.implicit)
65                && e1.tag != e2.tag)
66             {
67                 return false;
68             }
69             //Different scalar value.
70             if(e1.value != e2.value)
71             {
72                 return false;
73             }
74         }
75     }
76     return true;
77 }
78 
79 /// Test emitter by getting events from parsing a file, emitting them, parsing
80 /// the emitted result and comparing events from parsing the emitted result with
81 /// originally parsed events.
82 ///
83 /// Params:  dataFilename      = YAML file to parse.
84 ///          canonicalFilename = Canonical YAML file used as dummy to determine
85 ///                              which data files to load.
86 void testEmitterOnData(string dataFilename, string canonicalFilename) @safe
87 {
88     //Must exist due to Anchor, Tags reference counts.
89     auto loader = Loader.fromFile(dataFilename);
90     auto events = loader.parse();
91     auto emitStream = Appender!string();
92     emitTestCommon(emitStream, events);
93 
94     static if(verbose)
95     {
96         writeln(dataFilename);
97         writeln("ORIGINAL:\n", readText(dataFilename));
98         writeln("OUTPUT:\n", emitStream.data);
99     }
100 
101     auto loader2        = Loader.fromString(emitStream.data);
102     loader2.name        = "TEST";
103     auto newEvents = loader2.parse();
104     assert(compareEvents(events, newEvents));
105 }
106 
107 /// Test emitter by getting events from parsing a canonical YAML file, emitting
108 /// them both in canonical and normal format, parsing the emitted results and
109 /// comparing events from parsing the emitted result with originally parsed events.
110 ///
111 /// Params:  canonicalFilename = Canonical YAML file to parse.
112 void testEmitterOnCanonical(string canonicalFilename) @safe
113 {
114     //Must exist due to Anchor, Tags reference counts.
115     auto loader = Loader.fromFile(canonicalFilename);
116     auto events = loader.parse();
117     foreach(canonical; [false, true])
118     {
119         auto emitStream = Appender!string();
120         emitTestCommon(emitStream, events, canonical);
121         static if(verbose)
122         {
123             writeln("OUTPUT (canonical=", canonical, "):\n",
124                     emitStream.data);
125         }
126         auto loader2        = Loader.fromString(emitStream.data);
127         loader2.name        = "TEST";
128         auto newEvents = loader2.parse();
129         assert(compareEvents(events, newEvents));
130     }
131 }
132 
133 /// Test emitter by getting events from parsing a file, emitting them with all
134 /// possible scalar and collection styles, parsing the emitted results and
135 /// comparing events from parsing the emitted result with originally parsed events.
136 ///
137 /// Params:  dataFilename      = YAML file to parse.
138 ///          canonicalFilename = Canonical YAML file used as dummy to determine
139 ///                              which data files to load.
140 void testEmitterStyles(string dataFilename, string canonicalFilename) @safe
141 {
142     foreach(filename; [dataFilename, canonicalFilename])
143     {
144         //must exist due to Anchor, Tags reference counts
145         auto loader = Loader.fromFile(canonicalFilename);
146         auto events = loader.parse();
147         foreach(flowStyle; [CollectionStyle.block, CollectionStyle.flow])
148         {
149             foreach(style; [ScalarStyle.literal, ScalarStyle.folded,
150                             ScalarStyle.doubleQuoted, ScalarStyle.singleQuoted,
151                             ScalarStyle.plain])
152             {
153                 Event[] styledEvents;
154                 foreach(event; events)
155                 {
156                     if(event.id == EventID.scalar)
157                     {
158                         event = scalarEvent(Mark(), Mark(), event.anchor, event.tag,
159                                             event.implicit,
160                                             event.value, style);
161                     }
162                     else if(event.id == EventID.sequenceStart)
163                     {
164                         event = sequenceStartEvent(Mark(), Mark(), event.anchor,
165                                                    event.tag, event.implicit, flowStyle);
166                     }
167                     else if(event.id == EventID.mappingStart)
168                     {
169                         event = mappingStartEvent(Mark(), Mark(), event.anchor,
170                                                   event.tag, event.implicit, flowStyle);
171                     }
172                     styledEvents ~= event;
173                 }
174                 auto emitStream = Appender!string();
175                 emitTestCommon(emitStream, styledEvents);
176                 static if(verbose)
177                 {
178                     writeln("OUTPUT (", filename, ", ", to!string(flowStyle), ", ",
179                             to!string(style), ")");
180                     writeln(emitStream.data);
181                 }
182                 auto loader2        = Loader.fromString(emitStream.data);
183                 loader2.name        = "TEST";
184                 auto newEvents = loader2.parse();
185                 assert(compareEvents(events, newEvents));
186             }
187         }
188     }
189 }
190 
191 @safe unittest
192 {
193     printProgress("D:YAML Emitter unittest");
194     run("testEmitterOnData",      &testEmitterOnData,      ["data", "canonical"]);
195     run("testEmitterOnCanonical", &testEmitterOnCanonical, ["canonical"]);
196     run("testEmitterStyles",      &testEmitterStyles,      ["data", "canonical"]);
197 }
198 
199 } // version(unittest)