Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
# Ensure symlinks are stored as symlinks, not as text files
sample-unity6/Assets/Scenes text=auto
sample-unity6/Assets/Scripts text=auto
sample-unity6/Assets/Editor text=auto
sample-unity6/Assets/Scenes.meta text=auto
sample-unity6/Assets/Scripts.meta text=auto
sample-unity6/Assets/Editor.meta text=auto
sample-unity6/Tests text=auto
144 changes: 127 additions & 17 deletions .github/workflows/ui-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ on:
type: choice
options:
- All
- StandaloneOSX
- StandaloneOSX # Builds Unity 2021 macOS only
- StandaloneOSX-Unity6 # Builds Unity 6 macOS only
- StandaloneWindows64
- Android
- iOS
Expand All @@ -31,15 +32,22 @@ jobs:
fail-fast: false
matrix:
include:
# Unity 2021 macOS build
- targetPlatform: StandaloneOSX
buildMethod: MacBuilder.BuildForAltTester
buildPath: sample/Builds/MacOS
projectPath: sample
unityVersion: 2021.3.26f1
- targetPlatform: StandaloneWindows64
buildMethod: WindowsBuilder.BuildForAltTester
buildPath: sample/Builds/Windows64
projectPath: sample
unityVersion: 2021.3.26f1
- targetPlatform: Android
buildMethod: MobileBuilder.BuildForAltTester
buildPath: sample/Builds/Android
projectPath: sample
unityVersion: 2021.3.26f1
steps:
- uses: actions/checkout@v3
if: github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == matrix.targetPlatform
Expand All @@ -49,7 +57,7 @@ jobs:
if: github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == matrix.targetPlatform
with:
path: Library
key: Library-${{ matrix.targetPlatform }}-${{ hashFiles('sample/Assets/**', 'sample/Packages/**', 'sample/ProjectSettings/**') }}
key: Library-${{ matrix.targetPlatform }}-${{ hashFiles(format('{0}/Assets/**', matrix.projectPath), format('{0}/Packages/**', matrix.projectPath), format('{0}/ProjectSettings/**', matrix.projectPath)) }}
restore-keys: |
Library-${{ matrix.targetPlatform }}
Library-
Expand All @@ -61,8 +69,9 @@ jobs:
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with:
targetPlatform: ${{ matrix.targetPlatform }}
projectPath: sample
targetPlatform: ${{ contains(matrix.targetPlatform, 'Unity6') && 'StandaloneOSX' || matrix.targetPlatform }}
projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }}
buildMethod: ${{ matrix.buildMethod }}
customParameters: -logFile logFile.log -quit -batchmode
artifactsPath: ${{ matrix.buildPath }}
Expand All @@ -75,6 +84,97 @@ jobs:
with:
name: Build-${{ matrix.targetPlatform }}
path: ${{ matrix.buildPath }}

build-and-test-unity6-macos: # Unity 6 requires a full build cycle to compile AltTester packages properly. This doesn't work well in Game CI, so we have to build it manually.
name: Build & Test Unity 6 macOS 🛠️🧪
runs-on: [self-hosted, macOS]
concurrency:
group: ui-tests-email-inbox-macos
cancel-in-progress: false # Let tests complete rather than canceling
if: github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == 'StandaloneOSX-Unity6'
steps:
- name: Cleanup old builds
run: |
# Remove previous build to save space
rm -rf sample-unity6/Tests/* 2>/dev/null || true
- uses: actions/checkout@v3
with:
lfs: true
- name: Force clean package resolution
run: |
echo "Removing Library folder to force clean package resolution..."
rm -rf sample-unity6/Library
echo "✅ Library folder removed"
- name: First build (resolves packages)
run: |
echo "Running first build to trigger package resolution..."
export TMPDIR="/Users/svc_buildsdk/tmp"
mkdir -p "$TMPDIR"
mkdir -p sample-unity6/Tests
/Applications/Unity/Hub/Editor/6000.0.58f1/Unity.app/Contents/MacOS/Unity -projectPath "${{ github.workspace }}/sample-unity6" -executeMethod "MacBuilderUnity6.BuildForAltTester" -logFile "${{ github.workspace }}/sample-unity6/first-build-log.txt" -quit -batchmode --buildPath "${{ github.workspace }}/sample-unity6/Tests/Sample Unity 6 macOS" || true

echo "First build completed (may have failed, that's ok). Checking for AltTester..."
if ls -la "${{ github.workspace }}/sample-unity6/Library/PackageCache/" | grep alttester; then
echo "✅ AltTester found in PackageCache after first build"
else
echo "⚠️ AltTester not found yet, but will be ready for second build"
fi
- name: Build Unity 6 macOS with command line (second build with packages ready)
run: |
echo "Building Unity 6 macOS using command line..."
export TMPDIR="/Users/svc_buildsdk/tmp"
mkdir -p "$TMPDIR"
mkdir -p sample-unity6/Tests
/Applications/Unity/Hub/Editor/6000.0.58f1/Unity.app/Contents/MacOS/Unity -projectPath "${{ github.workspace }}/sample-unity6" -executeMethod "MacBuilderUnity6.BuildForAltTester" -logFile "${{ github.workspace }}/sample-unity6/build-log.log" -quit -batchmode --buildPath "${{ github.workspace }}/sample-unity6/Tests/Sample Unity 6 macOS"

echo "Build completed. Checking for build output..."
ls -la sample-unity6/Tests/
- name: Create temporary keychain
run: |
security list-keychains
security delete-keychain temporary || true
security list-keychains
security create-keychain -p "" temporary
security default-keychain -s temporary
security unlock-keychain -p "" temporary
security set-keychain-settings -lut 600 temporary
- name: Make macOS artifact executable
run: chmod -R +x 'sample-unity6/Tests/Sample Unity 6 macOS.app/Contents/MacOS'
- uses: actions/setup-python@v4
with:
python-version: "3.13"
- name: Install dependencies
run: pip install -r sample-unity6/Tests/requirements-desktop.txt
- name: Run UI tests
env:
UNITY_APP_PATH: Sample Unity 6 macOS.app
UNITY_APP_NAME: Sample Unity 6 macOS
MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }}
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
working-directory: sample-unity6/Tests
run: pytest -xs test/test_mac.py::MacTest
- name: Remove temporary keychain
if: always()
run: |
security list-keychains
security delete-keychain temporary
security default-keychain -s ~/Library/Keychains/login.keychain-db
security list-keychains -d user -s ~/Library/Keychains/login.keychain-db
security list-keychains
- name: Upload first build log
if: always()
uses: actions/upload-artifact@v4
with:
name: Unity6-First-Build-Log
path: sample-unity6/first-build-log.txt
- name: Upload build log
if: always()
uses: actions/upload-artifact@v4
with:
name: Unity6-Build-Log
path: sample-unity6/build-log.log

test:
name: Run ${{ matrix.targetPlatform }} UI tests 🧪
needs: build
Expand All @@ -84,14 +184,22 @@ jobs:
- targetPlatform: StandaloneOSX
runs-on: [self-hosted, macOS]
test_script: pytest -xs test/test_mac.py::MacTest
projectPath: sample
unityAppName: SampleApp
unityAppExtension: .app
concurrency_group: macos
- targetPlatform: StandaloneWindows64
runs-on: [self-hosted, windows]
test_script: python -m pytest -xs test/test_windows.py::WindowsTest
projectPath: sample
unityAppName: Immutable Sample
unityAppExtension: .exe
concurrency_group: windows
# - targetPlatform: Android
# runs-on: [ self-hosted, macOS ]
# test_script: browserstack-sdk pytest -s ./test/test_android.py --browserstack.config "browserstack.android.yml"
concurrency:
group: ui-tests-email-inbox
group: ui-tests-email-inbox-${{ matrix.concurrency_group }}
cancel-in-progress: false # Let tests complete rather than canceling
runs-on: ${{ matrix.runs-on }}
steps:
Expand All @@ -100,7 +208,7 @@ jobs:
with:
lfs: true
- name: Create temporary keychain
if: matrix.targetPlatform == 'StandaloneOSX' && (github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == 'StandaloneOSX')
if: contains(matrix.targetPlatform, 'StandaloneOSX') && (github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == matrix.targetPlatform)
run: |
security list-keychains
security delete-keychain temporary || true
Expand All @@ -113,32 +221,34 @@ jobs:
if: github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == matrix.targetPlatform
with:
name: Build-${{ matrix.targetPlatform }}
path: sample/Tests
path: ${{ matrix.projectPath }}/Tests
- name: Make macOS artifact executable
if: matrix.targetPlatform == 'StandaloneOSX' && (github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == 'StandaloneOSX')
run: chmod +x sample/Tests/SampleApp.app/Contents/MacOS/*
if: |
contains(matrix.targetPlatform, 'StandaloneOSX') &&
(github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == matrix.targetPlatform)
run: chmod -R +x '${{ matrix.projectPath }}/Tests/${{ matrix.unityAppName }}.app/Contents/MacOS'
- uses: actions/setup-python@v4
if: github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == matrix.targetPlatform
with:
python-version: "3.13"
- name: Install dependencies (Windows)
if: matrix.targetPlatform == 'StandaloneWindows64' && (github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == 'StandaloneWindows64')
run: pip install -r "sample/Tests/requirements-desktop.txt"
run: pip install -r "${{ matrix.projectPath }}/Tests/requirements-desktop.txt"
- name: Install dependencies (Mac)
if: matrix.targetPlatform == 'StandaloneOSX' && (github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == 'StandaloneOSX')
run: pip install -r "sample/Tests/requirements-desktop.txt"
if: contains(matrix.targetPlatform, 'StandaloneOSX') && (github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == matrix.targetPlatform)
run: pip install -r "${{ matrix.projectPath }}/Tests/requirements-desktop.txt"
- name: Run UI tests
if: github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == matrix.targetPlatform
env:
UNITY_APP_PATH: SampleApp.app
UNITY_APP_NAME: SampleApp
env:
UNITY_APP_PATH: ${{ matrix.unityAppName }}${{ matrix.unityAppExtension }}
UNITY_APP_NAME: ${{ matrix.unityAppName }}
MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }}
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
working-directory: sample/Tests
working-directory: ${{ matrix.projectPath }}/Tests
run: ${{ matrix.test_script }}
- name: Remove temporary keychain
if: matrix.targetPlatform == 'StandaloneOSX' && (github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == 'StandaloneOSX')
if: contains(matrix.targetPlatform, 'StandaloneOSX') && (github.event_name != 'workflow_dispatch' || github.event.inputs.targetPlatform == 'All' || github.event.inputs.targetPlatform == matrix.targetPlatform)
run: |
security list-keychains
security delete-keychain temporary
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,7 @@ __pycache__/
*.pyc
.pytest_cache/

xcuserdata/
xcuserdata/

logFile.log
logFile-improved.log
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ This repository contains two sample projects:
- **`sample/`** - Unity 2021.3.26f1 sample project
- **`sample-unity6/`** - Unity 6 sample project *(work in progress)*

Both projects share the same Scenes and Scripts via symbolic links, providing a single source of truth for the sample code. See [`sample-unity6/README.md`](sample-unity6/README.md) for setup instructions.
Both projects share the same Scenes, Scripts, Editor folders, and Tests via symbolic links, providing a single source of truth for the sample code. See [`sample-unity6/README.md`](sample-unity6/README.md) for setup instructions.

### First Time Setup (for contributors)

The `sample-unity6` project uses symbolic links to share Scenes and Scripts with the `sample` project.
The `sample-unity6` project uses symbolic links to share Scenes, Scripts, Editor folders, and Tests with the `sample` project.

**macOS/Linux:** Symlinks are created automatically when you clone/pull - no action needed.

**Windows:** Check if symlinks were created correctly:
1. Navigate to `sample-unity6/Assets/`
2. Check if `Scenes` and `Scripts` are folders (symlinks work) or small text files (symlinks didn't work)
1. Navigate to `sample-unity6/Assets/` and `sample-unity6/`
2. Check if `Scenes`, `Scripts`, `Editor`, and `Tests` are folders (symlinks work) or small text files (symlinks didn't work)

If symlinks didn't work, run the setup script:

Expand Down
15 changes: 15 additions & 0 deletions sample-unity6/Assets/DefaultVolumeProfile.asset
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3}
m_Name: DefaultVolumeProfile
m_EditorClassIdentifier:
components: []
8 changes: 8 additions & 0 deletions sample-unity6/Assets/DefaultVolumeProfile.asset.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sample-unity6/Assets/Editor
1 change: 1 addition & 0 deletions sample-unity6/Assets/Editor.meta
8 changes: 8 additions & 0 deletions sample-unity6/Assets/Resources/AltTester.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"name":"Horizontal","negativeButton":"left","positiveButton":"right","altPositiveButton":"d","altNegativeButton":"a","type":0,"axisDirection":0},{"name":"Vertical","negativeButton":"down","positiveButton":"up","altPositiveButton":"w","altNegativeButton":"s","type":0,"axisDirection":0},{"name":"Fire1","negativeButton":"","positiveButton":"left ctrl","altPositiveButton":"mouse 0","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Fire2","negativeButton":"","positiveButton":"left alt","altPositiveButton":"mouse 1","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Fire3","negativeButton":"","positiveButton":"left shift","altPositiveButton":"mouse 2","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Jump","negativeButton":"","positiveButton":"space","altPositiveButton":"","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Mouse X","negativeButton":"","positiveButton":"","altPositiveButton":"","altNegativeButton":"","type":1,"axisDirection":0},{"name":"Mouse Y","negativeButton":"","positiveButton":"","altPositiveButton":"","altNegativeButton":"","type":1,"axisDirection":1},{"name":"Mouse ScrollWheel","negativeButton":"","positiveButton":"","altPositiveButton":"","altNegativeButton":"","type":1,"axisDirection":2},{"name":"Horizontal","negativeButton":"","positiveButton":"","altPositiveButton":"","altNegativeButton":"","type":2,"axisDirection":0},{"name":"Vertical","negativeButton":"","positiveButton":"","altPositiveButton":"","altNegativeButton":"","type":2,"axisDirection":1},{"name":"Fire1","negativeButton":"","positiveButton":"joystick button 0","altPositiveButton":"","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Fire2","negativeButton":"","positiveButton":"joystick button 1","altPositiveButton":"","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Fire3","negativeButton":"","positiveButton":"joystick button 2","altPositiveButton":"","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Jump","negativeButton":"","positiveButton":"joystick button 3","altPositiveButton":"","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Submit","negativeButton":"","positiveButton":"return","altPositiveButton":"joystick button 0","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Submit","negativeButton":"","positiveButton":"enter","altPositiveButton":"space","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Cancel","negativeButton":"","positiveButton":"escape","altPositiveButton":"joystick button 1","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Enable Debug Button 1","negativeButton":"","positiveButton":"left ctrl","altPositiveButton":"joystick button 8","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Enable Debug Button 2","negativeButton":"","positiveButton":"backspace","altPositiveButton":"joystick button 9","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Debug Reset","negativeButton":"","positiveButton":"left alt","altPositiveButton":"joystick button 1","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Debug Next","negativeButton":"","positiveButton":"page down","altPositiveButton":"joystick button 5","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Debug Previous","negativeButton":"","positiveButton":"page up","altPositiveButton":"joystick button 4","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Debug Validate","negativeButton":"","positiveButton":"return","altPositiveButton":"joystick button 0","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Debug Persistent","negativeButton":"","positiveButton":"right shift","altPositiveButton":"joystick button 2","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Debug Multiplier","negativeButton":"","positiveButton":"left shift","altPositiveButton":"joystick button 3","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Debug Horizontal","negativeButton":"left","positiveButton":"right","altPositiveButton":"","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Debug Vertical","negativeButton":"down","positiveButton":"up","altPositiveButton":"","altNegativeButton":"","type":0,"axisDirection":0},{"name":"Debug Vertical","negativeButton":"down","positiveButton":"up","altPositiveButton":"","altNegativeButton":"","type":2,"axisDirection":6},{"name":"Debug Horizontal","negativeButton":"left","positiveButton":"right","altPositiveButton":"","altNegativeButton":"","type":2,"axisDirection":5}]
Loading
Loading