Compare commits

...

44 Commits

Author SHA1 Message Date
9ba8847626 changing itch action
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 22s
Create tag and build when new code gets to main / Test (push) Failing after 5m21s
Create tag and build when new code gets to main / Export (push) Failing after 7m11s
2026-01-28 17:56:59 +01:00
51b7328310 changing itch action
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 24s
Create tag and build when new code gets to main / Export (push) Failing after 4s
Create tag and build when new code gets to main / Test (push) Has been cancelled
2026-01-28 17:54:06 +01:00
fdc352596d were getting there...
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 37s
Create tag and build when new code gets to main / Test (push) Successful in 5m43s
Create tag and build when new code gets to main / Export (push) Failing after 8m23s
2026-01-28 17:39:23 +01:00
89ba5cc985 trying the the fix on export again and trying to cache lfs objects again
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 40s
Create tag and build when new code gets to main / Test (push) Successful in 5m45s
Create tag and build when new code gets to main / Export (push) Failing after 7m56s
2026-01-28 17:25:28 +01:00
fdc79166a0 testing ci
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 22s
Create tag and build when new code gets to main / Test (push) Successful in 4m39s
Create tag and build when new code gets to main / Export (push) Failing after 5m16s
2026-01-28 16:22:38 +01:00
b84487336b testing ci 2026-01-28 16:20:44 +01:00
e4ab103c4d testing ci
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 23s
Create tag and build when new code gets to main / Export (push) Failing after 1m15s
Create tag and build when new code gets to main / Test (push) Successful in 4m43s
2026-01-28 15:33:56 +01:00
3b6cf0252b testing ci
Some checks failed
Create tag and build when new code gets to main / Test (push) Failing after 13s
Create tag and build when new code gets to main / BumpTag (push) Successful in 30s
Create tag and build when new code gets to main / Export (push) Failing after 45s
2026-01-28 15:29:31 +01:00
e908cd3085 testing ci
Some checks failed
Create tag and build when new code gets to main / Test (push) Failing after 14s
Create tag and build when new code gets to main / BumpTag (push) Failing after 24s
Create tag and build when new code gets to main / Export (push) Failing after 51s
2026-01-28 15:19:04 +01:00
6b23fdbd26 testing ci
Some checks failed
Create tag and build when new code gets to main / Test (push) Failing after 12s
Create tag and build when new code gets to main / BumpTag (push) Failing after 22s
Create tag and build when new code gets to main / Export (push) Failing after 1m29s
2026-01-28 15:13:11 +01:00
ea6258ff19 testing ci
Some checks failed
Create tag and build when new code gets to main / Test (push) Failing after 12s
Create tag and build when new code gets to main / BumpTag (push) Failing after 21s
Create tag and build when new code gets to main / Export (push) Failing after 53s
2026-01-28 15:05:12 +01:00
5e54f0f83b testing ci
Some checks failed
Create tag and build when new code gets to main / Test (push) Failing after 12s
Create tag and build when new code gets to main / BumpTag (push) Successful in 23s
Create tag and build when new code gets to main / Export (push) Failing after 55s
2026-01-28 15:00:24 +01:00
f00439a430 testing ci
Some checks failed
Create tag and build when new code gets to main / Test (push) Failing after 18s
Create tag and build when new code gets to main / BumpTag (push) Successful in 22s
Create tag and build when new code gets to main / Export (push) Failing after 54s
2026-01-28 13:08:24 +01:00
02ec230b3f testing ci
Some checks failed
Create tag and build when new code gets to main / Test (push) Failing after 13s
Create tag and build when new code gets to main / BumpTag (push) Successful in 25s
Create tag and build when new code gets to main / Export (push) Failing after 52s
2026-01-28 12:48:28 +01:00
867554b835 testing ci
Some checks failed
Create tag and build when new code gets to main / Test (push) Failing after 18s
Create tag and build when new code gets to main / BumpTag (push) Successful in 30s
Create tag and build when new code gets to main / Export (push) Failing after 47s
2026-01-28 12:45:13 +01:00
a4835eeb3c testing ci
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 22s
Create tag and build when new code gets to main / Export (push) Failing after 7s
Create tag and build when new code gets to main / Test (push) Failing after 6m21s
2026-01-28 12:34:26 +01:00
55b877226e testing ci
Some checks failed
Create tag and build when new code gets to main / Export (push) Failing after 1m31s
2026-01-28 10:49:08 +01:00
d7d33d0dac testing ci
Some checks failed
Create tag and build when new code gets to main / Export (push) Failing after 1m28s
2026-01-28 10:47:15 +01:00
f92c6d282f testing ci
Some checks failed
Create tag and build when new code gets to main / Export (push) Failing after 1m33s
2026-01-28 10:43:32 +01:00
2d41523668 testing ci
Some checks failed
Create tag and build when new code gets to main / Export (push) Failing after 1m24s
2026-01-28 10:38:56 +01:00
34b04a365a testing ci
Some checks failed
Create tag and build when new code gets to main / Export (push) Has been cancelled
2026-01-28 10:38:21 +01:00
d0ac644e14 testing ci
Some checks failed
Create tag and build when new code gets to main / Export (push) Failing after 9m17s
2026-01-28 10:28:07 +01:00
510a3200d1 testing ci
Some checks failed
Create tag and build when new code gets to main / Export (push) Failing after 9m32s
2026-01-28 10:17:05 +01:00
bef601941c testing ci
Some checks failed
Create tag and build when new code gets to main / Export (push) Failing after 1m24s
2026-01-28 10:14:25 +01:00
b6605d6293 testing ci
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 1m11s
Create tag and build when new code gets to main / Export (push) Failing after 1m29s
2026-01-28 10:10:51 +01:00
1b6742ea45 testing ci
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 22s
Create tag and build when new code gets to main / Export (push) Failing after 1m27s
2026-01-28 10:06:32 +01:00
405e487881 testing ci
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 37s
Create tag and build when new code gets to main / Export (push) Failing after 2m18s
Create tag and build when new code gets to main / Test (push) Successful in 6m42s
2026-01-28 09:53:22 +01:00
af1f6da98d updating project files
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 31s
Create tag and build when new code gets to main / Test (push) Has been cancelled
Create tag and build when new code gets to main / Export (push) Has been cancelled
2026-01-28 09:50:10 +01:00
119850a7b4 testing ci
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 23s
Create tag and build when new code gets to main / Export (push) Failing after 3m29s
Create tag and build when new code gets to main / Test (push) Failing after 9m11s
2026-01-28 09:33:21 +01:00
b198aba09b updating state chart addon
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 21s
Create tag and build when new code gets to main / Export (push) Failing after 1m28s
2026-01-27 19:13:53 +01:00
37165d1562 fuck tests I guess
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 23s
Create tag and build when new code gets to main / Export (push) Failing after 1m57s
2026-01-27 19:04:52 +01:00
5684561b66 fuck tests I guess 2026-01-27 19:04:34 +01:00
2678cac0e6 FUUUUCK FUUUUUUUUUUCK
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 21s
Create tag and build when new code gets to main / Export (push) Failing after 2m1s
2026-01-27 19:01:05 +01:00
230b409abe FUUUUCK FUUUUUUUUUUCK
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 20s
Create tag and build when new code gets to main / Export (push) Failing after 15s
2026-01-27 18:56:12 +01:00
a6c80206c9 trying to fix syntax error in checkout action
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 22s
Create tag and build when new code gets to main / Export (push) Failing after 40s
2026-01-27 18:53:03 +01:00
1a73f23670 trying to fix syntax error in checkout action
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 22s
Create tag and build when new code gets to main / Export (push) Failing after 15s
2026-01-27 18:52:04 +01:00
38e62dcbb3 trying to fix syntax error in checkout action
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 35s
Create tag and build when new code gets to main / Export (push) Failing after 1m8s
2026-01-27 18:48:37 +01:00
19bdc143c1 trying to fix syntax error in checkout action
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 2m3s
Create tag and build when new code gets to main / Export (push) Failing after 1m37s
2026-01-27 18:44:03 +01:00
1709554e72 changing GDUnit dependency because 4.6 not yet supported
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 22s
Create tag and build when new code gets to main / Export (push) Failing after 3m44s
2026-01-27 18:07:54 +01:00
5c2e9408c5 caching improvements
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 26s
Create tag and build when new code gets to main / Test (push) Failing after 2m6s
Create tag and build when new code gets to main / Export (push) Has been skipped
2026-01-27 17:59:10 +01:00
caeae26a09 fixed camera and sword animation issue and upgraded to Godot 4.6
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 22s
Create tag and build when new code gets to main / Test (push) Failing after 2m10s
Create tag and build when new code gets to main / Export (push) Has been skipped
2026-01-27 17:47:19 +01:00
056a68b0ad small death animation and toolbox
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 35s
Create tag and build when new code gets to main / Test (push) Successful in 6m20s
Create tag and build when new code gets to main / Export (push) Successful in 7m20s
2026-01-27 16:42:31 +01:00
f1f0febf29 death and restart menu working 2026-01-27 15:11:43 +01:00
916a6e7153 some editor icons and a base level scene
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 33s
Create tag and build when new code gets to main / Test (push) Successful in 6m19s
Create tag and build when new code gets to main / Export (push) Successful in 7m39s
2026-01-27 10:35:35 +01:00
2340 changed files with 44452 additions and 8046 deletions

View File

@@ -10,49 +10,33 @@ on:
- "**"
env:
GODOT_VERSION: 4.6
GAME_NAME: MovementTests
ITCHIO_USERNAME: Minimata
ITCHIO_GAMEID: MovementTests
jobs:
Export:
runs-on: ubuntu-latest
runs-on: godot
steps:
- name: Install node, xvfb and curl
run: |
apt update && apt -y install curl nodejs xvfb
- name: Checkout with LFS
uses: https://git.game-dev.space/minimata/checkout-with-lfs.git@main
- name: Checkout
uses: actions/checkout@v6
with:
lfs: false
persist-credentials: true
- name: Checkout LFS
run: |
git lfs install --local
AUTH=$(git config http.${{ gitea.server_url }}/.extraheader)
AUTH_FILE=$(git config includeif.gitdir:/workspace/${{ gitea.repository }}/.git.path)
git config -f $AUTH_FILE --unset http.${{ gitea.server_url }}/.extraheader
git config -f $AUTH_FILE http.${{ gitea.server_url }}/${{ gitea.repository }}.git/info/lfs/objects/batch.extraheader "$AUTH"
git lfs pull
- name: Run tests
uses: godot-gdunit-labs/gdUnit4-action@v1
with:
godot-version: '4.5.1'
godot-version: ${GODOT_VERSION}
godot-net: true
godot-force-mono: true
dotnet-version: 'net9.0'
version: 'v6.0.3'
paths: |
res://tests/
timeout: 1
publish-report: false
upload-report: false
- name: Upload test report
uses: actions/upload-artifact@v3-node20
uses: actions/upload-artifact@v6
with:
name: Test Report
path: ${{ github.workspace }}/reports/test-result.html

View File

@@ -8,6 +8,7 @@ on:
- "**"
env:
GODOT_VERSION: 4.6
GAME_NAME: MovementTests
ITCHIO_USERNAME: Minimata
ITCHIO_GAMEID: MovementTests
@@ -21,6 +22,7 @@ jobs:
- name: Check out repository code
uses: actions/checkout@v4
with:
token: ${{ secrets.TOKEN }}
lfs: false
- name: Remove buggy pre-push hook
run: |
@@ -37,35 +39,20 @@ jobs:
DEFAULT_BUMP: patch
Test:
runs-on: ubuntu-latest
runs-on: godot
# env:
# RUNNER_TOOL_CACHE: /toolcache # Runner Tool Cache
steps:
- name: Install node, xvfb and curl
run: |
apt update && apt -y install curl nodejs xvfb
- name: Checkout
uses: actions/checkout@v6
with:
lfs: false
persist-credentials: true
- name: Checkout LFS
run: |
git lfs install --local
AUTH=$(git config http.${{ gitea.server_url }}/.extraheader)
AUTH_FILE=$(git config includeif.gitdir:/workspace/${{ gitea.repository }}/.git.path)
git config -f $AUTH_FILE --unset http.${{ gitea.server_url }}/.extraheader
git config -f $AUTH_FILE http.${{ gitea.server_url }}/${{ gitea.repository }}.git/info/lfs/objects/batch.extraheader "$AUTH"
git lfs pull
- name: Checkout with LFS
uses: https://git.game-dev.space/minimata/checkout-with-lfs.git@main
- name: Run tests
uses: godot-gdunit-labs/gdUnit4-action@v1
with:
godot-version: '4.5.1'
godot-version: ${GODOT_VERSION}
godot-net: true
godot-force-mono: true
dotnet-version: 'net9.0'
version: 'v6.0.3'
paths: |
res://tests/
timeout: 1
@@ -73,58 +60,55 @@ jobs:
upload-report: false
- name: Upload test report
uses: actions/upload-artifact@v3-node20
uses: actions/upload-artifact@v6
with:
name: Test Report
path: ${{ github.workspace }}/reports/test-result.html
Export:
runs-on: ubuntu-latest
runs-on: godot
needs:
- BumpTag
- Test # Wait for tests to finish
container:
image: barichello/godot-ci:mono-4.5
steps:
- name: Install node, curl and zip
run: |
apt update && apt -y install curl zip nodejs
- name: Checkout with LFS
uses: https://git.game-dev.space/minimata/checkout-with-lfs.git@main
- name: Checkout
uses: actions/checkout@v6
- name: Setup Godot
id: setup-godot
uses: https://git.game-dev.space/minimata/setup-godot.git@main
with:
lfs: false
persist-credentials: true
- name: Checkout LFS
run: |
git lfs install --local
AUTH=$(git config http.${{ gitea.server_url }}/.extraheader)
AUTH_FILE=$(git config includeif.gitdir:/workspace/${{ gitea.repository }}/.git.path)
git config -f $AUTH_FILE --unset http.${{ gitea.server_url }}/.extraheader
git config -f $AUTH_FILE http.${{ gitea.server_url }}/${{ gitea.repository }}.git/info/lfs/objects/batch.extraheader "$AUTH"
git lfs pull
- name: Remove GDUnit addon folder because it breaks the build
godot-version: '4.6'
dotnet-version: 'net9.0'
- name: Remove GDUnit addon
run: |
rm -rf ${{ gitea.workspace }}/addons/gdUnit4
- name: Import resources and build solution
run: |
godot --headless --editor --build-solutions --quit --import --path $PWD
- name: Build Windows
run: |
mkdir -v -p build/windows
godot --headless --verbose --build-solutions --export-release "Windows Desktop" build/windows/${{ env.GAME_NAME }}.exe
${{ steps.setup-godot.outputs.godot_bin }} --headless --verbose --export-release "Windows Desktop" build/windows/${{ env.GAME_NAME }}.exe
zip -r Windows.zip build/windows
- name: Upload to Itch
uses: KikimoraGames/itch-publish@v0.0.3
ls -la
pwd
echo ${{ github.workspace }}
- name: Upload build to Itch
uses: https://github.com/Oval-Tutu/publish-to-itch-with-butler.git@main
with:
butlerApiKey: ${{ secrets.BUTLER_TOKEN }}
itchUsername: ${{ env.ITCHIO_USERNAME }}
itchGameId: ${{ env.ITCHIO_GAMEID }}
buildNumber: ${{ needs.BumpTag.outputs.tag_name }}
gameData: Windows.zip
buildChannel: windows
api-key: ${{ secrets.BUTLER_TOKEN }}
itch_user: ${{ env.ITCHIO_USERNAME }}
itch_game: ${{ env.ITCHIO_GAMEID }}
channel: windows
package: Windows.zip
version: ${{ needs.BumpTag.outputs.tag_name }}
# - name: Upload to Itch
# uses: KikimoraGames/itch-publish@v0.0.3
# with:
# butlerApiKey: ${{ secrets.BUTLER_TOKEN }}
# itchUsername: ${{ env.ITCHIO_USERNAME }}
# itchGameId: ${{ env.ITCHIO_GAMEID }}
# buildNumber: 0.1.0 # ${{ needs.BumpTag.outputs.tag_name }}
# gameData: ${{ github.workspace }}/Windows.zip
# buildChannel: windows

View File

@@ -8,6 +8,7 @@ on:
- "**"
env:
GODOT_VERSION: 4.6
GAME_NAME: MovementTests
ITCHIO_USERNAME: Minimata
ITCHIO_GAMEID: MovementTests
@@ -26,35 +27,20 @@ jobs:
separator: '/'
Release:
runs-on: ubuntu-latest
runs-on: godot
if: ${{ contains(gitea.ref_name, 'release/') }}
needs: ReleaseName
container:
image: barichello/godot-ci:mono-4.5
steps:
- name: Install node, curl and zip
run: |
apt update && apt -y install curl zip nodejs
- name: Checkout with LFS
uses: https://git.game-dev.space/minimata/checkout-with-lfs.git@main
- name: Checkout
uses: actions/checkout@v6
- name: Setup Godot
id: setup-godot
uses: https://git.game-dev.space/minimata/setup-godot.git@main
with:
lfs: false
persist-credentials: true
- name: Checkout LFS
run: |
git lfs install --local
AUTH=$(git config http.${{ gitea.server_url }}/.extraheader)
AUTH_FILE=$(git config includeif.gitdir:/workspace/${{ gitea.repository }}/.git.path)
git config -f $AUTH_FILE --unset http.${{ gitea.server_url }}/.extraheader
git config -f $AUTH_FILE http.${{ gitea.server_url }}/${{ gitea.repository }}.git/info/lfs/objects/batch.extraheader "$AUTH"
git lfs pull
- name: Import resources and build solution
run: |
godot --headless --editor --build-solutions --quit --import --path $PWD
godot-version: '4.6'
dotnet-version: 'net9.0'
- name: Build Windows
run: |

View File

@@ -1,4 +1,4 @@
<Project Sdk="Godot.NET.Sdk/4.5.0">
<Project Sdk="Godot.NET.Sdk/4.6.0">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
@@ -131,9 +131,9 @@
</ItemGroup>
<!-- gdUnit4 package dependencies -->
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0"/>
<PackageReference Include="gdUnit4.api" Version="5.1.0-rc3"/>
<PackageReference Include="gdUnit4.test.adapter" Version="3.0.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="gdUnit4.api" Version="5.1.0-rc3" />
<PackageReference Include="gdUnit4.test.adapter" Version="3.0.0" />
<PackageReference Include="gdUnit4.analyzers" Version="1.0.0">
<PrivateAssets>none</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

142
Movement tests.csproj.old.1 Normal file
View File

@@ -0,0 +1,142 @@
<Project Sdk="Godot.NET.Sdk/4.5.0">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<RootNamespace>Movementtests</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Content Include=".runsettings" />
<Content Include="export_presets.cfg" />
<Content Include="menus\assets\git_logo\Git-Logo-2Color.png" />
<Content Include="menus\assets\git_logo\Git-Logo-2Color.png.import" />
<Content Include="menus\assets\git_logo\LICENSE.txt" />
<Content Include="menus\assets\godot_engine_logo\LICENSE.txt" />
<Content Include="menus\assets\godot_engine_logo\logo_vertical_color_dark.png" />
<Content Include="menus\assets\godot_engine_logo\logo_vertical_color_dark.png.import" />
<Content Include="menus\assets\icon.png" />
<Content Include="menus\assets\icon.png.import" />
<Content Include="menus\ATTRIBUTION.md" />
<Content Include="menus\resources\themes\expedition.tres" />
<Content Include="menus\resources\themes\gravity.tres" />
<Content Include="menus\resources\themes\grow.tres" />
<Content Include="menus\resources\themes\lab.tres" />
<Content Include="menus\resources\themes\lore.tres" />
<Content Include="menus\resources\themes\steal_this_theme.tres" />
<Content Include="menus\scenes\credits\scrollable_credits.gd" />
<Content Include="menus\scenes\credits\scrollable_credits.gd.uid" />
<Content Include="menus\scenes\credits\scrollable_credits.tscn" />
<Content Include="menus\scenes\credits\scrolling_credits.gd" />
<Content Include="menus\scenes\credits\scrolling_credits.gd.uid" />
<Content Include="menus\scenes\credits\scrolling_credits.tscn" />
<Content Include="menus\scenes\end_credits\end_credits.gd" />
<Content Include="menus\scenes\end_credits\end_credits.gd.uid" />
<Content Include="menus\scenes\end_credits\end_credits.tscn" />
<Content Include="menus\scenes\game_scene\configurable_sub_viewport.gd" />
<Content Include="menus\scenes\game_scene\configurable_sub_viewport.gd.uid" />
<Content Include="menus\scenes\game_scene\game_ui.tscn" />
<Content Include="menus\scenes\game_scene\input_display_label.gd" />
<Content Include="menus\scenes\game_scene\input_display_label.gd.uid" />
<Content Include="menus\scenes\game_scene\levels\level.gd" />
<Content Include="menus\scenes\game_scene\levels\level.gd.uid" />
<Content Include="menus\scenes\game_scene\levels\level_1.tscn" />
<Content Include="menus\scenes\game_scene\levels\level_2.tscn" />
<Content Include="menus\scenes\game_scene\levels\level_3.tscn" />
<Content Include="menus\scenes\game_scene\tutorials\tutorial_1.tscn" />
<Content Include="menus\scenes\game_scene\tutorials\tutorial_2.tscn" />
<Content Include="menus\scenes\game_scene\tutorials\tutorial_3.tscn" />
<Content Include="menus\scenes\game_scene\tutorial_manager.gd" />
<Content Include="menus\scenes\game_scene\tutorial_manager.gd.uid" />
<Content Include="menus\scenes\loading_screen\level_loading_screen.tscn" />
<Content Include="menus\scenes\loading_screen\loading_screen.gd" />
<Content Include="menus\scenes\loading_screen\loading_screen.gd.uid" />
<Content Include="menus\scenes\loading_screen\loading_screen.tscn" />
<Content Include="menus\scenes\loading_screen\loading_screen_with_shader_caching.gd" />
<Content Include="menus\scenes\loading_screen\loading_screen_with_shader_caching.gd.uid" />
<Content Include="menus\scenes\loading_screen\loading_screen_with_shader_caching.tscn" />
<Content Include="menus\scenes\menus\level_select_menu\level_select_menu.gd" />
<Content Include="menus\scenes\menus\level_select_menu\level_select_menu.gd.uid" />
<Content Include="menus\scenes\menus\level_select_menu\level_select_menu.tscn" />
<Content Include="menus\scenes\menus\main_menu\main_menu.gd" />
<Content Include="menus\scenes\menus\main_menu\main_menu.gd.uid" />
<Content Include="menus\scenes\menus\main_menu\main_menu.tscn" />
<Content Include="menus\scenes\menus\main_menu\main_menu_with_animations.gd" />
<Content Include="menus\scenes\menus\main_menu\main_menu_with_animations.gd.uid" />
<Content Include="menus\scenes\menus\main_menu\main_menu_with_animations.tscn" />
<Content Include="menus\scenes\menus\options_menu\audio\audio_input_option_control.gd" />
<Content Include="menus\scenes\menus\options_menu\audio\audio_input_option_control.gd.uid" />
<Content Include="menus\scenes\menus\options_menu\audio\audio_input_option_control.tscn" />
<Content Include="menus\scenes\menus\options_menu\audio\audio_options_menu.gd" />
<Content Include="menus\scenes\menus\options_menu\audio\audio_options_menu.gd.uid" />
<Content Include="menus\scenes\menus\options_menu\audio\audio_options_menu.tscn" />
<Content Include="menus\scenes\menus\options_menu\game\game_options_menu.gd" />
<Content Include="menus\scenes\menus\options_menu\game\game_options_menu.gd.uid" />
<Content Include="menus\scenes\menus\options_menu\game\game_options_menu.tscn" />
<Content Include="menus\scenes\menus\options_menu\game\reset_game_control\reset_game_control.gd" />
<Content Include="menus\scenes\menus\options_menu\game\reset_game_control\reset_game_control.gd.uid" />
<Content Include="menus\scenes\menus\options_menu\game\reset_game_control\reset_game_control.tscn" />
<Content Include="menus\scenes\menus\options_menu\input\input_extras_menu.tscn" />
<Content Include="menus\scenes\menus\options_menu\input\input_options_menu.gd" />
<Content Include="menus\scenes\menus\options_menu\input\input_options_menu.gd.uid" />
<Content Include="menus\scenes\menus\options_menu\input\input_options_menu.tscn" />
<Content Include="menus\scenes\menus\options_menu\input\input_options_menu_with_mouse_sensitivity.tscn" />
<Content Include="menus\scenes\menus\options_menu\master_options_menu.gd" />
<Content Include="menus\scenes\menus\options_menu\master_options_menu.gd.uid" />
<Content Include="menus\scenes\menus\options_menu\master_options_menu.tscn" />
<Content Include="menus\scenes\menus\options_menu\master_options_menu_with_tabs.tscn" />
<Content Include="menus\scenes\menus\options_menu\mini_options_menu.gd" />
<Content Include="menus\scenes\menus\options_menu\mini_options_menu.gd.uid" />
<Content Include="menus\scenes\menus\options_menu\mini_options_menu.tscn" />
<Content Include="menus\scenes\menus\options_menu\mini_options_menu_with_reset.gd" />
<Content Include="menus\scenes\menus\options_menu\mini_options_menu_with_reset.gd.uid" />
<Content Include="menus\scenes\menus\options_menu\mini_options_menu_with_reset.tscn" />
<Content Include="menus\scenes\menus\options_menu\video\video_options_menu.gd" />
<Content Include="menus\scenes\menus\options_menu\video\video_options_menu.gd.uid" />
<Content Include="menus\scenes\menus\options_menu\video\video_options_menu.tscn" />
<Content Include="menus\scenes\menus\options_menu\video\video_options_menu_with_extras.tscn" />
<Content Include="menus\scenes\opening\opening.gd" />
<Content Include="menus\scenes\opening\opening.gd.uid" />
<Content Include="menus\scenes\opening\opening.tscn" />
<Content Include="menus\scenes\opening\opening_with_logo.tscn" />
<Content Include="menus\scenes\overlaid_menus\game_won_menu.gd" />
<Content Include="menus\scenes\overlaid_menus\game_won_menu.gd.uid" />
<Content Include="menus\scenes\overlaid_menus\game_won_menu.tscn" />
<Content Include="menus\scenes\overlaid_menus\level_lost_menu.gd" />
<Content Include="menus\scenes\overlaid_menus\level_lost_menu.gd.uid" />
<Content Include="menus\scenes\overlaid_menus\level_lost_menu.tscn" />
<Content Include="menus\scenes\overlaid_menus\level_won_menu.gd" />
<Content Include="menus\scenes\overlaid_menus\level_won_menu.gd.uid" />
<Content Include="menus\scenes\overlaid_menus\level_won_menu.tscn" />
<Content Include="menus\scenes\overlaid_menus\mini_options_overlaid_menu.tscn" />
<Content Include="menus\scenes\overlaid_menus\overlaid_menu.gd" />
<Content Include="menus\scenes\overlaid_menus\overlaid_menu.gd.uid" />
<Content Include="menus\scenes\overlaid_menus\overlaid_menu.tscn" />
<Content Include="menus\scenes\overlaid_menus\overlaid_menu_container.gd" />
<Content Include="menus\scenes\overlaid_menus\overlaid_menu_container.gd.uid" />
<Content Include="menus\scenes\overlaid_menus\overlaid_menu_container.tscn" />
<Content Include="menus\scenes\overlaid_menus\pause_menu.gd" />
<Content Include="menus\scenes\overlaid_menus\pause_menu.gd.uid" />
<Content Include="menus\scenes\overlaid_menus\pause_menu.tscn" />
<Content Include="menus\scripts\game_state.gd" />
<Content Include="menus\scripts\game_state.gd.uid" />
<Content Include="menus\scripts\level_list_and_state_manager.gd" />
<Content Include="menus\scripts\level_list_and_state_manager.gd.uid" />
<Content Include="menus\scripts\level_state.gd" />
<Content Include="menus\scripts\level_state.gd.uid" />
</ItemGroup>
<ItemGroup>
<Folder Include="addons\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="RustyOptions" Version="0.10.1" />
</ItemGroup>
<!-- gdUnit4 package dependencies -->
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0"/>
<PackageReference Include="gdUnit4.api" Version="5.1.0-rc3"/>
<PackageReference Include="gdUnit4.test.adapter" Version="3.0.0"/>
<PackageReference Include="gdUnit4.analyzers" Version="1.0.0">
<PrivateAssets>none</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -1 +1 @@
uid://do2c2faoehm61
uid://i04mdqgjjfsa

View File

@@ -1 +1 @@
uid://bretpek2ehht4
uid://lcya8t25hn0j

View File

@@ -3,5 +3,5 @@
name="gdUnit4"
description="Unit Testing Framework for Godot Scripts"
author="Mike Schulze"
version="6.0.3"
version="6.1.0"
script="plugin.gd"

View File

@@ -1,23 +1,29 @@
@tool
extends EditorPlugin
# We need to define manually the slot id's, to be downwards compatible
const CONTEXT_SLOT_FILESYSTEM: int = 1 # EditorContextMenuPlugin.CONTEXT_SLOT_FILESYSTEM
const CONTEXT_SLOT_SCRIPT_EDITOR: int = 2 # EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR
var _gd_inspector: Control
var _gd_console: Control
var _gd_filesystem_context_menu: Variant
var _gd_scripteditor_context_menu: Variant
var _filesystem_context_menu: EditorContextMenuPlugin
var _editor_context_menu: EditorContextMenuPlugin
var _editor_code_context_menu: EditorContextMenuPlugin
func _enter_tree() -> void:
var inferred_declaration: int = ProjectSettings.get_setting("debug/gdscript/warnings/inferred_declaration")
var exclude_addons: bool = ProjectSettings.get_setting("debug/gdscript/warnings/exclude_addons")
if !exclude_addons and inferred_declaration != 0:
var is_gdunit_excluded_warnings: bool = false
if Engine.get_version_info().hex >= 0x40600:
var dirctrory_rules: Dictionary = ProjectSettings.get_setting("debug/gdscript/warnings/directory_rules")
if dirctrory_rules.has("res://addons/gdUnit4") and dirctrory_rules["res://addons/gdUnit4"] == 0:
is_gdunit_excluded_warnings = true
else:
is_gdunit_excluded_warnings = ProjectSettings.get_setting("debug/gdscript/warnings/exclude_addons")
if !is_gdunit_excluded_warnings and inferred_declaration != 0:
printerr("GdUnit4: 'inferred_declaration' is set to Warning/Error!")
printerr("GdUnit4 is not 'inferred_declaration' save, you have to excluded addons (debug/gdscript/warnings/exclude_addons)")
if Engine.get_version_info().hex >= 0x40600:
printerr("GdUnit4 is not 'inferred_declaration' save, you have to excluded the addon (debug/gdscript/warnings/directory_rules)")
else:
printerr("GdUnit4 is not 'inferred_declaration' save, you have to excluded addons (debug/gdscript/warnings/exclude_addons)")
printerr("Loading GdUnit4 Plugin failed.")
return
@@ -73,36 +79,21 @@ func check_running_in_test_env() -> bool:
func _add_context_menus() -> void:
if Engine.get_version_info().hex >= 0x40400:
# With Godot 4.4 we have to use the 'add_context_menu_plugin' to register editor context menus
_gd_filesystem_context_menu = _preload_gdx_script("res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandlerV44.gdx")
call_deferred("add_context_menu_plugin", CONTEXT_SLOT_FILESYSTEM, _gd_filesystem_context_menu)
# the CONTEXT_SLOT_SCRIPT_EDITOR is adding to the script panel instead of script editor see https://github.com/godotengine/godot/pull/100556
#_gd_scripteditor_context_menu = _preload("res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandlerV44.gdx")
#call_deferred("add_context_menu_plugin", CONTEXT_SLOT_SCRIPT_EDITOR, _gd_scripteditor_context_menu)
# so we use the old hacky way to add the context menu
_gd_inspector.add_child(preload("res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd").new())
else:
# TODO Delete it if the minimum requirement for the plugin is set to Godot 4.4.
_gd_inspector.add_child(preload("res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd").new())
_gd_inspector.add_child(preload("res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd").new())
_filesystem_context_menu = preload("res://addons/gdUnit4/src/ui/menu/GdUnitEditorFileSystemContextMenuHandler.gd").new()
_editor_context_menu = preload("res://addons/gdUnit4/src/ui/menu/GdUnitScriptEditorContextMenuHandler.gd").new()
_editor_code_context_menu = preload("res://addons/gdUnit4/src/ui/menu/GdUnitScriptEditorContextMenuHandler.gd").new()
add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_FILESYSTEM, _filesystem_context_menu)
add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _editor_context_menu)
add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, _editor_code_context_menu)
func _remove_context_menus() -> void:
if is_instance_valid(_gd_filesystem_context_menu):
call_deferred("remove_context_menu_plugin", _gd_filesystem_context_menu)
if is_instance_valid(_gd_scripteditor_context_menu):
call_deferred("remove_context_menu_plugin", _gd_scripteditor_context_menu)
func _preload_gdx_script(script_path: String) -> Variant:
var script: GDScript = GDScript.new()
script.source_code = GdUnitFileAccess.resource_as_string(script_path)
script.take_over_path(script_path)
var err :Error = script.reload()
if err != OK:
push_error("Can't create context menu %s, error: %s" % [script_path, error_string(err)])
return script.new()
if is_instance_valid(_filesystem_context_menu):
remove_context_menu_plugin(_filesystem_context_menu)
if is_instance_valid(_editor_context_menu):
remove_context_menu_plugin(_editor_context_menu)
if is_instance_valid(_editor_code_context_menu):
remove_context_menu_plugin(_editor_code_context_menu)
func _on_resource_saved(resource: Resource) -> void:

View File

@@ -1 +1 @@
uid://bc4fimf6ynr5d
uid://8wxua8uw7x7k

View File

@@ -1 +1 @@
uid://buiskkw1yyuw3
uid://duj13ipuced2q

View File

@@ -1 +1 @@
uid://drfioswpw8u2u
uid://wd2ydmpylh1e

View File

@@ -1 +1 @@
uid://byeulsiqvaugq
uid://bwaeyokx1kgfd

View File

@@ -1 +1 @@
uid://bmy2nu4w22wia
uid://qqwrtj2mj3xi

View File

@@ -1 +1 @@
uid://c1jp2le4lldby
uid://fnv7o85xbiiq

View File

@@ -1 +1 @@
uid://bftfpffmfb1il
uid://bav21rax06rdf

View File

@@ -1 +1 @@
uid://dkap7kpfh2bhg
uid://b750alnjl31nv

View File

@@ -1 +1 @@
uid://8s1lymhdvlpu
uid://cfqx4148ov21q

View File

@@ -1 +1 @@
uid://x54vf4fue301
uid://hqhd4a063x3n

View File

@@ -1 +1 @@
uid://vt1hx0i6pg4h
uid://xsowfqnkhk7j

View File

@@ -1 +1 @@
uid://l487wamffax1
uid://crdh3ctgqxrqi

View File

@@ -1 +1 @@
uid://bvvptcdhi1g14
uid://bqguqoaa43uev

View File

@@ -1 +1 @@
uid://bwkv3a1hhdt88
uid://clve38xv30uem

View File

@@ -1 +1 @@
uid://ghuy35olsym1
uid://chqkkjclq101n

View File

@@ -1 +1 @@
uid://dmunl8xg53sym
uid://b7mk5mihqqr65

View File

@@ -1 +1 @@
uid://b4n45twg8y2ar
uid://cgee6csuvo1ye

View File

@@ -24,9 +24,8 @@ extends RefCounted
## Simulates that a key has been pressed.[br]
## @deprecated: the modifier [b]shift_pressed[/b] and [b]ctrl_pressed[/b] will be removed in v7.0
## [member key_code] : the key code e.g. [constant KEY_ENTER][br]
## [member shift_pressed] : false by default set to true if simmulate shift is press[br]
## [member ctrl_pressed] : false by default set to true if simmulate control is press[br]
## [codeblock]
## func test_key_presssed():
## var runner = scene_runner("res://scenes/simple_scene.tscn")
@@ -35,17 +34,43 @@ extends RefCounted
@abstract func simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner
## Simulates that a key is pressed.[br]
## Simulates that a key is pressing.[br]
## @deprecated: the modifier [b]shift_pressed[/b] and [b]ctrl_pressed[/b] will be removed in v7.0[br]See `test_key_shift_and_A_presssing` for example using key combinations
## [member key_code] : the key code e.g. [constant KEY_ENTER][br]
## [member shift_pressed] : false by default set to true if simmulate shift is press[br]
## [member ctrl_pressed] : false by default set to true if simmulate control is press[br]
## [codeblock]
## # Do simulate key pressing A
## func test_key_A_presssing():
## var runner = scene_runner("res://scenes/simple_scene.tscn")
## await runner.simulate_key_press(KEY_A)
##
##
## # Do simulate keycombination pressing shift+A
## func test_key_shift_and_A_presssing():
## var runner = scene_runner("res://scenes/simple_scene.tscn")
## runner.simulate_key_press(KEY_SHIFT)
## runner.simulate_key_press(KEY_A)
## await _runner.await_input_processed()
## [/codeblock]
@abstract func simulate_key_press(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner
## Simulates that a key has been released.[br]
## [member key_code] : the key code e.g. [constant KEY_ENTER][br]
## [member shift_pressed] : false by default set to true if simmulate shift is press[br]
## [member ctrl_pressed] : false by default set to true if simmulate control is press[br]
## [codeblock]
## # Do simulate releasing key A
## func test_key_A_releasing():
## var runner = scene_runner("res://scenes/simple_scene.tscn")
## await runner.simulate_key_release(KEY_A)
##
##
## # Do simulate keycombination pressing shift+A
## func test_key_shift_and_A_releasing(():
## var runner = scene_runner("res://scenes/simple_scene.tscn")
## runner.simulate_key_release(KEY_SHIFT)
## runner.simulate_key_release(KEY_A)
## await _runner.await_input_processed()
## [/codeblock]
## @deprecated: the modifier [b]shift_pressed[/b] and [b]ctrl_pressed[/b] will be removed in v7.0[br]See `test_key_shift_and_A_releasing` for example using key combinations
@abstract func simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner

View File

@@ -1 +1 @@
uid://dn20c5e8kb3q3
uid://cg867wakih43u

View File

@@ -27,20 +27,132 @@ extends GdUnitAssert
@abstract func append_failure_message(message: String) -> GdUnitSignalAssert
## Verifies that given signal is emitted until waiting time
@abstract func is_emitted(name: String, args := []) -> GdUnitSignalAssert
## Verifies that the specified signal is emitted with the expected arguments.[br]
##
## This assertion waits for a signal to be emitted from the object under test and
## validates that it was emitted with the correct arguments. The function supports
## both typed signals (Signal type) and string-based signal names for flexibility
## in different testing scenarios.[br]
## [br]
## [b]Parameters:[/b][br]
## [param signal_name]: The signal to monitor. Can be either:[br]
## • A [Signal] reference (recommended for type safety)[br]
## • A [String] with the signal name
## [param signal_args]: Optional expected signal arguments.[br]
## When provided, verifies the signal was emitted with exactly these values.[br]
## [br]
## [b]Returns:[/b][br]
## [GdUnitSignalAssert] - Returns self for method chaining.[br]
## [br]
## [b]Examples:[/b]
## [codeblock]
## signal signal_a(value: int)
## signal signal_b(name: String, count: int)
##
## # Wait for signal emission without checking arguments
## # Using Signal reference (type-safe)
## await assert_signal(instance).is_emitted(signal_a)
## # Using string name (dynamic)
## await assert_signal(instance).is_emitted("signal_a")
##
## # Wait for signal emission with specific argument
## await assert_signal(instance).is_emitted(signal_a, 10)
##
## # Wait for signal with multiple arguments
## await assert_signal(instance).is_emitted(signal_b, "test", 42)
##
## # Wait max 500ms for signal with argument 10
## await assert_signal(instance).wait_until(500).is_emitted(signal_a, 10)
## [/codeblock]
## [br]
## [b]Note:[/b] This is an async operation - use [code]await[/code] when calling.[br]
## The assertion fails if the signal is not emitted within the timeout period.
@abstract func is_emitted(signal_name: Variant, ...signal_args: Array) -> GdUnitSignalAssert
## Verifies that given signal is NOT emitted until waiting time
@abstract func is_not_emitted(name: String, args := []) -> GdUnitSignalAssert
## Verifies that the specified signal is NOT emitted with the expected arguments.[br]
##
## This assertion waits for a specified time period and validates that a signal
## was not emitted with the given arguments. Useful for ensuring certain conditions
## don't trigger unwanted signals or for verifying signal filtering logic.[br]
## [br]
## [b]Parameters:[/b][br]
## [param signal_name]: The signal to monitor. Can be either:[br]
## • A [Signal] reference (recommended for type safety)[br]
## • A [String] with the signal name
## [param signal_args]: Optional expected signal arguments.[br]
## When provided, verifies the signal was not emitted with these specific values.[br]
## If omitted, verifies the signal was not emitted at all.[br]
## [br]
## [b]Returns:[/b][br]
## [GdUnitSignalAssert] - Returns self for method chaining.[br]
## [br]
## [b]Examples:[/b]
## [codeblock]
## signal signal_a(value: int)
## signal signal_b(name: String, count: int)
##
## # Verify signal is not emitted at all (without checking arguments)
## await assert_signal(instance).wait_until(500).is_not_emitted(signal_a)
## await assert_signal(instance).wait_until(500).is_not_emitted("signal_a")
##
## # Verify signal is not emitted with specific argument
## await assert_signal(instance).wait_until(500).is_not_emitted(signal_a, 10)
##
## # Verify signal is not emitted with multiple arguments
## await assert_signal(instance).wait_until(500).is_not_emitted(signal_b, "test", 42)
##
## # Can be emitted with different arguments (this passes)
## instance.emit_signal("signal_a", 20) # Emits with 20, not 10
## await assert_signal(instance).wait_until(500).is_not_emitted(signal_a, 10)
## [/codeblock]
## [br]
## [b]Note:[/b] This is an async operation - use [code]await[/code] when calling.[br]
## The assertion fails if the signal IS emitted with the specified arguments within the timeout period.
@abstract func is_not_emitted(signal_name: Variant, ...signal_args: Array) -> GdUnitSignalAssert
## Verifies the signal exists checked the emitter
@abstract func is_signal_exists(name: String) -> GdUnitSignalAssert
## Verifies that the specified signal exists on the emitter object.[br]
##
## This assertion checks if a signal is defined on the object under test,
## regardless of whether it has been emitted. Useful for validating that
## objects have the expected signals before testing their emission.[br]
## [br]
## [b]Parameters:[/b][br]
## [param signal_name]: The signal to check. Can be either:[br]
## • A [Signal] reference (recommended for type safety)[br]
## • A [String] with the signal name
## [br]
## [b]Returns:[/b][br]
## [GdUnitSignalAssert] - Returns self for method chaining.[br]
## [br]
## [b]Examples:[/b]
## [codeblock]
## signal my_signal(value: int)
## signal another_signal()
##
## # Verify signal exists using Signal reference
## assert_signal(instance).is_signal_exists(my_signal)
##
## # Verify signal exists using string name
## assert_signal(instance).is_signal_exists("my_signal")
##
## # Chain with other assertions
## assert_signal(instance) \
## .is_signal_exists(my_signal) \
## .is_emitted(my_signal, 42)
##
## [/codeblock]
## [br]
## [b]Note:[/b] This only checks signal definition, not emission.[br]
## The assertion fails if the signal is not defined on the object.
@abstract func is_signal_exists(signal_name: Variant) -> GdUnitSignalAssert
## Sets the assert signal timeout in ms, if the time over a failure is reported.[br]
## e.g.[br]
## Example:
## [codeblock]
## do wait until 5s the instance has emitted the signal `signal_a`[br]
## [code]assert_signal(instance).wait_until(5000).is_emitted("signal_a")[/code]
## assert_signal(instance).wait_until(5000).is_emitted("signal_a")
## [/codeblock]
@abstract func wait_until(timeout: int) -> GdUnitSignalAssert

View File

@@ -1 +1 @@
uid://572nse6u4l86
uid://ck4dbwhcpf144

View File

@@ -1 +1 @@
uid://ip241g801xri
uid://b2itt4pwvweqo

View File

@@ -14,14 +14,13 @@
class_name GdUnitTestSuite
extends Node
const NO_ARG :Variant = GdUnitConstants.NO_ARG
### internal runtime variables that must not be overwritten!!!
@warning_ignore("unused_private_class_variable")
var __is_skipped := false
@warning_ignore("unused_private_class_variable")
var __skip_reason :String = "Unknow."
var __active_test_case :String
var __skip_reason := "Unknow."
var __active_test_case: String
var __awaiter := __gdunit_awaiter()
@@ -29,7 +28,7 @@ var __awaiter := __gdunit_awaiter()
### in order to noticeably reduce the loading time of the test suite.
# We go this hard way to increase the loading performance to avoid reparsing all the used scripts
# for more detailed info -> https://github.com/godotengine/godot/issues/67400
func __lazy_load(script_path :String) -> GDScript:
func __lazy_load(script_path: String) -> GDScript:
return GdUnitAssertions.__lazy_load(script_path)
@@ -81,23 +80,23 @@ func after_test() -> void:
pass
func is_failure(_expected_failure :String = NO_ARG) -> bool:
func is_failure() -> bool:
return Engine.get_meta("GD_TEST_FAILURE") if Engine.has_meta("GD_TEST_FAILURE") else false
func set_active_test_case(test_case :String) -> void:
func set_active_test_case(test_case: String) -> void:
__active_test_case = test_case
# === Tools ====================================================================
# Mapps Godot error number to a readable error message. See at ERROR
# https://docs.godotengine.org/de/stable/classes/class_@globalscope.html#enum-globalscope-error
func error_as_string(error_number :int) -> String:
func error_as_string(error_number: int) -> String:
return error_string(error_number)
## A litle helper to auto freeing your created objects after test execution
func auto_free(obj :Variant) -> Variant:
func auto_free(obj: Variant) -> Variant:
var execution_context := GdUnitThreadManager.get_current_context().get_execution_context()
assert(execution_context != null, "INTERNAL ERROR: The current execution_context is null! Please report this as bug.")
@@ -105,7 +104,7 @@ func auto_free(obj :Variant) -> Variant:
@warning_ignore("native_method_override")
func add_child(node :Node, force_readable_name := false, internal := Node.INTERNAL_MODE_DISABLED) -> void:
func add_child(node: Node, force_readable_name := false, internal := Node.INTERNAL_MODE_DISABLED) -> void:
super.add_child(node, force_readable_name, internal)
var execution_context := GdUnitThreadManager.get_current_context().get_execution_context()
if execution_context != null:
@@ -123,7 +122,7 @@ func discard_error_interupted_by_timeout() -> void:
## Creates a new directory under the temporary directory *user://tmp*[br]
## Useful for storing data during test execution. [br]
## The directory is automatically deleted after test suite execution
func create_temp_dir(relative_path :String) -> String:
func create_temp_dir(relative_path: String) -> String:
@warning_ignore("unsafe_method_access")
return __gdunit_file_access().create_temp_dir(relative_path)
@@ -138,25 +137,25 @@ func clean_temp_dir() -> void:
## Creates a new file under the temporary directory *user://tmp* + <relative_path>[br]
## with given name <file_name> and given file <mode> (default = File.WRITE)[br]
## If success the returned File is automatically closed after the execution of the test suite
func create_temp_file(relative_path :String, file_name :String, mode := FileAccess.WRITE) -> FileAccess:
func create_temp_file(relative_path: String, file_name: String, mode := FileAccess.WRITE) -> FileAccess:
@warning_ignore("unsafe_method_access")
return __gdunit_file_access().create_temp_file(relative_path, file_name, mode)
## Reads a resource by given path <resource_path> into a PackedStringArray.
func resource_as_array(resource_path :String) -> PackedStringArray:
func resource_as_array(resource_path: String) -> PackedStringArray:
@warning_ignore("unsafe_method_access")
return __gdunit_file_access().resource_as_array(resource_path)
## Reads a resource by given path <resource_path> and returned the content as String.
func resource_as_string(resource_path :String) -> String:
func resource_as_string(resource_path: String) -> String:
@warning_ignore("unsafe_method_access")
return __gdunit_file_access().resource_as_string(resource_path)
## Reads a resource by given path <resource_path> and return Variand translated by str_to_var
func resource_as_var(resource_path :String) -> Variant:
func resource_as_var(resource_path: String) -> Variant:
@warning_ignore("unsafe_method_access", "unsafe_cast")
return str_to_var(__gdunit_file_access().resource_as_string(resource_path) as String)
@@ -166,7 +165,7 @@ func resource_as_var(resource_path :String) -> Variant:
## signal_name: signal name[br]
## args: the expected signal arguments as an array[br]
## timeout: the timeout in ms, default is set to 2000ms
func await_signal_on(source :Object, signal_name :String, args :Array = [], timeout :int = 2000) -> Variant:
func await_signal_on(source: Object, signal_name: String, args: Array = [], timeout: int = 2000) -> Variant:
@warning_ignore("unsafe_method_access")
return await __awaiter.await_signal_on(source, signal_name, args, timeout)
@@ -184,11 +183,50 @@ func await_idle_frame() -> void:
## await await_millis(myNode, 100).completed
## [/codeblock][br]
## use this waiter and not `await get_tree().create_timer().timeout to prevent errors when a test case is timed out
func await_millis(timeout :int) -> void:
func await_millis(timeout: int) -> void:
@warning_ignore("unsafe_method_access")
await __awaiter.await_millis(timeout)
## Collects detailed information about orphaned nodes for debugging purposes.[br]
##
## This function gathers comprehensive details about nodes that remain in memory
## after test execution (orphans). It provides debugging information to help
## identify the source of memory leaks in tests. Must be manually called in
## tests when orphan nodes are detected.[br]
## [br]
## [b]When to Use:[/b][br]
## - When GdUnit4 reports orphan nodes after test execution[br]
## - For debugging memory leaks in test scenarios[br]
## - To get detailed information about unreleased nodes[br]
## [br]
## [b]Usage Pattern:[/b][br]
## Add this call at the end of tests that are suspected to create orphans,
## or when the test runner reports orphan detection.[br]
## [br]
## [b]Examples:[/b]
## [codeblock]
## func test_scene_management():
## # Test code that might create orphan nodes
## var scene = preload("res://TestScene.tscn").instantiate()
## add_child(scene)
##
## # Do test operations
## scene.some_method()
##
## # Clean up (but might miss some nodes)
## scene.queue_free()
##
## # Collect orphan details if any are detected
## collect_orphan_node_details()
## [/codeblock]
## [br]
## [b]Note:[/b] This is a debugging utility function that should be removed
## or commented out once orphan issues are resolved.
func collect_orphan_node_details() -> void:
GdUnitThreadManager.get_current_context().get_execution_context().orphan_monitor_collect()
## Creates a new scene runner to allow simulate interactions checked a scene.[br]
## The runner will manage the scene instance and release after the runner is released[br]
## example:[br]
@@ -200,7 +238,7 @@ func await_millis(timeout :int) -> void:
## # or simply creates a runner by using the scene resource path
## var runner := scene_runner("res://foo/my_scne.tscn")
## [/codeblock]
func scene_runner(scene :Variant, verbose := false) -> GdUnitSceneRunner:
func scene_runner(scene: Variant, verbose := false) -> GdUnitSceneRunner:
return auto_free(__lazy_load("res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd").new(scene, verbose))
@@ -216,13 +254,13 @@ const RETURN_DEEP_STUB = GdUnitMock.RETURN_DEEP_STUB
## Creates a mock for given class name
func mock(clazz :Variant, mock_mode := RETURN_DEFAULTS) -> Variant:
func mock(clazz: Variant, mock_mode := RETURN_DEFAULTS) -> Variant:
@warning_ignore("unsafe_method_access")
return __lazy_load("res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd").build(clazz, mock_mode)
## Creates a spy checked given object instance
func spy(instance :Variant) -> Variant:
func spy(instance: Variant) -> Variant:
@warning_ignore("unsafe_method_access")
return __lazy_load("res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd").build(instance)
@@ -233,30 +271,30 @@ func spy(instance :Variant) -> Variant:
## # overrides the return value of myMock.is_selected() to false
## do_return(false).on(myMock).is_selected()
## [/codeblock]
func do_return(value :Variant) -> GdUnitMock:
func do_return(value: Variant) -> GdUnitMock:
return GdUnitMock.new(value)
## Verifies certain behavior happened at least once or exact number of times
func verify(obj :Variant, times := 1) -> Variant:
func verify(obj: Variant, times := 1) -> Variant:
@warning_ignore("unsafe_method_access")
return __gdunit_object_interactions().verify(obj, times)
## Verifies no interactions is happen checked this mock or spy
func verify_no_interactions(obj :Variant) -> GdUnitAssert:
func verify_no_interactions(obj: Variant) -> GdUnitAssert:
@warning_ignore("unsafe_method_access")
return __gdunit_object_interactions().verify_no_interactions(obj)
## Verifies the given mock or spy has any unverified interaction.
func verify_no_more_interactions(obj :Variant) -> GdUnitAssert:
func verify_no_more_interactions(obj: Variant) -> GdUnitAssert:
@warning_ignore("unsafe_method_access")
return __gdunit_object_interactions().verify_no_more_interactions(obj)
## Resets the saved function call counters checked a mock or spy
func reset(obj :Variant) -> void:
func reset(obj: Variant) -> void:
@warning_ignore("unsafe_method_access")
__gdunit_object_interactions().reset(obj)
@@ -273,7 +311,7 @@ func reset(obj :Variant) -> void:
## # verify the signial is emitted
## await assert_signal(emitter).is_emitted('my_signal')
## [/codeblock]
func monitor_signals(source :Object, _auto_free := true) -> Object:
func monitor_signals(source: Object, _auto_free := true) -> Object:
@warning_ignore("unsafe_method_access")
__lazy_load("res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd")\
.get_current_context()\
@@ -502,29 +540,46 @@ func any_class(clazz :Object) -> GdUnitArgumentMatcher:
# === value extract utils ======================================================
## Builds an extractor by given function name and optional arguments
func extr(func_name :String, args := Array()) -> GdUnitValueExtractor:
func extr(func_name: String, args := Array()) -> GdUnitValueExtractor:
return __lazy_load("res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd").new(func_name, args)
## Constructs a tuple by given arguments
func tuple(arg0 :Variant,
arg1 :Variant=NO_ARG,
arg2 :Variant=NO_ARG,
arg3 :Variant=NO_ARG,
arg4 :Variant=NO_ARG,
arg5 :Variant=NO_ARG,
arg6 :Variant=NO_ARG,
arg7 :Variant=NO_ARG,
arg8 :Variant=NO_ARG,
arg9 :Variant=NO_ARG) -> GdUnitTuple:
return GdUnitTuple.new(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
## Creates a GdUnitTuple from the provided arguments for use in test assertions.
## [br]
## This is the primary helper function for creating tuples in GdUnit4 tests.
## It provides a convenient way to group multiple expected values when using
## [method extractv] assertions. The function enforces that tuples must contain
## at least two values, as single-value extractions don't require tuple grouping.
## [br]
## [b]Parameters:[/b] [br]
## - [code]...args[/code]: Variable number of arguments (minimum 2) to group into a tuple.
## Each argument represents a value to be compared in assertions.
## [br]
## [b]Returns:[/b] [br]
## A [GdUnitTuple] containing the provided values, or an empty tuple if fewer than
## 2 arguments are provided (with an error message).
## [br]
## [b]Error Handling:[/b] [br]
## [codeblock]
## # This will push an error and return empty tuple
## var invalid = tuple("single_value") # Error: requires at least 2 arguments
## [br]
## # Correct usage - minimum 2 arguments
## var valid = tuple("name", "value")
## var valid_multi = tuple(1, 2, 3, 4, 5) # Can have many values
## [/codeblock]
func tuple(...args: Array) -> GdUnitTuple:
if args.size() < 2:
push_error("Tuple requires at least two arguments.")
return GdUnitTuple.new()
return GdUnitTuple.new.callv(args)
# === Asserts ==================================================================
## The common assertion tool to verify values.
## It checks the given value by type to fit to the best assert
func assert_that(current :Variant) -> GdUnitAssert:
func assert_that(current: Variant) -> GdUnitAssert:
match typeof(current):
TYPE_BOOL:
return assert_bool(current)
@@ -549,22 +604,22 @@ func assert_that(current :Variant) -> GdUnitAssert:
## An assertion tool to verify boolean values.
func assert_bool(current :Variant) -> GdUnitBoolAssert:
func assert_bool(current: Variant) -> GdUnitBoolAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd").new(current)
## An assertion tool to verify String values.
func assert_str(current :Variant) -> GdUnitStringAssert:
func assert_str(current: Variant) -> GdUnitStringAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd").new(current)
## An assertion tool to verify integer values.
func assert_int(current :Variant) -> GdUnitIntAssert:
func assert_int(current: Variant) -> GdUnitIntAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd").new(current)
## An assertion tool to verify float values.
func assert_float(current :Variant) -> GdUnitFloatAssert:
func assert_float(current: Variant) -> GdUnitFloatAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd").new(current)
@@ -574,41 +629,41 @@ func assert_float(current :Variant) -> GdUnitFloatAssert:
## [codeblock]
## assert_vector(Vector2(1.2, 1.000001)).is_equal(Vector2(1.2, 1.000001))
## [/codeblock]
func assert_vector(current :Variant, type_check := true) -> GdUnitVectorAssert:
func assert_vector(current: Variant, type_check := true) -> GdUnitVectorAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd").new(current, type_check)
## An assertion tool to verify arrays.
func assert_array(current :Variant, type_check := true) -> GdUnitArrayAssert:
func assert_array(current: Variant, type_check := true) -> GdUnitArrayAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd").new(current, type_check)
## An assertion tool to verify dictionaries.
func assert_dict(current :Variant) -> GdUnitDictionaryAssert:
func assert_dict(current: Variant) -> GdUnitDictionaryAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd").new(current)
## An assertion tool to verify FileAccess.
func assert_file(current :Variant) -> GdUnitFileAssert:
func assert_file(current: Variant) -> GdUnitFileAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd").new(current)
## An assertion tool to verify Objects.
func assert_object(current :Variant) -> GdUnitObjectAssert:
func assert_object(current: Variant) -> GdUnitObjectAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd").new(current)
func assert_result(current :Variant) -> GdUnitResultAssert:
func assert_result(current: Variant) -> GdUnitResultAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd").new(current)
## An assertion tool that waits until a certain time for an expected function return value
func assert_func(instance :Object, func_name :String, args := Array()) -> GdUnitFuncAssert:
func assert_func(instance: Object, func_name: String, args := Array()) -> GdUnitFuncAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd").new(instance, func_name, args)
## An assertion tool to verify for emitted signals until a certain time.
func assert_signal(instance :Object) -> GdUnitSignalAssert:
func assert_signal(instance: Object) -> GdUnitSignalAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd").new(instance)
@@ -619,7 +674,7 @@ func assert_signal(instance :Object) -> GdUnitSignalAssert:
## assert_failure(func(): assert_bool(true).is_not_equal(true)) \
## .has_message("Expecting:\n 'true'\n not equal to\n 'true'")
## [/codeblock]
func assert_failure(assertion :Callable) -> GdUnitFailureAssert:
func assert_failure(assertion: Callable) -> GdUnitFailureAssert:
@warning_ignore("unsafe_method_access")
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd").new().execute(assertion)
@@ -631,7 +686,7 @@ func assert_failure(assertion :Callable) -> GdUnitFailureAssert:
## await assert_failure_await(func(): assert_bool(true).is_not_equal(true)) \
## .has_message("Expecting:\n 'true'\n not equal to\n 'true'")
## [/codeblock]
func assert_failure_await(assertion :Callable) -> GdUnitFailureAssert:
func assert_failure_await(assertion: Callable) -> GdUnitFailureAssert:
@warning_ignore("unsafe_method_access")
return await __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd").new().execute_and_await(assertion)
@@ -648,7 +703,7 @@ func assert_failure_await(assertion :Callable) -> GdUnitFailureAssert:
## await assert_error(func (): push_error('test error') )\
## .is_push_error('test error')
## [/codeblock]
func assert_error(current :Callable) -> GdUnitGodotErrorAssert:
func assert_error(current: Callable) -> GdUnitGodotErrorAssert:
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd").new(current)

View File

@@ -1 +1 @@
uid://cgbfa4cflb5nl
uid://bfroomur047su

View File

@@ -1,28 +1,86 @@
## A tuple implementation to hold two or many values
## A tuple implementation for GdUnit4 test assertions and value extraction.
## @tutorial(GdUnit4 Array Assertions): https://mikeschulze.github.io/gdUnit4/latest/testing/assert-array/#extractv
## @tutorial(GdUnit4 Testing Framework): https://mikeschulze.github.io/gdUnit4/
## [br]
## The GdUnitTuple class is a utility container designed specifically for the GdUnit4
## testing framework. It enables advanced assertion operations, particularly when
## extracting and comparing multiple values from complex test results.
## [br]
## [b]Primary Use Cases in Testing:[/b] [br]
## - Extracting multiple properties from test objects with [method extractv]## [br]
## - Grouping related assertion values for comparison## [br]
## - Returning multiple values from test helper methods## [br]
## - Organizing expected vs actual value pairs in assertions## [br]
## [br]
## [b]Example Usage in Tests:[/b]
## [codeblock]
## func test_player_stats_after_level_up():
## var player = Player.new()
## player.level_up()
##
## # Extract multiple properties using tuple
## assert_array([player]) \
## .extractv(extr("name"), extr("level"), extr("hp")) \
## .contains(tuple("Hero", 2, 150))
##
## func test_enemy_spawn_positions():
## var enemies: Array = spawn_enemies(3)
##
## # Verify multiple enemies have correct position data
## assert_array(enemies) \
## .extractv(extr("position.x"), extr("position.y")) \
## .contains_exactly([
## tuple(100, 200),
## tuple(150, 200),
## tuple(200, 200)
## ])
## [/codeblock]
## [br]
## [b]Integration with GdUnit4 Assertions:[/b] [br]
## Tuples work seamlessly with array assertion methods like: [br]
## - [code]contains()[/code] - Check if extracted values contain specific tuples [br]
## - [code]contains_exactly()[/code] - Verify exact tuple matches [br]
## - [code]is_equal()[/code] - Compare tuple equality [br]
## [br]
## [b]Note:[/b] This class is part of the GdUnit4 testing framework's internal
## utilities and is primarily intended for use within test assertions rather
## than production code.
class_name GdUnitTuple
extends RefCounted
const NO_ARG :Variant = GdUnitConstants.NO_ARG
var __values :Array = Array()
var _values: Array = []
func _init(arg0:Variant,
arg1 :Variant=NO_ARG,
arg2 :Variant=NO_ARG,
arg3 :Variant=NO_ARG,
arg4 :Variant=NO_ARG,
arg5 :Variant=NO_ARG,
arg6 :Variant=NO_ARG,
arg7 :Variant=NO_ARG,
arg8 :Variant=NO_ARG,
arg9 :Variant=NO_ARG) -> void:
__values = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG)
## Initializes a new GdUnitTuple with test values.
## [br]
## Creates a tuple to hold multiple values extracted from test objects
## or expected values for assertions. Commonly used with the [code]tuple()[/code]
## helper function in GdUnit4 tests.
## [br]
## [b]Parameters:[/b]
## - [code]...args[/code]: Variable number of values to store.
func _init(...args: Array) -> void:
_values = args
## Returns the tuple's values as an array for assertion comparisons.
## [br]
## Provides access to the stored test values. Used internally by GdUnit4's
## assertion system when comparing tuples in test validations.
## [br]
## [b]Returns:[/b]
## An [Array] containing all values stored in the tuple.
func values() -> Array:
return __values
return _values
## Returns a string representation for test output and debugging.
## [br]
## Formats the tuple for display in test results, error messages, and debug logs.
## This method is automatically called by GdUnit4 when displaying assertion
## failures involving tuples.
## [br]
## [b]Returns:[/b]
## A [String] in the format "tuple([value1, value2, ...])"
func _to_string() -> String:
return "tuple(%s)" % str(__values)
return "tuple(%s)" % str(_values)

View File

@@ -1 +1 @@
uid://mjqw2uww51fk
uid://ckj5nn4gln5bw

View File

@@ -1 +1 @@
uid://2dylh01qtb66
uid://cyagv2phlu24d

View File

@@ -1 +1 @@
uid://bcx6bgypklb3e
uid://hrk46nhl8icp

View File

@@ -1 +1 @@
uid://r43u2usutiss
uid://bymqibokrfyuk

View File

@@ -1 +1 @@
uid://coauynw7rnsij
uid://bxkxeyj2jbth2

View File

@@ -40,22 +40,40 @@ static func input_event_as_text(event :InputEvent) -> String:
var text := ""
if event is InputEventKey:
var key_event := event as InputEventKey
text += "InputEventKey : key='%s', pressed=%s, keycode=%d, physical_keycode=%s" % [
event.as_text(), key_event.pressed, key_event.keycode, key_event.physical_keycode]
text += """
InputEventKey : keycode=%s (%s) pressed: %s
physical_keycode: %s
location: %s
echo: %s""" % [
key_event.keycode,
event.as_text_keycode(),
key_event.pressed,
key_event.physical_keycode,
key_event.location,
key_event.echo]
else:
text += event.as_text()
if event is InputEventMouse:
var mouse_event := event as InputEventMouse
text += ", global_position %s" % mouse_event.global_position
text += """
global_position: %s""" % mouse_event.global_position
if event is InputEventWithModifiers:
var mouse_event := event as InputEventWithModifiers
text += ", shift=%s, alt=%s, control=%s, meta=%s, command=%s" % [
text += """
--------
mods: %s
shift: %s
alt: %s
control: %s
meta: %s
command: %s""" % [
mouse_event.get_modifiers_mask(),
mouse_event.shift_pressed,
mouse_event.alt_pressed,
mouse_event.ctrl_pressed,
mouse_event.meta_pressed,
mouse_event.command_or_control_autoremap]
return text
return text.dedent()
static func _colored_string_div(characters: String) -> String:
@@ -119,6 +137,10 @@ static func _nerror(number :Variant) -> String:
return "[color=%s]%s[/color]" % [ERROR_COLOR, str(number)]
static func _colored(value: Variant, color: Color) -> String:
return "[color=%s]%s[/color]" % [color.to_html(), value]
static func _colored_value(value :Variant) -> String:
match typeof(value):
TYPE_STRING, TYPE_STRING_NAME:
@@ -161,19 +183,53 @@ static func _index_report_as_table(index_reports :Array) -> String:
return table.replace("$cells", cells)
static func orphan_detected_on_suite_setup(count :int) -> String:
return "%s\n Detected <%d> orphan nodes during test suite setup stage! [b]Check before() and after()![/b]" % [
_warning("WARNING:"), count]
static func orphan_warning(orphans_count: int) -> String:
return """
%s: Found %s possible orphan nodes.
Add %s to the end of the test to collect details.""".dedent().trim_prefix("\n") % [
_warning("WARNING:"),
_nerror(orphans_count),
_colored_value("collect_orphan_node_details()")
]
static func orphan_detected_on_suite_setup(orphans: Array[GdUnitOrphanNodeInfo]) -> String:
return """
%s Detected %s orphan nodes!
[b]Verify your test suite setup.[/b]
%s""".dedent().trim_prefix("\n") % [
_warning("WARNING:"),
_nerror(orphans.size()),
_build_orphan_node_stacktrace(orphans)]
static func orphan_detected_on_test_setup(count :int) -> String:
return "%s\n Detected <%d> orphan nodes during test setup! [b]Check before_test() and after_test()![/b]" % [
_warning("WARNING:"), count]
static func orphan_detected_on_test_setup(orphans: Array[GdUnitOrphanNodeInfo]) -> String:
return """
%s Detected %s orphan nodes on test setup!
[b]Check before_test() and after_test()![/b]
%s""".dedent().trim_prefix("\n") % [
_warning("WARNING:"),
_nerror(orphans.size()),
_build_orphan_node_stacktrace(orphans)
]
static func orphan_detected_on_test(count :int) -> String:
return "%s\n Detected <%d> orphan nodes during test execution!" % [
_warning("WARNING:"), count]
static func orphan_detected_on_test(orphans: Array[GdUnitOrphanNodeInfo]) -> String:
return """
%s Detected %s orphan nodes!
%s""".dedent().trim_prefix("\n") % [
_warning("WARNING:"),
_nerror(orphans.size()),
_build_orphan_node_stacktrace(orphans)
]
static func _build_orphan_node_stacktrace(orphans: Array[GdUnitOrphanNodeInfo]) -> String:
var stack_trace := "\n"
for orphan in orphans:
stack_trace += orphan.as_trace(orphan, true) + "\n"
return stack_trace.indent(" ")
static func fuzzer_interuped(iterations: int, error: String) -> String:
@@ -188,6 +244,10 @@ static func test_timeout(timeout :int) -> String:
return "%s\n %s" % [_error("Timeout !"), _colored_value("Test timed out after %s" % LocalTime.elapsed(timeout))]
static func test_session_terminated() -> String:
return "%s" % _error("Test Session Terminated")
# gdlint:disable = mixed-tabs-and-spaces
static func test_suite_skipped(hint :String, skip_count :int) -> String:
return """

View File

@@ -1 +1 @@
uid://vl7cfc01g5wl
uid://c178wdncle4i5

View File

@@ -1 +1 @@
uid://brxvavm3ml0om
uid://5fthlxduiurg

View File

@@ -75,7 +75,7 @@ func _toPackedStringArray(value: Variant) -> PackedStringArray:
return PackedStringArray([str(value)])
func _array_equals_div(current: Variant, expected: Variant, case_sensitive: bool = false) -> Array[Array]:
func _array_equals_div(current: Variant, expected: Variant, case_sensitive: bool = false) -> Array:
var current_value := _toPackedStringArray(current)
var expected_value := _toPackedStringArray(expected)
var index_report := Array()
@@ -374,24 +374,12 @@ func extractv(...extractors: Array) -> GdUnitArrayAssert:
_current_value_provider = DefaultValueProvider.new(null)
else:
for element: Variant in current:
var ev: Array[Variant] = [
GdUnitTuple.NO_ARG,
GdUnitTuple.NO_ARG,
GdUnitTuple.NO_ARG,
GdUnitTuple.NO_ARG,
GdUnitTuple.NO_ARG,
GdUnitTuple.NO_ARG,
GdUnitTuple.NO_ARG,
GdUnitTuple.NO_ARG,
GdUnitTuple.NO_ARG,
GdUnitTuple.NO_ARG
]
var ev: Array[Variant] = []
for index: int in extractors.size():
var extractor: GdUnitValueExtractor = extractors[index]
ev[index] = extractor.extract_value(element)
ev.append(extractor.extract_value(element))
if extractors.size() > 1:
extracted_elements.append(GdUnitTuple.new(ev[0], ev[1], ev[2], ev[3], ev[4], ev[5], ev[6], ev[7], ev[8], ev[9]))
extracted_elements.append(GdUnitTuple.new.callv(ev))
else:
extracted_elements.append(ev[0])
_current_value_provider = DefaultValueProvider.new(extracted_elements)

View File

@@ -1 +1 @@
uid://bx7cehfdh2x4w
uid://381c6r7r5uhp

View File

@@ -1 +1 @@
uid://cq38mcld2thyl
uid://8c05oepulju5

View File

@@ -1 +1 @@
uid://61d7pdgldg0r
uid://crrhhudfmy3ay

View File

@@ -1 +1 @@
uid://cxndss6mdq7de
uid://c310wp8sog1ti

View File

@@ -1 +1 @@
uid://dqrp7csbeyvon
uid://diqntuiv0y7pg

View File

@@ -1 +1 @@
uid://cbrj7dsr235i0
uid://b8htynygoyy0f

View File

@@ -1 +1 @@
uid://2s6h0titid8y
uid://dg0uusgasnr64

View File

@@ -1 +1 @@
uid://dvce6xeybbh1i
uid://fyb32t5gwdxk

View File

@@ -1 +1 @@
uid://c2jdw0vv5nldq
uid://bd15pn72awek1

View File

@@ -4,6 +4,7 @@ var _current_failure_message := ""
var _custom_failure_message := ""
var _additional_failure_message := ""
var _callable: Callable
var _logger := GodotGdErrorMonitor.new()
func _init(callable: Callable) -> void:
@@ -14,17 +15,10 @@ func _init(callable: Callable) -> void:
func _execute() -> Array[ErrorLogEntry]:
# execute the given code and monitor for runtime errors
if _callable == null or not _callable.is_valid():
@warning_ignore("return_value_discarded")
_report_error("Invalid Callable '%s'" % _callable)
else:
await _callable.call()
return await _error_monitor().scan(true)
func _error_monitor() -> GodotGdErrorMonitor:
return GdUnitThreadManager.get_current_context().get_execution_context().error_monitor
_logger.start()
await _callable.call()
_logger.stop()
return _logger.log_entries()
func failure_message() -> String:
@@ -46,8 +40,6 @@ func _report_error(error_message: String, failure_line_number: int = -1) -> GdUn
func _has_log_entry(log_entries: Array[ErrorLogEntry], type: ErrorLogEntry.TYPE, error: Variant) -> bool:
for entry in log_entries:
if entry._type == type and GdObjects.equals(entry._message, error):
# Erase the log entry we already handled it by this assertion, otherwise it will report at twice
_error_monitor().erase_log_entry(entry)
return true
return false
@@ -55,12 +47,11 @@ func _has_log_entry(log_entries: Array[ErrorLogEntry], type: ErrorLogEntry.TYPE,
func _to_list(log_entries: Array[ErrorLogEntry]) -> String:
if log_entries.is_empty():
return "no errors"
if log_entries.size() == 1:
return log_entries[0]._message
var value := ""
var values := []
for entry in log_entries:
value += "'%s'\n" % entry._message
return value
values.append(entry)
return "\n".join(values)
func is_null() -> GdUnitGodotErrorAssert:
@@ -90,6 +81,9 @@ func append_failure_message(message: String) -> GdUnitGodotErrorAssert:
func is_success() -> GdUnitGodotErrorAssert:
if not _validate_callable():
return self
var log_entries := await _execute()
if log_entries.is_empty():
return _report_success()
@@ -100,6 +94,9 @@ func is_success() -> GdUnitGodotErrorAssert:
func is_runtime_error(expected_error: Variant) -> GdUnitGodotErrorAssert:
if not _validate_callable():
return self
var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_error)
if result.is_error():
return _report_error(result.error_message())
@@ -108,12 +105,15 @@ func is_runtime_error(expected_error: Variant) -> GdUnitGodotErrorAssert:
return _report_success()
return _report_error("""
Expecting: a runtime error is triggered.
message: '%s'
found: %s
expected: '%s'
current: '%s'
""".dedent().trim_prefix("\n") % [expected_error, _to_list(log_entries)])
func is_push_warning(expected_warning: Variant) -> GdUnitGodotErrorAssert:
if not _validate_callable():
return self
var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_warning)
if result.is_error():
return _report_error(result.error_message())
@@ -122,12 +122,15 @@ func is_push_warning(expected_warning: Variant) -> GdUnitGodotErrorAssert:
return _report_success()
return _report_error("""
Expecting: push_warning() is called.
message: '%s'
found: %s
expected: '%s'
current: '%s'
""".dedent().trim_prefix("\n") % [expected_warning, _to_list(log_entries)])
func is_push_error(expected_error: Variant) -> GdUnitGodotErrorAssert:
if not _validate_callable():
return self
var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_error)
if result.is_error():
return _report_error(result.error_message())
@@ -136,6 +139,14 @@ func is_push_error(expected_error: Variant) -> GdUnitGodotErrorAssert:
return _report_success()
return _report_error("""
Expecting: push_error() is called.
message: '%s'
found: %s
expected: '%s'
current: '%s'
""".dedent().trim_prefix("\n") % [expected_error, _to_list(log_entries)])
func _validate_callable() -> bool:
if _callable == null or not _callable.is_valid():
@warning_ignore("return_value_discarded")
_report_error("Invalid Callable '%s'" % _callable)
return false
return true

View File

@@ -1 +1 @@
uid://cyi6ooahncq7q
uid://d3st26kcndm8l

View File

@@ -1 +1 @@
uid://j4mpmwm2hw61
uid://bcuh7lnvrg7mb

View File

@@ -1 +1 @@
uid://bm6qm58a0dacq
uid://ba515a8xk0ubo

View File

@@ -1 +1 @@
uid://b0dlq6jyjcvps
uid://dwl5ooagjg2nc

View File

@@ -91,7 +91,11 @@ func is_not_equal(_expected: Variant) -> GdUnitSignalAssert:
# Verifies the signal exists checked the emitter
func is_signal_exists(signal_name :String) -> GdUnitSignalAssert:
func is_signal_exists(signal_name: Variant) -> GdUnitSignalAssert:
if not (signal_name is String or signal_name is Signal):
return report_error("Invalid signal_name: expected String or Signal, but is '%s'" % type_string(typeof(signal_name)))
signal_name = (signal_name as Signal).get_name() if signal_name is Signal else signal_name
if not _emitter.has_signal(signal_name):
@warning_ignore("return_value_discarded")
report_error("The signal '%s' not exists checked object '%s'." % [signal_name, _emitter.get_class()])
@@ -99,20 +103,36 @@ func is_signal_exists(signal_name :String) -> GdUnitSignalAssert:
# Verifies that given signal is emitted until waiting time
func is_emitted(name :String, args := []) -> GdUnitSignalAssert:
func is_emitted(signal_name: Variant, ...signal_args: Array) -> GdUnitSignalAssert:
_line_number = GdUnitAssertions.get_line_number()
return await _wail_until_signal(name, args, false)
return await _wail_until_signal(
signal_name,
_wrap_arguments.callv(signal_args),
false)
# Verifies that given signal is NOT emitted until waiting time
func is_not_emitted(name :String, args := []) -> GdUnitSignalAssert:
func is_not_emitted(signal_name: Variant, ...signal_args: Array) -> GdUnitSignalAssert:
_line_number = GdUnitAssertions.get_line_number()
return await _wail_until_signal(name, args, true)
return await _wail_until_signal(
signal_name,
_wrap_arguments.callv(signal_args),
true)
func _wail_until_signal(signal_name :String, expected_args :Array, expect_not_emitted: bool) -> GdUnitSignalAssert:
func _wrap_arguments(...args: Array) -> Array:
# Check using old syntax
if not args.is_empty() and args[0] is Array:
return args[0]
return args
func _wail_until_signal(signal_value: Variant, expected_args: Array, expect_not_emitted: bool) -> GdUnitSignalAssert:
if _emitter == null:
return report_error("Can't wait for signal checked a NULL object.")
if not (signal_value is String or signal_value is Signal):
return report_error("Invalid signal_name: expected String or Signal, but is '%s'" % type_string(typeof(signal_value)))
var signal_name := (signal_value as Signal).get_name() if signal_value is Signal else signal_value
# first verify the signal is defined
if not _emitter.has_signal(signal_name):
return report_error("Can't wait for non-existion signal '%s' checked object '%s'." % [signal_name,_emitter.get_class()])

View File

@@ -1 +1 @@
uid://dlh37yc086vr5
uid://c3ytdhnam8ba7

View File

@@ -1 +1 @@
uid://dxqvilchqqeta
uid://cw5h1gecccd37

View File

@@ -1 +1 @@
uid://r4avfcakvscw
uid://pyvt1oi43853

View File

@@ -1 +1 @@
uid://8y15b6ts3kss
uid://beueufp3wgjw8

View File

@@ -1 +1 @@
uid://d4hd3vc50jltg
uid://34eyejb3x15x

View File

@@ -1 +1 @@
uid://w4mr1j0k0l
uid://crpirtbbekydy

View File

@@ -1 +1 @@
uid://ccm3ivfiaf3i7
uid://dbbsj4qqi8w3m

View File

@@ -1 +1 @@
uid://ccnb2ah35atho
uid://cpokylwutgxw6

View File

@@ -1 +1 @@
uid://0p8udx4tdwol
uid://swuykw4qh2xx

View File

@@ -1 +1 @@
uid://bk60ywsj4ekp7
uid://etq75dot1c03

View File

@@ -1 +1 @@
uid://b5sli0lem5xca
uid://cx8bp6vsxyrts

View File

@@ -1 +1 @@
uid://b7ldhc4ryfh1v
uid://dk4tv55wf5mlm

View File

@@ -1 +1 @@
uid://bbaqjhpbxce3u
uid://ds3n71e8n4wce

View File

@@ -196,7 +196,7 @@ static func resource_as_string(resource_path :String) -> String:
if file == null:
push_error("ERROR: Can't read resource '%s'. %s" % [resource_path, error_string(FileAccess.get_open_error())])
return ""
return file.get_as_text(true)
return file.get_as_text()
static func make_qualified_path(path :String) -> String:

View File

@@ -1 +1 @@
uid://dflqb5germp5n
uid://ddr26lg6f055n

View File

@@ -1 +1 @@
uid://cqndh0nuu8ltx
uid://c0ymnlx3jooyl

View File

@@ -1 +1 @@
uid://cnvq3nb61ei76
uid://b6k1ufvw6w2s5

View File

@@ -18,7 +18,10 @@ var _config := {
TESTS : Array([], TYPE_OBJECT, "RefCounted", GdUnitTestCase),
# the port of running test server for this session
SERVER_PORT : -1
SERVER_PORT : -1,
# Exit on first failure
EXIT_FAIL_FAST : false
}
@@ -40,6 +43,15 @@ func server_port() -> int:
return _config.get(SERVER_PORT, -1)
func do_fail_fast(is_fail_fast: bool) -> GdUnitRunnerConfig:
_config[EXIT_FAIL_FAST] = is_fail_fast
return self
func is_fail_fast() -> bool:
return _config.get(EXIT_FAIL_FAST, false)
func add_test_cases(tests: Array[GdUnitTestCase]) -> GdUnitRunnerConfig:
test_cases().append_array(tests)
return self
@@ -57,7 +69,8 @@ func save_config(path: String = CONFIG_FILE) -> GdUnitResult:
var to_save := {
VERSION : CONFIG_VERSION,
SERVER_PORT : _config.get(SERVER_PORT),
EXIT_FAIL_FAST : is_fail_fast(),
SERVER_PORT : server_port(),
TESTS : Array()
}

View File

@@ -1 +1 @@
uid://ltvpkh3ayklf
uid://dvrxtyni1604f

View File

@@ -72,12 +72,11 @@ func _init(p_scene: Variant, p_verbose: bool, p_hide_push_errors := false) -> vo
return
_scene_tree().root.add_child(_current_scene)
Engine.set_meta("GdUnitSceneRunner", self)
# do finally reset all open input events when the scene is removed
@warning_ignore("return_value_discarded")
_scene_tree().root.child_exiting_tree.connect(func f(child :Node) -> void:
_scene_tree().root.child_exiting_tree.connect(func f(child: Node) -> void:
if child == _current_scene:
# we need to disable the processing to avoid input flush buffer errors
_current_scene.process_mode = Node.PROCESS_MODE_DISABLED
_reset_input_to_default()
)
_simulate_start_time = LocalTime.now()
@@ -103,6 +102,7 @@ func _notification(what: int) -> void:
_current_scene.free()
_is_disposed = true
_current_scene = null
Engine.remove_meta("GdUnitSceneRunner")
func _scene_tree() -> SceneTree:
@@ -145,6 +145,7 @@ func simulate_action_release(action: String, event_index := -1) -> GdUnitSceneRu
@warning_ignore("return_value_discarded")
func simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner:
_push_warning_deprecated_arguments(shift_pressed, ctrl_pressed)
simulate_key_press(key_code, shift_pressed, ctrl_pressed)
await _scene_tree().process_frame
simulate_key_release(key_code, shift_pressed, ctrl_pressed)
@@ -152,30 +153,33 @@ func simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed :=
func simulate_key_press(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner:
_push_warning_deprecated_arguments(shift_pressed, ctrl_pressed)
__print_current_focus()
var event := InputEventKey.new()
event.pressed = true
event.keycode = key_code as Key
event.physical_keycode = key_code as Key
event.unicode = key_code
event.alt_pressed = key_code == KEY_ALT
event.shift_pressed = shift_pressed or key_code == KEY_SHIFT
event.ctrl_pressed = ctrl_pressed or key_code == KEY_CTRL
event.set_alt_pressed(key_code == KEY_ALT)
event.set_shift_pressed(shift_pressed)
event.set_ctrl_pressed(ctrl_pressed)
event.get_modifiers_mask()
_apply_input_modifiers(event)
_key_on_press.append(key_code)
return _handle_input_event(event)
func simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner:
_push_warning_deprecated_arguments(shift_pressed, ctrl_pressed)
__print_current_focus()
var event := InputEventKey.new()
event.pressed = false
event.keycode = key_code as Key
event.physical_keycode = key_code as Key
event.unicode = key_code
event.alt_pressed = key_code == KEY_ALT
event.shift_pressed = shift_pressed or key_code == KEY_SHIFT
event.ctrl_pressed = ctrl_pressed or key_code == KEY_CTRL
event.set_alt_pressed(key_code == KEY_ALT)
event.set_shift_pressed(shift_pressed)
event.set_ctrl_pressed(ctrl_pressed)
_apply_input_modifiers(event)
_key_on_press.erase(key_code)
return _handle_input_event(event)
@@ -485,6 +489,8 @@ func find_child(name: String, recursive: bool = true, owned: bool = false) -> No
func _scene_name() -> String:
if scene() == null:
return "unknown"
var scene_script :GDScript = scene().get_script()
var scene_name :String = scene().get_name()
if not scene_script:
@@ -515,6 +521,13 @@ func _apply_input_modifiers(event: InputEvent) -> void:
_event.ctrl_pressed = _event.ctrl_pressed or last_input_event.ctrl_pressed
# this line results into reset the control_pressed state!!!
#event.command_or_control_autoremap = event.command_or_control_autoremap or _last_input_event.command_or_control_autoremap
if _last_input_event is InputEventKey and event is InputEventWithModifiers:
var last_input_event := _last_input_event as InputEventKey
var _event := event as InputEventWithModifiers
_event.shift_pressed = _event.shift_pressed or last_input_event.keycode == KEY_SHIFT
_event.alt_pressed = _event.alt_pressed or last_input_event.keycode == KEY_ALT
_event.ctrl_pressed = _event.ctrl_pressed or last_input_event.keycode == KEY_CTRL
_event.meta_pressed = _event.meta_pressed or last_input_event.keycode == KEY_META
# copy over current active mouse mask and combine with curren mask
@@ -620,3 +633,10 @@ func scene() -> Node:
if not _is_disposed:
push_error("The current scene instance is not valid anymore! check your test is valid. e.g. check for missing awaits.")
return null
func _push_warning_deprecated_arguments(shift_pressed: bool, ctrl_pressed: bool) -> void:
if shift_pressed:
push_warning("Deprecated! Don't use 'shift_pressed' it will be removed in v7.0, checkout the documentaion how to use key combinations.")
if ctrl_pressed:
push_warning("Deprecated! Don't use 'ctrl_pressed' it will be removed in v7.0, checkout the documentaion how to use key combinations.")

View File

@@ -1 +1 @@
uid://7a566a4kfreu
uid://bdhmqovuioydb

View File

@@ -21,6 +21,7 @@ const TEST_SUITE_NAMING_CONVENTION = GROUP_TEST + "/test_suite_naming_convention
const TEST_DISCOVER_ENABLED = GROUP_TEST + "/test_discovery"
const TEST_FLAKY_CHECK = GROUP_TEST + "/flaky_check_enable"
const TEST_FLAKY_MAX_RETRIES = GROUP_TEST + "/flaky_max_retries"
const TEST_RERUN_UNTIL_FAILURE_RETRIES = GROUP_TEST + "/rerun_until_failure_retries"
# Report Setiings
@@ -62,6 +63,7 @@ const SHORTCUT_INSPECTOR_RERUN_TEST = GROUP_SHORTCUT_INSPECTOR + "/rerun_test"
const SHORTCUT_INSPECTOR_RERUN_TEST_DEBUG = GROUP_SHORTCUT_INSPECTOR + "/rerun_test_debug"
const SHORTCUT_INSPECTOR_RUN_TEST_OVERALL = GROUP_SHORTCUT_INSPECTOR + "/run_test_overall"
const SHORTCUT_INSPECTOR_RUN_TEST_STOP = GROUP_SHORTCUT_INSPECTOR + "/run_test_stop"
const SHORTCUT_INSPECTOR_RERUN_TEST_UNTIL_FAILURE = GROUP_SHORTCUT_INSPECTOR + "/rerun_test_until_failure"
const GROUP_SHORTCUT_EDITOR = SHORTCUT_SETTINGS + "/editor"
const SHORTCUT_EDITOR_RUN_TEST = GROUP_SHORTCUT_EDITOR + "/run_test"
@@ -112,6 +114,7 @@ static func setup() -> void:
create_property_if_need(TEST_DISCOVER_ENABLED, false, "Automatically detect new tests in test lookup folders at runtime")
create_property_if_need(TEST_FLAKY_CHECK, false, "Rerun tests on failure and mark them as FLAKY")
create_property_if_need(TEST_FLAKY_MAX_RETRIES, 3, "Sets the number of retries for rerunning a flaky test")
create_property_if_need(TEST_RERUN_UNTIL_FAILURE_RETRIES, 10, "The number of reruns until the test fails.")
# report settings
create_property_if_need(REPORT_PUSH_ERRORS, false, "Report push_error() as failure")
create_property_if_need(REPORT_SCRIPT_ERRORS, true, "Report script errors as failure")
@@ -148,6 +151,7 @@ static func create_shortcut_properties_if_need() -> void:
# inspector
create_property_if_need(SHORTCUT_INSPECTOR_RERUN_TEST, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RERUN_TESTS), "Rerun the most recently executed tests")
create_property_if_need(SHORTCUT_INSPECTOR_RERUN_TEST_DEBUG, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG), "Rerun the most recently executed tests (Debug mode)")
create_property_if_need(SHORTCUT_INSPECTOR_RERUN_TEST_UNTIL_FAILURE, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RERUN_TESTS_UNTIL_FAILURE), "Rerun tests until failure occurs")
create_property_if_need(SHORTCUT_INSPECTOR_RUN_TEST_OVERALL, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL), "Runs all tests (Debug mode)")
create_property_if_need(SHORTCUT_INSPECTOR_RUN_TEST_STOP, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.STOP_TEST_RUN), "Stop the current test execution")
# script editor
@@ -155,8 +159,8 @@ static func create_shortcut_properties_if_need() -> void:
create_property_if_need(SHORTCUT_EDITOR_RUN_TEST_DEBUG, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RUN_TESTCASE_DEBUG), "Run the currently selected test (Debug mode).")
create_property_if_need(SHORTCUT_EDITOR_CREATE_TEST, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.CREATE_TEST), "Create a new test case for the currently selected function")
# filesystem
create_property_if_need(SHORTCUT_FILESYSTEM_RUN_TEST, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.NONE), "Run all test suites in the selected folder or file")
create_property_if_need(SHORTCUT_FILESYSTEM_RUN_TEST_DEBUG, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.NONE), "Run all test suites in the selected folder or file (Debug)")
create_property_if_need(SHORTCUT_FILESYSTEM_RUN_TEST, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RUN_TESTSUITE), "Run all test suites in the selected folder or file")
create_property_if_need(SHORTCUT_FILESYSTEM_RUN_TEST_DEBUG, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RUN_TESTSUITE_DEBUG), "Run all test suites in the selected folder or file (Debug)")
static func create_property_if_need(name :String, default :Variant, help :="", value_set := PackedStringArray()) -> void:
@@ -306,6 +310,10 @@ static func get_flaky_max_retries() -> int:
return get_setting(TEST_FLAKY_MAX_RETRIES, 3)
static func get_rerun_max_retries() -> int:
return get_setting(TEST_RERUN_UNTIL_FAILURE_RETRIES, 10)
static func set_test_discover_enabled(enable :bool) -> void:
var property := get_property(TEST_DISCOVER_ENABLED)
property.set_value(enable)

View File

@@ -1 +1 @@
uid://coby4unvmd3eh
uid://djrx6fy3w3bb

View File

@@ -1 +1 @@
uid://ckx5jnr3ip6vp
uid://l1nsecnjoon6

View File

@@ -107,7 +107,7 @@ func is_signal_collecting(emitter: Object, signal_name: String) -> bool:
return _collected_signals.has(emitter) and (_collected_signals[emitter] as Dictionary).has(signal_name)
func match(emitter :Object, signal_name :String, args :Array) -> bool:
func match(emitter: Object, signal_name: String, args: Array) -> bool:
#prints("match", signal_name, _collected_signals[emitter][signal_name]);
if _collected_signals.is_empty() or not _collected_signals.has(emitter):
return false

View File

@@ -1 +1 @@
uid://cm0rbs8vhdhd1
uid://bnvdsssykfaeh

View File

@@ -33,9 +33,9 @@ signal gdunit_client_connected(client_id: int)
signal gdunit_client_disconnected(client_id: int)
## Emitted when a client terminates unexpectedly.
## Emitted when a the user stops (terminates) the current test session
@warning_ignore("unused_signal")
signal gdunit_client_terminated()
signal gdunit_test_session_terminate()
## Emitted when a test execution event occurs.[br]

View File

@@ -1 +1 @@
uid://kj16fg0hf6kn
uid://7fkqtqq0ib25

View File

@@ -1 +1 @@
uid://4sujouo3vf6d
uid://djyr7is32ffbs

View File

@@ -1 +1 @@
uid://ierjyaem56m3
uid://cg0fqsmpf8fdh

View File

@@ -1 +1 @@
uid://dthfh16tl5wqc
uid://cqe6i2xbwgneb

View File

@@ -70,8 +70,14 @@ func scan_directory(resource_path: String) -> Array[Script]:
func _scan_test_suites_scripts(dir: DirAccess, collected_suites: Array[Script]) -> Array[Script]:
# Skip excluded directories
if dir.file_exists(".gdignore"):
prints("Exclude directory %s, containing .gdignore file" % dir.get_current_dir())
return []
if exclude_scan_directories.has(dir.get_current_dir()):
return collected_suites
var err := dir.list_dir_begin()
if err != OK:
push_error("Error on scanning directory %s" % dir.get_current_dir(), error_string(err))

View File

@@ -1 +1 @@
uid://bju0nt1bgsc2s
uid://bymtxj63ek2kd

View File

@@ -1 +1 @@
uid://d05qgv6uu477i
uid://4tbcywx0qg1d

View File

@@ -1 +1 @@
uid://dehxycxsj68ev
uid://d0d4s6tkgoh3b

View File

@@ -1 +1 @@
uid://dmta1h7ndfnko
uid://dd7g37aslbmm1

View File

@@ -9,7 +9,8 @@ var _attribute: TestCaseAttribute
var _current_iteration: int = -1
var _expect_to_interupt := false
var _timer: Timer
var _interupted: bool = false
var _interupted := false
var _terminated := false
var _failed := false
var _parameter_set_resolver: GdUnitTestParameterSetResolver
var _is_disposed := false
@@ -123,7 +124,7 @@ func do_interrupt() -> void:
# We need to dispose manually the function state here
GdObjects.dispose_function_state(_func_state)
if not is_expect_interupted():
var execution_context:= GdUnitThreadManager.get_current_context().get_execution_context()
var execution_context := GdUnitThreadManager.get_current_context().get_execution_context()
if is_fuzzed():
execution_context.add_report(GdUnitReport.new()\
.create(GdUnitReport.INTERUPTED, line_number(), GdAssertMessages.fuzzer_interuped(_current_iteration, "timedout")))
@@ -133,6 +134,16 @@ func do_interrupt() -> void:
completed.emit()
func do_terminate() -> void:
_terminated = true
# We need to dispose manually the function state here
GdObjects.dispose_function_state(_func_state)
var execution_context := GdUnitThreadManager.get_current_context().get_execution_context()
execution_context.add_report(GdUnitReport.new()\
.create(GdUnitReport.TERMINATED, line_number(), GdAssertMessages.test_session_terminated()))
completed.emit()
func _set_failure_handler() -> void:
if not GdUnitSignals.instance().gdunit_set_test_failed.is_connected(_failure_received):
@warning_ignore("return_value_discarded")
@@ -172,6 +183,10 @@ func is_expect_interupted() -> bool:
return _expect_to_interupt
func is_terminated() -> bool:
return _terminated
func is_parameterized() -> bool:
return _parameter_set_resolver.is_parameterized()
@@ -192,11 +207,6 @@ func test_name() -> String:
return _test_case.test_name
@warning_ignore("native_method_override")
func get_name() -> StringName:
return _test_case.test_name
func line_number() -> int:
return _test_case.line_number

View File

@@ -1 +1 @@
uid://cb2lkpvh0liiv
uid://dvhr6i0bdk05n

View File

@@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://csgvrbao53xmv"
uid="uid://c0lvcprd6501t"
path="res://.godot/imported/touch-button.png-2fff40c8520d8e97a57db1b2b043f641.ctex"
metadata={
"vram_texture": false

View File

@@ -1 +1 @@
uid://d2bres53mgxnw
uid://b3ilfkx1js423

View File

@@ -0,0 +1,64 @@
@abstract class_name GdUnitBaseCommand
extends Node
var id: String
var icon: Texture2D
var shortcut: Shortcut = null
var shortcut_type: GdUnitShortcut.ShortCut
func _init(p_id: String, p_shortcut: GdUnitShortcut.ShortCut = GdUnitShortcut.ShortCut.NONE) -> void:
id = p_id
shortcut_type = p_shortcut
_set_shortcut()
func _shortcut_input(event: InputEvent) -> void:
if is_running():
return
if shortcut and shortcut.matches_event(event):
execute()
get_viewport().set_input_as_handled()
func update_shortcut() -> void:
_set_shortcut()
func _set_shortcut() -> void:
if shortcut_type == GdUnitShortcut.ShortCut.NONE:
return
var property_name := GdUnitShortcut.as_property(shortcut_type)
var property := GdUnitSettings.get_property(property_name)
var keys := GdUnitShortcut.default_keys(shortcut_type)
if property != null:
keys = property.value()
var inputEvent := _create_shortcut_input_even(keys)
shortcut = Shortcut.new()
shortcut.set_events([inputEvent])
func _create_shortcut_input_even(key_codes: PackedInt32Array) -> InputEventKey:
var inputEvent := InputEventKey.new()
inputEvent.pressed = true
for key_code in key_codes:
match key_code:
KEY_ALT:
inputEvent.alt_pressed = true
KEY_SHIFT:
inputEvent.shift_pressed = true
KEY_CTRL:
inputEvent.ctrl_pressed = true
_:
inputEvent.keycode = key_code as Key
inputEvent.physical_keycode = key_code as Key
return inputEvent
@abstract func is_running() -> bool
@abstract func execute(...parameters: Array) -> void

View File

@@ -0,0 +1 @@
uid://bxuturao0ahb5

Some files were not shown because too many files have changed in this diff Show More