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.testemitter;
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.stream;
19 import dyaml.dumper;
20 import dyaml.event;
21 import dyaml.testcommon;
22 import dyaml.token;
23 
24 
25 /// Determine if events in events1 are equivalent to events in events2.
26 ///
27 /// Params:  events1 = First event array to compare.
28 ///          events2 = Second event array to compare.
29 ///
30 /// Returns: true if the events are equivalent, false otherwise.
31 bool compareEvents(Event[] events1, Event[] events2)
32 {
33     if(events1.length != events2.length){return false;}
34 
35     for(uint e = 0; e < events1.length; ++e)
36     {
37         auto e1 = events1[e];
38         auto e2 = events2[e];
39 
40         //Different event types.
41         if(e1.id != e2.id){return false;}
42         //Different anchor (if applicable).
43         if([EventID.SequenceStart,
44             EventID.MappingStart,
45             EventID.Alias,
46             EventID.Scalar].canFind(e1.id)
47             && e1.anchor != e2.anchor)
48         {
49             return false;
50         }
51         //Different collection tag (if applicable).
52         if([EventID.SequenceStart, EventID.MappingStart].canFind(e1.id) && e1.tag != e2.tag)
53         {
54             return false;
55         }
56         if(e1.id == EventID.Scalar)
57         {
58             //Different scalar tag (if applicable).
59             if(![e1.implicit, e1.implicit_2, e2.implicit, e2.implicit_2].canFind(true)
60                && e1.tag != e2.tag)
61             {
62                 return false;
63             }
64             //Different scalar value.
65             if(e1.value != e2.value)
66             {
67                 return false;
68             }
69         }
70     }
71     return true;
72 }
73 
74 /// Test emitter by getting events from parsing a file, emitting them, parsing
75 /// the emitted result and comparing events from parsing the emitted result with
76 /// originally parsed events.
77 ///
78 /// Params:  verbose           = Print verbose output?
79 ///          dataFilename      = YAML file to parse.
80 ///          canonicalFilename = Canonical YAML file used as dummy to determine
81 ///                              which data files to load.
82 void testEmitterOnData(bool verbose, string dataFilename, string canonicalFilename)
83 {
84     //Must exist due to Anchor, Tags reference counts.
85     auto loader = Loader(dataFilename);
86     auto events = cast(Event[])loader.parse();
87     auto emitStream = new YMemoryStream;
88     Dumper(emitStream).emit(events);
89 
90     if(verbose)
91     {
92         writeln(dataFilename);
93         writeln("ORIGINAL:\n", readText(dataFilename));
94         writeln("OUTPUT:\n", cast(string)emitStream.data);
95     }
96 
97     auto loader2        = Loader(emitStream.data.dup);
98     loader2.name        = "TEST";
99     loader2.constructor = new Constructor;
100     loader2.resolver    = new Resolver;
101     auto newEvents = cast(Event[])loader2.parse();
102     assert(compareEvents(events, newEvents));
103 }
104 
105 /// Test emitter by getting events from parsing a canonical YAML file, emitting
106 /// them both in canonical and normal format, parsing the emitted results and
107 /// comparing events from parsing the emitted result with originally parsed events.
108 ///
109 /// Params:  verbose           = Print verbose output?
110 ///          canonicalFilename = Canonical YAML file to parse.
111 void testEmitterOnCanonical(bool verbose, string canonicalFilename)
112 {
113     //Must exist due to Anchor, Tags reference counts.
114     auto loader = Loader(canonicalFilename);
115     auto events = cast(Event[])loader.parse();
116     foreach(canonical; [false, true])
117     {
118         auto emitStream = new YMemoryStream;
119         auto dumper = Dumper(emitStream);
120         dumper.canonical = canonical;
121         dumper.emit(events);
122         if(verbose)
123         {
124             writeln("OUTPUT (canonical=", canonical, "):\n",
125                     cast(string)emitStream.data);
126         }
127         auto loader2        = Loader(emitStream.data.dup);
128         loader2.name        = "TEST";
129         loader2.constructor = new Constructor;
130         loader2.resolver    = new Resolver;
131         auto newEvents = cast(Event[])loader2.parse();
132         assert(compareEvents(events, newEvents));
133     }
134 }
135 
136 /// Test emitter by getting events from parsing a file, emitting them with all
137 /// possible scalar and collection styles, parsing the emitted results and
138 /// comparing events from parsing the emitted result with originally parsed events.
139 ///
140 /// Params:  verbose           = Print verbose output?
141 ///          dataFilename      = YAML file to parse.
142 ///          canonicalFilename = Canonical YAML file used as dummy to determine
143 ///                              which data files to load.
144 void testEmitterStyles(bool verbose, string dataFilename, string canonicalFilename)
145 {
146     foreach(filename; [dataFilename, canonicalFilename])
147     {
148         //must exist due to Anchor, Tags reference counts
149         auto loader = Loader(canonicalFilename);
150         auto events = cast(Event[])loader.parse();
151         foreach(flowStyle; [CollectionStyle.Block, CollectionStyle.Flow])
152         {
153             foreach(style; [ScalarStyle.Literal, ScalarStyle.Folded,
154                             ScalarStyle.DoubleQuoted, ScalarStyle.SingleQuoted,
155                             ScalarStyle.Plain])
156             {
157                 Event[] styledEvents;
158                 foreach(event; events)
159                 {
160                     if(event.id == EventID.Scalar)
161                     {
162                         event = scalarEvent(Mark(), Mark(), event.anchor, event.tag,
163                                             tuple(event.implicit, event.implicit_2),
164                                             event.value, style);
165                     }
166                     else if(event.id == EventID.SequenceStart)
167                     {
168                         event = sequenceStartEvent(Mark(), Mark(), event.anchor,
169                                                    event.tag, event.implicit, flowStyle);
170                     }
171                     else if(event.id == EventID.MappingStart)
172                     {
173                         event = mappingStartEvent(Mark(), Mark(), event.anchor,
174                                                   event.tag, event.implicit, flowStyle);
175                     }
176                     styledEvents ~= event;
177                 }
178                 auto emitStream = new YMemoryStream;
179                 Dumper(emitStream).emit(styledEvents);
180                 if(verbose)
181                 {
182                     writeln("OUTPUT (", filename, ", ", to!string(flowStyle), ", ",
183                             to!string(style), ")");
184                     writeln(emitStream.data);
185                 }
186                 auto loader2        = Loader(emitStream.data.dup);
187                 loader2.name        = "TEST";
188                 loader2.constructor = new Constructor;
189                 loader2.resolver    = new Resolver;
190                 auto newEvents = cast(Event[])loader2.parse();
191                 assert(compareEvents(events, newEvents));
192             }
193         }
194     }
195 }
196 
197 unittest
198 {
199     writeln("D:YAML Emitter unittest");
200     run("testEmitterOnData",      &testEmitterOnData,      ["data", "canonical"]);
201     run("testEmitterOnCanonical", &testEmitterOnCanonical, ["canonical"]);
202     run("testEmitterStyles",      &testEmitterStyles,      ["data", "canonical"]);
203 }
204 
205 } // version(unittest)