The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
test_help_markup.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2012 - 2025
3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY.
11
12 See the COPYING file for more details.
13*/
14
15#define GETTEXT_DOMAIN "wesnoth-test"
16
17#include <boost/test/unit_test.hpp>
18
19#include "config.hpp"
21
22BOOST_AUTO_TEST_SUITE( help_markup )
23
24BOOST_AUTO_TEST_CASE( test_simple_help_markup )
25{
26 // This tests markup with no tags.
28
29 // Text parses as text
30 output = markup::parse_text("Hello World");
31 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
32 BOOST_CHECK(output.has_child("text"));
33 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
34 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "Hello World");
35
36 // Backslashes protect the following character even if it's special
37 output = markup::parse_text(R"==(<not_a_tag>)==");
38 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
39 BOOST_CHECK(output.has_child("text"));
40 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
41 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "<not_a_tag>");
42
43 // Simple named character entities are substituted
44 output = markup::parse_text("Me &amp; You");
45 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
46 BOOST_CHECK(output.has_child("text"));
47 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
48 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "Me & You");
49
50 // Decimal character entities work for single-byte characters
51 output = markup::parse_text("&#198;");
52 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
53 BOOST_CHECK(output.has_child("text"));
54 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
55 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "\u00c6");
56
57 // Hex character entities work for single-byte characters
58 output = markup::parse_text("&#xc6;");
59 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
60 BOOST_CHECK(output.has_child("text"));
61 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
62 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "\u00c6");
63
64 // Decimal character entities work for two-byte characters
65 output = markup::parse_text("&#5792;");
66 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
67 BOOST_CHECK(output.has_child("text"));
68 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
69 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "\u16a0");
70
71 // Hex character entities work for two-byte characters
72 output = markup::parse_text("&#x16a0;");
73 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
74 BOOST_CHECK(output.has_child("text"));
75 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
76 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "\u16a0");
77
78 // Decimal character entities work for non-BMP characters
79 output = markup::parse_text("&#128519;");
80 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
81 BOOST_CHECK(output.has_child("text"));
82 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
83 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "\U0001f607");
84
85 // Hex character entities work for non-BMP characters
86 output = markup::parse_text("&#x1f607;");
87 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
88 BOOST_CHECK(output.has_child("text"));
89 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
90 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "\U0001f607");
91
92 // Single newlines are taken literally
93 output = markup::parse_text("Line One\nLine Two");
94 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
95 BOOST_CHECK(output.has_child("text"));
96 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
97 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "Line One\nLine Two");
98
99 // Double newlines split into paragraphs
100 output = markup::parse_text("Paragraph One\n\nParagraph Two");
101 BOOST_CHECK_EQUAL(output.all_children_count(), 2);
102 BOOST_CHECK_EQUAL(output.child_count("text"), 2);
103 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
104 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "Paragraph One");
105 BOOST_CHECK_EQUAL(output.mandatory_child("text", 1)["text"], "Paragraph Two");
106
107 // TODO: What about triple, quadruple newlines?
108
109 // Unknown named character entities are processed but not substituted
110 output = markup::parse_text("This &entity; is unknown!");
111 BOOST_CHECK_EQUAL(output.all_children_count(), 3);
112 BOOST_CHECK_EQUAL(output.child_count("text"), 2);
113 BOOST_CHECK(output.has_child("character_entity"));
114 BOOST_CHECK_EQUAL(output.find_total_first_of("character_entity"), 1);
115 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
116 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "This ");
117 BOOST_CHECK(output.mandatory_child("text", 1).has_attribute("text"));
118 BOOST_CHECK_EQUAL(output.mandatory_child("text", 1)["text"], " is unknown!");
119 BOOST_CHECK(output.mandatory_child("character_entity").has_attribute("name"));
120 BOOST_CHECK_EQUAL(output.mandatory_child("character_entity")["name"], "entity");
121
122 // A backslash at end-of-stream is literal
123 output = markup::parse_text(R"==(Ending with backslash\)==");
124 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
125 BOOST_CHECK(output.has_child("text"));
126 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
127 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], R"==(Ending with backslash\)==");
128
129 // A backslash can escape itself
130 output = markup::parse_text(R"==(Backslash\\in middle)==");
131 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
132 BOOST_CHECK(output.has_child("text"));
133 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
134 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], R"==(Backslash\in middle)==");
135
136 // A backslash is removed even if the escaped character is not special
137 output = markup::parse_text(R"==(\T\h\i\s is \p\o\i\n\t\l\e\s\s)==");
138 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
139 BOOST_CHECK(output.has_child("text"));
140 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
141 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "This is pointless");
142
143 // The other four simple named character entities are substituted
144 output = markup::parse_text("&quot;&lt;tag attr=&apos;val&apos;&gt;&quot;");
145 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
146 BOOST_CHECK(output.has_child("text"));
147 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
148 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], R"==("<tag attr='val'>")==");
149}
150
151BOOST_AUTO_TEST_CASE( test_help_markup_old )
152{
153 // This tests strings using old-style help markup tags
155
156 // A simple tag with text content
157 // This format is both old-style and new-style.
158 output = markup::parse_text("<tt>some text</tt>");
159 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
160 BOOST_CHECK(output.has_child("tt"));
161 BOOST_CHECK(output.mandatory_child("tt").has_attribute("text"));
162 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["text"], "some text");
163
164 // With explicit text attribute
165 output = markup::parse_text("<tt>text='some text'</tt>");
166 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
167 BOOST_CHECK(output.has_child("tt"));
168 BOOST_CHECK(output.mandatory_child("tt").has_attribute("text"));
169 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["text"], "some text");
170
171 // With implicit text attribute and another attribute
172 output = markup::parse_text("<tt>attr='value' some text</tt>");
173 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
174 BOOST_CHECK(output.has_child("tt"));
175 BOOST_CHECK(output.mandatory_child("tt").has_attribute("text"));
176 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["text"], "some text");
177 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
178 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value");
179
180 // With explict text attribute and another attribute
181 output = markup::parse_text("<tt>attr='value' text='some text'</tt>");
182 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
183 BOOST_CHECK(output.has_child("tt"));
184 BOOST_CHECK(output.mandatory_child("tt").has_attribute("text"));
185 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["text"], "some text");
186 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
187 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value");
188
189 // A tag in a larger span of text
190 output = markup::parse_text("Here we have <tt>attr='value' text='some text'</tt> with an unknown style applied!");
191 BOOST_CHECK_EQUAL(output.all_children_count(), 3);
192 BOOST_CHECK(output.has_child("tt"));
193 BOOST_CHECK_EQUAL(output.child_count("text"), 2);
194 BOOST_CHECK_EQUAL(output.find_total_first_of("tt"), 1);
195 BOOST_CHECK(output.mandatory_child("tt").has_attribute("text"));
196 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["text"], "some text");
197 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
198 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value");
199 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
200 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "Here we have ");
201 BOOST_CHECK(output.mandatory_child("text", 1).has_attribute("text"));
202 BOOST_CHECK_EQUAL(output.mandatory_child("text", 1)["text"], " with an unknown style applied!");
203
204 // The attribute value can be unquoted
205 output = markup::parse_text("<tt>attr=value</tt>");
206 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
207 BOOST_CHECK(output.has_child("tt"));
208 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
209 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value");
210
211 // Nonalphanumeric characters don't need to be quoted as long as they're not special
212 output = markup::parse_text("<tt>attr=!@#$%^</tt>");
213 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
214 BOOST_CHECK(output.has_child("tt"));
215 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
216 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "!@#$%^");
217
218 // Quoting with single quotes
219 output = markup::parse_text("<tt>attr='value with spaces'</tt>");
220 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
221 BOOST_CHECK(output.has_child("tt"));
222 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
223 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value with spaces");
224
225 // Quoting with double quotes
226 output = markup::parse_text(R"==(<tt>attr="value with spaces"</tt>)==");
227 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
228 BOOST_CHECK(output.has_child("tt"));
229 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
230 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value with spaces");
231
232 // Quotes only count as quotes if they're the first non-whitespace character after the =
233 output = markup::parse_text("<tt>attr=O'Brien</tt>");
234 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
235 BOOST_CHECK(output.has_child("tt"));
236 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
237 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "O'Brien");
238
239 // Single quotes in double-quoted value
240 output = markup::parse_text(R"==(<tt>attr="'tis futile"</tt>)==");
241 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
242 BOOST_CHECK(output.has_child("tt"));
243 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
244 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "'tis futile");
245
246 // Double quotes in single-quoted value
247 output = markup::parse_text(R"==(<tt>attr='the "mega" test'</tt>)==");
248 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
249 BOOST_CHECK(output.has_child("tt"));
250 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
251 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], R"==(the "mega" test)==");
252
253 // Spaces around the equals are allowed
254 output = markup::parse_text("<tt>attr = value</tt>");
255 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
256 BOOST_CHECK(output.has_child("tt"));
257 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
258 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value");
259
260 // Newlines are also allowed
261 output = markup::parse_text("<tt>attr=\nvalue\nthat=\nthis</tt>");
262 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
263 BOOST_CHECK(output.has_child("tt"));
264 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
265 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value");
266 BOOST_CHECK(output.mandatory_child("tt").has_attribute("that"));
267 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["that"], "this");
268
269 // Escaping a single quote in a single-quoted value
270 output = markup::parse_text(R"==(<tt>attr='Let\'s go?'</tt>)==");
271 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
272 BOOST_CHECK(output.has_child("tt"));
273 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
274 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "Let's go?");
275
276 // Using a simple character entity in a single-quoted value
277 output = markup::parse_text("<tt>attr='&apos;tis good'</tt>");
278 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
279 BOOST_CHECK(output.has_child("tt"));
280 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
281 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "'tis good");
282
283 // A newline in a single-quoted value
284 output = markup::parse_text("<tt>attr='Line 1\nLine 2'</tt>");
285 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
286 BOOST_CHECK(output.has_child("tt"));
287 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
288 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "Line 1\nLine 2");
289
290 // Using a simple character entity in a double-quoted value
291 output = markup::parse_text(R"==(<tt>attr="&quot;Yes!&quot;"</tt>)==");
292 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
293 BOOST_CHECK(output.has_child("tt"));
294 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
295 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], R"==("Yes!")==");
296}
297
298BOOST_AUTO_TEST_CASE( test_help_markup_new )
299{
300 // This tests strings using new-style help markup tags
302
303 // A simple tag with text content
304 // This format is both old-style and new-style.
305 output = markup::parse_text("<tt>some text</tt>");
306 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
307 BOOST_CHECK(output.has_child("tt"));
308 BOOST_CHECK(output.mandatory_child("tt").has_attribute("text"));
309 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["text"], "some text");
310
311 // A simple auto-closed tag
312 output = markup::parse_text("<tt/>");
313 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
314 BOOST_CHECK(output.has_child("tt"));
315 BOOST_CHECK(!output.mandatory_child("tt").has_attribute("text"));
316
317 // Auto-closed tag can have a space before the slash
318 output = markup::parse_text("<tt />");
319 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
320 BOOST_CHECK(output.has_child("tt"));
321 BOOST_CHECK(!output.mandatory_child("tt").has_attribute("text"));
322
323 // With an attribute
324 output = markup::parse_text("<tt attr='value'>some text</tt>");
325 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
326 BOOST_CHECK(output.has_child("tt"));
327 BOOST_CHECK(output.mandatory_child("tt").has_attribute("text"));
328 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["text"], "some text");
329 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
330 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value");
331
332 // With an attribute that has no value specified
333 output = markup::parse_text("<tt attr>some text</tt>");
334 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
335 BOOST_CHECK(output.has_child("tt"));
336 BOOST_CHECK(output.mandatory_child("tt").has_attribute("text"));
337 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["text"], "some text");
338 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
339 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "");
340
341 // A tag in a larger span of text
342 output = markup::parse_text("Here we have <tt attr='value'>some text</tt> with an unknown style applied!");
343 BOOST_CHECK_EQUAL(output.all_children_count(), 3);
344 BOOST_CHECK(output.has_child("tt"));
345 BOOST_CHECK_EQUAL(output.child_count("text"), 2);
346 BOOST_CHECK_EQUAL(output.find_total_first_of("tt"), 1);
347 BOOST_CHECK(output.mandatory_child("tt").has_attribute("text"));
348 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["text"], "some text");
349 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
350 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value");
351 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
352 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "Here we have ");
353 BOOST_CHECK(output.mandatory_child("text", 1).has_attribute("text"));
354 BOOST_CHECK_EQUAL(output.mandatory_child("text", 1)["text"], " with an unknown style applied!");
355
356 // The attribute value can be unquoted
357 output = markup::parse_text("<tt attr=value/>");
358 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
359 BOOST_CHECK(output.has_child("tt"));
360 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
361 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value");
362
363 // Nonalphanumeric characters don't need to be quoted as long as they're not special
364 output = markup::parse_text("<tt attr=!@#$%^/>");
365 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
366 BOOST_CHECK(output.has_child("tt"));
367 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
368 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "!@#$%^");
369
370 // Quoting with single quotes
371 output = markup::parse_text("<tt attr='value with spaces'/>");
372 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
373 BOOST_CHECK(output.has_child("tt"));
374 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
375 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value with spaces");
376
377 // Quoting with double quotes
378 output = markup::parse_text(R"==(<tt attr="value with spaces"/>)==");
379 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
380 BOOST_CHECK(output.has_child("tt"));
381 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
382 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value with spaces");
383
384 // Quotes only count as quotes if they're the first non-whitespace character after the =
385 output = markup::parse_text("<tt attr=O'Brien/>");
386 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
387 BOOST_CHECK(output.has_child("tt"));
388 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
389 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "O'Brien");
390
391 // Single quotes in double-quoted value
392 output = markup::parse_text(R"==(<tt attr="'tis futile"/>)==");
393 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
394 BOOST_CHECK(output.has_child("tt"));
395 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
396 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "'tis futile");
397
398 // Double quotes in single-quoted value
399 output = markup::parse_text(R"==(<tt attr='the "mega" test'/>)==");
400 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
401 BOOST_CHECK(output.has_child("tt"));
402 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
403 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], R"==(the "mega" test)==");
404
405 // Spaces around the equals are allowed
406 output = markup::parse_text("<tt attr = value/>");
407 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
408 BOOST_CHECK(output.has_child("tt"));
409 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
410 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value");
411
412 // Newlines are also allowed
413 output = markup::parse_text("<tt attr=\nvalue\nthat=\nthis/>");
414 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
415 BOOST_CHECK(output.has_child("tt"));
416 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
417 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "value");
418 BOOST_CHECK(output.mandatory_child("tt").has_attribute("that"));
419 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["that"], "this");
420
421 // Escaping a single quote in a single-quoted value
422 output = markup::parse_text(R"==(<tt attr='Let\'s go?'/>)==");
423 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
424 BOOST_CHECK(output.has_child("tt"));
425 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
426 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "Let's go?");
427
428 // Using a simple character entity in a single-quoted value
429 output = markup::parse_text("<tt attr='&apos;tis good'/>");
430 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
431 BOOST_CHECK(output.has_child("tt"));
432 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
433 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "'tis good");
434
435 // A newline in a single-quoted value
436 output = markup::parse_text("<tt attr='Line 1\nLine 2'/>");
437 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
438 BOOST_CHECK(output.has_child("tt"));
439 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
440 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], "Line 1\nLine 2");
441
442 // Using a simple character entity in a double-quoted value
443 output = markup::parse_text(R"==(<tt attr="&quot;Yes!&quot;"/>)==");
444 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
445 BOOST_CHECK(output.has_child("tt"));
446 BOOST_CHECK(output.mandatory_child("tt").has_attribute("attr"));
447 BOOST_CHECK_EQUAL(output.mandatory_child("tt")["attr"], R"==("Yes!")==");
448
449 // Using an unknown character entity in a tag
450 output = markup::parse_text("<tt>what &ndash; no</tt>");
451 BOOST_CHECK_EQUAL(output.all_children_count(), 1);
452 BOOST_CHECK(output.has_child("tt"));
453 // Simplify the remaining tests for this one by eliminating the outer layer
454 output = output.mandatory_child("tt");
455 BOOST_CHECK(output.has_child("character_entity"));
456 BOOST_CHECK_EQUAL(output.child_count("text"), 2);
457 BOOST_CHECK_EQUAL(output.find_total_first_of("character_entity"), 1);
458 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
459 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "what ");
460 BOOST_CHECK(output.mandatory_child("text", 1).has_attribute("text"));
461 BOOST_CHECK_EQUAL(output.mandatory_child("text", 1)["text"], " no");
462 BOOST_CHECK(output.mandatory_child("character_entity").has_attribute("name"));
463 BOOST_CHECK_EQUAL(output.mandatory_child("character_entity")["name"], "ndash");
464
465 // Tags can be nested
466 output = markup::parse_text("We like to <tt>nest <abc>various</abc> <def>tags</def> within</tt> each other!");
467 BOOST_CHECK_EQUAL(output.all_children_count(), 3);
468 BOOST_CHECK(output.has_child("tt"));
469 BOOST_CHECK_EQUAL(output.child_count("text"), 2);
470 BOOST_CHECK_EQUAL(output.find_total_first_of("tt"), 1);
471 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
472 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "We like to ");
473 BOOST_CHECK(output.mandatory_child("text", 1).has_attribute("text"));
474 BOOST_CHECK_EQUAL(output.mandatory_child("text", 1)["text"], " each other!");
475 // Simplify the remaining tests for this one by eliminating the outer layer
476 output = output.mandatory_child("tt");
477 BOOST_CHECK_EQUAL(output.all_children_count(), 5);
478 BOOST_CHECK(output.has_child("abc"));
479 BOOST_CHECK(output.has_child("def"));
480 BOOST_CHECK_EQUAL(output.child_count("text"), 3);
481 BOOST_CHECK_EQUAL(output.find_total_first_of("abc"), 1);
482 BOOST_CHECK_EQUAL(output.find_total_first_of("def"), 3);
483 BOOST_CHECK(output.mandatory_child("text").has_attribute("text"));
484 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "nest ");
485 BOOST_CHECK(output.mandatory_child("abc").has_attribute("text"));
486 BOOST_CHECK_EQUAL(output.mandatory_child("abc")["text"], "various");
487 BOOST_CHECK(output.mandatory_child("text", 1).has_attribute("text"));
488 BOOST_CHECK_EQUAL(output.mandatory_child("text", 1)["text"], " ");
489 BOOST_CHECK(output.mandatory_child("def").has_attribute("text"));
490 BOOST_CHECK_EQUAL(output.mandatory_child("def")["text"], "tags");
491 BOOST_CHECK(output.mandatory_child("text", 2).has_attribute("text"));
492 BOOST_CHECK_EQUAL(output.mandatory_child("text", 2)["text"], " within");
493
494 // Two tags with nothing between them shouldn't have an intervening text span.
495 output = markup::parse_text("<img src=help/orb-green.png align=here/><img src=help/orb-green.png align=there/>");
496 BOOST_CHECK_EQUAL(output.all_children_count(), 2);
497 BOOST_CHECK_EQUAL(output.child_count("img"), 2);
498 BOOST_CHECK(output.mandatory_child("img").has_attribute("src"));
499 BOOST_CHECK_EQUAL(output.mandatory_child("img")["src"], "help/orb-green.png");
500 BOOST_CHECK(output.mandatory_child("img").has_attribute("align"));
501 BOOST_CHECK_EQUAL(output.mandatory_child("img")["align"], "here");
502 BOOST_CHECK(output.mandatory_child("img", 1).has_attribute("src"));
503 BOOST_CHECK_EQUAL(output.mandatory_child("img", 1)["src"], "help/orb-green.png");
504 BOOST_CHECK(output.mandatory_child("img", 1).has_attribute("align"));
505 BOOST_CHECK_EQUAL(output.mandatory_child("img", 1)["align"], "there");
506
507 // Two tags with a newline between them should have an intervening text span containing exactly that.
508 output = markup::parse_text("<link dst=foo>First</link>\n<link dst=bar>Second</link>");
509 BOOST_CHECK_EQUAL(output.all_children_count(), 3);
510 BOOST_CHECK(output.has_child("text"));
511 BOOST_CHECK_EQUAL(output.child_count("link"), 2);
512 BOOST_CHECK_EQUAL(output.find_total_first_of("text"), 1);
513 BOOST_CHECK(output.mandatory_child("link").has_attribute("dst"));
514 BOOST_CHECK_EQUAL(output.mandatory_child("link")["dst"], "foo");
515 BOOST_CHECK(output.mandatory_child("link").has_attribute("text"));
516 BOOST_CHECK_EQUAL(output.mandatory_child("link")["text"], "First");
517 BOOST_CHECK((output.mandatory_child("text").has_attribute("text")));
518 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "\n");
519 BOOST_CHECK(output.mandatory_child("link", 1).has_attribute("dst"));
520 BOOST_CHECK_EQUAL(output.mandatory_child("link", 1)["dst"], "bar");
521 BOOST_CHECK(output.mandatory_child("link", 1).has_attribute("text"));
522 BOOST_CHECK_EQUAL(output.mandatory_child("link", 1)["text"], "Second");
523
524 // Tag at the end of a paragraph produces an empty text span.
525 output = markup::parse_text("See also: <link dst=details>Details</link>\n\nAnd here's an extra paragraph!");
526 BOOST_CHECK_EQUAL(output.all_children_count(), 4);
527 BOOST_CHECK(output.has_child("link"));
528 BOOST_CHECK_EQUAL(output.child_count("text"), 3);
529 BOOST_CHECK_EQUAL(output.find_total_first_of("link"), 1);
530 BOOST_CHECK(output.mandatory_child("link").has_attribute("dst"));
531 BOOST_CHECK_EQUAL(output.mandatory_child("link")["dst"], "details");
532 BOOST_CHECK(output.mandatory_child("link").has_attribute("text"));
533 BOOST_CHECK_EQUAL(output.mandatory_child("link")["text"], "Details");
534 BOOST_CHECK((output.mandatory_child("text").has_attribute("text")));
535 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "See also: ");
536 BOOST_CHECK((output.mandatory_child("text", 1).has_attribute("text")));
537 BOOST_CHECK_EQUAL(output.mandatory_child("text", 1)["text"], "");
538 BOOST_CHECK((output.mandatory_child("text", 2).has_attribute("text")));
539 BOOST_CHECK_EQUAL(output.mandatory_child("text", 2)["text"], "And here's an extra paragraph!");
540
541 // Tag at the beginning of a paragraph produces an empty text span.
542 output = markup::parse_text("This is some information.\n\n<img src='help/orb-red.png'/>And some more info!");
543 BOOST_CHECK_EQUAL(output.all_children_count(), 4);
544 BOOST_CHECK(output.has_child("img"));
545 BOOST_CHECK_EQUAL(output.child_count("text"), 3);
546 BOOST_CHECK_EQUAL(output.find_total_first_of("img"), 2);
547 BOOST_CHECK(output.mandatory_child("img").has_attribute("src"));
548 BOOST_CHECK_EQUAL(output.mandatory_child("img")["src"], "help/orb-red.png");
549 BOOST_CHECK((output.mandatory_child("text").has_attribute("text")));
550 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "This is some information.");
551 BOOST_CHECK((output.mandatory_child("text", 1).has_attribute("text")));
552 BOOST_CHECK_EQUAL(output.mandatory_child("text", 1)["text"], "");
553 BOOST_CHECK((output.mandatory_child("text", 2).has_attribute("text")));
554 BOOST_CHECK_EQUAL(output.mandatory_child("text", 2)["text"], "And some more info!");
555
556 // Paragraph break between two tags produces two empty text spans.
557 output = markup::parse_text("<link dst=toc>Table of Contents</link>\n\n<img src='fancy-bullet.png'/>First...");
558 BOOST_CHECK_EQUAL(output.all_children_count(), 5);
559 BOOST_CHECK(output.has_child("link"));
560 BOOST_CHECK(output.has_child("img"));
561 BOOST_CHECK_EQUAL(output.child_count("text"), 3);
562 BOOST_CHECK_EQUAL(output.find_total_first_of("link"), 0);
563 BOOST_CHECK_EQUAL(output.find_total_first_of("img"), 3);
564 BOOST_CHECK(output.mandatory_child("link").has_attribute("dst"));
565 BOOST_CHECK_EQUAL(output.mandatory_child("link")["dst"], "toc");
566 BOOST_CHECK(output.mandatory_child("link").has_attribute("text"));
567 BOOST_CHECK_EQUAL(output.mandatory_child("link")["text"], "Table of Contents");
568 BOOST_CHECK(output.mandatory_child("img").has_attribute("src"));
569 BOOST_CHECK_EQUAL(output.mandatory_child("img")["src"], "fancy-bullet.png");
570 BOOST_CHECK((output.mandatory_child("text").has_attribute("text")));
571 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "");
572 BOOST_CHECK((output.mandatory_child("text", 1).has_attribute("text")));
573 BOOST_CHECK_EQUAL(output.mandatory_child("text", 1)["text"], "");
574 BOOST_CHECK((output.mandatory_child("text", 2).has_attribute("text")));
575 BOOST_CHECK_EQUAL(output.mandatory_child("text", 2)["text"], "First...");
576
577 // Three consecutive newlines produces a paragraph beginning with a newline.
578 output = markup::parse_text("Let's have...\n\n\n...three consecutive newlines!");
579 BOOST_CHECK_EQUAL(output.all_children_count(), 2);
580 BOOST_CHECK_EQUAL(output.child_count("text"), 2);
581 BOOST_CHECK((output.mandatory_child("text").has_attribute("text")));
582 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "Let's have...");
583 BOOST_CHECK((output.mandatory_child("text", 1).has_attribute("text")));
584 BOOST_CHECK_EQUAL(output.mandatory_child("text", 1)["text"], "\n...three consecutive newlines!");
585
586 // Four consecutive newlines produces an empty paragraph.
587 output = markup::parse_text("Let's have...\n\n\n\n...four consecutive newlines!");
588 BOOST_CHECK_EQUAL(output.all_children_count(), 3);
589 BOOST_CHECK_EQUAL(output.child_count("text"), 3);
590 BOOST_CHECK((output.mandatory_child("text").has_attribute("text")));
591 BOOST_CHECK_EQUAL(output.mandatory_child("text")["text"], "Let's have...");
592 BOOST_CHECK((output.mandatory_child("text", 1).has_attribute("text")));
593 BOOST_CHECK_EQUAL(output.mandatory_child("text", 1)["text"], "");
594 BOOST_CHECK((output.mandatory_child("text", 2).has_attribute("text")));
595 BOOST_CHECK_EQUAL(output.mandatory_child("text", 2)["text"], "...four consecutive newlines!");
596}
597
598BOOST_AUTO_TEST_SUITE_END()
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
Definitions for the interface to Wesnoth Markup Language (WML).
static std::ostream & output()
Definition log.cpp:103
config parse_text(const std::string &text)
Parse a xml style marked up text string.
Definition markup.cpp:444
BOOST_AUTO_TEST_SUITE(filesystem)
BOOST_AUTO_TEST_CASE(test_simple_help_markup)