tests.cpp 13.3 KB
Newer Older
1
#include "tests.h"
Jesse Beder's avatar
Jesse Beder committed
2
3
#include "spectests.h"
#include "yaml.h"
4
5
6
7
8
9
10
#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>

namespace Test
{
11
	void RunAll()
12
13
	{
		bool passed = true;
14
15
		if(!RunParserTests())
			passed = false;
Jesse Beder's avatar
Jesse Beder committed
16
17
18
			
		if(!RunSpecTests())
			passed = false;
19
20
21
		
		if(!RunEmitterTests())
			passed = false;
22
23
24
25

		if(passed)
			std::cout << "All tests passed!\n";
	}
26
27
28
	
	////////////////////////////////////////////////////////////////////////////////////////
	// Parser tests
29

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
	namespace {
		void RunScalarParserTest(void (*test)(std::string&, std::string&), const std::string& name, bool& passed) {
			std::string error;
			std::string inputScalar, desiredOutput;
			std::string output;
			bool ok = true;
			try {
				test(inputScalar, desiredOutput);
				std::stringstream stream(inputScalar);
				YAML::Parser parser(stream);
				YAML::Node doc;
				parser.GetNextDocument(doc);
				doc >> output;
			} catch(const YAML::Exception& e) {
				ok = false;
				error = e.msg;
			}
			if(ok && output == desiredOutput) {
				std::cout << "Parser test passed: " << name << "\n";
			} else {
				passed = false;
				std::cout << "Parser test failed: " << name << "\n";
				if(error != "")
					std::cout << "Caught exception: " << error << "\n";
				else {
					std::cout << "Output:\n" << output << "<<<\n";
					std::cout << "Desired output:\n" << desiredOutput << "<<<\n";
				}
			}
		}
60

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
		void RunParserTest(bool (*test)(), const std::string& name, bool& passed) {
			std::string error;
			bool ok = true;
			try {
				ok = test();
			} catch(const YAML::Exception& e) {
				ok = false;
				error = e.msg;
			}
			if(ok) {
				std::cout << "Parser test passed: " << name << "\n";
			} else {
				passed = false;
				std::cout << "Parser test failed: " << name << "\n";
				if(error != "")
					std::cout << "Caught exception: " << error << "\n";
77
78
			}
		}
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

		typedef void (*EncodingFn)(std::ostream&, int);

		inline char Byte(int ch)
		{
			return static_cast<char>(static_cast<unsigned char>(static_cast<unsigned int>(ch)));
		}

		void EncodeToUtf8(std::ostream& stream, int ch)
		{
			if (ch <= 0x7F)
			{
				stream << Byte(ch);
			}
			else if (ch <= 0x7FF)
			{
				stream << Byte(0xC0 | (ch >> 6));
				stream << Byte(0x80 | (ch & 0x3F));
			}
			else if (ch <= 0xFFFF)
			{
				stream << Byte(0xE0 | (ch >> 12));
				stream << Byte(0x80 | ((ch >> 6) & 0x3F));
				stream << Byte(0x80 | (ch & 0x3F));
			}
			else if (ch <= 0x1FFFFF)
			{
				stream << Byte(0xF0 | (ch >> 18));
				stream << Byte(0x80 | ((ch >> 12) & 0x3F));
				stream << Byte(0x80 | ((ch >> 6) & 0x3F));
				stream << Byte(0x80 | (ch & 0x3F));
			}
		}

		bool SplitUtf16HighChar(std::ostream& stream, EncodingFn encoding, int ch)
		{
			int biasedValue = ch - 0x10000;
			if (biasedValue < 0)
			{
				return false;
			}
			int high = 0xD800 | (biasedValue >> 10);
			int low  = 0xDC00 | (biasedValue & 0x3FF);
			encoding(stream, high);
			encoding(stream, low);
			return true;
		}

		void EncodeToUtf16LE(std::ostream& stream, int ch)
		{
			if (!SplitUtf16HighChar(stream, &EncodeToUtf16LE, ch))
			{
				stream << Byte(ch & 0xFF) << Byte(ch >> 8);
			}
		}

		void EncodeToUtf16BE(std::ostream& stream, int ch)
		{
			if (!SplitUtf16HighChar(stream, &EncodeToUtf16BE, ch))
			{
				stream << Byte(ch >> 8) << Byte(ch & 0xFF);
			}
		}

		void EncodeToUtf32LE(std::ostream& stream, int ch)
		{
			stream << Byte(ch & 0xFF) << Byte((ch >> 8) & 0xFF) 
				<< Byte((ch >> 16) & 0xFF) << Byte((ch >> 24) & 0xFF);
		}

		void EncodeToUtf32BE(std::ostream& stream, int ch)
		{
			stream << Byte((ch >> 24) & 0xFF) << Byte((ch >> 16) & 0xFF)
				<< Byte((ch >> 8) & 0xFF) << Byte(ch & 0xFF);
		}

		class EncodingTester
		{
		public:
			EncodingTester(EncodingFn encoding, bool declareEncoding)
			{
				if (declareEncoding)
				{
					encoding(m_yaml, 0xFEFF);
				}

				AddEntry(encoding, 0x0021, 0x007E); // Basic Latin
				AddEntry(encoding, 0x00A1, 0x00FF); // Latin-1 Supplement
				AddEntry(encoding, 0x0660, 0x06FF); // Arabic (largest contiguous block)

				// CJK unified ideographs (multiple lines)
				AddEntry(encoding, 0x4E00, 0x4EFF);
				AddEntry(encoding, 0x4F00, 0x4FFF);
				AddEntry(encoding, 0x5000, 0x51FF); // 512 character line
				AddEntry(encoding, 0x5200, 0x54FF); // 768 character line
				AddEntry(encoding, 0x5500, 0x58FF); // 1024 character line

				AddEntry(encoding, 0x103A0, 0x103C3); // Old Persian

				m_yaml.seekg(0, std::ios::beg);
			}

			std::istream& stream() {return m_yaml;}
			const std::vector<std::string>& entries() {return m_entries;}

		private:
			std::stringstream m_yaml;
			std::vector<std::string> m_entries;

			void AddEntry(EncodingFn encoding, int startCh, int endCh)
			{
				encoding(m_yaml, '-');
				encoding(m_yaml, ' ');
				encoding(m_yaml, '|');
				encoding(m_yaml, '\n');
				encoding(m_yaml, ' ');
				encoding(m_yaml, ' ');

				std::stringstream entry;
				for (int ch = startCh; ch <= endCh; ++ch)
				{
					encoding(m_yaml, ch);
					EncodeToUtf8(entry, ch);
				}
				encoding(m_yaml, '\n');

				m_entries.push_back(entry.str());
			}
		};

		void RunEncodingTest(EncodingFn encoding, bool declareEncoding, const std::string& name, bool& passed)
		{
			EncodingTester tester(encoding, declareEncoding);
			std::string error;
			bool ok = true;
			try {
				YAML::Parser parser(tester.stream());
				YAML::Node doc;
				parser.GetNextDocument(doc);

				YAML::Iterator itNode = doc.begin();
				std::vector<std::string>::const_iterator itEntry = tester.entries().begin();
				for (; (itNode != doc.end()) && (itEntry != tester.entries().end()); ++itNode, ++itEntry)
				{
					std::string stScalarValue;
					if (!itNode->GetScalar(stScalarValue) && (stScalarValue == *itEntry))
					{
						break;
					}
				}

				if ((itNode != doc.end()) || (itEntry != tester.entries().end()))
				{
					ok = false;
				}
			} catch(const YAML::Exception& e) {
				ok = false;
				error = e.msg;
			}
			if(ok) {
				std::cout << "Parser test passed: " << name << "\n";
			} else {
				passed = false;
				std::cout << "Parser test failed: " << name << "\n";
				if(error != "")
					std::cout << "Caught exception: " << error << "\n";
			}
		}
247
	}
248

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
	bool RunParserTests()
	{
		bool passed = true;
		RunScalarParserTest(&Parser::SimpleScalar, "simple scalar", passed);
		RunScalarParserTest(&Parser::MultiLineScalar, "multi-line scalar", passed);
		RunScalarParserTest(&Parser::LiteralScalar, "literal scalar", passed);
		RunScalarParserTest(&Parser::FoldedScalar, "folded scalar", passed);
		RunScalarParserTest(&Parser::ChompedFoldedScalar, "chomped folded scalar", passed);
		RunScalarParserTest(&Parser::ChompedLiteralScalar, "chomped literal scalar", passed);
		RunScalarParserTest(&Parser::FoldedScalarWithIndent, "folded scalar with indent", passed);
		RunScalarParserTest(&Parser::ColonScalar, "colon scalar", passed);
		RunScalarParserTest(&Parser::QuotedScalar, "quoted scalar", passed);
		RunScalarParserTest(&Parser::CommaScalar, "comma scalar", passed);
		RunScalarParserTest(&Parser::DashScalar, "dash scalar", passed);
		RunScalarParserTest(&Parser::URLScalar, "url scalar", passed);

		RunParserTest(&Parser::SimpleSeq, "simple seq", passed);
		RunParserTest(&Parser::SimpleMap, "simple map", passed);
		RunParserTest(&Parser::FlowSeq, "flow seq", passed);
		RunParserTest(&Parser::FlowMap, "flow map", passed);
269
270
		RunParserTest(&Parser::FlowMapWithOmittedKey, "flow map with omitted key", passed);
		RunParserTest(&Parser::FlowMapWithOmittedValue, "flow map with omitted value", passed);
271
		RunParserTest(&Parser::QuotedSimpleKeys, "quoted simple keys", passed);
272
		RunParserTest(&Parser::CompressedMapAndSeq, "compressed map and seq", passed);
273
274
275
		RunParserTest(&Parser::NullBlockSeqEntry, "null block seq entry", passed);
		RunParserTest(&Parser::NullBlockMapKey, "null block map key", passed);
		RunParserTest(&Parser::NullBlockMapValue, "null block map value", passed);
276
277
		RunParserTest(&Parser::SimpleAlias, "simple alias", passed);
		RunParserTest(&Parser::AliasWithNull, "alias with null", passed);
278
279
		RunParserTest(&Parser::AnchorInSimpleKey, "anchor in simple key", passed);
		RunParserTest(&Parser::AliasAsSimpleKey, "alias as simple key", passed);
280
281
		RunParserTest(&Parser::ExplicitDoc, "explicit doc", passed);
		RunParserTest(&Parser::MultipleDocs, "multiple docs", passed);
282
283
		RunParserTest(&Parser::ExplicitEndDoc, "explicit end doc", passed);
		RunParserTest(&Parser::MultipleDocsWithSomeExplicitIndicators, "multiple docs with some explicit indicators", passed);
284
		
285
286
287
288
289
290
291
292
293
294
		RunEncodingTest(&EncodeToUtf8, false, "UTF-8, no BOM", passed);
		RunEncodingTest(&EncodeToUtf8, true, "UTF-8 with BOM", passed);
		RunEncodingTest(&EncodeToUtf16LE, false, "UTF-16LE, no BOM", passed);
		RunEncodingTest(&EncodeToUtf16LE, true, "UTF-16LE with BOM", passed);
		RunEncodingTest(&EncodeToUtf16BE, false, "UTF-16BE, no BOM", passed);
		RunEncodingTest(&EncodeToUtf16BE, true, "UTF-16BE with BOM", passed);
		RunEncodingTest(&EncodeToUtf32LE, false, "UTF-32LE, no BOM", passed);
		RunEncodingTest(&EncodeToUtf32LE, true, "UTF-32LE with BOM", passed);
		RunEncodingTest(&EncodeToUtf32BE, false, "UTF-32BE, no BOM", passed);
		RunEncodingTest(&EncodeToUtf32BE, true, "UTF-32BE with BOM", passed);
295
		return passed;
296
	}
297

298
299
300
301
	////////////////////////////////////////////////////////////////////////////////////////
	// Emitter tests
	
	namespace {
302
		void RunEmitterTest(void (*test)(YAML::Emitter&, std::string&), const std::string& name, bool& passed) {
303
304
305
			YAML::Emitter out;
			std::string desiredOutput;
			test(out, desiredOutput);
306
			std::string output = out.c_str();
307

308
			if(output == desiredOutput) {
309
				std::cout << "Emitter test passed: " << name << "\n";
310
311
			} else {
				passed = false;
312
				std::cout << "Emitter test failed: " << name << "\n";
313
				std::cout << "Output:\n";
314
				std::cout << output << "<<<\n";
315
316
317
318
319
				std::cout << "Desired output:\n";
				std::cout << desiredOutput << "<<<\n";				
			}
		}
		
320
		void RunEmitterErrorTest(void (*test)(YAML::Emitter&, std::string&), const std::string& name, bool& passed) {
321
322
323
324
325
			YAML::Emitter out;
			std::string desiredError;
			test(out, desiredError);
			std::string lastError = out.GetLastError();
			if(!out.good() && lastError == desiredError) {
326
				std::cout << "Emitter test passed: " << name << "\n";
327
328
			} else {
				passed = false;
329
				std::cout << "Emitter test failed: " << name << "\n";
330
331
332
333
334
335
336
337
338
339
340
341
				if(out.good())
					std::cout << "No error detected\n";
				else
					std::cout << "Detected error: " << lastError << "\n";
				std::cout << "Expected error: " << desiredError << "\n";
			}
		}
	}
	
	bool RunEmitterTests()
	{
		bool passed = true;
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
		RunEmitterTest(&Emitter::SimpleScalar, "simple scalar", passed);
		RunEmitterTest(&Emitter::SimpleSeq, "simple seq", passed);
		RunEmitterTest(&Emitter::SimpleFlowSeq, "simple flow seq", passed);
		RunEmitterTest(&Emitter::EmptyFlowSeq, "empty flow seq", passed);
		RunEmitterTest(&Emitter::NestedBlockSeq, "nested block seq", passed);
		RunEmitterTest(&Emitter::NestedFlowSeq, "nested flow seq", passed);
		RunEmitterTest(&Emitter::SimpleMap, "simple map", passed);
		RunEmitterTest(&Emitter::SimpleFlowMap, "simple flow map", passed);
		RunEmitterTest(&Emitter::MapAndList, "map and list", passed);
		RunEmitterTest(&Emitter::ListAndMap, "list and map", passed);
		RunEmitterTest(&Emitter::NestedBlockMap, "nested block map", passed);
		RunEmitterTest(&Emitter::NestedFlowMap, "nested flow map", passed);
		RunEmitterTest(&Emitter::MapListMix, "map list mix", passed);
		RunEmitterTest(&Emitter::SimpleLongKey, "simple long key", passed);
		RunEmitterTest(&Emitter::SingleLongKey, "single long key", passed);
		RunEmitterTest(&Emitter::ComplexLongKey, "complex long key", passed);
		RunEmitterTest(&Emitter::AutoLongKey, "auto long key", passed);
		RunEmitterTest(&Emitter::ScalarFormat, "scalar format", passed);
		RunEmitterTest(&Emitter::AutoLongKeyScalar, "auto long key scalar", passed);
		RunEmitterTest(&Emitter::LongKeyFlowMap, "long key flow map", passed);
		RunEmitterTest(&Emitter::BlockMapAsKey, "block map as key", passed);
		RunEmitterTest(&Emitter::AliasAndAnchor, "alias and anchor", passed);
364
		RunEmitterTest(&Emitter::AliasAndAnchorWithNull, "alias and anchor with null", passed);
365
366
367
368
369
370
371
372
		RunEmitterTest(&Emitter::ComplexDoc, "complex doc", passed);
		RunEmitterTest(&Emitter::STLContainers, "STL containers", passed);
		RunEmitterTest(&Emitter::SimpleComment, "simple comment", passed);
		RunEmitterTest(&Emitter::MultiLineComment, "multi-line comment", passed);
		RunEmitterTest(&Emitter::ComplexComments, "complex comments", passed);
		RunEmitterTest(&Emitter::Indentation, "indentation", passed);
		RunEmitterTest(&Emitter::SimpleGlobalSettings, "simple global settings", passed);
		RunEmitterTest(&Emitter::ComplexGlobalSettings, "complex global settings", passed);
373
		RunEmitterTest(&Emitter::Null, "null", passed);
374
375
376
377
378
379
380
381
382
383
		
		RunEmitterErrorTest(&Emitter::ExtraEndSeq, "extra EndSeq", passed);
		RunEmitterErrorTest(&Emitter::ExtraEndMap, "extra EndMap", passed);
		RunEmitterErrorTest(&Emitter::BadSingleQuoted, "bad single quoted string", passed);
		RunEmitterErrorTest(&Emitter::InvalidAnchor, "invalid anchor", passed);
		RunEmitterErrorTest(&Emitter::InvalidAlias, "invalid alias", passed);
		RunEmitterErrorTest(&Emitter::MissingKey, "missing key", passed);
		RunEmitterErrorTest(&Emitter::MissingValue, "missing value", passed);
		RunEmitterErrorTest(&Emitter::UnexpectedKey, "unexpected key", passed);
		RunEmitterErrorTest(&Emitter::UnexpectedValue, "unexpected value", passed);
384
385
386
		return passed;
	}
	
387
}