tests.cpp 11.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
#include "yaml.h"
#include "tests.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>

namespace Test
{
10
	void RunAll()
11
12
	{
		bool passed = true;
13
14
		if(!RunParserTests())
			passed = false;
15
16
17
		
		if(!RunEmitterTests())
			passed = false;
18
19
20
21

		if(passed)
			std::cout << "All tests passed!\n";
	}
22
23
24
	
	////////////////////////////////////////////////////////////////////////////////////////
	// Parser tests
25

26
27
28
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
	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";
				}
			}
		}
56

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
		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";
73
74
			}
		}
75
76
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

		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";
			}
		}
243
	}
244

245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
	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);
265
266
267
268
269
270
271
272
273
274
275

		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);
276
		return passed;
277
	}
278

279
280
281
282
	////////////////////////////////////////////////////////////////////////////////////////
	// Emitter tests
	
	namespace {
283
		void RunEmitterTest(void (*test)(YAML::Emitter&, std::string&), const std::string& name, bool& passed) {
284
285
286
			YAML::Emitter out;
			std::string desiredOutput;
			test(out, desiredOutput);
287
			std::string output = out.c_str();
288

289
			if(output == desiredOutput) {
290
				std::cout << "Emitter test passed: " << name << "\n";
291
292
			} else {
				passed = false;
293
				std::cout << "Emitter test failed: " << name << "\n";
294
				std::cout << "Output:\n";
295
				std::cout << output << "<<<\n";
296
297
298
299
300
				std::cout << "Desired output:\n";
				std::cout << desiredOutput << "<<<\n";				
			}
		}
		
301
		void RunEmitterErrorTest(void (*test)(YAML::Emitter&, std::string&), const std::string& name, bool& passed) {
302
303
304
305
306
			YAML::Emitter out;
			std::string desiredError;
			test(out, desiredError);
			std::string lastError = out.GetLastError();
			if(!out.good() && lastError == desiredError) {
307
				std::cout << "Emitter test passed: " << name << "\n";
308
309
			} else {
				passed = false;
310
				std::cout << "Emitter test failed: " << name << "\n";
311
312
313
314
315
316
317
318
319
320
321
322
				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;
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
		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);
		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);
		
		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);
363
364
365
		return passed;
	}
	
366
}