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