Skip to content

Commit 31abe73

Browse files
authored
feat: run CI on Xbox (#2616)
1 parent af6a6d6 commit 31abe73

14 files changed

Lines changed: 521 additions & 92 deletions

.github/workflows/test-run-android.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
env:
2929
ARTIFACTS_PATH: samples/IntegrationTest/test-artifacts/
3030
HOMEBREW_NO_INSTALL_CLEANUP: 1
31-
SENTRY_TEST_DSN: ${{ secrets.SENTRY_TEST_DSN }}
31+
SENTRY_DSN: ${{ secrets.SENTRY_TEST_DSN }}
3232
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
3333
# Map the job outputs to step outputs
3434
outputs:

.github/workflows/test-run-desktop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
name: ${{ inputs.platform }} ${{ inputs.unity-version }}
2020
runs-on: ${{ inputs.platform == 'linux' && 'ubuntu-latest' || 'windows-latest' }}
2121
env:
22-
SENTRY_TEST_DSN: ${{ secrets.SENTRY_TEST_DSN }}
22+
SENTRY_DSN: ${{ secrets.SENTRY_TEST_DSN }}
2323
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
2424

2525
steps:

.github/workflows/test-run-ios.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
IOS_VERSION: ${{ inputs.ios-version }}
3434
INIT_TYPE: ${{ inputs.init-type }}
3535
ARTIFACTS_PATH: samples/IntegrationTest/test-artifacts/
36-
SENTRY_TEST_DSN: ${{ secrets.SENTRY_TEST_DSN }}
36+
SENTRY_DSN: ${{ secrets.SENTRY_TEST_DSN }}
3737
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
3838

3939
steps:

.github/workflows/test-run-webgl.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
name: WebGL ${{ inputs.unity-version }}
1616
runs-on: ubuntu-latest
1717
env:
18-
SENTRY_TEST_DSN: ${{ secrets.SENTRY_TEST_DSN }}
18+
SENTRY_DSN: ${{ secrets.SENTRY_TEST_DSN }}
1919
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
2020

2121
steps:

test/IntegrationTest/Integration.Tests.ps1

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
# Integration tests for Sentry Unity SDK
44
#
55
# Environment variables:
6-
# SENTRY_TEST_PLATFORM: target platform (Android, Desktop, iOS, WebGL)
7-
# SENTRY_TEST_DSN: test DSN
6+
# SENTRY_TEST_PLATFORM: target platform (Android, Desktop, iOS, WebGL, Xbox)
7+
# SENTRY_DSN: test DSN
88
# SENTRY_AUTH_TOKEN: authentication token for Sentry API
99
#
10-
# SENTRY_TEST_APP: path to the test app (APK, executable, .app bundle, or WebGL build directory)
10+
# SENTRY_TEST_APP: path to the test app (APK, executable, .app bundle, WebGL build directory,
11+
# or Xbox packaged build directory containing a .xvc)
1112
#
1213
# Platform-specific environment variables:
1314
# iOS: SENTRY_IOS_VERSION - iOS simulator version (e.g. "17.0" or "latest")
15+
# Xbox: XBCONNECT_TARGET - Xbox devkit IP address
1416

1517
Set-StrictMode -Version latest
1618
$ErrorActionPreference = "Stop"
@@ -30,9 +32,45 @@ BeforeAll {
3032
"Android" { return @("-e", "test", $Action) }
3133
"Desktop" { return @("--test", $Action, "-logFile", "-") }
3234
"iOS" { return @("--test", $Action) }
35+
"Xbox" { return @("--test", $Action) }
3336
}
3437
}
3538

39+
# Retrieve the integration test log file from Xbox and attach it to the run result.
40+
# The Unity app writes to D:\Logs\UnityIntegrationTest.log on Xbox (UNITY_GAMECORE).
41+
function Get-XboxLogOutput {
42+
param(
43+
[Parameter(Mandatory=$true)]
44+
$RunResult,
45+
46+
[Parameter(Mandatory=$true)]
47+
[string]$Action
48+
)
49+
50+
$logFileName = "UnityIntegrationTest.log"
51+
$logLocalDir = "$PSScriptRoot/results/xbox-logs/$Action"
52+
New-Item -ItemType Directory -Path $logLocalDir -Force | Out-Null
53+
54+
try {
55+
Copy-DeviceItem -DevicePath "D:\Logs\$logFileName" -Destination $logLocalDir
56+
} catch {
57+
throw "Failed to retrieve Xbox log file (D:\Logs\$logFileName): $_"
58+
}
59+
60+
$localFile = Join-Path $logLocalDir $logFileName
61+
$logContent = Get-Content $localFile -ErrorAction SilentlyContinue
62+
if (-not $logContent -or $logContent.Count -eq 0) {
63+
$localFiles = Get-ChildItem -Path $logLocalDir -ErrorAction SilentlyContinue
64+
$localContents = if ($localFiles) { ($localFiles | ForEach-Object { $_.Name }) -join ", " } else { "(empty — D:\Logs\ likely did not exist on device)" }
65+
throw "Xbox log file was empty or missing (D:\Logs\$logFileName). Copied directory contains: $localContents"
66+
}
67+
68+
Write-Host "Retrieved log file from Xbox ($($logContent.Count) lines)" -ForegroundColor Green
69+
70+
$RunResult.Output = $logContent
71+
return $RunResult
72+
}
73+
3674
# Run a WebGL test action via headless Chrome
3775
function Invoke-WebGLTestAction {
3876
param (
@@ -110,6 +148,12 @@ BeforeAll {
110148
$appArgs = Get-AppArguments -Action $Action
111149
$runResult = Invoke-DeviceApp -ExecutablePath $script:ExecutablePath -Arguments $appArgs
112150

151+
# On Xbox, console output is not available in non-development builds.
152+
# Retrieve the log file the app writes directly to disk.
153+
if ($script:Platform -eq "Xbox") {
154+
$runResult = Get-XboxLogOutput -RunResult $runResult -Action $Action
155+
}
156+
113157
# Save result to JSON file
114158
$runResult | ConvertTo-Json -Depth 5 | Out-File -FilePath (Get-OutputFilePath "${Action}-result.json")
115159

@@ -120,6 +164,10 @@ BeforeAll {
120164
$sendArgs = Get-AppArguments -Action "crash-send"
121165
$sendResult = Invoke-DeviceApp -ExecutablePath $script:ExecutablePath -Arguments $sendArgs
122166

167+
if ($script:Platform -eq "Xbox") {
168+
$sendResult = Get-XboxLogOutput -RunResult $sendResult -Action "crash-send"
169+
}
170+
123171
# Save crash-send result to JSON for debugging
124172
$sendResult | ConvertTo-Json -Depth 5 | Out-File -FilePath (Get-OutputFilePath "crash-send-result.json")
125173

@@ -142,12 +190,12 @@ BeforeAll {
142190

143191
$script:Platform = $env:SENTRY_TEST_PLATFORM
144192
if ([string]::IsNullOrEmpty($script:Platform)) {
145-
throw "SENTRY_TEST_PLATFORM environment variable is not set. Expected: Android, Desktop, iOS, or WebGL"
193+
throw "SENTRY_TEST_PLATFORM environment variable is not set. Expected: Android, Desktop, iOS, WebGL, or Xbox"
146194
}
147195

148196
# Validate common environment
149-
if ([string]::IsNullOrEmpty($env:SENTRY_TEST_DSN)) {
150-
throw "SENTRY_TEST_DSN environment variable is not set."
197+
if ([string]::IsNullOrEmpty($env:SENTRY_DSN)) {
198+
throw "SENTRY_DSN environment variable is not set."
151199
}
152200
if ([string]::IsNullOrEmpty($env:SENTRY_AUTH_TOKEN)) {
153201
throw "SENTRY_AUTH_TOKEN environment variable is not set."
@@ -195,10 +243,25 @@ BeforeAll {
195243
Connect-Device -Platform "iOSSimulator" -Target $target
196244
Install-DeviceApp -Path $env:SENTRY_TEST_APP
197245
}
246+
"Xbox" {
247+
if ([string]::IsNullOrEmpty($env:XBCONNECT_TARGET)) {
248+
throw "XBCONNECT_TARGET environment variable is not set."
249+
}
250+
251+
Connect-Device -Platform "Xbox" -Target $env:XBCONNECT_TARGET
252+
253+
$xvcFile = Get-ChildItem -Path $env:SENTRY_TEST_APP -Filter "*.xvc" | Select-Object -First 1
254+
if (-not $xvcFile) {
255+
throw "No .xvc found in SENTRY_TEST_APP: $env:SENTRY_TEST_APP"
256+
}
257+
Install-DeviceApp -Path $xvcFile.FullName
258+
$script:ExecutablePath = Get-PackageAumid -PackagePath $env:SENTRY_TEST_APP
259+
Write-Host "Using AUMID: $($script:ExecutablePath)"
260+
}
198261
"WebGL" {
199262
}
200263
default {
201-
throw "Unknown platform: $($script:Platform). Expected: Android, Desktop, iOS, or WebGL"
264+
throw "Unknown platform: $($script:Platform). Expected: Android, Desktop, iOS, WebGL, or Xbox"
202265
}
203266
}
204267

@@ -209,7 +272,7 @@ BeforeAll {
209272
# Initialize test parameters
210273
$script:TestSetup = [PSCustomObject]@{
211274
Platform = $script:Platform
212-
Dsn = $env:SENTRY_TEST_DSN
275+
Dsn = $env:SENTRY_DSN
213276
AuthToken = $env:SENTRY_AUTH_TOKEN
214277
}
215278

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System;
2+
using System.IO;
3+
using System.Reflection;
4+
using UnityEditor;
5+
using UnityEditor.Build;
6+
using UnityEditor.Build.Reporting;
7+
using UnityEngine;
8+
9+
public class AllowInsecureHttp : IPostprocessBuildWithReport, IPreprocessBuildWithReport
10+
{
11+
public int callbackOrder { get; }
12+
public void OnPreprocessBuild(BuildReport report)
13+
{
14+
#if UNITY_2022_1_OR_NEWER
15+
PlayerSettings.insecureHttpOption = InsecureHttpOption.AlwaysAllowed;
16+
#endif
17+
}
18+
19+
// The `allow insecure http always` options don't seem to work. This is why we modify the info.plist directly.
20+
// Using reflection to get around the iOS module requirement on non-iOS platforms
21+
public void OnPostprocessBuild(BuildReport report)
22+
{
23+
var pathToBuiltProject = report.summary.outputPath;
24+
if (report.summary.platform == BuildTarget.iOS)
25+
{
26+
var plistPath = Path.Combine(pathToBuiltProject, "Info.plist");
27+
if (!File.Exists(plistPath))
28+
{
29+
Debug.LogError("Failed to find the plist.");
30+
return;
31+
}
32+
33+
var xcodeAssembly = Assembly.Load("UnityEditor.iOS.Extensions.Xcode");
34+
var plistType = xcodeAssembly.GetType("UnityEditor.iOS.Xcode.PlistDocument");
35+
var plistElementDictType = xcodeAssembly.GetType("UnityEditor.iOS.Xcode.PlistElementDict");
36+
37+
var plist = Activator.CreateInstance(plistType);
38+
plistType.GetMethod("ReadFromString", BindingFlags.Public | BindingFlags.Instance)
39+
?.Invoke(plist, new object[] { File.ReadAllText(plistPath) });
40+
41+
var root = plistType.GetField("root", BindingFlags.Public | BindingFlags.Instance);
42+
var allowDict = plistElementDictType.GetMethod("CreateDict", BindingFlags.Public | BindingFlags.Instance)
43+
?.Invoke(root?.GetValue(plist), new object[] { "NSAppTransportSecurity" });
44+
45+
plistElementDictType.GetMethod("SetBoolean", BindingFlags.Public | BindingFlags.Instance)
46+
?.Invoke(allowDict, new object[] { "NSAllowsArbitraryLoads", true });
47+
48+
var contents = (string)plistType.GetMethod("WriteToString", BindingFlags.Public | BindingFlags.Instance)
49+
?.Invoke(plist, null);
50+
51+
File.WriteAllText(plistPath, contents);
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)