diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e6213e347..fc1e988a1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -278,20 +278,23 @@ jobs:
- name: Make symbolic link for Unity (Windows)
run: New-Item -ItemType SymbolicLink -Path "C:\${{ steps.env.outputs.unityVersion }}" -Target "C:\Program Files\Unity\Hub\Editor\${{ steps.env.outputs.unityVersion }}"
- - name: Integration Test - Create new Project
+ - name: Create new Project
run: ./test/Scripts.Integration.Test/integration-create-project.ps1 "${{ env.UNITY_PATH }}"
- - name: Integration Test - Build Standalone Player without Sentry SDK
+ - name: Build Standalone Player without Sentry SDK
run: ./test/Scripts.Integration.Test/integration-build-project.ps1 "${{ env.UNITY_PATH }}"
- - name: Integration Test - Add Sentry to test project
+ - name: Add Sentry to test project
run: ./test/Scripts.Integration.Test/integration-update-sentry.ps1 "${{ env.UNITY_PATH }}"
- - name: Integration Test - Build Standalone Player Sentry SDK
+ - name: Build Standalone Player Sentry SDK
run: ./test/Scripts.Integration.Test/integration-build-project.ps1 "${{ env.UNITY_PATH }}"
- - name: Integration Test - Run Player - Smoke Test
- run: ./test/Scripts.Integration.Test/integration-run-smoke-test.ps1
+ - name: Run Player - Smoke Test
+ run: ./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Smoke
+
+ - name: Run Player - Crash Test
+ run: ./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Crash
android-smoke-test:
needs: [build]
diff --git a/Directory.Build.targets b/Directory.Build.targets
index dc433e6bf..a52838994 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -47,7 +47,7 @@
Builder.BuildWindowsIl2CPPPlayer
$(PlayerBuildPath)Windows/IL2CPP_Player.exe
$(StandaloneBuildPath)
- %USERPROFILE%\AppData\LocalLow\DefaultCompany\unity-of-bugs\
+ $(USERPROFILE)/AppData/LocalLow/DefaultCompany/unity-of-bugs/
https://94677106febe46b88b9b9ae5efd18a00@o447951.ingest.sentry.io/5439417
@@ -244,17 +244,7 @@ Related: https://forum.unity.com/threads/6572-debugger-agent-unable-to-listen-on
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs
index 0535c0504..74a98dbea 100644
--- a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs
+++ b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs
@@ -3,6 +3,8 @@
#define SENTRY_NATIVE_IOS
#elif UNITY_ANDROID
#define SENTRY_NATIVE_ANDROID
+#elif UNITY_STANDALONE_WIN && ENABLE_IL2CPP
+#define SENTRY_NATIVE_WINDOWS
#endif
#endif
@@ -10,6 +12,8 @@
using Sentry.Unity.iOS;
#elif UNITY_ANDROID
using Sentry.Unity.Android;
+#elif SENTRY_NATIVE_WINDOWS
+using Sentry.Unity.Native;
#endif
#if UNITY_IOS
@@ -23,6 +27,7 @@
using System.Linq;
using System.Net;
using System.Net.Http;
+using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Sentry;
@@ -34,16 +39,13 @@ public class SmokeTester : MonoBehaviour
{
public void Start()
{
+ string arg = null;
#if SENTRY_NATIVE_ANDROID
using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (var currentActivity = unityPlayer.GetStatic("currentActivity"))
using (var intent = currentActivity.Call("getIntent"))
{
- var text = intent.Call ("getStringExtra", "test");
- if (text == "smoke")
- {
- SmokeTest();
- }
+ arg = intent.Call ("getStringExtra", "test");
}
#elif UNITY_IOS
string pListPath = Path.Combine(Application.dataPath.Substring(0, Application.dataPath.LastIndexOf('/')), "Info.plist");
@@ -53,8 +55,8 @@ public void Start()
var key = "RunSentrySmokeTest";
if (rawPlist.Contains(key))
{
- Debug.Log("Key " + key + " found on Info.plistm starting Smoke test.");
- SmokeTest();
+ arg = "smoke";
+ Debug.Log("Key " + key + " found on Info.plist starting Smoke test.");
}
else
{
@@ -65,61 +67,87 @@ public void Start()
var args = Environment.GetCommandLineArgs();
if (args.Length > 2 && args[1] == "--test")
{
- if (args[2] == "smoke")
- {
- SmokeTest();
- }
+ arg = args[2];
}
#endif
+ if (arg == null)
+ {
+ Debug.Log($"SmokeTest not executed - no argument given");
+ }
+ else if (arg == "smoke")
+ {
+ SmokeTest();
+ }
+ else if (arg == "smoke-crash")
+ {
+ SmokeTestCrash();
+ }
+ else
+ {
+ Debug.Log($"Unknown command line argument: {arg}");
+ Application.Quit(-1);
+ }
}
- public static void SmokeTest()
+ public static void InitSentry(SentryUnityOptions options)
{
- var t = new TestHandler();
- try
- {
- Debug.Log("SMOKE TEST: Start");
-
- var options = new SentryUnityOptions();
- options.Dsn = "https://key@sentry/project";
- options.Debug = true;
- // TODO: Must be set explicitly for the time being.
- options.RequestBodyCompressionLevel = CompressionLevelWithAuto.Auto;
- options.DiagnosticLogger = new ConsoleDiagnosticLogger(SentryLevel.Debug);
- options.CreateHttpClientHandler = () => t;
-
- var sentryUnityInfo = new SentryUnityInfo();
+ options.Dsn = "http://publickey@localhost:8000/12345";
+ options.Debug = true;
+ options.DebugOnlyInEditor = false;
+ // Need to set the logger explicitly so it's available already for the native .Configure() methods.
+ // SentryUnity.Init() would set it to the same value, but too late for us.
+ options.DiagnosticLogger = new UnityLogger(options);
#if SENTRY_NATIVE_IOS
- Debug.Log("SMOKE TEST: Configure Native iOS.");
- SentryNativeIos.Configure(options);
+ Debug.Log("SMOKE TEST: Configure Native iOS.");
+ options.IosNativeSupportEnabled = true;
+ SentryNativeIos.Configure(options);
#elif SENTRY_NATIVE_ANDROID
- Debug.Log("SMOKE TEST: Configure Native Android.");
- SentryNativeAndroid.Configure(options, sentryUnityInfo);
+ Debug.Log("SMOKE TEST: Configure Native Android.");
+ options.AndroidNativeSupportEnabled = true;
+ SentryNativeAndroid.Configure(options, new SentryUnityInfo());
#elif SENTRY_NATIVE_WINDOWS
- Debug.Log("SMOKE TEST: Configure Native Windows.");
- SentryNative.Configure(options);
+ Debug.Log("SMOKE TEST: Configure Native Windows.");
+ options.WindowsNativeSupportEnabled = true;
+ SentryNative.Configure(options);
+#else
+ Debug.LogWarning("SMOKE TEST: Given platform is not supported for native sentry configuration");
+ throw new Exception("Given platform is not supported");
#endif
- Debug.Log("SMOKE TEST: SentryUnity Init.");
- SentryUnity.Init(options);
+ Debug.Log("SMOKE TEST: SentryUnity (.net) Init.");
+ SentryUnity.Init(options);
+ Debug.Log("SMOKE TEST: SentryUnity (.net) Init OK.");
+ }
- Debug.Log("SMOKE TEST: SentryUnity Init OK.");
+ public static void SmokeTest()
+ {
+ var t = new TestHandler();
+ try
+ {
+ Debug.Log("SMOKE TEST: Start");
+
+ InitSentry(new SentryUnityOptions() { CreateHttpClientHandler = () => t });
var currentMessage = 0;
t.ExpectMessage(currentMessage, "'type':'session'");
- t.ExpectMessage(currentMessage, "'init':");
- // if first message was init:false, wait for another one with init:true (this happens on windows...)
- if (t.GetMessage(currentMessage).Contains("\"init\":false"))
+ var guid = Guid.NewGuid().ToString();
+ Debug.LogError(guid);
+
+ // Skip the session init requests (there may be multiple of othem). We can't skip them by a "positive"
+ // because they're also repeated with standard events (in an envelope).
+ Debug.Log("Skipping all non-event requests");
+ for (; currentMessage < 10; currentMessage++)
{
- t.ExpectMessage(++currentMessage, "'type':'session'");
- t.ExpectMessage(currentMessage, "'init':true");
+ if (t.CheckMessage(currentMessage, "'type':'event'"))
+ {
+ break;
+ }
}
+ Debug.Log($"Done skipping non-event requests. Last one was: #{currentMessage}");
- var guid = Guid.NewGuid().ToString();
- Debug.LogError(guid);
- t.ExpectMessage(++currentMessage, "'type':'event'");
+ t.ExpectMessage(currentMessage, "'type':'event'");
t.ExpectMessage(currentMessage, guid);
SentrySdk.CaptureMessage(guid);
@@ -127,21 +155,7 @@ public static void SmokeTest()
t.ExpectMessage(currentMessage, guid);
var ex = new Exception("Exception & context test");
- SentrySdk.AddBreadcrumb("crumb", "bread", "error", new Dictionary() { { "foo", "bar" } }, BreadcrumbLevel.Critical);
- SentrySdk.ConfigureScope((Scope scope) =>
- {
- scope.SetExtra("extra-key", 42);
- scope.AddBreadcrumb("scope-crumb");
- scope.SetTag("tag-key", "tag-value");
- scope.User = new User()
- {
- Username = "username",
- Email = "email@example.com",
- IpAddress = "::1",
- Id = "user-id",
- Other = new Dictionary() { { "role", "admin" } }
- };
- });
+ AddContext();
SentrySdk.CaptureException(ex);
t.ExpectMessage(++currentMessage, "'type':'event'");
t.ExpectMessage(currentMessage, "'message':'crumb','type':'error','data':{'foo':'bar'},'category':'bread','level':'critical'}");
@@ -166,6 +180,45 @@ public static void SmokeTest()
}
}
+ public static void SmokeTestCrash()
+ {
+ Debug.Log("SMOKE TEST: Start");
+
+ InitSentry(new SentryUnityOptions());
+
+ AddContext();
+
+ Debug.Log("SMOKE TEST: Issuing a native crash (c++ unhandled exception)");
+ throw_cpp();
+
+ // shouldn't execute because the previous call should have failed
+ Debug.Log("SMOKE TEST: FAIL - unexpected code executed...");
+ Application.Quit(-1);
+ }
+
+ private static void AddContext()
+ {
+ SentrySdk.AddBreadcrumb("crumb", "bread", "error", new Dictionary() { { "foo", "bar" } }, BreadcrumbLevel.Critical);
+ SentrySdk.ConfigureScope((Scope scope) =>
+ {
+ scope.SetExtra("extra-key", 42);
+ scope.AddBreadcrumb("scope-crumb");
+ scope.SetTag("tag-key", "tag-value");
+ scope.User = new User()
+ {
+ Username = "username",
+ Email = "email@example.com",
+ IpAddress = "::1",
+ Id = "user-id",
+ Other = new Dictionary() { { "role", "admin" } }
+ };
+ });
+ }
+
+ // CppPlugin.cpp
+ [DllImport("__Internal")]
+ private static extern void throw_cpp();
+
private class TestHandler : HttpClientHandler
{
private List _requests = new List();
@@ -255,11 +308,13 @@ public string GetMessage(int index)
}
}
- public void ExpectMessage(int index, String substring)
+ public bool CheckMessage(int index, String substring)
{
var message = GetMessage(index);
- Expect($"HTTP Request #{index} contains \"{substring}\".",
- message.Contains(substring) || message.Contains(substring.Replace("'", "\"")));
+ return message.Contains(substring) || message.Contains(substring.Replace("'", "\""));
}
+
+ public void ExpectMessage(int index, String substring) =>
+ Expect($"HTTP Request #{index} contains \"{substring}\".", CheckMessage(index, substring));
}
}
diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs
index 5266f5235..2ce8f8e43 100644
--- a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs
+++ b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs
@@ -19,7 +19,7 @@ private static void ConfigureOptions(Dictionary args, [CallerMem
if (!EditorApplication.ExecuteMenuItem("Tools/Sentry"))
{
- throw new Exception("Menu item 'Tools -> Sentry' not found. Was the Sentry UPM package installed?");
+ throw new Exception($"{functionName} failed: Menu item 'Tools -> Sentry' not found. Was the Sentry UPM package installed?");
}
var optionsWindow = EditorWindow.GetWindow();
@@ -27,7 +27,7 @@ private static void ConfigureOptions(Dictionary args, [CallerMem
if (options is null)
{
- throw new InvalidOperationException("SentryOptions not found");
+ throw new InvalidOperationException($"{functionName} failed: SentryOptions not found");
}
Debug.LogFormat("{0}: Found SentryOptions", functionName);
diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs
index 47ba6502c..1942fb1fd 100644
--- a/src/Sentry.Unity.Native/SentryNativeBridge.cs
+++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs
@@ -1,6 +1,10 @@
using System;
+using System.IO;
using System.Runtime.InteropServices;
using Sentry.Extensibility;
+using AOT;
+using System.Threading;
+using System.Text;
namespace Sentry.Unity
{
@@ -29,6 +33,52 @@ public static void Init(SentryUnityOptions options)
sentry_options_set_release(cOptions, options.Release);
}
+ if (options.Environment is not null)
+ {
+ options.DiagnosticLogger?.LogDebug("Setting Environment: {0}", options.Environment);
+ sentry_options_set_environment(cOptions, options.Environment);
+ }
+
+ options.DiagnosticLogger?.LogDebug("Setting Debug: {0}", options.Debug);
+ sentry_options_set_debug(cOptions, options.Debug ? 1 : 0);
+
+ if (options.SampleRate.HasValue)
+ {
+ options.DiagnosticLogger?.LogDebug("Setting Sample Rate: {0}", options.SampleRate.Value);
+ sentry_options_set_sample_rate(cOptions, options.SampleRate.Value);
+ }
+
+ // Disabling the native in favor of the C# layer for now
+ options.DiagnosticLogger?.LogDebug("Disabling native auto session tracking");
+ sentry_options_set_auto_session_tracking(cOptions, 0);
+
+ if (options.CacheDirectoryPath is not null)
+ {
+ var dir = Path.Combine(options.CacheDirectoryPath, "SentryNative");
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ options.DiagnosticLogger?.LogDebug("Setting CacheDirectoryPath on Windows: {0}", dir);
+ sentry_options_set_database_pathw(cOptions, dir);
+ }
+ else
+ {
+ options.DiagnosticLogger?.LogDebug("Setting CacheDirectoryPath: {0}", dir);
+ sentry_options_set_database_path(cOptions, dir);
+ }
+ }
+
+ if (options.DiagnosticLogger is null)
+ {
+ _logger?.LogDebug("Unsetting the current native logger");
+ _logger = null;
+ }
+ else
+ {
+ options.DiagnosticLogger.LogDebug($"{(_logger is null ? "Setting a" : "Replacing the")} native logger");
+ _logger = options.DiagnosticLogger;
+ sentry_options_set_logger(cOptions, new sentry_logger_function_t(nativeLog), IntPtr.Zero);
+ }
+
sentry_init(cOptions);
}
@@ -42,9 +92,77 @@ public static void Init(SentryUnityOptions options)
[DllImport("sentry")]
private static extern void sentry_options_set_release(IntPtr options, string release);
- // TODO we could set a logger for sentry-native, forwarding the logs to `options.DiagnosticLogger?`
- // [DllImport("sentry")]
- // private static extern void sentry_options_set_logger(IntPtr options, IntPtr logger, IntPtr userData);
+ [DllImport("sentry")]
+ private static extern void sentry_options_set_debug(IntPtr options, int debug);
+
+ [DllImport("sentry")]
+ private static extern void sentry_options_set_environment(IntPtr options, string environment);
+
+ [DllImport("sentry")]
+ private static extern void sentry_options_set_sample_rate(IntPtr options, double rate);
+
+ [DllImport("sentry")]
+ private static extern void sentry_options_set_database_path(IntPtr options, string path);
+
+ [DllImport("sentry")]
+ private static extern void sentry_options_set_database_pathw(IntPtr options, [MarshalAs(UnmanagedType.LPWStr)] string path);
+
+ [DllImport("sentry")]
+ private static extern void sentry_options_set_auto_session_tracking(IntPtr options, int debug);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = true)]
+ private delegate void sentry_logger_function_t(int level, string message, IntPtr argsAddress, IntPtr userData);
+
+ [DllImport("sentry")]
+ private static extern void sentry_options_set_logger(IntPtr options, sentry_logger_function_t logger, IntPtr userData);
+
+ // The logger we should forward native messages to. This is referenced by nativeLog() which in turn for.
+ private static IDiagnosticLogger? _logger;
+
+ // This method is called from the C library and forwards incoming messages to the currently set _logger.
+ [MonoPInvokeCallback(typeof(sentry_logger_function_t))]
+ private static void nativeLog(int cLevel, string message, IntPtr args, IntPtr userData)
+ {
+ var logger = _logger;
+ if (logger is null)
+ {
+ return;
+ }
+
+ // see sentry.h: sentry_level_e
+ var level = cLevel switch
+ {
+ -1 => SentryLevel.Debug,
+ 0 => SentryLevel.Info,
+ 1 => SentryLevel.Warning,
+ 2 => SentryLevel.Error,
+ 3 => SentryLevel.Fatal,
+ _ => SentryLevel.Info,
+ };
+
+ if (!logger.IsEnabled(level))
+ {
+ return;
+ }
+
+ // If the message contains any "formatting" modifiers (that should be substituted by `args`), we need
+ // to apply the formatting. However, we cannot access C var-arg (va_list) in c# thus we pass it back to
+ // vsnprintf (to find out the length of the resulting buffer) & vsprintf (to actually format the message).
+ if (message.Contains("%"))
+ {
+ var formattedLength = vsnprintf(null, UIntPtr.Zero, message, args);
+ var buffer = new StringBuilder(formattedLength + 1);
+ vsprintf(buffer, message, args);
+ message = buffer.ToString();
+ }
+ logger.Log(level, $"Native: {message}");
+ }
+
+ [DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
+ private static extern int vsprintf(StringBuilder buffer, string format, IntPtr args);
+
+ [DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
+ private static extern int vsnprintf(string? buffer, UIntPtr bufferSize, string format, IntPtr args);
[DllImport("sentry")]
private static extern void sentry_init(IntPtr options);
diff --git a/test/Scripts.Integration.Test/crash-test-server.py b/test/Scripts.Integration.Test/crash-test-server.py
new file mode 100644
index 000000000..a65d42adf
--- /dev/null
+++ b/test/Scripts.Integration.Test/crash-test-server.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+
+from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
+import sys
+import threading
+
+
+class Handler(BaseHTTPRequestHandler):
+ def commonServe(self):
+ self.send_response(200, "")
+ self.end_headers()
+ sys.stdout.flush()
+ sys.stderr.flush()
+
+ if (self.path == "/STOP"):
+ print("HTTP server stopping!")
+ threading.Thread(target=self.server.shutdown).start()
+
+ def do_GET(self):
+ self.commonServe()
+
+ def do_POST(self):
+ self.commonServe()
+
+
+host = '127.0.0.1'
+port = 8000
+uri = 'http://{}:{}'.format(host, port)
+print("HTTP server listening on {}".format(uri))
+print("To stop the server, execute a GET request to {}/STOP".format(uri))
+httpd = ThreadingHTTPServer((host, port), Handler)
+target = httpd.serve_forever()
diff --git a/test/Scripts.Integration.Test/integration-create-project.ps1 b/test/Scripts.Integration.Test/integration-create-project.ps1
index e9136cf9e..2f4a307b9 100644
--- a/test/Scripts.Integration.Test/integration-create-project.ps1
+++ b/test/Scripts.Integration.Test/integration-create-project.ps1
@@ -76,5 +76,10 @@ Else
Copy-Item -Recurse "$IntegrationScriptsPath/SentrySetup.*" -Destination "$NewProjectAssetsPath/Editor/"
Write-Host " OK"
+ # Don't print stack traces in debug logs. See ./samples/unity-of-bugs/ProjectSettings/PresetManager.asset
+ $projectSettingsPath = "$NewProjectPath/ProjectSettings/ProjectSettings.asset"
+ (Get-Content $projectSettingsPath) -replace "m_StackTraceTypes: ?[01]+", "m_StackTraceTypes: 010000000000000000000000000000000100000001000000" | `
+ Out-File $projectSettingsPath
+
Write-Host "`nProject created!!"
}
diff --git a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1
index e2a49024e..def8d51e9 100644
--- a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1
+++ b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1
@@ -1,52 +1,174 @@
-. ./test/Scripts.Integration.Test/IntegrationGlobals.ps1
+param (
+ [Parameter(Position = 0)]
+ [string] $TestAppPath = "",
+ [Parameter()]
+ [string] $AppDataDir = "",
-If ($IsMacOS)
-{
- $testAppPath = "$NewProjectBuildPath/test.app/Contents/MacOS/$NewProjectName"
+ [Parameter()]
+ [switch] $Smoke,
+
+ [Parameter()]
+ [switch] $Crash
+)
+. $PSScriptRoot/IntegrationGlobals.ps1
+
+Write-Host "Given parameters:"
+Write-Host " TestAppPath: $TestAppPath"
+Write-Host " AppDataDir: $AppDataDir"
+Write-Host " Smoke: $Smoke"
+Write-Host " Crash: $Crash"
+
+If (!$Smoke -and !$Crash) {
+ Write-Error "Select one of the following tests (or both): -Smoke or -Crash"
}
-ElseIf ($IsWindows)
-{
- $testAppPath = "$NewProjectBuildPath/test.exe"
+
+if ("$TestAppPath" -eq "") {
+ If ($IsMacOS) {
+ $TestAppPath = "$NewProjectBuildPath/test.app/Contents/MacOS/$NewProjectName"
+ }
+ ElseIf ($IsWindows) {
+ $TestAppPath = "$NewProjectBuildPath/test.exe"
+ if ("$AppDataDir" -eq "") {
+ $AppDataDir = "$env:UserProfile\AppData\LocalLow\DefaultCompany\$NewProjectName\"
+ }
+ }
+ ElseIf ($IsLinux) {
+ $TestAppPath = "$NewProjectBuildPath/test"
+ }
+ Else {
+ Write-Error "Unsupported build"
+ }
}
-ElseIf ($IsLinux)
-{
- $testAppPath = "$NewProjectBuildPath/test"
+
+if ("$AppDataDir" -ne "") {
+ if (Test-Path $AppDataDir) {
+ Write-Warning "Removing AppDataDir '$AppDataDir'"
+ Remove-Item -Recurse $AppDataDir
+ }
}
-Else
-{
- Throw "Unsupported build"
+else {
+ Write-Warning "No AppDataDir param given - if you're running this after a previous smoke-crash test, the smoke test will fail."
+ Write-Warning "You can provide AppDataDir and it will be deleted before running the test."
+ Write-Warning 'On windows, this would normally be: -AppDataDir "$env:UserProfile\AppData\LocalLow\DefaultCompany\unity-of-bugs\"'
}
-$process = Start-Process -FilePath "$testAppPath" -ArgumentList "--test", "smoke" -PassThru
+Set-Strictmode -Version latest
-If ($null -eq $process)
-{
- Throw "Process not found."
-}
+function RunTest([string] $type) {
+ Write-Host "Running $TestAppPath --test $type"
+
+ $process = Start-Process "$TestAppPath" -ArgumentList "--test", $type -PassThru
+ If ($null -eq $process) {
+ Throw "Process not found."
+ }
+
+ # Wait for the test to finish
+ $timedOut = $null # reset any previously set timeout
+ $process | Wait-Process -Timeout 60 -ErrorAction SilentlyContinue -ErrorVariable timedOut
-# Wait for 1 minute (sleeps for 500ms per iteration)
-$timeout = 60 * 2
-$processName = $process.Name
-Write-Host -NoNewline "Waiting for $processName"
+ if ("$AppDataDir" -ne "") {
+ Write-Host "$type test: Player.log contents:" -ForegroundColor Yellow
+ Get-Content "$AppDataDir/Player.log"
+ Write-Host "================================================================================" -ForegroundColor Yellow
+ Write-Host "$type test: Player.log contents END" -ForegroundColor Yellow
+ }
-While (!$process.HasExited -and $timeout -gt 0)
-{
- Start-Sleep -Milliseconds 500
- Write-Host -NoNewline "."
- $timeout--
+ # ExitCode 200 is the status code indicating success inside SmokeTest.cs
+ If ($process.ExitCode -eq 200) {
+ Write-Host "$type test: PASSED" -ForegroundColor Green
+ }
+ ElseIf ($timedOut) {
+ $process | Stop-Process
+ Throw "Test process timed out."
+ }
+ Else {
+ $info = "Test process finished with status code $($process.ExitCode)."
+ If ($type -ne "smoke-crash") {
+ if ("$AppDataDir" -ne "") {
+ Get-Content "$AppDataDir/Player.log"
+ }
+ throw $info
+ }
+ Write-Host $info
+ }
}
-# ExitCode 200 is the status code indicating success inside SmokeTest.cs
-If ($process.ExitCode -eq 200)
-{
- Write-Host "`nPASSED"
+function RunApiServer() {
+ $result = "" | Select-Object -Property process, outFile, errFile
+ Write-Host "Starting the HTTP server (dummy API server)"
+ $result.outFile = New-TemporaryFile
+ $result.errFile = New-TemporaryFile
+
+ $result.process = Start-Process "python3" -ArgumentList "$PSScriptRoot/crash-test-server.py" -NoNewWindow -PassThru -RedirectStandardOutput $result.outFile -RedirectStandardError $result.errFile
+
+ # The process shouldn't finish by itself, if it did, there was an error, so let's check that
+ Start-Sleep -Second 1
+ if ($result.process.HasExited) {
+ Write-Host "Couldn't start the HTTP server" -ForegroundColor Red
+ Write-Host "Standard Output:" -ForegroundColor Yellow
+ Get-Content $result.outFile
+ Write-Host "Standard Error:" -ForegroundColor Yellow
+ Get-Content $result.errFile
+ Remove-Item $result.outFile
+ Remove-Item $result.errFile
+ exit 1
+ }
+
+ return $result
}
-ElseIf ($timeout -eq 0)
-{
- Throw "Test process timed out."
+
+# Simple smoke test
+if ($Smoke) {
+ RunTest "smoke"
}
-Else
-{
- Throw "Test process failed with status code $($process.ExitCode)."
+
+# Native crash test
+if ($Crash) {
+ $runs = 1 # You can increase this to run the crash test multiple times in a loop (until it fails)
+ for ($run = 1; $run -le $runs; $run++) {
+ $httpServer = RunApiServer
+ RunTest "smoke-crash"
+
+ $httpServerUri = "http://localhost:8000"
+ $successMessage = "POST /api/12345/minidump/"
+
+ Write-Host "Waiting for the expected message to appear in the server output logs ..."
+ # Wait for 1 minute (600 * 100 milliseconds) until the expected message comes in
+ for ($i = 0; $i -lt 600; $i++) {
+ $output = (Get-Content $httpServer.outFile -Raw) + (Get-Content $httpServer.errFile -Raw)
+ if ("$output".Contains($successMessage)) {
+ break
+ }
+ Start-Sleep -Milliseconds 100
+ }
+
+ # Stop the HTTP server
+ Write-Host "Stopping the dummy API server ... " -NoNewline
+ try {
+ (Invoke-WebRequest -URI "$httpServerUri/STOP").StatusDescription
+ }
+ catch {
+ Write-Host "/STOP request failed, killing the server process"
+ $httpServer.process | Stop-Process -Force -ErrorAction SilentlyContinue
+ }
+ $httpServer.process | Wait-Process -Timeout 10 -ErrorAction Continue
+
+ Write-Host "Server stdout:" -ForegroundColor Yellow
+ Get-Content $httpServer.outFile -Raw
+
+ Write-Host "Server stderr:" -ForegroundColor Yellow
+ Get-Content $httpServer.errFile -Raw
+
+ $output = (Get-Content $httpServer.outFile -Raw) + (Get-Content $httpServer.errFile -Raw)
+ Remove-Item $httpServer.outFile -ErrorAction Continue
+ Remove-Item $httpServer.errFile -ErrorAction Continue
+
+ if ($output.Contains($successMessage)) {
+ Write-Host "smoke-crash test $run/$runs : PASSED" -ForegroundColor Green
+ }
+ else {
+ Write-Error "smoke-crash test $run/$runs : FAILED"
+ }
+ }
}
diff --git a/test/Scripts.Integration.Test/integration-update-sentry.ps1 b/test/Scripts.Integration.Test/integration-update-sentry.ps1
index 7b605e37f..c846b2cce 100644
--- a/test/Scripts.Integration.Test/integration-update-sentry.ps1
+++ b/test/Scripts.Integration.Test/integration-update-sentry.ps1
@@ -4,37 +4,40 @@ param($arg)
$unityPath = FormatUnityPath $arg
-If (-not(Test-Path -Path "$PackageReleaseOutput"))
-{
+If (-not(Test-Path -Path "$PackageReleaseOutput")) {
Throw "Path $PackageReleaseOutput does not exist. Be sure to run ./test/Scripts.Integration.Test/integration-create-project."
}
-ClearUnityLog
-
-Write-Host -NoNewline "Starting Unity process:"
-$UnityProcess = RunUnity $unityPath @("-batchmode", "-projectPath ", "$NewProjectPath", "-logfile", "$NewProjectLogPath", "-installSentry", "Disk")
-Write-Host " OK"
+function RunUnityAndExpect([string] $name, [string] $successMessage, [string] $failMessage, [string[]] $arguments) {
+ ClearUnityLog
+
+ Write-Host -NoNewline "$name | Starting Unity process:"
+ $UnityProcess = RunUnity $unityPath $arguments
+ Write-Host " OK"
+
+ WaitForLogFile 30
+
+ Write-Host "$name | Waiting for Unity to finish."
+ $stdout = SubscribeToUnityLogFile $UnityProcess $successMessage $failMessage
+
+ Write-Host $stdout
+ If ($UnityProcess.ExitCode -ne 0) {
+ $exitCode = $UnityProcess.ExitCode
+ Write-Error "$name | Unity exited with code $exitCode"
+ }
+ ElseIf ($null -ne ($stdout | select-string $successMessage)) {
+ Write-Host "`n$name | SUCCESS" -ForegroundColor Green
+ }
+ Else {
+ Write-Error "$name | Unity exited without an error but the successMessage was not found in the output ('$successMessage')"
+ }
+}
-WaitForLogFile 30
-$successMessage = "Sentry Package Installation:"
+RunUnityAndExpect "AddSentryPackage" "Sentry Package Installation:" "Sentry setup: FAILED" @( `
+ "-batchmode", "-projectPath ", "$NewProjectPath", "-logfile", "$NewProjectLogPath", "-installSentry", "Disk")
-Write-Host "Waiting for Unity to add Sentry to the project."
-$stdout = SubscribeToUnityLogFile $UnityProcess $successMessage "Sentry setup: FAILED"
-
-Write-Host $stdout
-If ($UnityProcess.ExitCode -ne 0)
-{
- $exitCode = $UnityProcess.ExitCode
- Throw "Unity exited with code $exitCode"
-}
-ElseIf ($null -ne ($stdout | select-string $successMessage))
-{
- Write-Host "`nSentry added!!"
-}
-Else
-{
- Throw "Unity exited but failed to add the Sentry package."
-}
+RunUnityAndExpect "ConfigureSentryOptions" "ConfigureOptions: Sentry options Configured" "ConfigureOptions failed" @( `
+ "-quit", "-batchmode", "-nographics", "-projectPath ", "$NewProjectPath", "-logfile", "$NewProjectLogPath", "-executeMethod", "Sentry.Unity.Editor.ConfigurationWindow.SentryEditorWindowInstrumentation.ConfigureOptions", "-sentryOptions.Dsn", "http://publickey@localhost:8000/12345")
Write-Host -NoNewline "Updating test files "
# We were previously using an empty SmokeTester to not generate Build errors.
@@ -43,4 +46,6 @@ Remove-Item -Path "$NewProjectAssetsPath/Scripts/SmokeTester.cs"
Remove-Item -Path "$NewProjectAssetsPath/Scripts/SmokeTester.cs.meta"
Copy-Item "$PackageReleaseAssetsPath/Scripts/SmokeTester.cs" -Destination "$NewProjectAssetsPath/Scripts"
Copy-Item "$PackageReleaseAssetsPath/Scripts/SmokeTester.cs.meta" -Destination "$NewProjectAssetsPath/Scripts"
-Write-Host " OK"
+Copy-Item "$PackageReleaseAssetsPath/Scripts/NativeSupport/CppPlugin.*" -Destination "$NewProjectAssetsPath/Scripts/"
+
+Write-Host " Unity configuration finished successfully" -ForegroundColor Green