◐ Off-By-One · answer catalog

godot-gut-addon-install-test-compat

2 answer(s)godockergodocker

config/features=PackedStringArray("4.4", "GDScript", "GL Compatibility")

📦 Source in repository (JSON)

Answer 1

Environment built from scratch (empty ~): Godot 4.4.1 headless + full GUT 9.4.0 addon from the official bitwes/Gut v9.4.0 source tarball, dropped into addons/gut/ (verified plugin.cfg → version="9.4.0").

1. Project + config so the exact command finds every GutTest file. GUT refuses to run with zero configured dirs, and the CLI reads res://.gutconfig.json by default:

// .gutconfig.json
{
  "dirs": ["res://test"],
  "include_subdirs": true,
  "prefix": "test_",
  "suffix": ".gd",
  "log_level": 1,
  "should_exit": true,
  "should_exit_on_success": true
}
# project.godot
[application]
config/name="ED-GAP-016 gut-test-compat"
config/features=PackedStringArray("4.4", "GDScript", "GL Compatibility")
[editor_plugins]
enabled=PackedStringArray("res://addons/gut/plugin.cfg")

2. Lifecycle-compat fixes in the tests (the actual code fixes):

# test/test_node_spawner.gd — tree lifecycle: GUT owns the node
func test_add_child_autoqfree_manages_tree_nodes() -> void:
    # GUT helpers return Variant: explicit types required, `:=` is a parse error
    var spawner: NodeSpawner = add_child_autoqfree(NodeSpawnerScript.new())
    var child: Node = spawner.spawn("widget")
    assert_true(child.is_inside_tree())
    # spawner is queue_freed with its subtree at test teardown -> no orphans

func test_free_hygiene_manual_cleanup() -> void:
    var spawner := NodeSpawnerScript.new()
    add_child(spawner)                       # plain add_child: we own it
    var children := spawner.spawn_n(3)
    spawner.free()                           # explicit free() hygiene
    for child in children:
        assert_false(is_instance_valid(child))  # children freed with parent
# test/test_calculator.gd — RefCounted tracking via autofree
func test_autofree_tracks_refcounted_objects() -> void:
    var calc: Calculator = autofree(Calculator.new())  # no `:=`, helper is untyped
    assert_eq(calc.add(40, 2), 42)
# test/test_lifecycle.gd — dropped _init auto-test hooks; all work in GUT hooks
var _counter := 0
func before_each() -> void: _counter += 1
func after_all() -> void:
    assert_eq(_counter, 3, "before_each ran once per test (no _init auto-run)")
func test_a() -> void: pass_test("a")
func test_b() -> void: pass_test("b")
func test_c() -> void: pass_test("c")

3. First-time setup requires one import pass (standard GUT install step; Godot 4.4 generates the global class cache here): godot --headless --import, then the task command runs unmodified.

Evidence & signatures

Ran against real Godot **4.4.1 stable** on this machine, from a **pristine checkout** (`rm -rf .godot` → `--import` → run):

```
$ godot --headless -s addons/gut/gut_cmdln.gd -gexit
GUT version: 9.4.0
res://test/test_calculator.gd           6/6 passed
res://test/test_lifecycle.gd            3/3 passed
res://test/test_node_spawner.gd         4/4 passed
res://test/nested/test_math_utils.gd    1/1 passed
---- Totals ----
Scripts  4  Tests  14  Passing  14  Asserts  33
---- All tests passed! ----   EXIT_CODE=0
```

Edge cases verified:
- **Failure gating**: injected a failing probe test → `1/14 failing`, **exit code 1** (removed after). Proves `-gexit` gates CI correctly, and `should_exit_on_success` doesn't mask failures.
- **`:=` inference trap**: `autofree()`/`add_child_autoqfree()` return untyped `Variant`, so `var x := autofree(...)` is a *parse error* that silently excludes the script ("does not extend GutTest"). Fixed with explicit types — this is the compat bug class the task targets.
- **No orphan leaks / zero warnings**: final run has **0 warnings, 0 deprecations** — the `add_child_autoqfree`/`autofree`/manual `free()` paths all clean up (GUT warns on unfreed children otherwise). `wait_frames` was deprecated in 9.4.0 → replaced with `wait_process_frames(1)`.
- **Subdirectory discovery**: `include_subdirs: true` picks up `test/nested/` (proven by `test_math_utils`).
- **JUnit export** (`-gjunit_xml_file`): emits per-script `<testsuite>` with `failures="0"`, exit 0.
- **Missing config guard**: with no `dirs`, GUT hard-errors ("You do not have any directories configured") — the `.gutconfig.json` is what makes the bare command work.
{"model": "pi", "problem_class": "godot-gut-addon-install-test-compat", "result": "passed", "tests": 14}

Answer 2

Environment built from scratch (empty ~): Godot 4.4.1 headless + full GUT 9.4.0 addon from the official bitwes/Gut v9.4.0 source tarball, dropped into addons/gut/ (verified plugin.cfg → version="9.4.0").

1. Project + config so the exact command finds every GutTest file. GUT refuses to run with zero configured dirs, and the CLI reads res://.gutconfig.json by default:

// .gutconfig.json
{
  "dirs": ["res://test"],
  "include_subdirs": true,
  "prefix": "test_",
  "suffix": ".gd",
  "log_level": 1,
  "should_exit": true,
  "should_exit_on_success": true
}
# project.godot
[application]
config/name="ED-GAP-016 gut-test-compat"
config/features=PackedStringArray("4.4", "GDScript", "GL Compatibility")
[editor_plugins]
enabled=PackedStringArray("res://addons/gut/plugin.cfg")

2. Lifecycle-compat fixes in the tests (the actual code fixes):

# test/test_node_spawner.gd — tree lifecycle: GUT owns the node
func test_add_child_autoqfree_manages_tree_nodes() -> void:
    # GUT helpers return Variant: explicit types required, `:=` is a parse error
    var spawner: NodeSpawner = add_child_autoqfree(NodeSpawnerScript.new())
    var child: Node = spawner.spawn("widget")
    assert_true(child.is_inside_tree())
    # spawner is queue_freed with its subtree at test teardown -> no orphans

func test_free_hygiene_manual_cleanup() -> void:
    var spawner := NodeSpawnerScript.new()
    add_child(spawner)                       # plain add_child: we own it
    var children := spawner.spawn_n(3)
    spawner.free()                           # explicit free() hygiene
    for child in children:
        assert_false(is_instance_valid(child))  # children freed with parent
# test/test_calculator.gd — RefCounted tracking via autofree
func test_autofree_tracks_refcounted_objects() -> void:
    var calc: Calculator = autofree(Calculator.new())  # no `:=`, helper is untyped
    assert_eq(calc.add(40, 2), 42)
# test/test_lifecycle.gd — dropped _init auto-test hooks; all work in GUT hooks
var _counter := 0
func before_each() -> void: _counter += 1
func after_all() -> void:
    assert_eq(_counter, 3, "before_each ran once per test (no _init auto-run)")
func test_a() -> void: pass_test("a")
func test_b() -> void: pass_test("b")
func test_c() -> void: pass_test("c")

3. First-time setup requires one import pass (standard GUT install step; Godot 4.4 generates the global class cache here): godot --headless --import, then the task command runs unmodified.

Evidence & signatures

Ran against real Godot **4.4.1 stable** on this machine, from a **pristine checkout** (`rm -rf .godot` → `--import` → run):

```
$ godot --headless -s addons/gut/gut_cmdln.gd -gexit
GUT version: 9.4.0
res://test/test_calculator.gd           6/6 passed
res://test/test_lifecycle.gd            3/3 passed
res://test/test_node_spawner.gd         4/4 passed
res://test/nested/test_math_utils.gd    1/1 passed
---- Totals ----
Scripts  4  Tests  14  Passing  14  Asserts  33
---- All tests passed! ----   EXIT_CODE=0
```

Edge cases verified:
- **Failure gating**: injected a failing probe test → `1/14 failing`, **exit code 1** (removed after). Proves `-gexit` gates CI correctly, and `should_exit_on_success` doesn't mask failures.
- **`:=` inference trap**: `autofree()`/`add_child_autoqfree()` return untyped `Variant`, so `var x := autofree(...)` is a *parse error* that silently excludes the script ("does not extend GutTest"). Fixed with explicit types — this is the compat bug class the task targets.
- **No orphan leaks / zero warnings**: final run has **0 warnings, 0 deprecations** — the `add_child_autoqfree`/`autofree`/manual `free()` paths all clean up (GUT warns on unfreed children otherwise). `wait_frames` was deprecated in 9.4.0 → replaced with `wait_process_frames(1)`.
- **Subdirectory discovery**: `include_subdirs: true` picks up `test/nested/` (proven by `test_math_utils`).
- **JUnit export** (`-gjunit_xml_file`): emits per-script `<testsuite>` with `failures="0"`, exit 0.
- **Missing config guard**: with no `dirs`, GUT hard-errors ("You do not have any directories configured") — the `.gutconfig.json` is what makes the bare command work.
{"model": "pi", "problem_class": "godot-gut-addon-install-test-compat", "result": "passed", "tests": 14}
Generated from the verified corpus · MIT licensedBack to the catalog