The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
test_formula_core.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2008 - 2025
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
21#include "formula/formula.hpp"
22#include "formula/callable.hpp"
23#include "formula/tokenizer.hpp"
24
25using namespace wfl;
26
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
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(std::move(members));
51 } else if(key == "char") {
52 return variant(c_.fake_ptr());
53 } else {
54 return variant(0);
55 }
56 }
57
60
61public:
63};
64
65BOOST_AUTO_TEST_SUITE(formula_core)
66
69
70BOOST_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
92BOOST_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
108BOOST_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
118BOOST_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
127BOOST_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
143BOOST_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
148BOOST_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
176BOOST_AUTO_TEST_CASE(test_formula_tokenizer) {
177 using namespace wfl::tokenizer;
178 const std::string test = "[(abc + 4 * (5+3))^2.0, functions, '[']thing[']']";
179 auto i1 = test.begin();
180 auto i2 = test.end();
181 const std::array<std::pair<std::string, token_type>, 25> tokens {{
182 {"[", token_type::lsquare},
183 {"(", token_type::lparens},
184 {"abc", token_type::identifier},
185 {" ", token_type::whitespace},
186 {"+", token_type::operator_token},
187 {" ", token_type::whitespace},
188 {"4", token_type::integer},
189 {" ", token_type::whitespace},
190 {"*", token_type::operator_token},
191 {" ", token_type::whitespace},
192 {"(", token_type::lparens},
193 {"5", token_type::integer},
194 {"+", token_type::operator_token},
195 {"3", token_type::integer},
196 {")", token_type::rparens},
197 {")", token_type::rparens},
198 {"^", token_type::operator_token},
199 {"2.0", token_type::decimal},
200 {",", token_type::comma},
201 {" ", token_type::whitespace},
202 {"functions", token_type::keyword},
203 {",", token_type::comma},
204 {" ", token_type::whitespace},
205 {"'[']thing[']'", token_type::string_literal},
206 {"]", token_type::rsquare},
207 }};
208 for(const auto& [str, type] : tokens) {
209 const token t = get_token(i1, i2);
210 assert(std::string(t.begin, t.end) == str);
211 assert(t.type == type);
212 }
213}
214
215BOOST_AUTO_TEST_SUITE_END()
double t
variant get_value(const std::string &key) const
map_formula_callable i_[3]
variant get_value(const std::string &key) const
formula_callable_ptr fake_ptr()
Definition callable.hpp:42
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition formula.hpp:48
map_formula_callable & add(const std::string &key, const variant &value)
Definition callable.hpp:249
int as_int(int fallback=0) const
Returns the variant's value as an integer.
Definition variant.cpp:336
std::size_t num_elements() const
Definition variant.cpp:312
contains the current text being parsed as well as the token_type of what's being parsed.
Definition tokenizer.hpp:41
BOOST_AUTO_TEST_SUITE(filesystem)
mock_char c
BOOST_AUTO_TEST_CASE(test_formula_basic_arithmetic)
mock_party p
static map_location::direction n