Skip to content

Commit 87dff2b

Browse files
authored
Add automated tests using Meta XR Simulator (#150)
1 parent d73cb53 commit 87dff2b

File tree

4 files changed

+237
-1
lines changed

4 files changed

+237
-1
lines changed

Diff for: .github/workflows/build-addon-on-push.yml

+189
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,192 @@ jobs:
180180
omitPrereleaseDuringUpdate: true
181181
token: ${{ secrets.GITHUB_TOKEN }}
182182
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
183+
184+
run_xr_simulator:
185+
name: "Run XR Simulator"
186+
#runs-on: windows-latest
187+
runs-on: [Windows, self-hosted, gpu]
188+
needs: build
189+
190+
steps:
191+
- name: Checkout code
192+
uses: actions/checkout@v3
193+
194+
- name: Install Chocolatey
195+
run: |
196+
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
197+
198+
- name: Install 7zip
199+
run: |
200+
choco install 7zip -y
201+
202+
- name: Download Godot
203+
run: |
204+
# @todo Download an actual Godot build!
205+
Invoke-WebRequest -Uri "https://github.com/godotengine/godot-builds/releases/download/4.3-beta1/Godot_v4.3-beta1_win64.exe.zip" -OutFile "godot.zip"
206+
207+
- name: Extract Godot
208+
run: |
209+
Expand-Archive -Path "godot.zip" -DestinationPath .
210+
211+
#- name: Download SwiftShader vulkan-1.dll
212+
# run: |
213+
# Invoke-WebRequest -Uri "https://www.dropbox.com/scl/fi/r6radx6b125555xkxue20/vulkan-1.dll?rlkey=ffv3cgvqbytivm1zy119hss7n&st=frcettrf&dl=1" -OutFile "vulkan-1.dll"
214+
215+
- name: Download Meta XR Simulator
216+
run: |
217+
Invoke-WebRequest -Uri "https://npm.developer.oculus.com/com.meta.xr.simulator/-/com.meta.xr.simulator-65.0.0.tgz" -OutFile MetaXRSimulator.tgz
218+
219+
- name: Extract Meta XR Simulator
220+
run: |
221+
# Unzip.
222+
New-Item -Path "C:\Meta" -ItemType Directory -Force
223+
tar -xzvf "MetaXRSimulator.tgz" -C "C:\Meta\"
224+
rm "MetaXRSimulator.tgz"
225+
226+
# Gunzip.
227+
$tgzFile = Get-ChildItem -Path "C:\Meta" -Filter *.tgz
228+
7z x $tgzFile.FullName -oC:\Meta -aoa
229+
230+
# Untar.
231+
$tarFile = Get-ChildItem -Path "C:\Meta" -Filter *.tar
232+
7z x $tarFile.FullName -oC:\Meta -aoa
233+
234+
- name: Configure the Meta XR Simulator as the active OpenXR runtime
235+
run: |
236+
New-Item -Path "HKLM:\SOFTWARE\Khronos\OpenXR\1" -Name "ActiveRuntime" -Force
237+
Set-ItemProperty -Path "HKLM:\SOFTWARE\Khronos\OpenXR\1" -Name "ActiveRuntime" -Value "C:\Meta\package\MetaXRSimulator\meta_openxr_simulator.json"
238+
239+
- name: Download Windows build artifacts
240+
uses: actions/download-artifact@v3
241+
with:
242+
name: build-files-windows
243+
path: build-files-windows
244+
245+
- name: Copy Windows build of the addon into the demo project
246+
run: |
247+
mkdir -p demo/addons/godotopenxrvendors/.bin/windows/
248+
cp -r build-files-windows/* demo/addons/godotopenxrvendors/.bin/windows/
249+
250+
- name: Import the demo project
251+
run: |
252+
$godot = "Godot_v4.3-beta1_win64.exe"
253+
Start-Process -FilePath "$godot" -ArgumentList "--path demo --import --headless" -NoNewWindow -Wait
254+
255+
- name: Launch a synthetic environment
256+
run: |
257+
# Ensure a synthetic environment isn't already running.
258+
try {
259+
Get-Process -Name "synth_env_server" | Stop-Process
260+
} catch {
261+
# Do nothing if there is no existing process.
262+
}
263+
264+
Start-Process -FilePath "C:\Meta\package\MetaXRSimulator\.synth_env_server\synth_env_server.exe" -ArgumentList "Bedroom" -PassThru
265+
266+
- name: Run tests
267+
run: |
268+
$jsonPath = "$env:AppData\MetaXR\MetaXrSimulator\persistent_data.json"
269+
270+
$vrsFiles = Get-ChildItem -Path tests/vrs -Filter *.vrs
271+
foreach ($file in $vrsFiles) {
272+
$replayPath = Join-Path -Path $file.DirectoryName -ChildPath ($file.BaseName + "-replay.vrs")
273+
$jsonContent = @{
274+
session_capture = @{
275+
delay_start_ms = 5000
276+
exec_state = "replay"
277+
quit_buffer_ms = 5000
278+
quit_when_complete = $true
279+
record_path = $file.FullName
280+
replay_path = $replayPath
281+
}
282+
} | ConvertTo-Json
283+
284+
New-Item -ItemType Directory -Force -Path (Split-Path $jsonPath)
285+
Set-Content -Path $jsonPath -Value $jsonContent
286+
echo "$jsonContent"
287+
288+
$godot = "Godot_v4.3-beta1_win64.exe"
289+
$timeout = 300
290+
$waitTime = 0
291+
292+
$process = Start-Process -FilePath "$godot" -ArgumentList "--path demo --rendering-method mobile --verbose -- --quit-with-openxr" -NoNewWindow -PassThru
293+
294+
while ($process.HasExited -eq $false -and $waitTime -lt $timeout) {
295+
Start-Sleep -Seconds 1
296+
$waitTime++
297+
}
298+
299+
if ($process.HasExited -eq $false) {
300+
Write-Output "Process is still running after $timeout seconds. Stopping the process."
301+
$process.Kill()
302+
Exit 1
303+
} else {
304+
Write-Output "Process completed within $waitTime seconds."
305+
}
306+
307+
if (-Not (Test-Path $replayPath)) {
308+
Write-Error "Replay file not found: $replayPath"
309+
Exit 1
310+
}
311+
}
312+
313+
- name: Stop synthetic environment
314+
run: |
315+
Get-Process -Name "synth_env_server" | Stop-Process
316+
317+
- name: Copy vrs_pixmatch.py from Meta XR Simulator
318+
run: |
319+
$scriptsPath = "C:\Meta\package\MetaXRSimulator\scripts"
320+
cp "$scriptsPath\requirements.txt" tests\vrs\
321+
cp "$scriptsPath\vrs_pixmatch.py" tests\vrs\
322+
323+
- name: Upload VRS artifacts
324+
uses: actions/upload-artifact@v3
325+
with:
326+
name: ReplayVRS
327+
path: tests/vrs/**/*
328+
329+
compare_vrs_replay:
330+
name: "Compare VRS replay"
331+
runs-on: ubuntu-latest
332+
needs: run_xr_simulator
333+
334+
steps:
335+
- name: Checkout code
336+
uses: actions/checkout@v3
337+
338+
- name: Download VRS artifacts
339+
uses: actions/download-artifact@v3
340+
with:
341+
name: ReplayVRS
342+
path: tests/vrs
343+
344+
- name: Set up Python
345+
uses: actions/setup-python@v4
346+
with:
347+
python-version: '3.10'
348+
349+
- name: Install dependencies
350+
run: |
351+
python -m pip install --upgrade pip
352+
pip install -r tests/vrs/requirements.txt
353+
354+
- name: Compare VRS replay with expected recordings
355+
run: |
356+
cd tests/vrs/
357+
358+
# Fix bugs in vrs_pixmatch.py script.
359+
patch -p0 < ../../thirdparty/meta_xr_simulator/vrs_pixmatch.patch
360+
361+
mkdir diffs
362+
for replay in *-replay.vrs; do
363+
expected=$(echo $replay | sed -e 's/-replay.vrs$/.vrs/')
364+
python vrs_pixmatch.py "$replay" "$expected" --threshold 0.4 --best_match_pixels_diff_threshold 40000 --diffs_output_path diffs
365+
done
366+
367+
- name: Upload VRS diff artifacts
368+
uses: actions/upload-artifact@v3
369+
with:
370+
name: ReplayVRSDiff
371+
path: tests/vrs/diffs/**/*

Diff for: demo/main.gd

+9-1
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ const COLORS = [
4040
# Called when the node enters the scene tree for the first time.
4141
func _ready():
4242
xr_interface = XRServer.find_interface("OpenXR")
43-
if xr_interface and xr_interface.is_initialized():
43+
if xr_interface and xr_interface.initialize():
4444
xr_interface.session_begun.connect(self.load_spatial_anchors_from_file)
45+
xr_interface.session_stopping.connect(self._on_session_stopping)
4546
var vp: Viewport = get_viewport()
4647
vp.use_xr = true
4748

@@ -52,6 +53,13 @@ func _ready():
5253
randomize()
5354

5455

56+
func _on_session_stopping() -> void:
57+
if "--quit-with-openxr" in OS.get_cmdline_user_args():
58+
# When we're running tests via the XR Simulator, it will end the OpenXR
59+
# session automatically, and in that case, we want to quit.
60+
get_tree().quit()
61+
62+
5563
func load_spatial_anchors_from_file() -> void:
5664
var file := FileAccess.open(SPATIAL_ANCHORS_FILE, FileAccess.READ)
5765
if not file:

Diff for: tests/vrs/simulation1.vrs

5.81 MB
Binary file not shown.

Diff for: thirdparty/meta_xr_simulator/vrs_pixmatch.patch

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--- vrs_pixmatch.py-orig 2024-05-14 17:41:44.639317754 -0500
2+
+++ vrs_pixmatch.py 2024-05-14 17:42:42.316556319 -0500
3+
@@ -125,11 +125,12 @@
4+
5+
return numPixelsDiff
6+
7+
-def findTheMatchingFrame(frame_stream, frame_name, target_frame_stream, target_frame_name, sampleIdx, max_test_frames, args, minPixelsDiff):
8+
+def findTheMatchingFrame(frame_stream, frame_name, target_frame_stream, target_frame_name, sampleIdx, max_test_frames, args, minPixelsDiff, bestMatch):
9+
sampleTS = getTimestamp(frame_stream, sampleIdx)
10+
sampleFrame = getFrame(frame_stream, sampleIdx)
11+
matchStartIdx = getFrameIdx(target_frame_stream, sampleTS)
12+
- # search around the frame of interest until we find the closest match
13+
+
14+
+ # search around the frame of interest until the closest match is found
15+
16+
# find the matching frame in the target_frame_stream
17+
for matchIdx in range(matchStartIdx, matchStartIdx+max_test_frames):
18+
@@ -203,12 +204,12 @@
19+
bestMatch = None
20+
21+
# first check the replay against the record
22+
- (minPixelsDiff, bestMatch) = findTheMatchingFrame(record_frame_stream, "record", replay_frame_stream, "replay", sampleIdx, max_test_frames, args, minPixelsDiff)
23+
+ (minPixelsDiff, bestMatch) = findTheMatchingFrame(record_frame_stream, "record", replay_frame_stream, "replay", sampleIdx, max_test_frames, args, minPixelsDiff, bestMatch)
24+
25+
- if minPixelsDiff > 0:
26+
- print("best match diff is >0 so matching record against replay")
27+
+ if minPixelsDiff > args.best_match_pixels_diff_threshold:
28+
+ print("best match diff is > ", args.best_match_pixels_diff_threshold, " so matching record against replay")
29+
# then check the record against the replay
30+
- (minPixelsDiff, bestMatch) = findTheMatchingFrame(replay_frame_stream, "replay", record_frame_stream, "record", sampleIdx, max_test_frames, args, minPixelsDiff)
31+
+ (minPixelsDiff, bestMatch) = findTheMatchingFrame(replay_frame_stream, "replay", record_frame_stream, "record", sampleIdx, max_test_frames, args, minPixelsDiff, bestMatch)
32+
33+
# report the best match
34+
if bestMatch:
35+
@@ -230,3 +231,4 @@
36+
37+
if __name__ == "__main__":
38+
main()
39+
+

0 commit comments

Comments
 (0)