-
-
Notifications
You must be signed in to change notification settings - Fork 12
test: Add CLI commands to project #443
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
limbonaut
wants to merge
11
commits into
main
Choose a base branch
from
chore/cli-commands
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 6 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
d561fd8
Add CLI parser
limbonaut 910da99
Refactor CLI testing to use command-based approach
limbonaut 97f6a8e
improve CLI commands doc comments
limbonaut 0b8f0ff
Fix message-capture command
limbonaut 972f3b5
Refactor CLI commands for integration testing
limbonaut 8948657
Fixes
limbonaut efe1c5c
Fix await issues
limbonaut bf7c80a
Print EVENT_CAPTURED in crash test
limbonaut ac781a0
Sync command changes from other branch
limbonaut 26f81b1
Use `--es arg0 val0 --es arg1 val1` scheme for Android intents
limbonaut f81fc10
Fix crash-send context label in integration test
limbonaut File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| class_name AndroidCLIAdapter | ||
| extends RefCounted | ||
| ## Adapter class that converts Android intent extras into CLI-style arguments. | ||
| ## | ||
| ## Expects Android intent extras as key-value pairs: [br] | ||
| ## command: name of the command to run. [br] | ||
| ## arg0: first argument to the command. [br] | ||
| ## arg1: second argument, etc... [br] | ||
|
|
||
|
|
||
|
|
||
| # Reads Android intent extras and returns CLI-style arguments as PackedStringArray. | ||
| # Supports --es command and --es arg0, arg1, arg2... | ||
| static func get_command_argv() -> PackedStringArray: | ||
| var rv := PackedStringArray() | ||
| var extras: Dictionary = _get_android_intent_extras() | ||
|
|
||
| if extras.has("command"): | ||
| rv.append(extras["command"]) | ||
| for i in range(10): | ||
| var key := "arg%d" % i | ||
| if extras.has(key): | ||
| rv.append(extras[key]) | ||
| return rv | ||
|
|
||
|
|
||
| # Returns intent extras from AndroidRuntime (strings-only). | ||
| static func _get_android_intent_extras() -> Dictionary: | ||
| if not Engine.has_singleton("AndroidRuntime"): | ||
| return {} | ||
|
|
||
| var android_runtime = Engine.get_singleton("AndroidRuntime") | ||
| var activity = android_runtime.getActivity() | ||
| if not activity: | ||
| return {} | ||
| var intent = activity.getIntent() | ||
| if not intent: | ||
| return {} | ||
| var extras = intent.getExtras() | ||
| if not extras: | ||
| return {} | ||
|
|
||
| # Convert Java map to Godot Dictionary | ||
| var keys = extras.keySet().toArray() | ||
| var rv: Dictionary = {} | ||
| for i in range(keys.size()): | ||
| var key: String = keys[i].toString() | ||
| var raw_value = extras.get(key) | ||
| var value: String = raw_value.toString() if raw_value != null else "" | ||
| rv[key] = value | ||
|
|
||
| return rv |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| uid://bb5p3f8q35xjy |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| class_name CLICommands | ||
| extends Node | ||
| ## Contains command functions that can be executed via CLI. | ||
| ## | ||
| ## A command must return a POSIX-compliant integer exit code, where 0 indicates success. | ||
| ## Exit codes are typically in the range of 0–125.[br][br] | ||
| ## | ||
| ## Usage:[br] | ||
| ## godot --headless --path ./project -- COMMAND [ARGS...] | ||
| ## | ||
| ## [br][br] | ||
| ## Use "godot --headless --path ./project -- help" to list available commands. | ||
|
|
||
|
|
||
| var exit_code: int | ||
|
|
||
| var _parser := CLIParser.new() | ||
|
|
||
|
|
||
| func _ready() -> void: | ||
| _register_commands() | ||
|
|
||
|
|
||
| ## Checks and executes CLI commands if found. | ||
| ## Returns true if a command was executed (caller should handle app exit). | ||
| func check_and_execute_cli() -> bool: | ||
| var executed: bool = await _parser.check_and_execute_cli() | ||
| exit_code = _parser.exit_code | ||
| return executed | ||
|
|
||
|
|
||
| ## Registers all available commands with the parser. | ||
| func _register_commands() -> void: | ||
| _parser.add_command("help", _cmd_help, "Show available commands") | ||
| _parser.add_command("crash-capture", _cmd_crash_capture, "Generate a controlled crash for testing") | ||
| _parser.add_command("message-capture", _cmd_message_capture, "Capture a test message to Sentry") | ||
| _parser.add_command("run-tests", _cmd_run_tests, "Run unit tests") | ||
|
|
||
|
|
||
| ## Shows available commands and their arguments. | ||
| func _cmd_help() -> int: | ||
| print(_parser.generate_help()) | ||
| return 0 | ||
|
|
||
|
|
||
| ## Generates a controlled crash for testing. | ||
| func _cmd_crash_capture() -> int: | ||
| _init_sentry() | ||
| _add_integration_test_context("crash-capture") | ||
|
|
||
| await get_tree().create_timer(0.5).timeout | ||
|
|
||
| print("Triggering controlled crash...") | ||
|
|
||
| # NOTE: Borrowing UUID generation from SentryUser class. | ||
| var uuid_gen := SentryUser.new() | ||
| uuid_gen.generate_new_id() | ||
| SentrySDK.set_tag("test.crash_id", uuid_gen.id) | ||
|
|
||
| _print_test_result("crash-capture", true, "Pre-crash setup complete") | ||
| SentrySDK.add_breadcrumb(SentryBreadcrumb.create("About to trigger controlled crash")) | ||
|
|
||
| # Use the same crash method as the demo | ||
| SentrySDK._demo_helper_crash_app() | ||
| return 0 | ||
|
|
||
|
|
||
| ## Captures a test message to Sentry. | ||
| func _cmd_message_capture(p_message: String = "Integration test message", p_level: String = "info") -> int: | ||
| _init_sentry() | ||
| _add_integration_test_context("message-capture") | ||
limbonaut marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| await get_tree().create_timer(0.5).timeout | ||
|
|
||
| print("Capturing message: '%s' with level: %s" % [p_message, p_level]) | ||
|
|
||
| var level: SentrySDK.Level | ||
| match p_level.to_lower(): | ||
| "debug": level = SentrySDK.LEVEL_DEBUG | ||
| "info": level = SentrySDK.LEVEL_INFO | ||
| "warning", "warn": level = SentrySDK.LEVEL_WARNING | ||
| "error": level = SentrySDK.LEVEL_ERROR | ||
| "fatal": level = SentrySDK.LEVEL_FATAL | ||
| _: | ||
| printerr("Warning: Unknown level '%s', using INFO" % p_level) | ||
| level = SentrySDK.LEVEL_INFO | ||
|
|
||
| var event_id := SentrySDK.capture_message(p_message, level) | ||
| print("EVENT_CAPTURED: ", event_id) | ||
| _print_test_result("message-capture", true, "Test complete") | ||
| return 0 | ||
|
|
||
|
|
||
| func _cmd_run_tests(tests: String = "res://test/suites/") -> int: | ||
| if FileAccess.file_exists("res://test/util/test_runner.gd"): | ||
| print(">>> Initializing testing") | ||
| await get_tree().process_frame | ||
|
|
||
| var included_paths: PackedStringArray = tests.split(";", false) | ||
| if included_paths.is_empty(): | ||
| printerr("No test path provided.") | ||
| return 1 | ||
| print(" -- Tests included: ", included_paths) | ||
|
|
||
| # Add test runner node. | ||
| print(" -- Adding test runner...") | ||
| var test_runner: Node = load("res://test/util/test_runner.gd").new() | ||
| get_tree().root.add_child(test_runner) | ||
| for path in included_paths: | ||
| test_runner.include_tests(path) | ||
|
|
||
| # Wait for completion. | ||
| await test_runner.finished | ||
| print(">>> Test run complete with code: ", str(test_runner.result_code)) | ||
|
|
||
| return test_runner.result_code | ||
| else: | ||
| printerr("Error: Test runner not found") | ||
| return 1 | ||
|
|
||
|
|
||
| ## Initializes Sentry for integration testing. | ||
| func _init_sentry() -> void: | ||
| print("Initializing Sentry...") | ||
|
|
||
| SentrySDK.init(func(options: SentryOptions) -> void: | ||
| options.debug = true | ||
| options.release = "[email protected]" | ||
| options.environment = "integration-test" | ||
| ) | ||
|
|
||
| # Wait for Sentry to initialize | ||
| await get_tree().create_timer(0.3).timeout | ||
|
|
||
|
|
||
| ## Add additional context for integration tests. | ||
| func _add_integration_test_context(p_command: String) -> void: | ||
| SentrySDK.add_breadcrumb(SentryBreadcrumb.create("Integration test started")) | ||
|
|
||
| var user := SentryUser.new() | ||
| user.id = "test-user-123" | ||
| user.username = "ps-tester" | ||
| SentrySDK.set_user(user) | ||
|
|
||
| SentrySDK.set_tag("test.suite", "integration") | ||
| SentrySDK.set_tag("test.type", p_command) | ||
|
|
||
| SentrySDK.add_breadcrumb(SentryBreadcrumb.create("Context configuration finished")) | ||
|
|
||
|
|
||
| func _print_test_result(test_name: String, success: bool, message: String) -> void: | ||
| print("TEST_RESULT: {\"test\":\"%s\",\"success\":%s,\"message\":\"%s\"}" % [ | ||
| test_name, | ||
| success, | ||
| message | ||
| ]) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| uid://bg6afsun3ekyl |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.