Skip to content

Commit

Permalink
Add automated tests that run a GDExtension (rather than just building…
Browse files Browse the repository at this point in the history
… it)
  • Loading branch information
dsnopek committed May 1, 2023
1 parent feaba55 commit 4d720f1
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 92 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jobs:
platform: linux
artifact-name: godot-cpp-linux-glibc2.27-x86_64-release
artifact-path: bin/libgodot-cpp.linux.template_release.x86_64.a
run-tests: true
cache-name: linux-x86_64

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

- name: 🏁 Windows (x86_64, MSVC)
os: windows-2019
platform: windows
artifact-name: godot-cpp-windows-msvc2019-x86_64-release
artifact-path: bin/libgodot-cpp.windows.template_release.x86_64.lib
run-tests: false
cache-name: windows-x86_64-msvc

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

- name: 🍎 macOS (universal)
Expand All @@ -53,6 +57,7 @@ jobs:
artifact-name: godot-cpp-macos-universal-release
artifact-path: bin/libgodot-cpp.macos.template_release.universal.a
flags: arch=universal
run-tests: false
cache-name: macos-universal

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

- name: 🍏 iOS (arm64)
Expand All @@ -69,6 +75,7 @@ jobs:
artifact-name: godot-cpp-ios-arm64-release
artifact-path: bin/libgodot-cpp.ios.template_release.arm64.a
flags: arch=arm64
run-tests: false
cache-name: ios-arm64

env:
Expand Down Expand Up @@ -124,6 +131,30 @@ jobs:
cd test
scons platform=${{ matrix.platform }} target=template_release ${{ matrix.flags }}
- name: Download latest Godot artifacts
uses: dsnopek/action-download-artifact@91dda23aa09c68860977dd0ed11d93c0ed3795e7
if: ${{ matrix.run-tests }}
with:
repo: godotengine/godot
branch: master
event: push
workflow: linux_builds.yml
workflow_conclusion: success
name: linux-editor-mono
search_artifacts: true
check_artifacts: true
path: godot-artifacts

- name: Run tests
if: ${{ matrix.run-tests }}
run: |
chmod +x ./godot-artifacts/godot.linuxbsd.editor.x86_64.mono
./godot-artifacts/godot.linuxbsd.editor.x86_64.mono --headless --version
cd test
# Need to run the editor so .godot is generated... but it crashes! Ignore that :-)
(cd demo && (../../godot-artifacts/godot.linuxbsd.editor.x86_64.mono --editor --headless --quit >/dev/null 2>&1 || true))
GODOT=../godot-artifacts/godot.linuxbsd.editor.x86_64.mono ./run-tests.sh
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
Expand Down
5 changes: 1 addition & 4 deletions test/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
# godot-cpp example / integration test
# godot-cpp integration test

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

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

## License

This is free and unencumbered software released into the public domain.
Expand Down
2 changes: 1 addition & 1 deletion test/demo/default_env.tres
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

[resource]
background_mode = 2
sky = SubResource( "1" )
sky = SubResource("1")
2 changes: 1 addition & 1 deletion test/demo/icon.png.import
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.cte
[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/bptc_ldr=0
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
Expand Down
130 changes: 76 additions & 54 deletions test/demo/main.gd
Original file line number Diff line number Diff line change
@@ -1,80 +1,102 @@
extends Node
extends "res://test_base.gd"

var custom_signal_emitted = null

func _ready():
# Bind signals
prints("Signal bind")
$Button.button_up.connect($Example.emit_custom_signal.bind("Button", 42))

prints("")
func _ready():
# Signal.
$Example.emit_custom_signal("Button", 42)
assert_equal(custom_signal_emitted, ["Button", 42])

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

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

# Property list.
prints("Property list")
$Example.property_from_list = Vector3(100, 200, 300)
prints(" property value ", $Example.property_from_list)
assert_equal($Example.property_from_list, Vector3(100, 200, 300))

# Call methods.
prints("Instance method calls")
# Call simple methods.
$Example.simple_func()
assert_equal(custom_signal_emitted, ['simple_func', 3])
($Example as Example).simple_const_func() # Force use of ptrcall
prints(" returned", $Example.return_something("some string"))
prints(" returned const", $Example.return_something_const())
assert_equal(custom_signal_emitted, ['simple_const_func', 4])

# Pass custom reference.
assert_equal($Example.custom_ref_func(null), -1)
var ref1 = ExampleRef.new()
ref1.id = 27
assert_equal($Example.custom_ref_func(ref1), 27)
ref1.id += 1;
assert_equal($Example.custom_const_ref_func(ref1), 28)

# Pass core reference.
assert_equal($Example.image_ref_func(null), "invalid")
assert_equal($Example.image_const_ref_func(null), "invalid")
var image = Image.new()
assert_equal($Example.image_ref_func(image), "valid")
assert_equal($Example.image_const_ref_func(image), "valid")

# Return values.
assert_equal($Example.return_something("some string"), "some string42")
assert_equal($Example.return_something_const(), get_viewport())
var null_ref = $Example.return_empty_ref()
prints(" returned empty ref", null_ref)
assert_equal(null_ref, null)
var ret_ref = $Example.return_extended_ref()
prints(" returned ref", ret_ref.get_instance_id(), ", id:", ret_ref.get_id())
prints(" returned ", $Example.get_v4())
prints(" test node argument", $Example.test_node_argument($Example))

prints("VarArg method calls")
var ref = ExampleRef.new()
prints(" sending ref: ", ref.get_instance_id(), "returned ref: ", $Example.extended_ref_checks(ref).get_instance_id())
prints(" vararg args", $Example.varargs_func("some", "arguments", "to", "test"))
prints(" vararg_nv ret", $Example.varargs_func_nv("some", "arguments", "to", "test"))
assert_not_equal(ret_ref.get_instance_id(), 0)
assert_equal(ret_ref.get_id(), 0)
assert_equal($Example.get_v4(), Vector4(1.2, 3.4, 5.6, 7.8))
assert_equal($Example.test_node_argument($Example), $Example)

# VarArg method calls.
var var_ref = ExampleRef.new()
assert_not_equal($Example.extended_ref_checks(var_ref).get_instance_id(), var_ref.get_instance_id())
assert_equal($Example.varargs_func("some", "arguments", "to", "test"), 4)
assert_equal($Example.varargs_func_nv("some", "arguments", "to", "test"), 46)
$Example.varargs_func_void("some", "arguments", "to", "test")
assert_equal(custom_signal_emitted, ["varargs_func_void", 5])

prints("Method calls with default values")
prints(" defval (300)", $Example.def_args())
prints(" defval (250)", $Example.def_args(50))
prints(" defval (150)", $Example.def_args(50, 100))
# Method calls with default values.
assert_equal($Example.def_args(), 300)
assert_equal($Example.def_args(50), 250)
assert_equal($Example.def_args(50, 100), 150)

prints("Array and Dictionary")
prints(" test array", $Example.test_array())
prints(" test tarray", $Example.test_tarray())
prints(" test dictionary", $Example.test_dictionary())
# Array and Dictionary
assert_equal($Example.test_array(), [1, 2])
assert_equal($Example.test_tarray(), [ Vector2(1, 2), Vector2(2, 3) ])
assert_equal($Example.test_dictionary(), {"hello": "world", "foo": "bar"})
var array: Array[int] = [1, 2, 3]
$Example.test_tarray_arg(array)
assert_equal($Example.test_tarray_arg(array), 6)

prints("String += operator")
prints(" test string +=", $Example.test_string_ops())
# String += operator
assert_equal($Example.test_string_ops(), "ABCĎE")

prints("PackedArray iterators")
prints(" test packed array iterators", $Example.test_vector_ops())
# PackedArray iterators
assert_equal($Example.test_vector_ops(), 105)

prints("Properties")
prints(" custom position is", $Example.group_subgroup_custom_position)
# Properties.
assert_equal($Example.group_subgroup_custom_position, Vector2(0, 0))
$Example.group_subgroup_custom_position = Vector2(50, 50)
prints(" custom position now is", $Example.group_subgroup_custom_position)
assert_equal($Example.group_subgroup_custom_position, Vector2(50, 50))

# Constants.
assert_equal($Example.FIRST, 0)
assert_equal($Example.ANSWER_TO_EVERYTHING, 42)
assert_equal($Example.CONSTANT_WITHOUT_ENUM, 314)

prints("Constants")
prints(" FIRST", $Example.FIRST)
prints(" ANSWER_TO_EVERYTHING", $Example.ANSWER_TO_EVERYTHING)
prints(" CONSTANT_WITHOUT_ENUM", $Example.CONSTANT_WITHOUT_ENUM)
# BitFields.
assert_equal(Example.FLAG_ONE, 1)
assert_equal(Example.FLAG_TWO, 2)
assert_equal($Example.test_bitfield(0), 0)
assert_equal($Example.test_bitfield(Example.FLAG_ONE | Example.FLAG_TWO), 3)

prints("BitFields")
prints(" FLAG_ONE", Example.FLAG_ONE)
prints(" FLAG_TWO", Example.FLAG_TWO)
prints(" returned BitField", $Example.test_bitfield(0))
prints(" returned BitField", $Example.test_bitfield(Example.FLAG_ONE | Example.FLAG_TWO))
exit_with_status()

func _on_Example_custom_signal(signal_name, value):
prints("Example emitted:", signal_name, value)
custom_signal_emitted = [signal_name, value]
59 changes: 59 additions & 0 deletions test/demo/test_base.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
extends Node

var test_passes := 0
var test_failures := 0

func __get_stack_frame():
var me = get_script()
for s in get_stack():
if s.source == me.resource_path:
return s
return null

func __assert_pass():
test_passes += 1

func __assert_fail():
test_failures += 1
var s = __get_stack_frame()
if s != null:
print_rich ("[color=red] == FAILURE: In function %s() from '%s' on line %s[/color]" % [s.function, s.source, s.line])
else:
print_rich ("[color=red] == FAILURE (run with --debug to get more information!) ==[/color]")

func assert_equal(actual, expected):
if actual == expected:
__assert_pass()
else:
__assert_fail()
print (" |-> Expected '%s' but got '%s'" % [expected, actual])

func assert_true(v):
assert_equal(v, true)

func assert_false(v):
assert_equal(v, false)

func assert_not_equal(actual, expected):
if actual != expected:
__assert_pass()
else:
__assert_fail()
print (" |-> Expected '%s' NOT to equal '%s'" % [expected, actual])

func exit_with_status() -> void:
var success: bool = (test_failures == 0)
print ("")
print_rich ("[color=%s] ==== TESTS FINISHED ==== [/color]" % ("green" if success else "red"))
print ("")
print_rich (" PASSES: [color=green]%s[/color]" % test_passes)
print_rich (" FAILURES: [color=red]%s[/color]" % test_failures)
print ("")

if success:
print_rich("[color=green] ******** PASSED ******** [/color]")
else:
print_rich("[color=red] ******** FAILED ********[/color]")
print("")

get_tree().quit(0 if success else 1)
24 changes: 24 additions & 0 deletions test/run-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

GODOT=${GODOT:-godot}

END_STRING="==== TESTS FINISHED ===="
FAILURE_STRING="******** FAILED ********"

OUTPUT=$($GODOT --path demo --debug --headless --quit)
ERRCODE=$?

echo "$OUTPUT"
echo

if ! echo "$OUTPUT" | grep -e "$END_STRING" >/dev/null; then
echo "ERROR: Tests failed to complete"
exit 1
fi

if echo "$OUTPUT" | grep -e "$FAILURE_STRING" >/dev/null; then
exit 1
fi

# Success!
exit 0
Loading

0 comments on commit 4d720f1

Please sign in to comment.