The Battle for Wesnoth  1.19.7+dev
test_formula_core.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
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 
19 #include <array>
20 #include <ctime>
21 
22 #include "formula/formula.hpp"
23 #include "formula/callable.hpp"
24 #include "formula/tokenizer.hpp"
25 
26 using namespace wfl;
27 
28 class mock_char : public formula_callable {
29  variant get_value(const std::string& key) const {
30  if(key == "strength") {
31  return variant(15);
32  } else if(key == "agility") {
33  return variant(12);
34  }
35 
36  return variant(10);
37  }
38 };
39 
40 class mock_party : public formula_callable {
41  variant get_value(const std::string& key) const {
42  if(key == "members") {
43  i_[0].add("strength",variant(12));
44  i_[1].add("strength",variant(16));
45  i_[2].add("strength",variant(14));
46  std::vector<variant> members;
47  for(int n = 0; n != 3; ++n) {
48  members.emplace_back(i_[n].fake_ptr());
49  }
50 
51  return variant(members);
52  } else if(key == "char") {
53  return variant(c_.fake_ptr());
54  } else {
55  return variant(0);
56  }
57  }
58 
60  mutable map_formula_callable i_[3];
61 
62 public:
64 };
65 
66 BOOST_AUTO_TEST_SUITE(formula_core)
67 
70 
71 BOOST_AUTO_TEST_CASE(test_formula_basic_arithmetic)
72 {
73  BOOST_CHECK_EQUAL(formula("strength").evaluate(c).as_int(), 15);
74  BOOST_CHECK_EQUAL(formula("17").evaluate().as_int(), 17);
75 
76  BOOST_CHECK_EQUAL(formula("strength/2 + agility").evaluate(c).as_int(), 19);
77  BOOST_CHECK_EQUAL(formula("(strength+agility)/2").evaluate(c).as_int(), 13);
78 
79  BOOST_CHECK_EQUAL(formula("20 % 3").evaluate().as_int(), 2);
80  BOOST_CHECK_EQUAL(formula("19.5 % 3").evaluate().as_decimal(),
81  static_cast<int>(1000.0 * 1.5));
82 
83  BOOST_CHECK_EQUAL(formula("-5").evaluate().as_int(), -5);
84 
85  BOOST_CHECK_EQUAL(formula("4^2").evaluate().as_int(), 16);
86  BOOST_CHECK_EQUAL(formula("2+3^3").evaluate().as_int(), 29);
87  BOOST_CHECK_EQUAL(formula("2*3^3+2").evaluate().as_int(), 56);
88  BOOST_CHECK_EQUAL(formula("2^3^2").evaluate().as_int(), 512);
89  BOOST_CHECK_EQUAL(formula("9^3").evaluate().as_int(), 729);
90  BOOST_CHECK(formula("(-2)^0.5").evaluate().is_null());
91 }
92 
93 BOOST_AUTO_TEST_CASE(test_formula_basic_logic)
94 {
95  BOOST_CHECK_EQUAL(formula("strength > 12").evaluate(c).as_int(), 1);
96  BOOST_CHECK_EQUAL(formula("strength > 18").evaluate(c).as_int(), 0);
97 
98  BOOST_CHECK_EQUAL(formula("if(strength > 12, 7, 2)").evaluate(c).as_int(), 7);
99  BOOST_CHECK_EQUAL(formula("if(strength > 18, 7, 2)").evaluate(c).as_int(), 2);
100 
101  BOOST_CHECK_EQUAL(formula("2 and 1").evaluate().as_int(), 1);
102  BOOST_CHECK_EQUAL(formula("2 and 0").evaluate().as_int(), 0);
103  BOOST_CHECK_EQUAL(formula("2 or 0").evaluate().as_int(), 2);
104 
105  BOOST_CHECK_EQUAL(formula("not 5").evaluate().as_int(), 0);
106  BOOST_CHECK_EQUAL(formula("not 0").evaluate().as_int(), 1);
107 }
108 
109 BOOST_AUTO_TEST_CASE(test_formula_callable)
110 {
111  // These are just misc tests that were in the original unit tests
112  // I wasn't sure how to classify them.
113  BOOST_CHECK_EQUAL(formula("char.strength").evaluate(p).as_int(), 15);
114  BOOST_CHECK_EQUAL(formula("choose(members,strength).strength").evaluate(p).as_int(), 16);
115 
116  BOOST_CHECK_EQUAL(formula("char.sum([strength, agility, intelligence])").evaluate(p).as_int(), 37);
117 }
118 
119 BOOST_AUTO_TEST_CASE(test_formula_where_clause)
120 {
121  BOOST_CHECK_EQUAL(formula("x*5 where x=1").evaluate().as_int(), 5);
122  BOOST_CHECK_EQUAL(formula("x*5 where x=2").evaluate().as_int(), 10);
123 
124  BOOST_CHECK_EQUAL(formula("x*(a*b where a=2,b=1) where x=5").evaluate().as_int(), 10);
125  BOOST_CHECK_EQUAL(formula("char.strength * ability where ability=3").evaluate(p).as_int(), 45);
126 }
127 
128 BOOST_AUTO_TEST_CASE(test_formula_strings)
129 {
130  BOOST_CHECK_EQUAL(formula("'abcd' = 'abcd'").evaluate().as_bool(), true);
131  BOOST_CHECK_EQUAL(formula("'abcd' = 'acd'").evaluate().as_bool(), false);
132 
133  BOOST_CHECK_EQUAL(formula("'ab' .. 'cd'").evaluate().as_string(), "abcd");
134 
135  BOOST_CHECK_EQUAL(formula("'strength, agility: [strength], [agility]'").evaluate(c).as_string(),
136  "strength, agility: 15, 12");
137 
138  BOOST_CHECK_EQUAL(formula("'String with [']quotes['] and [(]brackets[)]!'").evaluate().as_string(),
139  "String with 'quotes' and [brackets]!");
140  BOOST_CHECK_EQUAL(formula("'String with ['embedded ' .. 'string']!'").evaluate().as_string(),
141  "String with embedded string!");
142 }
143 
144 BOOST_AUTO_TEST_CASE(test_formula_dice) {
145  const int dice_roll = formula("3d6").evaluate().as_int();
146  assert(dice_roll >= 3 && dice_roll <= 18);
147 }
148 
149 BOOST_AUTO_TEST_CASE(test_formula_containers) {
150  variant myarray = formula("[1,2,3]").evaluate();
151  BOOST_CHECK_EQUAL(myarray.num_elements(), 3);
152  BOOST_CHECK_EQUAL(myarray[0].as_int(), 1);
153  BOOST_CHECK_EQUAL(myarray[1].as_int(), 2);
154  BOOST_CHECK_EQUAL(myarray[2].as_int(), 3);
155 
156  variant mydict = formula("['foo' -> 5, 'bar' ->7]").evaluate();
157  BOOST_CHECK_EQUAL(mydict.num_elements(), 2);
158  BOOST_CHECK_EQUAL(mydict[variant("foo")].as_int(), 5);
159  BOOST_CHECK_EQUAL(mydict[variant("bar")].as_int(), 7);
160 
161  variant myrange = formula("-2~2").evaluate();
162  BOOST_CHECK_EQUAL(myrange.num_elements(), 5);
163  BOOST_CHECK_EQUAL(myrange[0].as_int(), -2);
164  BOOST_CHECK_EQUAL(myrange[1].as_int(), -1);
165  BOOST_CHECK_EQUAL(myrange[2].as_int(), 0);
166  BOOST_CHECK_EQUAL(myrange[3].as_int(), 1);
167  BOOST_CHECK_EQUAL(myrange[4].as_int(), 2);
168 
169  variant myslice = formula("(10~20)[[1,3,7,9]]").evaluate();
170  BOOST_CHECK_EQUAL(myslice.num_elements(), 4);
171  BOOST_CHECK_EQUAL(myslice[0].as_int(), 11);
172  BOOST_CHECK_EQUAL(myslice[1].as_int(), 13);
173  BOOST_CHECK_EQUAL(myslice[2].as_int(), 17);
174  BOOST_CHECK_EQUAL(myslice[3].as_int(), 19);
175 }
176 
177 BOOST_AUTO_TEST_CASE(test_formula_tokenizer) {
178  using namespace wfl::tokenizer;
179  const std::string test = "[(abc + 4 * (5+3))^2.0, functions, '[']thing[']']";
180  auto i1 = test.begin();
181  auto i2 = test.end();
182  const std::array<std::pair<std::string, token_type>, 25> tokens {{
183  {"[", token_type::lsquare},
184  {"(", token_type::lparens},
185  {"abc", token_type::identifier},
186  {" ", token_type::whitespace},
188  {" ", token_type::whitespace},
189  {"4", token_type::integer},
190  {" ", token_type::whitespace},
192  {" ", token_type::whitespace},
193  {"(", token_type::lparens},
194  {"5", token_type::integer},
196  {"3", token_type::integer},
197  {")", token_type::rparens},
198  {")", token_type::rparens},
200  {"2.0", token_type::decimal},
201  {",", token_type::comma},
202  {" ", token_type::whitespace},
203  {"functions", token_type::keyword},
204  {",", token_type::comma},
205  {" ", token_type::whitespace},
206  {"'[']thing[']'", token_type::string_literal},
207  {"]", token_type::rsquare},
208  }};
209  for(const auto& [str, type] : tokens) {
210  const token t = get_token(i1, i2);
211  assert(std::string(t.begin, t.end) == str);
212  assert(t.type == type);
213  }
214 }
215 
216 BOOST_AUTO_TEST_SUITE_END()
double t
Definition: astarsearch.cpp:63
variant get_value(const std::string &key) const
variant get_value(const std::string &key) const
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:40
int as_int() const
Definition: variant.cpp:291
std::size_t num_elements() const
Definition: variant.cpp:267
token get_token(iterator &i1, const iterator i2)
Definition: tokenizer.cpp:44
Definition: contexts.hpp:43
BOOST_AUTO_TEST_SUITE(filesystem)
mock_char c
BOOST_AUTO_TEST_CASE(test_formula_basic_arithmetic)
mock_party p
static map_location::direction n