summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test_array.h186
-rw-r--r--tests/test_class_db.h43
-rw-r--r--tests/test_main.cpp2
-rw-r--r--tests/test_path_follow_2d.h229
-rw-r--r--tests/test_path_follow_3d.h220
-rw-r--r--tests/test_render.cpp4
-rw-r--r--tests/test_resource.h114
7 files changed, 791 insertions, 7 deletions
diff --git a/tests/test_array.h b/tests/test_array.h
new file mode 100644
index 0000000000..52da256860
--- /dev/null
+++ b/tests/test_array.h
@@ -0,0 +1,186 @@
+/*************************************************************************/
+/* test_array.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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_ARRAY_H
+#define TEST_ARRAY_H
+
+#include "core/object/class_db.h"
+#include "core/object/script_language.h"
+#include "core/templates/hashfuncs.h"
+#include "core/templates/vector.h"
+#include "core/variant/array.h"
+#include "core/variant/container_type_validate.h"
+#include "core/variant/variant.h"
+#include "tests/test_macros.h"
+
+namespace TestArray {
+
+TEST_CASE("[Array] size(), clear(), and is_empty()") {
+ Array arr;
+ CHECK(arr.size() == 0);
+ CHECK(arr.is_empty());
+ arr.push_back(1);
+ CHECK(arr.size() == 1);
+ arr.clear();
+ CHECK(arr.is_empty());
+ CHECK(arr.size() == 0);
+}
+
+TEST_CASE("[Array] Assignment and comparison operators") {
+ Array arr1;
+ Array arr2;
+ arr1.push_back(1);
+ CHECK(arr1 != arr2);
+ CHECK(arr1 > arr2);
+ CHECK(arr1 >= arr2);
+ arr2.push_back(2);
+ CHECK(arr1 != arr2);
+ CHECK(arr1 < arr2);
+ CHECK(arr1 <= arr2);
+ CHECK(arr2 > arr1);
+ CHECK(arr2 >= arr1);
+ Array arr3 = arr2;
+ CHECK(arr3 == arr2);
+}
+
+TEST_CASE("[Array] append_array()") {
+ Array arr1;
+ Array arr2;
+ arr1.push_back(1);
+ arr1.append_array(arr2);
+ CHECK(arr1.size() == 1);
+ arr2.push_back(2);
+ arr1.append_array(arr2);
+ CHECK(arr1.size() == 2);
+ CHECK(int(arr1[0]) == 1);
+ CHECK(int(arr1[1]) == 2);
+}
+
+TEST_CASE("[Array] resize(), insert(), and erase()") {
+ Array arr;
+ arr.resize(2);
+ CHECK(arr.size() == 2);
+ arr.insert(0, 1);
+ CHECK(int(arr[0]) == 1);
+ arr.insert(0, 2);
+ CHECK(int(arr[0]) == 2);
+ arr.erase(2);
+ CHECK(int(arr[0]) == 1);
+}
+
+TEST_CASE("[Array] front() and back()") {
+ Array arr;
+ arr.push_back(1);
+ CHECK(int(arr.front()) == 1);
+ CHECK(int(arr.back()) == 1);
+ arr.push_back(3);
+ CHECK(int(arr.front()) == 1);
+ CHECK(int(arr.back()) == 3);
+}
+
+TEST_CASE("[Array] has() and count()") {
+ Array arr;
+ arr.push_back(1);
+ arr.push_back(1);
+ CHECK(arr.has(1));
+ CHECK(!arr.has(2));
+ CHECK(arr.count(1) == 2);
+ CHECK(arr.count(2) == 0);
+}
+
+TEST_CASE("[Array] remove()") {
+ Array arr;
+ arr.push_back(1);
+ arr.push_back(2);
+ arr.remove(0);
+ CHECK(arr.size() == 1);
+ CHECK(int(arr[0]) == 2);
+ arr.remove(0);
+ CHECK(arr.size() == 0);
+
+ // The array is now empty; try to use `remove()` again.
+ // Normally, this prints an error message so we silence it.
+ ERR_PRINT_OFF;
+ arr.remove(0);
+ ERR_PRINT_ON;
+
+ CHECK(arr.size() == 0);
+}
+
+TEST_CASE("[Array] get()") {
+ Array arr;
+ arr.push_back(1);
+ CHECK(int(arr.get(0)) == 1);
+}
+
+TEST_CASE("[Array] sort()") {
+ Array arr;
+
+ arr.push_back(3);
+ arr.push_back(4);
+ arr.push_back(2);
+ arr.push_back(1);
+ arr.sort();
+ int val = 1;
+ for (int i = 0; i < arr.size(); i++) {
+ CHECK(int(arr[i]) == val);
+ val++;
+ }
+}
+
+TEST_CASE("[Array] push_front(), pop_front(), pop_back()") {
+ Array arr;
+ arr.push_front(1);
+ arr.push_front(2);
+ CHECK(int(arr[0]) == 2);
+ arr.pop_front();
+ CHECK(int(arr[0]) == 1);
+ CHECK(arr.size() == 1);
+ arr.push_front(2);
+ arr.push_front(3);
+ arr.pop_back();
+ CHECK(int(arr[1]) == 2);
+ CHECK(arr.size() == 2);
+}
+
+TEST_CASE("[Array] max() and min()") {
+ Array arr;
+ arr.push_back(3);
+ arr.push_front(4);
+ arr.push_back(5);
+ arr.push_back(2);
+ int max = int(arr.max());
+ int min = int(arr.min());
+ CHECK(max == 5);
+ CHECK(min == 2);
+}
+} // namespace TestArray
+
+#endif // TEST_ARRAY_H
diff --git a/tests/test_class_db.h b/tests/test_class_db.h
index b1440b83ef..9ef4569c14 100644
--- a/tests/test_class_db.h
+++ b/tests/test_class_db.h
@@ -340,7 +340,14 @@ void validate_property(const Context &p_context, const ExposedClass &p_class, co
if (prop_class) {
TEST_COND(prop_class->is_singleton,
"Property type is a singleton: '", p_class.name, ".", String(p_prop.name), "'.");
+
+ if (p_class.api_type == ClassDB::API_CORE) {
+ TEST_COND(prop_class->api_type == ClassDB::API_EDITOR,
+ "Property '", p_class.name, ".", p_prop.name, "' has type '", prop_class->name,
+ "' from the editor API. Core API cannot have dependencies on the editor API.");
+ }
} else {
+ // Look for types that don't inherit Object
TEST_FAIL_COND(!p_context.has_type(prop_type_ref),
"Property type '", prop_type_ref.name, "' not found: '", p_class.name, ".", String(p_prop.name), "'.");
}
@@ -370,10 +377,22 @@ void validate_property(const Context &p_context, const ExposedClass &p_class, co
}
void validate_method(const Context &p_context, const ExposedClass &p_class, const MethodData &p_method) {
- const ExposedClass *return_class = p_context.find_exposed_class(p_method.return_type);
- if (return_class) {
- TEST_COND(return_class->is_singleton,
- "Method return type is a singleton: '", p_class.name, ".", p_method.name, "'.");
+ if (p_method.return_type.name != StringName()) {
+ const ExposedClass *return_class = p_context.find_exposed_class(p_method.return_type);
+ if (return_class) {
+ TEST_COND(return_class->is_singleton,
+ "Method return type is a singleton: '", p_class.name, ".", p_method.name, "'.");
+
+ if (p_class.api_type == ClassDB::API_CORE) {
+ TEST_COND(return_class->api_type == ClassDB::API_EDITOR,
+ "Method '", p_class.name, ".", p_method.name, "' has return type '", return_class->name,
+ "' from the editor API. Core API cannot have dependencies on the editor API.");
+ }
+ } else {
+ // Look for types that don't inherit Object
+ TEST_FAIL_COND(!p_context.has_type(p_method.return_type),
+ "Method return type '", p_method.return_type.name, "' not found: '", p_class.name, ".", p_method.name, "'.");
+ }
}
for (const List<ArgumentData>::Element *F = p_method.arguments.front(); F; F = F->next()) {
@@ -383,7 +402,14 @@ void validate_method(const Context &p_context, const ExposedClass &p_class, cons
if (arg_class) {
TEST_COND(arg_class->is_singleton,
"Argument type is a singleton: '", arg.name, "' of method '", p_class.name, ".", p_method.name, "'.");
+
+ if (p_class.api_type == ClassDB::API_CORE) {
+ TEST_COND(arg_class->api_type == ClassDB::API_EDITOR,
+ "Argument '", arg.name, "' of method '", p_class.name, ".", p_method.name, "' has type '",
+ arg_class->name, "' from the editor API. Core API cannot have dependencies on the editor API.");
+ }
} else {
+ // Look for types that don't inherit Object
TEST_FAIL_COND(!p_context.has_type(arg.type),
"Argument type '", arg.type.name, "' not found: '", arg.name, "' of method", p_class.name, ".", p_method.name, "'.");
}
@@ -407,8 +433,15 @@ void validate_signal(const Context &p_context, const ExposedClass &p_class, cons
const ExposedClass *arg_class = p_context.find_exposed_class(arg.type);
if (arg_class) {
TEST_COND(arg_class->is_singleton,
- "Argument class is a singleton: '", arg.name, "' of signal", p_class.name, ".", p_signal.name, "'.");
+ "Argument class is a singleton: '", arg.name, "' of signal '", p_class.name, ".", p_signal.name, "'.");
+
+ if (p_class.api_type == ClassDB::API_CORE) {
+ TEST_COND(arg_class->api_type == ClassDB::API_EDITOR,
+ "Argument '", arg.name, "' of signal '", p_class.name, ".", p_signal.name, "' has type '",
+ arg_class->name, "' from the editor API. Core API cannot have dependencies on the editor API.");
+ }
} else {
+ // Look for types that don't inherit Object
TEST_FAIL_COND(!p_context.has_type(arg.type),
"Argument type '", arg.type.name, "' not found: '", arg.name, "' of signal", p_class.name, ".", p_signal.name, "'.");
}
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 2697eb6399..df43b7424f 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -33,6 +33,7 @@
#include "core/templates/list.h"
#include "test_aabb.h"
+#include "test_array.h"
#include "test_astar.h"
#include "test_basis.h"
#include "test_class_db.h"
@@ -65,6 +66,7 @@
#include "test_random_number_generator.h"
#include "test_rect2.h"
#include "test_render.h"
+#include "test_resource.h"
#include "test_shader_lang.h"
#include "test_string.h"
#include "test_text_server.h"
diff --git a/tests/test_path_follow_2d.h b/tests/test_path_follow_2d.h
new file mode 100644
index 0000000000..28b62de5bb
--- /dev/null
+++ b/tests/test_path_follow_2d.h
@@ -0,0 +1,229 @@
+/*************************************************************************/
+/* test_path_follow_2d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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_PATH_FOLLOW_2D_H
+#define TEST_PATH_FOLLOW_2D_H
+
+#include "scene/2d/path_2d.h"
+#include "scene/resources/curve.h"
+
+#include "tests/test_macros.h"
+
+namespace TestPathFollow2D {
+
+TEST_CASE("[PathFollow2D] Sampling with unit offset") {
+ const Ref<Curve2D> &curve = memnew(Curve2D());
+ curve->add_point(Vector2(0, 0));
+ curve->add_point(Vector2(100, 0));
+ curve->add_point(Vector2(100, 100));
+ curve->add_point(Vector2(0, 100));
+ curve->add_point(Vector2(0, 0));
+ const Ref<Path2D> &path = memnew(Path2D());
+ path->set_curve(curve);
+ const Ref<PathFollow2D> &path_follow_2d = memnew(PathFollow2D());
+ path->add_child(path_follow_2d);
+
+ path_follow_2d->set_unit_offset(0);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(0, 0)));
+
+ path_follow_2d->set_unit_offset(0.125);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(50, 0)));
+
+ path_follow_2d->set_unit_offset(0.25);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(100, 0)));
+
+ path_follow_2d->set_unit_offset(0.375);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(100, 50)));
+
+ path_follow_2d->set_unit_offset(0.5);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(100, 100)));
+
+ path_follow_2d->set_unit_offset(0.625);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(50, 100)));
+
+ path_follow_2d->set_unit_offset(0.75);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(0, 100)));
+
+ path_follow_2d->set_unit_offset(0.875);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(0, 50)));
+
+ path_follow_2d->set_unit_offset(1);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(0, 0)));
+}
+
+TEST_CASE("[PathFollow2D] Sampling with offset") {
+ const Ref<Curve2D> &curve = memnew(Curve2D());
+ curve->add_point(Vector2(0, 0));
+ curve->add_point(Vector2(100, 0));
+ curve->add_point(Vector2(100, 100));
+ curve->add_point(Vector2(0, 100));
+ curve->add_point(Vector2(0, 0));
+ const Ref<Path2D> &path = memnew(Path2D());
+ path->set_curve(curve);
+ const Ref<PathFollow2D> &path_follow_2d = memnew(PathFollow2D());
+ path->add_child(path_follow_2d);
+
+ path_follow_2d->set_offset(0);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(0, 0)));
+
+ path_follow_2d->set_offset(50);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(50, 0)));
+
+ path_follow_2d->set_offset(100);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(100, 0)));
+
+ path_follow_2d->set_offset(150);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(100, 50)));
+
+ path_follow_2d->set_offset(200);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(100, 100)));
+
+ path_follow_2d->set_offset(250);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(50, 100)));
+
+ path_follow_2d->set_offset(300);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(0, 100)));
+
+ path_follow_2d->set_offset(350);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(0, 50)));
+
+ path_follow_2d->set_offset(400);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(0, 0)));
+}
+
+TEST_CASE("[PathFollow2D] Removal of a point in curve") {
+ const Ref<Curve2D> &curve = memnew(Curve2D());
+ curve->add_point(Vector2(0, 0));
+ curve->add_point(Vector2(100, 0));
+ curve->add_point(Vector2(100, 100));
+ const Ref<Path2D> &path = memnew(Path2D());
+ path->set_curve(curve);
+ const Ref<PathFollow2D> &path_follow_2d = memnew(PathFollow2D());
+ path->add_child(path_follow_2d);
+
+ path_follow_2d->set_unit_offset(0.5);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(100, 0)));
+
+ curve->remove_point(1);
+
+ CHECK_MESSAGE(
+ path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(50, 50)),
+ "Path follow's position should be updated after removing a point from the curve");
+}
+
+TEST_CASE("[PathFollow2D] Setting h_offset and v_offset") {
+ const Ref<Curve2D> &curve = memnew(Curve2D());
+ curve->add_point(Vector2(0, 0));
+ curve->add_point(Vector2(100, 0));
+ const Ref<Path2D> &path = memnew(Path2D());
+ path->set_curve(curve);
+ const Ref<PathFollow2D> &path_follow_2d = memnew(PathFollow2D());
+ path->add_child(path_follow_2d);
+
+ path_follow_2d->set_unit_offset(0.5);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(50, 0)));
+
+ path_follow_2d->set_h_offset(25);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(75, 0)));
+
+ path_follow_2d->set_v_offset(25);
+ CHECK(path_follow_2d->get_transform().get_origin().is_equal_approx(Vector2(75, 25)));
+}
+
+TEST_CASE("[PathFollow2D] Unit offset out of range") {
+ const Ref<Curve2D> &curve = memnew(Curve2D());
+ curve->add_point(Vector2(0, 0));
+ curve->add_point(Vector2(100, 0));
+ const Ref<Path2D> &path = memnew(Path2D());
+ path->set_curve(curve);
+ const Ref<PathFollow2D> &path_follow_2d = memnew(PathFollow2D());
+ path->add_child(path_follow_2d);
+
+ path_follow_2d->set_loop(true);
+
+ path_follow_2d->set_unit_offset(-0.3);
+ CHECK_MESSAGE(
+ path_follow_2d->get_unit_offset() == 0.7,
+ "Unit Offset should loop back from the end in the opposite direction");
+
+ path_follow_2d->set_unit_offset(1.3);
+ CHECK_MESSAGE(
+ path_follow_2d->get_unit_offset() == 0.3,
+ "Unit Offset should loop back from the end in the opposite direction");
+
+ path_follow_2d->set_loop(false);
+
+ path_follow_2d->set_unit_offset(-0.3);
+ CHECK_MESSAGE(
+ path_follow_2d->get_unit_offset() == 0,
+ "Unit Offset should be clamped at 0");
+
+ path_follow_2d->set_unit_offset(1.3);
+ CHECK_MESSAGE(
+ path_follow_2d->get_unit_offset() == 1,
+ "Unit Offset should be clamped at 1");
+}
+
+TEST_CASE("[PathFollow2D] Offset out of range") {
+ const Ref<Curve2D> &curve = memnew(Curve2D());
+ curve->add_point(Vector2(0, 0));
+ curve->add_point(Vector2(100, 0));
+ const Ref<Path2D> &path = memnew(Path2D());
+ path->set_curve(curve);
+ const Ref<PathFollow2D> &path_follow_2d = memnew(PathFollow2D());
+ path->add_child(path_follow_2d);
+
+ path_follow_2d->set_loop(true);
+
+ path_follow_2d->set_offset(-50);
+ CHECK_MESSAGE(
+ path_follow_2d->get_offset() == 50,
+ "Offset should loop back from the end in the opposite direction");
+
+ path_follow_2d->set_offset(150);
+ CHECK_MESSAGE(
+ path_follow_2d->get_offset() == 50,
+ "Offset should loop back from the end in the opposite direction");
+
+ path_follow_2d->set_loop(false);
+
+ path_follow_2d->set_offset(-50);
+ CHECK_MESSAGE(
+ path_follow_2d->get_offset() == 0,
+ "Offset should be clamped at 0");
+
+ path_follow_2d->set_offset(150);
+ CHECK_MESSAGE(
+ path_follow_2d->get_offset() == 100,
+ "Offset should be clamped at 1");
+}
+} // namespace TestPathFollow2D
+
+#endif // TEST_PATH_FOLLOW_2D_H
diff --git a/tests/test_path_follow_3d.h b/tests/test_path_follow_3d.h
new file mode 100644
index 0000000000..b6b4c88222
--- /dev/null
+++ b/tests/test_path_follow_3d.h
@@ -0,0 +1,220 @@
+/*************************************************************************/
+/* test_path_follow_3d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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_PATH_FOLLOW_3D_H
+#define TEST_PATH_FOLLOW_3D_H
+
+#include "scene/3d/path_3d.h"
+#include "scene/resources/curve.h"
+
+#include "tests/test_macros.h"
+
+namespace TestPathFollow3D {
+
+TEST_CASE("[PathFollow3D] Sampling with unit offset") {
+ const Ref<Curve3D> &curve = memnew(Curve3D());
+ curve->add_point(Vector3(0, 0, 0));
+ curve->add_point(Vector3(100, 0, 0));
+ curve->add_point(Vector3(100, 100, 0));
+ curve->add_point(Vector3(100, 100, 100));
+ curve->add_point(Vector3(100, 0, 100));
+ const Path3D *path = memnew(Path3D);
+ path->set_curve(curve);
+ const PathFollow3D *path_follow_3d = memnew(PathFollow3D);
+ path->add_child(path_follow_3d);
+
+ path_follow_3d->set_unit_offset(0);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(0, 0, 0));
+
+ path_follow_3d->set_unit_offset(0.125);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(50, 0, 0));
+
+ path_follow_3d->set_unit_offset(0.25);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 0, 0);
+
+ path_follow_3d->set_unit_offset(0.375);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 50, 0)));
+
+ path_follow_3d->set_unit_offset(0.5);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 0)));
+
+ path_follow_3d->set_unit_offset(0.625);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 50)));
+
+ path_follow_3d->set_unit_offset(0.75);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 100)));
+
+ path_follow_3d->set_unit_offset(0.875);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 50, 100)));
+
+ path_follow_3d->set_unit_offset(1);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 0, 100)));
+
+ memdelete(path);
+}
+
+TEST_CASE("[PathFollow3D] Sampling with offset") {
+ const Ref<Curve3D> &curve = memnew(Curve3D());
+ curve->add_point(Vector3(0, 0, 0));
+ curve->add_point(Vector3(100, 0, 0));
+ curve->add_point(Vector3(100, 100, 0));
+ curve->add_point(Vector3(100, 100, 100));
+ curve->add_point(Vector3(100, 0, 100));
+ const Path3D *path = memnew(Path3D);
+ path->set_curve(curve);
+ const PathFollow3D *path_follow_3d = memnew(PathFollow3D);
+ path->add_child(path_follow_3d);
+
+ path_follow_3d->set_offset(0);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(0, 0, 0));
+
+ path_follow_3d->set_offset(50);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(50, 0, 0));
+
+ path_follow_3d->set_offset(100);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 0, 0);
+
+ path_follow_3d->set_offset(150);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 50, 0)));
+
+ path_follow_3d->set_offset(200);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 0)));
+
+ path_follow_3d->set_offset(250);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 50)));
+
+ path_follow_3d->set_offset(300);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 100)));
+
+ path_follow_3d->set_offset(350);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 50, 100)));
+
+ path_follow_3d->set_offset(400);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 0, 100)));
+
+ memdelete(path);
+}
+
+TEST_CASE("[PathFollow3D] Removal of a point in curve") {
+ const Ref<Curve3D> &curve = memnew(Curve3D());
+ curve->add_point(Vector3(0, 0, 0));
+ curve->add_point(Vector3(100, 0, 0));
+ curve->add_point(Vector3(100, 100, 0));
+ const Path3D *path = memnew(Path3D);
+ path->set_curve(curve);
+ const PathFollow3D *path_follow_3d = memnew(PathFollow3D);
+ path->add_child(path_follow_3d);
+
+ path_follow_3d->set_unit_offset(0.5);
+ CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector2(100, 0, 0)));
+
+ curve->remove_point(1);
+
+ CHECK_MESSAGE(
+ path_follow_3d->get_transform().get_origin().is_equal_approx(Vector2(50, 50, 0)),
+ "Path follow's position should be updated after removing a point from the curve");
+
+ memdelete(path);
+}
+
+TEST_CASE("[PathFollow3D] Unit offset out of range") {
+ const Ref<Curve3D> &curve = memnew(Curve3D());
+ curve->add_point(Vector3(0, 0, 0));
+ curve->add_point(Vector3(100, 0, 0));
+ const Path3D *path = memnew(Path3D);
+ path->set_curve(curve);
+ const PathFollow3D *path_follow_3d = memnew(PathFollow3D);
+ path->add_child(path_follow_3d);
+
+ path_follow_3d->set_loop(true);
+
+ path_follow_3d->set_unit_offset(-0.3);
+ CHECK_MESSAGE(
+ path_follow_3d->get_unit_offset() == 0.7,
+ "Unit Offset should loop back from the end in the opposite direction");
+
+ path_follow_3d->set_unit_offset(1.3);
+ CHECK_MESSAGE(
+ path_follow_3d->get_unit_offset() == 0.3,
+ "Unit Offset should loop back from the end in the opposite direction");
+
+ path_follow_3d->set_loop(false);
+
+ path_follow_3d->set_unit_offset(-0.3);
+ CHECK_MESSAGE(
+ path_follow_3d->get_unit_offset() == 0,
+ "Unit Offset should be clamped at 0");
+
+ path_follow_3d->set_unit_offset(1.3);
+ CHECK_MESSAGE(
+ path_follow_3d->get_unit_offset() == 1,
+ "Unit Offset should be clamped at 1");
+
+ memdelete(path);
+}
+
+TEST_CASE("[PathFollow3D] Offset out of range") {
+ const Ref<Curve3D> &curve = memnew(Curve3D());
+ curve->add_point(Vector3(0, 0, 0));
+ curve->add_point(Vector3(100, 0, 0));
+ const Path3D *path = memnew(Path3D);
+ path->set_curve(curve);
+ const PathFollow3D *path_follow_3d = memnew(PathFollow3D);
+ path->add_child(path_follow_3d);
+
+ path_follow_3d->set_loop(true);
+
+ path_follow_3d->set_offset(-50);
+ CHECK_MESSAGE(
+ path_follow_3d->get_offset() == 50,
+ "Offset should loop back from the end in the opposite direction");
+
+ path_follow_3d->set_offset(150);
+ CHECK_MESSAGE(
+ path_follow_3d->get_offset() == 50,
+ "Offset should loop back from the end in the opposite direction");
+
+ path_follow_3d->set_loop(false);
+
+ path_follow_3d->set_offset(-50);
+ CHECK_MESSAGE(
+ path_follow_3d->get_offset() == 0,
+ "Offset should be clamped at 0");
+
+ path_follow_3d->set_offset(150);
+ CHECK_MESSAGE(
+ path_follow_3d->get_offset() == 100,
+ "Offset should be clamped at max value of curve");
+
+ memdelete(path);
+}
+} // namespace TestPathFollow3D
+
+#endif // TEST_PATH_FOLLOW_3D_H
diff --git a/tests/test_render.cpp b/tests/test_render.cpp
index 2a4ae8bd73..72b2840098 100644
--- a/tests/test_render.cpp
+++ b/tests/test_render.cpp
@@ -183,8 +183,8 @@ public:
//vs->light_set_shadow( lightaux, true );
light = vs->instance_create2(lightaux, scenario);
Transform lla;
- //lla.set_look_at(Vector3(),Vector3(1,-1,1),Vector3(0,1,0));
- lla.set_look_at(Vector3(), Vector3(-0.000000, -0.836026, -0.548690), Vector3(0, 1, 0));
+ //lla.set_look_at(Vector3(),Vector3(1, -1, 1));
+ lla.set_look_at(Vector3(), Vector3(0.0, -0.836026, -0.548690));
vs->instance_set_transform(light, lla);
diff --git a/tests/test_resource.h b/tests/test_resource.h
new file mode 100644
index 0000000000..cee3281995
--- /dev/null
+++ b/tests/test_resource.h
@@ -0,0 +1,114 @@
+/*************************************************************************/
+/* test_resource.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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_RESOURCE
+#define TEST_RESOURCE
+
+#include "core/io/resource.h"
+#include "core/io/resource_loader.h"
+#include "core/io/resource_saver.h"
+#include "core/os/os.h"
+
+#include "thirdparty/doctest/doctest.h"
+
+namespace TestResource {
+
+TEST_CASE("[Resource] Duplication") {
+ Ref<Resource> resource = memnew(Resource);
+ resource->set_name("Hello world");
+ Ref<Resource> child_resource = memnew(Resource);
+ child_resource->set_name("I'm a child resource");
+ resource->set_meta("other_resource", child_resource);
+
+ Ref<Resource> resource_dupe = resource->duplicate();
+ const Ref<Resource> &resource_dupe_reference = resource_dupe;
+ resource_dupe->set_name("Changed name");
+ child_resource->set_name("My name was changed too");
+
+ CHECK_MESSAGE(
+ resource_dupe->get_name() == "Changed name",
+ "Duplicated resource should have the new name.");
+ CHECK_MESSAGE(
+ resource_dupe_reference->get_name() == "Changed name",
+ "Reference to the duplicated resource should have the new name.");
+ CHECK_MESSAGE(
+ resource->get_name() == "Hello world",
+ "Original resource name should not be affected after editing the duplicate's name.");
+ CHECK_MESSAGE(
+ Ref<Resource>(resource_dupe->get_meta("other_resource"))->get_name() == "My name was changed too",
+ "Duplicated resource should share its child resource with the original.");
+}
+
+TEST_CASE("[Resource] Saving and loading") {
+ Ref<Resource> resource = memnew(Resource);
+ resource->set_name("Hello world");
+ resource->set_meta(" ExampleMetadata ", Vector2i(40, 80));
+ resource->set_meta("string", "The\nstring\nwith\nunnecessary\nline\n\t\\\nbreaks");
+ Ref<Resource> child_resource = memnew(Resource);
+ child_resource->set_name("I'm a child resource");
+ resource->set_meta("other_resource", child_resource);
+ const String save_path_binary = OS::get_singleton()->get_cache_path().plus_file("resource.res");
+ const String save_path_text = OS::get_singleton()->get_cache_path().plus_file("resource.tres");
+ ResourceSaver::save(save_path_binary, resource);
+ ResourceSaver::save(save_path_text, resource);
+
+ const Ref<Resource> &loaded_resource_binary = ResourceLoader::load(save_path_binary);
+ CHECK_MESSAGE(
+ loaded_resource_binary->get_name() == "Hello world",
+ "The loaded resource name should be equal to the expected value.");
+ CHECK_MESSAGE(
+ loaded_resource_binary->get_meta(" ExampleMetadata ") == Vector2i(40, 80),
+ "The loaded resource metadata should be equal to the expected value.");
+ CHECK_MESSAGE(
+ loaded_resource_binary->get_meta("string") == "The\nstring\nwith\nunnecessary\nline\n\t\\\nbreaks",
+ "The loaded resource metadata should be equal to the expected value.");
+ const Ref<Resource> &loaded_child_resource_binary = loaded_resource_binary->get_meta("other_resource");
+ CHECK_MESSAGE(
+ loaded_child_resource_binary->get_name() == "I'm a child resource",
+ "The loaded child resource name should be equal to the expected value.");
+
+ const Ref<Resource> &loaded_resource_text = ResourceLoader::load(save_path_text);
+ CHECK_MESSAGE(
+ loaded_resource_text->get_name() == "Hello world",
+ "The loaded resource name should be equal to the expected value.");
+ CHECK_MESSAGE(
+ loaded_resource_text->get_meta(" ExampleMetadata ") == Vector2i(40, 80),
+ "The loaded resource metadata should be equal to the expected value.");
+ CHECK_MESSAGE(
+ loaded_resource_text->get_meta("string") == "The\nstring\nwith\nunnecessary\nline\n\t\\\nbreaks",
+ "The loaded resource metadata should be equal to the expected value.");
+ const Ref<Resource> &loaded_child_resource_text = loaded_resource_text->get_meta("other_resource");
+ CHECK_MESSAGE(
+ loaded_child_resource_text->get_name() == "I'm a child resource",
+ "The loaded child resource name should be equal to the expected value.");
+}
+} // namespace TestResource
+
+#endif // TEST_RESOURCE