Add tests to validate Android plugin signal

This commit is contained in:
Anish Kumar
2026-05-05 02:19:59 +05:30
parent c3284cd696
commit d8cfa71eaf
11 changed files with 160 additions and 14 deletions
@@ -113,6 +113,28 @@ class GodotAppTest {
}
}
/**
* Runs test to validate android plugin signals.
*/
@Test
fun runPluginSignalTests() {
ActivityScenario.launch(GodotApp::class.java).use { scenario ->
scenario.onActivity { activity ->
val testPlugin = getTestPlugin()
assertNotNull(testPlugin)
Log.d(TAG, "Waiting for the Godot main loop to start...")
testPlugin.waitForGodotMainLoopStarted()
Log.d(TAG, "Running Android plugin signal tests...")
val result = testPlugin.runPluginSignalTests()
assertNotNull(result)
result.exceptionOrNull()?.let { throw it }
assertTrue(result.isSuccess)
}
}
}
/**
* Test implicit launch of the Godot app, and validates this resolves to the `GodotAppLauncher` activity alias.
*/
@@ -4,6 +4,10 @@
<meta-data
android:name="org.godotengine.plugin.v2.GodotAppInstrumentedTestPlugin"
android:value="com.godot.game.test.GodotAppInstrumentedTestPlugin"/>
<meta-data
android:name="org.godotengine.plugin.v2.SignalTestPlugin"
android:value="com.godot.game.test.SignalTestPlugin"/>
</application>
</manifest>
@@ -1,5 +1,21 @@
list=[{
"base": &"BaseTest",
"class": &"AndroidPluginSignalTests",
"icon": "",
"is_abstract": false,
"is_tool": false,
"language": &"GDScript",
"path": "res://test/android_plugin/signal_tests.gd"
}, {
"base": &"RefCounted",
"class": &"BaseTest",
"icon": "",
"is_abstract": true,
"is_tool": false,
"language": &"GDScript",
"path": "res://test/base_test.gd"
}, {
"base": &"BaseTest",
"class": &"FileAccessTests",
"icon": "",
"is_abstract": false,
@@ -14,12 +30,4 @@ list=[{
"is_tool": false,
"language": &"GDScript",
"path": "res://test/javaclasswrapper/java_class_wrapper_tests.gd"
}, {
"base": &"RefCounted",
"class": &"BaseTest",
"icon": "",
"is_abstract": true,
"is_tool": false,
"language": &"GDScript",
"path": "res://test/base_test.gd"
}]
@@ -3,13 +3,22 @@ extends Node2D
var _plugin_name = "GodotAppInstrumentedTestPlugin"
var _android_plugin
func _ready():
var _signal_test_plugin_name = "SignalTestPlugin"
var _signal_test_plugin
func _init():
# Verify plugin singleton in _init, since plugins should already be registered at this point.
if Engine.has_singleton(_plugin_name):
_android_plugin = Engine.get_singleton(_plugin_name)
_android_plugin.connect("launch_tests", _launch_tests)
_android_plugin.connect("update_quit_on_go_back", _update_quit_on_go_back)
else:
printerr("Couldn't find plugin " + _plugin_name)
if Engine.has_singleton(_signal_test_plugin_name):
_signal_test_plugin = Engine.get_singleton(_signal_test_plugin_name)
func _ready() -> void:
if not _android_plugin:
printerr("ERROR: Couldn't find plugin " + _plugin_name)
get_tree().quit()
func _launch_tests(test_label: String) -> void:
@@ -19,10 +28,12 @@ func _launch_tests(test_label: String) -> void:
test_instance = JavaClassWrapperTests.new()
"file_access_tests":
test_instance = FileAccessTests.new()
"android_plugin_signal_tests":
test_instance = AndroidPluginSignalTests.new(_signal_test_plugin)
if test_instance:
test_instance.__reset_tests()
test_instance.run_tests()
await test_instance.run_tests()
var incomplete_tests = test_instance._test_started - test_instance._test_completed
_android_plugin.onTestsCompleted(test_label, test_instance._test_completed, test_instance._test_assert_failures + incomplete_tests)
else:
@@ -16,7 +16,7 @@ compatibility/default_parent_skeleton_in_mesh_instance_3d=true
config/name="Godot App Instrumentation Tests"
run/main_scene="res://main.tscn"
config/features=PackedStringArray("4.6", "GL Compatibility")
config/features=PackedStringArray("4.7", "GL Compatibility")
config/icon="res://icon.svg"
[debug]
@@ -0,0 +1,60 @@
class_name AndroidPluginSignalTests
extends BaseTest
var _plugin: JNISingleton
const emission_test_signal = "emission_test_signal"
const launch_test_signal = "launch_tests"
signal emission_test_signal_emitted
func _init(plugin: JNISingleton) -> void:
_plugin = plugin
func run_tests():
print("Android plugin signal tests starting...")
__exec_test(test_plugin_exists)
__exec_test(test_signal_registration)
await __exec_test(test_signal_emission)
print("Android plugin signal tests completed.")
func test_plugin_exists() -> bool:
if _plugin == null:
printerr("ERROR: Couldn't find SignalTestPlugin plugin; _plugin is null")
return false
return true
func test_signal_registration() -> bool:
var signal_registered = _plugin.has_signal(emission_test_signal)
assert_true(signal_registered)
var launch_signal_registered = _plugin.has_signal(launch_test_signal)
assert_true(launch_signal_registered)
return true
func test_signal_emission() -> bool:
var err1 = _plugin.connect(emission_test_signal, _on_emission_test_signal_emitted)
assert_equal(err1, OK)
_plugin.triggerTestSignal1()
await emission_test_signal_emitted
# Test case: Same signal name, but different type and number of parameters
# The "launch_tests" signal is registered by both GodotAppInstrumentedTestPlugin and SignalTestPlugin.
# SignalTestPlugin emits it with a boolean and a string arguments, while GodotAppInstrumentedTestPlugin emits it with one string.
var err2 = _plugin.connect(launch_test_signal, _on_launch_tests_emitted)
assert_equal(err2, OK)
_plugin.triggerLaunchTestSignal()
await emission_test_signal_emitted
return true
func _on_emission_test_signal_emitted() -> void:
emission_test_signal_emitted.emit()
func _on_launch_tests_emitted(param1: bool, param2: String) -> void:
assert_true(param1)
assert_equal(param2, "second message")
emission_test_signal_emitted.emit()
@@ -0,0 +1 @@
uid://cq0mxqlbbug6r
@@ -9,7 +9,7 @@ var _test_assert_failures := 0
func __exec_test(test_func: Callable):
_test_started += 1
var ret = test_func.call()
var ret = await test_func.call()
if ret == true:
_test_completed += 1
@@ -52,6 +52,7 @@ class GodotAppInstrumentedTestPlugin(godot: Godot) : GodotPlugin(godot) {
private const val JAVACLASSWRAPPER_TESTS = "javaclasswrapper_tests"
private const val FILE_ACCESS_TESTS = "file_access_tests"
private const val PLUGIN_SIGNAL_TESTS = "android_plugin_signal_tests"
private val LAUNCH_TESTS_SIGNAL = SignalInfo("launch_tests", String::class.java)
private val UPDATE_QUIT_ON_GO_BACK_SIGNAL = SignalInfo("update_quit_on_go_back", java.lang.Boolean::class.java)
@@ -126,6 +127,10 @@ class GodotAppInstrumentedTestPlugin(godot: Godot) : GodotPlugin(godot) {
return launchTests(FILE_ACCESS_TESTS)
}
internal fun runPluginSignalTests(): Result<Any>? {
return launchTests(PLUGIN_SIGNAL_TESTS)
}
private fun launchTests(testLabel: String): Result<Any>? {
val latch = latches.getOrPut(testLabel) { CountDownLatch(1) }
emitSignal(LAUNCH_TESTS_SIGNAL, testLabel)
@@ -0,0 +1,35 @@
package com.godot.game.test
import android.util.Log
import org.godotengine.godot.Dictionary
import org.godotengine.godot.Godot
import org.godotengine.godot.plugin.GodotPlugin
import org.godotengine.godot.plugin.SignalInfo
import org.godotengine.godot.plugin.UsedByGodot
class SignalTestPlugin(godot: Godot) : GodotPlugin(godot) {
companion object {
private val EMISSION_TEST_SIGNAL = SignalInfo("emission_test_signal")
private val LAUNCH_TESTS_SIGNAL = SignalInfo("launch_tests", java.lang.Boolean::class.java, String::class.java)
}
override fun getPluginName() = "SignalTestPlugin"
override fun getPluginSignals(): Set<SignalInfo?> {
return setOf(
EMISSION_TEST_SIGNAL,
LAUNCH_TESTS_SIGNAL
)
}
@UsedByGodot
fun triggerTestSignal1() {
emitSignal(EMISSION_TEST_SIGNAL)
}
@UsedByGodot
fun triggerLaunchTestSignal() {
emitSignal(LAUNCH_TESTS_SIGNAL, true, "second message")
}
}