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