/bugsnag-android-core/src/test/java/com/bugsnag/android/LaunchCrashDeliveryTest.kt

https://github.com/bugsnag/bugsnag-android · Kotlin · 166 lines · 126 code · 21 blank · 19 comment · 0 complexity · 47c107768309c9ae0afbed19e2800a29 MD5 · raw file

  1. package com.bugsnag.android
  2. import com.bugsnag.android.BugsnagTestUtils.generateConfiguration
  3. import com.bugsnag.android.BugsnagTestUtils.generateEvent
  4. import org.junit.After
  5. import org.junit.Assert.assertEquals
  6. import org.junit.Assert.assertTrue
  7. import org.junit.Before
  8. import org.junit.Test
  9. import java.io.File
  10. import java.lang.Thread
  11. import java.nio.file.Files
  12. import java.util.concurrent.atomic.AtomicInteger
  13. /**
  14. * Verifies that crashes on launch are sent synchronously and other crashes are not.
  15. */
  16. class LaunchCrashDeliveryTest {
  17. private lateinit var storageDir: File
  18. private lateinit var errorDir: File
  19. @Before
  20. fun setUp() {
  21. storageDir = Files.createTempDirectory("tmp").toFile()
  22. errorDir = File(storageDir, "bugsnag-errors")
  23. }
  24. @After
  25. fun tearDown() {
  26. storageDir.deleteRecursively()
  27. }
  28. /**
  29. * Verifies that flushOnLaunch() blocks the main thread by simulating a slow delivery.
  30. */
  31. @Test
  32. fun flushOnLaunchBlocksMainThread() {
  33. val delivery = SlowDelivery()
  34. val backgroundTaskService = BackgroundTaskService()
  35. val eventStore = createEventStore(delivery, backgroundTaskService)
  36. val event = generateEvent()
  37. event.app.isLaunching = true
  38. eventStore.write(event)
  39. // check time difference in ms is >1500, proving thread was blocked
  40. val baseline = System.currentTimeMillis()
  41. eventStore.flushOnLaunch()
  42. val now = System.currentTimeMillis()
  43. assertTrue(now - baseline > 1500)
  44. backgroundTaskService.shutdown()
  45. assertEquals(1, delivery.count.get())
  46. }
  47. /**
  48. * Crashes on launch are delivered synchronously
  49. */
  50. @Test
  51. fun flushOnLaunchSync() {
  52. val delivery = TestDelivery()
  53. val backgroundTaskService = BackgroundTaskService()
  54. val eventStore = createEventStore(delivery, backgroundTaskService)
  55. // launch crashes are delivered in flushOnLaunch()
  56. val event = generateEvent()
  57. event.app.isLaunching = true
  58. eventStore.write(event)
  59. eventStore.flushOnLaunch()
  60. assertEquals(1, delivery.count.get())
  61. assertEquals("Bugsnag Error thread", delivery.threadName)
  62. // non-launch crashes are not delivered in flushOnLaunch()
  63. event.app.isLaunching = false
  64. eventStore.write(event)
  65. eventStore.flushOnLaunch()
  66. assertEquals(1, delivery.count.get())
  67. // non-launch crashes are delivered in flushAsync() instead
  68. eventStore.flushAsync()
  69. backgroundTaskService.shutdown()
  70. assertEquals(2, delivery.count.get())
  71. assertEquals("Bugsnag Error thread", delivery.threadName)
  72. }
  73. /**
  74. * Only the most recent crash report is sent by flushOnLaunch()
  75. */
  76. @Test
  77. fun flushOnLaunchSendsMostRecent() {
  78. val delivery = TestDelivery()
  79. val backgroundTaskService = BackgroundTaskService()
  80. val eventStore = createEventStore(delivery, backgroundTaskService)
  81. // launch crashes are delivered in flushOnLaunch()
  82. val event = generateEvent()
  83. event.app.isLaunching = true
  84. event.apiKey = "First"
  85. eventStore.write(event)
  86. event.apiKey = "Second"
  87. eventStore.write(event)
  88. // only the first event should be sent
  89. eventStore.flushOnLaunch()
  90. assertEquals(1, delivery.count.get())
  91. val payload = requireNotNull(delivery.payload)
  92. val filenameInfo = EventFilenameInfo.fromFile(
  93. requireNotNull(payload.eventFile),
  94. BugsnagTestUtils.generateImmutableConfig()
  95. )
  96. assertEquals("Second", filenameInfo.apiKey)
  97. }
  98. private class TestDelivery : Delivery {
  99. var threadName: String? = null
  100. val count = AtomicInteger(0)
  101. var payload: EventPayload? = null
  102. override fun deliver(payload: Session, deliveryParams: DeliveryParams) =
  103. DeliveryStatus.DELIVERED
  104. override fun deliver(
  105. payload: EventPayload,
  106. deliveryParams: DeliveryParams
  107. ): DeliveryStatus {
  108. // capture thread on which executor was running
  109. threadName = Thread.currentThread().name
  110. count.getAndIncrement()
  111. this.payload = payload
  112. return DeliveryStatus.DELIVERED
  113. }
  114. }
  115. private class SlowDelivery : Delivery {
  116. val count = AtomicInteger(0)
  117. override fun deliver(payload: Session, deliveryParams: DeliveryParams) =
  118. DeliveryStatus.DELIVERED
  119. override fun deliver(
  120. payload: EventPayload,
  121. deliveryParams: DeliveryParams
  122. ): DeliveryStatus {
  123. Thread.sleep(2000)
  124. count.getAndIncrement()
  125. return DeliveryStatus.DELIVERED
  126. }
  127. }
  128. private fun createEventStore(
  129. testDelivery: Delivery,
  130. backgroundTaskService: BackgroundTaskService
  131. ): EventStore {
  132. val config = generateConfiguration().apply {
  133. persistenceDirectory = storageDir
  134. this.delivery = testDelivery
  135. }
  136. return EventStore(
  137. BugsnagTestUtils.convert(config),
  138. NoopLogger,
  139. Notifier(),
  140. backgroundTaskService,
  141. FileStore.Delegate { _, _, _ -> },
  142. CallbackState()
  143. )
  144. }
  145. }