2013-08-03 14:47:47 +04:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This file is part of the MathExecutor package
|
|
|
|
*
|
|
|
|
* (c) Alexander Kiryukhin
|
|
|
|
*
|
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace NXP\Tests;
|
|
|
|
|
2020-05-15 21:51:23 +03:00
|
|
|
use Exception;
|
2018-10-24 21:16:11 +03:00
|
|
|
use NXP\Exception\DivisionByZeroException;
|
|
|
|
use NXP\Exception\IncorrectExpressionException;
|
Support unlimited args for min, max default funcs. (#106)
* Support unlimited args for min, max default funcs.
Default functions max and min were requiring 2 arguments strictly. Now they supoort unlimited args, same as php's min, max funcs.
* Improved functions: support unlimited parameters (see min, max funcs), optional parameters (see round func), parameters with types (see round func, throws IncorrectFunctionParameterException on unmatched type, union types and intersection types not supported because of min php level! there is a todo for this, to support them later @see CustomFunction@execute) Also added unittests for improvements.
* Run php-cs-fixer fix
2022-05-13 15:55:52 +03:00
|
|
|
use NXP\Exception\IncorrectFunctionParameterException;
|
2020-06-04 18:43:16 +03:00
|
|
|
use NXP\Exception\IncorrectNumberOfFunctionParametersException;
|
2020-07-27 19:25:59 +03:00
|
|
|
use NXP\Exception\MathExecutorException;
|
2018-10-24 21:16:11 +03:00
|
|
|
use NXP\Exception\UnknownFunctionException;
|
2020-06-04 18:43:16 +03:00
|
|
|
use NXP\Exception\UnknownVariableException;
|
2020-05-15 21:51:23 +03:00
|
|
|
use NXP\MathExecutor;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
2013-08-03 14:47:47 +04:00
|
|
|
|
2020-05-15 21:51:23 +03:00
|
|
|
class MathTest extends TestCase
|
2013-08-03 14:47:47 +04:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @dataProvider providerExpressions
|
|
|
|
*/
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testCalculating($expression) : void
|
2013-08-03 14:47:47 +04:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
|
|
|
|
/** @var float $phpResult */
|
|
|
|
eval('$phpResult = ' . $expression . ';');
|
2022-04-27 00:31:50 +03:00
|
|
|
|
2020-05-15 21:51:23 +03:00
|
|
|
try {
|
|
|
|
$result = $calculator->execute($expression);
|
|
|
|
} catch (Exception $e) {
|
2022-04-27 00:31:50 +03:00
|
|
|
$this->fail(\sprintf('Exception: %s (%s:%d), expression was: %s', \get_class($e), $e->getFile(), $e->getLine(), $expression));
|
2020-05-15 21:51:23 +03:00
|
|
|
}
|
2022-04-27 00:31:50 +03:00
|
|
|
$this->assertEquals($phpResult, $result, "Expression was: {$expression}");
|
2013-08-03 14:47:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Expressions data provider
|
2020-05-15 21:51:23 +03:00
|
|
|
*
|
|
|
|
* Most tests can go in here. The idea is that each expression will be evaluated by MathExecutor and by PHP with eval.
|
|
|
|
* The results should be the same. If they are not, then the test fails. No need to add extra test unless you are doing
|
2020-09-16 04:27:43 +03:00
|
|
|
* something more complex and not a simple mathmatical expression.
|
2013-08-03 14:47:47 +04:00
|
|
|
*/
|
|
|
|
public function providerExpressions()
|
|
|
|
{
|
2018-09-12 18:55:31 +03:00
|
|
|
return [
|
2022-04-27 00:31:50 +03:00
|
|
|
['-5'],
|
|
|
|
['-5+10'],
|
|
|
|
['4-5'],
|
|
|
|
['4 -5'],
|
|
|
|
['(4*2)-5'],
|
|
|
|
['(4*2) - 5'],
|
|
|
|
['4*-5'],
|
|
|
|
['4 * -5'],
|
|
|
|
['+5'],
|
|
|
|
['+(3+2)'],
|
|
|
|
['+(+3+2)'],
|
|
|
|
['+(-3+2)'],
|
|
|
|
['-5'],
|
|
|
|
['-(-5)'],
|
|
|
|
['-(+5)'],
|
|
|
|
['+(-5)'],
|
|
|
|
['+(+5)'],
|
|
|
|
['-(3+2)'],
|
|
|
|
['-(-3+-2)'],
|
|
|
|
|
|
|
|
['abs(1.5)'],
|
|
|
|
['acos(0.15)'],
|
|
|
|
['acosh(1.5)'],
|
|
|
|
['asin(0.15)'],
|
|
|
|
['atan(0.15)'],
|
|
|
|
['atan2(1.5, 3.5)'],
|
|
|
|
['atanh(0.15)'],
|
|
|
|
['bindec("10101")'],
|
|
|
|
['ceil(1.5)'],
|
|
|
|
['cos(1.5)'],
|
|
|
|
['cosh(1.5)'],
|
|
|
|
['decbin("15")'],
|
|
|
|
['dechex("15")'],
|
|
|
|
['decoct("15")'],
|
|
|
|
['deg2rad(1.5)'],
|
|
|
|
['exp(1.5)'],
|
|
|
|
['expm1(1.5)'],
|
|
|
|
['floor(1.5)'],
|
|
|
|
['fmod(1.5, 3.5)'],
|
|
|
|
['hexdec("abcdef")'],
|
|
|
|
['hypot(1.5, 3.5)'],
|
|
|
|
['intdiv(10, 2)'],
|
|
|
|
['log(1.5)'],
|
|
|
|
['log10(1.5)'],
|
|
|
|
['log1p(1.5)'],
|
|
|
|
['max(1.5, 3.5)'],
|
|
|
|
['min(1.5, 3.5)'],
|
|
|
|
['octdec("15")'],
|
|
|
|
['pi()'],
|
|
|
|
['pow(1.5, 3.5)'],
|
|
|
|
['rad2deg(1.5)'],
|
|
|
|
['round(1.5)'],
|
|
|
|
['sin(1.5)'],
|
|
|
|
['sin(12)'],
|
|
|
|
['+sin(12)'],
|
|
|
|
['-sin(12)'],
|
|
|
|
['sinh(1.5)'],
|
|
|
|
['sqrt(1.5)'],
|
|
|
|
['tan(1.5)'],
|
|
|
|
['tanh(1.5)'],
|
|
|
|
|
|
|
|
['0.1 + 0.2'],
|
|
|
|
['1 + 2'],
|
|
|
|
|
|
|
|
['0.1 - 0.2'],
|
|
|
|
['1 - 2'],
|
|
|
|
|
|
|
|
['0.1 * 2'],
|
|
|
|
['1 * 2'],
|
|
|
|
|
|
|
|
['0.1 / 0.2'],
|
|
|
|
['1 / 2'],
|
|
|
|
|
|
|
|
['2 * 2 + 3 * 3'],
|
|
|
|
['2 * 2 / 3 * 3'],
|
|
|
|
['2 / 2 / 3 / 3'],
|
|
|
|
['2 / 2 * 3 / 3'],
|
|
|
|
['2 / 2 * 3 * 3'],
|
|
|
|
|
|
|
|
['1 + 0.6 - 3 * 2 / 50'],
|
|
|
|
|
|
|
|
['(5 + 3) * -1'],
|
|
|
|
|
|
|
|
['-2- 2*2'],
|
|
|
|
['2- 2*2'],
|
|
|
|
['2-(2*2)'],
|
|
|
|
['(2- 2)*2'],
|
|
|
|
['2 + 2*2'],
|
|
|
|
['2+ 2*2'],
|
|
|
|
['2+2*2'],
|
|
|
|
['(2+2)*2'],
|
|
|
|
['(2 + 2)*-2'],
|
|
|
|
['(2+-2)*2'],
|
|
|
|
|
|
|
|
['1 + 2 * 3 / (min(1, 5) + 2 + 1)'],
|
|
|
|
['1 + 2 * 3 / (min(1, 5) - 2 + 5)'],
|
|
|
|
['1 + 2 * 3 / (min(1, 5) * 2 + 1)'],
|
|
|
|
['1 + 2 * 3 / (min(1, 5) / 2 + 1)'],
|
|
|
|
['1 + 2 * 3 / (min(1, 5) / 2 * 1)'],
|
|
|
|
['1 + 2 * 3 / (min(1, 5) / 2 / 1)'],
|
|
|
|
['1 + 2 * 3 / (3 + min(1, 5) + 2 + 1)'],
|
|
|
|
['1 + 2 * 3 / (3 - min(1, 5) - 2 + 1)'],
|
|
|
|
['1 + 2 * 3 / (3 * min(1, 5) * 2 + 1)'],
|
|
|
|
['1 + 2 * 3 / (3 / min(1, 5) / 2 + 1)'],
|
|
|
|
|
|
|
|
['(1 + 2) * 3 / (3 / min(1, 5) / 2 + 1)'],
|
|
|
|
|
|
|
|
['sin(10) * cos(50) / min(10, 20/2)'],
|
|
|
|
['sin(10) * cos(50) / min(10, (20/2))'],
|
|
|
|
['sin(10) * cos(50) / min(10, (max(10,20)/2))'],
|
|
|
|
|
|
|
|
['100500 * 3.5e5'],
|
|
|
|
['100500 * 3.5e-5'],
|
|
|
|
['100500 * 3.5E5'],
|
|
|
|
['100500 * 3.5E-5'],
|
|
|
|
|
|
|
|
['1 + "2" / 3'],
|
|
|
|
["1.5 + '2.5' / 4"],
|
|
|
|
['1.5 + "2.5" * ".5"'],
|
|
|
|
|
|
|
|
['-1 + -2'],
|
|
|
|
['-1+-2'],
|
|
|
|
['-1- -2'],
|
|
|
|
['-1/-2'],
|
|
|
|
['-1*-2'],
|
|
|
|
|
|
|
|
['(1+2+3+4-5)*7/100'],
|
|
|
|
['(-1+2+3+4- 5)*7/100'],
|
|
|
|
['(1+2+3+4- 5)*7/100'],
|
|
|
|
['( 1 + 2 + 3 + 4 - 5 ) * 7 / 100'],
|
|
|
|
|
|
|
|
['1 && 0'],
|
|
|
|
['1 && 0 && 1'],
|
|
|
|
['1 || 0'],
|
|
|
|
['1 && 0 || 1'],
|
|
|
|
|
|
|
|
['5 == 3'],
|
|
|
|
['5 == 5'],
|
|
|
|
['5 != 3'],
|
|
|
|
['5 != 5'],
|
|
|
|
['5 > 3'],
|
|
|
|
['3 > 5'],
|
|
|
|
['3 >= 5'],
|
|
|
|
['3 >= 3'],
|
|
|
|
['3 < 5'],
|
|
|
|
['5 < 3'],
|
|
|
|
['3 <= 5'],
|
|
|
|
['5 <= 5'],
|
|
|
|
['10 < 9 || 4 > (2+1)'],
|
|
|
|
['10 < 9 || 4 > (-2+1)'],
|
|
|
|
['10 < 9 || 4 > (2+1) && 5 == 5 || 4 != 6 || 3 >= 4 || 3 <= 7'],
|
|
|
|
|
|
|
|
['1 + 5 == 3 + 1'],
|
|
|
|
['1 + 5 == 5 + 1'],
|
|
|
|
['1 + 5 != 3 + 1'],
|
|
|
|
['1 + 5 != 5 + 1'],
|
|
|
|
['1 + 5 > 3 + 1'],
|
|
|
|
['1 + 3 > 5 + 1'],
|
|
|
|
['1 + 3 >= 5 + 1'],
|
|
|
|
['1 + 3 >= 3 + 1'],
|
|
|
|
['1 + 3 < 5 + 1'],
|
|
|
|
['1 + 5 < 3 + 1'],
|
|
|
|
['1 + 3 <= 5 + 1'],
|
|
|
|
['1 + 5 <= 5 + 1'],
|
|
|
|
|
|
|
|
['(-4)'],
|
|
|
|
['(-4 + 5)'],
|
|
|
|
['(3 * 1)'],
|
|
|
|
['(-3 * -1)'],
|
|
|
|
['1 + (-3 * -1)'],
|
|
|
|
['1 + ( -3 * 1)'],
|
|
|
|
['1 + (3 *-1)'],
|
|
|
|
['1 - 0'],
|
|
|
|
['1-0'],
|
|
|
|
|
|
|
|
['-(1.5)'],
|
|
|
|
['-log(4)'],
|
|
|
|
['0-acosh(1.5)'],
|
|
|
|
['-acosh(1.5)'],
|
|
|
|
['-(-4)'],
|
|
|
|
['-(-4 + 5)'],
|
|
|
|
['-(3 * 1)'],
|
|
|
|
['-(-3 * -1)'],
|
|
|
|
['-1 + (-3 * -1)'],
|
|
|
|
['-1 + ( -3 * 1)'],
|
|
|
|
['-1 + (3 *-1)'],
|
|
|
|
['-1 - 0'],
|
|
|
|
['-1-0'],
|
|
|
|
['-(4*2)-5'],
|
|
|
|
['-(4*-2)-5'],
|
|
|
|
['-(-4*2) - 5'],
|
|
|
|
['-4*-5'],
|
Support unlimited args for min, max default funcs. (#106)
* Support unlimited args for min, max default funcs.
Default functions max and min were requiring 2 arguments strictly. Now they supoort unlimited args, same as php's min, max funcs.
* Improved functions: support unlimited parameters (see min, max funcs), optional parameters (see round func), parameters with types (see round func, throws IncorrectFunctionParameterException on unmatched type, union types and intersection types not supported because of min php level! there is a todo for this, to support them later @see CustomFunction@execute) Also added unittests for improvements.
* Run php-cs-fixer fix
2022-05-13 15:55:52 +03:00
|
|
|
['max(1,2,4.9,3)']
|
2018-09-12 18:55:31 +03:00
|
|
|
];
|
2013-08-03 14:47:47 +04:00
|
|
|
}
|
2013-09-18 20:34:51 +04:00
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testUnknownFunctionException() : void
|
2018-10-30 23:16:01 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->expectException(UnknownFunctionException::class);
|
|
|
|
$calculator->execute('1 * fred("wilma") + 3');
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testIncorrectExpressionException() : void
|
2018-10-30 23:16:01 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->expectException(IncorrectExpressionException::class);
|
|
|
|
$calculator->execute('1 * + ');
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testZeroDivision() : void
|
2018-10-30 23:16:01 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2020-05-20 05:36:55 +03:00
|
|
|
$calculator->setDivisionByZeroIsZero();
|
2019-01-12 07:48:43 +03:00
|
|
|
$this->assertEquals(0, $calculator->execute('10 / 0'));
|
2018-10-30 23:16:01 +03:00
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testUnaryOperators() : void
|
2021-07-14 05:12:41 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->assertEquals(5, $calculator->execute('+5'));
|
|
|
|
$this->assertEquals(5, $calculator->execute('+(3+2)'));
|
|
|
|
$this->assertEquals(-5, $calculator->execute('-5'));
|
|
|
|
$this->assertEquals(5, $calculator->execute('-(-5)'));
|
|
|
|
$this->assertEquals(-5, $calculator->execute('+(-5)'));
|
|
|
|
$this->assertEquals(-5, $calculator->execute('-(3+2)'));
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testZeroDivisionException() : void
|
2018-10-30 23:16:01 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->expectException(DivisionByZeroException::class);
|
|
|
|
$calculator->execute('10 / 0');
|
2020-09-15 03:47:26 +03:00
|
|
|
$calculator->setVar('one', 1)->setVar('zero', 0);
|
|
|
|
$this->assertEquals(0.0, $calculator->execute('$one / $zero'));
|
2020-06-04 18:43:16 +03:00
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testVariableIncorrectExpressionException() : void
|
2020-06-04 18:43:16 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$calculator->setVar('four', 4);
|
|
|
|
$this->assertEquals(4, $calculator->execute('$four'));
|
|
|
|
$this->expectException(IncorrectExpressionException::class);
|
|
|
|
$this->assertEquals(0.0, $calculator->execute('$'));
|
|
|
|
$this->assertEquals(0.0, $calculator->execute('$ + $four'));
|
2018-10-30 23:16:01 +03:00
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testExponentiation() : void
|
2018-10-30 23:16:01 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2019-01-12 07:48:43 +03:00
|
|
|
$this->assertEquals(100, $calculator->execute('10 ^ 2'));
|
2018-10-30 23:16:01 +03:00
|
|
|
}
|
|
|
|
|
2022-05-17 00:57:37 +03:00
|
|
|
public function testArrays() : void
|
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->assertEquals([1, 5, 2], $calculator->execute('array(1, 5, 2)'));
|
|
|
|
$this->assertEquals([1, 5, 2], $calculator->execute('[1, 5, 2]'));
|
|
|
|
$this->assertEquals(\max([1, 5, 2]), $calculator->execute('max([1, 5, 2])'));
|
|
|
|
$this->assertEquals(\max([1, 5, 2]), $calculator->execute('max(array(1, 5, 2))'));
|
|
|
|
$calculator->addFunction('arr_with_max_elements', static function($arg1, ...$args) {
|
|
|
|
$args = \is_array($arg1) ? $arg1 : [$arg1, ...$args];
|
|
|
|
\usort($args, static fn($arr1, $arr2) => \count($arr2) <=> \count($arr1));
|
|
|
|
|
|
|
|
return $args[0];
|
|
|
|
});
|
|
|
|
$this->assertEquals([3, 3, 3], $calculator->execute('arr_with_max_elements([[1],array(2,2),[3,3,3]])'));
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testFunctionParameterOrder() : void
|
2019-01-16 04:04:16 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
$calculator->addFunction('concat', static function($arg1, $arg2) {
|
2020-05-15 21:51:23 +03:00
|
|
|
return $arg1 . $arg2;
|
|
|
|
});
|
2019-01-16 04:04:16 +03:00
|
|
|
$this->assertEquals('testing', $calculator->execute('concat("test","ing")'));
|
|
|
|
$this->assertEquals('testing', $calculator->execute("concat('test','ing')"));
|
|
|
|
}
|
2019-01-12 07:48:43 +03:00
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testFunction() : void
|
2013-09-18 20:34:51 +04:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2022-04-27 00:31:50 +03:00
|
|
|
$calculator->addFunction('round', static function($arg) {
|
|
|
|
return \round($arg);
|
2020-05-15 21:51:23 +03:00
|
|
|
});
|
2022-04-27 00:31:50 +03:00
|
|
|
$this->assertEquals(\round(100 / 30), $calculator->execute('round(100/30)'));
|
2013-09-18 20:34:51 +04:00
|
|
|
}
|
2019-01-11 04:18:10 +03:00
|
|
|
|
Support unlimited args for min, max default funcs. (#106)
* Support unlimited args for min, max default funcs.
Default functions max and min were requiring 2 arguments strictly. Now they supoort unlimited args, same as php's min, max funcs.
* Improved functions: support unlimited parameters (see min, max funcs), optional parameters (see round func), parameters with types (see round func, throws IncorrectFunctionParameterException on unmatched type, union types and intersection types not supported because of min php level! there is a todo for this, to support them later @see CustomFunction@execute) Also added unittests for improvements.
* Run php-cs-fixer fix
2022-05-13 15:55:52 +03:00
|
|
|
public function testFunctionUnlimitedParameters() : void
|
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2022-05-16 18:12:36 +03:00
|
|
|
$calculator->addFunction('give_me_an_array', static function() {
|
|
|
|
return [5, 3, 7, 9, 8];
|
Support unlimited args for min, max default funcs. (#106)
* Support unlimited args for min, max default funcs.
Default functions max and min were requiring 2 arguments strictly. Now they supoort unlimited args, same as php's min, max funcs.
* Improved functions: support unlimited parameters (see min, max funcs), optional parameters (see round func), parameters with types (see round func, throws IncorrectFunctionParameterException on unmatched type, union types and intersection types not supported because of min php level! there is a todo for this, to support them later @see CustomFunction@execute) Also added unittests for improvements.
* Run php-cs-fixer fix
2022-05-13 15:55:52 +03:00
|
|
|
});
|
2022-05-16 18:12:36 +03:00
|
|
|
$calculator->addFunction('my_avarage', static function($arg1, ...$args) {
|
|
|
|
if (\is_array($arg1)){
|
|
|
|
return \array_sum($arg1) / \count($arg1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (0 === \count($args)){
|
|
|
|
throw new IncorrectNumberOfFunctionParametersException();
|
|
|
|
}
|
|
|
|
$args = [$arg1, ...$args];
|
|
|
|
|
|
|
|
return \array_sum($args) / \count($args);
|
|
|
|
});
|
|
|
|
$this->assertEquals(10, $calculator->execute('my_avarage(12,8,15,5)'));
|
|
|
|
$this->assertEquals(6.4, $calculator->execute('my_avarage(give_me_an_array())'));
|
|
|
|
$this->assertEquals(3, $calculator->execute('min(give_me_an_array())'));
|
|
|
|
$this->assertEquals(1, $calculator->execute('min(1,2,3)'));
|
|
|
|
$this->assertEquals(9, $calculator->execute('max(give_me_an_array())'));
|
|
|
|
$this->assertEquals(3, $calculator->execute('max(1,2,3)'));
|
|
|
|
$calculator->setVar('monthly_salaries', [100, 200, 300]);
|
|
|
|
$this->assertEquals([100, 200, 300], $calculator->execute('$monthly_salaries'));
|
|
|
|
$this->assertEquals(\max([100, 200, 300]), $calculator->execute('max($monthly_salaries)'));
|
Support unlimited args for min, max default funcs. (#106)
* Support unlimited args for min, max default funcs.
Default functions max and min were requiring 2 arguments strictly. Now they supoort unlimited args, same as php's min, max funcs.
* Improved functions: support unlimited parameters (see min, max funcs), optional parameters (see round func), parameters with types (see round func, throws IncorrectFunctionParameterException on unmatched type, union types and intersection types not supported because of min php level! there is a todo for this, to support them later @see CustomFunction@execute) Also added unittests for improvements.
* Run php-cs-fixer fix
2022-05-13 15:55:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public function testFunctionOptionalParameters() : void
|
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$calculator->addFunction('round', static function($num, $precision = 0) {
|
|
|
|
return \round($num, $precision);
|
|
|
|
});
|
|
|
|
$this->assertEquals(\round(11.176), $calculator->execute('round(11.176)'));
|
|
|
|
$this->assertEquals(\round(11.176, 2), $calculator->execute('round(11.176,2)'));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testFunctionParameterTypes() : void
|
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->expectException(IncorrectFunctionParameterException::class);
|
|
|
|
$calculator->addFunction('myfunc', static function(string $name, int $age) {
|
|
|
|
return $name . $age;
|
|
|
|
});
|
|
|
|
$calculator->execute('myfunc(22, "John Doe")');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testFunctionIncorrectNumberOfParameters() : void
|
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->expectException(IncorrectNumberOfFunctionParametersException::class);
|
|
|
|
$calculator->addFunction('myfunc', static function($arg1, $arg2) {
|
|
|
|
return $arg1 + $arg2;
|
|
|
|
});
|
|
|
|
$calculator->execute('myfunc(1)');
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testFunctionIf() : void
|
Logicandcompare (#50)
* TokenFactory: allowing multicharacter tokens
* Added logical and compare operators: <, <=, >, >=, ==, !=, ||, &&
* Fixed operator priorities
* Error messages fixes
* Fixed operators priority
The priorities are assigned by following the php language standard
(https://www.php.net/manual/en/language.operators.precedence.php)
I've assigned precedence in steps of 10 units by following the linked page:
230 clone new
220 **
210 ++ -- ~ (int) (float) (string) (array) (object) (bool) @
200 instanceof
190 !
180 * / %
170 + - .
160 << >>
150 < <= > >=
140 == != === !== <> <=>
130 &
120 ^
110 |
100 &&
90 ||
80 ??
70 ? :
60 = += -= *= **= /= .= %= &= |= ^= <<= >>=
50 yield from
40 yield
30 and
20 xor
10 or
* Added if() function
* Cache key fix
There are cases where the cache key creation raised an error, for example
while evaluating the expression "if(cos(2), cos(2), 0)", because the
if() function was passing a float to the MathExecutor:execute() method.
2019-11-26 17:00:24 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->assertEquals(30, $calculator->execute(
|
2022-04-27 00:31:50 +03:00
|
|
|
'if(100 > 99, 30, 0)'
|
2020-06-01 03:50:39 +03:00
|
|
|
));
|
Logicandcompare (#50)
* TokenFactory: allowing multicharacter tokens
* Added logical and compare operators: <, <=, >, >=, ==, !=, ||, &&
* Fixed operator priorities
* Error messages fixes
* Fixed operators priority
The priorities are assigned by following the php language standard
(https://www.php.net/manual/en/language.operators.precedence.php)
I've assigned precedence in steps of 10 units by following the linked page:
230 clone new
220 **
210 ++ -- ~ (int) (float) (string) (array) (object) (bool) @
200 instanceof
190 !
180 * / %
170 + - .
160 << >>
150 < <= > >=
140 == != === !== <> <=>
130 &
120 ^
110 |
100 &&
90 ||
80 ??
70 ? :
60 = += -= *= **= /= .= %= &= |= ^= <<= >>=
50 yield from
40 yield
30 and
20 xor
10 or
* Added if() function
* Cache key fix
There are cases where the cache key creation raised an error, for example
while evaluating the expression "if(cos(2), cos(2), 0)", because the
if() function was passing a float to the MathExecutor:execute() method.
2019-11-26 17:00:24 +03:00
|
|
|
$this->assertEquals(0, $calculator->execute(
|
2022-04-27 00:31:50 +03:00
|
|
|
'if(100 < 99, 30, 0)'
|
2020-06-01 03:50:39 +03:00
|
|
|
));
|
Logicandcompare (#50)
* TokenFactory: allowing multicharacter tokens
* Added logical and compare operators: <, <=, >, >=, ==, !=, ||, &&
* Fixed operator priorities
* Error messages fixes
* Fixed operators priority
The priorities are assigned by following the php language standard
(https://www.php.net/manual/en/language.operators.precedence.php)
I've assigned precedence in steps of 10 units by following the linked page:
230 clone new
220 **
210 ++ -- ~ (int) (float) (string) (array) (object) (bool) @
200 instanceof
190 !
180 * / %
170 + - .
160 << >>
150 < <= > >=
140 == != === !== <> <=>
130 &
120 ^
110 |
100 &&
90 ||
80 ??
70 ? :
60 = += -= *= **= /= .= %= &= |= ^= <<= >>=
50 yield from
40 yield
30 and
20 xor
10 or
* Added if() function
* Cache key fix
There are cases where the cache key creation raised an error, for example
while evaluating the expression "if(cos(2), cos(2), 0)", because the
if() function was passing a float to the MathExecutor:execute() method.
2019-11-26 17:00:24 +03:00
|
|
|
$this->assertEquals(30, $calculator->execute(
|
2022-04-27 00:31:50 +03:00
|
|
|
'if(98 < 99 && sin(1) < 1, 30, 0)'
|
2020-06-01 03:50:39 +03:00
|
|
|
));
|
Logicandcompare (#50)
* TokenFactory: allowing multicharacter tokens
* Added logical and compare operators: <, <=, >, >=, ==, !=, ||, &&
* Fixed operator priorities
* Error messages fixes
* Fixed operators priority
The priorities are assigned by following the php language standard
(https://www.php.net/manual/en/language.operators.precedence.php)
I've assigned precedence in steps of 10 units by following the linked page:
230 clone new
220 **
210 ++ -- ~ (int) (float) (string) (array) (object) (bool) @
200 instanceof
190 !
180 * / %
170 + - .
160 << >>
150 < <= > >=
140 == != === !== <> <=>
130 &
120 ^
110 |
100 &&
90 ||
80 ??
70 ? :
60 = += -= *= **= /= .= %= &= |= ^= <<= >>=
50 yield from
40 yield
30 and
20 xor
10 or
* Added if() function
* Cache key fix
There are cases where the cache key creation raised an error, for example
while evaluating the expression "if(cos(2), cos(2), 0)", because the
if() function was passing a float to the MathExecutor:execute() method.
2019-11-26 17:00:24 +03:00
|
|
|
$this->assertEquals(40, $calculator->execute(
|
2022-04-27 00:31:50 +03:00
|
|
|
'if(98 < 99 && sin(1) < 1, max(30, 40), 0)'
|
2020-06-01 03:50:39 +03:00
|
|
|
));
|
Logicandcompare (#50)
* TokenFactory: allowing multicharacter tokens
* Added logical and compare operators: <, <=, >, >=, ==, !=, ||, &&
* Fixed operator priorities
* Error messages fixes
* Fixed operators priority
The priorities are assigned by following the php language standard
(https://www.php.net/manual/en/language.operators.precedence.php)
I've assigned precedence in steps of 10 units by following the linked page:
230 clone new
220 **
210 ++ -- ~ (int) (float) (string) (array) (object) (bool) @
200 instanceof
190 !
180 * / %
170 + - .
160 << >>
150 < <= > >=
140 == != === !== <> <=>
130 &
120 ^
110 |
100 &&
90 ||
80 ??
70 ? :
60 = += -= *= **= /= .= %= &= |= ^= <<= >>=
50 yield from
40 yield
30 and
20 xor
10 or
* Added if() function
* Cache key fix
There are cases where the cache key creation raised an error, for example
while evaluating the expression "if(cos(2), cos(2), 0)", because the
if() function was passing a float to the MathExecutor:execute() method.
2019-11-26 17:00:24 +03:00
|
|
|
$this->assertEquals(40, $calculator->execute(
|
2022-04-27 00:31:50 +03:00
|
|
|
'if(98 < 99 && sin(1) < 1, if(10 > 5, max(30, 40), 1), 0)'
|
2020-06-01 03:50:39 +03:00
|
|
|
));
|
Logicandcompare (#50)
* TokenFactory: allowing multicharacter tokens
* Added logical and compare operators: <, <=, >, >=, ==, !=, ||, &&
* Fixed operator priorities
* Error messages fixes
* Fixed operators priority
The priorities are assigned by following the php language standard
(https://www.php.net/manual/en/language.operators.precedence.php)
I've assigned precedence in steps of 10 units by following the linked page:
230 clone new
220 **
210 ++ -- ~ (int) (float) (string) (array) (object) (bool) @
200 instanceof
190 !
180 * / %
170 + - .
160 << >>
150 < <= > >=
140 == != === !== <> <=>
130 &
120 ^
110 |
100 &&
90 ||
80 ??
70 ? :
60 = += -= *= **= /= .= %= &= |= ^= <<= >>=
50 yield from
40 yield
30 and
20 xor
10 or
* Added if() function
* Cache key fix
There are cases where the cache key creation raised an error, for example
while evaluating the expression "if(cos(2), cos(2), 0)", because the
if() function was passing a float to the MathExecutor:execute() method.
2019-11-26 17:00:24 +03:00
|
|
|
$this->assertEquals(20, $calculator->execute(
|
2022-04-27 00:31:50 +03:00
|
|
|
'if(98 < 99 && sin(1) > 1, if(10 > 5, max(30, 40), 1), if(4 <= 4, 20, 21))'
|
2020-06-01 03:50:39 +03:00
|
|
|
));
|
2022-04-27 00:31:50 +03:00
|
|
|
$this->assertEquals(\cos(2), $calculator->execute(
|
|
|
|
'if(98 < 99 && sin(1) >= 1, max(30, 40), cos(2))'
|
2020-06-01 03:50:39 +03:00
|
|
|
));
|
2022-04-27 00:31:50 +03:00
|
|
|
$this->assertEquals(\cos(2), $calculator->execute(
|
|
|
|
'if(cos(2), cos(2), 0)'
|
2020-06-01 03:50:39 +03:00
|
|
|
));
|
2020-06-04 18:43:16 +03:00
|
|
|
$trx_amount = 100000;
|
|
|
|
$calculator->setVar('trx_amount', $trx_amount);
|
|
|
|
$this->assertEquals($trx_amount, $calculator->execute('$trx_amount'));
|
|
|
|
$this->assertEquals($trx_amount * 0.03, $calculator->execute(
|
2022-04-27 00:31:50 +03:00
|
|
|
'if($trx_amount < 40000, $trx_amount * 0.06, $trx_amount * 0.03)'
|
2020-06-04 18:43:16 +03:00
|
|
|
));
|
|
|
|
$this->assertEquals($trx_amount * 0.03, $calculator->execute(
|
2022-04-27 00:31:50 +03:00
|
|
|
'if($trx_amount < 40000, $trx_amount * 0.06, if($trx_amount < 60000, $trx_amount * 0.05, $trx_amount * 0.03))'
|
2020-06-04 18:43:16 +03:00
|
|
|
));
|
|
|
|
$trx_amount = 39000;
|
|
|
|
$calculator->setVar('trx_amount', $trx_amount);
|
|
|
|
$this->assertEquals($trx_amount * 0.06, $calculator->execute(
|
2022-04-27 00:31:50 +03:00
|
|
|
'if($trx_amount < 40000, $trx_amount * 0.06, if($trx_amount < 60000, $trx_amount * 0.05, $trx_amount * 0.03))'
|
2020-06-04 18:43:16 +03:00
|
|
|
));
|
|
|
|
$trx_amount = 59000;
|
|
|
|
$calculator->setVar('trx_amount', $trx_amount);
|
|
|
|
$this->assertEquals($trx_amount * 0.05, $calculator->execute(
|
2022-04-27 00:31:50 +03:00
|
|
|
'if($trx_amount < 40000, $trx_amount * 0.06, if($trx_amount < 60000, $trx_amount * 0.05, $trx_amount * 0.03))'
|
2020-06-04 18:43:16 +03:00
|
|
|
));
|
|
|
|
$this->expectException(IncorrectNumberOfFunctionParametersException::class);
|
|
|
|
$this->assertEquals(0.0, $calculator->execute(
|
2022-04-27 00:31:50 +03:00
|
|
|
'if($trx_amount < 40000, $trx_amount * 0.06)'
|
2020-06-04 18:43:16 +03:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testVariables() : void
|
2020-06-04 18:43:16 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->assertEquals(3.14159265359, $calculator->execute('$pi'));
|
2020-07-27 19:25:59 +03:00
|
|
|
$this->assertEquals(3.14159265359, $calculator->execute('pi'));
|
2020-06-04 18:43:16 +03:00
|
|
|
$this->assertEquals(2.71828182846, $calculator->execute('$e'));
|
2020-07-27 19:25:59 +03:00
|
|
|
$this->assertEquals(2.71828182846, $calculator->execute('e'));
|
2020-06-04 18:43:16 +03:00
|
|
|
$calculator->setVars([
|
2022-04-27 00:31:50 +03:00
|
|
|
'trx_amount' => 100000.01,
|
|
|
|
'ten' => 10,
|
|
|
|
'nine' => 9,
|
|
|
|
'eight' => 8,
|
|
|
|
'seven' => 7,
|
|
|
|
'six' => 6,
|
|
|
|
'five' => 5,
|
|
|
|
'four' => 4,
|
|
|
|
'three' => 3,
|
|
|
|
'two' => 2,
|
|
|
|
'one' => 1,
|
|
|
|
'zero' => 0,
|
|
|
|
]);
|
2020-07-27 19:25:59 +03:00
|
|
|
$this->assertEquals(100000.01, $calculator->execute('$trx_amount'));
|
2020-06-04 18:43:16 +03:00
|
|
|
$this->assertEquals(10 - 9, $calculator->execute('$ten - $nine'));
|
|
|
|
$this->assertEquals(9 - 10, $calculator->execute('$nine - $ten'));
|
|
|
|
$this->assertEquals(10 + 9, $calculator->execute('$ten + $nine'));
|
|
|
|
$this->assertEquals(10 * 9, $calculator->execute('$ten * $nine'));
|
|
|
|
$this->assertEquals(10 / 9, $calculator->execute('$ten / $nine'));
|
|
|
|
$this->assertEquals(10 / (9 / 5), $calculator->execute('$ten / ($nine / $five)'));
|
|
|
|
|
2020-07-27 19:25:59 +03:00
|
|
|
// test variables without leading $
|
|
|
|
$this->assertEquals(100000.01, $calculator->execute('trx_amount'));
|
|
|
|
$this->assertEquals(10 - 9, $calculator->execute('ten - nine'));
|
|
|
|
$this->assertEquals(9 - 10, $calculator->execute('nine - ten'));
|
|
|
|
$this->assertEquals(10 + 9, $calculator->execute('ten + nine'));
|
|
|
|
$this->assertEquals(10 * 9, $calculator->execute('ten * nine'));
|
|
|
|
$this->assertEquals(10 / 9, $calculator->execute('ten / nine'));
|
|
|
|
$this->assertEquals(10 / (9 / 5), $calculator->execute('ten / (nine / five)'));
|
Logicandcompare (#50)
* TokenFactory: allowing multicharacter tokens
* Added logical and compare operators: <, <=, >, >=, ==, !=, ||, &&
* Fixed operator priorities
* Error messages fixes
* Fixed operators priority
The priorities are assigned by following the php language standard
(https://www.php.net/manual/en/language.operators.precedence.php)
I've assigned precedence in steps of 10 units by following the linked page:
230 clone new
220 **
210 ++ -- ~ (int) (float) (string) (array) (object) (bool) @
200 instanceof
190 !
180 * / %
170 + - .
160 << >>
150 < <= > >=
140 == != === !== <> <=>
130 &
120 ^
110 |
100 &&
90 ||
80 ??
70 ? :
60 = += -= *= **= /= .= %= &= |= ^= <<= >>=
50 yield from
40 yield
30 and
20 xor
10 or
* Added if() function
* Cache key fix
There are cases where the cache key creation raised an error, for example
while evaluating the expression "if(cos(2), cos(2), 0)", because the
if() function was passing a float to the MathExecutor:execute() method.
2019-11-26 17:00:24 +03:00
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testEvaluateFunctionParameters() : void
|
2019-01-16 04:04:16 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2020-06-01 03:50:39 +03:00
|
|
|
$calculator->addFunction(
|
2022-04-27 00:31:50 +03:00
|
|
|
'round',
|
|
|
|
static function($value, $decimals) {
|
|
|
|
return \round($value, $decimals);
|
2020-09-15 03:47:26 +03:00
|
|
|
}
|
2019-01-16 04:04:16 +03:00
|
|
|
);
|
|
|
|
$expression = 'round(100 * 1.111111, 2)';
|
2020-05-15 21:51:23 +03:00
|
|
|
$phpResult = 0;
|
2019-01-16 04:04:16 +03:00
|
|
|
eval('$phpResult = ' . $expression . ';');
|
|
|
|
$this->assertEquals($phpResult, $calculator->execute($expression));
|
|
|
|
$expression = 'round((100*0.04)+(((100*1.02)+0.5)*1.28),2)';
|
|
|
|
eval('$phpResult = ' . $expression . ';');
|
|
|
|
$this->assertEquals($phpResult, $calculator->execute($expression));
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testFunctionsWithQuotes() : void
|
2019-08-16 17:27:44 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2022-04-27 00:31:50 +03:00
|
|
|
$calculator->addFunction('concat', static function($first, $second) {
|
2020-05-15 21:51:23 +03:00
|
|
|
return $first . $second;
|
|
|
|
});
|
2019-08-16 17:27:44 +03:00
|
|
|
$this->assertEquals('testing', $calculator->execute('concat("test", "ing")'));
|
|
|
|
$this->assertEquals('testing', $calculator->execute("concat('test', 'ing')"));
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testQuotes() : void
|
2019-01-11 04:18:10 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2022-04-27 00:31:50 +03:00
|
|
|
$testString = 'some, long. arg; with: different-separators!';
|
2020-06-01 03:50:39 +03:00
|
|
|
$calculator->addFunction(
|
2022-04-27 00:31:50 +03:00
|
|
|
'test',
|
|
|
|
function($arg) use ($testString) {
|
2020-09-15 03:47:26 +03:00
|
|
|
$this->assertEquals($testString, $arg);
|
2022-04-27 00:31:50 +03:00
|
|
|
|
2020-09-15 03:47:26 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2019-01-16 04:04:16 +03:00
|
|
|
);
|
2019-01-11 04:18:10 +03:00
|
|
|
$calculator->execute('test("' . $testString . '")'); // single quotes
|
|
|
|
$calculator->execute("test('" . $testString . "')"); // double quotes
|
|
|
|
}
|
2020-01-13 20:09:18 +03:00
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testBeginWithBracketAndMinus() : void
|
2020-01-13 20:09:18 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->assertEquals(-4, $calculator->execute('(-4)'));
|
|
|
|
$this->assertEquals(1, $calculator->execute('(-4 + 5)'));
|
|
|
|
}
|
2020-02-07 19:05:26 +03:00
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testStringComparison() : void
|
2020-07-26 05:27:26 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->assertEquals(true, $calculator->execute('"a" == \'a\''));
|
|
|
|
$this->assertEquals(true, $calculator->execute('"hello world" == "hello world"'));
|
|
|
|
$this->assertEquals(false, $calculator->execute('"hello world" == "hola mundo"'));
|
|
|
|
$this->assertEquals(true, $calculator->execute('"hello world" != "hola mundo"'));
|
2020-07-27 05:14:51 +03:00
|
|
|
$this->assertEquals(true, $calculator->execute('"a" < "b"'));
|
|
|
|
$this->assertEquals(false, $calculator->execute('"a" > "b"'));
|
|
|
|
$this->assertEquals(true, $calculator->execute('"a" <= "b"'));
|
|
|
|
$this->assertEquals(false, $calculator->execute('"a" >= "b"'));
|
|
|
|
$this->assertEquals(true, $calculator->execute('"A" != "a"'));
|
2020-07-26 05:27:26 +03:00
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testVarStringComparison() : void
|
2020-07-26 05:27:26 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2020-07-27 05:14:51 +03:00
|
|
|
$calculator->setVar('var', 97);
|
|
|
|
$this->assertEquals(false, $calculator->execute('97 == "a"'));
|
|
|
|
$this->assertEquals(false, $calculator->execute('$var == "a"'));
|
|
|
|
$calculator->setVar('var', 'a');
|
|
|
|
$this->assertEquals(true, $calculator->execute('$var == "a"'));
|
2020-07-26 05:27:26 +03:00
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testOnVarNotFound() : void
|
2020-07-26 05:27:26 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$calculator->setVarNotFoundHandler(
|
2022-04-27 00:31:50 +03:00
|
|
|
static function($varName) {
|
|
|
|
if ('undefined' == $varName) {
|
2020-07-26 05:27:26 +03:00
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
$this->assertEquals(15, $calculator->execute('5 * undefined'));
|
2022-03-21 19:52:25 +03:00
|
|
|
$this->assertEquals(3, $calculator->getVar('undefined'));
|
|
|
|
$this->assertNull($calculator->getVar('Lucy'));
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testGetVarException() : void
|
2022-03-21 19:52:25 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->expectException(UnknownVariableException::class);
|
|
|
|
$this->assertNull($calculator->getVar('Lucy'));
|
2020-07-26 05:27:26 +03:00
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testMinusZero() : void
|
2020-02-07 19:05:26 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->assertEquals(1, $calculator->execute('1 - 0'));
|
|
|
|
$this->assertEquals(1, $calculator->execute('1-0'));
|
|
|
|
}
|
2020-07-27 05:14:51 +03:00
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testScientificNotation() : void
|
2022-01-05 11:53:06 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->assertEquals(1.5e9, $calculator->execute('1.5e9'));
|
|
|
|
$this->assertEquals(1.5e-9, $calculator->execute('1.5e-9'));
|
|
|
|
$this->assertEquals(1.5e+9, $calculator->execute('1.5e+9'));
|
|
|
|
}
|
|
|
|
|
2022-05-09 21:13:30 +03:00
|
|
|
public function testNullReturnType() : void
|
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$calculator->setVar('nullValue', null);
|
|
|
|
$this->assertEquals(null, $calculator->execute('nullValue'));
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testGetFunctionsReturnsArray() : void
|
2020-07-27 05:14:51 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->assertIsArray($calculator->getFunctions());
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testGetFunctionsReturnsFunctions() : void
|
2020-07-27 05:14:51 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2022-04-27 00:31:50 +03:00
|
|
|
$this->assertGreaterThan(40, \count($calculator->getFunctions()));
|
2020-07-27 05:14:51 +03:00
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testGetVarsReturnsArray() : void
|
2020-07-27 05:14:51 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->assertIsArray($calculator->getVars());
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testGetVarsReturnsCount() : void
|
2020-07-27 05:14:51 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2022-04-27 00:31:50 +03:00
|
|
|
$this->assertGreaterThan(1, \count($calculator->getVars()));
|
2020-07-27 05:14:51 +03:00
|
|
|
}
|
2020-07-27 19:25:59 +03:00
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testUndefinedVarThrowsExecption() : void
|
2020-07-27 19:25:59 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2022-04-27 00:31:50 +03:00
|
|
|
$this->assertGreaterThan(1, \count($calculator->getVars()));
|
2020-07-27 19:25:59 +03:00
|
|
|
$this->expectException(UnknownVariableException::class);
|
|
|
|
$calculator->execute('5 * undefined');
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testSetVarsAcceptsAllScalars() : void
|
2020-07-27 19:25:59 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$calculator->setVar('boolTrue', true);
|
|
|
|
$calculator->setVar('boolFalse', false);
|
|
|
|
$calculator->setVar('int', 1);
|
2020-09-16 04:27:43 +03:00
|
|
|
$calculator->setVar('null', null);
|
2020-07-27 19:25:59 +03:00
|
|
|
$calculator->setVar('float', 1.1);
|
|
|
|
$calculator->setVar('string', 'string');
|
2022-05-09 21:13:30 +03:00
|
|
|
$this->assertCount(8, $calculator->getVars());
|
2020-09-16 04:27:43 +03:00
|
|
|
$this->assertEquals(true, $calculator->getVar('boolTrue'));
|
|
|
|
$this->assertEquals(false, $calculator->getVar('boolFalse'));
|
|
|
|
$this->assertEquals(1, $calculator->getVar('int'));
|
|
|
|
$this->assertEquals(null, $calculator->getVar('null'));
|
|
|
|
$this->assertEquals(1.1, $calculator->getVar('float'));
|
|
|
|
$this->assertEquals('string', $calculator->getVar('string'));
|
2022-04-26 23:14:59 +03:00
|
|
|
|
|
|
|
$this->expectException(MathExecutorException::class);
|
|
|
|
$calculator->setVar('validVar', new \DateTime());
|
2020-07-27 19:25:59 +03:00
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testSetVarsDoesNotAcceptObject() : void
|
2020-07-27 19:25:59 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->expectException(MathExecutorException::class);
|
|
|
|
$calculator->setVar('object', $this);
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testSetVarsDoesNotAcceptResource() : void
|
2020-07-27 19:25:59 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->expectException(MathExecutorException::class);
|
2022-04-27 00:31:50 +03:00
|
|
|
$calculator->setVar('resource', \tmpfile());
|
2020-07-27 19:25:59 +03:00
|
|
|
}
|
2020-10-20 01:48:30 +03:00
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testSetCustomVarValidator() : void
|
2022-04-25 17:50:51 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2022-04-27 00:31:50 +03:00
|
|
|
$calculator->setVarValidationHandler(static function(string $name, $variable) : void {
|
2022-04-26 23:14:59 +03:00
|
|
|
// allow all scalars and null
|
2022-04-27 00:31:50 +03:00
|
|
|
if (\is_scalar($variable) || null === $variable) {
|
2022-04-26 23:14:59 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Allow variables of type DateTime, but not others
|
|
|
|
if (! $variable instanceof \DateTime) {
|
2022-04-27 00:31:50 +03:00
|
|
|
throw new MathExecutorException('Invalid variable type');
|
2022-04-26 23:14:59 +03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
$calculator->setVar('validFloat', 0.0);
|
|
|
|
$calculator->setVar('validInt', 0);
|
|
|
|
$calculator->setVar('validTrue', true);
|
|
|
|
$calculator->setVar('validFalse', false);
|
|
|
|
$calculator->setVar('validString', 'string');
|
|
|
|
$calculator->setVar('validNull', null);
|
|
|
|
$calculator->setVar('validDateTime', new \DateTime());
|
|
|
|
|
|
|
|
$this->expectException(MathExecutorException::class);
|
|
|
|
$calculator->setVar('validVar', $this);
|
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testSetCustomVarNameValidator() : void
|
2022-04-26 23:14:59 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
2022-04-27 00:31:50 +03:00
|
|
|
$calculator->setVarValidationHandler(static function(string $name, $variable) : void {
|
2022-04-26 23:14:59 +03:00
|
|
|
// don't allow variable names with the word invalid in them
|
2022-04-27 00:31:50 +03:00
|
|
|
if (\str_contains($name, 'invalid')) {
|
|
|
|
throw new MathExecutorException('Invalid variable name');
|
2022-04-25 17:50:51 +03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-04-26 23:14:59 +03:00
|
|
|
$calculator->setVar('validFloat', 0.0);
|
|
|
|
$calculator->setVar('validInt', 0);
|
|
|
|
$calculator->setVar('validTrue', true);
|
|
|
|
$calculator->setVar('validFalse', false);
|
|
|
|
$calculator->setVar('validString', 'string');
|
|
|
|
$calculator->setVar('validNull', null);
|
|
|
|
$calculator->setVar('validDateTime', new \DateTime());
|
2022-04-25 17:50:51 +03:00
|
|
|
|
|
|
|
$this->expectException(MathExecutorException::class);
|
2022-04-26 23:14:59 +03:00
|
|
|
$calculator->setVar('invalidVar', 12);
|
2022-04-25 17:50:51 +03:00
|
|
|
}
|
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testVarExists() : void
|
2022-03-21 19:52:25 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$varName = 'Eythel';
|
|
|
|
$calculator->setVar($varName, 1);
|
|
|
|
$this->assertTrue($calculator->varExists($varName));
|
|
|
|
$this->assertFalse($calculator->varExists('Lucy'));
|
|
|
|
}
|
|
|
|
|
2020-10-20 01:48:30 +03:00
|
|
|
/**
|
|
|
|
* @dataProvider providerExpressionValues
|
|
|
|
*/
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testCalculatingValues($expression, $value) : void
|
2020-10-20 01:48:30 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
|
|
|
|
try {
|
|
|
|
$result = $calculator->execute($expression);
|
|
|
|
} catch (Exception $e) {
|
2022-04-27 00:31:50 +03:00
|
|
|
$this->fail(\sprintf('Exception: %s (%s:%d), expression was: %s', \get_class($e), $e->getFile(), $e->getLine(), $expression));
|
2020-10-20 01:48:30 +03:00
|
|
|
}
|
2022-04-27 00:31:50 +03:00
|
|
|
$this->assertEquals($value, $result, "{$expression} did not evaluate to {$value}");
|
2020-10-20 01:48:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Expressions data provider
|
|
|
|
*
|
2020-10-20 02:25:48 +03:00
|
|
|
* Most tests can go in here. The idea is that each expression will be evaluated by MathExecutor and by PHP directly.
|
2020-10-20 01:48:30 +03:00
|
|
|
* The results should be the same. If they are not, then the test fails. No need to add extra test unless you are doing
|
|
|
|
* something more complex and not a simple mathmatical expression.
|
|
|
|
*/
|
|
|
|
public function providerExpressionValues()
|
|
|
|
{
|
|
|
|
return [
|
2022-04-27 00:31:50 +03:00
|
|
|
['arccos(0.5)', 1.0471975511966],
|
|
|
|
['arccos(0.5)', \acos(0.5)],
|
|
|
|
['arccosec(4)', 0.2526802551421],
|
|
|
|
['arccosec(4)', \asin(1 / 4)],
|
|
|
|
['arccot(3)', M_PI / 2 - \atan(3)],
|
|
|
|
['arccotan(4)', 0.2449786631269],
|
|
|
|
['arccotan(4)', M_PI / 2 - \atan(4)],
|
|
|
|
['arccsc(4)', 0.2526802551421],
|
|
|
|
['arccsc(4)', \asin(1 / 4)],
|
|
|
|
['arcctg(3)', M_PI / 2 - \atan(3)],
|
|
|
|
['arcsec(4)', 1.3181160716528],
|
|
|
|
['arcsec(4)', \acos(1 / 4)],
|
|
|
|
['arcsin(0.5)', 0.5235987755983],
|
|
|
|
['arcsin(0.5)', \asin(0.5)],
|
|
|
|
['arctan(0.5)', \atan(0.5)],
|
|
|
|
['arctan(4)', 1.3258176636680],
|
|
|
|
['arctg(0.5)', \atan(0.5)],
|
|
|
|
['cosec(12)', 1 / \sin(12)],
|
|
|
|
['cosec(4)', -1.3213487088109],
|
|
|
|
['cosh(12)', \cosh(12)],
|
|
|
|
['cot(12)', \cos(12) / \sin(12)],
|
|
|
|
['cotan(12)', \cos(12) / \sin(12)],
|
|
|
|
['cotan(4)', 0.8636911544506],
|
|
|
|
['cotg(3)', \cos(3) / \sin(3)],
|
|
|
|
['csc(4)', 1 / \sin(4)],
|
|
|
|
['ctg(4)', \cos(4) / \sin(4)],
|
|
|
|
['ctn(4)', \cos(4) / \sin(4)],
|
|
|
|
['decbin(10)', \decbin(10)],
|
|
|
|
['lg(2)', 0.3010299956639],
|
|
|
|
['lg(2)', \log10(2)],
|
|
|
|
['ln(2)', 0.6931471805599],
|
|
|
|
['ln(2)', \log(2)],
|
|
|
|
['sec(4)', -1.5298856564664],
|
|
|
|
['tg(4)', 1.1578212823496],
|
2020-10-20 01:48:30 +03:00
|
|
|
];
|
|
|
|
}
|
2020-10-20 02:25:48 +03:00
|
|
|
|
2022-04-27 00:31:50 +03:00
|
|
|
public function testCache() : void
|
2021-01-06 03:06:04 +03:00
|
|
|
{
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
$this->assertEquals(256, $calculator->execute('2 ^ 8')); // second arg $cache is true by default
|
|
|
|
|
|
|
|
$this->assertIsArray($calculator->getCache());
|
2022-05-09 21:13:30 +03:00
|
|
|
$this->assertCount(1, $calculator->getCache());
|
2021-01-06 03:06:04 +03:00
|
|
|
|
|
|
|
$this->assertEquals(512, $calculator->execute('2 ^ 9', true));
|
2022-05-09 21:13:30 +03:00
|
|
|
$this->assertCount(2, $calculator->getCache());
|
2021-01-06 03:06:04 +03:00
|
|
|
|
|
|
|
$this->assertEquals(1024, $calculator->execute('2 ^ 10', false));
|
2022-05-09 21:13:30 +03:00
|
|
|
$this->assertCount(2, $calculator->getCache());
|
2021-01-06 03:06:04 +03:00
|
|
|
|
|
|
|
$calculator->clearCache();
|
|
|
|
$this->assertIsArray($calculator->getCache());
|
2022-05-09 21:13:30 +03:00
|
|
|
$this->assertCount(0, $calculator->getCache());
|
2021-01-06 03:06:04 +03:00
|
|
|
|
|
|
|
$this->assertEquals(2048, $calculator->execute('2 ^ 11', false));
|
2022-05-09 21:13:30 +03:00
|
|
|
$this->assertCount(0, $calculator->getCache());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testUnsupportedOperands() : void
|
|
|
|
{
|
|
|
|
if (\version_compare(PHP_VERSION, '8') >= 0) {
|
|
|
|
$calculator = new MathExecutor();
|
|
|
|
|
|
|
|
$calculator->setVar('stringVar', 'string');
|
|
|
|
$calculator->setVar('intVar', 1);
|
|
|
|
|
|
|
|
$this->expectException(\TypeError::class);
|
|
|
|
$calculator->execute('stringVar + intVar');
|
|
|
|
} else {
|
|
|
|
$this->expectNotToPerformAssertions();
|
|
|
|
}
|
2021-01-06 03:06:04 +03:00
|
|
|
}
|
2022-03-21 19:52:25 +03:00
|
|
|
}
|