The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
test_config.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2014 - 2025
3 by Chris Beck <render787@gmail.com>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16#define GETTEXT_DOMAIN "wesnoth-test"
17
18#include <boost/test/unit_test.hpp>
19// Work around a Boost<1.67 bug fixed in 1.67: Include test_case.hpp after
20// unit_tests.hpp. https://svn.boost.org/trac10/ticket/13387
21#include <boost/test/data/test_case.hpp> // for parametrized test
22#include <cmath>
23
24#include "config.hpp"
25#include "variable_info.hpp"
26
27BOOST_AUTO_TEST_SUITE(test_config)
28
29BOOST_AUTO_TEST_CASE(test_config_attribute_value)
30{
31 config c;
32 config c1;
33 config c2;
34 const config& cc = c;
35 int x_int;
36 std::string x_str;
37 long long x_sll;
38 double x_dbl;
39
40// compare identical assigned int vs string
41 c1["x"] = 6;
42 c2["x"] = "6";
43 BOOST_CHECK_EQUAL(c1["x"], c2["x"]);
44
45// compare identical assigned int vs floating point
46 c1["x"] = 6;
47 c2["x"] = 6.0;
48 BOOST_CHECK_EQUAL(c1["x"], c2["x"]);
49
50// compare identical assigned int-string vs floating point
51 c1["x"] = "6";
52 c2["x"] = 6.0;
53 BOOST_CHECK_EQUAL(c1["x"], c2["x"]);
54
55// compare identical assigned floating point-string vs int
56 c1["x"] = 6;
57 c2["x"] = "6.0";
58 BOOST_CHECK_NE(c1["x"], c2["x"]);
59
60// compare identical assigned floating point vs string
61 c1["x"] = 6.0;
62 c2["x"] = "6.0";
63 BOOST_CHECK_NE(c1["x"], c2["x"]);
64
65// check what happens when trying to get a numeric result from a non-numeric value
66 c["x"] = "1aaaa";
67 x_str = c["x"].str();
68 BOOST_CHECK_EQUAL(x_str, "1aaaa");
69 x_int = c["x"].to_int();
70 BOOST_CHECK_EQUAL(x_int, 1);
71 x_sll = c["x"].to_long_long();
72 BOOST_CHECK_EQUAL(x_sll, 1ll);
73 x_dbl = c["x"].to_double();
74 BOOST_CHECK_EQUAL(x_dbl, 1.0);
75
76 c["x"] = "1.7aaaa";
77 x_str = c["x"].str();
78 BOOST_CHECK_EQUAL(x_str, "1.7aaaa");
79 x_int = c["x"].to_int();
80 BOOST_CHECK_EQUAL(x_int, 1);
81 x_sll = c["x"].to_long_long();
82 BOOST_CHECK_EQUAL(x_sll, 1ll);
83 x_dbl = c["x"].to_double();
84 BOOST_CHECK_EQUAL(x_dbl, 1.7);
85
86 c["x"] = "aaaa1";
87 x_str = c["x"].str();
88 BOOST_CHECK_EQUAL(x_str, "aaaa1");
89 x_int = c["x"].to_int();
90 BOOST_CHECK_EQUAL(x_int, 0);
91 x_sll = c["x"].to_long_long();
92 BOOST_CHECK_EQUAL(x_sll, 0ll);
93 x_dbl = c["x"].to_double();
94 BOOST_CHECK_EQUAL(x_dbl, 0.0);
95
96// check type conversion when assigned as int
97 c["x"] = 1;
98 x_str = c["x"].str();
99 BOOST_CHECK_EQUAL(x_str, "1");
100 x_int = c["x"].to_int();
101 BOOST_CHECK_EQUAL(x_int, 1);
102 x_sll = c["x"].to_long_long();
103 BOOST_CHECK_EQUAL(x_sll, 1ll);
104 x_dbl = c["x"].to_double();
105 BOOST_CHECK_EQUAL(x_dbl, 1.0);
106
107// check type conversion when assigned as int (again)
108 c["x"] = 10000000;
109 x_int = c["x"].to_int();
110 BOOST_CHECK_EQUAL(x_int, 10000000);
111 x_str = c["x"].str();
112 BOOST_CHECK_EQUAL(x_str, "10000000");
113 x_sll = c["x"].to_long_long();
114 BOOST_CHECK_EQUAL(x_sll, 10000000ll);
115 x_dbl = c["x"].to_double();
116 BOOST_CHECK_EQUAL(x_dbl, 1e7);
117
118// check type conversion when assigned aan empty string
119 c["x"] = "";
120 x_sll = c["x"].to_long_long();
121 BOOST_CHECK_EQUAL(x_sll, 0ll);
122 x_str = c["x"].str();
123 BOOST_CHECK_EQUAL(x_str, "");
124 x_int = c["x"].to_int();
125 BOOST_CHECK_EQUAL(x_int, 0);
126 x_dbl = c["x"].to_double();
127 BOOST_CHECK_EQUAL(x_dbl, 0.0);
128
129// check type conversion when assigned as a hex string
130 c["x"] = "0x11";
131 x_int = c["x"].to_int();
132 BOOST_CHECK_EQUAL(x_int, 0);
133 x_str = c["x"].str();
134 BOOST_CHECK_EQUAL(x_str, "0x11");
135 x_sll = c["x"].to_long_long();
136 BOOST_CHECK_EQUAL(x_sll, 0ll);
137 x_dbl = c["x"].to_double();
138 BOOST_CHECK_EQUAL(x_dbl, 0.0);
139
140// check type conversion when assigned as a hex string (again)
141 c["x"] = "0xab";
142 x_int = c["x"].to_int();
143 BOOST_CHECK_EQUAL(x_int, 0);
144 x_str = c["x"].str();
145 BOOST_CHECK_EQUAL(x_str, "0xab");
146 x_sll = c["x"].to_long_long();
147 BOOST_CHECK_EQUAL(x_sll, 0ll);
148 x_dbl = c["x"].to_double();
149 BOOST_CHECK_EQUAL(x_dbl, 0.0);
150
151// check type conversion when assigned as a string with leading zeroes
152 c["x"] = "00001111";
153 x_int = c["x"].to_int();
154 BOOST_CHECK_EQUAL(x_int, 1111);
155 x_str = c["x"].str();
156 BOOST_CHECK_EQUAL(x_str, "00001111");
157 x_sll = c["x"].to_long_long();
158 BOOST_CHECK_EQUAL(x_sll, 1111ll);
159 x_dbl = c["x"].to_double();
160 BOOST_CHECK_EQUAL(x_dbl, 1.111e3);
161
162// check type conversion when assigned as a string with only zeroes
163 c["x"] = "000000";
164 x_int = c["x"].to_int();
165 BOOST_CHECK_EQUAL(x_int, 0);
166 x_str = c["x"].str();
167 BOOST_CHECK_EQUAL(x_str, "000000");
168 x_sll = c["x"].to_long_long();
169 BOOST_CHECK_EQUAL(x_sll, 0ll);
170 x_dbl = c["x"].to_double();
171 BOOST_CHECK_EQUAL(x_dbl, 0.0);
172
173// check type conversion when assigned as a string with leading zeroes and is too large to fit in an int
174 c["x"] = "01234567890123456789";
175 x_sll = c["x"].to_long_long();
176 BOOST_CHECK_EQUAL(x_sll, 1234567890123456789ll);
177 x_str = c["x"].str();
178 BOOST_CHECK_EQUAL(x_str, "01234567890123456789");
179 x_int = c["x"].to_int();
180 BOOST_CHECK_EQUAL(x_int, 0);
181 x_dbl = c["x"].to_double();
182 BOOST_CHECK_EQUAL(x_dbl, 1.23456789012345678e18);
183
184// check type conversion when assigned as a string with no leading zeroes and is too large to fit in an int
185 c["x"] = "99999999999999999999";
186 x_sll = c["x"].to_long_long();
187 BOOST_CHECK_EQUAL(x_sll, 0ll);
188 x_str = c["x"].str();
189 BOOST_CHECK_EQUAL(x_str, "99999999999999999999");
190 x_int = c["x"].to_int();
191 BOOST_CHECK_EQUAL(x_int, 0);
192 x_dbl = c["x"].to_double();
193 BOOST_CHECK_EQUAL(x_dbl, 1e20);
194
195// check type conversion when assigned as a floating point
196 c["x"] = 1.499;
197 x_sll = c["x"].to_long_long();
198 BOOST_CHECK_EQUAL(x_sll, 1ll);
199 x_str = c["x"].str();
200 BOOST_CHECK_EQUAL(x_str, "1.499");
201 x_int = c["x"].to_int();
202 BOOST_CHECK_EQUAL(x_int, 1);
203 x_dbl = c["x"].to_double();
204 BOOST_CHECK(std::abs(x_dbl - 1.499) < 1e-6);
205
206// check type conversion when assigned as a long long (int overflows)
207 c["x"] = 123456789123ll;
208 x_int = c["x"].to_int();
209 BOOST_CHECK_EQUAL(x_int, -1097262461);
210 x_dbl = c["x"].to_double();
211 BOOST_CHECK_EQUAL(x_dbl, 1.23456789123e11);
212 x_sll = c["x"].to_long_long();
213 BOOST_CHECK_EQUAL(x_sll, 123456789123ll);
214 x_str = c["x"].str();
215 BOOST_CHECK_EQUAL(x_str, "123456789123");
216
217// check heterogeneous comparison
218 using namespace std::literals;
219
220 c["x"] = "1";
221 BOOST_CHECK_EQUAL(c["x"], "1");
222 c["x"] = 222;
223 BOOST_CHECK_EQUAL(c["x"], "222");
224 BOOST_CHECK_EQUAL(c["x"], "222"s);
225 c["x"] = "test";
226 BOOST_CHECK_EQUAL(c["x"], "test"sv);
227 c["x"] = false;
228 BOOST_CHECK_EQUAL(c["x"], "no");
229 BOOST_CHECK_EQUAL(c["x"], "false");
230 c["x"] = 9.87654321;
231 BOOST_CHECK_EQUAL(c["x"], "9.87654321");
232 BOOST_CHECK_EQUAL(c["x"], t_string{"9.87654321"});
233 BOOST_CHECK_EQUAL(c["x"], config_attribute_value::create("9.87654321"));
234 c["x"] = "sfvsdgdsfg";
235 BOOST_CHECK_EQUAL(c["x"], "sfvsdgdsfg");
236 BOOST_CHECK_EQUAL(c["x"], "sfvsdgdsfg"s);
237 BOOST_CHECK_EQUAL(c["x"], "sfvsdgdsfg"sv);
238 BOOST_CHECK_EQUAL(c["x"], t_string{"sfvsdgdsfg"});
239 BOOST_CHECK_NE(c["x"], "a random string");
240
241 // reversed order heterogenous comparison
242 c["x"] = 9.87654321;
243 BOOST_CHECK_EQUAL("9.87654321", c["x"]);
244 BOOST_CHECK_EQUAL(t_string{"9.87654321"}, c["x"]);
245 BOOST_CHECK_EQUAL(config_attribute_value::create("9.87654321"), c["x"]);
246 BOOST_CHECK_NE("1.23456789", c["x"]);
247 BOOST_CHECK_NE("1.23456789"s, c["x"]);
248 BOOST_CHECK_NE("1.23456789"sv, c["x"]);
249
250 // blank != "" test.
251 c.clear();
252 BOOST_CHECK(cc["x"] != "");
253 BOOST_CHECK(cc["x"].empty());
254 BOOST_CHECK(cc["x"].blank());
255
256 BOOST_CHECK(c["x"] != "");
257 BOOST_CHECK(c["x"].empty());
258 BOOST_CHECK(c["x"].blank());
259
260 BOOST_CHECK_EQUAL(cc["x"], c["x"]);
261
262 c["x"] = "";
263 BOOST_CHECK(cc["x"].empty());
264 BOOST_CHECK(cc["x"].empty());
265 BOOST_CHECK(!cc["x"].blank());
266
267 BOOST_CHECK(c["x"].empty());
268 BOOST_CHECK(c["x"].empty());
269 BOOST_CHECK(!c["x"].blank());
270
271 BOOST_CHECK_EQUAL(cc["x"], c["x"]);
272}
273
274BOOST_AUTO_TEST_CASE(test_variable_info)
275{
276 // Returns the integer value of the given variable in the config
277 const auto scalar_value = [](const std::string& var, const config& cfg) {
278 return variable_access_const{var, cfg}.as_scalar().to_int();
279 };
280
281 config c;
282 {
283 variable_access_const access("", c);
284 // We dotn allow empty keys
285 BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
286 }
287 {
288 variable_access_const access("some_non_existent.", c);
289 // We dotn allow empty keys
290 BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
291 }
292 {
293 variable_access_const access("some_non_existent[0]value", c);
294 // We expect '.' after ']'
295 BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
296 }
297 {
298 variable_access_const access("some_non_existent", c);
299 // we return empty be default
300 BOOST_CHECK(!access.exists_as_container());
301 BOOST_CHECK_EQUAL(access.as_container(), config());
302 BOOST_CHECK(!access.exists_as_attribute());
303 BOOST_CHECK_EQUAL(access.as_scalar(), config::attribute_value());
304 }
305 {
306 variable_access_const access("a.b[0].c[1].d.e.f[2]", c);
307 // we return empty be default
308 BOOST_CHECK(!access.exists_as_container());
309 BOOST_CHECK_EQUAL(access.as_container(), config());
310 // Explicit indexes can never be an attribute
311 BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
312 }
313 BOOST_CHECK(c.empty());
314 {
315 config c2;
316 variable_access_create access("a.b[0].c[1].d.e.f[2].g", c2);
317 access.as_scalar() = 84;
318 BOOST_CHECK_EQUAL(scalar_value("a.length", c2), 1);
319 BOOST_CHECK_EQUAL(scalar_value("a.b.length", c2), 1);
320 BOOST_CHECK_EQUAL(scalar_value("a.b.c.length", c2), 2);
321 BOOST_CHECK_EQUAL(scalar_value("a.b.c[1].d.e.f.length", c2), 3);
322 // we set g as a scalar
323 BOOST_CHECK_EQUAL(scalar_value("a.b.c[1].d.e.f[2].g.length", c2), 0);
324 BOOST_CHECK_EQUAL(scalar_value("a.b.c[1].d.e.f[2].g", c2), 84);
325 }
326 {
327 config c2;
328 variable_access_throw access("a.b[9].c", c2);
329 BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
330 }
331 {
332 /* clang-format off */
333 const config nonempty{
334 "tag1", config(),
335 "tag1", config{
336 "tag2", config(),
337 "tag2", config(),
338 "tag2", config{
339 "atribute1", 88,
340 "atribute2", "value",
341 },
342 },
343 "tag1", config(),
344 };
345 /* clang-format on */
346 /** This is the config:
347 [tag1]
348 [/tag1]
349 [tag1]
350 [tag2]
351 [/tag2]
352 [tag2]
353 [/tag2]
354 [tag2]
355 atribute1 = 88
356 atribute2 = "value"
357 [/tag2]
358 [/tag1]
359 [tag1]
360 [/tag1]
361 */
362 BOOST_CHECK_EQUAL(scalar_value("tag1.length", nonempty), 3);
363 BOOST_CHECK_EQUAL(scalar_value("tag1.tag2.length", nonempty), 0);
364 BOOST_CHECK_EQUAL(scalar_value("tag1[1].tag2.length", nonempty), 3);
365 BOOST_CHECK_EQUAL(scalar_value("tag1[1].tag2[2].atribute1", nonempty), 88);
366 int count = 0;
367 for([[maybe_unused]] const config& child : variable_access_const("tag1", nonempty).as_array()) {
368 ++count;
369 }
370 BOOST_CHECK_EQUAL(count, 3);
371 count = 0;
372 for([[maybe_unused]] const config& child : variable_access_const("tag1.tag2", nonempty).as_array()) {
373 ++count;
374 }
375 BOOST_CHECK_EQUAL(count, 0);
376 count = 0;
377 // explicit indexes as range always return a one element range, whether they exist or not.
378 for([[maybe_unused]] const config& child : variable_access_const("tag1.tag2[5]", nonempty).as_array()) {
379 ++count;
380 }
381 BOOST_CHECK_EQUAL(count, 1);
382 }
383}
384
385BOOST_AUTO_TEST_CASE(add_child_EmptyThis_newKey_AppendAndReturnNewEmptyChild)
386{
387 config actual;
388 const config new_child = actual.add_child("A");
389 const config expected("A");
390 BOOST_CHECK_EQUAL(actual, expected);
391 BOOST_CHECK_EQUAL(new_child, config());
392}
393
394namespace bdata = boost::unit_test::data;
395BOOST_DATA_TEST_CASE(add_child_NonEmptyThis_newOrExistingKey_lOrRValue_AppendAndReturnNewChild,
396 bdata::make({"A", "B", "C"}) * bdata::make<std::string>({"lvalue_ref", "rvalue_ref"}),
397 key,
398 update_ref_kind) // 3 * 2 = 6 cases
399{
400 // Data for testing base.add_child(key, update)
401 const config base{"A", config(), "a", 1}; // [A][/A] a = 1
402 const config update{"B", config(), "b", 2}; // [B][/B] b = 2
403 // Expected config: [A][/A] a = 1 [key] [B][/B] b = 2 [/key]
404 /* clang-format off */
405 const config expected{
406 // base
407 "A", config(), "a", 1,
408 // [key] copy of update [/key]
409 key, config(update)};
410 /* clang-format on */
411 // Make actual
412 config actual(base);
413 config new_child;
414 if(update_ref_kind == std::string("lvalue_ref"))
415 new_child = actual.add_child(key, update);
416 else
417 new_child = actual.add_child(key, config(update)); // rvalue ref.
418
419 BOOST_CHECK_EQUAL(actual, expected);
420 BOOST_CHECK_EQUAL(new_child, update);
421 // Assert the new child is a copy of update
422 BOOST_CHECK_NE(&new_child, &update);
423}
424
425BOOST_AUTO_TEST_SUITE_END()
Variant for storing WML attributes.
static config_attribute_value create(const T val)
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
Additional functionality for a non-const variable_info.
Information on a WML variable.
maybe_const_t< config::attribute_value, V > & as_scalar() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
bool exists_as_container() const
maybe_const_t< config, V > & as_container() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
bool exists_as_attribute() const
Definitions for the interface to Wesnoth Markup Language (WML).
const config * cfg
BOOST_AUTO_TEST_CASE(test_config_attribute_value)
BOOST_DATA_TEST_CASE(add_child_NonEmptyThis_newOrExistingKey_lOrRValue_AppendAndReturnNewChild, bdata::make({"A", "B", "C"}) *bdata::make< std::string >({"lvalue_ref", "rvalue_ref"}), key, update_ref_kind)
BOOST_AUTO_TEST_SUITE(filesystem)
static map_location::direction s
variable_info< const variable_info_implementation::vi_policy_const > variable_access_const
Read-only access.
#define e
#define c