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)