Compare commits

...

63 Commits

Author SHA1 Message Date
0e412c2a42 fixed level loading and starting to setup proper gyms, zoos and playtest levels
All checks were successful
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 / Test (push) Successful in 8m23s
Create tag and build when new code gets to main / Export (push) Successful in 10m40s
2026-02-03 16:31:37 +01:00
8a3faff7d0 small refacto of dash actions 2026-02-03 15:10:22 +01:00
3525f0e3eb added a parry button and animation that lets player chose their enemy hit behaviour
All checks were successful
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 5m57s
Create tag and build when new code gets to main / Export (push) Successful in 7m43s
2026-01-30 13:19:28 +01:00
cc973b9f0d fixed spawners
All checks were successful
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 6m4s
Create tag and build when new code gets to main / Export (push) Successful in 8m2s
2026-01-28 19:14:05 +01:00
8a552f7993 #minor CI FIXED
All checks were successful
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 / Test (push) Successful in 6m0s
Create tag and build when new code gets to main / Export (push) Successful in 7m58s
2026-01-28 18:28:40 +01:00
93841bc85d manually uploading to itch
All checks were successful
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 / Test (push) Successful in 5m58s
Create tag and build when new code gets to main / Export (push) Successful in 7m52s
2026-01-28 18:15:15 +01:00
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
ddf1bd019b better healthbars and one for the player as well
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 42s
Create tag and build when new code gets to main / Test (push) Successful in 6m13s
Create tag and build when new code gets to main / Export (push) Successful in 7m5s
2026-01-26 18:09:29 +01:00
2fdc9c7ca8 instanciating explosion on slam
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Has been cancelled
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-26 16:34:18 +01:00
d79ca44972 everything should work fine now
All checks were successful
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) Successful in 8m7s
Create tag and build when new code gets to main / Export (push) Successful in 7m13s
2026-01-26 09:26:54 +01:00
148aea9bb4 trying to remove ad unit addon from build job because it breaks
All checks were successful
Create tag and build when new code gets to main / Export (push) Successful in 6m59s
2026-01-26 09:18:28 +01:00
bdce8b969c reinstalling GDUnit from assetlib
Some checks failed
Create tag and build when new code gets to main / Export (push) Failing after 6m41s
2026-01-26 09:05:55 +01:00
4095f818f6 gdunit addon post import by godot
All checks were successful
Create tag and build when new code gets to main / Export (push) Successful in 7m17s
2026-01-26 08:59:34 +01:00
72bf3d4cc5 making sure the issue comes from GDUnit addon folder
All checks were successful
Create tag and build when new code gets to main / Export (push) Successful in 7m6s
2026-01-26 08:51:14 +01:00
51907a1f01 trying to fix Export
All checks were successful
Create tag and build when new code gets to main / Export (push) Successful in 6m53s
2026-01-26 08:41:48 +01:00
c5c4ceb032 trying to fix Export
Some checks failed
Create tag and build when new code gets to main / Export (push) Failing after 6m47s
2026-01-26 08:30:31 +01:00
64957334de trying to fix Export
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) Has been cancelled
2026-01-26 08:29:43 +01:00
7323b6e814 trying to fix Export
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 8m32s
2026-01-26 08:12:23 +01:00
24f9801093 trying to fix Export
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 / Export (push) Failing after 7m16s
2026-01-26 07:58:33 +01:00
bcc8af76c2 trying to fix Export
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 6m41s
2026-01-25 23:58:19 +01:00
2394 changed files with 46392 additions and 8230 deletions

View File

@@ -10,50 +10,31 @@ on:
- "**" - "**"
env: env:
GODOT_VERSION: 4.6
GAME_NAME: MovementTests GAME_NAME: MovementTests
ITCHIO_USERNAME: Minimata ITCHIO_USERNAME: Minimata
ITCHIO_GAMEID: MovementTests ITCHIO_GAMEID: MovementTests
jobs: jobs:
Export: Export:
runs-on: ubuntu-latest runs-on: godot
steps: steps:
- name: Install node, xvfb and curl - name: Checkout with LFS
run: | uses: https://git.game-dev.space/minimata/checkout-with-lfs.git@main
apt update && apt -y install curl nodejs xvfb
- name: Checkout - name: Run tests
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: Launch Godot
uses: godot-gdunit-labs/gdUnit4-action@v1 uses: godot-gdunit-labs/gdUnit4-action@v1
with: with:
godot-version: '4.5.1' godot-version: ${GODOT_VERSION}
godot-net: true godot-net: true
godot-force-mono: true godot-force-mono: true
dotnet-version: 'net9.0' dotnet-version: 'net9.0'
version: 'v6.0.3'
paths: | paths: |
res://tests/ res://tests/
timeout: 1 timeout: 1
publish-report: false publish-report: false
upload-report: false upload-report: false
- name: Test action
run: echo ${{ github.workspace }}
- name: Upload test report - name: Upload test report
uses: actions/upload-artifact@v3-node20 uses: actions/upload-artifact@v3-node20
with: with:

View File

@@ -8,6 +8,7 @@ on:
- "**" - "**"
env: env:
GODOT_VERSION: 4.6
GAME_NAME: MovementTests GAME_NAME: MovementTests
ITCHIO_USERNAME: Minimata ITCHIO_USERNAME: Minimata
ITCHIO_GAMEID: MovementTests ITCHIO_GAMEID: MovementTests
@@ -21,6 +22,7 @@ jobs:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
token: ${{ secrets.TOKEN }}
lfs: false lfs: false
- name: Remove buggy pre-push hook - name: Remove buggy pre-push hook
run: | run: |
@@ -37,35 +39,20 @@ jobs:
DEFAULT_BUMP: patch DEFAULT_BUMP: patch
Test: Test:
runs-on: ubuntu-latest runs-on: godot
# env:
# RUNNER_TOOL_CACHE: /toolcache # Runner Tool Cache
steps: steps:
- name: Install node, xvfb and curl - name: Checkout with LFS
run: | uses: https://git.game-dev.space/minimata/checkout-with-lfs.git@main
apt update && apt -y install curl nodejs xvfb
- name: Checkout - name: Run tests
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: Launch Godot
uses: godot-gdunit-labs/gdUnit4-action@v1 uses: godot-gdunit-labs/gdUnit4-action@v1
with: with:
godot-version: '4.5.1' godot-version: ${GODOT_VERSION}
godot-net: true godot-net: true
godot-force-mono: true godot-force-mono: true
dotnet-version: 'net9.0' dotnet-version: 'net9.0'
version: 'v6.0.3'
paths: | paths: |
res://tests/ res://tests/
timeout: 1 timeout: 1
@@ -79,48 +66,51 @@ jobs:
path: ${{ github.workspace }}/reports/test-result.html path: ${{ github.workspace }}/reports/test-result.html
Export: Export:
runs-on: ubuntu-latest runs-on: godot
needs: needs:
- BumpTag - BumpTag
- Test # Wait for tests to finish
container:
image: barichello/godot-ci:mono-4.5
steps: steps:
- name: Install node, curl and zip - name: Checkout with LFS
run: | uses: https://git.game-dev.space/minimata/checkout-with-lfs.git@main
apt update && apt -y install curl zip nodejs
- name: Checkout - name: Setup Godot
uses: actions/checkout@v6 id: setup-godot
uses: https://git.game-dev.space/minimata/setup-godot.git@main
with: with:
lfs: false godot-version: '4.6'
persist-credentials: true dotnet-version: 'net9.0'
- name: Checkout LFS - name: Remove GDUnit addon
run: | run: |
git lfs install --local rm -rf ${{ gitea.workspace }}/addons/gdUnit4
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
- name: Build Windows - name: Build Windows
run: | run: |
mkdir -v -p build/windows 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 zip -r Windows.zip build/windows
- name: Upload to Itch
uses: KikimoraGames/itch-publish@v0.0.3 - name: Setup Butler
with: shell: bash
butlerApiKey: ${{ secrets.BUTLER_TOKEN }} env:
itchUsername: ${{ env.ITCHIO_USERNAME }} BUTLER_API_KEY: ${{ secrets.BUTLER_TOKEN }}
itchGameId: ${{ env.ITCHIO_GAMEID }} run: |
buildNumber: ${{ needs.BumpTag.outputs.tag_name }} mkdir ./tools 2>/dev/null || true
gameData: Windows.zip pushd tools
buildChannel: windows curl -sSLfo ./butler.zip "https://broth.itch.zone/butler/linux-amd64/LATEST/archive/default"
unzip butler.zip
chmod +x ./butler
popd
./tools/butler -V
- name: Upload to itch.io
shell: bash
env:
BUTLER_API_KEY: ${{ secrets.BUTLER_TOKEN }}
run: |
versionArgument="--userversion ${{ needs.BumpTag.outputs.tag_name }}"
./tools/butler push \
"Windows.zip" \
${{ env.ITCHIO_USERNAME }}/${{ env.ITCHIO_GAMEID }}:windows ${versionArgument}

View File

@@ -8,6 +8,7 @@ on:
- "**" - "**"
env: env:
GODOT_VERSION: 4.6
GAME_NAME: MovementTests GAME_NAME: MovementTests
ITCHIO_USERNAME: Minimata ITCHIO_USERNAME: Minimata
ITCHIO_GAMEID: MovementTests ITCHIO_GAMEID: MovementTests
@@ -26,92 +27,90 @@ jobs:
separator: '/' separator: '/'
Release: Release:
runs-on: ubuntu-latest runs-on: godot
if: ${{ contains(gitea.ref_name, 'release/') }} if: ${{ contains(gitea.ref_name, 'release/') }}
needs: ReleaseName needs: ReleaseName
container:
image: barichello/godot-ci:mono-4.5
steps: steps:
- name: Install node, curl and zip - name: Checkout with LFS
run: | uses: https://git.game-dev.space/minimata/checkout-with-lfs.git@main
apt update && apt -y install curl zip nodejs
- name: Checkout - name: Setup Godot
uses: actions/checkout@v6 id: setup-godot
uses: https://git.game-dev.space/minimata/setup-godot.git@main
with: with:
lfs: false godot-version: '4.6'
persist-credentials: true dotnet-version: 'net9.0'
- name: Checkout LFS - name: Setup Butler
shell: bash
env:
BUTLER_API_KEY: ${{ secrets.BUTLER_TOKEN }}
run: | run: |
git lfs install --local mkdir ./tools 2>/dev/null || true
AUTH=$(git config http.${{ gitea.server_url }}/.extraheader) pushd tools
AUTH_FILE=$(git config includeif.gitdir:/workspace/${{ gitea.repository }}/.git.path) curl -sSLfo ./butler.zip "https://broth.itch.zone/butler/linux-amd64/LATEST/archive/default"
git config -f $AUTH_FILE --unset http.${{ gitea.server_url }}/.extraheader unzip butler.zip
git config -f $AUTH_FILE http.${{ gitea.server_url }}/${{ gitea.repository }}.git/info/lfs/objects/batch.extraheader "$AUTH" chmod +x ./butler
git lfs pull popd
./tools/butler -V
- name: Import resources and build solution
run: |
godot --headless --editor --build-solutions --quit --import --path $PWD
- name: Build Windows - name: Build Windows
run: | run: |
mkdir -v -p build/windows mkdir -v -p build/windows
godot --headless --verbose --build-solutions --export-release "Windows Desktop" build/windows/${{ env.GAME_NAME }}.exe godot --headless --verbose --build-solutions --export-release "Windows Desktop" build/windows/${{ env.GAME_NAME }}.exe
zip -r Windows.zip build/windows zip -r Windows.zip build/windows
- name: Upload to Itch - name: Upload Windows to itch.io
uses: KikimoraGames/itch-publish@v0.0.3 shell: bash
with: env:
butlerApiKey: ${{ secrets.BUTLER_TOKEN }} BUTLER_API_KEY: ${{ secrets.BUTLER_TOKEN }}
itchUsername: ${{ env.ITCHIO_USERNAME }} run: |
itchGameId: ${{ env.ITCHIO_GAMEID }} versionArgument="--userversion ${{ needs.ReleaseName.outputs.release_name }}"
buildNumber: ${{ needs.ReleaseName.outputs.release_name }} ./tools/butler push \
gameData: Windows.zip "Windows.zip" \
buildChannel: windows ${{ env.ITCHIO_USERNAME }}/${{ env.ITCHIO_GAMEID }}:windows ${versionArgument}
- name: Build Windows ARM - name: Build Windows ARM
run: | run: |
mkdir -v -p build/windowsArm mkdir -v -p build/windowsArm
godot --headless --verbose --build-solutions --export-release "Windows ARM" build/windowsArm/${{ env.GAME_NAME }}.exe godot --headless --verbose --build-solutions --export-release "Windows ARM" build/windowsArm/${{ env.GAME_NAME }}.exe
zip -r WindowsArm.zip build/windowsArm zip -r WindowsArm.zip build/windowsArm
- name: Upload to Itch - name: Upload Windows to itch.io
uses: KikimoraGames/itch-publish@v0.0.3 shell: bash
with: env:
butlerApiKey: ${{ secrets.BUTLER_TOKEN }} BUTLER_API_KEY: ${{ secrets.BUTLER_TOKEN }}
itchUsername: ${{ env.ITCHIO_USERNAME }} run: |
itchGameId: ${{ env.ITCHIO_GAMEID }} versionArgument="--userversion ${{ needs.ReleaseName.outputs.release_name }}"
buildNumber: ${{ needs.ReleaseName.outputs.release_name }} ./tools/butler push \
gameData: WindowsArm.zip "WindowsArm.zip" \
buildChannel: windows-arm ${{ env.ITCHIO_USERNAME }}/${{ env.ITCHIO_GAMEID }}:windows-arm ${versionArgument}
- name: Linux Build - name: Linux Build
run: | run: |
mkdir -v -p build/linux mkdir -v -p build/linux
godot --headless --verbose --export-release "Linux/X11" build/linux/${{ env.GAME_NAME }}.x86_64 godot --headless --verbose --export-release "Linux/X11" build/linux/${{ env.GAME_NAME }}.x86_64
zip -r Linux.zip build/linux zip -r Linux.zip build/linux
- name: Upload to Itch - name: Upload Windows to itch.io
uses: KikimoraGames/itch-publish@v0.0.3 shell: bash
with: env:
butlerApiKey: ${{ secrets.BUTLER_TOKEN }} BUTLER_API_KEY: ${{ secrets.BUTLER_TOKEN }}
itchUsername: ${{ env.ITCHIO_USERNAME }} run: |
itchGameId: ${{ env.ITCHIO_GAMEID }} versionArgument="--userversion ${{ needs.ReleaseName.outputs.release_name }}"
buildNumber: ${{ needs.ReleaseName.outputs.release_name }} ./tools/butler push \
gameData: Linux.zip "Linux.zip" \
buildChannel: linux ${{ env.ITCHIO_USERNAME }}/${{ env.ITCHIO_GAMEID }}:linux ${versionArgument}
- name: Mac Build - name: Mac Build
run: | run: |
mkdir -v -p build/mac mkdir -v -p build/mac
godot --headless --verbose --export-release "macOS" build/mac/${{ env.GAME_NAME }}.zip godot --headless --verbose --export-release "macOS" build/mac/${{ env.GAME_NAME }}.zip
zip -r Mac.zip build/mac zip -r Mac.zip build/mac
- name: Upload to Itch - name: Upload Windows to itch.io
uses: KikimoraGames/itch-publish@v0.0.3 shell: bash
with: env:
butlerApiKey: ${{ secrets.BUTLER_TOKEN }} BUTLER_API_KEY: ${{ secrets.BUTLER_TOKEN }}
itchUsername: ${{ env.ITCHIO_USERNAME }} run: |
itchGameId: ${{ env.ITCHIO_GAMEID }} versionArgument="--userversion ${{ needs.ReleaseName.outputs.release_name }}"
buildNumber: ${{ needs.ReleaseName.outputs.release_name }} ./tools/butler push \
gameData: Mac.zip "Mac.zip" \
buildChannel: mac ${{ env.ITCHIO_USERNAME }}/${{ env.ITCHIO_GAMEID }}:mac ${versionArgument}

View File

@@ -1,4 +1,4 @@
<Project Sdk="Godot.NET.Sdk/4.5.0"> <Project Sdk="Godot.NET.Sdk/4.6.0">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading> <EnableDynamicLoading>true</EnableDynamicLoading>
@@ -131,9 +131,9 @@
</ItemGroup> </ItemGroup>
<!-- gdUnit4 package dependencies --> <!-- gdUnit4 package dependencies -->
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0"/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="gdUnit4.api" Version="5.1.0-rc3"/> <PackageReference Include="gdUnit4.api" Version="5.1.0-rc3" />
<PackageReference Include="gdUnit4.test.adapter" Version="3.0.0"/> <PackageReference Include="gdUnit4.test.adapter" Version="3.0.0" />
<PackageReference Include="gdUnit4.analyzers" Version="1.0.0"> <PackageReference Include="gdUnit4.analyzers" Version="1.0.0">
<PrivateAssets>none</PrivateAssets> <PrivateAssets>none</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <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" name="gdUnit4"
description="Unit Testing Framework for Godot Scripts" description="Unit Testing Framework for Godot Scripts"
author="Mike Schulze" author="Mike Schulze"
version="6.0.3" version="6.1.0"
script="plugin.gd" script="plugin.gd"

View File

@@ -1,23 +1,29 @@
@tool @tool
extends EditorPlugin 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_inspector: Control
var _gd_console: Control var _gd_console: Control
var _gd_filesystem_context_menu: Variant var _filesystem_context_menu: EditorContextMenuPlugin
var _gd_scripteditor_context_menu: Variant var _editor_context_menu: EditorContextMenuPlugin
var _editor_code_context_menu: EditorContextMenuPlugin
func _enter_tree() -> void: func _enter_tree() -> void:
var inferred_declaration: int = ProjectSettings.get_setting("debug/gdscript/warnings/inferred_declaration") 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: '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.") printerr("Loading GdUnit4 Plugin failed.")
return return
@@ -73,36 +79,21 @@ func check_running_in_test_env() -> bool:
func _add_context_menus() -> void: func _add_context_menus() -> void:
if Engine.get_version_info().hex >= 0x40400: _filesystem_context_menu = preload("res://addons/gdUnit4/src/ui/menu/GdUnitEditorFileSystemContextMenuHandler.gd").new()
# With Godot 4.4 we have to use the 'add_context_menu_plugin' to register editor context menus _editor_context_menu = preload("res://addons/gdUnit4/src/ui/menu/GdUnitScriptEditorContextMenuHandler.gd").new()
_gd_filesystem_context_menu = _preload_gdx_script("res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandlerV44.gdx") _editor_code_context_menu = preload("res://addons/gdUnit4/src/ui/menu/GdUnitScriptEditorContextMenuHandler.gd").new()
call_deferred("add_context_menu_plugin", CONTEXT_SLOT_FILESYSTEM, _gd_filesystem_context_menu) add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_FILESYSTEM, _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 add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _editor_context_menu)
#_gd_scripteditor_context_menu = _preload("res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandlerV44.gdx") add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, _editor_code_context_menu)
#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())
func _remove_context_menus() -> void: func _remove_context_menus() -> void:
if is_instance_valid(_gd_filesystem_context_menu): if is_instance_valid(_filesystem_context_menu):
call_deferred("remove_context_menu_plugin", _gd_filesystem_context_menu) remove_context_menu_plugin(_filesystem_context_menu)
if is_instance_valid(_gd_scripteditor_context_menu): if is_instance_valid(_editor_context_menu):
call_deferred("remove_context_menu_plugin", _gd_scripteditor_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 _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()
func _on_resource_saved(resource: Resource) -> void: 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] ## 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 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] ## [codeblock]
## func test_key_presssed(): ## func test_key_presssed():
## var runner = scene_runner("res://scenes/simple_scene.tscn") ## 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 @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 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] ## [codeblock]
## [member ctrl_pressed] : false by default set to true if simmulate control is press[br] ## # 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 @abstract func simulate_key_press(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner
## Simulates that a key has been released.[br] ## Simulates that a key has been released.[br]
## [member key_code] : the key code e.g. [constant KEY_ENTER][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] ## [codeblock]
## [member ctrl_pressed] : false by default set to true if simmulate control is press[br] ## # 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 @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 @abstract func append_failure_message(message: String) -> GdUnitSignalAssert
## Verifies that given signal is emitted until waiting time ## Verifies that the specified signal is emitted with the expected arguments.[br]
@abstract func is_emitted(name: String, args := []) -> GdUnitSignalAssert ##
## 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 ## Verifies that the specified signal is NOT emitted with the expected arguments.[br]
@abstract func is_not_emitted(name: String, args := []) -> GdUnitSignalAssert ##
## 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 ## Verifies that the specified signal exists on the emitter object.[br]
@abstract func is_signal_exists(name: String) -> GdUnitSignalAssert ##
## 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] ## 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] ## 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 @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 class_name GdUnitTestSuite
extends Node extends Node
const NO_ARG :Variant = GdUnitConstants.NO_ARG
### internal runtime variables that must not be overwritten!!! ### internal runtime variables that must not be overwritten!!!
@warning_ignore("unused_private_class_variable") @warning_ignore("unused_private_class_variable")
var __is_skipped := false var __is_skipped := false
@warning_ignore("unused_private_class_variable") @warning_ignore("unused_private_class_variable")
var __skip_reason :String = "Unknow." var __skip_reason := "Unknow."
var __active_test_case :String var __active_test_case: String
var __awaiter := __gdunit_awaiter() var __awaiter := __gdunit_awaiter()
@@ -29,7 +28,7 @@ var __awaiter := __gdunit_awaiter()
### in order to noticeably reduce the loading time of the test suite. ### 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 # 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 # 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) return GdUnitAssertions.__lazy_load(script_path)
@@ -81,23 +80,23 @@ func after_test() -> void:
pass 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 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 __active_test_case = test_case
# === Tools ==================================================================== # === Tools ====================================================================
# Mapps Godot error number to a readable error message. See at ERROR # 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 # 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) return error_string(error_number)
## A litle helper to auto freeing your created objects after test execution ## 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() 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.") 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") @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) super.add_child(node, force_readable_name, internal)
var execution_context := GdUnitThreadManager.get_current_context().get_execution_context() var execution_context := GdUnitThreadManager.get_current_context().get_execution_context()
if execution_context != null: 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] ## Creates a new directory under the temporary directory *user://tmp*[br]
## Useful for storing data during test execution. [br] ## Useful for storing data during test execution. [br]
## The directory is automatically deleted after test suite execution ## 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") @warning_ignore("unsafe_method_access")
return __gdunit_file_access().create_temp_dir(relative_path) 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] ## 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] ## 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 ## 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") @warning_ignore("unsafe_method_access")
return __gdunit_file_access().create_temp_file(relative_path, file_name, mode) return __gdunit_file_access().create_temp_file(relative_path, file_name, mode)
## Reads a resource by given path <resource_path> into a PackedStringArray. ## 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") @warning_ignore("unsafe_method_access")
return __gdunit_file_access().resource_as_array(resource_path) return __gdunit_file_access().resource_as_array(resource_path)
## Reads a resource by given path <resource_path> and returned the content as String. ## 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") @warning_ignore("unsafe_method_access")
return __gdunit_file_access().resource_as_string(resource_path) 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 ## 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") @warning_ignore("unsafe_method_access", "unsafe_cast")
return str_to_var(__gdunit_file_access().resource_as_string(resource_path) as String) 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] ## signal_name: signal name[br]
## args: the expected signal arguments as an array[br] ## args: the expected signal arguments as an array[br]
## timeout: the timeout in ms, default is set to 2000ms ## 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") @warning_ignore("unsafe_method_access")
return await __awaiter.await_signal_on(source, signal_name, args, timeout) 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 ## await await_millis(myNode, 100).completed
## [/codeblock][br] ## [/codeblock][br]
## use this waiter and not `await get_tree().create_timer().timeout to prevent errors when a test case is timed out ## 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") @warning_ignore("unsafe_method_access")
await __awaiter.await_millis(timeout) 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] ## 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] ## The runner will manage the scene instance and release after the runner is released[br]
## example:[br] ## example:[br]
@@ -200,7 +238,7 @@ func await_millis(timeout :int) -> void:
## # or simply creates a runner by using the scene resource path ## # or simply creates a runner by using the scene resource path
## var runner := scene_runner("res://foo/my_scne.tscn") ## var runner := scene_runner("res://foo/my_scne.tscn")
## [/codeblock] ## [/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)) 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 ## 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") @warning_ignore("unsafe_method_access")
return __lazy_load("res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd").build(clazz, mock_mode) return __lazy_load("res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd").build(clazz, mock_mode)
## Creates a spy checked given object instance ## Creates a spy checked given object instance
func spy(instance :Variant) -> Variant: func spy(instance: Variant) -> Variant:
@warning_ignore("unsafe_method_access") @warning_ignore("unsafe_method_access")
return __lazy_load("res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd").build(instance) 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 ## # overrides the return value of myMock.is_selected() to false
## do_return(false).on(myMock).is_selected() ## do_return(false).on(myMock).is_selected()
## [/codeblock] ## [/codeblock]
func do_return(value :Variant) -> GdUnitMock: func do_return(value: Variant) -> GdUnitMock:
return GdUnitMock.new(value) return GdUnitMock.new(value)
## Verifies certain behavior happened at least once or exact number of times ## 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") @warning_ignore("unsafe_method_access")
return __gdunit_object_interactions().verify(obj, times) return __gdunit_object_interactions().verify(obj, times)
## Verifies no interactions is happen checked this mock or spy ## 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") @warning_ignore("unsafe_method_access")
return __gdunit_object_interactions().verify_no_interactions(obj) return __gdunit_object_interactions().verify_no_interactions(obj)
## Verifies the given mock or spy has any unverified interaction. ## 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") @warning_ignore("unsafe_method_access")
return __gdunit_object_interactions().verify_no_more_interactions(obj) return __gdunit_object_interactions().verify_no_more_interactions(obj)
## Resets the saved function call counters checked a mock or spy ## 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") @warning_ignore("unsafe_method_access")
__gdunit_object_interactions().reset(obj) __gdunit_object_interactions().reset(obj)
@@ -273,7 +311,7 @@ func reset(obj :Variant) -> void:
## # verify the signial is emitted ## # verify the signial is emitted
## await assert_signal(emitter).is_emitted('my_signal') ## await assert_signal(emitter).is_emitted('my_signal')
## [/codeblock] ## [/codeblock]
func monitor_signals(source :Object, _auto_free := true) -> Object: func monitor_signals(source: Object, _auto_free := true) -> Object:
@warning_ignore("unsafe_method_access") @warning_ignore("unsafe_method_access")
__lazy_load("res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd")\ __lazy_load("res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd")\
.get_current_context()\ .get_current_context()\
@@ -502,29 +540,46 @@ func any_class(clazz :Object) -> GdUnitArgumentMatcher:
# === value extract utils ====================================================== # === value extract utils ======================================================
## Builds an extractor by given function name and optional arguments ## 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) return __lazy_load("res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd").new(func_name, args)
## Constructs a tuple by given arguments ## Creates a GdUnitTuple from the provided arguments for use in test assertions.
func tuple(arg0 :Variant, ## [br]
arg1 :Variant=NO_ARG, ## This is the primary helper function for creating tuples in GdUnit4 tests.
arg2 :Variant=NO_ARG, ## It provides a convenient way to group multiple expected values when using
arg3 :Variant=NO_ARG, ## [method extractv] assertions. The function enforces that tuples must contain
arg4 :Variant=NO_ARG, ## at least two values, as single-value extractions don't require tuple grouping.
arg5 :Variant=NO_ARG, ## [br]
arg6 :Variant=NO_ARG, ## [b]Parameters:[/b] [br]
arg7 :Variant=NO_ARG, ## - [code]...args[/code]: Variable number of arguments (minimum 2) to group into a tuple.
arg8 :Variant=NO_ARG, ## Each argument represents a value to be compared in assertions.
arg9 :Variant=NO_ARG) -> GdUnitTuple: ## [br]
return GdUnitTuple.new(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) ## [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 ================================================================== # === Asserts ==================================================================
## The common assertion tool to verify values. ## The common assertion tool to verify values.
## It checks the given value by type to fit to the best assert ## 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): match typeof(current):
TYPE_BOOL: TYPE_BOOL:
return assert_bool(current) return assert_bool(current)
@@ -549,22 +604,22 @@ func assert_that(current :Variant) -> GdUnitAssert:
## An assertion tool to verify boolean values. ## 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) return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd").new(current)
## An assertion tool to verify String values. ## 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) return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd").new(current)
## An assertion tool to verify integer values. ## 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) return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd").new(current)
## An assertion tool to verify float values. ## 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) return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd").new(current)
@@ -574,41 +629,41 @@ func assert_float(current :Variant) -> GdUnitFloatAssert:
## [codeblock] ## [codeblock]
## assert_vector(Vector2(1.2, 1.000001)).is_equal(Vector2(1.2, 1.000001)) ## assert_vector(Vector2(1.2, 1.000001)).is_equal(Vector2(1.2, 1.000001))
## [/codeblock] ## [/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) return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd").new(current, type_check)
## An assertion tool to verify arrays. ## 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) return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd").new(current, type_check)
## An assertion tool to verify dictionaries. ## 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) return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd").new(current)
## An assertion tool to verify FileAccess. ## 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) return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd").new(current)
## An assertion tool to verify Objects. ## 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) 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) 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 ## 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) 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. ## 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) 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)) \ ## assert_failure(func(): assert_bool(true).is_not_equal(true)) \
## .has_message("Expecting:\n 'true'\n not equal to\n 'true'") ## .has_message("Expecting:\n 'true'\n not equal to\n 'true'")
## [/codeblock] ## [/codeblock]
func assert_failure(assertion :Callable) -> GdUnitFailureAssert: func assert_failure(assertion: Callable) -> GdUnitFailureAssert:
@warning_ignore("unsafe_method_access") @warning_ignore("unsafe_method_access")
return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd").new().execute(assertion) 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)) \ ## await assert_failure_await(func(): assert_bool(true).is_not_equal(true)) \
## .has_message("Expecting:\n 'true'\n not equal to\n 'true'") ## .has_message("Expecting:\n 'true'\n not equal to\n 'true'")
## [/codeblock] ## [/codeblock]
func assert_failure_await(assertion :Callable) -> GdUnitFailureAssert: func assert_failure_await(assertion: Callable) -> GdUnitFailureAssert:
@warning_ignore("unsafe_method_access") @warning_ignore("unsafe_method_access")
return await __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd").new().execute_and_await(assertion) 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') )\ ## await assert_error(func (): push_error('test error') )\
## .is_push_error('test error') ## .is_push_error('test error')
## [/codeblock] ## [/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) 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 class_name GdUnitTuple
extends RefCounted extends RefCounted
const NO_ARG :Variant = GdUnitConstants.NO_ARG var _values: Array = []
var __values :Array = Array()
func _init(arg0:Variant, ## Initializes a new GdUnitTuple with test values.
arg1 :Variant=NO_ARG, ## [br]
arg2 :Variant=NO_ARG, ## Creates a tuple to hold multiple values extracted from test objects
arg3 :Variant=NO_ARG, ## or expected values for assertions. Commonly used with the [code]tuple()[/code]
arg4 :Variant=NO_ARG, ## helper function in GdUnit4 tests.
arg5 :Variant=NO_ARG, ## [br]
arg6 :Variant=NO_ARG, ## [b]Parameters:[/b]
arg7 :Variant=NO_ARG, ## - [code]...args[/code]: Variable number of values to store.
arg8 :Variant=NO_ARG, func _init(...args: Array) -> void:
arg9 :Variant=NO_ARG) -> void: _values = args
__values = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG)
## 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: 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: 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 := "" var text := ""
if event is InputEventKey: if event is InputEventKey:
var key_event := event as InputEventKey var key_event := event as InputEventKey
text += "InputEventKey : key='%s', pressed=%s, keycode=%d, physical_keycode=%s" % [ text += """
event.as_text(), key_event.pressed, key_event.keycode, key_event.physical_keycode] 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: else:
text += event.as_text() text += event.as_text()
if event is InputEventMouse: if event is InputEventMouse:
var mouse_event := event as 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: if event is InputEventWithModifiers:
var mouse_event := event as 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.shift_pressed,
mouse_event.alt_pressed, mouse_event.alt_pressed,
mouse_event.ctrl_pressed, mouse_event.ctrl_pressed,
mouse_event.meta_pressed, mouse_event.meta_pressed,
mouse_event.command_or_control_autoremap] mouse_event.command_or_control_autoremap]
return text return text.dedent()
static func _colored_string_div(characters: String) -> String: 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)] 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: static func _colored_value(value :Variant) -> String:
match typeof(value): match typeof(value):
TYPE_STRING, TYPE_STRING_NAME: TYPE_STRING, TYPE_STRING_NAME:
@@ -161,19 +183,53 @@ static func _index_report_as_table(index_reports :Array) -> String:
return table.replace("$cells", cells) 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]" % [ static func orphan_warning(orphans_count: int) -> String:
_warning("WARNING:"), count] 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: static func orphan_detected_on_test_setup(orphans: Array[GdUnitOrphanNodeInfo]) -> String:
return "%s\n Detected <%d> orphan nodes during test setup! [b]Check before_test() and after_test()![/b]" % [ return """
_warning("WARNING:"), count] %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: static func orphan_detected_on_test(orphans: Array[GdUnitOrphanNodeInfo]) -> String:
return "%s\n Detected <%d> orphan nodes during test execution!" % [ return """
_warning("WARNING:"), count] %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: 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))] 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 # gdlint:disable = mixed-tabs-and-spaces
static func test_suite_skipped(hint :String, skip_count :int) -> String: static func test_suite_skipped(hint :String, skip_count :int) -> String:
return """ 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)]) 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 current_value := _toPackedStringArray(current)
var expected_value := _toPackedStringArray(expected) var expected_value := _toPackedStringArray(expected)
var index_report := Array() var index_report := Array()
@@ -374,24 +374,12 @@ func extractv(...extractors: Array) -> GdUnitArrayAssert:
_current_value_provider = DefaultValueProvider.new(null) _current_value_provider = DefaultValueProvider.new(null)
else: else:
for element: Variant in current: for element: Variant in current:
var ev: Array[Variant] = [ 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
]
for index: int in extractors.size(): for index: int in extractors.size():
var extractor: GdUnitValueExtractor = extractors[index] var extractor: GdUnitValueExtractor = extractors[index]
ev[index] = extractor.extract_value(element) ev.append(extractor.extract_value(element))
if extractors.size() > 1: 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: else:
extracted_elements.append(ev[0]) extracted_elements.append(ev[0])
_current_value_provider = DefaultValueProvider.new(extracted_elements) _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 _custom_failure_message := ""
var _additional_failure_message := "" var _additional_failure_message := ""
var _callable: Callable var _callable: Callable
var _logger := GodotGdErrorMonitor.new()
func _init(callable: Callable) -> void: func _init(callable: Callable) -> void:
@@ -14,17 +15,10 @@ func _init(callable: Callable) -> void:
func _execute() -> Array[ErrorLogEntry]: func _execute() -> Array[ErrorLogEntry]:
# execute the given code and monitor for runtime errors _logger.start()
if _callable == null or not _callable.is_valid(): await _callable.call()
@warning_ignore("return_value_discarded") _logger.stop()
_report_error("Invalid Callable '%s'" % _callable) return _logger.log_entries()
else:
await _callable.call()
return await _error_monitor().scan(true)
func _error_monitor() -> GodotGdErrorMonitor:
return GdUnitThreadManager.get_current_context().get_execution_context().error_monitor
func failure_message() -> String: 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: func _has_log_entry(log_entries: Array[ErrorLogEntry], type: ErrorLogEntry.TYPE, error: Variant) -> bool:
for entry in log_entries: for entry in log_entries:
if entry._type == type and GdObjects.equals(entry._message, error): 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 true
return false 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: func _to_list(log_entries: Array[ErrorLogEntry]) -> String:
if log_entries.is_empty(): if log_entries.is_empty():
return "no errors" return "no errors"
if log_entries.size() == 1:
return log_entries[0]._message var values := []
var value := ""
for entry in log_entries: for entry in log_entries:
value += "'%s'\n" % entry._message values.append(entry)
return value return "\n".join(values)
func is_null() -> GdUnitGodotErrorAssert: func is_null() -> GdUnitGodotErrorAssert:
@@ -90,6 +81,9 @@ func append_failure_message(message: String) -> GdUnitGodotErrorAssert:
func is_success() -> GdUnitGodotErrorAssert: func is_success() -> GdUnitGodotErrorAssert:
if not _validate_callable():
return self
var log_entries := await _execute() var log_entries := await _execute()
if log_entries.is_empty(): if log_entries.is_empty():
return _report_success() return _report_success()
@@ -100,6 +94,9 @@ func is_success() -> GdUnitGodotErrorAssert:
func is_runtime_error(expected_error: Variant) -> GdUnitGodotErrorAssert: func is_runtime_error(expected_error: Variant) -> GdUnitGodotErrorAssert:
if not _validate_callable():
return self
var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_error) var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_error)
if result.is_error(): if result.is_error():
return _report_error(result.error_message()) return _report_error(result.error_message())
@@ -108,12 +105,15 @@ func is_runtime_error(expected_error: Variant) -> GdUnitGodotErrorAssert:
return _report_success() return _report_success()
return _report_error(""" return _report_error("""
Expecting: a runtime error is triggered. Expecting: a runtime error is triggered.
message: '%s' expected: '%s'
found: %s current: '%s'
""".dedent().trim_prefix("\n") % [expected_error, _to_list(log_entries)]) """.dedent().trim_prefix("\n") % [expected_error, _to_list(log_entries)])
func is_push_warning(expected_warning: Variant) -> GdUnitGodotErrorAssert: func is_push_warning(expected_warning: Variant) -> GdUnitGodotErrorAssert:
if not _validate_callable():
return self
var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_warning) var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_warning)
if result.is_error(): if result.is_error():
return _report_error(result.error_message()) return _report_error(result.error_message())
@@ -122,12 +122,15 @@ func is_push_warning(expected_warning: Variant) -> GdUnitGodotErrorAssert:
return _report_success() return _report_success()
return _report_error(""" return _report_error("""
Expecting: push_warning() is called. Expecting: push_warning() is called.
message: '%s' expected: '%s'
found: %s current: '%s'
""".dedent().trim_prefix("\n") % [expected_warning, _to_list(log_entries)]) """.dedent().trim_prefix("\n") % [expected_warning, _to_list(log_entries)])
func is_push_error(expected_error: Variant) -> GdUnitGodotErrorAssert: func is_push_error(expected_error: Variant) -> GdUnitGodotErrorAssert:
if not _validate_callable():
return self
var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_error) var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_error)
if result.is_error(): if result.is_error():
return _report_error(result.error_message()) return _report_error(result.error_message())
@@ -136,6 +139,14 @@ func is_push_error(expected_error: Variant) -> GdUnitGodotErrorAssert:
return _report_success() return _report_success()
return _report_error(""" return _report_error("""
Expecting: push_error() is called. Expecting: push_error() is called.
message: '%s' expected: '%s'
found: %s current: '%s'
""".dedent().trim_prefix("\n") % [expected_error, _to_list(log_entries)]) """.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 # 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): if not _emitter.has_signal(signal_name):
@warning_ignore("return_value_discarded") @warning_ignore("return_value_discarded")
report_error("The signal '%s' not exists checked object '%s'." % [signal_name, _emitter.get_class()]) 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 # 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() _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 # 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() _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: if _emitter == null:
return report_error("Can't wait for signal checked a NULL object.") 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 # first verify the signal is defined
if not _emitter.has_signal(signal_name): 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()]) 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: if file == null:
push_error("ERROR: Can't read resource '%s'. %s" % [resource_path, error_string(FileAccess.get_open_error())]) push_error("ERROR: Can't read resource '%s'. %s" % [resource_path, error_string(FileAccess.get_open_error())])
return "" return ""
return file.get_as_text(true) return file.get_as_text()
static func make_qualified_path(path :String) -> String: 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), TESTS : Array([], TYPE_OBJECT, "RefCounted", GdUnitTestCase),
# the port of running test server for this session # 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) 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: func add_test_cases(tests: Array[GdUnitTestCase]) -> GdUnitRunnerConfig:
test_cases().append_array(tests) test_cases().append_array(tests)
return self return self
@@ -57,7 +69,8 @@ func save_config(path: String = CONFIG_FILE) -> GdUnitResult:
var to_save := { var to_save := {
VERSION : CONFIG_VERSION, VERSION : CONFIG_VERSION,
SERVER_PORT : _config.get(SERVER_PORT), EXIT_FAIL_FAST : is_fail_fast(),
SERVER_PORT : server_port(),
TESTS : Array() 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 return
_scene_tree().root.add_child(_current_scene) _scene_tree().root.add_child(_current_scene)
Engine.set_meta("GdUnitSceneRunner", self)
# do finally reset all open input events when the scene is removed # do finally reset all open input events when the scene is removed
@warning_ignore("return_value_discarded") @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: 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() _reset_input_to_default()
) )
_simulate_start_time = LocalTime.now() _simulate_start_time = LocalTime.now()
@@ -103,6 +102,7 @@ func _notification(what: int) -> void:
_current_scene.free() _current_scene.free()
_is_disposed = true _is_disposed = true
_current_scene = null _current_scene = null
Engine.remove_meta("GdUnitSceneRunner")
func _scene_tree() -> SceneTree: func _scene_tree() -> SceneTree:
@@ -145,6 +145,7 @@ func simulate_action_release(action: String, event_index := -1) -> GdUnitSceneRu
@warning_ignore("return_value_discarded") @warning_ignore("return_value_discarded")
func simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner: 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) simulate_key_press(key_code, shift_pressed, ctrl_pressed)
await _scene_tree().process_frame await _scene_tree().process_frame
simulate_key_release(key_code, shift_pressed, ctrl_pressed) 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: 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() __print_current_focus()
var event := InputEventKey.new() var event := InputEventKey.new()
event.pressed = true event.pressed = true
event.keycode = key_code as Key event.keycode = key_code as Key
event.physical_keycode = key_code as Key event.physical_keycode = key_code as Key
event.unicode = key_code event.unicode = key_code
event.alt_pressed = key_code == KEY_ALT event.set_alt_pressed(key_code == KEY_ALT)
event.shift_pressed = shift_pressed or key_code == KEY_SHIFT event.set_shift_pressed(shift_pressed)
event.ctrl_pressed = ctrl_pressed or key_code == KEY_CTRL event.set_ctrl_pressed(ctrl_pressed)
event.get_modifiers_mask()
_apply_input_modifiers(event) _apply_input_modifiers(event)
_key_on_press.append(key_code) _key_on_press.append(key_code)
return _handle_input_event(event) return _handle_input_event(event)
func simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner: 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() __print_current_focus()
var event := InputEventKey.new() var event := InputEventKey.new()
event.pressed = false event.pressed = false
event.keycode = key_code as Key event.keycode = key_code as Key
event.physical_keycode = key_code as Key event.physical_keycode = key_code as Key
event.unicode = key_code event.unicode = key_code
event.alt_pressed = key_code == KEY_ALT event.set_alt_pressed(key_code == KEY_ALT)
event.shift_pressed = shift_pressed or key_code == KEY_SHIFT event.set_shift_pressed(shift_pressed)
event.ctrl_pressed = ctrl_pressed or key_code == KEY_CTRL event.set_ctrl_pressed(ctrl_pressed)
_apply_input_modifiers(event) _apply_input_modifiers(event)
_key_on_press.erase(key_code) _key_on_press.erase(key_code)
return _handle_input_event(event) 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: func _scene_name() -> String:
if scene() == null:
return "unknown"
var scene_script :GDScript = scene().get_script() var scene_script :GDScript = scene().get_script()
var scene_name :String = scene().get_name() var scene_name :String = scene().get_name()
if not scene_script: 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 _event.ctrl_pressed = _event.ctrl_pressed or last_input_event.ctrl_pressed
# this line results into reset the control_pressed state!!! # 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 #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 # copy over current active mouse mask and combine with curren mask
@@ -620,3 +633,10 @@ func scene() -> Node:
if not _is_disposed: 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.") push_error("The current scene instance is not valid anymore! check your test is valid. e.g. check for missing awaits.")
return null 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_DISCOVER_ENABLED = GROUP_TEST + "/test_discovery"
const TEST_FLAKY_CHECK = GROUP_TEST + "/flaky_check_enable" const TEST_FLAKY_CHECK = GROUP_TEST + "/flaky_check_enable"
const TEST_FLAKY_MAX_RETRIES = GROUP_TEST + "/flaky_max_retries" const TEST_FLAKY_MAX_RETRIES = GROUP_TEST + "/flaky_max_retries"
const TEST_RERUN_UNTIL_FAILURE_RETRIES = GROUP_TEST + "/rerun_until_failure_retries"
# Report Setiings # 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_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_OVERALL = GROUP_SHORTCUT_INSPECTOR + "/run_test_overall"
const SHORTCUT_INSPECTOR_RUN_TEST_STOP = GROUP_SHORTCUT_INSPECTOR + "/run_test_stop" 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 GROUP_SHORTCUT_EDITOR = SHORTCUT_SETTINGS + "/editor"
const SHORTCUT_EDITOR_RUN_TEST = GROUP_SHORTCUT_EDITOR + "/run_test" 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_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_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_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 # report settings
create_property_if_need(REPORT_PUSH_ERRORS, false, "Report push_error() as failure") 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") 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 # 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, 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_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_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") create_property_if_need(SHORTCUT_INSPECTOR_RUN_TEST_STOP, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.STOP_TEST_RUN), "Stop the current test execution")
# script editor # 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_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") 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 # 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, 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.NONE), "Run all test suites in the selected folder or file (Debug)") 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: 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) 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: static func set_test_discover_enabled(enable :bool) -> void:
var property := get_property(TEST_DISCOVER_ENABLED) var property := get_property(TEST_DISCOVER_ENABLED)
property.set_value(enable) 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) 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]); #prints("match", signal_name, _collected_signals[emitter][signal_name]);
if _collected_signals.is_empty() or not _collected_signals.has(emitter): if _collected_signals.is_empty() or not _collected_signals.has(emitter):
return false 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) 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") @warning_ignore("unused_signal")
signal gdunit_client_terminated() signal gdunit_test_session_terminate()
## Emitted when a test execution event occurs.[br] ## 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]: 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()): if exclude_scan_directories.has(dir.get_current_dir()):
return collected_suites return collected_suites
var err := dir.list_dir_begin() var err := dir.list_dir_begin()
if err != OK: if err != OK:
push_error("Error on scanning directory %s" % dir.get_current_dir(), error_string(err)) 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 _current_iteration: int = -1
var _expect_to_interupt := false var _expect_to_interupt := false
var _timer: Timer var _timer: Timer
var _interupted: bool = false var _interupted := false
var _terminated := false
var _failed := false var _failed := false
var _parameter_set_resolver: GdUnitTestParameterSetResolver var _parameter_set_resolver: GdUnitTestParameterSetResolver
var _is_disposed := false var _is_disposed := false
@@ -123,7 +124,7 @@ func do_interrupt() -> void:
# We need to dispose manually the function state here # We need to dispose manually the function state here
GdObjects.dispose_function_state(_func_state) GdObjects.dispose_function_state(_func_state)
if not is_expect_interupted(): 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(): if is_fuzzed():
execution_context.add_report(GdUnitReport.new()\ execution_context.add_report(GdUnitReport.new()\
.create(GdUnitReport.INTERUPTED, line_number(), GdAssertMessages.fuzzer_interuped(_current_iteration, "timedout"))) .create(GdUnitReport.INTERUPTED, line_number(), GdAssertMessages.fuzzer_interuped(_current_iteration, "timedout")))
@@ -133,6 +134,16 @@ func do_interrupt() -> void:
completed.emit() 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: func _set_failure_handler() -> void:
if not GdUnitSignals.instance().gdunit_set_test_failed.is_connected(_failure_received): if not GdUnitSignals.instance().gdunit_set_test_failed.is_connected(_failure_received):
@warning_ignore("return_value_discarded") @warning_ignore("return_value_discarded")
@@ -172,6 +183,10 @@ func is_expect_interupted() -> bool:
return _expect_to_interupt return _expect_to_interupt
func is_terminated() -> bool:
return _terminated
func is_parameterized() -> bool: func is_parameterized() -> bool:
return _parameter_set_resolver.is_parameterized() return _parameter_set_resolver.is_parameterized()
@@ -192,11 +207,6 @@ func test_name() -> String:
return _test_case.test_name return _test_case.test_name
@warning_ignore("native_method_override")
func get_name() -> StringName:
return _test_case.test_name
func line_number() -> int: func line_number() -> int:
return _test_case.line_number return _test_case.line_number

View File

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

View File

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