The Battle for Wesnoth  1.17.0-dev
test_config.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2021
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  const config& cc = c;
33  int x_int;
34  std::string x_str;
35  long long x_sll;
36  double x_dbl;
37 
38  c["x"] = 1;
39  x_str = c["x"].str();
40  BOOST_CHECK_EQUAL(x_str, "1");
41  x_int = c["x"].to_int();
42  BOOST_CHECK_EQUAL(x_int, 1);
43  x_sll = c["x"].to_long_long();
44  BOOST_CHECK_EQUAL(x_sll, 1ll);
45  x_dbl = c["x"].to_double();
46  BOOST_CHECK_EQUAL(x_dbl, 1.0);
47 
48  c["x"] = 10000000;
49  x_int = c["x"].to_int();
50  BOOST_CHECK_EQUAL(x_int, 10000000);
51  x_str = c["x"].str();
52  BOOST_CHECK_EQUAL(x_str, "10000000");
53  x_sll = c["x"].to_long_long();
54  BOOST_CHECK_EQUAL(x_sll, 10000000ll);
55  x_dbl = c["x"].to_double();
56  BOOST_CHECK_EQUAL(x_dbl, 1e7);
57 
58  c["x"] = "";
59  x_sll = c["x"].to_long_long();
60  BOOST_CHECK_EQUAL(x_sll, 0ll);
61  x_str = c["x"].str();
62  BOOST_CHECK_EQUAL(x_str, "");
63  x_int = c["x"].to_int();
64  BOOST_CHECK_EQUAL(x_int, 0);
65  x_dbl = c["x"].to_double();
66  BOOST_CHECK_EQUAL(x_dbl, 0.0);
67 
68  c["x"] = "0x11";
69  x_int = c["x"].to_int();
70  BOOST_CHECK_EQUAL(x_int, 0);
71  x_str = c["x"].str();
72  BOOST_CHECK_EQUAL(x_str, "0x11");
73  x_sll = c["x"].to_long_long();
74  BOOST_CHECK_EQUAL(x_sll, 0ll);
75  x_dbl = c["x"].to_double();
76  BOOST_CHECK_EQUAL(x_dbl, 0.0);
77 
78  c["x"] = "0xab";
79  x_int = c["x"].to_int();
80  BOOST_CHECK_EQUAL(x_int, 0);
81  x_str = c["x"].str();
82  BOOST_CHECK_EQUAL(x_str, "0xab");
83  x_sll = c["x"].to_long_long();
84  BOOST_CHECK_EQUAL(x_sll, 0ll);
85  x_dbl = c["x"].to_double();
86  BOOST_CHECK_EQUAL(x_dbl, 0.0);
87 
88  c["x"] = "00001111";
89  x_int = c["x"].to_int();
90  BOOST_CHECK_EQUAL(x_int, 1111);
91  x_str = c["x"].str();
92  BOOST_CHECK_EQUAL(x_str, "00001111");
93  x_sll = c["x"].to_long_long();
94  BOOST_CHECK_EQUAL(x_sll, 1111ll);
95  x_dbl = c["x"].to_double();
96  BOOST_CHECK_EQUAL(x_dbl, 1.111e3);
97 
98  c["x"] = "000000";
99  x_int = c["x"].to_int();
100  BOOST_CHECK_EQUAL(x_int, 0);
101  x_str = c["x"].str();
102  BOOST_CHECK_EQUAL(x_str, "000000");
103  x_sll = c["x"].to_long_long();
104  BOOST_CHECK_EQUAL(x_sll, 0ll);
105  x_dbl = c["x"].to_double();
106  BOOST_CHECK_EQUAL(x_dbl, 0.0);
107 
108  c["x"] = "01234567890123456789";
109  x_sll = c["x"].to_long_long();
110  BOOST_CHECK_EQUAL(x_sll, 1234567890123456789ll);
111  x_str = c["x"].str();
112  BOOST_CHECK_EQUAL(x_str, "01234567890123456789");
113  x_int = c["x"].to_int();
114  BOOST_CHECK_EQUAL(x_int, 0);
115  x_dbl = c["x"].to_double();
116  BOOST_CHECK_EQUAL(x_dbl, 1.23456789012345678e18);
117 
118  c["x"] = "99999999999999999999";
119  x_sll = c["x"].to_long_long();
120  BOOST_CHECK_EQUAL(x_sll, 0ll);
121  x_str = c["x"].str();
122  BOOST_CHECK_EQUAL(x_str, "99999999999999999999");
123  x_int = c["x"].to_int();
124  BOOST_CHECK_EQUAL(x_int, 0);
125  x_dbl = c["x"].to_double();
126  BOOST_CHECK_EQUAL(x_dbl, 1e20);
127 
128  c["x"] = 1.499;
129  x_sll = c["x"].to_long_long();
130  BOOST_CHECK_EQUAL(x_sll, 1ll);
131  x_str = c["x"].str();
132  BOOST_CHECK_EQUAL(x_str, "1.499");
133  x_int = c["x"].to_int();
134  BOOST_CHECK_EQUAL(x_int, 1);
135  x_dbl = c["x"].to_double();
136  BOOST_CHECK(std::abs(x_dbl - 1.499) < 1e-6);
137 
138  c["x"] = 123456789123ll;
139  x_int = c["x"].to_int();
140  BOOST_CHECK_EQUAL(x_int, -1097262461);
141  x_dbl = c["x"].to_double();
142  BOOST_CHECK_EQUAL(x_dbl, 1.23456789123e11);
143  x_sll = c["x"].to_long_long();
144  BOOST_CHECK_EQUAL(x_sll, 123456789123ll);
145  x_str = c["x"].str();
146  BOOST_CHECK_EQUAL(x_str, "123456789123");
147 
148  // blank != "" test.
149  c.clear();
150  BOOST_CHECK(cc["x"] != "");
151  BOOST_CHECK(cc["x"].empty());
152  BOOST_CHECK(cc["x"].blank());
153 
154  BOOST_CHECK(c["x"] != "");
155  BOOST_CHECK(c["x"].empty());
156  BOOST_CHECK(c["x"].blank());
157 
158  BOOST_CHECK_EQUAL(cc["x"], c["x"]);
159 
160  c["x"] = "";
161  BOOST_CHECK(cc["x"].empty());
162  BOOST_CHECK(cc["x"].empty());
163  BOOST_CHECK(!cc["x"].blank());
164 
165  BOOST_CHECK(c["x"].empty());
166  BOOST_CHECK(c["x"].empty());
167  BOOST_CHECK(!c["x"].blank());
168 
169  BOOST_CHECK_EQUAL(cc["x"], c["x"]);
170 }
171 
172 BOOST_AUTO_TEST_CASE(test_variable_info)
173 {
174  config c;
175  {
176  variable_access_const access("", c);
177  // We dotn allow empty keys
178  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
179  }
180  {
181  variable_access_const access("some_non_existent.", c);
182  // We dotn allow empty keys
183  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
184  }
185  {
186  variable_access_const access("some_non_existent[0]value", c);
187  // We expect '.' after ']'
188  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
189  }
190  {
191  variable_access_const access("some_non_existent", c);
192  // we return empty be default
193  BOOST_CHECK(!access.exists_as_container());
194  BOOST_CHECK_EQUAL(access.as_container(), config());
195  BOOST_CHECK(!access.exists_as_attribute());
196  BOOST_CHECK_EQUAL(access.as_scalar(), config::attribute_value());
197  }
198  {
199  variable_access_const access("a.b[0].c[1].d.e.f[2]", c);
200  // we return empty be default
201  BOOST_CHECK(!access.exists_as_container());
202  BOOST_CHECK_EQUAL(access.as_container(), config());
203  // Explicit indexes can never be an attribute
204  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
205  }
206  BOOST_CHECK(c.empty());
207  {
208  config c2;
209  variable_access_create access("a.b[0].c[1].d.e.f[2].g", c2);
210  access.as_scalar() = 84;
211  BOOST_CHECK_EQUAL(variable_access_const("a.length", c2).as_scalar(), 1);
212  BOOST_CHECK_EQUAL(variable_access_const("a.b.length", c2).as_scalar(), 1);
213  BOOST_CHECK_EQUAL(variable_access_const("a.b.c.length", c2).as_scalar(), 2);
214  BOOST_CHECK_EQUAL(variable_access_const("a.b.c[1].d.e.f.length", c2).as_scalar(), 3);
215  // we set g as a scalar
216  BOOST_CHECK_EQUAL(variable_access_const("a.b.c[1].d.e.f[2].g.length", c2).as_scalar(), 0);
217  BOOST_CHECK_EQUAL(variable_access_const("a.b.c[1].d.e.f[2].g", c2).as_scalar(), 84);
218  }
219  {
220  config c2;
221  variable_access_throw access("a.b[9].c", c2);
222  BOOST_CHECK_THROW(access.as_scalar(), invalid_variablename_exception);
223  }
224  {
225  /* clang-format off */
226  const config nonempty{
227  "tag1", config(),
228  "tag1", config{
229  "tag2", config(),
230  "tag2", config(),
231  "tag2", config{
232  "atribute1", 88,
233  "atribute2", "value",
234  },
235  },
236  "tag1", config(),
237  };
238  /* clang-format on */
239  /** This is the config:
240  [tag1]
241  [/tag1]
242  [tag1]
243  [tag2]
244  [/tag2]
245  [tag2]
246  [/tag2]
247  [tag2]
248  atribute1 = 88
249  atribute2 = "value"
250  [/tag2]
251  [/tag1]
252  [tag1]
253  [/tag1]
254  */
255  BOOST_CHECK_EQUAL(variable_access_const("tag1.length", nonempty).as_scalar(), 3);
256  BOOST_CHECK_EQUAL(variable_access_const("tag1.tag2.length", nonempty).as_scalar(), 0);
257  BOOST_CHECK_EQUAL(variable_access_const("tag1[1].tag2.length", nonempty).as_scalar(), 3);
258  BOOST_CHECK_EQUAL(variable_access_const("tag1[1].tag2[2].atribute1", nonempty).as_scalar().to_int(), 88);
259  int count = 0;
260  for([[maybe_unused]] const config& child : variable_access_const("tag1", nonempty).as_array()) {
261  ++count;
262  }
263  BOOST_CHECK_EQUAL(count, 3);
264  count = 0;
265  for([[maybe_unused]] const config& child : variable_access_const("tag1.tag2", nonempty).as_array()) {
266  ++count;
267  }
268  BOOST_CHECK_EQUAL(count, 0);
269  count = 0;
270  // explicit indexes as range always return a one element range, whether they exist or not.
271  for([[maybe_unused]] const config& child : variable_access_const("tag1.tag2[5]", nonempty).as_array()) {
272  ++count;
273  }
274  BOOST_CHECK_EQUAL(count, 1);
275  }
276 }
277 
278 BOOST_AUTO_TEST_CASE(add_child_EmptyThis_newKey_AppendAndReturnNewEmptyChild)
279 {
280  config actual;
281  const config new_child = actual.add_child("A");
282  const config expected("A");
283  BOOST_CHECK_EQUAL(actual, expected);
284  BOOST_CHECK_EQUAL(new_child, config());
285 }
286 
287 namespace bdata = boost::unit_test::data;
288 BOOST_DATA_TEST_CASE(add_child_NonEmptyThis_newOrExistingKey_lOrRValue_AppendAndReturnNewChild,
289  bdata::make({"A", "B", "C"}) * bdata::make<std::string>({"lvalue_ref", "rvalue_ref"}),
290  key,
291  update_ref_kind) // 3 * 2 = 6 cases
292 {
293  // Data for testing base.add_child(key, update)
294  const config base{"A", config(), "a", 1}; // [A][/A] a = 1
295  const config update{"B", config(), "b", 2}; // [B][/B] b = 2
296  // Expected config: [A][/A] a = 1 [key] [B][/B] b = 2 [/key]
297  /* clang-format off */
298  const config expected{
299  // base
300  "A", config(), "a", 1,
301  // [key] copy of update [/key]
302  key, config(update)};
303  /* clang-format on */
304  // Make actual
305  config actual(base);
306  config new_child;
307  if(update_ref_kind == std::string("lvalue_ref"))
308  new_child = actual.add_child(key, update);
309  else
310  new_child = actual.add_child(key, config(update)); // rvalue ref.
311 
312  BOOST_CHECK_EQUAL(actual, expected);
313  BOOST_CHECK_EQUAL(new_child, update);
314  // Assert the new child is a copy of update
315  BOOST_CHECK_NE(&new_child, &update);
316 }
317 
318 BOOST_AUTO_TEST_SUITE_END()
bool exists_as_attribute() const
BOOST_AUTO_TEST_CASE(test_config_attribute_value)
Definition: test_config.cpp:29
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...
Additional functionality for a non-const variable_info.
maybe_const_t< config, V > & as_container() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
void clear()
Definition: config.cpp:920
Definitions for the interface to Wesnoth Markup Language (WML).
variable_info< const variable_info_implementation::vi_policy_const > variable_access_const
Read-only access.
BOOST_AUTO_TEST_SUITE(filesystem)
config & add_child(config_key_type key)
Definition: config.cpp:514
Information on a WML variable.
bool exists_as_container() const
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)
#define e
config_attribute_value attribute_value
Variant for storing WML attributes.
Definition: config.hpp:215
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
mock_char c
bool empty() const
Definition: config.cpp:941