The Battle for Wesnoth  1.19.0-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  // blank != "" test.
218  c.clear();
219  BOOST_CHECK(cc["x"] != "");
220  BOOST_CHECK(cc["x"].empty());
221  BOOST_CHECK(cc["x"].blank());
222 
223  BOOST_CHECK(c["x"] != "");
224  BOOST_CHECK(c["x"].empty());
225  BOOST_CHECK(c["x"].blank());
226 
227  BOOST_CHECK_EQUAL(cc["x"], c["x"]);
228 
229  c["x"] = "";
230  BOOST_CHECK(cc["x"].empty());
231  BOOST_CHECK(cc["x"].empty());
232  BOOST_CHECK(!cc["x"].blank());
233 
234  BOOST_CHECK(c["x"].empty());
235  BOOST_CHECK(c["x"].empty());
236  BOOST_CHECK(!c["x"].blank());
237 
238  BOOST_CHECK_EQUAL(cc["x"], c["x"]);
239 }
240 
241 BOOST_AUTO_TEST_CASE(test_variable_info)
242 {
243  config c;
244  {
245  variable_access_const access("", c);
246  // We dotn allow empty keys
247  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
248  }
249  {
250  variable_access_const access("some_non_existent.", c);
251  // We dotn allow empty keys
252  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
253  }
254  {
255  variable_access_const access("some_non_existent[0]value", c);
256  // We expect '.' after ']'
257  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
258  }
259  {
260  variable_access_const access("some_non_existent", c);
261  // we return empty be default
262  BOOST_CHECK(!access.exists_as_container());
263  BOOST_CHECK_EQUAL(access.as_container(), config());
264  BOOST_CHECK(!access.exists_as_attribute());
265  BOOST_CHECK_EQUAL(access.as_scalar(), config::attribute_value());
266  }
267  {
268  variable_access_const access("a.b[0].c[1].d.e.f[2]", c);
269  // we return empty be default
270  BOOST_CHECK(!access.exists_as_container());
271  BOOST_CHECK_EQUAL(access.as_container(), config());
272  // Explicit indexes can never be an attribute
273  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
274  }
275  BOOST_CHECK(c.empty());
276  {
277  config c2;
278  variable_access_create access("a.b[0].c[1].d.e.f[2].g", c2);
279  access.as_scalar() = 84;
280  BOOST_CHECK_EQUAL(variable_access_const("a.length", c2).as_scalar(), 1);
281  BOOST_CHECK_EQUAL(variable_access_const("a.b.length", c2).as_scalar(), 1);
282  BOOST_CHECK_EQUAL(variable_access_const("a.b.c.length", c2).as_scalar(), 2);
283  BOOST_CHECK_EQUAL(variable_access_const("a.b.c[1].d.e.f.length", c2).as_scalar(), 3);
284  // we set g as a scalar
285  BOOST_CHECK_EQUAL(variable_access_const("a.b.c[1].d.e.f[2].g.length", c2).as_scalar(), 0);
286  BOOST_CHECK_EQUAL(variable_access_const("a.b.c[1].d.e.f[2].g", c2).as_scalar(), 84);
287  }
288  {
289  config c2;
290  variable_access_throw access("a.b[9].c", c2);
291  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
292  }
293  {
294  /* clang-format off */
295  const config nonempty{
296  "tag1", config(),
297  "tag1", config{
298  "tag2", config(),
299  "tag2", config(),
300  "tag2", config{
301  "atribute1", 88,
302  "atribute2", "value",
303  },
304  },
305  "tag1", config(),
306  };
307  /* clang-format on */
308  /** This is the config:
309  [tag1]
310  [/tag1]
311  [tag1]
312  [tag2]
313  [/tag2]
314  [tag2]
315  [/tag2]
316  [tag2]
317  atribute1 = 88
318  atribute2 = "value"
319  [/tag2]
320  [/tag1]
321  [tag1]
322  [/tag1]
323  */
324  BOOST_CHECK_EQUAL(variable_access_const("tag1.length", nonempty).as_scalar(), 3);
325  BOOST_CHECK_EQUAL(variable_access_const("tag1.tag2.length", nonempty).as_scalar(), 0);
326  BOOST_CHECK_EQUAL(variable_access_const("tag1[1].tag2.length", nonempty).as_scalar(), 3);
327  BOOST_CHECK_EQUAL(variable_access_const("tag1[1].tag2[2].atribute1", nonempty).as_scalar().to_int(), 88);
328  int count = 0;
329  for([[maybe_unused]] const config& child : variable_access_const("tag1", nonempty).as_array()) {
330  ++count;
331  }
332  BOOST_CHECK_EQUAL(count, 3);
333  count = 0;
334  for([[maybe_unused]] const config& child : variable_access_const("tag1.tag2", nonempty).as_array()) {
335  ++count;
336  }
337  BOOST_CHECK_EQUAL(count, 0);
338  count = 0;
339  // explicit indexes as range always return a one element range, whether they exist or not.
340  for([[maybe_unused]] const config& child : variable_access_const("tag1.tag2[5]", nonempty).as_array()) {
341  ++count;
342  }
343  BOOST_CHECK_EQUAL(count, 1);
344  }
345 }
346 
347 BOOST_AUTO_TEST_CASE(add_child_EmptyThis_newKey_AppendAndReturnNewEmptyChild)
348 {
349  config actual;
350  const config new_child = actual.add_child("A");
351  const config expected("A");
352  BOOST_CHECK_EQUAL(actual, expected);
353  BOOST_CHECK_EQUAL(new_child, config());
354 }
355 
356 namespace bdata = boost::unit_test::data;
357 BOOST_DATA_TEST_CASE(add_child_NonEmptyThis_newOrExistingKey_lOrRValue_AppendAndReturnNewChild,
358  bdata::make({"A", "B", "C"}) * bdata::make<std::string>({"lvalue_ref", "rvalue_ref"}),
359  key,
360  update_ref_kind) // 3 * 2 = 6 cases
361 {
362  // Data for testing base.add_child(key, update)
363  const config base{"A", config(), "a", 1}; // [A][/A] a = 1
364  const config update{"B", config(), "b", 2}; // [B][/B] b = 2
365  // Expected config: [A][/A] a = 1 [key] [B][/B] b = 2 [/key]
366  /* clang-format off */
367  const config expected{
368  // base
369  "A", config(), "a", 1,
370  // [key] copy of update [/key]
371  key, config(update)};
372  /* clang-format on */
373  // Make actual
374  config actual(base);
375  config new_child;
376  if(update_ref_kind == std::string("lvalue_ref"))
377  new_child = actual.add_child(key, update);
378  else
379  new_child = actual.add_child(key, config(update)); // rvalue ref.
380 
381  BOOST_CHECK_EQUAL(actual, expected);
382  BOOST_CHECK_EQUAL(new_child, update);
383  // Assert the new child is a copy of update
384  BOOST_CHECK_NE(&new_child, &update);
385 }
386 
387 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:159
config & add_child(config_key_type key)
Definition: config.cpp:441
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
static void update()
std::string_view data
Definition: picture.cpp:194
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