diff options
20 files changed, 653 insertions, 9 deletions
diff --git a/core/math/quat.h b/core/math/quat.h index 9db914fe52..d9b130c050 100644 --- a/core/math/quat.h +++ b/core/math/quat.h @@ -28,14 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -// Circular dependency between Vector3 and Basis :/ -#include "core/math/vector3.h" - #ifndef QUAT_H #define QUAT_H #include "core/math/math_defs.h" #include "core/math/math_funcs.h" +#include "core/math/vector3.h" #include "core/string/ustring.h" class Quat { diff --git a/doc/classes/Node2D.xml b/doc/classes/Node2D.xml index eed0ec8d7e..988fb72267 100644 --- a/doc/classes/Node2D.xml +++ b/doc/classes/Node2D.xml @@ -154,7 +154,7 @@ If [code]true[/code], the node's Z index is relative to its parent's Z index. If this node's Z index is 2 and its parent's effective Z index is 3, then this node's effective Z index will be 2 + 3 = 5. </member> <member name="z_index" type="int" setter="set_z_index" getter="get_z_index" default="0"> - Z index. Controls the order in which the nodes render. A node with a higher Z index will display in front of others. + Z index. Controls the order in which the nodes render. A node with a higher Z index will display in front of others. Must be between [constant RenderingServer.CANVAS_ITEM_Z_MIN] and [constant RenderingServer.CANVAS_ITEM_Z_MAX] (inclusive). </member> </members> <constants> diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml index 229facd08b..cfa4215fd4 100644 --- a/doc/classes/PhysicsServer2D.xml +++ b/doc/classes/PhysicsServer2D.xml @@ -1018,6 +1018,15 @@ Activates or deactivates the 2D physics engine. </description> </method> + <method name="set_collision_iterations"> + <return type="void"> + </return> + <argument index="0" name="iterations" type="int"> + </argument> + <description> + Sets the amount of iterations for calculating velocities of colliding bodies. The greater the amount, the more accurate the collisions, but with a performance loss. + </description> + </method> <method name="shape_get_data" qualifiers="const"> <return type="Variant"> </return> diff --git a/modules/gdscript/tests/gdscript_test_runner_suite.h b/modules/gdscript/tests/gdscript_test_runner_suite.h index 136907b316..8fd77239cd 100644 --- a/modules/gdscript/tests/gdscript_test_runner_suite.h +++ b/modules/gdscript/tests/gdscript_test_runner_suite.h @@ -48,6 +48,27 @@ TEST_SUITE("[Modules][GDScript]") { } } +TEST_CASE("[Modules][GDScript] Load source code dynamically and run it") { + Ref<GDScript> gdscript = memnew(GDScript); + gdscript->set_source_code(R"( +extends Reference + +func _init(): + set_meta("result", 42) +)"); + // A spurious `Condition "err" is true` message is printed (despite parsing being successful and returning `OK`). + // Silence it. + ERR_PRINT_OFF; + const Error error = gdscript->reload(); + ERR_PRINT_ON; + CHECK_MESSAGE(error == OK, "The script should parse successfully."); + + // Run the script by assigning it to a reference-counted object. + Ref<Reference> reference = memnew(Reference); + reference->set_script(gdscript); + CHECK_MESSAGE(int(reference->get_meta("result")) == 42, "The script should assign object metadata successfully."); +} + } // namespace GDScriptTests #endif // GDSCRIPT_TEST_RUNNER_SUITE_H diff --git a/platform/android/detect.py b/platform/android/detect.py index 8808b032a6..1b6af8662e 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -250,7 +250,7 @@ def configure(env): env["RANLIB"] = tools_path + "/ranlib" env["AS"] = tools_path + "/as" - common_opts = ["-fno-integrated-as", "-gcc-toolchain", gcc_toolchain_path] + common_opts = ["-gcc-toolchain", gcc_toolchain_path] # Compile flags diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 005497b82f..f46dcf58dc 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -36,8 +36,6 @@ #include "java_godot_wrapper.h" #include "os_android.h" -#include <android/input.h> - #if defined(VULKAN_ENABLED) #include "drivers/vulkan/rendering_device_vulkan.h" #include "platform/android/vulkan/vulkan_context_android.h" @@ -51,7 +49,7 @@ DisplayServerAndroid *DisplayServerAndroid::get_singleton() { bool DisplayServerAndroid::has_feature(Feature p_feature) const { switch (p_feature) { //case FEATURE_CONSOLE_WINDOW: - //case FEATURE_CURSOR_SHAPE: + case FEATURE_CURSOR_SHAPE: //case FEATURE_CUSTOM_CURSOR_SHAPE: //case FEATURE_GLOBAL_MENU: //case FEATURE_HIDPI: @@ -829,6 +827,12 @@ void DisplayServerAndroid::mouse_set_mode(MouseMode p_mode) { return; } + if (p_mode == MouseMode::MOUSE_MODE_HIDDEN) { + OS_Android::get_singleton()->get_godot_java()->get_godot_view()->set_pointer_icon(CURSOR_TYPE_NULL); + } else { + cursor_set_shape(cursor_shape); + } + if (p_mode == MouseMode::MOUSE_MODE_CAPTURED) { OS_Android::get_singleton()->get_godot_java()->get_godot_view()->request_pointer_capture(); } else { @@ -870,3 +874,19 @@ int DisplayServerAndroid::_android_button_mask_to_godot_button_mask(int android_ return godot_button_mask; } + +void DisplayServerAndroid::cursor_set_shape(DisplayServer::CursorShape p_shape) { + if (cursor_shape == p_shape) { + return; + } + + cursor_shape = p_shape; + + if (mouse_mode == MouseMode::MOUSE_MODE_VISIBLE || mouse_mode == MouseMode::MOUSE_MODE_CONFINED) { + OS_Android::get_singleton()->get_godot_java()->get_godot_view()->set_pointer_icon(android_cursors[cursor_shape]); + } +} + +DisplayServer::CursorShape DisplayServerAndroid::cursor_get_shape() const { + return cursor_shape; +} diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index b9d1641656..53c768f406 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -70,6 +70,28 @@ private: int buttons_state; + // https://developer.android.com/reference/android/view/PointerIcon + // mapping between Godot's cursor shape to Android's' + int android_cursors[CURSOR_MAX] = { + 1000, //CURSOR_ARROW + 1008, //CURSOR_IBEAM + 1002, //CURSOR_POINTIN + 1007, //CURSOR_CROSS + 1004, //CURSOR_WAIT + 1004, //CURSOR_BUSY + 1021, //CURSOR_DRAG + 1021, //CURSOR_CAN_DRO + 1000, //CURSOR_FORBIDD (no corresponding icon in Android's icon so fallback to default) + 1015, //CURSOR_VSIZE + 1014, //CURSOR_HSIZE + 1017, //CURSOR_BDIAGSI + 1016, //CURSOR_FDIAGSI + 1020, //CURSOR_MOVE + 1015, //CURSOR_VSPLIT + 1014, //CURSOR_HSPLIT + 1003, //CURSOR_HELP + }; + const int CURSOR_TYPE_NULL = 0; MouseMode mouse_mode; bool keep_screen_on; @@ -78,6 +100,8 @@ private: Point2 hover_prev_pos; // needed to calculate the relative position on hover events Point2 scroll_prev_pos; // needed to calculate the relative position on scroll events + CursorShape cursor_shape = CursorShape::CURSOR_ARROW; + #if defined(VULKAN_ENABLED) VulkanContextAndroid *context_vulkan; RenderingDeviceVulkan *rendering_device_vulkan; @@ -180,6 +204,9 @@ public: void process_joy_event(JoypadEvent p_event); void process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed); + virtual void cursor_set_shape(CursorShape p_shape); + virtual CursorShape cursor_get_shape() const; + void mouse_set_mode(MouseMode p_mode); MouseMode mouse_get_mode() const; diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index 63c91561ff..b3ee55ea64 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -44,11 +44,15 @@ import android.annotation.SuppressLint; import android.content.Context; import android.graphics.PixelFormat; import android.opengl.GLSurfaceView; +import android.os.Build; import android.view.GestureDetector; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.PointerIcon; import android.view.SurfaceView; +import androidx.annotation.Keep; + /** * A simple GLSurfaceView sub-class that demonstrate how to perform * OpenGL ES 2.0 rendering into a GL Surface. Note the following important @@ -72,6 +76,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView private final GodotInputHandler inputHandler; private final GestureDetector detector; private final GodotRenderer godotRenderer; + private PointerIcon pointerIcon; public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_32_bits, boolean p_use_debug_opengl) { @@ -83,6 +88,9 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView this.inputHandler = new GodotInputHandler(this); this.detector = new GestureDetector(context, new GodotGestureHandler(this)); this.godotRenderer = new GodotRenderer(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT); + } init(xrMode, false, 16, 0); } @@ -149,6 +157,21 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView return inputHandler.onGenericMotionEvent(event); } + /** + * called from JNI to change pointer icon + */ + @Keep + public void setPointerIcon(int pointerType) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType); + } + } + + @Override + public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) { + return pointerIcon; + } + private void init(XRMode xrMode, boolean translucent, int depth, int stencil) { setPreserveEGLContextOnPause(true); setFocusableInTouchMode(true); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java index 2047c88070..ac333dd827 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java @@ -47,4 +47,6 @@ public interface GodotRenderView { abstract public void onBackPressed(); abstract public GodotInputHandler getInputHandler(); + + abstract public void setPointerIcon(int pointerType); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java index 2e59dbc0d0..169c6cf770 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java @@ -37,17 +37,22 @@ import org.godotengine.godot.vulkan.VkSurfaceView; import android.annotation.SuppressLint; import android.content.Context; +import android.os.Build; import android.view.GestureDetector; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.PointerIcon; import android.view.SurfaceView; +import androidx.annotation.Keep; + public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView { private final Godot godot; private final GodotInputHandler mInputHandler; private final GestureDetector mGestureDetector; private final VkRenderer mRenderer; + private PointerIcon pointerIcon; public GodotVulkanRenderView(Context context, Godot godot) { super(context); @@ -56,7 +61,9 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV mInputHandler = new GodotInputHandler(this); mGestureDetector = new GestureDetector(context, new GodotGestureHandler(this)); mRenderer = new VkRenderer(); - + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT); + } setFocusableInTouchMode(true); startRenderer(mRenderer); } @@ -124,6 +131,21 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV return mInputHandler.onGenericMotionEvent(event); } + /** + * called from JNI to change pointer icon + */ + @Keep + public void setPointerIcon(int pointerType) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType); + } + } + + @Override + public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) { + return pointerIcon; + } + @Override public void onResume() { super.onResume(); diff --git a/platform/android/java_godot_view_wrapper.cpp b/platform/android/java_godot_view_wrapper.cpp index 6b5e44f371..837d2aeced 100644 --- a/platform/android/java_godot_view_wrapper.cpp +++ b/platform/android/java_godot_view_wrapper.cpp @@ -43,6 +43,7 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) { if (android_get_device_api_level() >= __ANDROID_API_O__) { _request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V"); _release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V"); + _set_pointer_icon = env->GetMethodID(_cls, "setPointerIcon", "(I)V"); } } @@ -64,6 +65,15 @@ void GodotJavaViewWrapper::release_pointer_capture() { } } +void GodotJavaViewWrapper::set_pointer_icon(int pointer_type) { + if (_set_pointer_icon != 0) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_COND(env == nullptr); + + env->CallVoidMethod(_godot_view, _set_pointer_icon, pointer_type); + } +} + GodotJavaViewWrapper::~GodotJavaViewWrapper() { JNIEnv *env = get_jni_env(); ERR_FAIL_COND(env == nullptr); diff --git a/platform/android/java_godot_view_wrapper.h b/platform/android/java_godot_view_wrapper.h index bfb4369fb8..da547d8118 100644 --- a/platform/android/java_godot_view_wrapper.h +++ b/platform/android/java_godot_view_wrapper.h @@ -45,12 +45,14 @@ private: jmethodID _request_pointer_capture = 0; jmethodID _release_pointer_capture = 0; + jmethodID _set_pointer_icon = 0; public: GodotJavaViewWrapper(jobject godot_view); void request_pointer_capture(); void release_pointer_capture(); + void set_pointer_icon(int pointer_type); ~GodotJavaViewWrapper(); }; diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 30c5a75bca..93851aaedd 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -1958,6 +1958,8 @@ void PhysicalBone3D::_notification(int p_what) { if (parent_skeleton) { if (-1 != bone_id) { parent_skeleton->unbind_physical_bone_from_bone(bone_id); + parent_skeleton->unbind_child_node_from_bone(bone_id, this); + bone_id = -1; } } parent_skeleton = nullptr; diff --git a/servers/physics_2d/physics_server_2d_sw.cpp b/servers/physics_2d/physics_server_2d_sw.cpp index 425546e5ca..1c2dca0259 100644 --- a/servers/physics_2d/physics_server_2d_sw.cpp +++ b/servers/physics_2d/physics_server_2d_sw.cpp @@ -1237,6 +1237,10 @@ void PhysicsServer2DSW::set_active(bool p_active) { active = p_active; }; +void PhysicsServer2DSW::set_collision_iterations(int p_iterations) { + iterations = p_iterations; +}; + void PhysicsServer2DSW::init() { doing_sync = false; last_step = 0.001; diff --git a/servers/physics_2d/physics_server_2d_sw.h b/servers/physics_2d/physics_server_2d_sw.h index efa0784245..f1eb78a776 100644 --- a/servers/physics_2d/physics_server_2d_sw.h +++ b/servers/physics_2d/physics_server_2d_sw.h @@ -288,6 +288,8 @@ public: virtual void end_sync() override; virtual void finish() override; + virtual void set_collision_iterations(int p_iterations) override; + virtual bool is_flushing_queries() const override { return flushing_queries; } int get_process_info(ProcessInfo p_info) override; diff --git a/servers/physics_2d/physics_server_2d_wrap_mt.h b/servers/physics_2d/physics_server_2d_wrap_mt.h index 88ac742e40..c776641699 100644 --- a/servers/physics_2d/physics_server_2d_wrap_mt.h +++ b/servers/physics_2d/physics_server_2d_wrap_mt.h @@ -303,6 +303,7 @@ public: FUNC1(free, RID); FUNC1(set_active, bool); + FUNC1(set_collision_iterations, int); virtual void init() override; virtual void step(real_t p_step) override; diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 384179f2c3..7c5761cc61 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -675,6 +675,8 @@ void PhysicsServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_active", "active"), &PhysicsServer2D::set_active); + ClassDB::bind_method(D_METHOD("set_collision_iterations", "iterations"), &PhysicsServer2D::set_collision_iterations); + ClassDB::bind_method(D_METHOD("get_process_info", "process_info"), &PhysicsServer2D::get_process_info); BIND_ENUM_CONSTANT(SPACE_PARAM_CONTACT_RECYCLE_RADIUS); diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h index a5cf3f3a46..f2836961f2 100644 --- a/servers/physics_server_2d.h +++ b/servers/physics_server_2d.h @@ -589,6 +589,8 @@ public: virtual bool is_flushing_queries() const = 0; + virtual void set_collision_iterations(int iterations) = 0; + enum ProcessInfo { INFO_ACTIVE_OBJECTS, INFO_COLLISION_PAIRS, diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 0e916697a0..67fb38aa86 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -77,6 +77,7 @@ #include "test_translation.h" #include "test_validate_testing.h" #include "test_variant.h" +#include "test_vector.h" #include "test_xml_parser.h" #include "modules/modules_tests.gen.h" diff --git a/tests/test_vector.h b/tests/test_vector.h new file mode 100644 index 0000000000..02c56e59f6 --- /dev/null +++ b/tests/test_vector.h @@ -0,0 +1,496 @@ +/*************************************************************************/ +/* test_vector.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_VECTOR_H +#define TEST_VECTOR_H + +#include "core/templates/vector.h" + +#include "tests/test_macros.h" + +namespace TestVector { + +TEST_CASE("[Vector] Push back and append") { + Vector<int> vector; + vector.push_back(0); + vector.push_back(1); + vector.push_back(2); + vector.push_back(3); + // Alias for `push_back`. + vector.append(4); + + CHECK(vector[0] == 0); + CHECK(vector[1] == 1); + CHECK(vector[2] == 2); + CHECK(vector[3] == 3); + CHECK(vector[4] == 4); +} + +TEST_CASE("[Vector] Append array") { + Vector<int> vector; + vector.push_back(1); + vector.push_back(2); + + Vector<int> vector_other; + vector_other.push_back(128); + vector_other.push_back(129); + vector.append_array(vector_other); + + CHECK(vector.size() == 4); + CHECK(vector[0] == 1); + CHECK(vector[1] == 2); + CHECK(vector[2] == 128); + CHECK(vector[3] == 129); +} + +TEST_CASE("[Vector] Insert") { + Vector<int> vector; + vector.insert(0, 2); + vector.insert(0, 8); + vector.insert(2, 5); + vector.insert(1, 5); + vector.insert(0, -2); + + CHECK(vector.size() == 5); + CHECK(vector[0] == -2); + CHECK(vector[1] == 8); + CHECK(vector[2] == 5); + CHECK(vector[3] == 2); + CHECK(vector[4] == 5); +} + +TEST_CASE("[Vector] Ordered insert") { + Vector<int> vector; + vector.ordered_insert(2); + vector.ordered_insert(8); + vector.ordered_insert(5); + vector.ordered_insert(5); + vector.ordered_insert(-2); + + CHECK(vector.size() == 5); + CHECK(vector[0] == -2); + CHECK(vector[1] == 2); + CHECK(vector[2] == 5); + CHECK(vector[3] == 5); + CHECK(vector[4] == 8); +} + +TEST_CASE("[Vector] Insert + Ordered insert") { + Vector<int> vector; + vector.ordered_insert(2); + vector.ordered_insert(8); + vector.insert(0, 5); + vector.ordered_insert(5); + vector.insert(1, -2); + + CHECK(vector.size() == 5); + CHECK(vector[0] == 5); + CHECK(vector[1] == -2); + CHECK(vector[2] == 2); + CHECK(vector[3] == 5); + CHECK(vector[4] == 8); +} + +TEST_CASE("[Vector] Fill large array and modify it") { + Vector<int> vector; + vector.resize(1'000'000); + vector.fill(0x60d07); + + vector.write[200] = 0; + CHECK(vector.size() == 1'000'000); + CHECK(vector[0] == 0x60d07); + CHECK(vector[200] == 0); + CHECK(vector[499'999] == 0x60d07); + CHECK(vector[999'999] == 0x60d07); + vector.remove(200); + CHECK(vector[200] == 0x60d07); + + vector.clear(); + CHECK(vector.size() == 0); +} + +TEST_CASE("[Vector] Copy creation") { + Vector<int> vector; + vector.push_back(0); + vector.push_back(1); + vector.push_back(2); + vector.push_back(3); + vector.push_back(4); + + Vector<int> vector_other = Vector<int>(vector); + vector_other.remove(0); + CHECK(vector_other[0] == 1); + CHECK(vector_other[1] == 2); + CHECK(vector_other[2] == 3); + CHECK(vector_other[3] == 4); + + // Make sure the original vector isn't modified. + CHECK(vector[0] == 0); + CHECK(vector[1] == 1); + CHECK(vector[2] == 2); + CHECK(vector[3] == 3); + CHECK(vector[4] == 4); +} + +TEST_CASE("[Vector] Duplicate") { + Vector<int> vector; + vector.push_back(0); + vector.push_back(1); + vector.push_back(2); + vector.push_back(3); + vector.push_back(4); + + Vector<int> vector_other = vector.duplicate(); + vector_other.remove(0); + CHECK(vector_other[0] == 1); + CHECK(vector_other[1] == 2); + CHECK(vector_other[2] == 3); + CHECK(vector_other[3] == 4); + + // Make sure the original vector isn't modified. + CHECK(vector[0] == 0); + CHECK(vector[1] == 1); + CHECK(vector[2] == 2); + CHECK(vector[3] == 3); + CHECK(vector[4] == 4); +} + +TEST_CASE("[Vector] Get, set") { + Vector<int> vector; + vector.push_back(0); + vector.push_back(1); + vector.push_back(2); + vector.push_back(3); + vector.push_back(4); + + CHECK(vector.get(0) == 0); + CHECK(vector.get(1) == 1); + vector.set(2, 256); + CHECK(vector.get(2) == 256); + CHECK(vector.get(3) == 3); + + ERR_PRINT_OFF; + // Invalid (but should not crash): setting out of bounds. + vector.set(6, 500); + ERR_PRINT_ON; + + CHECK(vector.get(4) == 4); +} + +TEST_CASE("[Vector] To byte array") { + Vector<int> vector; + vector.push_back(0); + vector.push_back(-1); + vector.push_back(2008); + vector.push_back(999999999); + + Vector<uint8_t> byte_array = vector.to_byte_array(); + CHECK(byte_array.size() == 16); + // vector[0] + CHECK(byte_array[0] == 0); + CHECK(byte_array[1] == 0); + CHECK(byte_array[2] == 0); + CHECK(byte_array[3] == 0); + + // vector[1] + CHECK(byte_array[4] == 255); + CHECK(byte_array[5] == 255); + CHECK(byte_array[6] == 255); + CHECK(byte_array[7] == 255); + + // vector[2] + CHECK(byte_array[8] == 216); + CHECK(byte_array[9] == 7); + CHECK(byte_array[10] == 0); + CHECK(byte_array[11] == 0); + + // vector[3] + CHECK(byte_array[12] == 255); + CHECK(byte_array[13] == 201); + CHECK(byte_array[14] == 154); + CHECK(byte_array[15] == 59); +} + +TEST_CASE("[Vector] Subarray") { + Vector<int> vector; + vector.push_back(0); + vector.push_back(1); + vector.push_back(2); + vector.push_back(3); + vector.push_back(4); + + Vector<int> subarray1 = vector.subarray(1, 2); + CHECK(subarray1.size() == 2); + CHECK(subarray1[0] == 1); + CHECK(subarray1[1] == 2); + + Vector<int> subarray2 = vector.subarray(1, -1); + CHECK(subarray2.size() == 4); + CHECK(subarray2[0] == 1); + CHECK(subarray2[1] == 2); + CHECK(subarray2[2] == 3); + CHECK(subarray2[3] == 4); + + Vector<int> subarray3 = vector.subarray(-2, -1); + CHECK(subarray3.size() == 2); + CHECK(subarray3[0] == 3); + CHECK(subarray3[1] == 4); + + Vector<int> subarray4 = vector.subarray(-3, 3); + CHECK(subarray4.size() == 2); + CHECK(subarray4[0] == 2); + CHECK(subarray4[1] == 3); +} + +TEST_CASE("[Vector] Find, has") { + Vector<int> vector; + vector.push_back(3); + vector.push_back(1); + vector.push_back(4); + vector.push_back(0); + vector.push_back(2); + + CHECK(vector[0] == 3); + CHECK(vector[1] == 1); + CHECK(vector[2] == 4); + CHECK(vector[3] == 0); + CHECK(vector[4] == 2); + + CHECK(vector.find(0) == 3); + CHECK(vector.find(1) == 1); + CHECK(vector.find(2) == 4); + CHECK(vector.find(3) == 0); + CHECK(vector.find(4) == 2); + + CHECK(vector.find(-1) == -1); + CHECK(vector.find(5) == -1); + + CHECK(vector.has(0)); + CHECK(vector.has(1)); + CHECK(vector.has(2)); + CHECK(vector.has(3)); + CHECK(vector.has(4)); + + CHECK(!vector.has(-1)); + CHECK(!vector.has(5)); +} + +TEST_CASE("[Vector] Remove") { + Vector<int> vector; + vector.push_back(0); + vector.push_back(1); + vector.push_back(2); + vector.push_back(3); + vector.push_back(4); + + vector.remove(0); + + CHECK(vector[0] == 1); + CHECK(vector[1] == 2); + CHECK(vector[2] == 3); + CHECK(vector[3] == 4); + + vector.remove(2); + + CHECK(vector[0] == 1); + CHECK(vector[1] == 2); + CHECK(vector[2] == 4); + + vector.remove(1); + + CHECK(vector[0] == 1); + CHECK(vector[1] == 4); + + vector.remove(0); + + CHECK(vector[0] == 4); +} + +TEST_CASE("[Vector] Remove and find") { + Vector<int> vector; + vector.push_back(0); + vector.push_back(1); + vector.push_back(2); + vector.push_back(3); + vector.push_back(4); + + CHECK(vector.size() == 5); + + vector.remove(0); + + CHECK(vector.size() == 4); + + CHECK(vector.find(0) == -1); + CHECK(vector.find(1) != -1); + CHECK(vector.find(2) != -1); + CHECK(vector.find(3) != -1); + CHECK(vector.find(4) != -1); + + vector.remove(vector.find(3)); + + CHECK(vector.size() == 3); + + CHECK(vector.find(3) == -1); + CHECK(vector.find(1) != -1); + CHECK(vector.find(2) != -1); + CHECK(vector.find(4) != -1); + + vector.remove(vector.find(2)); + + CHECK(vector.size() == 2); + + CHECK(vector.find(2) == -1); + CHECK(vector.find(1) != -1); + CHECK(vector.find(4) != -1); + + vector.remove(vector.find(4)); + + CHECK(vector.size() == 1); + + CHECK(vector.find(4) == -1); + CHECK(vector.find(1) != -1); + + vector.remove(0); + + CHECK(vector.is_empty()); + CHECK(vector.size() == 0); +} + +TEST_CASE("[Vector] Erase") { + Vector<int> vector; + vector.push_back(1); + vector.push_back(3); + vector.push_back(0); + vector.push_back(2); + vector.push_back(4); + + CHECK(vector.find(2) == 3); + + vector.erase(2); + + CHECK(vector.find(2) == -1); + CHECK(vector.size() == 4); +} + +TEST_CASE("[Vector] Size, resize, reserve") { + Vector<int> vector; + CHECK(vector.is_empty()); + CHECK(vector.size() == 0); + + vector.resize(10); + + CHECK(vector.size() == 10); + + vector.resize(5); + + CHECK(vector.size() == 5); + + vector.remove(0); + vector.remove(0); + vector.remove(0); + + CHECK(vector.size() == 2); + + vector.clear(); + + CHECK(vector.size() == 0); + CHECK(vector.is_empty()); + + vector.push_back(0); + vector.push_back(0); + vector.push_back(0); + + CHECK(vector.size() == 3); + + vector.push_back(0); + + CHECK(vector.size() == 4); +} + +TEST_CASE("[Vector] Sort") { + Vector<int> vector; + vector.push_back(2); + vector.push_back(8); + vector.push_back(-4); + vector.push_back(5); + vector.sort(); + + CHECK(vector.size() == 4); + CHECK(vector[0] == -4); + CHECK(vector[1] == 2); + CHECK(vector[2] == 5); + CHECK(vector[3] == 8); +} + +TEST_CASE("[Vector] Sort custom") { + Vector<String> vector; + vector.push_back("world"); + vector.push_back("World"); + vector.push_back("Hello"); + vector.push_back("10Hello"); + vector.push_back("12Hello"); + vector.push_back("01Hello"); + vector.push_back("1Hello"); + vector.push_back(".Hello"); + vector.sort_custom<NaturalNoCaseComparator>(); + + CHECK(vector.size() == 8); + CHECK(vector[0] == ".Hello"); + CHECK(vector[1] == "01Hello"); + CHECK(vector[2] == "1Hello"); + CHECK(vector[3] == "10Hello"); + CHECK(vector[4] == "12Hello"); + CHECK(vector[5] == "Hello"); + CHECK(vector[6] == "world"); + CHECK(vector[7] == "World"); +} + +TEST_CASE("[Vector] Operators") { + Vector<int> vector; + vector.push_back(2); + vector.push_back(8); + vector.push_back(-4); + vector.push_back(5); + + Vector<int> vector_other; + vector_other.push_back(2); + vector_other.push_back(8); + vector_other.push_back(-4); + vector_other.push_back(5); + + CHECK(vector == vector_other); + + vector_other.push_back(10); + CHECK(vector != vector_other); +} + +} // namespace TestVector + +#endif // TEST_VECTOR_H |