summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/math/expression.h2
-rw-r--r--doc/classes/Expression.xml2
-rw-r--r--doc/classes/Node.xml2
-rw-r--r--doc/classes/Texture2D.xml2
-rw-r--r--editor/editor_feature_profile.cpp17
-rw-r--r--editor/editor_feature_profile.h2
-rw-r--r--editor/editor_node.cpp6
-rw-r--r--platform/android/java/lib/res/values/dimens.xml4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java6
-rw-r--r--scene/resources/texture.h3
-rw-r--r--tests/test_expression.h431
-rw-r--r--tests/test_main.cpp1
12 files changed, 463 insertions, 15 deletions
diff --git a/core/math/expression.h b/core/math/expression.h
index 59a9a2f4ed..f2cfe6b1a6 100644
--- a/core/math/expression.h
+++ b/core/math/expression.h
@@ -343,7 +343,7 @@ protected:
public:
Error parse(const String &p_expression, const Vector<String> &p_input_names = Vector<String>());
- Variant execute(Array p_inputs, Object *p_base = nullptr, bool p_show_error = true);
+ Variant execute(Array p_inputs = Array(), Object *p_base = nullptr, bool p_show_error = true);
bool has_execute_failed() const;
String get_error_text() const;
diff --git a/doc/classes/Expression.xml b/doc/classes/Expression.xml
index fcd1aa43c0..f2611dc850 100644
--- a/doc/classes/Expression.xml
+++ b/doc/classes/Expression.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
An expression can be made of any arithmetic operation, built-in math function call, method call of a passed instance, or built-in type construction call.
- An example expression text using the built-in math functions could be [code]sqrt(pow(3,2) + pow(4,2))[/code].
+ An example expression text using the built-in math functions could be [code]sqrt(pow(3, 2) + pow(4, 2))[/code].
In the following example we use a [LineEdit] node to write our expression and show the result.
[codeblock]
onready var expression = Expression.new()
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 8f66c9df38..d31e336ea3 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -194,6 +194,7 @@
Finds a descendant of this node whose name matches [code]mask[/code] as in [method String.match] (i.e. case-sensitive, but [code]"*"[/code] matches zero or more characters and [code]"?"[/code] matches any single character except [code]"."[/code]).
[b]Note:[/b] It does not match against the full path, just against individual node names.
If [code]owned[/code] is [code]true[/code], this method only finds nodes whose owner is this node. This is especially important for scenes instantiated through a script, because those scenes don't have an owner.
+ [b]Note:[/b] As this method walks through all the descendants of the node, it is the slowest way to get a reference to another node. Whenever possible, consider using [method get_node] instead. To avoid using [method find_node] too often, consider caching the node reference into a variable.
</description>
</method>
<method name="find_parent" qualifiers="const">
@@ -204,6 +205,7 @@
<description>
Finds the first parent of the current node whose name matches [code]mask[/code] as in [method String.match] (i.e. case-sensitive, but [code]"*"[/code] matches zero or more characters and [code]"?"[/code] matches any single character except [code]"."[/code]).
[b]Note:[/b] It does not match against the full path, just against individual node names.
+ [b]Note:[/b] As this method walks upwards in the scene tree, it can be slow in large, deeply nested scene trees. Whenever possible, consider using [method get_node] instead. To avoid using [method find_parent] too often, consider caching the node reference into a variable.
</description>
</method>
<method name="get_child" qualifiers="const">
diff --git a/doc/classes/Texture2D.xml b/doc/classes/Texture2D.xml
index ffe806cef7..f283efdc3d 100644
--- a/doc/classes/Texture2D.xml
+++ b/doc/classes/Texture2D.xml
@@ -96,7 +96,7 @@
<return type="Image">
</return>
<description>
- Returns an [Image] with the data from this [Texture2D]. [Image]s can be accessed and manipulated directly.
+ Returns an [Image] that is a copy of data from this [Texture2D]. [Image]s can be accessed and manipulated directly.
</description>
</method>
<method name="get_height" qualifiers="const">
diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp
index f68cc3b323..418370a7c3 100644
--- a/editor/editor_feature_profile.cpp
+++ b/editor/editor_feature_profile.cpp
@@ -41,9 +41,9 @@ const char *EditorFeatureProfile::feature_names[FEATURE_MAX] = {
TTRC("Script Editor"),
TTRC("Asset Library"),
TTRC("Scene Tree Editing"),
- TTRC("Import Dock"),
TTRC("Node Dock"),
- TTRC("FileSystem and Import Docks")
+ TTRC("FileSystem Dock"),
+ TTRC("Import Dock"),
};
const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = {
@@ -51,9 +51,9 @@ const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = {
"script",
"asset_lib",
"scene_tree",
- "import_dock",
"node_dock",
- "filesystem_dock"
+ "filesystem_dock",
+ "import_dock",
};
void EditorFeatureProfile::set_disable_class(const StringName &p_class, bool p_disabled) {
@@ -678,9 +678,16 @@ void EditorFeatureProfileManager::_update_selected_profile() {
TreeItem *root = class_list->create_item();
TreeItem *features = class_list->create_item(root);
+ TreeItem *last_feature;
features->set_text(0, TTR("Enabled Features:"));
for (int i = 0; i < EditorFeatureProfile::FEATURE_MAX; i++) {
- TreeItem *feature = class_list->create_item(features);
+ TreeItem *feature;
+ if (i == EditorFeatureProfile::FEATURE_IMPORT_DOCK) {
+ feature = class_list->create_item(last_feature);
+ } else {
+ feature = class_list->create_item(features);
+ last_feature = feature;
+ }
feature->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
feature->set_text(0, TTRGET(EditorFeatureProfile::get_feature_name(EditorFeatureProfile::Feature(i))));
feature->set_selectable(0, true);
diff --git a/editor/editor_feature_profile.h b/editor/editor_feature_profile.h
index 38413e35a2..d0d08c61f4 100644
--- a/editor/editor_feature_profile.h
+++ b/editor/editor_feature_profile.h
@@ -49,9 +49,9 @@ public:
FEATURE_SCRIPT,
FEATURE_ASSET_LIB,
FEATURE_SCENE_TREE,
- FEATURE_IMPORT_DOCK,
FEATURE_NODE_DOCK,
FEATURE_FILESYSTEM_DOCK,
+ FEATURE_IMPORT_DOCK,
FEATURE_MAX
};
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 8544bb8856..26281a232b 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -5344,9 +5344,11 @@ void EditorNode::_feature_profile_changed() {
TabContainer *node_tabs = cast_to<TabContainer>(node_dock->get_parent());
TabContainer *fs_tabs = cast_to<TabContainer>(filesystem_dock->get_parent());
if (profile.is_valid()) {
- import_tabs->set_tab_hidden(import_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_IMPORT_DOCK));
node_tabs->set_tab_hidden(node_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_NODE_DOCK));
- fs_tabs->set_tab_hidden(filesystem_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_FILESYSTEM_DOCK));
+ // The Import dock is useless without the FileSystem dock. Ensure the configuration is valid.
+ bool fs_dock_disabled = profile->is_feature_disabled(EditorFeatureProfile::FEATURE_FILESYSTEM_DOCK);
+ fs_tabs->set_tab_hidden(filesystem_dock->get_index(), fs_dock_disabled);
+ import_tabs->set_tab_hidden(import_dock->get_index(), fs_dock_disabled || profile->is_feature_disabled(EditorFeatureProfile::FEATURE_IMPORT_DOCK));
main_editor_buttons[EDITOR_3D]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D));
main_editor_buttons[EDITOR_SCRIPT]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT));
diff --git a/platform/android/java/lib/res/values/dimens.xml b/platform/android/java/lib/res/values/dimens.xml
new file mode 100644
index 0000000000..9034dbbcc1
--- /dev/null
+++ b/platform/android/java/lib/res/values/dimens.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="text_edit_height">48dp</dimen>
+</resources>
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 1b55090451..524f32bf5e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -151,8 +151,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
private void setButtonPausedState(boolean paused) {
mStatePaused = paused;
- int stringResourceID = paused ? R.string.text_button_resume :
- R.string.text_button_pause;
+ int stringResourceID = paused ? R.string.text_button_resume : R.string.text_button_pause;
mPauseButton.setText(stringResourceID);
}
@@ -221,7 +220,8 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
// GodotEditText layout
GodotEditText editText = new GodotEditText(activity);
- editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,
+ (int)getResources().getDimension(R.dimen.text_edit_height)));
// ...add to FrameLayout
containerLayout.addView(editText);
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index d439d34c95..fd213859b7 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -632,11 +632,12 @@ class AnimatedTexture : public Texture2D {
//use readers writers lock for this, since its far more times read than written to
RWLock *rw_lock;
-private:
+public:
enum {
MAX_FRAMES = 256
};
+private:
RID proxy_ph;
RID proxy;
diff --git a/tests/test_expression.h b/tests/test_expression.h
new file mode 100644
index 0000000000..85d37d1460
--- /dev/null
+++ b/tests/test_expression.h
@@ -0,0 +1,431 @@
+/*************************************************************************/
+/* test_expression.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_EXPRESSION_H
+#define TEST_EXPRESSION_H
+
+#include "core/math/expression.h"
+
+#include "tests/test_macros.h"
+
+namespace TestExpression {
+
+TEST_CASE("[Expression] Integer arithmetic") {
+ Expression expression;
+
+ CHECK_MESSAGE(
+ expression.parse("-123456") == OK,
+ "Integer identity should parse successfully.");
+ CHECK_MESSAGE(
+ int(expression.execute()) == -123456,
+ "Integer identity should return the expected result.");
+
+ CHECK_MESSAGE(
+ expression.parse("2 + 3") == OK,
+ "Integer addition should parse successfully.");
+ CHECK_MESSAGE(
+ int(expression.execute()) == 5,
+ "Integer addition should return the expected result.");
+
+ CHECK_MESSAGE(
+ expression.parse("999999999999 + 999999999999") == OK,
+ "Large integer addition should parse successfully.");
+ CHECK_MESSAGE(
+ int64_t(expression.execute()) == 1'999'999'999'998,
+ "Large integer addition should return the expected result.");
+
+ CHECK_MESSAGE(
+ expression.parse("25 / 10") == OK,
+ "Integer / integer division should parse successfully.");
+ CHECK_MESSAGE(
+ int(expression.execute()) == 2,
+ "Integer / integer divsion should return the expected result.");
+
+ CHECK_MESSAGE(
+ expression.parse("2 * (6 + 14) / 2 - 5") == OK,
+ "Integer multiplication-addition-subtraction-division should parse successfully.");
+ CHECK_MESSAGE(
+ int(expression.execute()) == 15,
+ "Integer multiplication-addition-subtraction-division should return the expected result.");
+}
+
+TEST_CASE("[Expression] Floating-point arithmetic") {
+ Expression expression;
+
+ CHECK_MESSAGE(
+ expression.parse("-123.456") == OK,
+ "Float identity should parse successfully.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(float(expression.execute()), -123.456),
+ "Float identity should return the expected result.");
+
+ CHECK_MESSAGE(
+ expression.parse("2.0 + 3.0") == OK,
+ "Float addition should parse successfully.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(float(expression.execute()), 5),
+ "Float addition should return the expected result.");
+
+ CHECK_MESSAGE(
+ expression.parse("3.0 / 10") == OK,
+ "Float / integer division should parse successfully.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(float(expression.execute()), 0.3),
+ "Float / integer divsion should return the expected result.");
+
+ CHECK_MESSAGE(
+ expression.parse("3 / 10.0") == OK,
+ "Basic integer / float division should parse successfully.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(float(expression.execute()), 0.3),
+ "Basic integer / float divsion should return the expected result.");
+
+ CHECK_MESSAGE(
+ expression.parse("3.0 / 10.0") == OK,
+ "Float / float division should parse successfully.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(float(expression.execute()), 0.3),
+ "Float / float divsion should return the expected result.");
+
+ CHECK_MESSAGE(
+ expression.parse("2.5 * (6.0 + 14.25) / 2.0 - 5.12345") == OK,
+ "Float multiplication-addition-subtraction-division should parse successfully.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(float(expression.execute()), 20.18905),
+ "Float multiplication-addition-subtraction-division should return the expected result.");
+}
+
+TEST_CASE("[Expression] Scientific notation") {
+ Expression expression;
+
+ CHECK_MESSAGE(
+ expression.parse("2.e5") == OK,
+ "The expression should parse successfully.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(float(expression.execute()), 200'000),
+ "The expression should return the expected result.");
+
+ // The middle "e" is ignored here.
+ CHECK_MESSAGE(
+ expression.parse("2e5") == OK,
+ "The expression should parse successfully.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(float(expression.execute()), 25),
+ "The expression should return the expected result.");
+
+ CHECK_MESSAGE(
+ expression.parse("2e.5") == OK,
+ "The expression should parse successfully.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(float(expression.execute()), 2),
+ "The expression should return the expected result.");
+}
+
+TEST_CASE("[Expression] Built-in functions") {
+ Expression expression;
+
+ CHECK_MESSAGE(
+ expression.parse("sqrt(pow(3, 2) + pow(4, 2))") == OK,
+ "The expression should parse successfully.");
+ CHECK_MESSAGE(
+ int(expression.execute()) == 5,
+ "`sqrt(pow(3, 2) + pow(4, 2))` should return the expected result.");
+
+ CHECK_MESSAGE(
+ expression.parse("stepify(sin(0.5), 0.01)") == OK,
+ "The expression should parse successfully.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(float(expression.execute()), 0.48),
+ "`stepify(sin(0.5), 0.01)` should return the expected result.");
+
+ CHECK_MESSAGE(
+ expression.parse("pow(2.0, -2500)") == OK,
+ "The expression should parse successfully.");
+ CHECK_MESSAGE(
+ Math::is_zero_approx(float(expression.execute())),
+ "`pow(2.0, -2500)` should return the expected result (asymptotically zero).");
+}
+
+TEST_CASE("[Expression] Boolean expressions") {
+ Expression expression;
+
+ CHECK_MESSAGE(
+ expression.parse("24 >= 12") == OK,
+ "The boolean expression should parse successfully.");
+ CHECK_MESSAGE(
+ bool(expression.execute()),
+ "The boolean expression should evaluate to `true`.");
+
+ CHECK_MESSAGE(
+ expression.parse("1.0 < 1.25 && 1.25 < 2.0") == OK,
+ "The boolean expression should parse successfully.");
+ CHECK_MESSAGE(
+ bool(expression.execute()),
+ "The boolean expression should evaluate to `true`.");
+
+ CHECK_MESSAGE(
+ expression.parse("!2") == OK,
+ "The boolean expression should parse successfully.");
+ CHECK_MESSAGE(
+ !bool(expression.execute()),
+ "The boolean expression should evaluate to `false`.");
+
+ CHECK_MESSAGE(
+ expression.parse("!!2") == OK,
+ "The boolean expression should parse successfully.");
+ CHECK_MESSAGE(
+ bool(expression.execute()),
+ "The boolean expression should evaluate to `true`.");
+
+ CHECK_MESSAGE(
+ expression.parse("!0") == OK,
+ "The boolean expression should parse successfully.");
+ CHECK_MESSAGE(
+ bool(expression.execute()),
+ "The boolean expression should evaluate to `true`.");
+
+ CHECK_MESSAGE(
+ expression.parse("!!0") == OK,
+ "The boolean expression should parse successfully.");
+ CHECK_MESSAGE(
+ !bool(expression.execute()),
+ "The boolean expression should evaluate to `false`.");
+
+ CHECK_MESSAGE(
+ expression.parse("2 && 5") == OK,
+ "The boolean expression should parse successfully.");
+ CHECK_MESSAGE(
+ bool(expression.execute()),
+ "The boolean expression should evaluate to `true`.");
+
+ CHECK_MESSAGE(
+ expression.parse("0 || 0") == OK,
+ "The boolean expression should parse successfully.");
+ CHECK_MESSAGE(
+ !bool(expression.execute()),
+ "The boolean expression should evaluate to `false`.");
+
+ CHECK_MESSAGE(
+ expression.parse("(2 <= 4) && (2 > 5)") == OK,
+ "The boolean expression should parse successfully.");
+ CHECK_MESSAGE(
+ !bool(expression.execute()),
+ "The boolean expression should evaluate to `false`.");
+}
+
+TEST_CASE("[Expression] Expressions with variables") {
+ Expression expression;
+
+ PackedStringArray parameter_names;
+ parameter_names.push_back("foo");
+ parameter_names.push_back("bar");
+ CHECK_MESSAGE(
+ expression.parse("foo + bar + 50", parameter_names) == OK,
+ "The expression should parse successfully.");
+ Array values;
+ values.push_back(60);
+ values.push_back(20);
+ CHECK_MESSAGE(
+ int(expression.execute(values)) == 130,
+ "The expression should return the expected value.");
+
+ PackedStringArray parameter_names_invalid;
+ parameter_names_invalid.push_back("foo");
+ parameter_names_invalid.push_back("baz"); // Invalid parameter name.
+ CHECK_MESSAGE(
+ expression.parse("foo + bar + 50", parameter_names_invalid) == OK,
+ "The expression should parse successfully.");
+ Array values_invalid;
+ values_invalid.push_back(60);
+ values_invalid.push_back(20);
+ // Invalid parameters will parse successfully but print an error message when executing.
+ ERR_PRINT_OFF;
+ CHECK_MESSAGE(
+ int(expression.execute(values_invalid)) == 0,
+ "The expression should return the expected value.");
+ ERR_PRINT_ON;
+
+ // Mismatched argument count (more values than parameters).
+ PackedStringArray parameter_names_mismatch;
+ parameter_names_mismatch.push_back("foo");
+ parameter_names_mismatch.push_back("bar");
+ CHECK_MESSAGE(
+ expression.parse("foo + bar + 50", parameter_names_mismatch) == OK,
+ "The expression should parse successfully.");
+ Array values_mismatch;
+ values_mismatch.push_back(60);
+ values_mismatch.push_back(20);
+ values_mismatch.push_back(110);
+ CHECK_MESSAGE(
+ int(expression.execute(values_mismatch)) == 130,
+ "The expression should return the expected value.");
+
+ // Mismatched argument count (more parameters than values).
+ PackedStringArray parameter_names_mismatch2;
+ parameter_names_mismatch2.push_back("foo");
+ parameter_names_mismatch2.push_back("bar");
+ parameter_names_mismatch2.push_back("baz");
+ CHECK_MESSAGE(
+ expression.parse("foo + bar + baz + 50", parameter_names_mismatch2) == OK,
+ "The expression should parse successfully.");
+ Array values_mismatch2;
+ values_mismatch2.push_back(60);
+ values_mismatch2.push_back(20);
+ // Having more parameters than values will parse successfully but print an
+ // error message when executing.
+ ERR_PRINT_OFF;
+ CHECK_MESSAGE(
+ int(expression.execute(values_mismatch2)) == 0,
+ "The expression should return the expected value.");
+ ERR_PRINT_ON;
+}
+
+TEST_CASE("[Expression] Invalid expressions") {
+ Expression expression;
+
+ CHECK_MESSAGE(
+ expression.parse("\\") == ERR_INVALID_PARAMETER,
+ "The expression shouldn't parse successfully.");
+
+ CHECK_MESSAGE(
+ expression.parse("0++") == ERR_INVALID_PARAMETER,
+ "The expression shouldn't parse successfully.");
+
+ CHECK_MESSAGE(
+ expression.parse("()") == ERR_INVALID_PARAMETER,
+ "The expression shouldn't parse successfully.");
+
+ CHECK_MESSAGE(
+ expression.parse("()()") == ERR_INVALID_PARAMETER,
+ "The expression shouldn't parse successfully.");
+
+ CHECK_MESSAGE(
+ expression.parse("() - ()") == ERR_INVALID_PARAMETER,
+ "The expression shouldn't parse successfully.");
+
+ CHECK_MESSAGE(
+ expression.parse("() * 12345") == ERR_INVALID_PARAMETER,
+ "The expression shouldn't parse successfully.");
+
+ CHECK_MESSAGE(
+ expression.parse("() * 12345") == ERR_INVALID_PARAMETER,
+ "The expression shouldn't parse successfully.");
+
+ CHECK_MESSAGE(
+ expression.parse("123'456") == ERR_INVALID_PARAMETER,
+ "The expression shouldn't parse successfully.");
+
+ CHECK_MESSAGE(
+ expression.parse("123\"456") == ERR_INVALID_PARAMETER,
+ "The expression shouldn't parse successfully.");
+}
+
+TEST_CASE("[Expression] Unusual expressions") {
+ Expression expression;
+
+ // Redundant parentheses don't cause a parse error as long as they're matched.
+ CHECK_MESSAGE(
+ expression.parse("(((((((((((((((666)))))))))))))))") == OK,
+ "The expression should parse successfully.");
+
+ // Using invalid identifiers doesn't cause a parse error.
+ ERR_PRINT_OFF;
+ CHECK_MESSAGE(
+ expression.parse("hello + hello") == OK,
+ "The expression should parse successfully.");
+ CHECK_MESSAGE(
+ int(expression.execute()) == 0,
+ "The expression should return the expected result.");
+ ERR_PRINT_ON;
+
+ ERR_PRINT_OFF;
+ CHECK_MESSAGE(
+ expression.parse("$1.00 + €5") == OK,
+ "The expression should parse successfully.");
+ CHECK_MESSAGE(
+ int(expression.execute()) == 0,
+ "The expression should return the expected result.");
+ ERR_PRINT_ON;
+
+ // Commas can't be used as a decimal parameter.
+ CHECK_MESSAGE(
+ expression.parse("123,456") == OK,
+ "The expression should parse successfully.");
+ CHECK_MESSAGE(
+ int(expression.execute()) == 123,
+ "The expression should return the expected result.");
+
+ // Spaces can't be used as a separator for large numbers.
+ CHECK_MESSAGE(
+ expression.parse("123 456") == OK,
+ "The expression should parse successfully.");
+ CHECK_MESSAGE(
+ int(expression.execute()) == 123,
+ "The expression should return the expected result.");
+
+ // Division by zero is accepted, even though it prints an error message normally.
+ CHECK_MESSAGE(
+ expression.parse("-25.4 / 0") == OK,
+ "The expression should parse successfully.");
+ ERR_PRINT_OFF;
+ CHECK_MESSAGE(
+ Math::is_zero_approx(float(expression.execute())),
+ "`-25.4 / 0` should return 0.");
+ ERR_PRINT_ON;
+
+ CHECK_MESSAGE(
+ expression.parse("0 / 0") == OK,
+ "The expression should parse successfully.");
+ ERR_PRINT_OFF;
+ CHECK_MESSAGE(
+ int(expression.execute()) == 0,
+ "`0 / 0` should return 0.");
+ ERR_PRINT_ON;
+
+ // The tests below currently crash the engine.
+ //
+ //CHECK_MESSAGE(
+ // expression.parse("(-9223372036854775807 - 1) % -1") == OK,
+ // "The expression should parse successfully.");
+ //CHECK_MESSAGE(
+ // int64_t(expression.execute()) == 0,
+ // "`(-9223372036854775807 - 1) % -1` should return the expected result.");
+ //
+ //CHECK_MESSAGE(
+ // expression.parse("(-9223372036854775807 - 1) / -1") == OK,
+ // "The expression should parse successfully.");
+ //CHECK_MESSAGE(
+ // int64_t(expression.execute()) == 0,
+ // "`(-9223372036854775807 - 1) / -1` should return the expected result.");
+}
+
+} // namespace TestExpression
+
+#endif // TEST_EXPRESSION_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 892cc26a51..43c1ef331f 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -36,6 +36,7 @@
#include "test_basis.h"
#include "test_class_db.h"
#include "test_color.h"
+#include "test_expression.h"
#include "test_gdscript.h"
#include "test_gradient.h"
#include "test_gui.h"