Skip to content

Commit 4d4e2dd

Browse files
authored
feat: CI runs for runtime and buildtime init on mobile (#1944)
1 parent e53f5a6 commit 4d4e2dd

File tree

8 files changed

+91
-35
lines changed

8 files changed

+91
-35
lines changed

Diff for: .github/workflows/android-smoke-test.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ on:
77
api-level:
88
required: true
99
type: string
10+
init-type:
11+
required: true
12+
type: string
1013
# Map the workflow outputs to job outputs
1114
outputs:
1215
status:
@@ -33,7 +36,7 @@ jobs:
3336
- name: Download test app artifact
3437
uses: actions/download-artifact@v4
3538
with:
36-
name: testapp-Android-compiled-${{ inputs.unity-version }}
39+
name: testapp-Android-compiled-${{ inputs.unity-version }}-${{ inputs.init-type }}
3740
path: samples/IntegrationTest/Build
3841

3942
# See https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/
@@ -83,4 +86,4 @@ jobs:
8386
with:
8487
name: testapp-android-logs-${{ inputs.api-level }}-${{ inputs.unity-version }}
8588
path: ${{ env.ARTIFACTS_PATH }}
86-
retention-days: 14
89+
retention-days: 14

Diff for: .github/workflows/ci.yml

+46-14
Original file line numberDiff line numberDiff line change
@@ -339,17 +339,45 @@ jobs:
339339
run: |
340340
# Note: remove local.properties file that contains Android SDK & NDK paths in the Unity installation.
341341
rm -rf samples/IntegrationTest/Build/*_BackUpThisFolder_ButDontShipItWithYourGame
342-
tar -cvzf test-app.tar.gz samples/IntegrationTest/Build
342+
tar -cvzf test-app-runtime.tar.gz samples/IntegrationTest/Build
343343
344+
# Upload runtime initialization build
344345
- name: Upload test app
345346
uses: actions/upload-artifact@v4
346347
with:
347-
name: testapp-${{ matrix.platform }}-${{ matrix.unity-version }}
348+
name: testapp-${{ matrix.platform }}-${{ matrix.unity-version }}-runtime
348349
if-no-files-found: error
349-
path: test-app.tar.gz
350-
# Lower retention period - we only need this to retry CI.
350+
path: test-app-runtime.tar.gz
351351
retention-days: 14
352352

353+
- name: Configure Sentry for mobile platforms (build-time initialization)
354+
if: ${{ matrix.platform == 'iOS' || matrix.platform == 'Android' }}
355+
run: |
356+
$optionsPath = "samples/IntegrationTest/Assets/Scripts/OptionsConfiguration.cs"
357+
$content = Get-Content $optionsPath -Raw
358+
$content = $content -replace 'AndroidNativeInitializationType = NativeInitializationType.Runtime', 'AndroidNativeInitializationType = NativeInitializationType.BuildTime'
359+
$content = $content -replace 'IosNativeInitializationType = NativeInitializationType.Runtime', 'IosNativeInitializationType = NativeInitializationType.BuildTime'
360+
Set-Content $optionsPath $content
361+
362+
- name: Build Project for mobile platforms (build-time initialization)
363+
if: ${{ matrix.platform == 'iOS' || matrix.platform == 'Android' }}
364+
run: ./test/Scripts.Integration.Test/build-project.ps1 -UnityPath "${{ env.UNITY_PATH }}" -Platform ${{ matrix.build_platform }} -CheckSymbols:$${{ matrix.check_symbols }} -UnityVersion "${{ matrix.unity-version }}"
365+
366+
- name: Create archive (build-time initialization)
367+
shell: bash
368+
run: |
369+
rm -rf samples/IntegrationTest/Build/*_BackUpThisFolder_ButDontShipItWithYourGame
370+
tar -cvzf test-app-buildtime.tar.gz samples/IntegrationTest/Build
371+
372+
# Upload build-time initialization build
373+
- name: Upload test app (build-time initialization)
374+
uses: actions/upload-artifact@v4
375+
with:
376+
name: testapp-${{ matrix.platform }}-${{ matrix.unity-version }}-buildtime
377+
if-no-files-found: error
378+
path: test-app-buildtime.tar.gz
379+
retention-days: 14
380+
353381
- name: Upload IntegrationTest project on failure
354382
if: ${{ failure() }}
355383
uses: actions/upload-artifact@v4
@@ -437,27 +465,30 @@ jobs:
437465
android-smoke-test-run:
438466
if: ${{ !startsWith(github.ref, 'refs/heads/release/') }}
439467
needs: [mobile-smoke-test-compile]
440-
name: ${{ matrix.unity-version }} Android ${{ matrix.api-level }} Run Smoke Test
468+
name: ${{ matrix.unity-version }} Android ${{ matrix.api-level }} ${{ matrix.init-type }} Run Smoke Test
441469
uses: ./.github/workflows/android-smoke-test.yml
442470
with:
443471
unity-version: ${{ matrix.unity-version }}
444472
api-level: ${{ matrix.api-level }}
473+
init-type: ${{ matrix.init-type }}
445474
strategy:
446475
fail-fast: false
447476
matrix:
448477
api-level: [30, 31, 34] # last updated January 2025
478+
init-type: ["runtime", "buildtime"]
449479
unity-version: ["2019", "6000"]
450480

451481
mobile-smoke-test-compile:
452482
if: ${{ !startsWith(github.ref, 'refs/heads/release/') }}
453483
needs: [smoke-test-build]
454-
name: ${{ matrix.unity-version }} ${{ matrix.platform }} Compile Smoke Test
484+
name: ${{ matrix.unity-version }} ${{ matrix.platform }} ${{ matrix.init-type }} Compile Smoke Test
455485
runs-on: ${{ matrix.platform == 'iOS' && 'macos-latest' || 'ubuntu-latest-4-cores' }}
456486
strategy:
457487
fail-fast: false
458488
matrix:
459489
unity-version: ["2019", "2022", "6000"]
460490
platform: ["Android", "iOS"]
491+
init-type: ["runtime", "buildtime"]
461492
include:
462493
# See supported version in https://docs.unity3d.com/6000.0/Documentation/Manual/android-sdksetup.html
463494
- unity-version: "2019"
@@ -474,10 +505,10 @@ jobs:
474505
- name: Download app project
475506
uses: actions/download-artifact@v4
476507
with:
477-
name: testapp-${{ matrix.platform }}-${{ matrix.unity-version }}
508+
name: testapp-${{ matrix.platform }}-${{ matrix.unity-version }}-${{ matrix.init-type }}
478509

479510
- name: Extract app project
480-
run: tar -xvzf test-app.tar.gz
511+
run: tar -xvzf test-app-${{ matrix.init-type }}.tar.gz
481512

482513
- name: Setup Android
483514
uses: android-actions/setup-android@7c5672355aaa8fde5f97a91aa9a99616d1ace6bc # pin@v2
@@ -526,7 +557,7 @@ jobs:
526557
if: ${{ failure() }}
527558
uses: actions/upload-artifact@v4
528559
with:
529-
name: failed-project-${{ matrix.platform }}-${{ matrix.unity-version }}-but-compiled
560+
name: failed-project-${{ matrix.platform }}-${{ matrix.unity-version }}-${{ matrix.init-type }}-but-compiled
530561
path: |
531562
samples/IntegrationTest
532563
!samples/IntegrationTest/Build/*_BackUpThisFolder_ButDontShipItWithYourGame
@@ -539,7 +570,7 @@ jobs:
539570
if: |
540571
!(matrix.platform == 'Android' && matrix.unity-version == '2022') || matrix.platform == 'iOS'
541572
with:
542-
name: testapp-${{ matrix.platform }}-compiled-${{ matrix.unity-version }}
573+
name: testapp-${{ matrix.platform }}-compiled-${{ matrix.unity-version }}-${{ matrix.init-type }}
543574
# Collect app but ignore the files that are not required for the test.
544575
path: |
545576
samples/IntegrationTest/Build/*.apk
@@ -552,7 +583,7 @@ jobs:
552583
ios-smoke-test-run:
553584
if: ${{ !startsWith(github.ref, 'refs/heads/release/') }}
554585
needs: [mobile-smoke-test-compile]
555-
name: ${{ matrix.unity-version }} iOS ${{ matrix.ios }} Run Smoke Test
586+
name: ${{ matrix.unity-version }} iOS ${{ matrix.ios }} ${{ matrix.init-type }} Run Smoke Test
556587
runs-on: macos-13 # Pinning to get the oldest, supported version of iOS simulator
557588
strategy:
558589
fail-fast: false
@@ -568,6 +599,7 @@ jobs:
568599
# Also make sure to match the versions available here:
569600
# - https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md
570601
ios: ["16.1", latest] # last updated October 2024
602+
init-type: ["runtime", "buildtime"]
571603

572604
steps:
573605
- name: Checkout
@@ -576,7 +608,7 @@ jobs:
576608
- name: Download app artifact
577609
uses: actions/download-artifact@v4
578610
with:
579-
name: testapp-iOS-compiled-${{ matrix.unity-version }}
611+
name: testapp-iOS-compiled-${{ matrix.unity-version }}-${{ matrix.init-type }}
580612
path: samples/IntegrationTest/Build
581613

582614
- name: Set Xcode for iOS version ${{matrix.ios}}
@@ -614,10 +646,10 @@ jobs:
614646
uses: actions/download-artifact@v4
615647
id: download
616648
with:
617-
name: testapp-${{ matrix.platform }}-${{ matrix.unity-version }}
649+
name: testapp-${{ matrix.platform }}-${{ matrix.unity-version }}-runtime
618650

619651
- name: Extract test app
620-
run: tar -xvzf test-app.tar.gz
652+
run: tar -xvzf test-app-runtime.tar.gz
621653

622654
- name: Run (WebGL)
623655
if: ${{ matrix.platform == 'WebGL' }}

Diff for: scripts/smoke-test-android.ps1

+2-2
Original file line numberDiff line numberDiff line change
@@ -424,8 +424,8 @@ $results.hasntCrashedTestPassed = RunTestWithRetry -Name "hasnt-crashed" -Succes
424424
try
425425
{
426426
CrashTestWithServer -SuccessString "POST /api/12345/envelope/ HTTP/1.1`" 200 -b'1f8b08000000000000" -CrashTestCallback {
427-
$results.crashTestPassed = RunTestWithRetry -Name "crash" -SuccessString "CRASH TEST: Issuing a native crash" -FailureString "CRASH TEST: FAIL" -MaxRetries 3
428-
$results.hasCrashTestPassed = RunTestWithRetry -Name "has-crashed" -SuccessString "HAS-CRASHED TEST: PASS" -FailureString "HAS-CRASHED TEST: FAIL" -MaxRetries 3
427+
$results.crashTestPassed = RunTest -Name "crash" -SuccessString "CRASH TEST: Issuing a native crash" -FailureString "CRASH TEST: FAIL"
428+
$results.hasCrashTestPassed = RunTest -Name "has-crashed" -SuccessString "HAS-CRASHED TEST: PASS" -FailureString "HAS-CRASHED TEST: FAIL"
429429
}
430430
}
431431
catch

Diff for: src/Sentry.Unity.Android/SentryJava.cs

+5-7
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ namespace Sentry.Unity.Android;
88

99
internal interface ISentryJava
1010
{
11-
public bool IsEnabled(IJniExecutor jniExecutor);
12-
public bool Init(IJniExecutor jniExecutor, SentryUnityOptions options, TimeSpan timeout);
11+
public bool IsEnabled(IJniExecutor jniExecutor, TimeSpan timeout);
12+
public void Init(IJniExecutor jniExecutor, SentryUnityOptions options, TimeSpan timeout);
1313
public string? GetInstallationId(IJniExecutor jniExecutor);
1414
public bool? CrashedLastRun(IJniExecutor jniExecutor);
1515
public void Close(IJniExecutor jniExecutor);
@@ -45,16 +45,16 @@ internal class SentryJava : ISentryJava
4545
{
4646
private static AndroidJavaObject GetSentryJava() => new AndroidJavaClass("io.sentry.Sentry");
4747

48-
public bool IsEnabled(IJniExecutor jniExecutor)
48+
public bool IsEnabled(IJniExecutor jniExecutor, TimeSpan timeout)
4949
{
5050
return jniExecutor.Run(() =>
5151
{
5252
using var sentry = GetSentryJava();
5353
return sentry.CallStatic<bool>("isEnabled");
54-
});
54+
}, timeout);
5555
}
5656

57-
public bool Init(IJniExecutor jniExecutor, SentryUnityOptions options, TimeSpan timeout)
57+
public void Init(IJniExecutor jniExecutor, SentryUnityOptions options, TimeSpan timeout)
5858
{
5959
jniExecutor.Run(() =>
6060
{
@@ -97,8 +97,6 @@ public bool Init(IJniExecutor jniExecutor, SentryUnityOptions options, TimeSpan
9797
androidOptions.Call("setEnableScopePersistence", false);
9898
}, options.DiagnosticLogger));
9999
}, timeout);
100-
101-
return IsEnabled(jniExecutor);
102100
}
103101

104102
internal class AndroidOptionsConfiguration : AndroidJavaProxy

Diff for: src/Sentry.Unity.Android/SentryNativeAndroid.cs

+25-6
Original file line numberDiff line numberDiff line change
@@ -28,33 +28,50 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry
2828
return;
2929
}
3030

31+
options.DiagnosticLogger?.LogDebug("Checking whether the Android SDK is present.");
32+
3133
if (!SentryJava.IsSentryJavaPresent())
3234
{
3335
options.DiagnosticLogger?.LogError("Android Native Support has been enabled but the " +
34-
"Sentry Java SDK is missing. This could have been caused by a mismatching" +
36+
"Android SDK is missing. This could have been caused by a mismatching" +
3537
"build time / runtime configuration. Please make sure you have " +
3638
"Android Native Support enabled during build time.");
3739
return;
3840
}
3941

4042
JniExecutor ??= new JniExecutor(options.DiagnosticLogger);
4143

42-
if (SentryJava.IsEnabled(JniExecutor))
44+
options.DiagnosticLogger?.LogDebug("Checking whether the Android SDK has already been initialized");
45+
46+
if (SentryJava.IsEnabled(JniExecutor, TimeSpan.FromMilliseconds(200)))
4347
{
4448
options.DiagnosticLogger?.LogDebug("The Android SDK is already initialized");
4549
}
46-
// Local testing had Init at an average of about 25ms.
47-
else if (!SentryJava.Init(JniExecutor, options, TimeSpan.FromMilliseconds(200)))
50+
else
4851
{
49-
options.DiagnosticLogger?.LogError("Failed to initialize Android Native Support");
50-
return;
52+
options.DiagnosticLogger?.LogInfo("Initializing the Android SDK");
53+
54+
// Local testing had Init at an average of about 25ms.
55+
SentryJava.Init(JniExecutor, options, TimeSpan.FromMilliseconds(200));
56+
57+
options.DiagnosticLogger?.LogDebug("Validating Android SDK initialization");
58+
59+
if (!SentryJava.IsEnabled(JniExecutor, TimeSpan.FromMilliseconds(200)))
60+
{
61+
options.DiagnosticLogger?.LogError("Failed to initialize Android Native Support");
62+
return;
63+
}
5164
}
5265

66+
options.DiagnosticLogger?.LogDebug("Configuring scope sync");
67+
5368
options.NativeContextWriter = new NativeContextWriter(JniExecutor, SentryJava);
5469
options.ScopeObserver = new AndroidJavaScopeObserver(options, JniExecutor);
5570
options.EnableScopeSync = true;
5671
options.CrashedLastRun = () =>
5772
{
73+
options.DiagnosticLogger?.LogDebug("Checking for `CrashedLastRun`");
74+
5875
var crashedLastRun = SentryJava.CrashedLastRun(JniExecutor);
5976
if (crashedLastRun is null)
6077
{
@@ -89,6 +106,8 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry
89106

90107
options.NativeSupportCloseCallback = () => Close(options, sentryUnityInfo);
91108

109+
options.DiagnosticLogger?.LogDebug("Fetching installation ID");
110+
92111
options.DefaultUserId = SentryJava.GetInstallationId(JniExecutor);
93112
if (string.IsNullOrEmpty(options.DefaultUserId))
94113
{

Diff for: test/Scripts.Integration.Test/Scripts/OptionsConfiguration.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ public override void Configure(SentryUnityOptions options)
4040
// If an ANR triggers while the smoke test runs, the test would fail because we expect exact order of events.
4141
options.DisableAnrIntegration();
4242

43-
Debug.Log("Sentry: BuildTimeOptions::Configure() finished");
43+
// These options will get overwritten by CI. This allows us to create artifacts for both initialization types.
44+
options.AndroidNativeInitializationType = NativeInitializationType.Runtime;
45+
options.IosNativeInitializationType = NativeInitializationType.Runtime;
46+
47+
Debug.Log("Sentry: OptionsConfig::Configure() finished");
4448
}
4549
}

Diff for: test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public void Configure_DefaultConfigurationSentryJavaNotPresent_LogsErrorAndRetur
122122

123123
Assert.IsTrue(_logger.Logs.Any(log =>
124124
log.logLevel == SentryLevel.Error &&
125-
log.message.Contains("Sentry Java SDK is missing.")));
125+
log.message.Contains("Android SDK is missing.")));
126126

127127
Assert.Null(_options.ScopeObserver);
128128
}

Diff for: test/Sentry.Unity.Android.Tests/TestSentryJava.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ internal class TestSentryJava : ISentryJava
1010
public string? InstallationId { get; set; }
1111
public bool? IsCrashedLastRun { get; set; }
1212

13-
public bool IsEnabled(IJniExecutor jniExecutor) => Enabled;
13+
public bool IsEnabled(IJniExecutor jniExecutor, TimeSpan timeout) => Enabled;
1414

15-
public bool Init(IJniExecutor jniExecutor, SentryUnityOptions options, TimeSpan timeout) => InitSuccessful;
15+
public void Init(IJniExecutor jniExecutor, SentryUnityOptions options, TimeSpan timeout) { }
1616

1717
public string? GetInstallationId(IJniExecutor jniExecutor) => InstallationId;
1818

0 commit comments

Comments
 (0)