1 |
85ad3d82
|
Assos Assos
|
<?php
|
2 |
|
|
|
3 |
|
|
/**
|
4 |
|
|
* Tests the MathExpression library of ctools.
|
5 |
|
|
*/
|
6 |
|
|
class CtoolsMathExpressionTestCase extends DrupalWebTestCase {
|
7 |
7e72b748
|
Assos Assos
|
|
8 |
|
|
/**
|
9 |
c304a780
|
Assos Assos
|
* {@inheritdoc}
|
10 |
7e72b748
|
Assos Assos
|
*/
|
11 |
85ad3d82
|
Assos Assos
|
public static function getInfo() {
|
12 |
|
|
return array(
|
13 |
7e72b748
|
Assos Assos
|
'name' => 'Math expressions',
|
14 |
85ad3d82
|
Assos Assos
|
'description' => 'Test the math expression library of ctools.',
|
15 |
1e39edcb
|
Assos Assos
|
'group' => 'ctools',
|
16 |
7e72b748
|
Assos Assos
|
'dependencies' => array('ctools'),
|
17 |
85ad3d82
|
Assos Assos
|
);
|
18 |
|
|
}
|
19 |
|
|
|
20 |
7e72b748
|
Assos Assos
|
/**
|
21 |
c304a780
|
Assos Assos
|
* {@inheritdoc}
|
22 |
7e72b748
|
Assos Assos
|
*/
|
23 |
c304a780
|
Assos Assos
|
public function setUp(array $modules = array()) {
|
24 |
7e72b748
|
Assos Assos
|
$modules[] = 'ctools';
|
25 |
|
|
$modules[] = 'ctools_plugin_test';
|
26 |
|
|
parent::setUp($modules);
|
27 |
85ad3d82
|
Assos Assos
|
}
|
28 |
|
|
|
29 |
|
|
/**
|
30 |
219d19c4
|
Assos Assos
|
* Return the sign of the numeric arg $n as an integer -1, 0, 1.
|
31 |
|
|
*
|
32 |
|
|
* Note: Not defined when $n is Infinity or NaN (or NULL or ...)!
|
33 |
|
|
*
|
34 |
|
|
* @param int|float $n
|
35 |
|
|
* The number to test.
|
36 |
|
|
*
|
37 |
|
|
* @return int
|
38 |
|
|
* -1 if the $n is negative, 0 if $n is zero or 1 if $n is positive.
|
39 |
|
|
*
|
40 |
|
|
* @see gmp_sign()
|
41 |
|
|
*/
|
42 |
|
|
protected static function sign($n) {
|
43 |
|
|
return ($n > 0) - ($n < 0);
|
44 |
|
|
}
|
45 |
|
|
|
46 |
|
|
/**
|
47 |
|
|
* Returns a random number between 0 and 1.
|
48 |
|
|
*
|
49 |
|
|
* @return float
|
50 |
|
|
* A random number between 0 and 1 inclusive.
|
51 |
85ad3d82
|
Assos Assos
|
*/
|
52 |
|
|
protected function rand01() {
|
53 |
219d19c4
|
Assos Assos
|
return mt_rand(0, PHP_INT_MAX) / PHP_INT_MAX;
|
54 |
85ad3d82
|
Assos Assos
|
}
|
55 |
|
|
|
56 |
|
|
/**
|
57 |
|
|
* A custom assertion with checks the values in a certain range.
|
58 |
219d19c4
|
Assos Assos
|
*
|
59 |
|
|
* @param float $first
|
60 |
|
|
* A value to check for equality.
|
61 |
|
|
* @param float $second
|
62 |
|
|
* A value to check for equality.
|
63 |
|
|
* @param string $message
|
64 |
|
|
* The message describing the correct behaviour, eg. "2/4 equals 1/2". The
|
65 |
|
|
* default message is used if this value is empty.
|
66 |
|
|
* @param float $delta
|
67 |
|
|
* The precision with which values must match. This accounts for rounding
|
68 |
|
|
* errors and imprecise representation errors in the floating point format.
|
69 |
|
|
* The value passed in should ideally be proportional to the values being
|
70 |
|
|
* compared.
|
71 |
|
|
* @param string $group
|
72 |
|
|
* Which group this assert belongs to.
|
73 |
|
|
*
|
74 |
|
|
* @return bool
|
75 |
|
|
* TRUE if the assertion was correct (that is, $first == $second within the
|
76 |
|
|
* given limits), FALSE otherwise.
|
77 |
85ad3d82
|
Assos Assos
|
*/
|
78 |
219d19c4
|
Assos Assos
|
protected function assertFloat($first, $second, $message = '', $delta = 0.00000001, $group = 'Other') {
|
79 |
|
|
// Check for NaN and Inf because the abs() and sign() code won't like those.
|
80 |
|
|
$equal = FALSE
|
81 |
|
|
// Equal if both an infinity.
|
82 |
|
|
|| (is_infinite($first) && is_infinite($second))
|
83 |
|
|
|
84 |
|
|
// Equal if both NaN.
|
85 |
|
|
|| (is_nan($first) && is_nan($second))
|
86 |
|
|
|
87 |
|
|
// Equal if same absolute value (within limits) and same sign.
|
88 |
|
|
|| ((abs($first - $second) <= $delta) && (self::sign($first) === self::sign($second)));
|
89 |
|
|
|
90 |
|
|
if (empty($message)) {
|
91 |
|
|
$default = t('Value !first is equal to value !second.',
|
92 |
|
|
array(
|
93 |
|
|
'!first' => var_export($first, TRUE),
|
94 |
|
|
'!second' => var_export($second, TRUE),
|
95 |
|
|
));
|
96 |
|
|
$message = $default;
|
97 |
|
|
}
|
98 |
|
|
|
99 |
|
|
return $this->assert($equal, $message, $group);
|
100 |
85ad3d82
|
Assos Assos
|
}
|
101 |
|
|
|
102 |
7e72b748
|
Assos Assos
|
/**
|
103 |
|
|
* Test some arithmetic handling.
|
104 |
|
|
*/
|
105 |
85ad3d82
|
Assos Assos
|
public function testArithmetic() {
|
106 |
219d19c4
|
Assos Assos
|
$math_expr = new ctools_math_expr();
|
107 |
|
|
|
108 |
|
|
$this->assertEqual($math_expr->evaluate('2'), 2, 'Check Literal 2');
|
109 |
|
|
|
110 |
|
|
$this->assertEqual($math_expr->e('2+1'), $math_expr->evaluate('2+1'), 'Check that e() and evaluate() are equivalent.');
|
111 |
|
|
|
112 |
|
|
foreach (range(1, 4) as $n) {
|
113 |
|
|
// Test constant expressions.
|
114 |
|
|
$random_number = mt_rand(0, 20);
|
115 |
|
|
$this->assertEqual($random_number, $math_expr->evaluate((string) $random_number), "Literal $random_number");
|
116 |
|
|
|
117 |
|
|
// Test simple arithmetic.
|
118 |
|
|
$number_a = mt_rand(-55, 777);
|
119 |
|
|
$number_b = mt_rand(-555, 77);
|
120 |
|
|
$this->assertEqual(
|
121 |
|
|
$number_a + $number_b,
|
122 |
|
|
$math_expr->evaluate("$number_a + $number_b"),
|
123 |
|
|
"Addition: $number_a + $number_b");
|
124 |
|
|
$this->assertEqual(
|
125 |
|
|
$number_a - $number_b,
|
126 |
|
|
$math_expr->evaluate("$number_a - $number_b"),
|
127 |
|
|
"Subtraction: $number_a + $number_b");
|
128 |
|
|
$this->assertFloat(
|
129 |
|
|
($number_a * $number_b),
|
130 |
|
|
$math_expr->evaluate("$number_a * $number_b"),
|
131 |
|
|
"Multiplication: $number_a * $number_b = " . ($number_a * $number_b));
|
132 |
|
|
$this->assertFloat(
|
133 |
|
|
($number_a / $number_b),
|
134 |
|
|
$math_expr->evaluate("$number_a / $number_b"),
|
135 |
|
|
"Division: $number_a / $number_b = " . ($number_a / $number_b));
|
136 |
|
|
|
137 |
|
|
// Test Associative property.
|
138 |
|
|
$number_c = mt_rand(-99, 77);
|
139 |
|
|
$this->assertEqual(
|
140 |
|
|
$math_expr->evaluate("$number_a + ($number_b + $number_c)"),
|
141 |
|
|
$math_expr->evaluate("($number_a + $number_b) + $number_c"),
|
142 |
|
|
"Associative: $number_a + ($number_b + $number_c)");
|
143 |
|
|
$this->assertEqual(
|
144 |
|
|
$math_expr->evaluate("$number_a * ($number_b * $number_c)"),
|
145 |
|
|
$math_expr->evaluate("($number_a * $number_b) * $number_c"),
|
146 |
|
|
"Associative: $number_a * ($number_b * $number_c)");
|
147 |
|
|
|
148 |
|
|
// Test Commutative property.
|
149 |
|
|
$this->assertEqual(
|
150 |
|
|
$math_expr->evaluate("$number_a + $number_b"),
|
151 |
|
|
$math_expr->evaluate("$number_b + $number_a"),
|
152 |
|
|
"Commutative: $number_a + $number_b");
|
153 |
|
|
$this->assertEqual(
|
154 |
|
|
$math_expr->evaluate("$number_a * $number_b"),
|
155 |
|
|
$math_expr->evaluate("$number_b * $number_a"),
|
156 |
|
|
"Commutative: $number_a * $number_b");
|
157 |
|
|
|
158 |
|
|
// Test Distributive property.
|
159 |
|
|
$this->assertEqual(
|
160 |
|
|
$math_expr->evaluate("($number_a + $number_b) * $number_c"),
|
161 |
|
|
$math_expr->evaluate("($number_a * $number_c + $number_b * $number_c)"),
|
162 |
|
|
"Distributive: ($number_a + $number_b) * $number_c");
|
163 |
|
|
|
164 |
|
|
// @todo: Doesn't work with zero or negative powers when number is zero or negative, e.g. 0^0, 0^-2, -2^0, -2^-2.
|
165 |
|
|
$random_number = mt_rand(1, 15);
|
166 |
|
|
$random_power = mt_rand(-15, 15);
|
167 |
|
|
|
168 |
|
|
$this->assertFloat(
|
169 |
|
|
pow($random_number, $random_power),
|
170 |
|
|
$math_expr->evaluate("$random_number ^ $random_power"),
|
171 |
|
|
"$random_number ^ $random_power");
|
172 |
|
|
|
173 |
|
|
$this->assertFloat(
|
174 |
|
|
pow($random_number, $random_power),
|
175 |
|
|
$math_expr->evaluate("pow($random_number, $random_power)"),
|
176 |
|
|
"pow($random_number, $random_power)");
|
177 |
|
|
}
|
178 |
85ad3d82
|
Assos Assos
|
}
|
179 |
|
|
|
180 |
7e72b748
|
Assos Assos
|
/**
|
181 |
219d19c4
|
Assos Assos
|
* Test various built-in transcendental and extended functions.
|
182 |
7e72b748
|
Assos Assos
|
*/
|
183 |
85ad3d82
|
Assos Assos
|
public function testBuildInFunctions() {
|
184 |
219d19c4
|
Assos Assos
|
$math_expr = new ctools_math_expr();
|
185 |
|
|
|
186 |
|
|
foreach (range(1, 4) as $n) {
|
187 |
|
|
$random_double = $this->rand01();
|
188 |
|
|
$random_int = mt_rand(-65535, 65535);
|
189 |
|
|
$this->assertFloat(sin($random_double), $math_expr->evaluate("sin($random_double)"), "sin($random_double)");
|
190 |
|
|
$this->assertFloat(cos($random_double), $math_expr->evaluate("cos($random_double)"), "cos($random_double)");
|
191 |
|
|
$this->assertFloat(tan($random_double), $math_expr->evaluate("tan($random_double)"), "tan($random_double)");
|
192 |
|
|
$this->assertFloat(exp($random_double), $math_expr->evaluate("exp($random_double)"), "exp($random_double)");
|
193 |
|
|
$this->assertFloat(sqrt($random_double), $math_expr->evaluate("sqrt($random_double)"), "sqrt($random_double)");
|
194 |
|
|
$this->assertFloat(log($random_double), $math_expr->evaluate("ln($random_double)"), "ln($random_double)");
|
195 |
|
|
$this->assertFloat(round($random_double), $math_expr->evaluate("round($random_double)"), "round($random_double)");
|
196 |
|
|
|
197 |
|
|
$random_real = $random_double + $random_int;
|
198 |
|
|
$this->assertFloat(abs($random_real), $math_expr->evaluate('abs(' . $random_real . ')'), "abs($random_real)");
|
199 |
|
|
$this->assertEqual(round($random_real), $math_expr->evaluate('round(' . $random_real . ')'), "round($random_real)");
|
200 |
|
|
$this->assertEqual(ceil($random_real), $math_expr->evaluate('ceil(' . $random_real . ')'), "ceil($random_real)");
|
201 |
|
|
$this->assertEqual(floor($random_real), $math_expr->evaluate('floor(' . $random_real . ')'), "floor($random_real)");
|
202 |
|
|
}
|
203 |
|
|
|
204 |
|
|
$this->assertFloat(time(), $math_expr->evaluate('time()'), "time()");
|
205 |
85ad3d82
|
Assos Assos
|
|
206 |
|
|
$random_double_a = $this->rand01();
|
207 |
|
|
$random_double_b = $this->rand01();
|
208 |
219d19c4
|
Assos Assos
|
$this->assertFloat(
|
209 |
|
|
max($random_double_a, $random_double_b),
|
210 |
|
|
$math_expr->evaluate("max($random_double_a, $random_double_b)"),
|
211 |
|
|
"max($random_double_a, $random_double_b)");
|
212 |
|
|
$this->assertFloat(
|
213 |
|
|
min($random_double_a, $random_double_b),
|
214 |
|
|
$math_expr->evaluate("min($random_double_a, $random_double_b)"),
|
215 |
|
|
"min($random_double_a, $random_double_b)");
|
216 |
85ad3d82
|
Assos Assos
|
}
|
217 |
|
|
|
218 |
7e72b748
|
Assos Assos
|
/**
|
219 |
|
|
* Test variable handling.
|
220 |
|
|
*/
|
221 |
85ad3d82
|
Assos Assos
|
public function testVariables() {
|
222 |
219d19c4
|
Assos Assos
|
$math_expr = new ctools_math_expr();
|
223 |
|
|
|
224 |
|
|
// We should have a definition of pi:
|
225 |
|
|
$this->assertFloat(pi(), $math_expr->evaluate('pi'));
|
226 |
|
|
|
227 |
|
|
// And a definition of e:
|
228 |
|
|
$this->assertFloat(exp(1), $math_expr->evaluate('e'));
|
229 |
|
|
|
230 |
|
|
$number_a = 5;
|
231 |
|
|
$number_b = 10;
|
232 |
|
|
|
233 |
|
|
// Store the first number and use it on a calculation.
|
234 |
|
|
$math_expr->evaluate("var = $number_a");
|
235 |
|
|
$this->assertEqual($number_a + $number_b, $math_expr->evaluate("var + $number_b"));
|
236 |
85ad3d82
|
Assos Assos
|
|
237 |
219d19c4
|
Assos Assos
|
// Change the value and check the new value is used.
|
238 |
|
|
$math_expr->evaluate("var = $number_b");
|
239 |
|
|
$this->assertEqual(
|
240 |
|
|
$number_b + $number_b,
|
241 |
|
|
$math_expr->evaluate("var + $number_b"),
|
242 |
|
|
"var + $number_b");
|
243 |
85ad3d82
|
Assos Assos
|
|
244 |
219d19c4
|
Assos Assos
|
// Store another number and use it on a calculation.
|
245 |
|
|
$math_expr->evaluate("var = $number_a");
|
246 |
|
|
$math_expr->evaluate("newvar = $number_a");
|
247 |
|
|
|
248 |
|
|
$this->assertEqual(
|
249 |
|
|
$number_a + $number_a,
|
250 |
|
|
$math_expr->evaluate('var + newvar'),
|
251 |
|
|
'var + newvar');
|
252 |
|
|
|
253 |
|
|
$this->assertFloat(
|
254 |
|
|
$number_a / $number_b,
|
255 |
|
|
$math_expr->evaluate("var / $number_b"),
|
256 |
|
|
"var / $number_b");
|
257 |
85ad3d82
|
Assos Assos
|
}
|
258 |
|
|
|
259 |
7e72b748
|
Assos Assos
|
/**
|
260 |
|
|
* Test custom function handling.
|
261 |
|
|
*/
|
262 |
85ad3d82
|
Assos Assos
|
public function testCustomFunctions() {
|
263 |
219d19c4
|
Assos Assos
|
$math_expr = new ctools_math_expr();
|
264 |
85ad3d82
|
Assos Assos
|
|
265 |
219d19c4
|
Assos Assos
|
$number_a = mt_rand(5, 10);
|
266 |
|
|
$number_b = mt_rand(5, 10);
|
267 |
85ad3d82
|
Assos Assos
|
|
268 |
|
|
// Create a one-argument function.
|
269 |
219d19c4
|
Assos Assos
|
$math_expr->evaluate("f(x) = 2 * x");
|
270 |
|
|
$this->assertEqual($number_a * 2, $math_expr->evaluate("f($number_a)"));
|
271 |
|
|
$this->assertEqual($number_b * 2, $math_expr->evaluate("f($number_b)"));
|
272 |
85ad3d82
|
Assos Assos
|
|
273 |
|
|
// Create a two-argument function.
|
274 |
219d19c4
|
Assos Assos
|
$math_expr->evaluate("g(x, y) = 2 * x + y");
|
275 |
|
|
$this->assertEqual(
|
276 |
|
|
$number_a * 2 + $number_b,
|
277 |
|
|
$math_expr->evaluate("g($number_a, $number_b)"),
|
278 |
|
|
"g($number_a, $number_b)");
|
279 |
85ad3d82
|
Assos Assos
|
|
280 |
|
|
// Use a custom function in another function.
|
281 |
219d19c4
|
Assos Assos
|
$this->assertEqual(
|
282 |
|
|
($number_a * 2 + $number_b) * 2,
|
283 |
|
|
$math_expr->evaluate("f(g($number_a, $number_b))"),
|
284 |
|
|
"f(g($number_a, $number_b))");
|
285 |
|
|
}
|
286 |
|
|
|
287 |
|
|
/**
|
288 |
|
|
* Test conditional handling.
|
289 |
|
|
*/
|
290 |
|
|
public function testIf() {
|
291 |
|
|
$math_expr = new ctools_math_expr();
|
292 |
|
|
|
293 |
|
|
$number_a = mt_rand(1, 10);
|
294 |
|
|
$number_b = mt_rand(11, 20);
|
295 |
|
|
|
296 |
|
|
foreach (range(1, 4) as $n) {
|
297 |
|
|
// @todo: Doesn't work with negative numbers.
|
298 |
|
|
if ($n == 2 || $n == 4) {
|
299 |
|
|
//$number_a = -$number_a;
|
300 |
|
|
}
|
301 |
|
|
|
302 |
|
|
if ($n == 3 || $n == 4) {
|
303 |
|
|
//$number_b = -$number_b;
|
304 |
|
|
}
|
305 |
|
|
|
306 |
|
|
$this->assertEqual(
|
307 |
|
|
$number_a,
|
308 |
|
|
$math_expr->evaluate("if(1, $number_a, $number_b)"),
|
309 |
|
|
"if(1, $number_a, $number_b)");
|
310 |
|
|
|
311 |
|
|
$this->assertEqual(
|
312 |
|
|
$number_a,
|
313 |
|
|
$math_expr->evaluate("if(1, $number_a)",
|
314 |
|
|
"if(1, $number_a)"));
|
315 |
|
|
|
316 |
|
|
$this->assertEqual(
|
317 |
|
|
$number_b,
|
318 |
|
|
$math_expr->evaluate("if(0, $number_a, $number_b)"),
|
319 |
|
|
"if(0, $number_a, $number_b)");
|
320 |
|
|
|
321 |
|
|
// Also add an expression so ensure it's evaluated.
|
322 |
|
|
$this->assertEqual(
|
323 |
|
|
$number_b,
|
324 |
|
|
$math_expr->evaluate("if($number_a > $number_b, $number_a, $number_b)"),
|
325 |
|
|
"if($number_a > $number_b, $number_a, $number_b)");
|
326 |
|
|
|
327 |
|
|
$this->assertEqual(
|
328 |
|
|
$number_b,
|
329 |
|
|
$math_expr->evaluate("if($number_a < $number_b, $number_b, $number_a)"),
|
330 |
|
|
"if($number_a < $number_b, $number_b, $number_a)");
|
331 |
|
|
}
|
332 |
85ad3d82
|
Assos Assos
|
}
|
333 |
7e72b748
|
Assos Assos
|
|
334 |
85ad3d82
|
Assos Assos
|
} |