Skip to content
Draft
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
82 changes: 78 additions & 4 deletions test/IntegrationTest/Integration.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
# Integration tests for Sentry Unity SDK
#
# Environment variables:
# SENTRY_TEST_PLATFORM: target platform (Android, Desktop, iOS, WebGL)
# SENTRY_TEST_PLATFORM: target platform (Android, Desktop, iOS, WebGL, Xbox)
# SENTRY_TEST_DSN: test DSN
# SENTRY_AUTH_TOKEN: authentication token for Sentry API
#
# SENTRY_TEST_APP: path to the test app (APK, executable, .app bundle, or WebGL build directory)
# SENTRY_TEST_APP: path to the test app (APK, executable, .app bundle, WebGL build directory,
# or Xbox package directory containing .xvc)
#
# Platform-specific environment variables:
# iOS: SENTRY_IOS_VERSION - iOS simulator version (e.g. "17.0" or "latest")
# Xbox: XBCONNECT_TARGET - Xbox devkit IP address

Set-StrictMode -Version latest
$ErrorActionPreference = "Stop"
Expand All @@ -30,6 +32,7 @@ BeforeAll {
"Android" { return @("-e", "test", $Action) }
"Desktop" { return @("--test", $Action, "-logFile", "-") }
"iOS" { return @("--test", $Action) }
"Xbox" { return @("--test", $Action, "-logFile", "D:\Logs\UnityIntegrationTest.log") }
}
}

Expand Down Expand Up @@ -110,6 +113,35 @@ BeforeAll {
$appArgs = Get-AppArguments -Action $Action
$runResult = Invoke-DeviceApp -ExecutablePath $script:ExecutablePath -Arguments $appArgs

# On Xbox, console output is not available in non-development builds.
# Instead, Unity writes to a log file on the device which we retrieve and use as output.
if ($script:Platform -eq "Xbox") {
$logLocalDir = "$PSScriptRoot/results/xbox-logs/$Action"
New-Item -ItemType Directory -Path $logLocalDir -Force | Out-Null
try {
Copy-DeviceItem -DevicePath "D:\Logs\UnityIntegrationTest.log" -Destination $logLocalDir
$logContent = Get-Content "$logLocalDir/UnityIntegrationTest.log" -ErrorAction SilentlyContinue
if ($logContent -and $logContent.Count -gt 0) {
Write-Host "Retrieved Unity log file from Xbox ($($logContent.Count) lines)"
$runResult.Output = $logContent
} else {
Write-Warning "Unity log file was empty or not found on Xbox."
}
} catch {
Write-Warning "Failed to retrieve Unity log file from Xbox: $_"
}
try {
Copy-DeviceItem -DevicePath "D:\Logs\sentry-sdk.log" -Destination $logLocalDir
$sdkLogContent = Get-Content "$logLocalDir/sentry-sdk.log" -ErrorAction SilentlyContinue
if ($sdkLogContent -and $sdkLogContent.Count -gt 0) {
Write-Host "Retrieved SDK log file from Xbox ($($sdkLogContent.Count) lines)"
$runResult.Output = @($runResult.Output) + @($sdkLogContent)
}
} catch {
Write-Warning "Failed to retrieve SDK log file from Xbox: $_"
}
}

# Save result to JSON file
$runResult | ConvertTo-Json -Depth 5 | Out-File -FilePath (Get-OutputFilePath "${Action}-result.json")

Expand All @@ -120,6 +152,32 @@ BeforeAll {
$sendArgs = Get-AppArguments -Action "crash-send"
$sendResult = Invoke-DeviceApp -ExecutablePath $script:ExecutablePath -Arguments $sendArgs

# On Xbox, retrieve the log file for crash-send output too
if ($script:Platform -eq "Xbox") {
$sendLogDir = "$PSScriptRoot/results/xbox-logs/crash-send"
New-Item -ItemType Directory -Path $sendLogDir -Force | Out-Null
try {
Copy-DeviceItem -DevicePath "D:\Logs\UnityIntegrationTest.log" -Destination $sendLogDir
$sendLogContent = Get-Content "$sendLogDir/UnityIntegrationTest.log" -ErrorAction SilentlyContinue
if ($sendLogContent -and $sendLogContent.Count -gt 0) {
Write-Host "Retrieved Unity crash-send log file from Xbox ($($sendLogContent.Count) lines)"
$sendResult.Output = $sendLogContent
}
} catch {
Write-Warning "Failed to retrieve Unity crash-send log file from Xbox: $_"
}
try {
Copy-DeviceItem -DevicePath "D:\Logs\sentry-sdk.log" -Destination $sendLogDir
$sdkLogContent = Get-Content "$sendLogDir/sentry-sdk.log" -ErrorAction SilentlyContinue
if ($sdkLogContent -and $sdkLogContent.Count -gt 0) {
Write-Host "Retrieved SDK log file from Xbox for crash-send ($($sdkLogContent.Count) lines)"
$sendResult.Output = @($sendResult.Output) + @($sdkLogContent)
}
} catch {
Write-Warning "Failed to retrieve SDK log file from Xbox for crash-send: $_"
}
}

# Save crash-send result to JSON for debugging
$sendResult | ConvertTo-Json -Depth 5 | Out-File -FilePath (Get-OutputFilePath "crash-send-result.json")

Expand All @@ -142,7 +200,7 @@ BeforeAll {

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

# Validate common environment
Expand Down Expand Up @@ -195,10 +253,26 @@ BeforeAll {
Connect-Device -Platform "iOSSimulator" -Target $target
Install-DeviceApp -Path $env:SENTRY_TEST_APP
}
"Xbox" {
if ([string]::IsNullOrEmpty($env:XBCONNECT_TARGET)) {
throw "XBCONNECT_TARGET environment variable is not set."
}

Connect-Device -Platform "Xbox" -Target $env:XBCONNECT_TARGET

# Xbox uses packaged .xvc flow — SENTRY_TEST_APP points to the package directory
$xvcFile = Get-ChildItem -Path $env:SENTRY_TEST_APP -Filter "*.xvc" | Select-Object -First 1
if (-not $xvcFile) {
throw "No .xvc package found in: $env:SENTRY_TEST_APP"
}
Install-DeviceApp -Path $xvcFile.FullName
$script:ExecutablePath = Get-PackageAumid -PackagePath $env:SENTRY_TEST_APP
Write-Host "Using AUMID: $($script:ExecutablePath)"
}
"WebGL" {
}
default {
throw "Unknown platform: $($script:Platform). Expected: Android, Desktop, iOS, or WebGL"
throw "Unknown platform: $($script:Platform). Expected: Android, Desktop, iOS, WebGL, or Xbox"
}
}

Expand Down
47 changes: 47 additions & 0 deletions test/Scripts.Integration.Test/Editor/Builder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public static void BuildIl2CPPPlayer(BuildTarget target, BuildTargetGroup group,

// Make sure the configuration is right.
EditorUserBuildSettings.selectedBuildTargetGroup = group;
EditorUserBuildSettings.development = false;
EditorUserBuildSettings.allowDebugging = false;
PlayerSettings.SetScriptingBackend(NamedBuildTarget.FromBuildTargetGroup(group), ScriptingImplementation.IL2CPP);
// Making sure that the app keeps on running in the background. Linux CI is very unhappy with coroutines otherwise.
Expand Down Expand Up @@ -183,12 +184,14 @@ public static void BuildSwitchIL2CPPPlayer()
public static void BuildXSXIL2CPPPlayer()
{
Debug.Log("Builder: Building Xbox Series X|S IL2CPP Player");
SetXboxSubtargetToMaster();
BuildIl2CPPPlayer(BuildTarget.GameCoreXboxSeries, BuildTargetGroup.GameCoreXboxSeries, BuildOptions.StrictMode);
}

public static void BuildXB1IL2CPPPlayer()
{
Debug.Log("Builder: Building Xbox One IL2CPP Player");
SetXboxSubtargetToMaster();
BuildIl2CPPPlayer(BuildTarget.GameCoreXboxOne, BuildTargetGroup.GameCoreXboxOne, BuildOptions.StrictMode);
}

Expand All @@ -198,6 +201,50 @@ public static void BuildPS5IL2CPPPlayer()
BuildIl2CPPPlayer(BuildTarget.PS5, BuildTargetGroup.PS5, BuildOptions.StrictMode);
}

// We'll likely extend this to also work with PS and Switch
private static void SetXboxSubtargetToMaster()
{
// The actual editor API to set this has bee deprecated: https://docs.unity3d.com/6000.3/Documentation/ScriptReference/XboxBuildSubtarget.html
// Modifying the build profiles and build setting assets on disk does not work. Some of the properties are
// stored inside a binary. Instead we're setting the properties via reflection and then saving the asset.
var buildProfileType = Type.GetType("UnityEditor.Build.Profile.BuildProfile, UnityEditor.CoreModule");
if (buildProfileType == null)
return;

foreach (var profile in Resources.FindObjectsOfTypeAll(buildProfileType))
{
// BuildTarget.GameCoreXboxSeries = 42, BuildTarget.GameCoreXboxOne = 43.
var buildTarget = new SerializedObject(profile).FindProperty("m_BuildTarget")?.intValue ?? -1;
if (buildTarget != 42 && buildTarget != 43)
continue;

var platformSettings = buildProfileType
.GetProperty("platformBuildProfile", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
?.GetValue(profile);
var settingsData = platformSettings?.GetType()
.GetField("m_settingsData", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
?.GetValue(platformSettings);

GetFieldInHierarchy(settingsData?.GetType(), "buildSubtarget")?.SetValue(settingsData, 1); // 1 = Master
GetFieldInHierarchy(platformSettings?.GetType(), "m_Development")?.SetValue(platformSettings, false);
EditorUtility.SetDirty(profile);
Debug.Log($"Builder: Xbox Build Profile (BuildTarget {buildTarget}) set to Master");
}
AssetDatabase.SaveAssets();
}

private static FieldInfo GetFieldInHierarchy(Type type, string fieldName)
{
while (type != null)
{
var field = type.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
if (field != null)
return field;
type = type.BaseType;
}
return null;
}

private static void ValidateArguments(Dictionary<string, string> args)
{
Debug.Log("Builder: Validating command line arguments");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using Sentry;
using Sentry.Extensibility;
using Sentry.Unity;
using UnityEngine;

Expand All @@ -21,6 +24,14 @@ public override void Configure(SentryUnityOptions options)
options.DiagnosticLevel = SentryLevel.Debug;
options.TracesSampleRate = 1.0d;

#if UNITY_GAMECORE
// On Xbox, Debug.Log output is suppressed in non-development builds, so SDK diagnostic
// logs are written to the app's persistent data path for the test harness to retrieve.
options.DiagnosticLogger = new SdkFileLogger(
@"D:\SystemScratch\Logs\sentry-sdk.log",
options.DiagnosticLevel);
#endif

// No custom HTTP handler -- events go to real sentry.io

// Filtering test output from breadcrumbs
Expand All @@ -42,4 +53,51 @@ public override void Configure(SentryUnityOptions options)

Debug.Log("Sentry: IntegrationOptionsConfig::Configure() finished");
}

private class SdkFileLogger : IDiagnosticLogger
{
private readonly StreamWriter _writer;
private readonly SentryLevel _minLevel;

public SdkFileLogger(string logFilePath, SentryLevel minLevel)
{
_minLevel = minLevel;
try
{
var directory = Path.GetDirectoryName(logFilePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
_writer = new StreamWriter(logFilePath, append: true) { AutoFlush = true };
}
catch (Exception ex)
{
Debug.LogWarning($"SdkFileLogger: Failed to open log file '{logFilePath}': {ex.Message}");
}
}

public bool IsEnabled(SentryLevel level) => level >= _minLevel;

public void Log(SentryLevel logLevel, string message, Exception? exception = null, params object?[] args)
{
if (!IsEnabled(logLevel) || _writer == null)
{
return;
}

try
{
var text = args.Length == 0 ? message : string.Format(message, args);
var line = exception == null
? $"[Sentry] {logLevel}: {text}"
: $"[Sentry] {logLevel}: {text}{Environment.NewLine}{exception}";
_writer.WriteLine(line);
}
catch
{
// Don't let file writing errors break the app.
}
}
}
}
21 changes: 21 additions & 0 deletions test/Scripts.Integration.Test/create-project.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ $projectSettings = $projectSettings -replace "AndroidTargetArchitectures: ?[0-9]
$projectSettings = $projectSettings -replace "iPhoneSdkVersion: ?[0-9]+", "iPhoneSdkVersion: 989"
$projectSettings | Out-File $projectSettingsPath

# Set Xbox GameCore builds to Master (non-development) when the GameCore module is present.
# The build type is stored in Library/BuildProfiles and is only written to disk when the editor closes,
# so we patch the files directly after project creation while Unity is not running.
$buildProfilesPath = "$(GetNewProjectPath)/Library/BuildProfiles"
If (Test-Path -Path $buildProfilesPath)
{
foreach ($profileFile in Get-ChildItem "$buildProfilesPath/PlatformProfile.*.asset")
{
$content = Get-Content $profileFile.FullName -Raw
If ($content -match "GameCoreXboxOne|GameCoreScarlett")
{
$updated = $content -replace "m_Development: 1", "m_Development: 0"
If ($updated -ne $content)
{
$updated | Out-File $profileFile.FullName -Encoding utf8 -NoNewline
Write-Detail "Set Master build profile: $($profileFile.Name)"
}
}
}
}

# Add Unity UI package to manifest.json if not already present
# Creating a new project via command line doesn't include the Unity UI package by default while creating it via the Hub does.
Write-Log "Checking Unity UI package in manifest.json..."
Expand Down
7 changes: 4 additions & 3 deletions test/Scripts.Integration.Test/integration-test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ Else {
"^Switch$" {
Write-PhaseSuccess "Switch build completed - no automated test execution available"
}
"^(XSX|XB1)$"
{
Write-PhaseSuccess "Xbox build completed - no automated test execution available"
"^(XSX|XB1)$" {
$env:SENTRY_TEST_PLATFORM = "Xbox"
$env:SENTRY_TEST_APP = GetNewProjectBuildPath
Invoke-Pester -Path test/IntegrationTest/Integration.Tests.ps1 -CI
}
"^PS5$"
{
Expand Down
Loading