basic ECS spawner
This commit is contained in:
9
GECS/components/c_health.gd
Normal file
9
GECS/components/c_health.gd
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
class_name C_Health
|
||||||
|
extends Component
|
||||||
|
|
||||||
|
@export var current: float = 100.0
|
||||||
|
@export var maximum: float = 100.0
|
||||||
|
|
||||||
|
func _init(max_health: float = 100.0):
|
||||||
|
maximum = max_health
|
||||||
|
current = max_health
|
||||||
1
GECS/components/c_health.gd.uid
Normal file
1
GECS/components/c_health.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bg653v2p104l1
|
||||||
15
GECS/components/c_spawn_point.gd
Normal file
15
GECS/components/c_spawn_point.gd
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
class_name C_SpawnPoint
|
||||||
|
extends Component
|
||||||
|
|
||||||
|
@export var spawn_prefab: PackedScene
|
||||||
|
@export var spawn_frequency: float = 1
|
||||||
|
var spawn_cooldown: float = 0
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
pass
|
||||||
|
|
||||||
|
func should_spawn() -> bool:
|
||||||
|
return spawn_cooldown < 0
|
||||||
|
|
||||||
|
func start_spawn_cooldown() -> void:
|
||||||
|
spawn_cooldown = spawn_frequency
|
||||||
1
GECS/components/c_spawn_point.gd.uid
Normal file
1
GECS/components/c_spawn_point.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cmflg422miab6
|
||||||
7
GECS/components/c_transform.gd
Normal file
7
GECS/components/c_transform.gd
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
class_name C_Transform
|
||||||
|
extends Component
|
||||||
|
|
||||||
|
@export var position: Vector3 = Vector3.ZERO
|
||||||
|
|
||||||
|
func _init(pos: Vector3 = Vector3.ZERO):
|
||||||
|
position = pos
|
||||||
1
GECS/components/c_transform.gd.uid
Normal file
1
GECS/components/c_transform.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://c4ihfoefv2vwd
|
||||||
7
GECS/components/c_velocity.gd
Normal file
7
GECS/components/c_velocity.gd
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
class_name C_Velocity
|
||||||
|
extends Component
|
||||||
|
|
||||||
|
@export var velocity: Vector3 = Vector3.ZERO
|
||||||
|
|
||||||
|
func _init(vel: Vector3 = Vector3.ZERO):
|
||||||
|
velocity = vel
|
||||||
1
GECS/components/c_velocity.gd.uid
Normal file
1
GECS/components/c_velocity.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cnqotfu7xgxwa
|
||||||
36
GECS/entities/BasicEnemy/BasicEnemy.tscn
Normal file
36
GECS/entities/BasicEnemy/BasicEnemy.tscn
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
[gd_scene load_steps=11 format=3 uid="uid://bct6stmj0qyp2"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://ci3kmg1eck6gb" path="res://GECS/entities/BasicEnemy/e_basic_enemy.gd" id="1_h3v2l"]
|
||||||
|
[ext_resource type="Script" uid="uid://b6k13gc2m4e5s" path="res://addons/gecs/ecs/component.gd" id="2_gsgoc"]
|
||||||
|
[ext_resource type="Script" uid="uid://bg653v2p104l1" path="res://GECS/components/c_health.gd" id="3_unr0g"]
|
||||||
|
[ext_resource type="Script" uid="uid://c4ihfoefv2vwd" path="res://GECS/components/c_transform.gd" id="4_07lse"]
|
||||||
|
[ext_resource type="Script" uid="uid://cnqotfu7xgxwa" path="res://GECS/components/c_velocity.gd" id="5_vhe2b"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_vas4o"]
|
||||||
|
script = ExtResource("3_unr0g")
|
||||||
|
metadata/_custom_type_script = "uid://bg653v2p104l1"
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_yuljp"]
|
||||||
|
script = ExtResource("4_07lse")
|
||||||
|
metadata/_custom_type_script = "uid://c4ihfoefv2vwd"
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_6hf1n"]
|
||||||
|
script = ExtResource("5_vhe2b")
|
||||||
|
velocity = Vector3(2, 0, 0)
|
||||||
|
metadata/_custom_type_script = "uid://cnqotfu7xgxwa"
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ocib1"]
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleMesh" id="CapsuleMesh_4ju8b"]
|
||||||
|
|
||||||
|
[node name="BasicEnemy" type="CharacterBody3D"]
|
||||||
|
script = ExtResource("1_h3v2l")
|
||||||
|
component_resources = Array[ExtResource("2_gsgoc")]([SubResource("Resource_vas4o"), SubResource("Resource_yuljp"), SubResource("Resource_6hf1n")])
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||||
|
shape = SubResource("CapsuleShape3D_ocib1")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||||
|
mesh = SubResource("CapsuleMesh_4ju8b")
|
||||||
10
GECS/entities/BasicEnemy/e_basic_enemy.gd
Normal file
10
GECS/entities/BasicEnemy/e_basic_enemy.gd
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
@tool
|
||||||
|
class_name BasicEnemy
|
||||||
|
extends Entity
|
||||||
|
|
||||||
|
|
||||||
|
func on_ready():
|
||||||
|
# Sync the entity's scene position to the Transform component
|
||||||
|
if has_component(C_Transform):
|
||||||
|
var c_trs = get_component(C_Transform) as C_Transform
|
||||||
|
self.global_position = c_trs.position
|
||||||
1
GECS/entities/BasicEnemy/e_basic_enemy.gd.uid
Normal file
1
GECS/entities/BasicEnemy/e_basic_enemy.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://ci3kmg1eck6gb
|
||||||
25
GECS/entities/Spawner/SpawnPoint.tscn
Normal file
25
GECS/entities/Spawner/SpawnPoint.tscn
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[gd_scene load_steps=7 format=3 uid="uid://d0075ch03hfri"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://ckqlc1tqyh7gm" path="res://GECS/entities/Spawner/e_spawn_point.gd" id="1_sp3sh"]
|
||||||
|
[ext_resource type="Script" uid="uid://b6k13gc2m4e5s" path="res://addons/gecs/ecs/component.gd" id="2_ksqpe"]
|
||||||
|
[ext_resource type="Script" uid="uid://cmflg422miab6" path="res://GECS/components/c_spawn_point.gd" id="3_ksqpe"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bct6stmj0qyp2" path="res://GECS/entities/BasicEnemy/BasicEnemy.tscn" id="4_2ixog"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_dgltp"]
|
||||||
|
script = ExtResource("3_ksqpe")
|
||||||
|
spawn_prefab = ExtResource("4_2ixog")
|
||||||
|
metadata/_custom_type_script = "uid://cmflg422miab6"
|
||||||
|
|
||||||
|
[sub_resource type="SphereShape3D" id="SphereShape3D_sp3sh"]
|
||||||
|
|
||||||
|
[node name="SpawnPoint" type="CharacterBody3D"]
|
||||||
|
collision_layer = 0
|
||||||
|
collision_mask = 0
|
||||||
|
motion_mode = 1
|
||||||
|
script = ExtResource("1_sp3sh")
|
||||||
|
component_resources = Array[ExtResource("2_ksqpe")]([SubResource("Resource_dgltp")])
|
||||||
|
|
||||||
|
[node name="Marker3D" type="Marker3D" parent="."]
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
shape = SubResource("SphereShape3D_sp3sh")
|
||||||
14
GECS/entities/Spawner/e_spawn_point.gd
Normal file
14
GECS/entities/Spawner/e_spawn_point.gd
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
@tool
|
||||||
|
class_name E_SpawnPoint
|
||||||
|
extends Entity
|
||||||
|
|
||||||
|
@export var spawn_prefab: PackedScene
|
||||||
|
@export var spawn_frequency: float = -1
|
||||||
|
|
||||||
|
func on_ready():
|
||||||
|
if has_component(C_SpawnPoint):
|
||||||
|
var c_spawn_point = get_component(C_SpawnPoint) as C_SpawnPoint
|
||||||
|
if spawn_prefab != null && spawn_prefab.can_instantiate():
|
||||||
|
c_spawn_point.spawn_prefab = spawn_prefab
|
||||||
|
if spawn_frequency > 0:
|
||||||
|
c_spawn_point.spawn_frequency = spawn_frequency
|
||||||
1
GECS/entities/Spawner/e_spawn_point.gd.uid
Normal file
1
GECS/entities/Spawner/e_spawn_point.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://ckqlc1tqyh7gm
|
||||||
25
GECS/systems/default_systems.tscn
Normal file
25
GECS/systems/default_systems.tscn
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[gd_scene load_steps=4 format=3 uid="uid://b2v1bngfh5te"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://b3vi2ingux88g" path="res://addons/gecs/lib/system_group.gd" id="1_sk1vy"]
|
||||||
|
[ext_resource type="Script" uid="uid://dpglt5gt5ijrn" path="res://GECS/systems/s_movement.gd" id="2_aqda8"]
|
||||||
|
[ext_resource type="Script" uid="uid://cb2kev6dsctfu" path="res://GECS/systems/s_spawner.gd" id="2_hdbau"]
|
||||||
|
|
||||||
|
[node name="Systems" type="Node"]
|
||||||
|
|
||||||
|
[node name="gameplay" type="Node" parent="."]
|
||||||
|
script = ExtResource("1_sk1vy")
|
||||||
|
metadata/_custom_type_script = "uid://b3vi2ingux88g"
|
||||||
|
|
||||||
|
[node name="SpawnSystem" type="Node" parent="gameplay"]
|
||||||
|
script = ExtResource("2_hdbau")
|
||||||
|
group = &"gameplay"
|
||||||
|
metadata/_custom_type_script = "uid://cb2kev6dsctfu"
|
||||||
|
|
||||||
|
[node name="physics" type="Node" parent="."]
|
||||||
|
script = ExtResource("1_sk1vy")
|
||||||
|
metadata/_custom_type_script = "uid://b3vi2ingux88g"
|
||||||
|
|
||||||
|
[node name="MovementSystem" type="Node" parent="physics"]
|
||||||
|
script = ExtResource("2_aqda8")
|
||||||
|
group = &"physics"
|
||||||
|
metadata/_custom_type_script = "uid://dpglt5gt5ijrn"
|
||||||
25
GECS/systems/s_movement.gd
Normal file
25
GECS/systems/s_movement.gd
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
class_name MovementSystem
|
||||||
|
extends System
|
||||||
|
|
||||||
|
func query():
|
||||||
|
# Find all entities that have both transform and velocity
|
||||||
|
return q.with_all([C_Velocity])
|
||||||
|
|
||||||
|
func process(entities: Array[Entity], _components: Array, delta: float):
|
||||||
|
for entity in entities:
|
||||||
|
var c_velocity = entity.get_component(C_Velocity) as C_Velocity
|
||||||
|
|
||||||
|
if entity.velocity.length() == 0:
|
||||||
|
entity.velocity = c_velocity.velocity
|
||||||
|
|
||||||
|
# Add the gravity.
|
||||||
|
if not entity.is_on_floor():
|
||||||
|
entity.velocity += entity.get_gravity() * delta
|
||||||
|
|
||||||
|
# Bounce off screen edges (simple example)
|
||||||
|
if entity.global_position.x > 10 && entity.velocity.x > 0:
|
||||||
|
entity.velocity = -c_velocity.velocity
|
||||||
|
if entity.global_position.x < -10 && entity.velocity.x < 0:
|
||||||
|
entity.velocity = c_velocity.velocity
|
||||||
|
|
||||||
|
entity.move_and_slide()
|
||||||
1
GECS/systems/s_movement.gd.uid
Normal file
1
GECS/systems/s_movement.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dpglt5gt5ijrn
|
||||||
21
GECS/systems/s_spawner.gd
Normal file
21
GECS/systems/s_spawner.gd
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
class_name SpawnSystem
|
||||||
|
extends System
|
||||||
|
|
||||||
|
func query():
|
||||||
|
# Find all entities that have both transform and velocity
|
||||||
|
return q.with_all([C_SpawnPoint]).enabled()
|
||||||
|
|
||||||
|
func process(entities: Array[Entity], _components: Array, delta: float):
|
||||||
|
for entity in entities:
|
||||||
|
var spawn_point = entity.get_component(C_SpawnPoint) as C_SpawnPoint
|
||||||
|
|
||||||
|
if not spawn_point.should_spawn():
|
||||||
|
spawn_point.spawn_cooldown -= delta
|
||||||
|
continue
|
||||||
|
|
||||||
|
var spawned = spawn_point.spawn_prefab.instantiate() as Entity
|
||||||
|
get_tree().current_scene.add_child(spawned)
|
||||||
|
ECS.world.add_entity(spawned)
|
||||||
|
|
||||||
|
spawned.global_position = entity.global_position
|
||||||
|
spawn_point.start_spawn_cooldown()
|
||||||
1
GECS/systems/s_spawner.gd.uid
Normal file
1
GECS/systems/s_spawner.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cb2kev6dsctfu
|
||||||
21
addons/debug_menu/LICENSE.md
Normal file
21
addons/debug_menu/LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# MIT License
|
||||||
|
|
||||||
|
Copyright © 2023-present Hugo Locurcio and contributors
|
||||||
|
|
||||||
|
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.
|
||||||
482
addons/debug_menu/debug_menu.gd
Normal file
482
addons/debug_menu/debug_menu.gd
Normal file
@@ -0,0 +1,482 @@
|
|||||||
|
extends CanvasLayer
|
||||||
|
|
||||||
|
@export var fps: Label
|
||||||
|
@export var num_entities: Label
|
||||||
|
@export var frame_time: Label
|
||||||
|
@export var frame_number: Label
|
||||||
|
@export var frame_history_total_avg: Label
|
||||||
|
@export var frame_history_total_min: Label
|
||||||
|
@export var frame_history_total_max: Label
|
||||||
|
@export var frame_history_total_last: Label
|
||||||
|
@export var frame_history_cpu_avg: Label
|
||||||
|
@export var frame_history_cpu_min: Label
|
||||||
|
@export var frame_history_cpu_max: Label
|
||||||
|
@export var frame_history_cpu_last: Label
|
||||||
|
@export var frame_history_gpu_avg: Label
|
||||||
|
@export var frame_history_gpu_min: Label
|
||||||
|
@export var frame_history_gpu_max: Label
|
||||||
|
@export var frame_history_gpu_last: Label
|
||||||
|
@export var fps_graph: Panel
|
||||||
|
@export var total_graph: Panel
|
||||||
|
@export var cpu_graph: Panel
|
||||||
|
@export var gpu_graph: Panel
|
||||||
|
@export var information: Label
|
||||||
|
@export var settings: Label
|
||||||
|
|
||||||
|
## The number of frames to keep in history for graph drawing and best/worst calculations.
|
||||||
|
## Currently, this also affects how FPS is measured.
|
||||||
|
const HISTORY_NUM_FRAMES = 150
|
||||||
|
|
||||||
|
const GRAPH_SIZE = Vector2(150, 25)
|
||||||
|
const GRAPH_MIN_FPS = 10
|
||||||
|
const GRAPH_MAX_FPS = 160
|
||||||
|
const GRAPH_MIN_FRAMETIME = 1.0 / GRAPH_MIN_FPS
|
||||||
|
const GRAPH_MAX_FRAMETIME = 1.0 / GRAPH_MAX_FPS
|
||||||
|
|
||||||
|
## Debug menu display style.
|
||||||
|
enum Style {
|
||||||
|
HIDDEN, ## Debug menu is hidden.
|
||||||
|
VISIBLE_COMPACT, ## Debug menu is visible, with only the FPS, FPS cap (if any) and time taken to render the last frame.
|
||||||
|
VISIBLE_DETAILED, ## Debug menu is visible with full information, including graphs.
|
||||||
|
MAX, ## Represents the size of the Style enum.
|
||||||
|
}
|
||||||
|
|
||||||
|
## The style to use when drawing the debug menu.
|
||||||
|
var style := Style.HIDDEN:
|
||||||
|
set(value):
|
||||||
|
style = value
|
||||||
|
match style:
|
||||||
|
Style.HIDDEN:
|
||||||
|
visible = false
|
||||||
|
Style.VISIBLE_COMPACT, Style.VISIBLE_DETAILED:
|
||||||
|
visible = true
|
||||||
|
frame_number.visible = style == Style.VISIBLE_DETAILED
|
||||||
|
$DebugMenu/VBoxContainer/FrameTimeHistory.visible = style == Style.VISIBLE_DETAILED
|
||||||
|
$DebugMenu/VBoxContainer/FPSGraph.visible = style == Style.VISIBLE_DETAILED
|
||||||
|
$DebugMenu/VBoxContainer/TotalGraph.visible = style == Style.VISIBLE_DETAILED
|
||||||
|
$DebugMenu/VBoxContainer/CPUGraph.visible = style == Style.VISIBLE_DETAILED
|
||||||
|
$DebugMenu/VBoxContainer/GPUGraph.visible = style == Style.VISIBLE_DETAILED
|
||||||
|
information.visible = style == Style.VISIBLE_DETAILED
|
||||||
|
settings.visible = style == Style.VISIBLE_DETAILED
|
||||||
|
|
||||||
|
# Value of `Time.get_ticks_usec()` on the previous frame.
|
||||||
|
var last_tick := 0
|
||||||
|
|
||||||
|
var thread := Thread.new()
|
||||||
|
|
||||||
|
## Returns the sum of all values of an array (use as a parameter to `Array.reduce()`).
|
||||||
|
var sum_func := func avg(accum: float, number: float) -> float: return accum + number
|
||||||
|
|
||||||
|
# History of the last `HISTORY_NUM_FRAMES` rendered frames.
|
||||||
|
var frame_history_total: Array[float] = []
|
||||||
|
var frame_history_cpu: Array[float] = []
|
||||||
|
var frame_history_gpu: Array[float] = []
|
||||||
|
var fps_history: Array[float] = [] # Only used for graphs.
|
||||||
|
|
||||||
|
var frametime_avg := GRAPH_MIN_FRAMETIME
|
||||||
|
var frametime_cpu_avg := GRAPH_MAX_FRAMETIME
|
||||||
|
var frametime_gpu_avg := GRAPH_MIN_FRAMETIME
|
||||||
|
var frames_per_second := float(GRAPH_MIN_FPS)
|
||||||
|
var frame_time_gradient := Gradient.new()
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
# This must be done here instead of `_ready()` to avoid having `visibility_changed` be emitted immediately.
|
||||||
|
visible = false
|
||||||
|
|
||||||
|
if not InputMap.has_action("cycle_debug_menu"):
|
||||||
|
# Create default input action if no user-defined override exists.
|
||||||
|
# We can't do it in the editor plugin's activation code as it doesn't seem to work there.
|
||||||
|
InputMap.add_action("cycle_debug_menu")
|
||||||
|
var event := InputEventKey.new()
|
||||||
|
event.keycode = KEY_F3
|
||||||
|
InputMap.action_add_event("cycle_debug_menu", event)
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
fps_graph.draw.connect(_fps_graph_draw)
|
||||||
|
total_graph.draw.connect(_total_graph_draw)
|
||||||
|
cpu_graph.draw.connect(_cpu_graph_draw)
|
||||||
|
gpu_graph.draw.connect(_gpu_graph_draw)
|
||||||
|
|
||||||
|
fps_history.resize(HISTORY_NUM_FRAMES)
|
||||||
|
frame_history_total.resize(HISTORY_NUM_FRAMES)
|
||||||
|
frame_history_cpu.resize(HISTORY_NUM_FRAMES)
|
||||||
|
frame_history_gpu.resize(HISTORY_NUM_FRAMES)
|
||||||
|
|
||||||
|
# NOTE: Both FPS and frametimes are colored following FPS logic
|
||||||
|
# (red = 10 FPS, yellow = 60 FPS, green = 110 FPS, cyan = 160 FPS).
|
||||||
|
# This makes the color gradient non-linear.
|
||||||
|
# Colors are taken from <https://tailwindcolor.com/>.
|
||||||
|
frame_time_gradient.set_color(0, Color8(239, 68, 68)) # red-500
|
||||||
|
frame_time_gradient.set_color(1, Color8(56, 189, 248)) # light-blue-400
|
||||||
|
frame_time_gradient.add_point(0.3333, Color8(250, 204, 21)) # yellow-400
|
||||||
|
frame_time_gradient.add_point(0.6667, Color8(128, 226, 95)) # 50-50 mix of lime-400 and green-400
|
||||||
|
|
||||||
|
get_viewport().size_changed.connect(update_settings_label)
|
||||||
|
|
||||||
|
# Display loading text while information is being queried,
|
||||||
|
# in case the user toggles the full debug menu just after starting the project.
|
||||||
|
information.text = "Loading hardware information...\n\n "
|
||||||
|
settings.text = "Loading project information..."
|
||||||
|
thread.start(
|
||||||
|
func():
|
||||||
|
# Disable thread safety checks as they interfere with this add-on.
|
||||||
|
# This only affects this particular thread, not other thread instances in the project.
|
||||||
|
# See <https://github.com/godotengine/godot/pull/78000> for details.
|
||||||
|
# Use a Callable so that this can be ignored on Godot 4.0 without causing a script error
|
||||||
|
# (thread safety checks were added in Godot 4.1).
|
||||||
|
if Engine.get_version_info()["hex"] >= 0x040100:
|
||||||
|
Callable(Thread, "set_thread_safety_checks_enabled").call(false)
|
||||||
|
|
||||||
|
# Enable required time measurements to display CPU/GPU frame time information.
|
||||||
|
# These lines are time-consuming operations, so run them in a separate thread.
|
||||||
|
RenderingServer.viewport_set_measure_render_time(get_viewport().get_viewport_rid(), true)
|
||||||
|
update_information_label()
|
||||||
|
update_settings_label()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _input(event: InputEvent) -> void:
|
||||||
|
if event.is_action_pressed("cycle_debug_menu"):
|
||||||
|
style = wrapi(style + 1, 0, Style.MAX) as Style
|
||||||
|
|
||||||
|
|
||||||
|
func _exit_tree() -> void:
|
||||||
|
thread.wait_to_finish()
|
||||||
|
|
||||||
|
|
||||||
|
## Update hardware information label (this can change at runtime based on window
|
||||||
|
## size and graphics settings). This is only called when the window is resized.
|
||||||
|
## To update when graphics settings are changed, the function must be called manually
|
||||||
|
## using `DebugMenu.update_settings_label()`.
|
||||||
|
func update_settings_label() -> void:
|
||||||
|
settings.text = ""
|
||||||
|
if ProjectSettings.has_setting("application/config/version"):
|
||||||
|
settings.text += "Project Version: %s\n" % ProjectSettings.get_setting("application/config/version")
|
||||||
|
|
||||||
|
var rendering_method := str(ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method"))
|
||||||
|
var rendering_method_string := rendering_method
|
||||||
|
match rendering_method:
|
||||||
|
"forward_plus":
|
||||||
|
rendering_method_string = "Forward+"
|
||||||
|
"mobile":
|
||||||
|
rendering_method_string = "Forward Mobile"
|
||||||
|
"gl_compatibility":
|
||||||
|
rendering_method_string = "Compatibility"
|
||||||
|
settings.text += "Rendering Method: %s\n" % rendering_method_string
|
||||||
|
|
||||||
|
var viewport := get_viewport()
|
||||||
|
|
||||||
|
# The size of the viewport rendering, which determines which resolution 3D is rendered at.
|
||||||
|
var viewport_render_size := Vector2i()
|
||||||
|
|
||||||
|
if viewport.content_scale_mode == Window.CONTENT_SCALE_MODE_VIEWPORT:
|
||||||
|
viewport_render_size = viewport.get_visible_rect().size
|
||||||
|
settings.text += "Viewport: %d×%d, Window: %d×%d\n" % [viewport.get_visible_rect().size.x, viewport.get_visible_rect().size.y, viewport.size.x, viewport.size.y]
|
||||||
|
else:
|
||||||
|
# Window size matches viewport size.
|
||||||
|
viewport_render_size = viewport.size
|
||||||
|
settings.text += "Viewport: %d×%d\n" % [viewport.size.x, viewport.size.y]
|
||||||
|
|
||||||
|
# Display 3D settings only if relevant.
|
||||||
|
if viewport.get_camera_3d():
|
||||||
|
var scaling_3d_mode_string := "(unknown)"
|
||||||
|
match viewport.scaling_3d_mode:
|
||||||
|
Viewport.SCALING_3D_MODE_BILINEAR:
|
||||||
|
scaling_3d_mode_string = "Bilinear"
|
||||||
|
Viewport.SCALING_3D_MODE_FSR:
|
||||||
|
scaling_3d_mode_string = "FSR 1.0"
|
||||||
|
Viewport.SCALING_3D_MODE_FSR2:
|
||||||
|
scaling_3d_mode_string = "FSR 2.2"
|
||||||
|
|
||||||
|
var antialiasing_3d_string := ""
|
||||||
|
if viewport.scaling_3d_mode == Viewport.SCALING_3D_MODE_FSR2:
|
||||||
|
# The FSR2 scaling mode includes its own temporal antialiasing implementation.
|
||||||
|
antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "FSR 2.2"
|
||||||
|
if viewport.scaling_3d_mode != Viewport.SCALING_3D_MODE_FSR2 and viewport.use_taa:
|
||||||
|
# Godot's own TAA is ignored when using FSR2 scaling mode, as FSR2 provides its own TAA implementation.
|
||||||
|
antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "TAA"
|
||||||
|
if viewport.msaa_3d >= Viewport.MSAA_2X:
|
||||||
|
antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "%d× MSAA" % pow(2, viewport.msaa_3d)
|
||||||
|
if viewport.screen_space_aa == Viewport.SCREEN_SPACE_AA_FXAA:
|
||||||
|
antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "FXAA"
|
||||||
|
|
||||||
|
settings.text += "3D scale (%s): %d%% = %d×%d" % [
|
||||||
|
scaling_3d_mode_string,
|
||||||
|
viewport.scaling_3d_scale * 100,
|
||||||
|
viewport_render_size.x * viewport.scaling_3d_scale,
|
||||||
|
viewport_render_size.y * viewport.scaling_3d_scale,
|
||||||
|
]
|
||||||
|
|
||||||
|
if not antialiasing_3d_string.is_empty():
|
||||||
|
settings.text += "\n3D Antialiasing: %s" % antialiasing_3d_string
|
||||||
|
|
||||||
|
var environment := viewport.get_camera_3d().get_world_3d().environment
|
||||||
|
if environment:
|
||||||
|
if environment.ssr_enabled:
|
||||||
|
settings.text += "\nSSR: %d Steps" % environment.ssr_max_steps
|
||||||
|
|
||||||
|
if environment.ssao_enabled:
|
||||||
|
settings.text += "\nSSAO: On"
|
||||||
|
if environment.ssil_enabled:
|
||||||
|
settings.text += "\nSSIL: On"
|
||||||
|
|
||||||
|
if environment.sdfgi_enabled:
|
||||||
|
settings.text += "\nSDFGI: %d Cascades" % environment.sdfgi_cascades
|
||||||
|
|
||||||
|
if environment.glow_enabled:
|
||||||
|
settings.text += "\nGlow: On"
|
||||||
|
|
||||||
|
if environment.volumetric_fog_enabled:
|
||||||
|
settings.text += "\nVolumetric Fog: On"
|
||||||
|
var antialiasing_2d_string := ""
|
||||||
|
if viewport.msaa_2d >= Viewport.MSAA_2X:
|
||||||
|
antialiasing_2d_string = "%d× MSAA" % pow(2, viewport.msaa_2d)
|
||||||
|
|
||||||
|
if not antialiasing_2d_string.is_empty():
|
||||||
|
settings.text += "\n2D Antialiasing: %s" % antialiasing_2d_string
|
||||||
|
|
||||||
|
|
||||||
|
## Update hardware/software information label (this never changes at runtime).
|
||||||
|
func update_information_label() -> void:
|
||||||
|
var adapter_string := ""
|
||||||
|
# Make "NVIDIA Corporation" and "NVIDIA" be considered identical (required when using OpenGL to avoid redundancy).
|
||||||
|
if RenderingServer.get_video_adapter_vendor().trim_suffix(" Corporation") in RenderingServer.get_video_adapter_name():
|
||||||
|
# Avoid repeating vendor name before adapter name.
|
||||||
|
# Trim redundant suffix sometimes reported by NVIDIA graphics cards when using OpenGL.
|
||||||
|
adapter_string = RenderingServer.get_video_adapter_name().trim_suffix("/PCIe/SSE2")
|
||||||
|
else:
|
||||||
|
adapter_string = RenderingServer.get_video_adapter_vendor() + " - " + RenderingServer.get_video_adapter_name().trim_suffix("/PCIe/SSE2")
|
||||||
|
|
||||||
|
# Graphics driver version information isn't always availble.
|
||||||
|
var driver_info := OS.get_video_adapter_driver_info()
|
||||||
|
var driver_info_string := ""
|
||||||
|
if driver_info.size() >= 2:
|
||||||
|
driver_info_string = driver_info[1]
|
||||||
|
else:
|
||||||
|
driver_info_string = "(unknown)"
|
||||||
|
|
||||||
|
var release_string := ""
|
||||||
|
if OS.has_feature("editor"):
|
||||||
|
# Editor build (implies `debug`).
|
||||||
|
release_string = "editor"
|
||||||
|
elif OS.has_feature("debug"):
|
||||||
|
# Debug export template build.
|
||||||
|
release_string = "debug"
|
||||||
|
else:
|
||||||
|
# Release export template build.
|
||||||
|
release_string = "release"
|
||||||
|
|
||||||
|
var rendering_method := str(ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method"))
|
||||||
|
var rendering_driver := str(ProjectSettings.get_setting_with_override("rendering/rendering_device/driver"))
|
||||||
|
var graphics_api_string := rendering_driver
|
||||||
|
if rendering_method != "gl_compatibility":
|
||||||
|
if rendering_driver == "d3d12":
|
||||||
|
graphics_api_string = "Direct3D 12"
|
||||||
|
elif rendering_driver == "metal":
|
||||||
|
graphics_api_string = "Metal"
|
||||||
|
elif rendering_driver == "vulkan":
|
||||||
|
if OS.has_feature("macos") or OS.has_feature("ios"):
|
||||||
|
graphics_api_string = "Vulkan via MoltenVK"
|
||||||
|
else:
|
||||||
|
graphics_api_string = "Vulkan"
|
||||||
|
else:
|
||||||
|
if rendering_driver == "opengl3_angle":
|
||||||
|
graphics_api_string = "OpenGL via ANGLE"
|
||||||
|
elif OS.has_feature("mobile") or rendering_driver == "opengl3_es":
|
||||||
|
graphics_api_string = "OpenGL ES"
|
||||||
|
elif OS.has_feature("web"):
|
||||||
|
graphics_api_string = "WebGL"
|
||||||
|
elif rendering_driver == "opengl3":
|
||||||
|
graphics_api_string = "OpenGL"
|
||||||
|
|
||||||
|
information.text = (
|
||||||
|
"%s, %d threads\n" % [OS.get_processor_name().replace("(R)", "").replace("(TM)", ""), OS.get_processor_count()]
|
||||||
|
+"%s %s (%s %s), %s %s\n" % [OS.get_name(), "64-bit" if OS.has_feature("64") else "32-bit", release_string, "double" if OS.has_feature("double") else "single", graphics_api_string, RenderingServer.get_video_adapter_api_version()]
|
||||||
|
+"%s, %s" % [adapter_string, driver_info_string]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _fps_graph_draw() -> void:
|
||||||
|
var fps_polyline := PackedVector2Array()
|
||||||
|
fps_polyline.resize(HISTORY_NUM_FRAMES)
|
||||||
|
for fps_index in fps_history.size():
|
||||||
|
fps_polyline[fps_index] = Vector2(
|
||||||
|
remap(fps_index, 0, fps_history.size(), 0, GRAPH_SIZE.x),
|
||||||
|
remap(clampf(fps_history[fps_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0)
|
||||||
|
)
|
||||||
|
# Don't use antialiasing to speed up line drawing, but use a width that scales with
|
||||||
|
# viewport scale to keep the line easily readable on hiDPI displays.
|
||||||
|
fps_graph.draw_polyline(fps_polyline, frame_time_gradient.sample(remap(frames_per_second, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
func _total_graph_draw() -> void:
|
||||||
|
var total_polyline := PackedVector2Array()
|
||||||
|
total_polyline.resize(HISTORY_NUM_FRAMES)
|
||||||
|
for total_index in frame_history_total.size():
|
||||||
|
total_polyline[total_index] = Vector2(
|
||||||
|
remap(total_index, 0, frame_history_total.size(), 0, GRAPH_SIZE.x),
|
||||||
|
remap(clampf(frame_history_total[total_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0)
|
||||||
|
)
|
||||||
|
# Don't use antialiasing to speed up line drawing, but use a width that scales with
|
||||||
|
# viewport scale to keep the line easily readable on hiDPI displays.
|
||||||
|
total_graph.draw_polyline(total_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
func _cpu_graph_draw() -> void:
|
||||||
|
var cpu_polyline := PackedVector2Array()
|
||||||
|
cpu_polyline.resize(HISTORY_NUM_FRAMES)
|
||||||
|
for cpu_index in frame_history_cpu.size():
|
||||||
|
cpu_polyline[cpu_index] = Vector2(
|
||||||
|
remap(cpu_index, 0, frame_history_cpu.size(), 0, GRAPH_SIZE.x),
|
||||||
|
remap(clampf(frame_history_cpu[cpu_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0)
|
||||||
|
)
|
||||||
|
# Don't use antialiasing to speed up line drawing, but use a width that scales with
|
||||||
|
# viewport scale to keep the line easily readable on hiDPI displays.
|
||||||
|
cpu_graph.draw_polyline(cpu_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_cpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
func _gpu_graph_draw() -> void:
|
||||||
|
var gpu_polyline := PackedVector2Array()
|
||||||
|
gpu_polyline.resize(HISTORY_NUM_FRAMES)
|
||||||
|
for gpu_index in frame_history_gpu.size():
|
||||||
|
gpu_polyline[gpu_index] = Vector2(
|
||||||
|
remap(gpu_index, 0, frame_history_gpu.size(), 0, GRAPH_SIZE.x),
|
||||||
|
remap(clampf(frame_history_gpu[gpu_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0)
|
||||||
|
)
|
||||||
|
# Don't use antialiasing to speed up line drawing, but use a width that scales with
|
||||||
|
# viewport scale to keep the line easily readable on hiDPI displays.
|
||||||
|
gpu_graph.draw_polyline(gpu_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_gpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
func _process(_delta: float) -> void:
|
||||||
|
if visible:
|
||||||
|
fps_graph.queue_redraw()
|
||||||
|
total_graph.queue_redraw()
|
||||||
|
cpu_graph.queue_redraw()
|
||||||
|
gpu_graph.queue_redraw()
|
||||||
|
|
||||||
|
# Difference between the last two rendered frames in milliseconds.
|
||||||
|
var frametime := (Time.get_ticks_usec() - last_tick) * 0.001
|
||||||
|
|
||||||
|
frame_history_total.push_back(frametime)
|
||||||
|
if frame_history_total.size() > HISTORY_NUM_FRAMES:
|
||||||
|
frame_history_total.pop_front()
|
||||||
|
|
||||||
|
# Frametimes are colored following FPS logic (red = 10 FPS, yellow = 60 FPS, green = 110 FPS, cyan = 160 FPS).
|
||||||
|
# This makes the color gradient non-linear.
|
||||||
|
frametime_avg = frame_history_total.reduce(sum_func) / frame_history_total.size()
|
||||||
|
frame_history_total_avg.text = str(frametime_avg).pad_decimals(2)
|
||||||
|
frame_history_total_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
|
||||||
|
var frametime_min: float = frame_history_total.min()
|
||||||
|
frame_history_total_min.text = str(frametime_min).pad_decimals(2)
|
||||||
|
frame_history_total_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
|
||||||
|
var frametime_max: float = frame_history_total.max()
|
||||||
|
frame_history_total_max.text = str(frametime_max).pad_decimals(2)
|
||||||
|
frame_history_total_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
|
||||||
|
frame_history_total_last.text = str(frametime).pad_decimals(2)
|
||||||
|
frame_history_total_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
|
||||||
|
var viewport_rid := get_viewport().get_viewport_rid()
|
||||||
|
var frametime_cpu := RenderingServer.viewport_get_measured_render_time_cpu(viewport_rid) + RenderingServer.get_frame_setup_time_cpu()
|
||||||
|
frame_history_cpu.push_back(frametime_cpu)
|
||||||
|
if frame_history_cpu.size() > HISTORY_NUM_FRAMES:
|
||||||
|
frame_history_cpu.pop_front()
|
||||||
|
|
||||||
|
frametime_cpu_avg = frame_history_cpu.reduce(sum_func) / frame_history_cpu.size()
|
||||||
|
frame_history_cpu_avg.text = str(frametime_cpu_avg).pad_decimals(2)
|
||||||
|
frame_history_cpu_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
|
||||||
|
var frametime_cpu_min: float = frame_history_cpu.min()
|
||||||
|
frame_history_cpu_min.text = str(frametime_cpu_min).pad_decimals(2)
|
||||||
|
frame_history_cpu_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
|
||||||
|
var frametime_cpu_max: float = frame_history_cpu.max()
|
||||||
|
frame_history_cpu_max.text = str(frametime_cpu_max).pad_decimals(2)
|
||||||
|
frame_history_cpu_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
|
||||||
|
frame_history_cpu_last.text = str(frametime_cpu).pad_decimals(2)
|
||||||
|
frame_history_cpu_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
|
||||||
|
var frametime_gpu := RenderingServer.viewport_get_measured_render_time_gpu(viewport_rid)
|
||||||
|
frame_history_gpu.push_back(frametime_gpu)
|
||||||
|
if frame_history_gpu.size() > HISTORY_NUM_FRAMES:
|
||||||
|
frame_history_gpu.pop_front()
|
||||||
|
|
||||||
|
frametime_gpu_avg = frame_history_gpu.reduce(sum_func) / frame_history_gpu.size()
|
||||||
|
frame_history_gpu_avg.text = str(frametime_gpu_avg).pad_decimals(2)
|
||||||
|
frame_history_gpu_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
|
||||||
|
var frametime_gpu_min: float = frame_history_gpu.min()
|
||||||
|
frame_history_gpu_min.text = str(frametime_gpu_min).pad_decimals(2)
|
||||||
|
frame_history_gpu_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
|
||||||
|
var frametime_gpu_max: float = frame_history_gpu.max()
|
||||||
|
frame_history_gpu_max.text = str(frametime_gpu_max).pad_decimals(2)
|
||||||
|
frame_history_gpu_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
|
||||||
|
frame_history_gpu_last.text = str(frametime_gpu).pad_decimals(2)
|
||||||
|
frame_history_gpu_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
|
||||||
|
frames_per_second = 1000.0 / frametime_avg
|
||||||
|
fps_history.push_back(frames_per_second)
|
||||||
|
if fps_history.size() > HISTORY_NUM_FRAMES:
|
||||||
|
fps_history.pop_front()
|
||||||
|
|
||||||
|
fps.text = str(floor(frames_per_second)) + " FPS"
|
||||||
|
var frame_time_color := frame_time_gradient.sample(remap(frames_per_second, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
|
||||||
|
fps.modulate = frame_time_color
|
||||||
|
|
||||||
|
num_entities.text = "Entities: " + str(ECS.world.entities.size())
|
||||||
|
|
||||||
|
frame_time.text = str(frametime).pad_decimals(2) + " mspf"
|
||||||
|
frame_time.modulate = frame_time_color
|
||||||
|
|
||||||
|
var vsync_string := ""
|
||||||
|
match DisplayServer.window_get_vsync_mode():
|
||||||
|
DisplayServer.VSYNC_ENABLED:
|
||||||
|
vsync_string = "V-Sync"
|
||||||
|
DisplayServer.VSYNC_ADAPTIVE:
|
||||||
|
vsync_string = "Adaptive V-Sync"
|
||||||
|
DisplayServer.VSYNC_MAILBOX:
|
||||||
|
vsync_string = "Mailbox V-Sync"
|
||||||
|
|
||||||
|
if Engine.max_fps > 0 or OS.low_processor_usage_mode:
|
||||||
|
# Display FPS cap determined by `Engine.max_fps` or low-processor usage mode sleep duration
|
||||||
|
# (the lowest FPS cap is used).
|
||||||
|
var low_processor_max_fps := roundi(1000000.0 / OS.low_processor_usage_mode_sleep_usec)
|
||||||
|
var fps_cap := low_processor_max_fps
|
||||||
|
if Engine.max_fps > 0:
|
||||||
|
fps_cap = mini(Engine.max_fps, low_processor_max_fps)
|
||||||
|
frame_time.text += " (cap: " + str(fps_cap) + " FPS"
|
||||||
|
|
||||||
|
if not vsync_string.is_empty():
|
||||||
|
frame_time.text += " + " + vsync_string
|
||||||
|
|
||||||
|
frame_time.text += ")"
|
||||||
|
else:
|
||||||
|
if not vsync_string.is_empty():
|
||||||
|
frame_time.text += " (" + vsync_string + ")"
|
||||||
|
|
||||||
|
frame_number.text = "Frame: " + str(Engine.get_frames_drawn())
|
||||||
|
|
||||||
|
last_tick = Time.get_ticks_usec()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_visibility_changed() -> void:
|
||||||
|
if visible:
|
||||||
|
# Reset graphs to prevent them from looking strange before `HISTORY_NUM_FRAMES` frames
|
||||||
|
# have been drawn.
|
||||||
|
var frametime_last := (Time.get_ticks_usec() - last_tick) * 0.001
|
||||||
|
fps_history.resize(HISTORY_NUM_FRAMES)
|
||||||
|
fps_history.fill(1000.0 / frametime_last)
|
||||||
|
frame_history_total.resize(HISTORY_NUM_FRAMES)
|
||||||
|
frame_history_total.fill(frametime_last)
|
||||||
|
frame_history_cpu.resize(HISTORY_NUM_FRAMES)
|
||||||
|
var viewport_rid := get_viewport().get_viewport_rid()
|
||||||
|
frame_history_cpu.fill(RenderingServer.viewport_get_measured_render_time_cpu(viewport_rid) + RenderingServer.get_frame_setup_time_cpu())
|
||||||
|
frame_history_gpu.resize(HISTORY_NUM_FRAMES)
|
||||||
|
frame_history_gpu.fill(RenderingServer.viewport_get_measured_render_time_gpu(viewport_rid))
|
||||||
1
addons/debug_menu/debug_menu.gd.uid
Normal file
1
addons/debug_menu/debug_menu.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://datrr7iu7jc62
|
||||||
412
addons/debug_menu/debug_menu.tscn
Normal file
412
addons/debug_menu/debug_menu.tscn
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://cggqb75a8w8r"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://datrr7iu7jc62" path="res://addons/debug_menu/debug_menu.gd" id="1_p440y"]
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ki0n8"]
|
||||||
|
bg_color = Color(0, 0, 0, 0.25098)
|
||||||
|
|
||||||
|
[node name="CanvasLayer" type="CanvasLayer" node_paths=PackedStringArray("fps", "num_entities", "frame_time", "frame_number", "frame_history_total_avg", "frame_history_total_min", "frame_history_total_max", "frame_history_total_last", "frame_history_cpu_avg", "frame_history_cpu_min", "frame_history_cpu_max", "frame_history_cpu_last", "frame_history_gpu_avg", "frame_history_gpu_min", "frame_history_gpu_max", "frame_history_gpu_last", "fps_graph", "total_graph", "cpu_graph", "gpu_graph", "information", "settings")]
|
||||||
|
layer = 128
|
||||||
|
script = ExtResource("1_p440y")
|
||||||
|
fps = NodePath("DebugMenu/VBoxContainer/FPS")
|
||||||
|
num_entities = NodePath("DebugMenu/VBoxContainer/NumEntities")
|
||||||
|
frame_time = NodePath("DebugMenu/VBoxContainer/FrameTime")
|
||||||
|
frame_number = NodePath("DebugMenu/VBoxContainer/FrameNumber")
|
||||||
|
frame_history_total_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalAvg")
|
||||||
|
frame_history_total_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalMin")
|
||||||
|
frame_history_total_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalMax")
|
||||||
|
frame_history_total_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalLast")
|
||||||
|
frame_history_cpu_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUAvg")
|
||||||
|
frame_history_cpu_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUMin")
|
||||||
|
frame_history_cpu_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUMax")
|
||||||
|
frame_history_cpu_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPULast")
|
||||||
|
frame_history_gpu_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUAvg")
|
||||||
|
frame_history_gpu_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUMin")
|
||||||
|
frame_history_gpu_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUMax")
|
||||||
|
frame_history_gpu_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPULast")
|
||||||
|
fps_graph = NodePath("DebugMenu/VBoxContainer/FPSGraph/Graph")
|
||||||
|
total_graph = NodePath("DebugMenu/VBoxContainer/TotalGraph/Graph")
|
||||||
|
cpu_graph = NodePath("DebugMenu/VBoxContainer/CPUGraph/Graph")
|
||||||
|
gpu_graph = NodePath("DebugMenu/VBoxContainer/GPUGraph/Graph")
|
||||||
|
information = NodePath("DebugMenu/VBoxContainer/Information")
|
||||||
|
settings = NodePath("DebugMenu/VBoxContainer/Settings")
|
||||||
|
|
||||||
|
[node name="DebugMenu" type="Control" parent="."]
|
||||||
|
custom_minimum_size = Vector2(400, 400)
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -416.0
|
||||||
|
offset_top = 8.0
|
||||||
|
offset_right = -16.0
|
||||||
|
offset_bottom = 408.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
size_flags_horizontal = 8
|
||||||
|
size_flags_vertical = 4
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="DebugMenu"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 1
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_left = -300.0
|
||||||
|
offset_bottom = 374.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_constants/separation = 0
|
||||||
|
|
||||||
|
[node name="FPS" type="Label" parent="DebugMenu/VBoxContainer"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/line_spacing = 0
|
||||||
|
theme_override_constants/outline_size = 5
|
||||||
|
theme_override_font_sizes/font_size = 18
|
||||||
|
text = "60 FPS"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="NumEntities" type="Label" parent="DebugMenu/VBoxContainer"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/line_spacing = 0
|
||||||
|
theme_override_constants/outline_size = 5
|
||||||
|
theme_override_font_sizes/font_size = 18
|
||||||
|
text = "0 Ents"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="FrameTime" type="Label" parent="DebugMenu/VBoxContainer"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "16.67 mspf (cap: 123 FPS + Adaptive V-Sync)"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="FrameNumber" type="Label" parent="DebugMenu/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "Frame: 1234"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="FrameTimeHistory" type="GridContainer" parent="DebugMenu/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 8
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_constants/h_separation = 0
|
||||||
|
theme_override_constants/v_separation = 0
|
||||||
|
columns = 5
|
||||||
|
|
||||||
|
[node name="Spacer" type="Control" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
custom_minimum_size = Vector2(60, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="AvgHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "Average"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="MinHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "Best"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="MaxHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "Worst"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="LastHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "Last"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="TotalHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "Total:"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="TotalAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "123.45"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="TotalMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "123.45"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="TotalMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "123.45"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="TotalLast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "123.45"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="CPUHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "CPU:"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="CPUAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "123.45"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="CPUMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "12.34"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="CPUMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "123.45"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="CPULast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "123.45"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="GPUHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "GPU:"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="GPUAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "123.45"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="GPUMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "1.23"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="GPUMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "123.45"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="GPULast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
|
||||||
|
modulate = Color(0, 1, 0, 1)
|
||||||
|
custom_minimum_size = Vector2(50, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "123.45"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="FPSGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
alignment = 2
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/FPSGraph"]
|
||||||
|
custom_minimum_size = Vector2(0, 27)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 8
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "FPS: ↑"
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/FPSGraph"]
|
||||||
|
custom_minimum_size = Vector2(150, 25)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 0
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8")
|
||||||
|
|
||||||
|
[node name="TotalGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
alignment = 2
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/TotalGraph"]
|
||||||
|
custom_minimum_size = Vector2(0, 27)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 8
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "Total: ↓"
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/TotalGraph"]
|
||||||
|
custom_minimum_size = Vector2(150, 25)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 0
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8")
|
||||||
|
|
||||||
|
[node name="CPUGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
alignment = 2
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/CPUGraph"]
|
||||||
|
custom_minimum_size = Vector2(0, 27)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 8
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "CPU: ↓"
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/CPUGraph"]
|
||||||
|
custom_minimum_size = Vector2(150, 25)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 0
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8")
|
||||||
|
|
||||||
|
[node name="GPUGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_filter = 2
|
||||||
|
alignment = 2
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/GPUGraph"]
|
||||||
|
custom_minimum_size = Vector2(0, 27)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 8
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "GPU: ↓"
|
||||||
|
vertical_alignment = 1
|
||||||
|
|
||||||
|
[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/GPUGraph"]
|
||||||
|
custom_minimum_size = Vector2(150, 25)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 0
|
||||||
|
mouse_filter = 2
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8")
|
||||||
|
|
||||||
|
[node name="Information" type="Label" parent="DebugMenu/VBoxContainer"]
|
||||||
|
modulate = Color(1, 1, 1, 0.752941)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "12th Gen Intel(R) Core(TM) i0-1234K
|
||||||
|
Windows 12 64-bit (double precision), Vulkan 1.2.34
|
||||||
|
NVIDIA GeForce RTX 1234, 123.45.67"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[node name="Settings" type="Label" parent="DebugMenu/VBoxContainer"]
|
||||||
|
modulate = Color(0.8, 0.84, 1, 0.752941)
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||||
|
theme_override_constants/outline_size = 3
|
||||||
|
theme_override_font_sizes/font_size = 12
|
||||||
|
text = "Project Version: 1.2.3
|
||||||
|
Rendering Method: Forward+
|
||||||
|
Window: 1234×567, Viewport: 1234×567
|
||||||
|
3D Scale (FSR 1.0): 100% = 1234×567
|
||||||
|
3D Antialiasing: TAA + 2× MSAA + FXAA
|
||||||
|
SSR: 123 Steps
|
||||||
|
SSAO: On
|
||||||
|
SSIL: On
|
||||||
|
SDFGI: 1 Cascades
|
||||||
|
Glow: On
|
||||||
|
Volumetric Fog: On
|
||||||
|
2D Antialiasing: 2× MSAA"
|
||||||
|
horizontal_alignment = 2
|
||||||
|
|
||||||
|
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
|
||||||
7
addons/debug_menu/plugin.cfg
Normal file
7
addons/debug_menu/plugin.cfg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[plugin]
|
||||||
|
|
||||||
|
name="Debug Menu"
|
||||||
|
description="In-game debug menu displaying performance metrics and hardware information"
|
||||||
|
author="Calinou"
|
||||||
|
version="1.2.0"
|
||||||
|
script="plugin.gd"
|
||||||
29
addons/debug_menu/plugin.gd
Normal file
29
addons/debug_menu/plugin.gd
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
@tool
|
||||||
|
extends EditorPlugin
|
||||||
|
|
||||||
|
func _enter_tree() -> void:
|
||||||
|
add_autoload_singleton("DebugMenu", "res://addons/debug_menu/debug_menu.tscn")
|
||||||
|
|
||||||
|
# FIXME: This appears to do nothing.
|
||||||
|
# if not ProjectSettings.has_setting("application/config/version"):
|
||||||
|
# ProjectSettings.set_setting("application/config/version", "1.0.0")
|
||||||
|
#
|
||||||
|
# ProjectSettings.set_initial_value("application/config/version", "1.0.0")
|
||||||
|
# ProjectSettings.add_property_info({
|
||||||
|
# name = "application/config/version",
|
||||||
|
# type = TYPE_STRING,
|
||||||
|
# })
|
||||||
|
#
|
||||||
|
# if not InputMap.has_action("cycle_debug_menu"):
|
||||||
|
# InputMap.add_action("cycle_debug_menu")
|
||||||
|
# var event := InputEventKey.new()
|
||||||
|
# event.keycode = KEY_F3
|
||||||
|
# InputMap.action_add_event("cycle_debug_menu", event)
|
||||||
|
#
|
||||||
|
# ProjectSettings.save()
|
||||||
|
|
||||||
|
|
||||||
|
func _exit_tree() -> void:
|
||||||
|
remove_autoload_singleton("DebugMenu")
|
||||||
|
# Don't remove the project setting's value and input map action,
|
||||||
|
# as the plugin may be re-enabled in the future.
|
||||||
1
addons/debug_menu/plugin.gd.uid
Normal file
1
addons/debug_menu/plugin.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://pb60bcxfhgp2
|
||||||
1320
addons/gdUnit4/GdUnitRunner.cfg
Normal file
1320
addons/gdUnit4/GdUnitRunner.cfg
Normal file
File diff suppressed because it is too large
Load Diff
21
addons/gdUnit4/LICENSE
Normal file
21
addons/gdUnit4/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Mike Schulze
|
||||||
|
|
||||||
|
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.
|
||||||
21
addons/gdUnit4/bin/GdUnitCmdTool.gd
Normal file
21
addons/gdUnit4/bin/GdUnitCmdTool.gd
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env -S godot -s
|
||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
|
||||||
|
var _cli_runner: GdUnitTestCIRunner
|
||||||
|
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED)
|
||||||
|
_cli_runner = GdUnitTestCIRunner.new()
|
||||||
|
root.add_child(_cli_runner)
|
||||||
|
|
||||||
|
|
||||||
|
# do not use print statements on _finalize it results in random crashes
|
||||||
|
func _finalize() -> void:
|
||||||
|
queue_delete(_cli_runner)
|
||||||
|
if OS.is_stdout_verbose():
|
||||||
|
prints("Finallize ..")
|
||||||
|
prints("-Orphan nodes report-----------------------")
|
||||||
|
Window.print_orphan_nodes()
|
||||||
|
prints("Finallize .. done")
|
||||||
1
addons/gdUnit4/bin/GdUnitCmdTool.gd.uid
Normal file
1
addons/gdUnit4/bin/GdUnitCmdTool.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://chdvqgrairx2r
|
||||||
167
addons/gdUnit4/bin/GdUnitCopyLog.gd
Normal file
167
addons/gdUnit4/bin/GdUnitCopyLog.gd
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
#!/usr/bin/env -S godot -s
|
||||||
|
extends MainLoop
|
||||||
|
|
||||||
|
const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd")
|
||||||
|
|
||||||
|
# gdlint: disable=max-line-length
|
||||||
|
const LOG_FRAME_TEMPLATE = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html style="display: inline-grid;">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Godot Logging</title>
|
||||||
|
<link rel="stylesheet" href="css/styles.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body style="background-color: #eee;">
|
||||||
|
<div class="godot-report-frame"">
|
||||||
|
${content}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
const NO_LOG_MESSAGE = """
|
||||||
|
<h3>No logging available!</h3>
|
||||||
|
</br>
|
||||||
|
<p>In order for logging to take place, you must activate the Activate file logging option in the project settings.</p>
|
||||||
|
<p>You can enable the logging under:
|
||||||
|
<b>Project Settings</b> > <b>Debug</b> > <b>File Logging</b> > <b>Enable File Logging</b> in the project settings.</p>
|
||||||
|
"""
|
||||||
|
|
||||||
|
#warning-ignore-all:return_value_discarded
|
||||||
|
var _cmd_options := CmdOptions.new([
|
||||||
|
CmdOption.new(
|
||||||
|
"-rd, --report-directory",
|
||||||
|
"-rd <directory>",
|
||||||
|
"Specifies the output directory in which the reports are to be written. The default is res://reports/.",
|
||||||
|
TYPE_STRING,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
var _report_root_path: String
|
||||||
|
var _current_report_path: String
|
||||||
|
var _debug_cmd_args := PackedStringArray()
|
||||||
|
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
set_report_directory(GdUnitFileAccess.current_dir() + "reports")
|
||||||
|
set_current_report_path()
|
||||||
|
|
||||||
|
|
||||||
|
func _process(_delta: float) -> bool:
|
||||||
|
# check if reports exists
|
||||||
|
if not reports_available():
|
||||||
|
prints("no reports found")
|
||||||
|
return true
|
||||||
|
|
||||||
|
# only process if godot logging is enabled
|
||||||
|
if not GdUnitSettings.is_log_enabled():
|
||||||
|
write_report(NO_LOG_MESSAGE, "")
|
||||||
|
return true
|
||||||
|
|
||||||
|
# parse possible custom report path,
|
||||||
|
var cmd_parser := CmdArgumentParser.new(_cmd_options, "GdUnitCmdTool.gd")
|
||||||
|
# ignore erros and exit quitly
|
||||||
|
if cmd_parser.parse(get_cmdline_args(), true).is_error():
|
||||||
|
return true
|
||||||
|
CmdCommandHandler.new(_cmd_options).register_cb("-rd", set_report_directory)
|
||||||
|
|
||||||
|
var godot_log_file := scan_latest_godot_log()
|
||||||
|
var result := read_log_file_content(godot_log_file)
|
||||||
|
if result.is_error():
|
||||||
|
write_report(result.error_message(), godot_log_file)
|
||||||
|
return true
|
||||||
|
write_report(result.value_as_string(), godot_log_file)
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func set_current_report_path() -> void:
|
||||||
|
# scan for latest report directory
|
||||||
|
var iteration := GdUnitFileAccess.find_last_path_index(
|
||||||
|
_report_root_path, GdUnitConstants.REPORT_DIR_PREFIX
|
||||||
|
)
|
||||||
|
_current_report_path = "%s/%s%d" % [_report_root_path, GdUnitConstants.REPORT_DIR_PREFIX, iteration]
|
||||||
|
|
||||||
|
|
||||||
|
func set_report_directory(path: String) -> void:
|
||||||
|
_report_root_path = path
|
||||||
|
|
||||||
|
|
||||||
|
func get_log_report_html() -> String:
|
||||||
|
return _current_report_path + "/godot_report_log.html"
|
||||||
|
|
||||||
|
|
||||||
|
func reports_available() -> bool:
|
||||||
|
return DirAccess.dir_exists_absolute(_report_root_path)
|
||||||
|
|
||||||
|
|
||||||
|
func scan_latest_godot_log() -> String:
|
||||||
|
var path := GdUnitSettings.get_log_path().get_base_dir()
|
||||||
|
var files_sorted := Array()
|
||||||
|
for file in GdUnitFileAccess.scan_dir(path):
|
||||||
|
var file_name := "%s/%s" % [path, file]
|
||||||
|
files_sorted.append(file_name)
|
||||||
|
# sort by name, the name contains the timestamp so we sort at the end by timestamp
|
||||||
|
files_sorted.sort()
|
||||||
|
return files_sorted.back()
|
||||||
|
|
||||||
|
|
||||||
|
func read_log_file_content(log_file: String) -> GdUnitResult:
|
||||||
|
var file := FileAccess.open(log_file, FileAccess.READ)
|
||||||
|
if file == null:
|
||||||
|
return GdUnitResult.error(
|
||||||
|
"Can't find log file '%s'. Error: %s"
|
||||||
|
% [log_file, error_string(FileAccess.get_open_error())]
|
||||||
|
)
|
||||||
|
var content := "<pre>" + file.get_as_text()
|
||||||
|
# patch out console format codes
|
||||||
|
for color_index in range(0, 256):
|
||||||
|
var to_replace := "[38;5;%dm" % color_index
|
||||||
|
content = content.replace(to_replace, "")
|
||||||
|
content += "</pre>"
|
||||||
|
content = content\
|
||||||
|
.replace("[0m", "")\
|
||||||
|
.replace(GdUnitCSIMessageWriter.CSI_BOLD, "")\
|
||||||
|
.replace(GdUnitCSIMessageWriter.CSI_ITALIC, "")\
|
||||||
|
.replace(GdUnitCSIMessageWriter.CSI_UNDERLINE, "")
|
||||||
|
return GdUnitResult.success(content)
|
||||||
|
|
||||||
|
|
||||||
|
func write_report(content: String, godot_log_file: String) -> GdUnitResult:
|
||||||
|
var file := FileAccess.open(get_log_report_html(), FileAccess.WRITE)
|
||||||
|
if file == null:
|
||||||
|
return GdUnitResult.error(
|
||||||
|
"Can't open to write '%s'. Error: %s"
|
||||||
|
% [get_log_report_html(), error_string(FileAccess.get_open_error())]
|
||||||
|
)
|
||||||
|
var report_html := LOG_FRAME_TEMPLATE.replace("${content}", content)
|
||||||
|
file.store_string(report_html)
|
||||||
|
_update_index_html(godot_log_file)
|
||||||
|
return GdUnitResult.success(file)
|
||||||
|
|
||||||
|
|
||||||
|
func _update_index_html(godot_log_file: String) -> void:
|
||||||
|
var index_path := "%s/index.html" % _current_report_path
|
||||||
|
var index_file := FileAccess.open(index_path, FileAccess.READ_WRITE)
|
||||||
|
if index_file == null:
|
||||||
|
push_error(
|
||||||
|
"Can't add log path '%s' to `%s`. Error: %s"
|
||||||
|
% [godot_log_file, index_path, error_string(FileAccess.get_open_error())]
|
||||||
|
)
|
||||||
|
return
|
||||||
|
var content := index_file.get_as_text()\
|
||||||
|
.replace("${log_report}", get_log_report_html())\
|
||||||
|
.replace("${godot_log_file}", godot_log_file)
|
||||||
|
# overide it
|
||||||
|
index_file.seek(0)
|
||||||
|
index_file.store_string(content)
|
||||||
|
|
||||||
|
|
||||||
|
func get_cmdline_args() -> PackedStringArray:
|
||||||
|
if _debug_cmd_args.is_empty():
|
||||||
|
return OS.get_cmdline_args()
|
||||||
|
return _debug_cmd_args
|
||||||
1
addons/gdUnit4/bin/GdUnitCopyLog.gd.uid
Normal file
1
addons/gdUnit4/bin/GdUnitCopyLog.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cca26e4thsgr2
|
||||||
7
addons/gdUnit4/plugin.cfg
Normal file
7
addons/gdUnit4/plugin.cfg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[plugin]
|
||||||
|
|
||||||
|
name="gdUnit4"
|
||||||
|
description="Unit Testing Framework for Godot Scripts"
|
||||||
|
author="Mike Schulze"
|
||||||
|
version="6.0.0"
|
||||||
|
script="plugin.gd"
|
||||||
110
addons/gdUnit4/plugin.gd
Normal file
110
addons/gdUnit4/plugin.gd
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
@tool
|
||||||
|
extends EditorPlugin
|
||||||
|
|
||||||
|
# We need to define manually the slot id's, to be downwards compatible
|
||||||
|
const CONTEXT_SLOT_FILESYSTEM: int = 1 # EditorContextMenuPlugin.CONTEXT_SLOT_FILESYSTEM
|
||||||
|
const CONTEXT_SLOT_SCRIPT_EDITOR: int = 2 # EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR
|
||||||
|
|
||||||
|
var _gd_inspector: Control
|
||||||
|
var _gd_console: Control
|
||||||
|
var _gd_filesystem_context_menu: Variant
|
||||||
|
var _gd_scripteditor_context_menu: Variant
|
||||||
|
|
||||||
|
|
||||||
|
func _enter_tree() -> void:
|
||||||
|
|
||||||
|
var inferred_declaration: int = ProjectSettings.get_setting("debug/gdscript/warnings/inferred_declaration")
|
||||||
|
var exclude_addons: bool = ProjectSettings.get_setting("debug/gdscript/warnings/exclude_addons")
|
||||||
|
if !exclude_addons and inferred_declaration != 0:
|
||||||
|
printerr("GdUnit4: 'inferred_declaration' is set to Warning/Error!")
|
||||||
|
printerr("GdUnit4 is not 'inferred_declaration' save, you have to excluded addons (debug/gdscript/warnings/exclude_addons)")
|
||||||
|
printerr("Loading GdUnit4 Plugin failed.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if check_running_in_test_env():
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
GdUnitCSIMessageWriter.new().prints_warning("It was recognized that GdUnit4 is running in a test environment, therefore the GdUnit4 plugin will not be executed!")
|
||||||
|
return
|
||||||
|
|
||||||
|
if Engine.get_version_info().hex < 0x40500:
|
||||||
|
prints("This GdUnit4 plugin version '%s' requires Godot version '4.5' or higher to run." % GdUnit4Version.current())
|
||||||
|
return
|
||||||
|
GdUnitSettings.setup()
|
||||||
|
# Install the GdUnit Inspector
|
||||||
|
_gd_inspector = (load("res://addons/gdUnit4/src/ui/GdUnitInspector.tscn") as PackedScene).instantiate()
|
||||||
|
_add_context_menus()
|
||||||
|
add_control_to_dock(EditorPlugin.DOCK_SLOT_LEFT_UR, _gd_inspector)
|
||||||
|
# Install the GdUnit Console
|
||||||
|
_gd_console = (load("res://addons/gdUnit4/src/ui/GdUnitConsole.tscn") as PackedScene).instantiate()
|
||||||
|
var control: Control = add_control_to_bottom_panel(_gd_console, "gdUnitConsole")
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
await _gd_console.setup_update_notification(control)
|
||||||
|
if GdUnit4CSharpApiLoader.is_api_loaded():
|
||||||
|
prints("GdUnit4Net version '%s' loaded." % GdUnit4CSharpApiLoader.version())
|
||||||
|
else:
|
||||||
|
prints("No GdUnit4Net found.")
|
||||||
|
# Connect to be notified for script changes to be able to discover new tests
|
||||||
|
GdUnitTestDiscoverGuard.instance()
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
resource_saved.connect(_on_resource_saved)
|
||||||
|
prints("Loading GdUnit4 Plugin success")
|
||||||
|
|
||||||
|
|
||||||
|
func _exit_tree() -> void:
|
||||||
|
if check_running_in_test_env():
|
||||||
|
return
|
||||||
|
if is_instance_valid(_gd_inspector):
|
||||||
|
remove_control_from_docks(_gd_inspector)
|
||||||
|
_gd_inspector.free()
|
||||||
|
_remove_context_menus()
|
||||||
|
if is_instance_valid(_gd_console):
|
||||||
|
remove_control_from_bottom_panel(_gd_console)
|
||||||
|
_gd_console.free()
|
||||||
|
var gdUnitTools: GDScript = load("res://addons/gdUnit4/src/core/GdUnitTools.gd")
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
gdUnitTools.dispose_all(true)
|
||||||
|
prints("Unload GdUnit4 Plugin success")
|
||||||
|
|
||||||
|
|
||||||
|
func check_running_in_test_env() -> bool:
|
||||||
|
var args: PackedStringArray = OS.get_cmdline_args()
|
||||||
|
args.append_array(OS.get_cmdline_user_args())
|
||||||
|
return DisplayServer.get_name() == "headless" or args.has("--selftest") or args.has("--add") or args.has("-a") or args.has("--quit-after") or args.has("--import")
|
||||||
|
|
||||||
|
|
||||||
|
func _add_context_menus() -> void:
|
||||||
|
if Engine.get_version_info().hex >= 0x40400:
|
||||||
|
# With Godot 4.4 we have to use the 'add_context_menu_plugin' to register editor context menus
|
||||||
|
_gd_filesystem_context_menu = _preload_gdx_script("res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandlerV44.gdx")
|
||||||
|
call_deferred("add_context_menu_plugin", CONTEXT_SLOT_FILESYSTEM, _gd_filesystem_context_menu)
|
||||||
|
# the CONTEXT_SLOT_SCRIPT_EDITOR is adding to the script panel instead of script editor see https://github.com/godotengine/godot/pull/100556
|
||||||
|
#_gd_scripteditor_context_menu = _preload("res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandlerV44.gdx")
|
||||||
|
#call_deferred("add_context_menu_plugin", CONTEXT_SLOT_SCRIPT_EDITOR, _gd_scripteditor_context_menu)
|
||||||
|
# so we use the old hacky way to add the context menu
|
||||||
|
_gd_inspector.add_child(preload("res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd").new())
|
||||||
|
else:
|
||||||
|
# TODO Delete it if the minimum requirement for the plugin is set to Godot 4.4.
|
||||||
|
_gd_inspector.add_child(preload("res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd").new())
|
||||||
|
_gd_inspector.add_child(preload("res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd").new())
|
||||||
|
|
||||||
|
|
||||||
|
func _remove_context_menus() -> void:
|
||||||
|
if is_instance_valid(_gd_filesystem_context_menu):
|
||||||
|
call_deferred("remove_context_menu_plugin", _gd_filesystem_context_menu)
|
||||||
|
if is_instance_valid(_gd_scripteditor_context_menu):
|
||||||
|
call_deferred("remove_context_menu_plugin", _gd_scripteditor_context_menu)
|
||||||
|
|
||||||
|
|
||||||
|
func _preload_gdx_script(script_path: String) -> Variant:
|
||||||
|
var script: GDScript = GDScript.new()
|
||||||
|
script.source_code = GdUnitFileAccess.resource_as_string(script_path)
|
||||||
|
script.take_over_path(script_path)
|
||||||
|
var err :Error = script.reload()
|
||||||
|
if err != OK:
|
||||||
|
push_error("Can't create context menu %s, error: %s" % [script_path, error_string(err)])
|
||||||
|
return script.new()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_resource_saved(resource: Resource) -> void:
|
||||||
|
if resource is Script:
|
||||||
|
await GdUnitTestDiscoverGuard.instance().discover(resource as Script)
|
||||||
1
addons/gdUnit4/plugin.gd.uid
Normal file
1
addons/gdUnit4/plugin.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://ddael08u8cd37
|
||||||
62
addons/gdUnit4/runtest.cmd
Normal file
62
addons/gdUnit4/runtest.cmd
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
:: Initialize variables
|
||||||
|
set "godot_binary="
|
||||||
|
set "filtered_args="
|
||||||
|
|
||||||
|
:: Process all arguments
|
||||||
|
set "i=0"
|
||||||
|
:parse_args
|
||||||
|
if "%~1"=="" goto end_parse_args
|
||||||
|
|
||||||
|
if "%~1"=="--godot_binary" (
|
||||||
|
set "godot_binary=%~2"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
) else (
|
||||||
|
set "filtered_args=!filtered_args! %~1"
|
||||||
|
shift
|
||||||
|
)
|
||||||
|
goto parse_args
|
||||||
|
:end_parse_args
|
||||||
|
|
||||||
|
:: If --godot_binary wasn't provided, fallback to environment variable
|
||||||
|
if "!godot_binary!"=="" (
|
||||||
|
set "godot_binary=%GODOT_BIN%"
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Check if we have a godot_binary value from any source
|
||||||
|
if "!godot_binary!"=="" (
|
||||||
|
echo Godot binary path is not specified.
|
||||||
|
echo Please either:
|
||||||
|
echo - Set the environment variable: set GODOT_BIN=C:\path\to\godot.exe
|
||||||
|
echo - Or use the --godot_binary argument: --godot_binary C:\path\to\godot.exe
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Check if the Godot binary exists
|
||||||
|
if not exist "!godot_binary!" (
|
||||||
|
echo Error: The specified Godot binary '!godot_binary!' does not exist.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Get Godot version and check if it's a mono build
|
||||||
|
for /f "tokens=*" %%i in ('"!godot_binary!" --version') do set GODOT_VERSION=%%i
|
||||||
|
echo !GODOT_VERSION! | findstr /I "mono" >nul
|
||||||
|
if !errorlevel! equ 0 (
|
||||||
|
echo Godot .NET detected
|
||||||
|
echo Compiling c# classes ... Please Wait
|
||||||
|
dotnet build --debug
|
||||||
|
echo done !errorlevel!
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Run the tests with the filtered arguments
|
||||||
|
"!godot_binary!" --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd !filtered_args!
|
||||||
|
set exit_code=%ERRORLEVEL%
|
||||||
|
echo Run tests ends with %exit_code%
|
||||||
|
|
||||||
|
:: Run the copy log command
|
||||||
|
"!godot_binary!" --headless --path . --quiet -s res://addons/gdUnit4/bin/GdUnitCopyLog.gd !filtered_args! > nul
|
||||||
|
set exit_code2=%ERRORLEVEL%
|
||||||
|
exit /b %exit_code%
|
||||||
62
addons/gdUnit4/runtest.sh
Normal file
62
addons/gdUnit4/runtest.sh
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Check for command-line argument
|
||||||
|
godot_binary=""
|
||||||
|
filtered_args=""
|
||||||
|
|
||||||
|
# Process all arguments with a more compatible approach
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
if [ "$1" = "--godot_binary" ] && [ $# -gt 1 ]; then
|
||||||
|
# Get the next argument as the value
|
||||||
|
godot_binary="$2"
|
||||||
|
shift 2
|
||||||
|
else
|
||||||
|
# Keep non-godot_binary arguments for passing to Godot
|
||||||
|
filtered_args="$filtered_args $1"
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# If --godot_binary wasn't provided, fallback to environment variable
|
||||||
|
if [ -z "$godot_binary" ]; then
|
||||||
|
godot_binary="$GODOT_BIN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if we have a godot_binary value from any source
|
||||||
|
if [ -z "$godot_binary" ]; then
|
||||||
|
echo "Godot binary path is not specified."
|
||||||
|
echo "Please either:"
|
||||||
|
echo " - Set the environment variable: export GODOT_BIN=/path/to/godot"
|
||||||
|
echo " - Or use the --godot_binary argument: --godot_binary /path/to/godot"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if the Godot binary exists and is executable
|
||||||
|
if [ ! -f "$godot_binary" ]; then
|
||||||
|
echo "Error: The specified Godot binary '$godot_binary' does not exist."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$godot_binary" ]; then
|
||||||
|
echo "Error: The specified Godot binary '$godot_binary' is not executable."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get Godot version and check if it's a .NET build
|
||||||
|
GODOT_VERSION=$("$godot_binary" --version)
|
||||||
|
if echo "$GODOT_VERSION" | grep -i "mono" > /dev/null; then
|
||||||
|
echo "Godot .NET detected"
|
||||||
|
echo "Compiling c# classes ... Please Wait"
|
||||||
|
dotnet build --debug
|
||||||
|
echo "done $?"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run the tests with the filtered arguments
|
||||||
|
"$godot_binary" --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd $filtered_args
|
||||||
|
exit_code=$?
|
||||||
|
echo "Run tests ends with $exit_code"
|
||||||
|
|
||||||
|
# Run the copy log command
|
||||||
|
"$godot_binary" --headless --path . --quiet -s res://addons/gdUnit4/bin/GdUnitCopyLog.gd $filtered_args > /dev/null
|
||||||
|
exit_code2=$?
|
||||||
|
exit $exit_code
|
||||||
12
addons/gdUnit4/src/Comparator.gd
Normal file
12
addons/gdUnit4/src/Comparator.gd
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
class_name Comparator
|
||||||
|
extends Resource
|
||||||
|
|
||||||
|
enum {
|
||||||
|
EQUAL,
|
||||||
|
LESS_THAN,
|
||||||
|
LESS_EQUAL,
|
||||||
|
GREATER_THAN,
|
||||||
|
GREATER_EQUAL,
|
||||||
|
BETWEEN_EQUAL,
|
||||||
|
NOT_BETWEEN_EQUAL,
|
||||||
|
}
|
||||||
1
addons/gdUnit4/src/Comparator.gd.uid
Normal file
1
addons/gdUnit4/src/Comparator.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://diowb66hireor
|
||||||
34
addons/gdUnit4/src/Fuzzers.gd
Normal file
34
addons/gdUnit4/src/Fuzzers.gd
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
## A fuzzer implementation to provide default implementation
|
||||||
|
class_name Fuzzers
|
||||||
|
extends Resource
|
||||||
|
|
||||||
|
|
||||||
|
## Generates an random string with min/max length and given charset
|
||||||
|
static func rand_str(min_length: int, max_length :int, charset := StringFuzzer.DEFAULT_CHARSET) -> Fuzzer:
|
||||||
|
return StringFuzzer.new(min_length, max_length, charset)
|
||||||
|
|
||||||
|
|
||||||
|
## Generates an random integer in a range form to
|
||||||
|
static func rangei(from: int, to: int) -> Fuzzer:
|
||||||
|
return IntFuzzer.new(from, to)
|
||||||
|
|
||||||
|
## Generates a randon float within in a given range
|
||||||
|
static func rangef(from: float, to: float) -> Fuzzer:
|
||||||
|
return FloatFuzzer.new(from, to)
|
||||||
|
|
||||||
|
## Generates an random Vector2 in a range form to
|
||||||
|
static func rangev2(from: Vector2, to: Vector2) -> Fuzzer:
|
||||||
|
return Vector2Fuzzer.new(from, to)
|
||||||
|
|
||||||
|
|
||||||
|
## Generates an random Vector3 in a range form to
|
||||||
|
static func rangev3(from: Vector3, to: Vector3) -> Fuzzer:
|
||||||
|
return Vector3Fuzzer.new(from, to)
|
||||||
|
|
||||||
|
## Generates an integer in a range form to that can be divided exactly by 2
|
||||||
|
static func eveni(from: int, to: int) -> Fuzzer:
|
||||||
|
return IntFuzzer.new(from, to, IntFuzzer.EVEN)
|
||||||
|
|
||||||
|
## Generates an integer in a range form to that cannot be divided exactly by 2
|
||||||
|
static func oddi(from: int, to: int) -> Fuzzer:
|
||||||
|
return IntFuzzer.new(from, to, IntFuzzer.ODD)
|
||||||
1
addons/gdUnit4/src/Fuzzers.gd.uid
Normal file
1
addons/gdUnit4/src/Fuzzers.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://b5k74b3q0djbr
|
||||||
122
addons/gdUnit4/src/GdUnitArrayAssert.gd
Normal file
122
addons/gdUnit4/src/GdUnitArrayAssert.gd
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
## An Assertion Tool to verify array values
|
||||||
|
@abstract class_name GdUnitArrayAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array is equal to the given one.
|
||||||
|
@abstract func is_equal(...expected: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array is equal to the given one, ignoring case considerations.
|
||||||
|
@abstract func is_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array is not equal to the given one.
|
||||||
|
@abstract func is_not_equal(...expected: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array is not equal to the given one, ignoring case considerations.
|
||||||
|
@abstract func is_not_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array is empty, it has a size of 0.
|
||||||
|
@abstract func is_empty() -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array is not empty, it has a size of minimum 1.
|
||||||
|
@abstract func is_not_empty() -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array is the same. [br]
|
||||||
|
## Compares the current by object reference equals
|
||||||
|
@abstract func is_same(expected: Variant) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array is NOT the same. [br]
|
||||||
|
## Compares the current by object reference equals
|
||||||
|
@abstract func is_not_same(expected: Variant) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array has a size of given value.
|
||||||
|
@abstract func has_size(expectd: int) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array contains the given values, in any order.[br]
|
||||||
|
## The values are compared by deep parameter comparision, for object reference compare you have to use [method contains_same]
|
||||||
|
@abstract func contains(...expected: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array contains exactly only the given values and nothing else, in same order.[br]
|
||||||
|
## The values are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_exactly]
|
||||||
|
@abstract func contains_exactly(...expected: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array contains exactly only the given values and nothing else, in any order.[br]
|
||||||
|
## The values are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_exactly_in_any_order]
|
||||||
|
@abstract func contains_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array contains the given values, in any order.[br]
|
||||||
|
## The values are compared by object reference, for deep parameter comparision use [method contains]
|
||||||
|
@abstract func contains_same(...expected: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array contains exactly only the given values and nothing else, in same order.[br]
|
||||||
|
## The values are compared by object reference, for deep parameter comparision use [method contains_exactly]
|
||||||
|
@abstract func contains_same_exactly(...expected: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array contains exactly only the given values and nothing else, in any order.[br]
|
||||||
|
## The values are compared by object reference, for deep parameter comparision use [method contains_exactly_in_any_order]
|
||||||
|
@abstract func contains_same_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array do NOT contains the given values, in any order.[br]
|
||||||
|
## The values are compared by deep parameter comparision, for object reference compare you have to use [method not_contains_same]
|
||||||
|
## [b]Example:[/b]
|
||||||
|
## [codeblock]
|
||||||
|
## # will succeed
|
||||||
|
## assert_array([1, 2, 3, 4, 5]).not_contains(6)
|
||||||
|
## # will fail
|
||||||
|
## assert_array([1, 2, 3, 4, 5]).not_contains(2, 6)
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func not_contains(...expected: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current Array do NOT contains the given values, in any order.[br]
|
||||||
|
## The values are compared by object reference, for deep parameter comparision use [method not_contains]
|
||||||
|
## [b]Example:[/b]
|
||||||
|
## [codeblock]
|
||||||
|
## # will succeed
|
||||||
|
## assert_array([1, 2, 3, 4, 5]).not_contains(6)
|
||||||
|
## # will fail
|
||||||
|
## assert_array([1, 2, 3, 4, 5]).not_contains(2, 6)
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func not_contains_same(...expected: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Extracts all values by given function name and optional arguments into a new ArrayAssert.
|
||||||
|
## If the elements not accessible by `func_name` the value is converted to `"n.a"`, expecting null values
|
||||||
|
@abstract func extract(func_name: String, ...func_args: Array) -> GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Extracts all values by given extractor's into a new ArrayAssert.
|
||||||
|
## If the elements not extractable than the value is converted to `"n.a"`, expecting null values
|
||||||
|
## -- The argument type is Array[GdUnitValueExtractor]
|
||||||
|
@abstract func extractv(...extractors: Array) -> GdUnitArrayAssert
|
||||||
1
addons/gdUnit4/src/GdUnitArrayAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitArrayAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://b7jtmrldpoyys
|
||||||
47
addons/gdUnit4/src/GdUnitAssert.gd
Normal file
47
addons/gdUnit4/src/GdUnitAssert.gd
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
## Base interface of all GdUnit asserts
|
||||||
|
@abstract class_name GdUnitAssert
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to expected one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to expected one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.[br]
|
||||||
|
## This function allows you to replace the automatically generated failure message with a more specific
|
||||||
|
## or user-friendly message that better describes the test failure context.[br]
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## # Override with custom context-specific message
|
||||||
|
## func test_player_inventory():
|
||||||
|
## assert_that(player.get_item_count("sword"))\
|
||||||
|
## .override_failure_message("Player should have exactly one sword")\
|
||||||
|
## .is_equal(1)
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.[br]
|
||||||
|
## This can be used to add additional information to the generated failure message
|
||||||
|
## while keeping the original assertion details for better debugging context.[br]
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## # Add context to existing failure message
|
||||||
|
## func test_player_health():
|
||||||
|
## assert_that(player.health)\
|
||||||
|
## .append_failure_message("Player was damaged by: %s" % last_damage_source)\
|
||||||
|
## .is_greater(0)
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitAssert
|
||||||
1
addons/gdUnit4/src/GdUnitAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://b8ypyyuevakpd
|
||||||
72
addons/gdUnit4/src/GdUnitAwaiter.gd
Normal file
72
addons/gdUnit4/src/GdUnitAwaiter.gd
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
class_name GdUnitAwaiter
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
|
||||||
|
# Waits for a specified signal in an interval of 50ms sent from the <source>, and terminates with an error after the specified timeout has elapsed.
|
||||||
|
# source: the object from which the signal is emitted
|
||||||
|
# signal_name: signal name
|
||||||
|
# args: the expected signal arguments as an array
|
||||||
|
# timeout: the timeout in ms, default is set to 2000ms
|
||||||
|
func await_signal_on(source :Object, signal_name :String, args :Array = [], timeout_millis :int = 2000) -> Variant:
|
||||||
|
# fail fast if the given source instance invalid
|
||||||
|
var assert_that := GdUnitAssertImpl.new(signal_name)
|
||||||
|
var line_number := GdUnitAssertions.get_line_number()
|
||||||
|
if not is_instance_valid(source):
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
assert_that.report_error(GdAssertMessages.error_await_signal_on_invalid_instance(source, signal_name, args), line_number)
|
||||||
|
return await (Engine.get_main_loop() as SceneTree).process_frame
|
||||||
|
# fail fast if the given source instance invalid
|
||||||
|
if not is_instance_valid(source):
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
assert_that.report_error(GdAssertMessages.error_await_signal_on_invalid_instance(source, signal_name, args), line_number)
|
||||||
|
return await await_idle_frame()
|
||||||
|
var awaiter := GdUnitSignalAwaiter.new(timeout_millis)
|
||||||
|
var value :Variant = await awaiter.on_signal(source, signal_name, args)
|
||||||
|
if awaiter.is_interrupted():
|
||||||
|
var failure := "await_signal_on(%s, %s) timed out after %sms" % [signal_name, args, timeout_millis]
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
assert_that.report_error(failure, line_number)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
# Waits for a specified signal sent from the <source> between idle frames and aborts with an error after the specified timeout has elapsed
|
||||||
|
# source: the object from which the signal is emitted
|
||||||
|
# signal_name: signal name
|
||||||
|
# args: the expected signal arguments as an array
|
||||||
|
# timeout: the timeout in ms, default is set to 2000ms
|
||||||
|
func await_signal_idle_frames(source :Object, signal_name :String, args :Array = [], timeout_millis :int = 2000) -> Variant:
|
||||||
|
var line_number := GdUnitAssertions.get_line_number()
|
||||||
|
# fail fast if the given source instance invalid
|
||||||
|
if not is_instance_valid(source):
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
GdUnitAssertImpl.new(signal_name)\
|
||||||
|
.report_error(GdAssertMessages.error_await_signal_on_invalid_instance(source, signal_name, args), line_number)
|
||||||
|
return await await_idle_frame()
|
||||||
|
var awaiter := GdUnitSignalAwaiter.new(timeout_millis, true)
|
||||||
|
var value :Variant = await awaiter.on_signal(source, signal_name, args)
|
||||||
|
if awaiter.is_interrupted():
|
||||||
|
var failure := "await_signal_idle_frames(%s, %s) timed out after %sms" % [signal_name, args, timeout_millis]
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
GdUnitAssertImpl.new(signal_name).report_error(failure, line_number)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
# Waits for for a given amount of milliseconds
|
||||||
|
# example:
|
||||||
|
# # waits for 100ms
|
||||||
|
# await GdUnitAwaiter.await_millis(myNode, 100).completed
|
||||||
|
# use this waiter and not `await get_tree().create_timer().timeout to prevent errors when a test case is timed out
|
||||||
|
func await_millis(milliSec :int) -> void:
|
||||||
|
var timer :Timer = Timer.new()
|
||||||
|
timer.set_name("gdunit_await_millis_timer_%d" % timer.get_instance_id())
|
||||||
|
(Engine.get_main_loop() as SceneTree).root.add_child(timer)
|
||||||
|
timer.add_to_group("GdUnitTimers")
|
||||||
|
timer.set_one_shot(true)
|
||||||
|
timer.start(milliSec / 1000.0)
|
||||||
|
await timer.timeout
|
||||||
|
timer.queue_free()
|
||||||
|
|
||||||
|
|
||||||
|
# Waits until the next idle frame
|
||||||
|
func await_idle_frame() -> void:
|
||||||
|
await (Engine.get_main_loop() as SceneTree).process_frame
|
||||||
1
addons/gdUnit4/src/GdUnitAwaiter.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitAwaiter.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cmpgcgbf1v3he
|
||||||
35
addons/gdUnit4/src/GdUnitBoolAssert.gd
Normal file
35
addons/gdUnit4/src/GdUnitBoolAssert.gd
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
## An Assertion Tool to verify boolean values
|
||||||
|
@abstract class_name GdUnitBoolAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitBoolAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitBoolAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to the given one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitBoolAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to the given one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitBoolAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitBoolAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitBoolAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is true.
|
||||||
|
@abstract func is_true() -> GdUnitBoolAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is false.
|
||||||
|
@abstract func is_false() -> GdUnitBoolAssert
|
||||||
1
addons/gdUnit4/src/GdUnitBoolAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitBoolAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://caidddxgb3yj
|
||||||
10
addons/gdUnit4/src/GdUnitConstants.gd
Normal file
10
addons/gdUnit4/src/GdUnitConstants.gd
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
class_name GdUnitConstants
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
const NO_ARG :Variant = "<--null-->"
|
||||||
|
|
||||||
|
const EXPECT_ASSERT_REPORT_FAILURES := "expect_assert_report_failures"
|
||||||
|
|
||||||
|
## The maximum number of report history files to store
|
||||||
|
const DEFAULT_REPORT_HISTORY_COUNT = 20
|
||||||
|
const REPORT_DIR_PREFIX = "report_"
|
||||||
1
addons/gdUnit4/src/GdUnitConstants.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitConstants.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dgclscvuh5v84
|
||||||
79
addons/gdUnit4/src/GdUnitDictionaryAssert.gd
Normal file
79
addons/gdUnit4/src/GdUnitDictionaryAssert.gd
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
## An Assertion Tool to verify dictionary
|
||||||
|
@abstract class_name GdUnitDictionaryAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary is equal to the given one, ignoring order.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary is not equal to the given one, ignoring order.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary is empty, it has a size of 0.
|
||||||
|
@abstract func is_empty() -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary is not empty, it has a size of minimum 1.
|
||||||
|
@abstract func is_not_empty() -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary is the same. [br]
|
||||||
|
## Compares the current by object reference equals
|
||||||
|
@abstract func is_same(expected: Variant) -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary is NOT the same. [br]
|
||||||
|
## Compares the current by object reference equals
|
||||||
|
@abstract func is_not_same(expected: Variant) -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary has a size of given value.
|
||||||
|
@abstract func has_size(expected: int) -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary contains the given key(s).[br]
|
||||||
|
## The keys are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_keys]
|
||||||
|
@abstract func contains_keys(...expected: Array) -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary contains the given key and value.[br]
|
||||||
|
## The key and value are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_key_value]
|
||||||
|
@abstract func contains_key_value(key: Variant, value: Variant) -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary not contains the given key(s).[br]
|
||||||
|
## The keys are compared by deep parameter comparision, for object reference compare you have to use [method not_contains_same_keys]
|
||||||
|
@abstract func not_contains_keys(...expected: Array) -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary contains the given key(s).[br]
|
||||||
|
## The keys are compared by object reference, for deep parameter comparision use [method contains_keys]
|
||||||
|
@abstract func contains_same_keys(expected: Array) -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary contains the given key and value.[br]
|
||||||
|
## The key and value are compared by object reference, for deep parameter comparision use [method contains_key_value]
|
||||||
|
@abstract func contains_same_key_value(key: Variant, value: Variant) -> GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current dictionary not contains the given key(s).
|
||||||
|
## The keys are compared by object reference, for deep parameter comparision use [method not_contains_keys]
|
||||||
|
@abstract func not_contains_same_keys(...expected: Array) -> GdUnitDictionaryAssert
|
||||||
1
addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bu6473b8ymdyh
|
||||||
52
addons/gdUnit4/src/GdUnitFailureAssert.gd
Normal file
52
addons/gdUnit4/src/GdUnitFailureAssert.gd
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
## An assertion tool to verify GDUnit asserts.
|
||||||
|
## This assert is for internal use only, to verify that failed asserts work as expected.
|
||||||
|
@abstract class_name GdUnitFailureAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitFailureAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitFailureAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to the given one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitFailureAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to expected one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitFailureAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitFailureAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitFailureAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies if the executed assert was successful
|
||||||
|
@abstract func is_success() -> GdUnitFailureAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies if the executed assert has failed
|
||||||
|
@abstract func is_failed() -> GdUnitFailureAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies the failure line is equal to expected one.
|
||||||
|
@abstract func has_line(expected: int) -> GdUnitFailureAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies the failure message is equal to expected one.
|
||||||
|
@abstract func has_message(expected: String) -> GdUnitFailureAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the failure message starts with the expected message.
|
||||||
|
@abstract func starts_with_message(expected: String) -> GdUnitFailureAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the failure message contains the expected message.
|
||||||
|
@abstract func contains_message(expected: String) -> GdUnitFailureAssert
|
||||||
1
addons/gdUnit4/src/GdUnitFailureAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitFailureAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://c2itwjhst3f1j
|
||||||
38
addons/gdUnit4/src/GdUnitFileAssert.gd
Normal file
38
addons/gdUnit4/src/GdUnitFileAssert.gd
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
@abstract class_name GdUnitFileAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitFileAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitFileAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to the given one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitFileAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to expected one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitFileAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitFileAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitFileAssert
|
||||||
|
|
||||||
|
|
||||||
|
@abstract func is_file() -> GdUnitFileAssert
|
||||||
|
|
||||||
|
|
||||||
|
@abstract func exists() -> GdUnitFileAssert
|
||||||
|
|
||||||
|
|
||||||
|
@abstract func is_script() -> GdUnitFileAssert
|
||||||
|
|
||||||
|
|
||||||
|
@abstract func contains_exactly(expected_rows :Array) -> GdUnitFileAssert
|
||||||
1
addons/gdUnit4/src/GdUnitFileAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitFileAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dw44nkyt05u6h
|
||||||
75
addons/gdUnit4/src/GdUnitFloatAssert.gd
Normal file
75
addons/gdUnit4/src/GdUnitFloatAssert.gd
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
## An Assertion Tool to verify float values
|
||||||
|
@abstract class_name GdUnitFloatAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to the given one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to expected one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current and expected value are approximately equal.
|
||||||
|
@abstract func is_equal_approx(expected: float, approx: float) -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is less than the given one.
|
||||||
|
@abstract func is_less(expected: float) -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is less than or equal the given one.
|
||||||
|
@abstract func is_less_equal(expected: float) -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is greater than the given one.
|
||||||
|
@abstract func is_greater(expected: float) -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is greater than or equal the given one.
|
||||||
|
@abstract func is_greater_equal(expected: float) -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is negative.
|
||||||
|
@abstract func is_negative() -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not negative.
|
||||||
|
@abstract func is_not_negative() -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to zero.
|
||||||
|
@abstract func is_zero() -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to zero.
|
||||||
|
@abstract func is_not_zero() -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is in the given set of values.
|
||||||
|
@abstract func is_in(expected: Array) -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not in the given set of values.
|
||||||
|
@abstract func is_not_in(expected: Array) -> GdUnitFloatAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is between the given boundaries (inclusive).
|
||||||
|
@abstract func is_between(from: float, to: float) -> GdUnitFloatAssert
|
||||||
1
addons/gdUnit4/src/GdUnitFloatAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitFloatAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://x57gp00db8lb
|
||||||
42
addons/gdUnit4/src/GdUnitFuncAssert.gd
Normal file
42
addons/gdUnit4/src/GdUnitFuncAssert.gd
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
## An Assertion Tool to verify function callback values
|
||||||
|
@abstract class_name GdUnitFuncAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitFuncAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitFuncAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to the given one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitFuncAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to expected one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitFuncAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitFuncAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitFuncAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is true.
|
||||||
|
@abstract func is_true() -> GdUnitFuncAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is false.
|
||||||
|
@abstract func is_false() -> GdUnitFuncAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Sets the timeout in ms to wait the function returnd the expected value, if the time over a failure is emitted.[br]
|
||||||
|
## e.g.[br]
|
||||||
|
## do wait until 5s the function `is_state` is returns 10 [br]
|
||||||
|
## [code]assert_func(instance, "is_state").wait_until(5000).is_equal(10)[/code]
|
||||||
|
@abstract func wait_until(timeout: int) -> GdUnitFuncAssert
|
||||||
1
addons/gdUnit4/src/GdUnitFuncAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitFuncAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dq7qrmge6m2ug
|
||||||
59
addons/gdUnit4/src/GdUnitGodotErrorAssert.gd
Normal file
59
addons/gdUnit4/src/GdUnitGodotErrorAssert.gd
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
## An assertion tool to verify for Godot runtime errors like assert() and push notifications like push_error().
|
||||||
|
@abstract class_name GdUnitGodotErrorAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitGodotErrorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitGodotErrorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to the given one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitGodotErrorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to expected one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitGodotErrorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitGodotErrorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitGodotErrorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies if the executed code runs without any runtime errors
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## await assert_error(<callable>).is_success()
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func is_success() -> GdUnitGodotErrorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies if the executed code runs into a runtime error
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## await assert_error(<callable>).is_runtime_error(<expected error message>)
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func is_runtime_error(expected_error: Variant) -> GdUnitGodotErrorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies if the executed code has a push_warning() used
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## await assert_error(<callable>).is_push_warning(<expected push warning message>)
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func is_push_warning(expected_warning: Variant) -> GdUnitGodotErrorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies if the executed code has a push_error() used
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## await assert_error(<callable>).is_push_error(<expected push error message>)
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func is_push_error(expected_error: Variant) -> GdUnitGodotErrorAssert
|
||||||
1
addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bv0opv8h54qk0
|
||||||
79
addons/gdUnit4/src/GdUnitIntAssert.gd
Normal file
79
addons/gdUnit4/src/GdUnitIntAssert.gd
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
## An Assertion Tool to verify integer values
|
||||||
|
@abstract class_name GdUnitIntAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to the given one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to expected one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is less than the given one.
|
||||||
|
@abstract func is_less(expected: int) -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is less than or equal the given one.
|
||||||
|
@abstract func is_less_equal(expected: int) -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is greater than the given one.
|
||||||
|
@abstract func is_greater(expected: int) -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is greater than or equal the given one.
|
||||||
|
@abstract func is_greater_equal(expected: int) -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is even.
|
||||||
|
@abstract func is_even() -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is odd.
|
||||||
|
@abstract func is_odd() -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is negative.
|
||||||
|
@abstract func is_negative() -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not negative.
|
||||||
|
@abstract func is_not_negative() -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to zero.
|
||||||
|
@abstract func is_zero() -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to zero.
|
||||||
|
@abstract func is_not_zero() -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is in the given set of values.
|
||||||
|
@abstract func is_in(expected: Array) -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not in the given set of values.
|
||||||
|
@abstract func is_not_in(expected: Array) -> GdUnitIntAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is between the given boundaries (inclusive).
|
||||||
|
@abstract func is_between(from: int, to: int) -> GdUnitIntAssert
|
||||||
1
addons/gdUnit4/src/GdUnitIntAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitIntAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://b56cu45wqwctd
|
||||||
51
addons/gdUnit4/src/GdUnitObjectAssert.gd
Normal file
51
addons/gdUnit4/src/GdUnitObjectAssert.gd
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
## An Assertion Tool to verify Object values
|
||||||
|
@abstract class_name GdUnitObjectAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitObjectAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitObjectAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to the given one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitObjectAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to expected one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitObjectAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitObjectAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitObjectAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current object is the same as the given one.
|
||||||
|
@abstract func is_same(expected: Variant) -> GdUnitObjectAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current object is not the same as the given one.
|
||||||
|
@abstract func is_not_same(expected: Variant) -> GdUnitObjectAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current object is an instance of the given type.
|
||||||
|
@abstract func is_instanceof(type: Variant) -> GdUnitObjectAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current object is not an instance of the given type.
|
||||||
|
@abstract func is_not_instanceof(type: Variant) -> GdUnitObjectAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Checks whether the current object inherits from the specified type.
|
||||||
|
@abstract func is_inheriting(type: Variant) -> GdUnitObjectAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Checks whether the current object does NOT inherit from the specified type.
|
||||||
|
@abstract func is_not_inheriting(type: Variant) -> GdUnitObjectAssert
|
||||||
1
addons/gdUnit4/src/GdUnitObjectAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitObjectAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://huu1od8c2rtu
|
||||||
51
addons/gdUnit4/src/GdUnitResultAssert.gd
Normal file
51
addons/gdUnit4/src/GdUnitResultAssert.gd
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
## An Assertion Tool to verify Results
|
||||||
|
@abstract class_name GdUnitResultAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitResultAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitResultAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to the given one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitResultAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to expected one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitResultAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitResultAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitResultAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the result is ends up with empty
|
||||||
|
@abstract func is_empty() -> GdUnitResultAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the result is ends up with success
|
||||||
|
@abstract func is_success() -> GdUnitResultAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the result is ends up with warning
|
||||||
|
@abstract func is_warning() -> GdUnitResultAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the result is ends up with error
|
||||||
|
@abstract func is_error() -> GdUnitResultAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the result contains the given message
|
||||||
|
@abstract func contains_message(expected: String) -> GdUnitResultAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the result contains the given value
|
||||||
|
@abstract func is_value(expected: Variant) -> GdUnitResultAssert
|
||||||
1
addons/gdUnit4/src/GdUnitResultAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitResultAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://b0xr5fcj2q42p
|
||||||
325
addons/gdUnit4/src/GdUnitSceneRunner.gd
Normal file
325
addons/gdUnit4/src/GdUnitSceneRunner.gd
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
## The Scene Runner is a tool used for simulating interactions on a scene.
|
||||||
|
## With this tool, you can simulate input events such as keyboard or mouse input and/or simulate scene processing over a certain number of frames.
|
||||||
|
## This tool is typically used for integration testing a scene.
|
||||||
|
@abstract class_name GdUnitSceneRunner
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates that an action has been pressed.[br]
|
||||||
|
## [member action] : the action e.g. [code]"ui_up"[/code][br]
|
||||||
|
## [member event_index] : [url=https://docs.godotengine.org/en/4.4/classes/class_inputeventaction.html#class-inputeventaction-property-event-index]default=-1[/url][br]
|
||||||
|
@abstract func simulate_action_pressed(action: String, event_index := -1) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates that an action is pressed.[br]
|
||||||
|
## [member action] : the action e.g. [code]"ui_up"[/code][br]
|
||||||
|
## [member event_index] : [url=https://docs.godotengine.org/en/4.4/classes/class_inputeventaction.html#class-inputeventaction-property-event-index]default=-1[/url][br]
|
||||||
|
@abstract func simulate_action_press(action: String, event_index := -1) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates that an action has been released.[br]
|
||||||
|
## [member action] : the action e.g. [code]"ui_up"[/code][br]
|
||||||
|
## [member event_index] : [url=https://docs.godotengine.org/en/4.4/classes/class_inputeventaction.html#class-inputeventaction-property-event-index]default=-1[/url][br]
|
||||||
|
@abstract func simulate_action_release(action: String, event_index := -1) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates that a key has been pressed.[br]
|
||||||
|
## [member key_code] : the key code e.g. [constant KEY_ENTER][br]
|
||||||
|
## [member shift_pressed] : false by default set to true if simmulate shift is press[br]
|
||||||
|
## [member ctrl_pressed] : false by default set to true if simmulate control is press[br]
|
||||||
|
## [codeblock]
|
||||||
|
## func test_key_presssed():
|
||||||
|
## var runner = scene_runner("res://scenes/simple_scene.tscn")
|
||||||
|
## await runner.simulate_key_pressed(KEY_SPACE)
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates that a key is pressed.[br]
|
||||||
|
## [member key_code] : the key code e.g. [constant KEY_ENTER][br]
|
||||||
|
## [member shift_pressed] : false by default set to true if simmulate shift is press[br]
|
||||||
|
## [member ctrl_pressed] : false by default set to true if simmulate control is press[br]
|
||||||
|
@abstract func simulate_key_press(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates that a key has been released.[br]
|
||||||
|
## [member key_code] : the key code e.g. [constant KEY_ENTER][br]
|
||||||
|
## [member shift_pressed] : false by default set to true if simmulate shift is press[br]
|
||||||
|
## [member ctrl_pressed] : false by default set to true if simmulate control is press[br]
|
||||||
|
@abstract func simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Sets the mouse position to the specified vector, provided in pixels and relative to an origin at the upper left corner of the currently focused Window Manager game window.[br]
|
||||||
|
## [member position] : The absolute position in pixels as Vector2
|
||||||
|
@abstract func set_mouse_position(position: Vector2) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Returns the mouse's position in this Viewport using the coordinate system of this Viewport.
|
||||||
|
@abstract func get_mouse_position() -> Vector2
|
||||||
|
|
||||||
|
|
||||||
|
## Gets the current global mouse position of the current window
|
||||||
|
@abstract func get_global_mouse_position() -> Vector2
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a mouse moved to final position.[br]
|
||||||
|
## [member position] : The final mouse position
|
||||||
|
@abstract func simulate_mouse_move(position: Vector2) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a mouse move to the relative coordinates (offset).[br]
|
||||||
|
## [color=yellow]You must use [b]await[/b] to wait until the simulated mouse movement is complete.[/color][br]
|
||||||
|
## [br]
|
||||||
|
## [member relative] : The relative position, indicating the mouse position offset.[br]
|
||||||
|
## [member time] : The time to move the mouse by the relative position in seconds (default is 1 second).[br]
|
||||||
|
## [member trans_type] : Sets the type of transition used (default is TRANS_LINEAR).[br]
|
||||||
|
## [codeblock]
|
||||||
|
## func test_move_mouse():
|
||||||
|
## var runner = scene_runner("res://scenes/simple_scene.tscn")
|
||||||
|
## await runner.simulate_mouse_move_relative(Vector2(100,100))
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func simulate_mouse_move_relative(relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a mouse move to the absolute coordinates.[br]
|
||||||
|
## [color=yellow]You must use [b]await[/b] to wait until the simulated mouse movement is complete.[/color][br]
|
||||||
|
## [br]
|
||||||
|
## [member position] : The final position of the mouse.[br]
|
||||||
|
## [member time] : The time to move the mouse to the final position in seconds (default is 1 second).[br]
|
||||||
|
## [member trans_type] : Sets the type of transition used (default is TRANS_LINEAR).[br]
|
||||||
|
## [codeblock]
|
||||||
|
## func test_move_mouse():
|
||||||
|
## var runner = scene_runner("res://scenes/simple_scene.tscn")
|
||||||
|
## await runner.simulate_mouse_move_absolute(Vector2(100,100))
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func simulate_mouse_move_absolute(position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a mouse button pressed.[br]
|
||||||
|
## [member button_index] : The mouse button identifier, one of the [enum MouseButton] or button wheel constants.
|
||||||
|
## [member double_click] : Set to true to simulate a double-click
|
||||||
|
@abstract func simulate_mouse_button_pressed(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a mouse button press (holding)[br]
|
||||||
|
## [member button_index] : The mouse button identifier, one of the [enum MouseButton] or button wheel constants.
|
||||||
|
## [member double_click] : Set to true to simulate a double-click
|
||||||
|
@abstract func simulate_mouse_button_press(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a mouse button released.[br]
|
||||||
|
## [member button_index] : The mouse button identifier, one of the [enum MouseButton] or button wheel constants.
|
||||||
|
@abstract func simulate_mouse_button_release(button_index: MouseButton) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a screen touch is pressed.[br]
|
||||||
|
## [member index] : The touch index in the case of a multi-touch event.[br]
|
||||||
|
## [member position] : The position to touch the screen.[br]
|
||||||
|
## [member double_tap] : If true, the touch's state is a double tab.
|
||||||
|
@abstract func simulate_screen_touch_pressed(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a screen touch press without releasing it immediately, effectively simulating a "hold" action.[br]
|
||||||
|
## [member index] : The touch index in the case of a multi-touch event.[br]
|
||||||
|
## [member position] : The position to touch the screen.[br]
|
||||||
|
## [member double_tap] : If true, the touch's state is a double tab.
|
||||||
|
@abstract func simulate_screen_touch_press(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a screen touch is released.[br]
|
||||||
|
## [member index] : The touch index in the case of a multi-touch event.[br]
|
||||||
|
## [member double_tap] : If true, the touch's state is a double tab.
|
||||||
|
@abstract func simulate_screen_touch_release(index: int, double_tap := false) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a touch drag and drop event to a relative position.[br]
|
||||||
|
## [color=yellow]You must use [b]await[/b] to wait until the simulated drag&drop is complete.[/color][br]
|
||||||
|
## [br]
|
||||||
|
## [member index] : The touch index in the case of a multi-touch event.[br]
|
||||||
|
## [member relative] : The relative position, indicating the drag&drop position offset.[br]
|
||||||
|
## [member time] : The time to move to the relative position in seconds (default is 1 second).[br]
|
||||||
|
## [member trans_type] : Sets the type of transition used (default is TRANS_LINEAR).[br]
|
||||||
|
## [codeblock]
|
||||||
|
## func test_touch_drag_drop():
|
||||||
|
## var runner = scene_runner("res://scenes/simple_scene.tscn")
|
||||||
|
## # start drag at position 50,50
|
||||||
|
## runner.simulate_screen_touch_drag_begin(1, Vector2(50, 50))
|
||||||
|
## # and drop it at final at 150,50 relative (50,50 + 100,0)
|
||||||
|
## await runner.simulate_screen_touch_drag_relative(1, Vector2(100,0))
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func simulate_screen_touch_drag_relative(index: int, relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a touch screen drop to the absolute coordinates (offset).[br]
|
||||||
|
## [color=yellow]You must use [b]await[/b] to wait until the simulated drop is complete.[/color][br]
|
||||||
|
## [br]
|
||||||
|
## [member index] : The touch index in the case of a multi-touch event.[br]
|
||||||
|
## [member position] : The final position, indicating the drop position.[br]
|
||||||
|
## [member time] : The time to move to the final position in seconds (default is 1 second).[br]
|
||||||
|
## [member trans_type] : Sets the type of transition used (default is TRANS_LINEAR).[br]
|
||||||
|
## [codeblock]
|
||||||
|
## func test_touch_drag_drop():
|
||||||
|
## var runner = scene_runner("res://scenes/simple_scene.tscn")
|
||||||
|
## # start drag at position 50,50
|
||||||
|
## runner.simulate_screen_touch_drag_begin(1, Vector2(50, 50))
|
||||||
|
## # and drop it at 100,50
|
||||||
|
## await runner.simulate_screen_touch_drag_absolute(1, Vector2(100,50))
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func simulate_screen_touch_drag_absolute(index: int, position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a complete drag and drop event from one position to another.[br]
|
||||||
|
## This is ideal for testing complex drag-and-drop scenarios that require a specific start and end position.[br]
|
||||||
|
## [color=yellow]You must use [b]await[/b] to wait until the simulated drop is complete.[/color][br]
|
||||||
|
## [br]
|
||||||
|
## [member index] : The touch index in the case of a multi-touch event.[br]
|
||||||
|
## [member position] : The drag start position, indicating the drag position.[br]
|
||||||
|
## [member drop_position] : The drop position, indicating the drop position.[br]
|
||||||
|
## [member time] : The time to move to the final position in seconds (default is 1 second).[br]
|
||||||
|
## [member trans_type] : Sets the type of transition used (default is TRANS_LINEAR).[br]
|
||||||
|
## [codeblock]
|
||||||
|
## func test_touch_drag_drop():
|
||||||
|
## var runner = scene_runner("res://scenes/simple_scene.tscn")
|
||||||
|
## # start drag at position 50,50 and drop it at 100,50
|
||||||
|
## await runner.simulate_screen_touch_drag_drop(1, Vector2(50, 50), Vector2(100,50))
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func simulate_screen_touch_drag_drop(index: int, position: Vector2, drop_position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates a touch screen drag event to given position.[br]
|
||||||
|
## [member index] : The touch index in the case of a multi-touch event.[br]
|
||||||
|
## [member position] : The drag start position, indicating the drag position.[br]
|
||||||
|
@abstract func simulate_screen_touch_drag(index: int, position: Vector2) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Returns the actual position of the touchscreen drag position by given index.
|
||||||
|
## [member index] : The touch index in the case of a multi-touch event.[br]
|
||||||
|
@abstract func get_screen_touch_drag_position(index: int) -> Vector2
|
||||||
|
|
||||||
|
|
||||||
|
## Sets how fast or slow the scene simulation is processed (clock ticks versus the real).[br]
|
||||||
|
## It defaults to 1.0. A value of 2.0 means the game moves twice as fast as real life,
|
||||||
|
## whilst a value of 0.5 means the game moves at half the regular speed.
|
||||||
|
## [member time_factor] : A float representing the simulation speed.[br]
|
||||||
|
## - Default is 1.0, meaning the simulation runs at normal speed.[br]
|
||||||
|
## - A value of 2.0 means the simulation runs twice as fast as real time.[br]
|
||||||
|
## - A value of 0.5 means the simulation runs at half the regular speed.[br]
|
||||||
|
@abstract func set_time_factor(time_factor: float = 1.0) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates scene processing for a certain number of frames.[br]
|
||||||
|
## [member frames] : amount of frames to process[br]
|
||||||
|
## [member delta_milli] : the time delta between a frame in milliseconds
|
||||||
|
@abstract func simulate_frames(frames: int, delta_milli: int = -1) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates scene processing until the given signal is emitted by the scene.[br]
|
||||||
|
## [member signal_name] : the signal to stop the simulation[br]
|
||||||
|
## [member args] : optional signal arguments to be matched for stop[br]
|
||||||
|
@abstract func simulate_until_signal(signal_name: String, ...args: Array) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Simulates scene processing until the given signal is emitted by the given object.[br]
|
||||||
|
## [member source] : the object that should emit the signal[br]
|
||||||
|
## [member signal_name] : the signal to stop the simulation[br]
|
||||||
|
## [member args] : optional signal arguments to be matched for stop
|
||||||
|
@abstract func simulate_until_object_signal(source: Object, signal_name: String, ...args: Array) -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Waits for all input events to be processed by flushing any buffered input events
|
||||||
|
## and then awaiting a full cycle of both the process and physics frames.[br]
|
||||||
|
## [br]
|
||||||
|
## This is typically used to ensure that any simulated or queued inputs are fully
|
||||||
|
## processed before proceeding with the next steps in the scene.[br]
|
||||||
|
## It's essential for reliable input simulation or when synchronizing logic based
|
||||||
|
## on inputs.[br]
|
||||||
|
##
|
||||||
|
## Usage Example:
|
||||||
|
## [codeblock]
|
||||||
|
## await await_input_processed() # Ensure all inputs are processed before continuing
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func await_input_processed() -> void
|
||||||
|
|
||||||
|
|
||||||
|
## The await_func function pauses execution until a specified function in the scene returns a value.[br]
|
||||||
|
## It returns a [GdUnitFuncAssert], which provides a suite of assertion methods to verify the returned value.[br]
|
||||||
|
## [member func_name] : The name of the function to wait for.[br]
|
||||||
|
## [member args] : Optional function arguments
|
||||||
|
## [br]
|
||||||
|
## Usage Example:
|
||||||
|
## [codeblock]
|
||||||
|
## # Waits for 'calculate_score' function and verifies the result is equal to 100.
|
||||||
|
## await_func("calculate_score").is_equal(100)
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func await_func(func_name: String, ...args: Array) -> GdUnitFuncAssert
|
||||||
|
|
||||||
|
|
||||||
|
## The await_func_on function extends the functionality of await_func by allowing you to specify a source node within the scene.[br]
|
||||||
|
## It waits for a specified function on that node to return a value and returns a [GdUnitFuncAssert] object for assertions.[br]
|
||||||
|
## [member source] : The object where implements the function.[br]
|
||||||
|
## [member func_name] : The name of the function to wait for.[br]
|
||||||
|
## [member args] : optional function arguments
|
||||||
|
## [br]
|
||||||
|
## Usage Example:
|
||||||
|
## [codeblock]
|
||||||
|
## # Waits for 'calculate_score' function and verifies the result is equal to 100.
|
||||||
|
## var my_instance := ScoreCalculator.new()
|
||||||
|
## await_func(my_instance, "calculate_score").is_equal(100)
|
||||||
|
## [/codeblock]
|
||||||
|
@abstract func await_func_on(source: Object, func_name: String, ...args: Array) -> GdUnitFuncAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Waits for the specified signal to be emitted by the scene. If the signal is not emitted within the given timeout, the operation fails.[br]
|
||||||
|
## [member signal_name] : The name of the signal to wait for[br]
|
||||||
|
## [member args] : The signal arguments as an array[br]
|
||||||
|
## [member timeout] : The maximum duration (in milliseconds) to wait for the signal to be emitted before failing
|
||||||
|
@abstract func await_signal(signal_name: String, args := [], timeout := 2000 ) -> void
|
||||||
|
|
||||||
|
|
||||||
|
## Waits for the specified signal to be emitted by a particular source node. If the signal is not emitted within the given timeout, the operation fails.[br]
|
||||||
|
## [member source] : the object from which the signal is emitted[br]
|
||||||
|
## [member signal_name] : The name of the signal to wait for[br]
|
||||||
|
## [member args] : The signal arguments as an array[br]
|
||||||
|
## [member timeout] : tThe maximum duration (in milliseconds) to wait for the signal to be emitted before failing
|
||||||
|
@abstract func await_signal_on(source: Object, signal_name: String, args := [], timeout := 2000 ) -> void
|
||||||
|
|
||||||
|
|
||||||
|
## Restores the scene window to a windowed mode and brings it to the foreground.[br]
|
||||||
|
## This ensures that the scene is visible and active during testing, making it easier to observe and interact with.
|
||||||
|
@abstract func move_window_to_foreground() -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Minimizes the scene window to a windowed mode and brings it to the background.[br]
|
||||||
|
## This ensures that the scene is hidden during testing.
|
||||||
|
@abstract func move_window_to_background() -> GdUnitSceneRunner
|
||||||
|
|
||||||
|
|
||||||
|
## Return the current value of the property with the name <name>.[br]
|
||||||
|
## [member name] : name of property[br]
|
||||||
|
## [member return] : the value of the property
|
||||||
|
@abstract func get_property(name: String) -> Variant
|
||||||
|
|
||||||
|
|
||||||
|
## Set the value <value> of the property with the name <name>.[br]
|
||||||
|
## [member name] : name of property[br]
|
||||||
|
## [member value] : value of property[br]
|
||||||
|
## [member return] : true|false depending on valid property name.
|
||||||
|
@abstract func set_property(name: String, value: Variant) -> bool
|
||||||
|
|
||||||
|
|
||||||
|
## executes the function specified by <name> in the scene and returns the result.[br]
|
||||||
|
## [member name] : the name of the function to execute[br]
|
||||||
|
## [member args] : optional function arguments[br]
|
||||||
|
## [member return] : the function result
|
||||||
|
@abstract func invoke(name: String, ...args: Array) -> Variant
|
||||||
|
|
||||||
|
|
||||||
|
## Searches for the specified node with the name in the current scene and returns it, otherwise null.[br]
|
||||||
|
## [member name] : the name of the node to find[br]
|
||||||
|
## [member recursive] : enables/disables seraching recursive[br]
|
||||||
|
## [member return] : the node if find otherwise null
|
||||||
|
@abstract func find_child(name: String, recursive: bool = true, owned: bool = false) -> Node
|
||||||
|
|
||||||
|
|
||||||
|
## Access to current running scene
|
||||||
|
@abstract func scene() -> Node
|
||||||
1
addons/gdUnit4/src/GdUnitSceneRunner.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitSceneRunner.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://2q5oityuhdkr
|
||||||
46
addons/gdUnit4/src/GdUnitSignalAssert.gd
Normal file
46
addons/gdUnit4/src/GdUnitSignalAssert.gd
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
## An Assertion Tool to verify for emitted signals until a waiting time
|
||||||
|
@abstract class_name GdUnitSignalAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitSignalAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitSignalAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to the given one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitSignalAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to expected one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitSignalAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitSignalAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitSignalAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that given signal is emitted until waiting time
|
||||||
|
@abstract func is_emitted(name: String, args := []) -> GdUnitSignalAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that given signal is NOT emitted until waiting time
|
||||||
|
@abstract func is_not_emitted(name: String, args := []) -> GdUnitSignalAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies the signal exists checked the emitter
|
||||||
|
@abstract func is_signal_exists(name: String) -> GdUnitSignalAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Sets the assert signal timeout in ms, if the time over a failure is reported.[br]
|
||||||
|
## e.g.[br]
|
||||||
|
## do wait until 5s the instance has emitted the signal `signal_a`[br]
|
||||||
|
## [code]assert_signal(instance).wait_until(5000).is_emitted("signal_a")[/code]
|
||||||
|
@abstract func wait_until(timeout: int) -> GdUnitSignalAssert
|
||||||
1
addons/gdUnit4/src/GdUnitSignalAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitSignalAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://mbxuu2iwaq2
|
||||||
71
addons/gdUnit4/src/GdUnitStringAssert.gd
Normal file
71
addons/gdUnit4/src/GdUnitStringAssert.gd
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
## An Assertion Tool to verify String values
|
||||||
|
@abstract class_name GdUnitStringAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to the given one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current String is equal to the given one, ignoring case considerations.
|
||||||
|
@abstract func is_equal_ignoring_case(expected: Variant) -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to expected one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current String is not equal to the given one, ignoring case considerations.
|
||||||
|
@abstract func is_not_equal_ignoring_case(expected: Variant) -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current String is empty, it has a length of 0.
|
||||||
|
@abstract func is_empty() -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current String is not empty, it has a length of minimum 1.
|
||||||
|
@abstract func is_not_empty() -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current String contains the given String.
|
||||||
|
@abstract func contains(expected: String) -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current String does not contain the given String.
|
||||||
|
@abstract func not_contains(expected: String) -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current String does not contain the given String, ignoring case considerations.
|
||||||
|
@abstract func contains_ignoring_case(expected: String) -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current String does not contain the given String, ignoring case considerations.
|
||||||
|
@abstract func not_contains_ignoring_case(expected: String) -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current String starts with the given prefix.
|
||||||
|
@abstract func starts_with(expected: String) -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current String ends with the given suffix.
|
||||||
|
@abstract func ends_with(expected: String) -> GdUnitStringAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current String has the expected length by used comparator.
|
||||||
|
@abstract func has_length(length: int, comparator: int = Comparator.EQUAL) -> GdUnitStringAssert
|
||||||
1
addons/gdUnit4/src/GdUnitStringAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitStringAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://haccmssdxpsq
|
||||||
691
addons/gdUnit4/src/GdUnitTestSuite.gd
Normal file
691
addons/gdUnit4/src/GdUnitTestSuite.gd
Normal file
@@ -0,0 +1,691 @@
|
|||||||
|
## The main class for all GdUnit test suites[br]
|
||||||
|
## This class is the main class to implement your unit tests[br]
|
||||||
|
## You have to extend and implement your test cases as described[br]
|
||||||
|
## e.g MyTests.gd [br]
|
||||||
|
## [codeblock]
|
||||||
|
## extends GdUnitTestSuite
|
||||||
|
## # testcase
|
||||||
|
## func test_case_a():
|
||||||
|
## assert_that("value").is_equal("value")
|
||||||
|
## [/codeblock]
|
||||||
|
## @tutorial: https://mikeschulze.github.io/gdUnit4/faq/test-suite/
|
||||||
|
|
||||||
|
@icon("res://addons/gdUnit4/src/ui/settings/logo.png")
|
||||||
|
class_name GdUnitTestSuite
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
const NO_ARG :Variant = GdUnitConstants.NO_ARG
|
||||||
|
|
||||||
|
### internal runtime variables that must not be overwritten!!!
|
||||||
|
@warning_ignore("unused_private_class_variable")
|
||||||
|
var __is_skipped := false
|
||||||
|
@warning_ignore("unused_private_class_variable")
|
||||||
|
var __skip_reason :String = "Unknow."
|
||||||
|
var __active_test_case :String
|
||||||
|
var __awaiter := __gdunit_awaiter()
|
||||||
|
|
||||||
|
|
||||||
|
### We now load all used asserts and tool scripts into the cache according to the principle of "lazy loading"
|
||||||
|
### in order to noticeably reduce the loading time of the test suite.
|
||||||
|
# We go this hard way to increase the loading performance to avoid reparsing all the used scripts
|
||||||
|
# for more detailed info -> https://github.com/godotengine/godot/issues/67400
|
||||||
|
func __lazy_load(script_path :String) -> GDScript:
|
||||||
|
return GdUnitAssertions.__lazy_load(script_path)
|
||||||
|
|
||||||
|
|
||||||
|
func __gdunit_assert() -> GDScript:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd")
|
||||||
|
|
||||||
|
|
||||||
|
func __gdunit_tools() -> GDScript:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/core/GdUnitTools.gd")
|
||||||
|
|
||||||
|
|
||||||
|
func __gdunit_file_access() -> GDScript:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/core/GdUnitFileAccess.gd")
|
||||||
|
|
||||||
|
|
||||||
|
func __gdunit_awaiter() -> Object:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/GdUnitAwaiter.gd").new()
|
||||||
|
|
||||||
|
|
||||||
|
func __gdunit_argument_matchers() -> GDScript:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd")
|
||||||
|
|
||||||
|
|
||||||
|
func __gdunit_object_interactions() -> GDScript:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd")
|
||||||
|
|
||||||
|
|
||||||
|
## This function is called before a test suite starts[br]
|
||||||
|
## You can overwrite to prepare test data or initalizize necessary variables
|
||||||
|
func before() -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
## This function is called at least when a test suite is finished[br]
|
||||||
|
## You can overwrite to cleanup data created during test running
|
||||||
|
func after() -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
## This function is called before a test case starts[br]
|
||||||
|
## You can overwrite to prepare test case specific data
|
||||||
|
func before_test() -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
## This function is called after the test case is finished[br]
|
||||||
|
## You can overwrite to cleanup your test case specific data
|
||||||
|
func after_test() -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func is_failure(_expected_failure :String = NO_ARG) -> bool:
|
||||||
|
return Engine.get_meta("GD_TEST_FAILURE") if Engine.has_meta("GD_TEST_FAILURE") else false
|
||||||
|
|
||||||
|
|
||||||
|
func set_active_test_case(test_case :String) -> void:
|
||||||
|
__active_test_case = test_case
|
||||||
|
|
||||||
|
|
||||||
|
# === Tools ====================================================================
|
||||||
|
# Mapps Godot error number to a readable error message. See at ERROR
|
||||||
|
# https://docs.godotengine.org/de/stable/classes/class_@globalscope.html#enum-globalscope-error
|
||||||
|
func error_as_string(error_number :int) -> String:
|
||||||
|
return error_string(error_number)
|
||||||
|
|
||||||
|
|
||||||
|
## A litle helper to auto freeing your created objects after test execution
|
||||||
|
func auto_free(obj :Variant) -> Variant:
|
||||||
|
var execution_context := GdUnitThreadManager.get_current_context().get_execution_context()
|
||||||
|
|
||||||
|
assert(execution_context != null, "INTERNAL ERROR: The current execution_context is null! Please report this as bug.")
|
||||||
|
return execution_context.register_auto_free(obj)
|
||||||
|
|
||||||
|
|
||||||
|
@warning_ignore("native_method_override")
|
||||||
|
func add_child(node :Node, force_readable_name := false, internal := Node.INTERNAL_MODE_DISABLED) -> void:
|
||||||
|
super.add_child(node, force_readable_name, internal)
|
||||||
|
var execution_context := GdUnitThreadManager.get_current_context().get_execution_context()
|
||||||
|
if execution_context != null:
|
||||||
|
execution_context.orphan_monitor_start()
|
||||||
|
|
||||||
|
|
||||||
|
## Discard the error message triggered by a timeout (interruption).[br]
|
||||||
|
## By default, an interrupted test is reported as an error.[br]
|
||||||
|
## This function allows you to change the message to Success when an interrupted error is reported.
|
||||||
|
func discard_error_interupted_by_timeout() -> void:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
__gdunit_tools().register_expect_interupted_by_timeout(self, __active_test_case)
|
||||||
|
|
||||||
|
|
||||||
|
## Creates a new directory under the temporary directory *user://tmp*[br]
|
||||||
|
## Useful for storing data during test execution. [br]
|
||||||
|
## The directory is automatically deleted after test suite execution
|
||||||
|
func create_temp_dir(relative_path :String) -> String:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_file_access().create_temp_dir(relative_path)
|
||||||
|
|
||||||
|
|
||||||
|
## Deletes the temporary base directory[br]
|
||||||
|
## Is called automatically after each execution of the test suite
|
||||||
|
func clean_temp_dir() -> void:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
__gdunit_file_access().clear_tmp()
|
||||||
|
|
||||||
|
|
||||||
|
## Creates a new file under the temporary directory *user://tmp* + <relative_path>[br]
|
||||||
|
## with given name <file_name> and given file <mode> (default = File.WRITE)[br]
|
||||||
|
## If success the returned File is automatically closed after the execution of the test suite
|
||||||
|
func create_temp_file(relative_path :String, file_name :String, mode := FileAccess.WRITE) -> FileAccess:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_file_access().create_temp_file(relative_path, file_name, mode)
|
||||||
|
|
||||||
|
|
||||||
|
## Reads a resource by given path <resource_path> into a PackedStringArray.
|
||||||
|
func resource_as_array(resource_path :String) -> PackedStringArray:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_file_access().resource_as_array(resource_path)
|
||||||
|
|
||||||
|
|
||||||
|
## Reads a resource by given path <resource_path> and returned the content as String.
|
||||||
|
func resource_as_string(resource_path :String) -> String:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_file_access().resource_as_string(resource_path)
|
||||||
|
|
||||||
|
|
||||||
|
## Reads a resource by given path <resource_path> and return Variand translated by str_to_var
|
||||||
|
func resource_as_var(resource_path :String) -> Variant:
|
||||||
|
@warning_ignore("unsafe_method_access", "unsafe_cast")
|
||||||
|
return str_to_var(__gdunit_file_access().resource_as_string(resource_path) as String)
|
||||||
|
|
||||||
|
|
||||||
|
## Waits for given signal to be emitted by <source> until a specified timeout to fail[br]
|
||||||
|
## source: the object from which the signal is emitted[br]
|
||||||
|
## signal_name: signal name[br]
|
||||||
|
## args: the expected signal arguments as an array[br]
|
||||||
|
## timeout: the timeout in ms, default is set to 2000ms
|
||||||
|
func await_signal_on(source :Object, signal_name :String, args :Array = [], timeout :int = 2000) -> Variant:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return await __awaiter.await_signal_on(source, signal_name, args, timeout)
|
||||||
|
|
||||||
|
|
||||||
|
## Waits until the next idle frame
|
||||||
|
func await_idle_frame() -> void:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
await __awaiter.await_idle_frame()
|
||||||
|
|
||||||
|
|
||||||
|
## Waits for a given amount of milliseconds[br]
|
||||||
|
## example:[br]
|
||||||
|
## [codeblock]
|
||||||
|
## # waits for 100ms
|
||||||
|
## await await_millis(myNode, 100).completed
|
||||||
|
## [/codeblock][br]
|
||||||
|
## use this waiter and not `await get_tree().create_timer().timeout to prevent errors when a test case is timed out
|
||||||
|
func await_millis(timeout :int) -> void:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
await __awaiter.await_millis(timeout)
|
||||||
|
|
||||||
|
|
||||||
|
## Creates a new scene runner to allow simulate interactions checked a scene.[br]
|
||||||
|
## The runner will manage the scene instance and release after the runner is released[br]
|
||||||
|
## example:[br]
|
||||||
|
## [codeblock]
|
||||||
|
## # creates a runner by using a instanciated scene
|
||||||
|
## var scene = load("res://foo/my_scne.tscn").instantiate()
|
||||||
|
## var runner := scene_runner(scene)
|
||||||
|
##
|
||||||
|
## # or simply creates a runner by using the scene resource path
|
||||||
|
## var runner := scene_runner("res://foo/my_scne.tscn")
|
||||||
|
## [/codeblock]
|
||||||
|
func scene_runner(scene :Variant, verbose := false) -> GdUnitSceneRunner:
|
||||||
|
return auto_free(__lazy_load("res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd").new(scene, verbose))
|
||||||
|
|
||||||
|
|
||||||
|
# === Mocking & Spy ===========================================================
|
||||||
|
|
||||||
|
## do return a default value for primitive types or null
|
||||||
|
const RETURN_DEFAULTS = GdUnitMock.RETURN_DEFAULTS
|
||||||
|
## do call the real implementation
|
||||||
|
const CALL_REAL_FUNC = GdUnitMock.CALL_REAL_FUNC
|
||||||
|
## do return a default value for primitive types and a fully mocked value for Object types
|
||||||
|
## builds full deep mocked object
|
||||||
|
const RETURN_DEEP_STUB = GdUnitMock.RETURN_DEEP_STUB
|
||||||
|
|
||||||
|
|
||||||
|
## Creates a mock for given class name
|
||||||
|
func mock(clazz :Variant, mock_mode := RETURN_DEFAULTS) -> Variant:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd").build(clazz, mock_mode)
|
||||||
|
|
||||||
|
|
||||||
|
## Creates a spy checked given object instance
|
||||||
|
func spy(instance :Variant) -> Variant:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd").build(instance)
|
||||||
|
|
||||||
|
|
||||||
|
## Configures a return value for the specified function and used arguments.[br]
|
||||||
|
## [b]Example:
|
||||||
|
## [codeblock]
|
||||||
|
## # overrides the return value of myMock.is_selected() to false
|
||||||
|
## do_return(false).on(myMock).is_selected()
|
||||||
|
## [/codeblock]
|
||||||
|
func do_return(value :Variant) -> GdUnitMock:
|
||||||
|
return GdUnitMock.new(value)
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies certain behavior happened at least once or exact number of times
|
||||||
|
func verify(obj :Variant, times := 1) -> Variant:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_object_interactions().verify(obj, times)
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies no interactions is happen checked this mock or spy
|
||||||
|
func verify_no_interactions(obj :Variant) -> GdUnitAssert:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_object_interactions().verify_no_interactions(obj)
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies the given mock or spy has any unverified interaction.
|
||||||
|
func verify_no_more_interactions(obj :Variant) -> GdUnitAssert:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_object_interactions().verify_no_more_interactions(obj)
|
||||||
|
|
||||||
|
|
||||||
|
## Resets the saved function call counters checked a mock or spy
|
||||||
|
func reset(obj :Variant) -> void:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
__gdunit_object_interactions().reset(obj)
|
||||||
|
|
||||||
|
|
||||||
|
## Starts monitoring the specified source to collect all transmitted signals.[br]
|
||||||
|
## The collected signals can then be checked with 'assert_signal'.[br]
|
||||||
|
## By default, the specified source is automatically released when the test ends.
|
||||||
|
## You can control this behavior by setting auto_free to false if you do not want the source to be automatically freed.[br]
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## var emitter := monitor_signals(MyEmitter.new())
|
||||||
|
## # call the function to send the signal
|
||||||
|
## emitter.do_it()
|
||||||
|
## # verify the signial is emitted
|
||||||
|
## await assert_signal(emitter).is_emitted('my_signal')
|
||||||
|
## [/codeblock]
|
||||||
|
func monitor_signals(source :Object, _auto_free := true) -> Object:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
__lazy_load("res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd")\
|
||||||
|
.get_current_context()\
|
||||||
|
.get_signal_collector()\
|
||||||
|
.register_emitter(source)
|
||||||
|
return auto_free(source) if _auto_free else source
|
||||||
|
|
||||||
|
|
||||||
|
# === Argument matchers ========================================================
|
||||||
|
## Argument matcher to match any argument
|
||||||
|
func any() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().any()
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any boolean value
|
||||||
|
func any_bool() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_BOOL)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any integer value
|
||||||
|
func any_int() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_INT)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any float value
|
||||||
|
func any_float() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_FLOAT)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any String value
|
||||||
|
func any_string() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_STRING)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Color value
|
||||||
|
func any_color() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_COLOR)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Vector typed value
|
||||||
|
func any_vector() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_types([
|
||||||
|
TYPE_VECTOR2,
|
||||||
|
TYPE_VECTOR2I,
|
||||||
|
TYPE_VECTOR3,
|
||||||
|
TYPE_VECTOR3I,
|
||||||
|
TYPE_VECTOR4,
|
||||||
|
TYPE_VECTOR4I,
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Vector2 value
|
||||||
|
func any_vector2() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_VECTOR2)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Vector2i value
|
||||||
|
func any_vector2i() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_VECTOR2I)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Vector3 value
|
||||||
|
func any_vector3() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_VECTOR3)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Vector3i value
|
||||||
|
func any_vector3i() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_VECTOR3I)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Vector4 value
|
||||||
|
func any_vector4() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_VECTOR4)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Vector4i value
|
||||||
|
func any_vector4i() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_VECTOR4I)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Rect2 value
|
||||||
|
func any_rect2() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_RECT2)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Plane value
|
||||||
|
func any_plane() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_PLANE)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Quaternion value
|
||||||
|
func any_quat() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_QUATERNION)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any AABB value
|
||||||
|
func any_aabb() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_AABB)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Basis value
|
||||||
|
func any_basis() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_BASIS)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Transform2D value
|
||||||
|
func any_transform_2d() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_TRANSFORM2D)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Transform3D value
|
||||||
|
func any_transform_3d() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_TRANSFORM3D)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any NodePath value
|
||||||
|
func any_node_path() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_NODE_PATH)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any RID value
|
||||||
|
func any_rid() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_RID)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Object value
|
||||||
|
func any_object() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_OBJECT)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Dictionary value
|
||||||
|
func any_dictionary() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_DICTIONARY)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any Array value
|
||||||
|
func any_array() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_ARRAY)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any PackedByteArray value
|
||||||
|
func any_packed_byte_array() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_PACKED_BYTE_ARRAY)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any PackedInt32Array value
|
||||||
|
func any_packed_int32_array() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_PACKED_INT32_ARRAY)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any PackedInt64Array value
|
||||||
|
func any_packed_int64_array() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_PACKED_INT64_ARRAY)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any PackedFloat32Array value
|
||||||
|
func any_packed_float32_array() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_PACKED_FLOAT32_ARRAY)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any PackedFloat64Array value
|
||||||
|
func any_packed_float64_array() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_PACKED_FLOAT64_ARRAY)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any PackedStringArray value
|
||||||
|
func any_packed_string_array() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_PACKED_STRING_ARRAY)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any PackedVector2Array value
|
||||||
|
func any_packed_vector2_array() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_PACKED_VECTOR2_ARRAY)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any PackedVector3Array value
|
||||||
|
func any_packed_vector3_array() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_PACKED_VECTOR3_ARRAY)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any PackedColorArray value
|
||||||
|
func any_packed_color_array() -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().by_type(TYPE_PACKED_COLOR_ARRAY)
|
||||||
|
|
||||||
|
|
||||||
|
## Argument matcher to match any instance of given class
|
||||||
|
func any_class(clazz :Object) -> GdUnitArgumentMatcher:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __gdunit_argument_matchers().any_class(clazz)
|
||||||
|
|
||||||
|
|
||||||
|
# === value extract utils ======================================================
|
||||||
|
## Builds an extractor by given function name and optional arguments
|
||||||
|
func extr(func_name :String, args := Array()) -> GdUnitValueExtractor:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd").new(func_name, args)
|
||||||
|
|
||||||
|
|
||||||
|
## Constructs a tuple by given arguments
|
||||||
|
func tuple(arg0 :Variant,
|
||||||
|
arg1 :Variant=NO_ARG,
|
||||||
|
arg2 :Variant=NO_ARG,
|
||||||
|
arg3 :Variant=NO_ARG,
|
||||||
|
arg4 :Variant=NO_ARG,
|
||||||
|
arg5 :Variant=NO_ARG,
|
||||||
|
arg6 :Variant=NO_ARG,
|
||||||
|
arg7 :Variant=NO_ARG,
|
||||||
|
arg8 :Variant=NO_ARG,
|
||||||
|
arg9 :Variant=NO_ARG) -> GdUnitTuple:
|
||||||
|
return GdUnitTuple.new(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
|
||||||
|
|
||||||
|
|
||||||
|
# === Asserts ==================================================================
|
||||||
|
|
||||||
|
## The common assertion tool to verify values.
|
||||||
|
## It checks the given value by type to fit to the best assert
|
||||||
|
func assert_that(current :Variant) -> GdUnitAssert:
|
||||||
|
match typeof(current):
|
||||||
|
TYPE_BOOL:
|
||||||
|
return assert_bool(current)
|
||||||
|
TYPE_INT:
|
||||||
|
return assert_int(current)
|
||||||
|
TYPE_FLOAT:
|
||||||
|
return assert_float(current)
|
||||||
|
TYPE_STRING:
|
||||||
|
return assert_str(current)
|
||||||
|
TYPE_VECTOR2, TYPE_VECTOR2I, TYPE_VECTOR3, TYPE_VECTOR3I, TYPE_VECTOR4, TYPE_VECTOR4I:
|
||||||
|
return assert_vector(current, false)
|
||||||
|
TYPE_DICTIONARY:
|
||||||
|
return assert_dict(current)
|
||||||
|
TYPE_ARRAY, TYPE_PACKED_BYTE_ARRAY, TYPE_PACKED_INT32_ARRAY, TYPE_PACKED_INT64_ARRAY,\
|
||||||
|
TYPE_PACKED_FLOAT32_ARRAY, TYPE_PACKED_FLOAT64_ARRAY, TYPE_PACKED_STRING_ARRAY,\
|
||||||
|
TYPE_PACKED_VECTOR2_ARRAY, TYPE_PACKED_VECTOR3_ARRAY, TYPE_PACKED_COLOR_ARRAY:
|
||||||
|
return assert_array(current, false)
|
||||||
|
TYPE_OBJECT, TYPE_NIL:
|
||||||
|
return assert_object(current)
|
||||||
|
_:
|
||||||
|
return __gdunit_assert().new(current)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to verify boolean values.
|
||||||
|
func assert_bool(current :Variant) -> GdUnitBoolAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd").new(current)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to verify String values.
|
||||||
|
func assert_str(current :Variant) -> GdUnitStringAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd").new(current)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to verify integer values.
|
||||||
|
func assert_int(current :Variant) -> GdUnitIntAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd").new(current)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to verify float values.
|
||||||
|
func assert_float(current :Variant) -> GdUnitFloatAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd").new(current)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to verify Vector values.[br]
|
||||||
|
## This assertion supports all vector types.[br]
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## assert_vector(Vector2(1.2, 1.000001)).is_equal(Vector2(1.2, 1.000001))
|
||||||
|
## [/codeblock]
|
||||||
|
func assert_vector(current :Variant, type_check := true) -> GdUnitVectorAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd").new(current, type_check)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to verify arrays.
|
||||||
|
func assert_array(current :Variant, type_check := true) -> GdUnitArrayAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd").new(current, type_check)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to verify dictionaries.
|
||||||
|
func assert_dict(current :Variant) -> GdUnitDictionaryAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd").new(current)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to verify FileAccess.
|
||||||
|
func assert_file(current :Variant) -> GdUnitFileAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd").new(current)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to verify Objects.
|
||||||
|
func assert_object(current :Variant) -> GdUnitObjectAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd").new(current)
|
||||||
|
|
||||||
|
|
||||||
|
func assert_result(current :Variant) -> GdUnitResultAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd").new(current)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool that waits until a certain time for an expected function return value
|
||||||
|
func assert_func(instance :Object, func_name :String, args := Array()) -> GdUnitFuncAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd").new(instance, func_name, args)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to verify for emitted signals until a certain time.
|
||||||
|
func assert_signal(instance :Object) -> GdUnitSignalAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd").new(instance)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to test for failing assertions.[br]
|
||||||
|
## This assert is only designed for internal use to verify failing asserts working as expected.[br]
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## assert_failure(func(): assert_bool(true).is_not_equal(true)) \
|
||||||
|
## .has_message("Expecting:\n 'true'\n not equal to\n 'true'")
|
||||||
|
## [/codeblock]
|
||||||
|
func assert_failure(assertion :Callable) -> GdUnitFailureAssert:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd").new().execute(assertion)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to test for failing assertions.[br]
|
||||||
|
## This assert is only designed for internal use to verify failing asserts working as expected.[br]
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## await assert_failure_await(func(): assert_bool(true).is_not_equal(true)) \
|
||||||
|
## .has_message("Expecting:\n 'true'\n not equal to\n 'true'")
|
||||||
|
## [/codeblock]
|
||||||
|
func assert_failure_await(assertion :Callable) -> GdUnitFailureAssert:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
return await __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd").new().execute_and_await(assertion)
|
||||||
|
|
||||||
|
|
||||||
|
## An assertion tool to verify Godot errors.[br]
|
||||||
|
## You can use to verify certain Godot errors like failing assertions, push_error, push_warn.[br]
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## # tests no error occurred during execution of the code
|
||||||
|
## await assert_error(func (): return 0 )\
|
||||||
|
## .is_success()
|
||||||
|
##
|
||||||
|
## # tests a push_error('test error') occured during execution of the code
|
||||||
|
## await assert_error(func (): push_error('test error') )\
|
||||||
|
## .is_push_error('test error')
|
||||||
|
## [/codeblock]
|
||||||
|
func assert_error(current :Callable) -> GdUnitGodotErrorAssert:
|
||||||
|
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd").new(current)
|
||||||
|
|
||||||
|
|
||||||
|
## Explicitly fails the current test indicating that the feature is not yet implemented.[br]
|
||||||
|
## This function is useful during development when you want to write test cases before implementing the actual functionality.[br]
|
||||||
|
## It provides a clear indication that the test failure is expected because the feature is still under development.[br]
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## # Test for a feature that will be implemented later
|
||||||
|
## func test_advanced_ai_behavior():
|
||||||
|
## assert_not_yet_implemented()
|
||||||
|
##
|
||||||
|
## [/codeblock]
|
||||||
|
func assert_not_yet_implemented() -> void:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
__gdunit_assert().new(null).do_fail()
|
||||||
|
|
||||||
|
|
||||||
|
## Explicitly fails the current test with a custom error message.[br]
|
||||||
|
## This function reports an error but does not terminate test execution automatically.[br]
|
||||||
|
## You must use 'return' after calling fail() to stop the test since GDScript has no exception support.[br]
|
||||||
|
## Useful for complex conditional testing scenarios where standard assertions are insufficient.[br]
|
||||||
|
## Usage:
|
||||||
|
## [codeblock]
|
||||||
|
## # Fail test when conditions are not met
|
||||||
|
## if !custom_check(player):
|
||||||
|
## fail("Player should be alive but has %d health" % player.health)
|
||||||
|
## return
|
||||||
|
##
|
||||||
|
## # Continue with test if conditions pass
|
||||||
|
## assert_that(player.health).is_greater(0)
|
||||||
|
## [/codeblock]
|
||||||
|
func fail(message: String) -> void:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
__gdunit_assert().new(null).report_error(message)
|
||||||
|
|
||||||
|
|
||||||
|
# --- internal stuff do not override!!!
|
||||||
|
func ResourcePath() -> String:
|
||||||
|
return get_script().resource_path
|
||||||
1
addons/gdUnit4/src/GdUnitTestSuite.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitTestSuite.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bvnw1dvfilyp3
|
||||||
28
addons/gdUnit4/src/GdUnitTuple.gd
Normal file
28
addons/gdUnit4/src/GdUnitTuple.gd
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
## A tuple implementation to hold two or many values
|
||||||
|
class_name GdUnitTuple
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
const NO_ARG :Variant = GdUnitConstants.NO_ARG
|
||||||
|
|
||||||
|
var __values :Array = Array()
|
||||||
|
|
||||||
|
|
||||||
|
func _init(arg0:Variant,
|
||||||
|
arg1 :Variant=NO_ARG,
|
||||||
|
arg2 :Variant=NO_ARG,
|
||||||
|
arg3 :Variant=NO_ARG,
|
||||||
|
arg4 :Variant=NO_ARG,
|
||||||
|
arg5 :Variant=NO_ARG,
|
||||||
|
arg6 :Variant=NO_ARG,
|
||||||
|
arg7 :Variant=NO_ARG,
|
||||||
|
arg8 :Variant=NO_ARG,
|
||||||
|
arg9 :Variant=NO_ARG) -> void:
|
||||||
|
__values = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG)
|
||||||
|
|
||||||
|
|
||||||
|
func values() -> Array:
|
||||||
|
return __values
|
||||||
|
|
||||||
|
|
||||||
|
func _to_string() -> String:
|
||||||
|
return "tuple(%s)" % str(__values)
|
||||||
1
addons/gdUnit4/src/GdUnitTuple.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitTuple.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://3biuc1g6112i
|
||||||
9
addons/gdUnit4/src/GdUnitValueExtractor.gd
Normal file
9
addons/gdUnit4/src/GdUnitValueExtractor.gd
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
## This is the base interface for value extraction
|
||||||
|
class_name GdUnitValueExtractor
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
|
||||||
|
## Extracts a value by given implementation
|
||||||
|
func extract_value(value :Variant) -> Variant:
|
||||||
|
push_error("Uninplemented func 'extract_value'")
|
||||||
|
return value
|
||||||
1
addons/gdUnit4/src/GdUnitValueExtractor.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitValueExtractor.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bldih0qi4d5k4
|
||||||
55
addons/gdUnit4/src/GdUnitVectorAssert.gd
Normal file
55
addons/gdUnit4/src/GdUnitVectorAssert.gd
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
## An Assertion Tool to verify Vector values
|
||||||
|
@abstract class_name GdUnitVectorAssert
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is null.
|
||||||
|
@abstract func is_null() -> GdUnitVectorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not null.
|
||||||
|
@abstract func is_not_null() -> GdUnitVectorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is equal to the given one.
|
||||||
|
@abstract func is_equal(expected: Variant) -> GdUnitVectorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not equal to expected one.
|
||||||
|
@abstract func is_not_equal(expected: Variant) -> GdUnitVectorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current and expected value are approximately equal.
|
||||||
|
@abstract func is_equal_approx(expected: Variant, approx: Variant) -> GdUnitVectorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Overrides the default failure message by given custom message.
|
||||||
|
@abstract func override_failure_message(message: String) -> GdUnitVectorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Appends a custom message to the failure message.
|
||||||
|
@abstract func append_failure_message(message: String) -> GdUnitVectorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is less than the given one.
|
||||||
|
@abstract func is_less(expected: Variant) -> GdUnitVectorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is less than or equal the given one.
|
||||||
|
@abstract func is_less_equal(expected: Variant) -> GdUnitVectorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is greater than the given one.
|
||||||
|
@abstract func is_greater(expected: Variant) -> GdUnitVectorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is greater than or equal the given one.
|
||||||
|
@abstract func is_greater_equal(expected: Variant) -> GdUnitVectorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is between the given boundaries (inclusive).
|
||||||
|
@abstract func is_between(from: Variant, to: Variant) -> GdUnitVectorAssert
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies that the current value is not between the given boundaries (inclusive).
|
||||||
|
@abstract func is_not_between(from: Variant, to: Variant) -> GdUnitVectorAssert
|
||||||
1
addons/gdUnit4/src/GdUnitVectorAssert.gd.uid
Normal file
1
addons/gdUnit4/src/GdUnitVectorAssert.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bqqs7cg70gaw2
|
||||||
25
addons/gdUnit4/src/asserts/CallBackValueProvider.gd
Normal file
25
addons/gdUnit4/src/asserts/CallBackValueProvider.gd
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# a value provider unsing a callback to get `next` value from a certain function
|
||||||
|
class_name CallBackValueProvider
|
||||||
|
extends ValueProvider
|
||||||
|
|
||||||
|
var _cb :Callable
|
||||||
|
var _args :Array
|
||||||
|
|
||||||
|
|
||||||
|
func _init(instance :Object, func_name :String, args :Array = Array(), force_error := true) -> void:
|
||||||
|
_cb = Callable(instance, func_name);
|
||||||
|
_args = args
|
||||||
|
if force_error and not _cb.is_valid():
|
||||||
|
push_error("Can't find function '%s' checked instance %s" % [func_name, instance])
|
||||||
|
|
||||||
|
|
||||||
|
func get_value() -> Variant:
|
||||||
|
if not _cb.is_valid():
|
||||||
|
return null
|
||||||
|
if _args.is_empty():
|
||||||
|
return await _cb.call()
|
||||||
|
return await _cb.callv(_args)
|
||||||
|
|
||||||
|
|
||||||
|
func dispose() -> void:
|
||||||
|
_cb = Callable()
|
||||||
1
addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid
Normal file
1
addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cxwhijm17t4gi
|
||||||
13
addons/gdUnit4/src/asserts/DefaultValueProvider.gd
Normal file
13
addons/gdUnit4/src/asserts/DefaultValueProvider.gd
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# default value provider, simple returns the initial value
|
||||||
|
class_name DefaultValueProvider
|
||||||
|
extends ValueProvider
|
||||||
|
|
||||||
|
var _value: Variant
|
||||||
|
|
||||||
|
|
||||||
|
func _init(value: Variant) -> void:
|
||||||
|
_value = value
|
||||||
|
|
||||||
|
|
||||||
|
func get_value() -> Variant:
|
||||||
|
return _value
|
||||||
1
addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid
Normal file
1
addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://c8rypp0tibdrq
|
||||||
692
addons/gdUnit4/src/asserts/GdAssertMessages.gd
Normal file
692
addons/gdUnit4/src/asserts/GdAssertMessages.gd
Normal file
@@ -0,0 +1,692 @@
|
|||||||
|
class_name GdAssertMessages
|
||||||
|
extends Resource
|
||||||
|
|
||||||
|
const WARN_COLOR = "#EFF883"
|
||||||
|
const ERROR_COLOR = "#CD5C5C"
|
||||||
|
const VALUE_COLOR = "#1E90FF"
|
||||||
|
const SUB_COLOR := Color(1, 0, 0, .3)
|
||||||
|
const ADD_COLOR := Color(0, 1, 0, .3)
|
||||||
|
|
||||||
|
|
||||||
|
# Dictionary of control characters and their readable representations
|
||||||
|
const CONTROL_CHARS = {
|
||||||
|
"\n": "<LF>", # Line Feed
|
||||||
|
"\r": "<CR>", # Carriage Return
|
||||||
|
"\t": "<TAB>", # Tab
|
||||||
|
"\b": "<BS>", # Backspace
|
||||||
|
"\f": "<FF>", # Form Feed
|
||||||
|
"\v": "<VT>", # Vertical Tab
|
||||||
|
"\a": "<BEL>", # Bell
|
||||||
|
"": "<ESC>" # Escape
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static func format_dict(value :Variant) -> String:
|
||||||
|
if not value is Dictionary:
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
var dict_value: Dictionary = value
|
||||||
|
if dict_value.is_empty():
|
||||||
|
return "{ }"
|
||||||
|
var as_rows := var_to_str(value).split("\n")
|
||||||
|
for index in range( 1, as_rows.size()-1):
|
||||||
|
as_rows[index] = " " + as_rows[index]
|
||||||
|
as_rows[-1] = " " + as_rows[-1]
|
||||||
|
return "\n".join(as_rows)
|
||||||
|
|
||||||
|
|
||||||
|
# improved version of InputEvent as text
|
||||||
|
static func input_event_as_text(event :InputEvent) -> String:
|
||||||
|
var text := ""
|
||||||
|
if event is InputEventKey:
|
||||||
|
var key_event := event as InputEventKey
|
||||||
|
text += "InputEventKey : key='%s', pressed=%s, keycode=%d, physical_keycode=%s" % [
|
||||||
|
event.as_text(), key_event.pressed, key_event.keycode, key_event.physical_keycode]
|
||||||
|
else:
|
||||||
|
text += event.as_text()
|
||||||
|
if event is InputEventMouse:
|
||||||
|
var mouse_event := event as InputEventMouse
|
||||||
|
text += ", global_position %s" % mouse_event.global_position
|
||||||
|
if event is InputEventWithModifiers:
|
||||||
|
var mouse_event := event as InputEventWithModifiers
|
||||||
|
text += ", shift=%s, alt=%s, control=%s, meta=%s, command=%s" % [
|
||||||
|
mouse_event.shift_pressed,
|
||||||
|
mouse_event.alt_pressed,
|
||||||
|
mouse_event.ctrl_pressed,
|
||||||
|
mouse_event.meta_pressed,
|
||||||
|
mouse_event.command_or_control_autoremap]
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
static func _colored_string_div(characters: String) -> String:
|
||||||
|
return colored_array_div(characters.to_utf32_buffer().to_int32_array())
|
||||||
|
|
||||||
|
|
||||||
|
static func colored_array_div(characters: PackedInt32Array) -> String:
|
||||||
|
if characters.is_empty():
|
||||||
|
return "<empty>"
|
||||||
|
var result := PackedInt32Array()
|
||||||
|
var index := 0
|
||||||
|
var missing_chars := PackedInt32Array()
|
||||||
|
var additional_chars := PackedInt32Array()
|
||||||
|
|
||||||
|
while index < characters.size():
|
||||||
|
var character := characters[index]
|
||||||
|
match character:
|
||||||
|
GdDiffTool.DIV_ADD:
|
||||||
|
index += 1
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
additional_chars.append(characters[index])
|
||||||
|
GdDiffTool.DIV_SUB:
|
||||||
|
index += 1
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
missing_chars.append(characters[index])
|
||||||
|
_:
|
||||||
|
if not missing_chars.is_empty():
|
||||||
|
result.append_array(format_chars(missing_chars, SUB_COLOR))
|
||||||
|
missing_chars = PackedInt32Array()
|
||||||
|
if not additional_chars.is_empty():
|
||||||
|
result.append_array(format_chars(additional_chars, ADD_COLOR))
|
||||||
|
additional_chars = PackedInt32Array()
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
result.append(character)
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
result.append_array(format_chars(missing_chars, SUB_COLOR))
|
||||||
|
result.append_array(format_chars(additional_chars, ADD_COLOR))
|
||||||
|
return result.to_byte_array().get_string_from_utf32()
|
||||||
|
|
||||||
|
|
||||||
|
static func _typed_value(value :Variant) -> String:
|
||||||
|
return GdDefaultValueDecoder.decode(value)
|
||||||
|
|
||||||
|
|
||||||
|
static func _warning(error :String) -> String:
|
||||||
|
return "[color=%s]%s[/color]" % [WARN_COLOR, error]
|
||||||
|
|
||||||
|
|
||||||
|
static func _error(error :String) -> String:
|
||||||
|
return "[color=%s]%s[/color]" % [ERROR_COLOR, error]
|
||||||
|
|
||||||
|
|
||||||
|
static func _nerror(number :Variant) -> String:
|
||||||
|
match typeof(number):
|
||||||
|
TYPE_INT:
|
||||||
|
return "[color=%s]%d[/color]" % [ERROR_COLOR, number]
|
||||||
|
TYPE_FLOAT:
|
||||||
|
return "[color=%s]%f[/color]" % [ERROR_COLOR, number]
|
||||||
|
_:
|
||||||
|
return "[color=%s]%s[/color]" % [ERROR_COLOR, str(number)]
|
||||||
|
|
||||||
|
|
||||||
|
static func _colored_value(value :Variant) -> String:
|
||||||
|
match typeof(value):
|
||||||
|
TYPE_STRING, TYPE_STRING_NAME:
|
||||||
|
return "'[color=%s]%s[/color]'" % [VALUE_COLOR, _colored_string_div(str(value))]
|
||||||
|
TYPE_INT:
|
||||||
|
return "'[color=%s]%d[/color]'" % [VALUE_COLOR, value]
|
||||||
|
TYPE_FLOAT:
|
||||||
|
return "'[color=%s]%s[/color]'" % [VALUE_COLOR, _typed_value(value)]
|
||||||
|
TYPE_COLOR:
|
||||||
|
return "'[color=%s]%s[/color]'" % [VALUE_COLOR, _typed_value(value)]
|
||||||
|
TYPE_OBJECT:
|
||||||
|
if value == null:
|
||||||
|
return "'[color=%s]<null>[/color]'" % [VALUE_COLOR]
|
||||||
|
if value is InputEvent:
|
||||||
|
var ie: InputEvent = value
|
||||||
|
return "[color=%s]<%s>[/color]" % [VALUE_COLOR, input_event_as_text(ie)]
|
||||||
|
var obj_value: Object = value
|
||||||
|
if obj_value.has_method("_to_string"):
|
||||||
|
return "[color=%s]<%s>[/color]" % [VALUE_COLOR, str(value)]
|
||||||
|
return "[color=%s]<%s>[/color]" % [VALUE_COLOR, obj_value.get_class()]
|
||||||
|
TYPE_DICTIONARY:
|
||||||
|
return "'[color=%s]%s[/color]'" % [VALUE_COLOR, format_dict(value)]
|
||||||
|
_:
|
||||||
|
if GdArrayTools.is_array_type(value):
|
||||||
|
return "'[color=%s]%s[/color]'" % [VALUE_COLOR, _typed_value(value)]
|
||||||
|
return "'[color=%s]%s[/color]'" % [VALUE_COLOR, value]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static func _index_report_as_table(index_reports :Array) -> String:
|
||||||
|
var table := "[table=3]$cells[/table]"
|
||||||
|
var header := "[cell][right][b]$text[/b][/right]\t[/cell]"
|
||||||
|
var cell := "[cell][right]$text[/right]\t[/cell]"
|
||||||
|
var cells := header.replace("$text", "Index") + header.replace("$text", "Current") + header.replace("$text", "Expected")
|
||||||
|
for report :Variant in index_reports:
|
||||||
|
var index :String = str(report["index"])
|
||||||
|
var current :String = str(report["current"])
|
||||||
|
var expected :String = str(report["expected"])
|
||||||
|
cells += cell.replace("$text", index) + cell.replace("$text", current) + cell.replace("$text", expected)
|
||||||
|
return table.replace("$cells", cells)
|
||||||
|
|
||||||
|
|
||||||
|
static func orphan_detected_on_suite_setup(count :int) -> String:
|
||||||
|
return "%s\n Detected <%d> orphan nodes during test suite setup stage! [b]Check before() and after()![/b]" % [
|
||||||
|
_warning("WARNING:"), count]
|
||||||
|
|
||||||
|
|
||||||
|
static func orphan_detected_on_test_setup(count :int) -> String:
|
||||||
|
return "%s\n Detected <%d> orphan nodes during test setup! [b]Check before_test() and after_test()![/b]" % [
|
||||||
|
_warning("WARNING:"), count]
|
||||||
|
|
||||||
|
|
||||||
|
static func orphan_detected_on_test(count :int) -> String:
|
||||||
|
return "%s\n Detected <%d> orphan nodes during test execution!" % [
|
||||||
|
_warning("WARNING:"), count]
|
||||||
|
|
||||||
|
|
||||||
|
static func fuzzer_interuped(iterations: int, error: String) -> String:
|
||||||
|
return "%s %s %s\n %s" % [
|
||||||
|
_error("Found an error after"),
|
||||||
|
_colored_value(iterations + 1),
|
||||||
|
_error("test iterations"),
|
||||||
|
error]
|
||||||
|
|
||||||
|
|
||||||
|
static func test_timeout(timeout :int) -> String:
|
||||||
|
return "%s\n %s" % [_error("Timeout !"), _colored_value("Test timed out after %s" % LocalTime.elapsed(timeout))]
|
||||||
|
|
||||||
|
|
||||||
|
# gdlint:disable = mixed-tabs-and-spaces
|
||||||
|
static func test_suite_skipped(hint :String, skip_count :int) -> String:
|
||||||
|
return """
|
||||||
|
%s
|
||||||
|
Skipped %s tests
|
||||||
|
Reason: %s
|
||||||
|
""".dedent().trim_prefix("\n")\
|
||||||
|
% [_error("The Entire test-suite is skipped!"), _colored_value(skip_count), _colored_value(hint)]
|
||||||
|
|
||||||
|
|
||||||
|
static func test_skipped(hint :String) -> String:
|
||||||
|
return """
|
||||||
|
%s
|
||||||
|
Reason: %s
|
||||||
|
""".dedent().trim_prefix("\n")\
|
||||||
|
% [_error("This test is skipped!"), _colored_value(hint)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_not_implemented() -> String:
|
||||||
|
return _error("Test not implemented!")
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_null(current :Variant) -> String:
|
||||||
|
return "%s %s but was %s" % [_error("Expecting:"), _colored_value(null), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_not_null() -> String:
|
||||||
|
return "%s %s" % [_error("Expecting: not to be"), _colored_value(null)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_equal(current :Variant, expected :Variant, index_reports :Array = []) -> String:
|
||||||
|
var report := """
|
||||||
|
%s
|
||||||
|
%s
|
||||||
|
but was
|
||||||
|
%s""".dedent().trim_prefix("\n") % [_error("Expecting:"), _colored_value(expected), _colored_value(current)]
|
||||||
|
if not index_reports.is_empty():
|
||||||
|
report += "\n\n%s\n%s" % [_error("Differences found:"), _index_report_as_table(index_reports)]
|
||||||
|
return report
|
||||||
|
|
||||||
|
|
||||||
|
static func error_not_equal(current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s\n not equal to\n %s" % [_error("Expecting:"), _colored_value(expected), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_not_equal_case_insensetiv(current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s\n not equal to (case insensitiv)\n %s" % [
|
||||||
|
_error("Expecting:"), _colored_value(expected), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_empty(current :Variant) -> String:
|
||||||
|
return "%s\n must be empty but was\n %s" % [_error("Expecting:"), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_not_empty() -> String:
|
||||||
|
return "%s\n must not be empty" % [_error("Expecting:")]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_same(current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s\n to refer to the same object\n %s" % [_error("Expecting:"), _colored_value(expected), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
@warning_ignore("unused_parameter")
|
||||||
|
static func error_not_same(_current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s" % [_error("Expecting not same:"), _colored_value(expected)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_not_same_error(current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s\n but was\n %s" % [_error("Expecting error message:"), _colored_value(expected), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_instanceof(current: GdUnitResult, expected :GdUnitResult) -> String:
|
||||||
|
return "%s\n %s\n But it was %s" % [_error("Expected instance of:"),\
|
||||||
|
_colored_value(expected.or_else(null)), _colored_value(current.or_else(null))]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Boolean Assert specific messages -----------------------------------------------------
|
||||||
|
static func error_is_true(current :Variant) -> String:
|
||||||
|
return "%s %s but is %s" % [_error("Expecting:"), _colored_value(true), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_false(current :Variant) -> String:
|
||||||
|
return "%s %s but is %s" % [_error("Expecting:"), _colored_value(false), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
# - Integer/Float Assert specific messages -----------------------------------------------------
|
||||||
|
|
||||||
|
static func error_is_even(current :Variant) -> String:
|
||||||
|
return "%s\n %s must be even" % [_error("Expecting:"), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_odd(current :Variant) -> String:
|
||||||
|
return "%s\n %s must be odd" % [_error("Expecting:"), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_negative(current :Variant) -> String:
|
||||||
|
return "%s\n %s be negative" % [_error("Expecting:"), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_not_negative(current :Variant) -> String:
|
||||||
|
return "%s\n %s be not negative" % [_error("Expecting:"), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_zero(current :Variant) -> String:
|
||||||
|
return "%s\n equal to 0 but is %s" % [_error("Expecting:"), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_not_zero() -> String:
|
||||||
|
return "%s\n not equal to 0" % [_error("Expecting:")]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_wrong_type(current_type :Variant.Type, expected_type :Variant.Type) -> String:
|
||||||
|
return "%s\n Expecting type %s but is %s" % [
|
||||||
|
_error("Unexpected type comparison:"),
|
||||||
|
_colored_value(GdObjects.type_as_string(current_type)),
|
||||||
|
_colored_value(GdObjects.type_as_string(expected_type))]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_value(operation :int, current :Variant, expected :Variant, expected2 :Variant = null) -> String:
|
||||||
|
match operation:
|
||||||
|
Comparator.EQUAL:
|
||||||
|
return "%s\n %s but was '%s'" % [_error("Expecting:"), _colored_value(expected), _nerror(current)]
|
||||||
|
Comparator.LESS_THAN:
|
||||||
|
return "%s\n %s but was '%s'" % [_error("Expecting to be less than:"), _colored_value(expected), _nerror(current)]
|
||||||
|
Comparator.LESS_EQUAL:
|
||||||
|
return "%s\n %s but was '%s'" % [_error("Expecting to be less than or equal:"), _colored_value(expected), _nerror(current)]
|
||||||
|
Comparator.GREATER_THAN:
|
||||||
|
return "%s\n %s but was '%s'" % [_error("Expecting to be greater than:"), _colored_value(expected), _nerror(current)]
|
||||||
|
Comparator.GREATER_EQUAL:
|
||||||
|
return "%s\n %s but was '%s'" % [_error("Expecting to be greater than or equal:"), _colored_value(expected), _nerror(current)]
|
||||||
|
Comparator.BETWEEN_EQUAL:
|
||||||
|
return "%s\n %s\n in range between\n %s <> %s" % [
|
||||||
|
_error("Expecting:"), _colored_value(current), _colored_value(expected), _colored_value(expected2)]
|
||||||
|
Comparator.NOT_BETWEEN_EQUAL:
|
||||||
|
return "%s\n %s\n not in range between\n %s <> %s" % [
|
||||||
|
_error("Expecting:"), _colored_value(current), _colored_value(expected), _colored_value(expected2)]
|
||||||
|
return "TODO create expected message"
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_in(current :Variant, expected :Array) -> String:
|
||||||
|
return "%s\n %s\n is in\n %s" % [_error("Expecting:"), _colored_value(current), _colored_value(str(expected))]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_is_not_in(current :Variant, expected :Array) -> String:
|
||||||
|
return "%s\n %s\n is not in\n %s" % [_error("Expecting:"), _colored_value(current), _colored_value(str(expected))]
|
||||||
|
|
||||||
|
|
||||||
|
# - StringAssert ---------------------------------------------------------------------------------
|
||||||
|
static func error_equal_ignoring_case(current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s\n but was\n %s (ignoring case)" % [_error("Expecting:"), _colored_value(expected), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_contains(current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s\n do contains\n %s" % [_error("Expecting:"), _colored_value(current), _colored_value(expected)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_not_contains(current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s\n not do contain\n %s" % [_error("Expecting:"), _colored_value(current), _colored_value(expected)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_contains_ignoring_case(current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s\n contains\n %s\n (ignoring case)" % [_error("Expecting:"), _colored_value(current), _colored_value(expected)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_not_contains_ignoring_case(current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s\n not do contains\n %s\n (ignoring case)" % [_error("Expecting:"), _colored_value(current), _colored_value(expected)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_starts_with(current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s\n to start with\n %s" % [_error("Expecting:"), _colored_value(current), _colored_value(expected)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_ends_with(current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s\n to end with\n %s" % [_error("Expecting:"), _colored_value(current), _colored_value(expected)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_has_length(current :Variant, expected: int, compare_operator :int) -> String:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
var current_length :Variant = current.length() if current != null else null
|
||||||
|
match compare_operator:
|
||||||
|
Comparator.EQUAL:
|
||||||
|
return "%s\n %s but was '%s' in\n %s" % [
|
||||||
|
_error("Expecting size:"), _colored_value(expected), _nerror(current_length), _colored_value(current)]
|
||||||
|
Comparator.LESS_THAN:
|
||||||
|
return "%s\n %s but was '%s' in\n %s" % [
|
||||||
|
_error("Expecting size to be less than:"), _colored_value(expected), _nerror(current_length), _colored_value(current)]
|
||||||
|
Comparator.LESS_EQUAL:
|
||||||
|
return "%s\n %s but was '%s' in\n %s" % [
|
||||||
|
_error("Expecting size to be less than or equal:"), _colored_value(expected),
|
||||||
|
_nerror(current_length), _colored_value(current)]
|
||||||
|
Comparator.GREATER_THAN:
|
||||||
|
return "%s\n %s but was '%s' in\n %s" % [
|
||||||
|
_error("Expecting size to be greater than:"), _colored_value(expected),
|
||||||
|
_nerror(current_length), _colored_value(current)]
|
||||||
|
Comparator.GREATER_EQUAL:
|
||||||
|
return "%s\n %s but was '%s' in\n %s" % [
|
||||||
|
_error("Expecting size to be greater than or equal:"), _colored_value(expected),
|
||||||
|
_nerror(current_length), _colored_value(current)]
|
||||||
|
return "TODO create expected message"
|
||||||
|
|
||||||
|
|
||||||
|
# - ArrayAssert specific messgaes ---------------------------------------------------
|
||||||
|
|
||||||
|
static func error_arr_contains(current: Variant, expected: Variant, not_expect: Variant, not_found: Variant, by_reference: bool) -> String:
|
||||||
|
var failure_message := "Expecting contains SAME elements:" if by_reference else "Expecting contains elements:"
|
||||||
|
var error := "%s\n %s\n do contains (in any order)\n %s" % [
|
||||||
|
_error(failure_message), _colored_value(current), _colored_value(expected)]
|
||||||
|
if not is_empty(not_expect):
|
||||||
|
error += "\nbut some elements where not expected:\n %s" % _colored_value(not_expect)
|
||||||
|
if not is_empty(not_found):
|
||||||
|
var prefix := "but" if is_empty(not_expect) else "and"
|
||||||
|
error += "\n%s could not find elements:\n %s" % [prefix, _colored_value(not_found)]
|
||||||
|
return error
|
||||||
|
|
||||||
|
|
||||||
|
static func error_arr_contains_exactly(
|
||||||
|
current: Variant,
|
||||||
|
expected: Variant,
|
||||||
|
not_expect: Variant,
|
||||||
|
not_found: Variant, compare_mode: GdObjects.COMPARE_MODE) -> String:
|
||||||
|
var failure_message := (
|
||||||
|
"Expecting contains exactly elements:" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST
|
||||||
|
else "Expecting contains SAME exactly elements:"
|
||||||
|
)
|
||||||
|
if is_empty(not_expect) and is_empty(not_found):
|
||||||
|
var arr_current: Array = current
|
||||||
|
var arr_expected: Array = expected
|
||||||
|
var diff := _find_first_diff(arr_current, arr_expected)
|
||||||
|
return "%s\n %s\n do contains (in same order)\n %s\n but has different order %s" % [
|
||||||
|
_error(failure_message), _colored_value(current), _colored_value(expected), diff]
|
||||||
|
|
||||||
|
var error := "%s\n %s\n do contains (in same order)\n %s" % [
|
||||||
|
_error(failure_message), _colored_value(current), _colored_value(expected)]
|
||||||
|
if not is_empty(not_expect):
|
||||||
|
error += "\nbut some elements where not expected:\n %s" % _colored_value(not_expect)
|
||||||
|
if not is_empty(not_found):
|
||||||
|
var prefix := "but" if is_empty(not_expect) else "and"
|
||||||
|
error += "\n%s could not find elements:\n %s" % [prefix, _colored_value(not_found)]
|
||||||
|
return error
|
||||||
|
|
||||||
|
|
||||||
|
static func error_arr_contains_exactly_in_any_order(
|
||||||
|
current: Variant,
|
||||||
|
expected: Variant,
|
||||||
|
not_expect: Variant,
|
||||||
|
not_found: Variant,
|
||||||
|
compare_mode: GdObjects.COMPARE_MODE) -> String:
|
||||||
|
|
||||||
|
var failure_message := (
|
||||||
|
"Expecting contains exactly elements:" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST
|
||||||
|
else "Expecting contains SAME exactly elements:"
|
||||||
|
)
|
||||||
|
var error := "%s\n %s\n do contains exactly (in any order)\n %s" % [
|
||||||
|
_error(failure_message), _colored_value(current), _colored_value(expected)]
|
||||||
|
if not is_empty(not_expect):
|
||||||
|
error += "\nbut some elements where not expected:\n %s" % _colored_value(not_expect)
|
||||||
|
if not is_empty(not_found):
|
||||||
|
var prefix := "but" if is_empty(not_expect) else "and"
|
||||||
|
error += "\n%s could not find elements:\n %s" % [prefix, _colored_value(not_found)]
|
||||||
|
return error
|
||||||
|
|
||||||
|
|
||||||
|
static func error_arr_not_contains(current: Variant, expected: Variant, found: Variant, compare_mode: GdObjects.COMPARE_MODE) -> String:
|
||||||
|
var failure_message := "Expecting:" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST else "Expecting SAME:"
|
||||||
|
var error := "%s\n %s\n do not contains\n %s" % [
|
||||||
|
_error(failure_message), _colored_value(current), _colored_value(expected)]
|
||||||
|
if not is_empty(found):
|
||||||
|
error += "\n but found elements:\n %s" % _colored_value(found)
|
||||||
|
return error
|
||||||
|
|
||||||
|
|
||||||
|
# - DictionaryAssert specific messages ----------------------------------------------
|
||||||
|
static func error_contains_keys(current :Array, expected :Array, keys_not_found :Array, compare_mode :GdObjects.COMPARE_MODE) -> String:
|
||||||
|
var failure := (
|
||||||
|
"Expecting contains keys:" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST
|
||||||
|
else "Expecting contains SAME keys:"
|
||||||
|
)
|
||||||
|
return "%s\n %s\n to contains:\n %s\n but can't find key's:\n %s" % [
|
||||||
|
_error(failure), _colored_value(current), _colored_value(expected), _colored_value(keys_not_found)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_not_contains_keys(current :Array, expected :Array, keys_not_found :Array, compare_mode :GdObjects.COMPARE_MODE) -> String:
|
||||||
|
var failure := (
|
||||||
|
"Expecting NOT contains keys:" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST
|
||||||
|
else "Expecting NOT contains SAME keys"
|
||||||
|
)
|
||||||
|
return "%s\n %s\n do not contains:\n %s\n but contains key's:\n %s" % [
|
||||||
|
_error(failure), _colored_value(current), _colored_value(expected), _colored_value(keys_not_found)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_contains_key_value(key :Variant, value :Variant, current_value :Variant, compare_mode :GdObjects.COMPARE_MODE) -> String:
|
||||||
|
var failure := (
|
||||||
|
"Expecting contains key and value:" if compare_mode == GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST
|
||||||
|
else "Expecting contains SAME key and value:"
|
||||||
|
)
|
||||||
|
return "%s\n %s : %s\n but contains\n %s : %s" % [
|
||||||
|
_error(failure), _colored_value(key), _colored_value(value), _colored_value(key), _colored_value(current_value)]
|
||||||
|
|
||||||
|
|
||||||
|
# - ResultAssert specific errors ----------------------------------------------------
|
||||||
|
static func error_result_is_empty(current :GdUnitResult) -> String:
|
||||||
|
return _result_error_message(current, GdUnitResult.EMPTY)
|
||||||
|
|
||||||
|
|
||||||
|
static func error_result_is_success(current :GdUnitResult) -> String:
|
||||||
|
return _result_error_message(current, GdUnitResult.SUCCESS)
|
||||||
|
|
||||||
|
|
||||||
|
static func error_result_is_warning(current :GdUnitResult) -> String:
|
||||||
|
return _result_error_message(current, GdUnitResult.WARN)
|
||||||
|
|
||||||
|
|
||||||
|
static func error_result_is_error(current :GdUnitResult) -> String:
|
||||||
|
return _result_error_message(current, GdUnitResult.ERROR)
|
||||||
|
|
||||||
|
|
||||||
|
static func error_result_has_message(current :String, expected :String) -> String:
|
||||||
|
return "%s\n %s\n but was\n %s." % [_error("Expecting:"), _colored_value(expected), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_result_has_message_on_success(expected :String) -> String:
|
||||||
|
return "%s\n %s\n but the GdUnitResult is a success." % [_error("Expecting:"), _colored_value(expected)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_result_is_value(current :Variant, expected :Variant) -> String:
|
||||||
|
return "%s\n %s\n but was\n %s." % [_error("Expecting to contain same value:"), _colored_value(expected), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func _result_error_message(current :GdUnitResult, expected_type :int) -> String:
|
||||||
|
if current == null:
|
||||||
|
return _error("Expecting the result must be a %s but was <null>." % result_type(expected_type))
|
||||||
|
if current.is_success():
|
||||||
|
return _error("Expecting the result must be a %s but was SUCCESS." % result_type(expected_type))
|
||||||
|
var error := "Expecting the result must be a %s but was %s:" % [result_type(expected_type), result_type(current._state)]
|
||||||
|
return "%s\n %s" % [_error(error), _colored_value(result_message(current))]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_interrupted(func_name :String, expected :Variant, elapsed :String) -> String:
|
||||||
|
func_name = humanized(func_name)
|
||||||
|
if expected == null:
|
||||||
|
return "%s %s but timed out after %s" % [_error("Expected:"), func_name, elapsed]
|
||||||
|
return "%s %s %s but timed out after %s" % [_error("Expected:"), func_name, _colored_value(expected), elapsed]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_wait_signal(signal_name :String, args :Array, elapsed :String) -> String:
|
||||||
|
if args.is_empty():
|
||||||
|
return "%s %s but timed out after %s" % [
|
||||||
|
_error("Expecting emit signal:"), _colored_value(signal_name + "()"), elapsed]
|
||||||
|
return "%s %s but timed out after %s" % [
|
||||||
|
_error("Expecting emit signal:"), _colored_value(signal_name + "(" + str(args) + ")"), elapsed]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_signal_emitted(signal_name :String, args :Array, elapsed :String) -> String:
|
||||||
|
if args.is_empty():
|
||||||
|
return "%s %s but is emitted after %s" % [
|
||||||
|
_error("Expecting do not emit signal:"), _colored_value(signal_name + "()"), elapsed]
|
||||||
|
return "%s %s but is emitted after %s" % [
|
||||||
|
_error("Expecting do not emit signal:"), _colored_value(signal_name + "(" + str(args) + ")"), elapsed]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_await_signal_on_invalid_instance(source :Variant, signal_name :String, args :Array) -> String:
|
||||||
|
return "%s\n await_signal_on(%s, %s, %s)" % [
|
||||||
|
_error("Invalid source! Can't await on signal:"), _colored_value(source), signal_name, args]
|
||||||
|
|
||||||
|
|
||||||
|
static func result_type(type :int) -> String:
|
||||||
|
match type:
|
||||||
|
GdUnitResult.SUCCESS: return "SUCCESS"
|
||||||
|
GdUnitResult.WARN: return "WARNING"
|
||||||
|
GdUnitResult.ERROR: return "ERROR"
|
||||||
|
GdUnitResult.EMPTY: return "EMPTY"
|
||||||
|
return "UNKNOWN"
|
||||||
|
|
||||||
|
|
||||||
|
static func result_message(result :GdUnitResult) -> String:
|
||||||
|
match result._state:
|
||||||
|
GdUnitResult.SUCCESS: return ""
|
||||||
|
GdUnitResult.WARN: return result.warn_message()
|
||||||
|
GdUnitResult.ERROR: return result.error_message()
|
||||||
|
GdUnitResult.EMPTY: return ""
|
||||||
|
return "UNKNOWN"
|
||||||
|
# -----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# - Spy|Mock specific errors ----------------------------------------------------
|
||||||
|
static func error_no_more_interactions(summary :Dictionary) -> String:
|
||||||
|
var interactions := PackedStringArray()
|
||||||
|
for args :Array in summary.keys():
|
||||||
|
var times :int = summary[args]
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
interactions.append(_format_arguments(args, times))
|
||||||
|
return "%s\n%s\n%s" % [_error("Expecting no more interactions!"), _error("But found interactions on:"), "\n".join(interactions)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_validate_interactions(current_interactions: Dictionary, expected_interactions: Dictionary) -> String:
|
||||||
|
var collected_interactions := PackedStringArray()
|
||||||
|
for args: Array in current_interactions.keys():
|
||||||
|
var times: int = current_interactions[args]
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
collected_interactions.append(_format_arguments(args, times))
|
||||||
|
|
||||||
|
var arguments: Array = expected_interactions.keys()[0]
|
||||||
|
var interactions: int = expected_interactions.values()[0]
|
||||||
|
var expected_interaction := _format_arguments(arguments, interactions)
|
||||||
|
return "%s\n%s\n%s\n%s" % [
|
||||||
|
_error("Expecting interaction on:"), expected_interaction, _error("But found interactions on:"), "\n".join(collected_interactions)]
|
||||||
|
|
||||||
|
|
||||||
|
static func _format_arguments(args :Array, times :int) -> String:
|
||||||
|
var fname :String = args[0]
|
||||||
|
var fargs := args.slice(1) as Array
|
||||||
|
var typed_args := _to_typed_args(fargs)
|
||||||
|
var fsignature := _colored_value("%s(%s)" % [fname, ", ".join(typed_args)])
|
||||||
|
return " %s %d time's" % [fsignature, times]
|
||||||
|
|
||||||
|
|
||||||
|
static func _to_typed_args(args :Array) -> PackedStringArray:
|
||||||
|
var typed := PackedStringArray()
|
||||||
|
for arg :Variant in args:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
typed.append(_format_arg(arg) + " :" + GdObjects.type_as_string(typeof(arg)))
|
||||||
|
return typed
|
||||||
|
|
||||||
|
|
||||||
|
static func _format_arg(arg :Variant) -> String:
|
||||||
|
if arg is InputEvent:
|
||||||
|
var ie: InputEvent = arg
|
||||||
|
return input_event_as_text(ie)
|
||||||
|
return str(arg)
|
||||||
|
|
||||||
|
|
||||||
|
static func _find_first_diff(left :Array, right :Array) -> String:
|
||||||
|
for index in left.size():
|
||||||
|
var l :Variant = left[index]
|
||||||
|
var r :Variant = "<no entry>" if index >= right.size() else right[index]
|
||||||
|
if not GdObjects.equals(l, r):
|
||||||
|
return "at position %s\n '%s' vs '%s'" % [_colored_value(index), _typed_value(l), _typed_value(r)]
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
static func error_has_size(current :Variant, expected: int) -> String:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
var current_size :Variant = null if current == null else current.size()
|
||||||
|
return "%s\n %s\n but was\n %s" % [_error("Expecting size:"), _colored_value(expected), _colored_value(current_size)]
|
||||||
|
|
||||||
|
|
||||||
|
static func error_contains_exactly(current: Array, expected: Array) -> String:
|
||||||
|
return "%s\n %s\n but was\n %s" % [_error("Expecting exactly equal:"), _colored_value(expected), _colored_value(current)]
|
||||||
|
|
||||||
|
|
||||||
|
static func format_chars(characters: PackedInt32Array, type: Color) -> PackedInt32Array:
|
||||||
|
if characters.size() == 0:# or characters[0] == 10:
|
||||||
|
return characters
|
||||||
|
|
||||||
|
# Replace each control character with its readable form
|
||||||
|
var formatted_text := characters.to_byte_array().get_string_from_utf32()
|
||||||
|
for control_char: String in CONTROL_CHARS:
|
||||||
|
var replace_text: String = CONTROL_CHARS[control_char]
|
||||||
|
formatted_text = formatted_text.replace(control_char, replace_text)
|
||||||
|
|
||||||
|
# Handle special ASCII control characters (0x00-0x1F, 0x7F)
|
||||||
|
var ascii_text := ""
|
||||||
|
for i in formatted_text.length():
|
||||||
|
var character := formatted_text[i]
|
||||||
|
var code := character.unicode_at(0)
|
||||||
|
if code < 0x20 and not CONTROL_CHARS.has(character): # Control characters not handled above
|
||||||
|
ascii_text += "<0x%02X>" % code
|
||||||
|
elif code == 0x7F: # DEL character
|
||||||
|
ascii_text += "<DEL>"
|
||||||
|
else:
|
||||||
|
ascii_text += character
|
||||||
|
|
||||||
|
var message := "[bgcolor=#%s][color=white]%s[/color][/bgcolor]" % [
|
||||||
|
type.to_html(),
|
||||||
|
ascii_text
|
||||||
|
]
|
||||||
|
|
||||||
|
var result := PackedInt32Array()
|
||||||
|
result.append_array(message.to_utf32_buffer().to_int32_array())
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
static func format_invalid(value :String) -> String:
|
||||||
|
return "[bgcolor=#%s][color=with]%s[/color][/bgcolor]" % [SUB_COLOR.to_html(), value]
|
||||||
|
|
||||||
|
|
||||||
|
static func humanized(value :String) -> String:
|
||||||
|
return value.replace("_", " ")
|
||||||
|
|
||||||
|
|
||||||
|
static func build_failure_message(failure :String, additional_failure_message: String, custom_failure_message: String) -> String:
|
||||||
|
var message := failure if custom_failure_message.is_empty() else custom_failure_message
|
||||||
|
if additional_failure_message.is_empty():
|
||||||
|
return message
|
||||||
|
return """
|
||||||
|
%s
|
||||||
|
[color=LIME_GREEN][b]Additional info:[/b][/color]
|
||||||
|
%s""".dedent().trim_prefix("\n") % [message, additional_failure_message]
|
||||||
|
|
||||||
|
|
||||||
|
static func is_empty(value: Variant) -> bool:
|
||||||
|
var arry_value: Array = value
|
||||||
|
return arry_value != null and arry_value.is_empty()
|
||||||
1
addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid
Normal file
1
addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://vy15s8xjek4s
|
||||||
54
addons/gdUnit4/src/asserts/GdAssertReports.gd
Normal file
54
addons/gdUnit4/src/asserts/GdAssertReports.gd
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
class_name GdAssertReports
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
const LAST_ERROR = "last_assert_error_message"
|
||||||
|
const LAST_ERROR_LINE = "last_assert_error_line"
|
||||||
|
|
||||||
|
|
||||||
|
static func report_success() -> void:
|
||||||
|
GdUnitSignals.instance().gdunit_set_test_failed.emit(false)
|
||||||
|
GdAssertReports.set_last_error_line_number(-1)
|
||||||
|
Engine.remove_meta(LAST_ERROR)
|
||||||
|
|
||||||
|
|
||||||
|
static func report_warning(message :String, line_number :int) -> void:
|
||||||
|
GdUnitSignals.instance().gdunit_set_test_failed.emit(false)
|
||||||
|
send_report(GdUnitReport.new().create(GdUnitReport.WARN, line_number, message))
|
||||||
|
|
||||||
|
|
||||||
|
static func report_error(message:String, line_number :int) -> void:
|
||||||
|
GdUnitSignals.instance().gdunit_set_test_failed.emit(true)
|
||||||
|
GdAssertReports.set_last_error_line_number(line_number)
|
||||||
|
Engine.set_meta(LAST_ERROR, message)
|
||||||
|
# if we expect to fail we handle as success test
|
||||||
|
if _do_expect_assert_failing():
|
||||||
|
return
|
||||||
|
send_report(GdUnitReport.new().create(GdUnitReport.FAILURE, line_number, message))
|
||||||
|
|
||||||
|
|
||||||
|
static func reset_last_error_line_number() -> void:
|
||||||
|
Engine.remove_meta(LAST_ERROR_LINE)
|
||||||
|
|
||||||
|
|
||||||
|
static func set_last_error_line_number(line_number :int) -> void:
|
||||||
|
Engine.set_meta(LAST_ERROR_LINE, line_number)
|
||||||
|
|
||||||
|
|
||||||
|
static func get_last_error_line_number() -> int:
|
||||||
|
if Engine.has_meta(LAST_ERROR_LINE):
|
||||||
|
return Engine.get_meta(LAST_ERROR_LINE)
|
||||||
|
return -1
|
||||||
|
|
||||||
|
|
||||||
|
static func _do_expect_assert_failing() -> bool:
|
||||||
|
if Engine.has_meta(GdUnitConstants.EXPECT_ASSERT_REPORT_FAILURES):
|
||||||
|
return Engine.get_meta(GdUnitConstants.EXPECT_ASSERT_REPORT_FAILURES)
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
static func current_failure() -> String:
|
||||||
|
return Engine.get_meta(LAST_ERROR)
|
||||||
|
|
||||||
|
|
||||||
|
static func send_report(report :GdUnitReport) -> void:
|
||||||
|
GdUnitThreadManager.get_current_context().get_execution_context().add_report(report)
|
||||||
1
addons/gdUnit4/src/asserts/GdAssertReports.gd.uid
Normal file
1
addons/gdUnit4/src/asserts/GdAssertReports.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://6vkjauii7gha
|
||||||
433
addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd
Normal file
433
addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
class_name GdUnitArrayAssertImpl
|
||||||
|
extends GdUnitArrayAssert
|
||||||
|
|
||||||
|
|
||||||
|
var _base: GdUnitAssertImpl
|
||||||
|
var _current_value_provider: ValueProvider
|
||||||
|
var _type_check: bool
|
||||||
|
|
||||||
|
|
||||||
|
func _init(current: Variant, type_check := true) -> void:
|
||||||
|
_type_check = type_check
|
||||||
|
_current_value_provider = DefaultValueProvider.new(current)
|
||||||
|
_base = GdUnitAssertImpl.new(current)
|
||||||
|
# save the actual assert instance on the current thread context
|
||||||
|
GdUnitThreadManager.get_current_context().set_assert(self)
|
||||||
|
if not _validate_value_type(current):
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
report_error("GdUnitArrayAssert inital error, unexpected type <%s>" % GdObjects.typeof_as_string(current))
|
||||||
|
|
||||||
|
|
||||||
|
func _notification(event: int) -> void:
|
||||||
|
if event == NOTIFICATION_PREDELETE:
|
||||||
|
if _base != null:
|
||||||
|
_base.notification(event)
|
||||||
|
_base = null
|
||||||
|
|
||||||
|
|
||||||
|
func report_success() -> GdUnitArrayAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.report_success()
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func report_error(error: String) -> GdUnitArrayAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.report_error(error)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func failure_message() -> String:
|
||||||
|
return _base.failure_message()
|
||||||
|
|
||||||
|
|
||||||
|
func override_failure_message(message: String) -> GdUnitArrayAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.override_failure_message(message)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func append_failure_message(message: String) -> GdUnitArrayAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.append_failure_message(message)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func _validate_value_type(value: Variant) -> bool:
|
||||||
|
return value == null or GdArrayTools.is_array_type(value)
|
||||||
|
|
||||||
|
|
||||||
|
func get_current_value() -> Variant:
|
||||||
|
return _current_value_provider.get_value()
|
||||||
|
|
||||||
|
|
||||||
|
func max_length(left: Variant, right: Variant) -> int:
|
||||||
|
var ls := str(left).length()
|
||||||
|
var rs := str(right).length()
|
||||||
|
return rs if ls < rs else ls
|
||||||
|
|
||||||
|
|
||||||
|
# gdlint: disable=function-name
|
||||||
|
func _toPackedStringArray(value: Variant) -> PackedStringArray:
|
||||||
|
if GdArrayTools.is_array_type(value):
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
return PackedStringArray(value as Array)
|
||||||
|
return PackedStringArray([str(value)])
|
||||||
|
|
||||||
|
|
||||||
|
func _array_equals_div(current: Variant, expected: Variant, case_sensitive: bool = false) -> Array[Array]:
|
||||||
|
var current_value := _toPackedStringArray(current)
|
||||||
|
var expected_value := _toPackedStringArray(expected)
|
||||||
|
var index_report := Array()
|
||||||
|
for index in current_value.size():
|
||||||
|
var c := current_value[index]
|
||||||
|
if index < expected_value.size():
|
||||||
|
var e := expected_value[index]
|
||||||
|
if not GdObjects.equals(c, e, case_sensitive):
|
||||||
|
var length := max_length(c, e)
|
||||||
|
current_value[index] = GdAssertMessages.format_invalid(c.lpad(length))
|
||||||
|
expected_value[index] = e.lpad(length)
|
||||||
|
index_report.push_back({"index": index, "current": c, "expected": e})
|
||||||
|
else:
|
||||||
|
current_value[index] = GdAssertMessages.format_invalid(c)
|
||||||
|
index_report.push_back({"index": index, "current": c, "expected": "<N/A>"})
|
||||||
|
|
||||||
|
for index in range(current_value.size(), expected_value.size()):
|
||||||
|
var value := expected_value[index]
|
||||||
|
expected_value[index] = GdAssertMessages.format_invalid(value)
|
||||||
|
index_report.push_back({"index": index, "current": "<N/A>", "expected": value})
|
||||||
|
return [current_value, expected_value, index_report]
|
||||||
|
|
||||||
|
|
||||||
|
func _array_div(compare_mode: GdObjects.COMPARE_MODE, left: Array[Variant], right: Array[Variant], _same_order := false) -> Array[Variant]:
|
||||||
|
var not_expect := left.duplicate(true)
|
||||||
|
var not_found := right.duplicate(true)
|
||||||
|
for index_c in left.size():
|
||||||
|
var c: Variant = left[index_c]
|
||||||
|
for index_e in right.size():
|
||||||
|
var e: Variant = right[index_e]
|
||||||
|
if GdObjects.equals(c, e, false, compare_mode):
|
||||||
|
GdArrayTools.erase_value(not_expect, e)
|
||||||
|
GdArrayTools.erase_value(not_found, c)
|
||||||
|
break
|
||||||
|
return [not_expect, not_found]
|
||||||
|
|
||||||
|
|
||||||
|
func _contains(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert:
|
||||||
|
var by_reference := compare_mode == GdObjects.COMPARE_MODE.OBJECT_REFERENCE
|
||||||
|
var current_value: Variant = get_current_value()
|
||||||
|
var expected_value: Variant = _extract_variadic_value(expected)
|
||||||
|
if not _validate_value_type(expected_value):
|
||||||
|
return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
|
||||||
|
|
||||||
|
if current_value == null:
|
||||||
|
return report_error(GdAssertMessages.error_arr_contains(current_value, expected_value, [], expected_value, by_reference))
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
var diffs := _array_div(compare_mode, current_value as Array[Variant], expected_value as Array[Variant])
|
||||||
|
#var not_expect := diffs[0] as Array
|
||||||
|
var not_found: Array = diffs[1]
|
||||||
|
if not not_found.is_empty():
|
||||||
|
return report_error(GdAssertMessages.error_arr_contains(current_value, expected_value, [], not_found, by_reference))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func _contains_exactly(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert:
|
||||||
|
var current_value: Variant = get_current_value()
|
||||||
|
var expected_value: Variant = _extract_variadic_value(expected)
|
||||||
|
if not _validate_value_type(expected_value):
|
||||||
|
return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
|
||||||
|
|
||||||
|
if current_value == null:
|
||||||
|
return report_error(GdAssertMessages.error_arr_contains_exactly(null, expected_value, [], expected_value, compare_mode))
|
||||||
|
# has same content in same order
|
||||||
|
if _is_equal(current_value, expected_value, false, compare_mode):
|
||||||
|
return report_success()
|
||||||
|
# check has same elements but in different order
|
||||||
|
if _is_equals_sorted(current_value, expected_value, false, compare_mode):
|
||||||
|
return report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected_value, [], [], compare_mode))
|
||||||
|
# find the difference
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
var diffs := _array_div(compare_mode,
|
||||||
|
current_value as Array[Variant],
|
||||||
|
expected_value as Array[Variant],
|
||||||
|
GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
|
||||||
|
var not_expect: Array[Variant] = diffs[0]
|
||||||
|
var not_found: Array[Variant] = diffs[1]
|
||||||
|
return report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected_value, not_expect, not_found, compare_mode))
|
||||||
|
|
||||||
|
|
||||||
|
func _contains_exactly_in_any_order(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert:
|
||||||
|
var current_value: Variant = get_current_value()
|
||||||
|
var expected_value: Variant = _extract_variadic_value(expected)
|
||||||
|
if not _validate_value_type(expected_value):
|
||||||
|
return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
|
||||||
|
|
||||||
|
if current_value == null:
|
||||||
|
return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected_value, [], expected_value, compare_mode))
|
||||||
|
# find the difference
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
var diffs := _array_div(compare_mode, current_value as Array[Variant], expected_value as Array[Variant], false)
|
||||||
|
var not_expect: Array[Variant] = diffs[0]
|
||||||
|
var not_found: Array[Variant] = diffs[1]
|
||||||
|
if not_expect.is_empty() and not_found.is_empty():
|
||||||
|
return report_success()
|
||||||
|
return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected_value, not_expect, not_found, compare_mode))
|
||||||
|
|
||||||
|
|
||||||
|
func _not_contains(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert:
|
||||||
|
var current_value: Variant = get_current_value()
|
||||||
|
var expected_value: Variant = _extract_variadic_value(expected)
|
||||||
|
if not _validate_value_type(expected_value):
|
||||||
|
return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
|
||||||
|
if current_value == null:
|
||||||
|
return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected_value, [], expected_value, compare_mode))
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
var diffs := _array_div(compare_mode, current_value as Array[Variant], expected_value as Array[Variant])
|
||||||
|
var found: Array[Variant] = diffs[0]
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
if found.size() == (current_value as Array).size():
|
||||||
|
return report_success()
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
var diffs2 := _array_div(compare_mode, expected_value as Array[Variant], diffs[1] as Array[Variant])
|
||||||
|
return report_error(GdAssertMessages.error_arr_not_contains(current_value, expected_value, diffs2[0], compare_mode))
|
||||||
|
|
||||||
|
|
||||||
|
func is_null() -> GdUnitArrayAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.is_null()
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func is_not_null() -> GdUnitArrayAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.is_not_null()
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func is_equal(...expected: Array) -> GdUnitArrayAssert:
|
||||||
|
var current_value: Variant = get_current_value()
|
||||||
|
var expected_value: Variant= _extract_variadic_value(expected)
|
||||||
|
if not _validate_value_type(expected_value):
|
||||||
|
return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
|
||||||
|
if current_value == null and expected_value != null:
|
||||||
|
return report_error(GdAssertMessages.error_equal(null, expected_value))
|
||||||
|
|
||||||
|
if not _is_equal(current_value, expected_value):
|
||||||
|
var diff := _array_equals_div(current_value, expected_value)
|
||||||
|
var expected_as_list := GdArrayTools.as_string(diff[0], false)
|
||||||
|
var current_as_list := GdArrayTools.as_string(diff[1], false)
|
||||||
|
var index_report: Array = diff[2]
|
||||||
|
return report_error(GdAssertMessages.error_equal(expected_as_list, current_as_list, index_report))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
# Verifies that the current Array is equal to the given one, ignoring case considerations.
|
||||||
|
func is_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert:
|
||||||
|
var current_value: Variant = get_current_value()
|
||||||
|
var expected_value: Variant = _extract_variadic_value(expected)
|
||||||
|
if not _validate_value_type(expected_value):
|
||||||
|
return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
|
||||||
|
if current_value == null and expected_value != null:
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
return report_error(GdAssertMessages.error_equal(null, GdArrayTools.as_string(expected_value)))
|
||||||
|
|
||||||
|
if not _is_equal(current_value, expected_value, true):
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
var diff := _array_equals_div(current_value, expected_value, true)
|
||||||
|
var expected_as_list := GdArrayTools.as_string(diff[0])
|
||||||
|
var current_as_list := GdArrayTools.as_string(diff[1])
|
||||||
|
var index_report: Array = diff[2]
|
||||||
|
return report_error(GdAssertMessages.error_equal(expected_as_list, current_as_list, index_report))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func is_not_equal(...expected: Array) -> GdUnitArrayAssert:
|
||||||
|
var current_value: Variant = get_current_value()
|
||||||
|
var expected_value: Variant = _extract_variadic_value(expected)
|
||||||
|
if not _validate_value_type(expected_value):
|
||||||
|
return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
|
||||||
|
|
||||||
|
if _is_equal(current_value, expected_value):
|
||||||
|
return report_error(GdAssertMessages.error_not_equal(current_value, expected_value))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func is_not_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert:
|
||||||
|
var current_value: Variant = get_current_value()
|
||||||
|
var expected_value: Variant = _extract_variadic_value(expected)
|
||||||
|
if not _validate_value_type(expected_value):
|
||||||
|
return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value))
|
||||||
|
|
||||||
|
if _is_equal(current_value, expected_value, true):
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
var c := GdArrayTools.as_string(current_value as Array)
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
var e := GdArrayTools.as_string(expected_value)
|
||||||
|
return report_error(GdAssertMessages.error_not_equal_case_insensetiv(c, e))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func is_empty() -> GdUnitArrayAssert:
|
||||||
|
var current_value: Variant = get_current_value()
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
if current_value == null or (current_value as Array).size() > 0:
|
||||||
|
return report_error(GdAssertMessages.error_is_empty(current_value))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func is_not_empty() -> GdUnitArrayAssert:
|
||||||
|
var current_value: Variant = get_current_value()
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
if current_value != null and (current_value as Array).size() == 0:
|
||||||
|
return report_error(GdAssertMessages.error_is_not_empty())
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
@warning_ignore("unused_parameter", "shadowed_global_identifier")
|
||||||
|
func is_same(expected: Variant) -> GdUnitArrayAssert:
|
||||||
|
if not _validate_value_type(expected):
|
||||||
|
return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected))
|
||||||
|
var current: Variant = get_current_value()
|
||||||
|
if not is_same(current, expected):
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
report_error(GdAssertMessages.error_is_same(current, expected))
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func is_not_same(expected: Variant) -> GdUnitArrayAssert:
|
||||||
|
if not _validate_value_type(expected):
|
||||||
|
return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected))
|
||||||
|
var current: Variant = get_current_value()
|
||||||
|
if is_same(current, expected):
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
report_error(GdAssertMessages.error_not_same(current, expected))
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func has_size(expected: int) -> GdUnitArrayAssert:
|
||||||
|
var current_value: Variant = get_current_value()
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
if current_value == null or (current_value as Array).size() != expected:
|
||||||
|
return report_error(GdAssertMessages.error_has_size(current_value, expected))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func contains(...expected: Array) -> GdUnitArrayAssert:
|
||||||
|
return _contains(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
|
||||||
|
|
||||||
|
|
||||||
|
func contains_exactly(...expected: Array) -> GdUnitArrayAssert:
|
||||||
|
return _contains_exactly(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
|
||||||
|
|
||||||
|
|
||||||
|
func contains_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert:
|
||||||
|
return _contains_exactly_in_any_order(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
|
||||||
|
|
||||||
|
|
||||||
|
func contains_same(...expected: Array) -> GdUnitArrayAssert:
|
||||||
|
return _contains(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
|
||||||
|
|
||||||
|
|
||||||
|
func contains_same_exactly(...expected: Array) -> GdUnitArrayAssert:
|
||||||
|
return _contains_exactly(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
|
||||||
|
|
||||||
|
|
||||||
|
func contains_same_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert:
|
||||||
|
return _contains_exactly_in_any_order(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
|
||||||
|
|
||||||
|
|
||||||
|
func not_contains(...expected: Array) -> GdUnitArrayAssert:
|
||||||
|
return _not_contains(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
|
||||||
|
|
||||||
|
|
||||||
|
func not_contains_same(...expected: Array) -> GdUnitArrayAssert:
|
||||||
|
return _not_contains(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
|
||||||
|
|
||||||
|
|
||||||
|
func is_instanceof(expected: Variant) -> GdUnitAssert:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
_base.is_instanceof(expected)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func extract(func_name: String, ...func_args: Array) -> GdUnitArrayAssert:
|
||||||
|
var extracted_elements := Array()
|
||||||
|
var args: Array = _extract_variadic_value(func_args)
|
||||||
|
var extractor := GdUnitFuncValueExtractor.new(func_name, args)
|
||||||
|
var current: Variant = get_current_value()
|
||||||
|
if current == null:
|
||||||
|
_current_value_provider = DefaultValueProvider.new(null)
|
||||||
|
else:
|
||||||
|
for element: Variant in current:
|
||||||
|
extracted_elements.append(extractor.extract_value(element))
|
||||||
|
_current_value_provider = DefaultValueProvider.new(extracted_elements)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func extractv(...extractors: Array) -> GdUnitArrayAssert:
|
||||||
|
var extracted_elements := Array()
|
||||||
|
var current: Variant = get_current_value()
|
||||||
|
if current == null:
|
||||||
|
_current_value_provider = DefaultValueProvider.new(null)
|
||||||
|
else:
|
||||||
|
for element: Variant in current:
|
||||||
|
var ev: Array[Variant] = [
|
||||||
|
GdUnitTuple.NO_ARG,
|
||||||
|
GdUnitTuple.NO_ARG,
|
||||||
|
GdUnitTuple.NO_ARG,
|
||||||
|
GdUnitTuple.NO_ARG,
|
||||||
|
GdUnitTuple.NO_ARG,
|
||||||
|
GdUnitTuple.NO_ARG,
|
||||||
|
GdUnitTuple.NO_ARG,
|
||||||
|
GdUnitTuple.NO_ARG,
|
||||||
|
GdUnitTuple.NO_ARG,
|
||||||
|
GdUnitTuple.NO_ARG
|
||||||
|
]
|
||||||
|
|
||||||
|
for index: int in extractors.size():
|
||||||
|
var extractor: GdUnitValueExtractor = extractors[index]
|
||||||
|
ev[index] = extractor.extract_value(element)
|
||||||
|
if extractors.size() > 1:
|
||||||
|
extracted_elements.append(GdUnitTuple.new(ev[0], ev[1], ev[2], ev[3], ev[4], ev[5], ev[6], ev[7], ev[8], ev[9]))
|
||||||
|
else:
|
||||||
|
extracted_elements.append(ev[0])
|
||||||
|
_current_value_provider = DefaultValueProvider.new(extracted_elements)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
## Small helper to support the old expected arguments as single array and variadic arguments
|
||||||
|
func _extract_variadic_value(values: Variant) -> Variant:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
if values != null and values.size() == 1 and GdArrayTools.is_array_type(values[0]):
|
||||||
|
return values[0]
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
@warning_ignore("incompatible_ternary")
|
||||||
|
func _is_equal(
|
||||||
|
left: Variant,
|
||||||
|
right: Variant,
|
||||||
|
case_sensitive := false,
|
||||||
|
compare_mode := GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -> bool:
|
||||||
|
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
return GdObjects.equals(
|
||||||
|
(left as Array) if GdArrayTools.is_array_type(left) else left,
|
||||||
|
(right as Array) if GdArrayTools.is_array_type(right) else right,
|
||||||
|
case_sensitive,
|
||||||
|
compare_mode
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _is_equals_sorted(
|
||||||
|
left: Variant,
|
||||||
|
right: Variant,
|
||||||
|
case_sensitive := false,
|
||||||
|
compare_mode := GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -> bool:
|
||||||
|
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
return GdObjects.equals_sorted(
|
||||||
|
left as Array,
|
||||||
|
right as Array,
|
||||||
|
case_sensitive,
|
||||||
|
compare_mode)
|
||||||
1
addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid
Normal file
1
addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://ltwqaslcmub0
|
||||||
80
addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd
Normal file
80
addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
class_name GdUnitAssertImpl
|
||||||
|
extends GdUnitAssert
|
||||||
|
|
||||||
|
|
||||||
|
var _current :Variant
|
||||||
|
var _current_failure_message :String = ""
|
||||||
|
var _custom_failure_message :String = ""
|
||||||
|
var _additional_failure_message: String = ""
|
||||||
|
|
||||||
|
|
||||||
|
func _init(current :Variant) -> void:
|
||||||
|
_current = current
|
||||||
|
# save the actual assert instance on the current thread context
|
||||||
|
GdUnitThreadManager.get_current_context().set_assert(self)
|
||||||
|
GdAssertReports.reset_last_error_line_number()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func failure_message() -> String:
|
||||||
|
return _current_failure_message
|
||||||
|
|
||||||
|
|
||||||
|
func current_value() -> Variant:
|
||||||
|
return _current
|
||||||
|
|
||||||
|
|
||||||
|
func report_success() -> GdUnitAssert:
|
||||||
|
GdAssertReports.report_success()
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func report_error(failure :String, failure_line_number: int = -1) -> GdUnitAssert:
|
||||||
|
var line_number := failure_line_number if failure_line_number != -1 else GdUnitAssertions.get_line_number()
|
||||||
|
GdAssertReports.set_last_error_line_number(line_number)
|
||||||
|
_current_failure_message = GdAssertMessages.build_failure_message(failure, _additional_failure_message, _custom_failure_message)
|
||||||
|
GdAssertReports.report_error(_current_failure_message, line_number)
|
||||||
|
Engine.set_meta("GD_TEST_FAILURE", true)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func do_fail() -> GdUnitAssert:
|
||||||
|
return report_error(GdAssertMessages.error_not_implemented())
|
||||||
|
|
||||||
|
|
||||||
|
func override_failure_message(message: String) -> GdUnitAssert:
|
||||||
|
_custom_failure_message = message
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func append_failure_message(message: String) -> GdUnitAssert:
|
||||||
|
_additional_failure_message = message
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func is_null() -> GdUnitAssert:
|
||||||
|
var current :Variant = current_value()
|
||||||
|
if current != null:
|
||||||
|
return report_error(GdAssertMessages.error_is_null(current))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func is_not_null() -> GdUnitAssert:
|
||||||
|
var current :Variant = current_value()
|
||||||
|
if current == null:
|
||||||
|
return report_error(GdAssertMessages.error_is_not_null())
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func is_equal(expected: Variant) -> GdUnitAssert:
|
||||||
|
var current: Variant = current_value()
|
||||||
|
if not GdObjects.equals(current, expected):
|
||||||
|
return report_error(GdAssertMessages.error_equal(current, expected))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func is_not_equal(expected: Variant) -> GdUnitAssert:
|
||||||
|
var current: Variant = current_value()
|
||||||
|
if GdObjects.equals(current, expected):
|
||||||
|
return report_error(GdAssertMessages.error_not_equal(current, expected))
|
||||||
|
return report_success()
|
||||||
1
addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid
Normal file
1
addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bs5xosk58gxia
|
||||||
68
addons/gdUnit4/src/asserts/GdUnitAssertions.gd
Normal file
68
addons/gdUnit4/src/asserts/GdUnitAssertions.gd
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# Preloads all GdUnit assertions
|
||||||
|
class_name GdUnitAssertions
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
func _init() -> void:
|
||||||
|
# preload all gdunit assertions to speedup testsuite loading time
|
||||||
|
# gdlint:disable=private-method-call
|
||||||
|
@warning_ignore_start("return_value_discarded")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd")
|
||||||
|
GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd")
|
||||||
|
@warning_ignore_restore("return_value_discarded")
|
||||||
|
|
||||||
|
|
||||||
|
### We now load all used asserts and tool scripts into the cache according to the principle of "lazy loading"
|
||||||
|
### in order to noticeably reduce the loading time of the test suite.
|
||||||
|
# We go this hard way to increase the loading performance to avoid reparsing all the used scripts
|
||||||
|
# for more detailed info -> https://github.com/godotengine/godot/issues/67400
|
||||||
|
# gdlint:disable=function-name
|
||||||
|
static func __lazy_load(script_path :String) -> GDScript:
|
||||||
|
return ResourceLoader.load(script_path, "GDScript", ResourceLoader.CACHE_MODE_REUSE)
|
||||||
|
|
||||||
|
|
||||||
|
static func validate_value_type(value :Variant, type :Variant.Type) -> bool:
|
||||||
|
return value == null or typeof(value) == type
|
||||||
|
|
||||||
|
|
||||||
|
# Scans the current stack trace for the root cause to extract the line number
|
||||||
|
static func get_line_number() -> int:
|
||||||
|
var stack_trace := get_stack()
|
||||||
|
if stack_trace == null or stack_trace.is_empty():
|
||||||
|
return -1
|
||||||
|
for index in stack_trace.size():
|
||||||
|
var stack_info :Dictionary = stack_trace[index]
|
||||||
|
var function :String = stack_info.get("function")
|
||||||
|
# we catch helper asserts to skip over to return the correct line number
|
||||||
|
if function.begins_with("assert_"):
|
||||||
|
continue
|
||||||
|
if function.begins_with("test_"):
|
||||||
|
return stack_info.get("line")
|
||||||
|
var source :String = stack_info.get("source")
|
||||||
|
if source.is_empty() \
|
||||||
|
or source.begins_with("user://") \
|
||||||
|
or source.ends_with("GdUnitAssert.gd") \
|
||||||
|
or source.ends_with("GdUnitAssertions.gd") \
|
||||||
|
or source.ends_with("AssertImpl.gd") \
|
||||||
|
or source.ends_with("GdUnitTestSuite.gd") \
|
||||||
|
or source.ends_with("GdUnitSceneRunnerImpl.gd") \
|
||||||
|
or source.ends_with("GdUnitObjectInteractions.gd") \
|
||||||
|
or source.ends_with("GdUnitObjectInteractionsVerifier.gd") \
|
||||||
|
or source.ends_with("GdUnitAwaiter.gd"):
|
||||||
|
continue
|
||||||
|
return stack_info.get("line")
|
||||||
|
return -1
|
||||||
1
addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid
Normal file
1
addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cs8lnu7bwdr3x
|
||||||
87
addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd
Normal file
87
addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
extends GdUnitBoolAssert
|
||||||
|
|
||||||
|
var _base: GdUnitAssertImpl
|
||||||
|
|
||||||
|
|
||||||
|
func _init(current :Variant) -> void:
|
||||||
|
_base = GdUnitAssertImpl.new(current)
|
||||||
|
# save the actual assert instance on the current thread context
|
||||||
|
GdUnitThreadManager.get_current_context().set_assert(self)
|
||||||
|
if not GdUnitAssertions.validate_value_type(current, TYPE_BOOL):
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
report_error("GdUnitBoolAssert inital error, unexpected type <%s>" % GdObjects.typeof_as_string(current))
|
||||||
|
|
||||||
|
|
||||||
|
func _notification(event :int) -> void:
|
||||||
|
if event == NOTIFICATION_PREDELETE:
|
||||||
|
if _base != null:
|
||||||
|
_base.notification(event)
|
||||||
|
_base = null
|
||||||
|
|
||||||
|
|
||||||
|
func current_value() -> Variant:
|
||||||
|
return _base.current_value()
|
||||||
|
|
||||||
|
|
||||||
|
func report_success() -> GdUnitBoolAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.report_success()
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func report_error(error :String) -> GdUnitBoolAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.report_error(error)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func failure_message() -> String:
|
||||||
|
return _base.failure_message()
|
||||||
|
|
||||||
|
|
||||||
|
func override_failure_message(message: String) -> GdUnitBoolAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.override_failure_message(message)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func append_failure_message(message: String) -> GdUnitBoolAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.append_failure_message(message)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func is_null() -> GdUnitBoolAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.is_null()
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func is_not_null() -> GdUnitBoolAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.is_not_null()
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func is_equal(expected: Variant) -> GdUnitBoolAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.is_equal(expected)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func is_not_equal(expected: Variant) -> GdUnitBoolAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.is_not_equal(expected)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func is_true() -> GdUnitBoolAssert:
|
||||||
|
if current_value() != true:
|
||||||
|
return report_error(GdAssertMessages.error_is_true(current_value()))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func is_false() -> GdUnitBoolAssert:
|
||||||
|
if current_value() == true || current_value() == null:
|
||||||
|
return report_error(GdAssertMessages.error_is_false(current_value()))
|
||||||
|
return report_success()
|
||||||
1
addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid
Normal file
1
addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://ul5ajqk7fu8g
|
||||||
206
addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd
Normal file
206
addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
extends GdUnitDictionaryAssert
|
||||||
|
|
||||||
|
var _base: GdUnitAssertImpl
|
||||||
|
|
||||||
|
|
||||||
|
func _init(current :Variant) -> void:
|
||||||
|
_base = GdUnitAssertImpl.new(current)
|
||||||
|
# save the actual assert instance on the current thread context
|
||||||
|
GdUnitThreadManager.get_current_context().set_assert(self)
|
||||||
|
if not GdUnitAssertions.validate_value_type(current, TYPE_DICTIONARY):
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
report_error("GdUnitDictionaryAssert inital error, unexpected type <%s>" % GdObjects.typeof_as_string(current))
|
||||||
|
|
||||||
|
|
||||||
|
func _notification(event :int) -> void:
|
||||||
|
if event == NOTIFICATION_PREDELETE:
|
||||||
|
if _base != null:
|
||||||
|
_base.notification(event)
|
||||||
|
_base = null
|
||||||
|
|
||||||
|
|
||||||
|
func report_success() -> GdUnitDictionaryAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.report_success()
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func report_error(error :String) -> GdUnitDictionaryAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.report_error(error)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func failure_message() -> String:
|
||||||
|
return _base.failure_message()
|
||||||
|
|
||||||
|
|
||||||
|
func override_failure_message(message: String) -> GdUnitDictionaryAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.override_failure_message(message)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func append_failure_message(message: String) -> GdUnitDictionaryAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.append_failure_message(message)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func current_value() -> Variant:
|
||||||
|
return _base.current_value()
|
||||||
|
|
||||||
|
|
||||||
|
func is_null() -> GdUnitDictionaryAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.is_null()
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func is_not_null() -> GdUnitDictionaryAssert:
|
||||||
|
@warning_ignore("return_value_discarded")
|
||||||
|
_base.is_not_null()
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
func is_equal(expected: Variant) -> GdUnitDictionaryAssert:
|
||||||
|
var current :Variant = current_value()
|
||||||
|
if current == null:
|
||||||
|
return report_error(GdAssertMessages.error_equal(null, GdAssertMessages.format_dict(expected)))
|
||||||
|
if not GdObjects.equals(current, expected):
|
||||||
|
var c := GdAssertMessages.format_dict(current)
|
||||||
|
var e := GdAssertMessages.format_dict(expected)
|
||||||
|
return report_error(GdAssertMessages.error_equal(c, e))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func is_not_equal(expected: Variant) -> GdUnitDictionaryAssert:
|
||||||
|
var current: Variant = current_value()
|
||||||
|
if GdObjects.equals(current, expected):
|
||||||
|
return report_error(GdAssertMessages.error_not_equal(current, expected))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
@warning_ignore("unused_parameter", "shadowed_global_identifier")
|
||||||
|
func is_same(expected :Variant) -> GdUnitDictionaryAssert:
|
||||||
|
var current :Variant = current_value()
|
||||||
|
if current == null:
|
||||||
|
return report_error(GdAssertMessages.error_equal(null, GdAssertMessages.format_dict(expected)))
|
||||||
|
if not is_same(current, expected):
|
||||||
|
var c := GdAssertMessages.format_dict(current)
|
||||||
|
var e := GdAssertMessages.format_dict(expected)
|
||||||
|
return report_error(GdAssertMessages.error_is_same(c, e))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
@warning_ignore("unused_parameter", "shadowed_global_identifier")
|
||||||
|
func is_not_same(expected :Variant) -> GdUnitDictionaryAssert:
|
||||||
|
var current :Variant = current_value()
|
||||||
|
if is_same(current, expected):
|
||||||
|
return report_error(GdAssertMessages.error_not_same(current, expected))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func is_empty() -> GdUnitDictionaryAssert:
|
||||||
|
var current :Variant = current_value()
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
if current == null or not (current as Dictionary).is_empty():
|
||||||
|
return report_error(GdAssertMessages.error_is_empty(current))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func is_not_empty() -> GdUnitDictionaryAssert:
|
||||||
|
var current :Variant = current_value()
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
if current == null or (current as Dictionary).is_empty():
|
||||||
|
return report_error(GdAssertMessages.error_is_not_empty())
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func has_size(expected: int) -> GdUnitDictionaryAssert:
|
||||||
|
var current :Variant = current_value()
|
||||||
|
if current == null:
|
||||||
|
return report_error(GdAssertMessages.error_is_not_null())
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
if (current as Dictionary).size() != expected:
|
||||||
|
return report_error(GdAssertMessages.error_has_size(current, expected))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func _contains_keys(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert:
|
||||||
|
var current :Variant = current_value()
|
||||||
|
var expected_value: Array = _extract_variadic_value(expected)
|
||||||
|
if current == null:
|
||||||
|
return report_error(GdAssertMessages.error_is_not_null())
|
||||||
|
# find expected keys
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
var keys_not_found :Array = expected_value.filter(_filter_by_key.bind((current as Dictionary).keys(), compare_mode))
|
||||||
|
if not keys_not_found.is_empty():
|
||||||
|
@warning_ignore("unsafe_cast")
|
||||||
|
return report_error(GdAssertMessages.error_contains_keys((current as Dictionary).keys() as Array, expected_value, keys_not_found, compare_mode))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func _contains_key_value(key :Variant, value :Variant, compare_mode :GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert:
|
||||||
|
var current :Variant = current_value()
|
||||||
|
var expected := [key]
|
||||||
|
if current == null:
|
||||||
|
return report_error(GdAssertMessages.error_is_not_null())
|
||||||
|
var dict_current: Dictionary = current
|
||||||
|
var keys_not_found :Array = expected.filter(_filter_by_key.bind(dict_current.keys(), compare_mode))
|
||||||
|
if not keys_not_found.is_empty():
|
||||||
|
return report_error(GdAssertMessages.error_contains_keys(dict_current.keys() as Array, expected, keys_not_found, compare_mode))
|
||||||
|
if not GdObjects.equals(dict_current[key], value, false, compare_mode):
|
||||||
|
return report_error(GdAssertMessages.error_contains_key_value(key, value, dict_current[key], compare_mode))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func _not_contains_keys(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert:
|
||||||
|
var current :Variant = current_value()
|
||||||
|
var expected_value: Array = _extract_variadic_value(expected)
|
||||||
|
if current == null:
|
||||||
|
return report_error(GdAssertMessages.error_is_not_null())
|
||||||
|
var dict_current: Dictionary = current
|
||||||
|
var keys_found :Array = dict_current.keys().filter(_filter_by_key.bind(expected_value, compare_mode, true))
|
||||||
|
if not keys_found.is_empty():
|
||||||
|
return report_error(GdAssertMessages.error_not_contains_keys(dict_current.keys() as Array, expected_value, keys_found, compare_mode))
|
||||||
|
return report_success()
|
||||||
|
|
||||||
|
|
||||||
|
func contains_keys(...expected: Array) -> GdUnitDictionaryAssert:
|
||||||
|
return _contains_keys(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
|
||||||
|
|
||||||
|
|
||||||
|
func contains_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert:
|
||||||
|
return _contains_key_value(key, value, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
|
||||||
|
|
||||||
|
|
||||||
|
func not_contains_keys(...expected: Array) -> GdUnitDictionaryAssert:
|
||||||
|
return _not_contains_keys(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST)
|
||||||
|
|
||||||
|
|
||||||
|
func contains_same_keys(expected :Array) -> GdUnitDictionaryAssert:
|
||||||
|
return _contains_keys(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
|
||||||
|
|
||||||
|
|
||||||
|
func contains_same_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert:
|
||||||
|
return _contains_key_value(key, value, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
|
||||||
|
|
||||||
|
|
||||||
|
func not_contains_same_keys(...expected: Array) -> GdUnitDictionaryAssert:
|
||||||
|
return _not_contains_keys(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE)
|
||||||
|
|
||||||
|
|
||||||
|
func _filter_by_key(element :Variant, values :Array, compare_mode :GdObjects.COMPARE_MODE, is_not :bool = false) -> bool:
|
||||||
|
for key :Variant in values:
|
||||||
|
if GdObjects.equals(key, element, false, compare_mode):
|
||||||
|
return is_not
|
||||||
|
return !is_not
|
||||||
|
|
||||||
|
|
||||||
|
## Small helper to support the old expected arguments as single array and variadic arguments
|
||||||
|
func _extract_variadic_value(values: Variant) -> Variant:
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
if values != null and values.size() == 1 and GdArrayTools.is_array_type(values[0]):
|
||||||
|
return values[0]
|
||||||
|
return values
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user