Path: blob/master/tests/core/math/test_expression.cpp
23450 views
/**************************************************************************/1/* test_expression.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "tests/test_macros.h"3132TEST_FORCE_LINK(test_expression)3334#include "core/math/expression.h"3536namespace TestExpression {3738TEST_CASE("[Expression] Integer arithmetic") {39Expression expression;4041CHECK_MESSAGE(42expression.parse("-123456") == OK,43"Integer identity should parse successfully.");44CHECK_MESSAGE(45int(expression.execute()) == -123456,46"Integer identity should return the expected result.");4748CHECK_MESSAGE(49expression.parse("2 + 3") == OK,50"Integer addition should parse successfully.");51CHECK_MESSAGE(52int(expression.execute()) == 5,53"Integer addition should return the expected result.");5455CHECK_MESSAGE(56expression.parse("999999999999 + 999999999999") == OK,57"Large integer addition should parse successfully.");58CHECK_MESSAGE(59int64_t(expression.execute()) == 1'999'999'999'998,60"Large integer addition should return the expected result.");6162CHECK_MESSAGE(63expression.parse("25 / 10") == OK,64"Integer / integer division should parse successfully.");65CHECK_MESSAGE(66int(expression.execute()) == 2,67"Integer / integer division should return the expected result.");6869CHECK_MESSAGE(70expression.parse("2 * (6 + 14) / 2 - 5") == OK,71"Integer multiplication-addition-subtraction-division should parse successfully.");72CHECK_MESSAGE(73int(expression.execute()) == 15,74"Integer multiplication-addition-subtraction-division should return the expected result.");75}7677TEST_CASE("[Expression] Floating-point arithmetic") {78Expression expression;7980CHECK_MESSAGE(81expression.parse("-123.456") == OK,82"Float identity should parse successfully.");83CHECK_MESSAGE(84double(expression.execute()) == doctest::Approx(-123.456),85"Float identity should return the expected result.");8687CHECK_MESSAGE(88expression.parse("2.0 + 3.0") == OK,89"Float addition should parse successfully.");90CHECK_MESSAGE(91double(expression.execute()) == doctest::Approx(5),92"Float addition should return the expected result.");9394CHECK_MESSAGE(95expression.parse("3.0 / 10") == OK,96"Float / integer division should parse successfully.");97CHECK_MESSAGE(98double(expression.execute()) == doctest::Approx(0.3),99"Float / integer division should return the expected result.");100101CHECK_MESSAGE(102expression.parse("3 / 10.0") == OK,103"Basic integer / float division should parse successfully.");104CHECK_MESSAGE(105double(expression.execute()) == doctest::Approx(0.3),106"Basic integer / float division should return the expected result.");107108CHECK_MESSAGE(109expression.parse("3.0 / 10.0") == OK,110"Float / float division should parse successfully.");111CHECK_MESSAGE(112double(expression.execute()) == doctest::Approx(0.3),113"Float / float division should return the expected result.");114115CHECK_MESSAGE(116expression.parse("2.5 * (6.0 + 14.25) / 2.0 - 5.12345") == OK,117"Float multiplication-addition-subtraction-division should parse successfully.");118CHECK_MESSAGE(119double(expression.execute()) == doctest::Approx(20.18905),120"Float multiplication-addition-subtraction-division should return the expected result.");121}122123TEST_CASE("[Expression] Floating-point notation") {124Expression expression;125126CHECK_MESSAGE(127expression.parse("2.") == OK,128"The expression should parse successfully.");129CHECK_MESSAGE(130double(expression.execute()) == doctest::Approx(2.0),131"The expression should return the expected result.");132133CHECK_MESSAGE(134expression.parse("(2.)") == OK,135"The expression should parse successfully.");136CHECK_MESSAGE(137double(expression.execute()) == doctest::Approx(2.0),138"The expression should return the expected result.");139140CHECK_MESSAGE(141expression.parse(".3") == OK,142"The expression should parse successfully.");143CHECK_MESSAGE(144double(expression.execute()) == doctest::Approx(0.3),145"The expression should return the expected result.");146147CHECK_MESSAGE(148expression.parse("2.+5.") == OK,149"The expression should parse successfully.");150CHECK_MESSAGE(151double(expression.execute()) == doctest::Approx(7.0),152"The expression should return the expected result.");153154CHECK_MESSAGE(155expression.parse(".3-.8") == OK,156"The expression should parse successfully.");157CHECK_MESSAGE(158double(expression.execute()) == doctest::Approx(-0.5),159"The expression should return the expected result.");160161CHECK_MESSAGE(162expression.parse("2.+.2") == OK,163"The expression should parse successfully.");164CHECK_MESSAGE(165double(expression.execute()) == doctest::Approx(2.2),166"The expression should return the expected result.");167168CHECK_MESSAGE(169expression.parse(".0*0.") == OK,170"The expression should parse successfully.");171CHECK_MESSAGE(172double(expression.execute()) == doctest::Approx(0.0),173"The expression should return the expected result.");174}175176TEST_CASE("[Expression] Scientific notation") {177Expression expression;178179CHECK_MESSAGE(180expression.parse("2.e5") == OK,181"The expression should parse successfully.");182CHECK_MESSAGE(183expression.parse("2.E5") == OK,184"The expression should parse successfully.");185CHECK_MESSAGE(186double(expression.execute()) == doctest::Approx(200'000),187"The expression should return the expected result.");188189// The middle "e" is ignored here.190CHECK_MESSAGE(191expression.parse("2e5") == OK,192"The expression should parse successfully.");193CHECK_MESSAGE(194double(expression.execute()) == doctest::Approx(2e5),195"The expression should return the expected result.");196197CHECK_MESSAGE(198expression.parse("2e.5") == OK,199"The expression should parse successfully.");200CHECK_MESSAGE(201double(expression.execute()) == doctest::Approx(2),202"The expression should return the expected result.");203}204205TEST_CASE("[Expression] Underscored numeric literals") {206Expression expression;207208CHECK_MESSAGE(209expression.parse("1_000_000") == OK,210"The expression should parse successfully.");211CHECK_MESSAGE(212expression.parse("1_000.000") == OK,213"The expression should parse successfully.");214CHECK_MESSAGE(215expression.parse("0xff_99_00") == OK,216"The expression should parse successfully.");217CHECK_MESSAGE(218expression.parse("0Xff_99_00") == OK,219"The expression should parse successfully.");220CHECK_MESSAGE(221expression.parse("0b10_11_00") == OK,222"The expression should parse successfully.");223CHECK_MESSAGE(224expression.parse("0B10_11_00") == OK,225"The expression should parse successfully.");226}227228TEST_CASE("[Expression] Built-in functions") {229Expression expression;230231CHECK_MESSAGE(232expression.parse("sqrt(pow(3, 2) + pow(4, 2))") == OK,233"The expression should parse successfully.");234CHECK_MESSAGE(235int(expression.execute()) == 5,236"`sqrt(pow(3, 2) + pow(4, 2))` should return the expected result.");237238CHECK_MESSAGE(239expression.parse("snapped(sin(0.5), 0.01)") == OK,240"The expression should parse successfully.");241CHECK_MESSAGE(242double(expression.execute()) == doctest::Approx(0.48),243"`snapped(sin(0.5), 0.01)` should return the expected result.");244245CHECK_MESSAGE(246expression.parse("pow(2.0, -2500)") == OK,247"The expression should parse successfully.");248CHECK_MESSAGE(249Math::is_zero_approx(double(expression.execute())),250"`pow(2.0, -2500)` should return the expected result (asymptotically zero).");251}252253TEST_CASE("[Expression] Boolean expressions") {254Expression expression;255256CHECK_MESSAGE(257expression.parse("24 >= 12") == OK,258"The boolean expression should parse successfully.");259CHECK_MESSAGE(260bool(expression.execute()),261"The boolean expression should evaluate to `true`.");262263CHECK_MESSAGE(264expression.parse("1.0 < 1.25 && 1.25 < 2.0") == OK,265"The boolean expression should parse successfully.");266CHECK_MESSAGE(267bool(expression.execute()),268"The boolean expression should evaluate to `true`.");269270CHECK_MESSAGE(271expression.parse("!2") == OK,272"The boolean expression should parse successfully.");273CHECK_MESSAGE(274!bool(expression.execute()),275"The boolean expression should evaluate to `false`.");276277CHECK_MESSAGE(278expression.parse("!!2") == OK,279"The boolean expression should parse successfully.");280CHECK_MESSAGE(281bool(expression.execute()),282"The boolean expression should evaluate to `true`.");283284CHECK_MESSAGE(285expression.parse("!0") == OK,286"The boolean expression should parse successfully.");287CHECK_MESSAGE(288bool(expression.execute()),289"The boolean expression should evaluate to `true`.");290291CHECK_MESSAGE(292expression.parse("!!0") == OK,293"The boolean expression should parse successfully.");294CHECK_MESSAGE(295!bool(expression.execute()),296"The boolean expression should evaluate to `false`.");297298CHECK_MESSAGE(299expression.parse("2 && 5") == OK,300"The boolean expression should parse successfully.");301CHECK_MESSAGE(302bool(expression.execute()),303"The boolean expression should evaluate to `true`.");304305CHECK_MESSAGE(306expression.parse("0 || 0") == OK,307"The boolean expression should parse successfully.");308CHECK_MESSAGE(309!bool(expression.execute()),310"The boolean expression should evaluate to `false`.");311312CHECK_MESSAGE(313expression.parse("(2 <= 4) && (2 > 5)") == OK,314"The boolean expression should parse successfully.");315CHECK_MESSAGE(316!bool(expression.execute()),317"The boolean expression should evaluate to `false`.");318}319320TEST_CASE("[Expression] Expressions with variables") {321Expression expression;322323PackedStringArray parameter_names = { "foo", "bar" };324CHECK_MESSAGE(325expression.parse("foo + bar + 50", parameter_names) == OK,326"The expression should parse successfully.");327Array values = { 60, 20 };328CHECK_MESSAGE(329int(expression.execute(values)) == 130,330"The expression should return the expected value.");331332PackedStringArray parameter_names_invalid;333parameter_names_invalid.push_back("foo");334parameter_names_invalid.push_back("baz"); // Invalid parameter name.335CHECK_MESSAGE(336expression.parse("foo + bar + 50", parameter_names_invalid) == OK,337"The expression should parse successfully.");338Array values_invalid = { 60, 20 };339// Invalid parameters will parse successfully but print an error message when executing.340ERR_PRINT_OFF;341CHECK_MESSAGE(342int(expression.execute(values_invalid)) == 0,343"The expression should return the expected value.");344ERR_PRINT_ON;345346// Mismatched argument count (more values than parameters).347PackedStringArray parameter_names_mismatch = { "foo", "bar" };348CHECK_MESSAGE(349expression.parse("foo + bar + 50", parameter_names_mismatch) == OK,350"The expression should parse successfully.");351Array values_mismatch = { 60, 20, 110 };352CHECK_MESSAGE(353int(expression.execute(values_mismatch)) == 130,354"The expression should return the expected value.");355356// Mismatched argument count (more parameters than values).357PackedStringArray parameter_names_mismatch2 = { "foo", "bar", "baz" };358CHECK_MESSAGE(359expression.parse("foo + bar + baz + 50", parameter_names_mismatch2) == OK,360"The expression should parse successfully.");361Array values_mismatch2 = { 60, 20 };362// Having more parameters than values will parse successfully but print an363// error message when executing.364ERR_PRINT_OFF;365CHECK_MESSAGE(366int(expression.execute(values_mismatch2)) == 0,367"The expression should return the expected value.");368ERR_PRINT_ON;369}370371TEST_CASE("[Expression] Invalid expressions") {372Expression expression;373374CHECK_MESSAGE(375expression.parse("\\") == ERR_INVALID_PARAMETER,376"The expression shouldn't parse successfully.");377378CHECK_MESSAGE(379expression.parse("0++") == ERR_INVALID_PARAMETER,380"The expression shouldn't parse successfully.");381382CHECK_MESSAGE(383expression.parse("()") == ERR_INVALID_PARAMETER,384"The expression shouldn't parse successfully.");385386CHECK_MESSAGE(387expression.parse("()()") == ERR_INVALID_PARAMETER,388"The expression shouldn't parse successfully.");389390CHECK_MESSAGE(391expression.parse("() - ()") == ERR_INVALID_PARAMETER,392"The expression shouldn't parse successfully.");393394CHECK_MESSAGE(395expression.parse("() * 12345") == ERR_INVALID_PARAMETER,396"The expression shouldn't parse successfully.");397398CHECK_MESSAGE(399expression.parse("() * 12345") == ERR_INVALID_PARAMETER,400"The expression shouldn't parse successfully.");401402CHECK_MESSAGE(403expression.parse("123'456") == ERR_INVALID_PARAMETER,404"The expression shouldn't parse successfully.");405406CHECK_MESSAGE(407expression.parse("123\"456") == ERR_INVALID_PARAMETER,408"The expression shouldn't parse successfully.");409}410411TEST_CASE("[Expression] Unusual expressions") {412Expression expression;413414// Redundant parentheses don't cause a parse error as long as they're matched.415CHECK_MESSAGE(416expression.parse("(((((((((((((((666)))))))))))))))") == OK,417"The expression should parse successfully.");418419// Using invalid identifiers doesn't cause a parse error.420ERR_PRINT_OFF;421CHECK_MESSAGE(422expression.parse("hello + hello") == OK,423"The expression should parse successfully.");424CHECK_MESSAGE(425int(expression.execute()) == 0,426"The expression should return the expected result.");427ERR_PRINT_ON;428429ERR_PRINT_OFF;430CHECK_MESSAGE(431expression.parse("$1.00 + ???5") == OK,432"The expression should parse successfully.");433CHECK_MESSAGE(434int(expression.execute()) == 0,435"The expression should return the expected result.");436ERR_PRINT_ON;437438// Commas can't be used as a decimal parameter.439CHECK_MESSAGE(440expression.parse("123,456") == OK,441"The expression should parse successfully.");442CHECK_MESSAGE(443int(expression.execute()) == 123,444"The expression should return the expected result.");445446// Spaces can't be used as a separator for large numbers.447CHECK_MESSAGE(448expression.parse("123 456") == OK,449"The expression should parse successfully.");450CHECK_MESSAGE(451int(expression.execute()) == 123,452"The expression should return the expected result.");453454// Division by zero is accepted, even though it prints an error message normally.455CHECK_MESSAGE(456expression.parse("-25.4 / 0") == OK,457"The expression should parse successfully.");458ERR_PRINT_OFF;459CHECK_MESSAGE(460Math::is_inf(double(expression.execute())),461"`-25.4 / 0` should return inf.");462ERR_PRINT_ON;463464CHECK_MESSAGE(465expression.parse("0 / 0") == OK,466"The expression should parse successfully.");467ERR_PRINT_OFF;468CHECK_MESSAGE(469int(expression.execute()) == 0,470"`0 / 0` should return 0.");471ERR_PRINT_ON;472473// The tests below currently crash the engine.474//475//CHECK_MESSAGE(476// expression.parse("(-9223372036854775807 - 1) % -1") == OK,477// "The expression should parse successfully.");478//CHECK_MESSAGE(479// int64_t(expression.execute()) == 0,480// "`(-9223372036854775807 - 1) % -1` should return the expected result.");481//482//CHECK_MESSAGE(483// expression.parse("(-9223372036854775807 - 1) / -1") == OK,484// "The expression should parse successfully.");485//CHECK_MESSAGE(486// int64_t(expression.execute()) == 0,487// "`(-9223372036854775807 - 1) / -1` should return the expected result.");488}489490} // namespace TestExpression491492493