The Battle for Wesnoth  1.19.5+dev
test_config.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
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 
27 BOOST_AUTO_TEST_SUITE(test_config)
28 
29 BOOST_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  c["x"] = 987654321;
219  BOOST_CHECK_EQUAL(c["x"], 987654321);
220  c["x"] = "1";
221  BOOST_CHECK_EQUAL(c["x"], "1");
222  c["x"] = 222;
223  BOOST_CHECK_EQUAL(c["x"], "222");
224  c["x"] = "test";
225  BOOST_CHECK_EQUAL(c["x"], "test");
226  c["x"] = "33333";
227  BOOST_CHECK_EQUAL(c["x"], 33333);
228  c["x"] = "yes";
229  BOOST_CHECK_EQUAL(c["x"], true);
230  c["x"] = false;
231  BOOST_CHECK_EQUAL(c["x"], "no");
232  c["x"] = 1.23456789;
233  BOOST_CHECK_EQUAL(c["x"], 1.23456789);
234 #if 0 // FIXME: this should work. it doesn't work. looks like it's getting stored as 9.8765432099999995
235  c["x"] = "9.87654321";
236  BOOST_CHECK_EQUAL(c["x"], 9.87654321);
237 #endif
238  c["x"] = "sfvsdgdsfg";
239  BOOST_CHECK_NE(c["x"], 0);
240  BOOST_CHECK_NE(c["x"], true);
241  BOOST_CHECK_NE(c["x"], "a random string");
242 
243  // blank != "" test.
244  c.clear();
245  BOOST_CHECK(cc["x"] != "");
246  BOOST_CHECK(cc["x"].empty());
247  BOOST_CHECK(cc["x"].blank());
248 
249  BOOST_CHECK(c["x"] != "");
250  BOOST_CHECK(c["x"].empty());
251  BOOST_CHECK(c["x"].blank());
252 
253  BOOST_CHECK_EQUAL(cc["x"], c["x"]);
254 
255  c["x"] = "";
256  BOOST_CHECK(cc["x"].empty());
257  BOOST_CHECK(cc["x"].empty());
258  BOOST_CHECK(!cc["x"].blank());
259 
260  BOOST_CHECK(c["x"].empty());
261  BOOST_CHECK(c["x"].empty());
262  BOOST_CHECK(!c["x"].blank());
263 
264  BOOST_CHECK_EQUAL(cc["x"], c["x"]);
265 }
266 
267 BOOST_AUTO_TEST_CASE(test_variable_info)
268 {
269  config c;
270  {
271  variable_access_const access("", c);
272  // We dotn allow empty keys
273  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
274  }
275  {
276  variable_access_const access("some_non_existent.", c);
277  // We dotn allow empty keys
278  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
279  }
280  {
281  variable_access_const access("some_non_existent[0]value", c);
282  // We expect '.' after ']'
283  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
284  }
285  {
286  variable_access_const access("some_non_existent", c);
287  // we return empty be default
288  BOOST_CHECK(!access.exists_as_container());
289  BOOST_CHECK_EQUAL(access.as_container(), config());
290  BOOST_CHECK(!access.exists_as_attribute());
291  BOOST_CHECK_EQUAL(access.as_scalar(), config::attribute_value());
292  }
293  {
294  variable_access_const access("a.b[0].c[1].d.e.f[2]", c);
295  // we return empty be default
296  BOOST_CHECK(!access.exists_as_container());
297  BOOST_CHECK_EQUAL(access.as_container(), config());
298  // Explicit indexes can never be an attribute
299  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
300  }
301  BOOST_CHECK(c.empty());
302  {
303  config c2;
304  variable_access_create access("a.b[0].c[1].d.e.f[2].g", c2);
305  access.as_scalar() = 84;
306  BOOST_CHECK_EQUAL(variable_access_const("a.length", c2).as_scalar(), 1);
307  BOOST_CHECK_EQUAL(variable_access_const("a.b.length", c2).as_scalar(), 1);
308  BOOST_CHECK_EQUAL(variable_access_const("a.b.c.length", c2).as_scalar(), 2);
309  BOOST_CHECK_EQUAL(variable_access_const("a.b.c[1].d.e.f.length", c2).as_scalar(), 3);
310  // we set g as a scalar
311  BOOST_CHECK_EQUAL(variable_access_const("a.b.c[1].d.e.f[2].g.length", c2).as_scalar(), 0);
312  BOOST_CHECK_EQUAL(variable_access_const("a.b.c[1].d.e.f[2].g", c2).as_scalar(), 84);
313  }
314  {
315  config c2;
316  variable_access_throw access("a.b[9].c", c2);
317  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
318  }
319  {
320  /* clang-format off */
321  const config nonempty{
322  "tag1", config(),
323  "tag1", config{
324  "tag2", config(),
325  "tag2", config(),
326  "tag2", config{
327  "atribute1", 88,
328  "atribute2", "value",
329  },
330  },
331  "tag1", config(),
332  };
333  /* clang-format on */
334  /** This is the config:
335  [tag1]
336  [/tag1]
337  [tag1]
338  [tag2]
339  [/tag2]
340  [tag2]
341  [/tag2]
342  [tag2]
343  atribute1 = 88
344  atribute2 = "value"
345  [/tag2]
346  [/tag1]
347  [tag1]
348  [/tag1]
349  */
350  BOOST_CHECK_EQUAL(variable_access_const("tag1.length", nonempty).as_scalar(), 3);
351  BOOST_CHECK_EQUAL(variable_access_const("tag1.tag2.length", nonempty).as_scalar(), 0);
352  BOOST_CHECK_EQUAL(variable_access_const("tag1[1].tag2.length", nonempty).as_scalar(), 3);
353  BOOST_CHECK_EQUAL(variable_access_const("tag1[1].tag2[2].atribute1", nonempty).as_scalar().to_int(), 88);
354  int count = 0;
355  for([[maybe_unused]] const config& child : variable_access_const("tag1", nonempty).as_array()) {
356  ++count;
357  }
358  BOOST_CHECK_EQUAL(count, 3);
359  count = 0;
360  for([[maybe_unused]] const config& child : variable_access_const("tag1.tag2", nonempty).as_array()) {
361  ++count;
362  }
363  BOOST_CHECK_EQUAL(count, 0);
364  count = 0;
365  // explicit indexes as range always return a one element range, whether they exist or not.
366  for([[maybe_unused]] const config& child : variable_access_const("tag1.tag2[5]", nonempty).as_array()) {
367  ++count;
368  }
369  BOOST_CHECK_EQUAL(count, 1);
370  }
371 }
372 
373 BOOST_AUTO_TEST_CASE(add_child_EmptyThis_newKey_AppendAndReturnNewEmptyChild)
374 {
375  config actual;
376  const config new_child = actual.add_child("A");
377  const config expected("A");
378  BOOST_CHECK_EQUAL(actual, expected);
379  BOOST_CHECK_EQUAL(new_child, config());
380 }
381 
382 namespace bdata = boost::unit_test::data;
383 BOOST_DATA_TEST_CASE(add_child_NonEmptyThis_newOrExistingKey_lOrRValue_AppendAndReturnNewChild,
384  bdata::make({"A", "B", "C"}) * bdata::make<std::string>({"lvalue_ref", "rvalue_ref"}),
385  key,
386  update_ref_kind) // 3 * 2 = 6 cases
387 {
388  // Data for testing base.add_child(key, update)
389  const config base{"A", config(), "a", 1}; // [A][/A] a = 1
390  const config update{"B", config(), "b", 2}; // [B][/B] b = 2
391  // Expected config: [A][/A] a = 1 [key] [B][/B] b = 2 [/key]
392  /* clang-format off */
393  const config expected{
394  // base
395  "A", config(), "a", 1,
396  // [key] copy of update [/key]
397  key, config(update)};
398  /* clang-format on */
399  // Make actual
400  config actual(base);
401  config new_child;
402  if(update_ref_kind == std::string("lvalue_ref"))
403  new_child = actual.add_child(key, update);
404  else
405  new_child = actual.add_child(key, config(update)); // rvalue ref.
406 
407  BOOST_CHECK_EQUAL(actual, expected);
408  BOOST_CHECK_EQUAL(new_child, update);
409  // Assert the new child is a copy of update
410  BOOST_CHECK_NE(&new_child, &update);
411 }
412 
413 BOOST_AUTO_TEST_SUITE_END()
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
config & add_child(config_key_type key)
Definition: config.cpp:440
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).
static void update()
std::string_view data
Definition: picture.cpp:178
BOOST_AUTO_TEST_CASE(test_config_attribute_value)
Definition: test_config.cpp:29
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)
mock_char c
variable_info< const variable_info_implementation::vi_policy_const > variable_access_const
Read-only access.
#define e