Skip to content

Commit c91d7ea

Browse files
authored
Merge 4a9a750 into 78f7c1a
2 parents 78f7c1a + 4a9a750 commit c91d7ea

File tree

10 files changed

+144
-108
lines changed

10 files changed

+144
-108
lines changed

.sauce/sentry-uitest-android-ui.yml

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,6 @@ suites:
2626
- name: ".*"
2727
platformVersion: "15"
2828

29-
- name: "Android 14 Ui test (api 34)"
30-
testOptions:
31-
clearPackageData: true
32-
useTestOrchestrator: true
33-
devices:
34-
- name: ".*"
35-
platformVersion: "14"
36-
37-
- name: "Android 13 Ui test (api 33)"
38-
testOptions:
39-
clearPackageData: true
40-
useTestOrchestrator: true
41-
devices:
42-
- name: ".*"
43-
platformVersion: "13"
44-
4529
# Controls what artifacts to fetch when the suite on Sauce Cloud has finished.
4630
artifacts:
4731
download:

gradle/libs.versions.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
apollo = "2.5.9"
33
androidxLifecycle = "2.2.0"
44
androidxNavigation = "2.4.2"
5-
androidxTestCore = "1.6.1"
5+
androidxTestCore = "1.7.0"
66
androidxCompose = "1.6.3"
77
composeCompiler = "1.5.14"
88
coroutines = "1.6.1"
9-
espresso = "3.5.0"
9+
espresso = "3.7.0"
1010
feign = "11.6"
1111
jacoco = "0.8.7"
1212
jackson = "2.18.3"

sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import java.util.HashMap;
4747
import java.util.List;
4848
import java.util.Map;
49+
import java.util.concurrent.RejectedExecutionException;
4950
import java.util.concurrent.atomic.AtomicBoolean;
5051
import org.jetbrains.annotations.NotNull;
5152
import org.jetbrains.annotations.Nullable;
@@ -181,26 +182,30 @@ private void registerReceiver(
181182
}
182183
}
183184

184-
private void unregisterReceiver() {
185+
@SuppressWarnings("Convert2MethodRef") // older AGP versions do not support method references
186+
private void scheduleUnregisterReceiver() {
185187
if (options == null) {
186188
return;
187189
}
188190

189-
options
190-
.getExecutorService()
191-
.submit(
192-
() -> {
193-
final @Nullable SystemEventsBroadcastReceiver receiverRef;
194-
try (final @NotNull ISentryLifecycleToken ignored = receiverLock.acquire()) {
195-
isStopped = true;
196-
receiverRef = receiver;
197-
receiver = null;
198-
}
191+
try {
192+
options.getExecutorService().submit(() -> unregisterReceiver());
193+
} catch (RejectedExecutionException e) {
194+
unregisterReceiver();
195+
}
196+
}
199197

200-
if (receiverRef != null) {
201-
context.unregisterReceiver(receiverRef);
202-
}
203-
});
198+
private void unregisterReceiver() {
199+
final @Nullable SystemEventsBroadcastReceiver receiverRef;
200+
try (final @NotNull ISentryLifecycleToken ignored = receiverLock.acquire()) {
201+
isStopped = true;
202+
receiverRef = receiver;
203+
receiver = null;
204+
}
205+
206+
if (receiverRef != null) {
207+
context.unregisterReceiver(receiverRef);
208+
}
204209
}
205210

206211
@Override
@@ -215,7 +220,7 @@ public void close() throws IOException {
215220
}
216221

217222
AppState.getInstance().removeAppStateListener(this);
218-
unregisterReceiver();
223+
scheduleUnregisterReceiver();
219224

220225
if (options != null) {
221226
options.getLogger().log(SentryLevel.DEBUG, "SystemEventsBreadcrumbsIntegration removed.");
@@ -264,7 +269,7 @@ public void onForeground() {
264269

265270
@Override
266271
public void onBackground() {
267-
unregisterReceiver();
272+
scheduleUnregisterReceiver();
268273
}
269274

270275
final class SystemEventsBroadcastReceiver extends BroadcastReceiver {

sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/BaseUiTest.kt

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,17 @@ import androidx.test.espresso.IdlingRegistry
88
import androidx.test.espresso.idling.CountingIdlingResource
99
import androidx.test.platform.app.InstrumentationRegistry
1010
import androidx.test.runner.AndroidJUnitRunner
11+
import io.sentry.JsonSerializer
12+
import io.sentry.ProfilingTraceData
1113
import io.sentry.Sentry
1214
import io.sentry.Sentry.OptionsConfiguration
15+
import io.sentry.SentryEnvelope
16+
import io.sentry.SentryEvent
17+
import io.sentry.SentryItemType
18+
import io.sentry.SentryOptions
1319
import io.sentry.android.core.SentryAndroid
1420
import io.sentry.android.core.SentryAndroidOptions
21+
import io.sentry.protocol.SentryTransaction
1522
import io.sentry.test.applyTestOptions
1623
import io.sentry.test.initForTest
1724
import io.sentry.uitest.android.mockservers.MockRelay
@@ -43,9 +50,9 @@ abstract class BaseUiTest {
4350
/** Mock relay server that receives all envelopes sent during the test. */
4451
protected val relay = MockRelay(false, relayIdlingResource)
4552

46-
private fun disableDontKeepActivities() {
53+
private fun runCommand(cmd: String) {
4754
val automation = InstrumentationRegistry.getInstrumentation().uiAutomation
48-
val pfd = automation.executeShellCommand("settings put global always_finish_activities 0")
55+
val pfd = automation.executeShellCommand(cmd)
4956
try {
5057
FileInputStream(pfd.fileDescriptor).readBytes()
5158
} catch (e: Throwable) {
@@ -54,6 +61,16 @@ abstract class BaseUiTest {
5461
pfd.close()
5562
}
5663

64+
private fun disableDontKeepActivities() {
65+
runCommand("settings put global always_finish_activities 0")
66+
}
67+
68+
fun disableSystemAnimations() {
69+
runCommand("settings put global window_animation_scale 0")
70+
runCommand("settings put global transition_animation_scale 0")
71+
runCommand("settings put global animator_duration_scale 0")
72+
}
73+
5774
@BeforeTest
5875
fun baseSetUp() {
5976
runner = InstrumentationRegistry.getInstrumentation() as AndroidJUnitRunner
@@ -63,6 +80,7 @@ abstract class BaseUiTest {
6380
relay.start()
6481
mockDsn = relay.createMockDsn()
6582
disableDontKeepActivities()
83+
disableSystemAnimations()
6684
}
6785

6886
@AfterTest
@@ -126,3 +144,40 @@ fun initForTest(
126144
optionsConfiguration.configure(it)
127145
}
128146
}
147+
148+
/**
149+
* Function used to describe the content of the envelope to print in the logs. For debugging
150+
* purposes only.
151+
*/
152+
internal fun SentryEnvelope.describeForTest(): String {
153+
var descr = ""
154+
items.forEach { item ->
155+
when (item.header.type) {
156+
SentryItemType.Event -> {
157+
val deserialized =
158+
JsonSerializer(SentryOptions())
159+
.deserialize(item.data.inputStream().reader(), SentryEvent::class.java)!!
160+
descr +=
161+
"Event (${deserialized.eventId}) - message: ${deserialized.message!!.formatted} -- "
162+
}
163+
SentryItemType.Transaction -> {
164+
val deserialized =
165+
JsonSerializer(SentryOptions())
166+
.deserialize(item.data.inputStream().reader(), SentryTransaction::class.java)!!
167+
descr +=
168+
"Transaction (${deserialized.eventId}) - transaction: ${deserialized.transaction} - spans: ${deserialized.spans.joinToString { "${it.op} ${it.description}" }} -- "
169+
}
170+
SentryItemType.Profile -> {
171+
val deserialized =
172+
JsonSerializer(SentryOptions())
173+
.deserialize(item.data.inputStream().reader(), ProfilingTraceData::class.java)!!
174+
descr +=
175+
"Profile (${deserialized.profileId}) - transactionName: ${deserialized.transactionName} -- "
176+
}
177+
else -> {
178+
descr += "${item.header.type} -- "
179+
}
180+
}
181+
}
182+
return "*** Envelope: $descr ***"
183+
}

sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/EnvelopeTests.kt

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ class EnvelopeTests : BaseUiTest() {
5656

5757
relayIdlingResource.increment()
5858
IdlingRegistry.getInstance().register(ProfilingSampleActivity.scrollingIdlingResource)
59-
Thread.sleep(1000)
6059
val transaction = Sentry.startTransaction("profiledTransaction", "test1")
6160
val sampleScenario = launchActivity<ProfilingSampleActivity>()
6261
swipeList(1)
@@ -171,15 +170,17 @@ class EnvelopeTests : BaseUiTest() {
171170
values.last().relativeStartNs.toLong() <= maxTimestampAllowed,
172171
"Last measurement value for '$name' is outside bounds (was: ${values.last().relativeStartNs.toLong()}ns, max: ${maxTimestampAllowed}ns",
173172
)
174-
}
175173

176-
// Timestamps of measurements should differ at least 10 milliseconds from each other
177-
(1 until values.size).forEach { i ->
178-
assertTrue(
179-
values[i].relativeStartNs.toLong() >=
180-
values[i - 1].relativeStartNs.toLong() + TimeUnit.MILLISECONDS.toNanos(10),
181-
"Measurement value timestamp for '$name' does not differ at least 10ms",
182-
)
174+
// Timestamps of measurements should differ at least 10 milliseconds from each other
175+
(1 until values.size).forEach { i ->
176+
val measurementTimestampDiff =
177+
values[i].relativeStartNs.toLong() - values[i - 1].relativeStartNs.toLong()
178+
179+
assertTrue(
180+
measurementTimestampDiff >= TimeUnit.MILLISECONDS.toNanos(10),
181+
"Measurement value timestamp for '$name' should differ at least 10ms, but was $measurementTimestampDiff",
182+
)
183+
}
183184
}
184185
}
185186

sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,16 @@ class SdkInitTests : BaseUiTest() {
5656
}
5757
val transaction = Sentry.startTransaction("e2etests", "testInit")
5858
val sampleScenario = launchActivity<EmptyActivity>()
59+
5960
initSentry(true) {
6061
it.tracesSampleRate = 1.0
6162
it.profilesSampleRate = 1.0
6263
// We use the same executorService before and after closing the SDK
6364
it.executorService = options.executorService
6465
it.isDebug = true
6566
}
67+
68+
relayIdlingResource.increment()
6669
relayIdlingResource.increment()
6770
relayIdlingResource.increment()
6871
transaction.finish()
@@ -79,9 +82,19 @@ class SdkInitTests : BaseUiTest() {
7982
it.assertNoOtherItems()
8083
assertEquals("e2etests", transactionItem.transaction)
8184
}
82-
}
8385

84-
relay.assert {
86+
findEnvelope {
87+
assertEnvelopeTransaction(it.items.toList(), AndroidLogger()).transaction ==
88+
"EmptyActivity"
89+
}
90+
.assert {
91+
val transactionItem: SentryTransaction = it.assertTransaction()
92+
// Transaction-based Profiling is already in e2etests transaction, so it won't run again
93+
// here
94+
it.assertNoOtherItems()
95+
assertEquals("EmptyActivity", transactionItem.transaction)
96+
}
97+
8598
findEnvelope {
8699
assertEnvelopeTransaction(it.items.toList(), AndroidLogger()).transaction == "e2etests2"
87100
}

sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
2323
import io.sentry.Sentry
2424
import io.sentry.SentryEvent
2525
import io.sentry.SentryFeedbackOptions.SentryFeedbackCallback
26+
import io.sentry.SentryItemType
2627
import io.sentry.SentryOptions
2728
import io.sentry.android.core.AndroidLogger
2829
import io.sentry.android.core.R
@@ -471,6 +472,9 @@ class UserFeedbackUiTest : BaseUiTest() {
471472
// because it would block the espresso interactions (button click)
472473
it.feedbackOptions.onSubmitSuccess = SentryFeedbackCallback {
473474
relayIdlingResource.increment()
475+
if (enableReplay) {
476+
relayIdlingResource.increment()
477+
}
474478
}
475479
// Let's capture a replay, so we can check the replayId in the feedback
476480
if (enableReplay) {
@@ -511,6 +515,10 @@ class UserFeedbackUiTest : BaseUiTest() {
511515
)
512516
}
513517
}
518+
if (enableReplay) {
519+
findEnvelope { it.items.first().header.type == SentryItemType.ReplayVideo }
520+
}
521+
assertNoOtherEnvelopes()
514522
}
515523
}
516524

sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/mockservers/MockRelay.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.sentry.uitest.android.mockservers
22

33
import androidx.test.espresso.idling.CountingIdlingResource
4+
import io.sentry.uitest.android.describeForTest
45
import io.sentry.uitest.android.waitUntilIdle
56
import kotlin.test.assertNotNull
67
import okhttp3.mockwebserver.Dispatcher
@@ -118,7 +119,19 @@ class MockRelay(
118119
/** Wait to receive all requests (if [waitForRequests] is true) and run the [assertion]. */
119120
fun assert(assertion: RelayAsserter.() -> Unit) {
120121
if (waitForRequests) {
121-
waitUntilIdle()
122+
try {
123+
waitUntilIdle()
124+
} catch (e: Exception) {
125+
if (unassertedEnvelopes.isNotEmpty()) {
126+
throw AssertionError(
127+
"There was a total of ${unassertedEnvelopes.size} envelopes: " +
128+
unassertedEnvelopes.joinToString { it.envelope!!.describeForTest() },
129+
e,
130+
)
131+
} else {
132+
throw e
133+
}
134+
}
122135
}
123136
assertion(RelayAsserter(unassertedEnvelopes))
124137
}

0 commit comments

Comments
 (0)