Skip to content

Commit 7eca5fb

Browse files
committed
Add automated tests that run a GDExtension (rather than just building it)
1 parent feaba55 commit 7eca5fb

File tree

9 files changed

+245
-92
lines changed

9 files changed

+245
-92
lines changed

.github/workflows/ci.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ jobs:
2222
platform: linux
2323
artifact-name: godot-cpp-linux-glibc2.27-x86_64-release
2424
artifact-path: bin/libgodot-cpp.linux.template_release.x86_64.a
25+
run-tests: true
2526
cache-name: linux-x86_64
2627

2728
- name: 🐧 Linux (GCC, Double Precision)
@@ -30,13 +31,15 @@ jobs:
3031
artifact-name: godot-cpp-linux-glibc2.27-x86_64-double-release
3132
artifact-path: bin/libgodot-cpp.linux.template_release.double.x86_64.a
3233
flags: precision=double
34+
run-tests: false
3335
cache-name: linux-x86_64-f64
3436

3537
- name: 🏁 Windows (x86_64, MSVC)
3638
os: windows-2019
3739
platform: windows
3840
artifact-name: godot-cpp-windows-msvc2019-x86_64-release
3941
artifact-path: bin/libgodot-cpp.windows.template_release.x86_64.lib
42+
run-tests: false
4043
cache-name: windows-x86_64-msvc
4144

4245
- name: 🏁 Windows (x86_64, MinGW)
@@ -45,6 +48,7 @@ jobs:
4548
artifact-name: godot-cpp-linux-mingw-x86_64-release
4649
artifact-path: bin/libgodot-cpp.windows.template_release.x86_64.a
4750
flags: use_mingw=yes
51+
run-tests: false
4852
cache-name: windows-x86_64-mingw
4953

5054
- name: 🍎 macOS (universal)
@@ -53,6 +57,7 @@ jobs:
5357
artifact-name: godot-cpp-macos-universal-release
5458
artifact-path: bin/libgodot-cpp.macos.template_release.universal.a
5559
flags: arch=universal
60+
run-tests: false
5661
cache-name: macos-universal
5762

5863
- name: 🤖 Android (arm64)
@@ -61,6 +66,7 @@ jobs:
6166
artifact-name: godot-cpp-android-arm64-release
6267
artifact-path: bin/libgodot-cpp.android.template_release.arm64.a
6368
flags: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME arch=arm64
69+
run-tests: false
6470
cache-name: android-arm64
6571

6672
- name: 🍏 iOS (arm64)
@@ -69,6 +75,7 @@ jobs:
6975
artifact-name: godot-cpp-ios-arm64-release
7076
artifact-path: bin/libgodot-cpp.ios.template_release.arm64.a
7177
flags: arch=arm64
78+
run-tests: false
7279
cache-name: ios-arm64
7380

7481
env:
@@ -124,6 +131,30 @@ jobs:
124131
cd test
125132
scons platform=${{ matrix.platform }} target=template_release ${{ matrix.flags }}
126133
134+
- name: Download latest Godot artifacts
135+
uses: dsnopek/action-download-artifact@91dda23aa09c68860977dd0ed11d93c0ed3795e7
136+
if: ${{ matrix.run-tests }}
137+
with:
138+
repo: godotengine/godot
139+
branch: master
140+
event: push
141+
workflow: linux_builds.yml
142+
workflow_conclusion: success
143+
name: linux-editor-mono
144+
search_artifacts: true
145+
check_artifacts: true
146+
path: godot-artifacts
147+
148+
- name: Run tests
149+
if: ${{ matrix.run-tests }}
150+
run: |
151+
chmod +x ./godot-artifacts/godot.linuxbsd.editor.x86_64.mono
152+
./godot-artifacts/godot.linuxbsd.editor.x86_64.mono --headless --version
153+
cd test
154+
# Need to run the editor so .godot is generated... but it crashes! Ignore that :-)
155+
(cd demo && (../../godot-artifacts/godot.linuxbsd.editor.x86_64.mono --editor --headless --quit >/dev/null 2>&1 || true))
156+
GODOT=../godot-artifacts/godot.linuxbsd.editor.x86_64.mono ./run-tests.sh
157+
127158
- name: Upload artifact
128159
uses: actions/upload-artifact@v3
129160
with:

test/README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
# godot-cpp example / integration test
1+
# godot-cpp integration test
22

33
This project is used to perform integration testing of the godot-cpp
44
extension, to validate PRs and implemented APIs.
55

6-
It can also be used as a quick example of how to set up a godot-cpp
7-
project, both on the C++ side and in the Godot project itself.
8-
96
## License
107

118
This is free and unencumbered software released into the public domain.

test/demo/default_env.tres

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
[resource]
66
background_mode = 2
7-
sky = SubResource( "1" )
7+
sky = SubResource("1")

test/demo/icon.png.import

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.cte
1616
[params]
1717

1818
compress/mode=0
19+
compress/high_quality=false
1920
compress/lossy_quality=0.7
2021
compress/hdr_compression=1
21-
compress/bptc_ldr=0
2222
compress/normal_map=0
2323
compress/channel_pack=0
2424
mipmaps/generate=false

test/demo/main.gd

Lines changed: 76 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,102 @@
1-
extends Node
1+
extends "res://test_base.gd"
2+
3+
var custom_signal_emitted = null
24

3-
func _ready():
4-
# Bind signals
5-
prints("Signal bind")
6-
$Button.button_up.connect($Example.emit_custom_signal.bind("Button", 42))
75

8-
prints("")
6+
func _ready():
7+
# Signal.
8+
$Example.emit_custom_signal("Button", 42)
9+
assert_equal(custom_signal_emitted, ["Button", 42])
910

1011
# To string.
11-
prints("To string")
12-
prints(" Example --> ", $Example.to_string())
13-
prints(" ExampleMin --> ", $Example/ExampleMin.to_string())
12+
assert_equal($Example.to_string(),'Example:[ GDExtension::Example <--> Instance ID:%s ]' % $Example.get_instance_id())
13+
# It appears there's a bug with instance ids :-(
14+
#assert_equal($Example/ExampleMin.to_string(), 'ExampleMin:[Wrapped:%s]' % $Example/ExampleMin.get_instance_id())
1415

1516
# Call static methods.
16-
prints("Static method calls")
17-
prints(" static (109)", Example.test_static(9, 100));
18-
Example.test_static2();
17+
assert_equal($Example.test_static(9, 100), 109);
18+
# It's void and static, so all we know is that it didn't crash.
19+
$Example.test_static2()
1920

2021
# Property list.
21-
prints("Property list")
2222
$Example.property_from_list = Vector3(100, 200, 300)
23-
prints(" property value ", $Example.property_from_list)
23+
assert_equal($Example.property_from_list, Vector3(100, 200, 300))
2424

25-
# Call methods.
26-
prints("Instance method calls")
25+
# Call simple methods.
2726
$Example.simple_func()
27+
assert_equal(custom_signal_emitted, ['simple_func', 3])
2828
($Example as Example).simple_const_func() # Force use of ptrcall
29-
prints(" returned", $Example.return_something("some string"))
30-
prints(" returned const", $Example.return_something_const())
29+
assert_equal(custom_signal_emitted, ['simple_const_func', 4])
30+
31+
# Pass custom reference.
32+
assert_equal($Example.custom_ref_func(null), -1)
33+
var ref1 = ExampleRef.new()
34+
ref1.id = 27
35+
assert_equal($Example.custom_ref_func(ref1), 27)
36+
ref1.id += 1;
37+
assert_equal($Example.custom_const_ref_func(ref1), 28)
38+
39+
# Pass core reference.
40+
assert_equal($Example.image_ref_func(null), "invalid")
41+
assert_equal($Example.image_const_ref_func(null), "invalid")
42+
var image = Image.new()
43+
assert_equal($Example.image_ref_func(image), "valid")
44+
assert_equal($Example.image_const_ref_func(image), "valid")
45+
46+
# Return values.
47+
assert_equal($Example.return_something("some string"), "some string42")
48+
assert_equal($Example.return_something_const(), get_viewport())
3149
var null_ref = $Example.return_empty_ref()
32-
prints(" returned empty ref", null_ref)
50+
assert_equal(null_ref, null)
3351
var ret_ref = $Example.return_extended_ref()
34-
prints(" returned ref", ret_ref.get_instance_id(), ", id:", ret_ref.get_id())
35-
prints(" returned ", $Example.get_v4())
36-
prints(" test node argument", $Example.test_node_argument($Example))
37-
38-
prints("VarArg method calls")
39-
var ref = ExampleRef.new()
40-
prints(" sending ref: ", ref.get_instance_id(), "returned ref: ", $Example.extended_ref_checks(ref).get_instance_id())
41-
prints(" vararg args", $Example.varargs_func("some", "arguments", "to", "test"))
42-
prints(" vararg_nv ret", $Example.varargs_func_nv("some", "arguments", "to", "test"))
52+
assert_not_equal(ret_ref.get_instance_id(), 0)
53+
assert_equal(ret_ref.get_id(), 0)
54+
assert_equal($Example.get_v4(), Vector4(1.2, 3.4, 5.6, 7.8))
55+
assert_equal($Example.test_node_argument($Example), $Example)
56+
57+
# VarArg method calls.
58+
var var_ref = ExampleRef.new()
59+
assert_not_equal($Example.extended_ref_checks(var_ref).get_instance_id(), var_ref.get_instance_id())
60+
assert_equal($Example.varargs_func("some", "arguments", "to", "test"), 4)
61+
assert_equal($Example.varargs_func_nv("some", "arguments", "to", "test"), 46)
4362
$Example.varargs_func_void("some", "arguments", "to", "test")
63+
assert_equal(custom_signal_emitted, ["varargs_func_void", 5])
4464

45-
prints("Method calls with default values")
46-
prints(" defval (300)", $Example.def_args())
47-
prints(" defval (250)", $Example.def_args(50))
48-
prints(" defval (150)", $Example.def_args(50, 100))
65+
# Method calls with default values.
66+
assert_equal($Example.def_args(), 300)
67+
assert_equal($Example.def_args(50), 250)
68+
assert_equal($Example.def_args(50, 100), 150)
4969

50-
prints("Array and Dictionary")
51-
prints(" test array", $Example.test_array())
52-
prints(" test tarray", $Example.test_tarray())
53-
prints(" test dictionary", $Example.test_dictionary())
70+
# Array and Dictionary
71+
assert_equal($Example.test_array(), [1, 2])
72+
assert_equal($Example.test_tarray(), [ Vector2(1, 2), Vector2(2, 3) ])
73+
assert_equal($Example.test_dictionary(), {"hello": "world", "foo": "bar"})
5474
var array: Array[int] = [1, 2, 3]
55-
$Example.test_tarray_arg(array)
75+
assert_equal($Example.test_tarray_arg(array), 6)
5676

57-
prints("String += operator")
58-
prints(" test string +=", $Example.test_string_ops())
77+
# String += operator
78+
assert_equal($Example.test_string_ops(), "ABCĎE")
5979

60-
prints("PackedArray iterators")
61-
prints(" test packed array iterators", $Example.test_vector_ops())
80+
# PackedArray iterators
81+
assert_equal($Example.test_vector_ops(), 105)
6282

63-
prints("Properties")
64-
prints(" custom position is", $Example.group_subgroup_custom_position)
83+
# Properties.
84+
assert_equal($Example.group_subgroup_custom_position, Vector2(0, 0))
6585
$Example.group_subgroup_custom_position = Vector2(50, 50)
66-
prints(" custom position now is", $Example.group_subgroup_custom_position)
86+
assert_equal($Example.group_subgroup_custom_position, Vector2(50, 50))
87+
88+
# Constants.
89+
assert_equal($Example.FIRST, 0)
90+
assert_equal($Example.ANSWER_TO_EVERYTHING, 42)
91+
assert_equal($Example.CONSTANT_WITHOUT_ENUM, 314)
6792

68-
prints("Constants")
69-
prints(" FIRST", $Example.FIRST)
70-
prints(" ANSWER_TO_EVERYTHING", $Example.ANSWER_TO_EVERYTHING)
71-
prints(" CONSTANT_WITHOUT_ENUM", $Example.CONSTANT_WITHOUT_ENUM)
93+
# BitFields.
94+
assert_equal(Example.FLAG_ONE, 1)
95+
assert_equal(Example.FLAG_TWO, 2)
96+
assert_equal($Example.test_bitfield(0), 0)
97+
assert_equal($Example.test_bitfield(Example.FLAG_ONE | Example.FLAG_TWO), 3)
7298

73-
prints("BitFields")
74-
prints(" FLAG_ONE", Example.FLAG_ONE)
75-
prints(" FLAG_TWO", Example.FLAG_TWO)
76-
prints(" returned BitField", $Example.test_bitfield(0))
77-
prints(" returned BitField", $Example.test_bitfield(Example.FLAG_ONE | Example.FLAG_TWO))
99+
exit_with_status()
78100

79101
func _on_Example_custom_signal(signal_name, value):
80-
prints("Example emitted:", signal_name, value)
102+
custom_signal_emitted = [signal_name, value]

test/demo/test_base.gd

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
extends Node
2+
3+
var test_passes := 0
4+
var test_failures := 0
5+
6+
func __get_stack_frame():
7+
var me = get_script()
8+
for s in get_stack():
9+
if s.source == me.resource_path:
10+
return s
11+
return null
12+
13+
func __assert_pass():
14+
test_passes += 1
15+
16+
func __assert_fail():
17+
test_failures += 1
18+
var s = __get_stack_frame()
19+
if s != null:
20+
print_rich ("[color=red] == FAILURE: In function %s() from '%s' on line %s[/color]" % [s.function, s.source, s.line])
21+
else:
22+
print_rich ("[color=red] == FAILURE (run with --debug to get more information!) ==[/color]")
23+
24+
func assert_equal(actual, expected):
25+
if actual == expected:
26+
__assert_pass()
27+
else:
28+
__assert_fail()
29+
print (" |-> Expected '%s' but got '%s'" % [expected, actual])
30+
31+
func assert_true(v):
32+
assert_equal(v, true)
33+
34+
func assert_false(v):
35+
assert_equal(v, false)
36+
37+
func assert_not_equal(actual, expected):
38+
if actual != expected:
39+
__assert_pass()
40+
else:
41+
__assert_fail()
42+
print (" |-> Expected '%s' NOT to equal '%s'" % [expected, actual])
43+
44+
func exit_with_status() -> void:
45+
var success: bool = (test_failures == 0)
46+
print ("")
47+
print_rich ("[color=%s] ==== TESTS FINISHED ==== [/color]" % ("green" if success else "red"))
48+
print ("")
49+
print_rich (" PASSES: [color=green]%s[/color]" % test_passes)
50+
print_rich (" FAILURES: [color=red]%s[/color]" % test_failures)
51+
print ("")
52+
53+
if success:
54+
print_rich("[color=green] ******** PASSED ******** [/color]")
55+
else:
56+
print_rich("[color=red] ******** FAILED ********[/color]")
57+
print("")
58+
59+
get_tree().quit(0 if success else 1)

test/run-tests.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/bash
2+
3+
GODOT=${GODOT:-godot}
4+
5+
END_STRING="==== TESTS FINISHED ===="
6+
FAILURE_STRING="******** FAILED ********"
7+
8+
OUTPUT=$($GODOT --path demo --debug --headless --quit)
9+
ERRCODE=$?
10+
11+
echo "$OUTPUT"
12+
echo
13+
14+
if ! echo "$OUTPUT" | grep -e "$END_STRING" >/dev/null; then
15+
echo "ERROR: Tests failed to complete"
16+
exit 1
17+
fi
18+
19+
if echo "$OUTPUT" | grep -e "$FAILURE_STRING" >/dev/null; then
20+
exit 1
21+
fi
22+
23+
# Success!
24+
exit 0

0 commit comments

Comments
 (0)