@@ -22,6 +22,7 @@ import kotlin.test.Test
2222import kotlin.test.assertEquals
2323import kotlin.test.assertFalse
2424import kotlin.test.assertNotNull
25+ import kotlin.test.assertNull
2526import kotlin.test.assertTrue
2627
2728class SentryTest {
@@ -241,6 +242,108 @@ class SentryTest {
241242 assertFalse(hub is NoOpHub )
242243 }
243244
245+ @Test
246+ fun `main hub can be cloned and does not share scope with current hub` () {
247+ // noop as not yet initialized, caches NoOpHub in ThreadLocal
248+ Sentry .addBreadcrumb(" breadcrumbNoOp" )
249+ Sentry .captureMessage(" messageNoOp" )
250+
251+ assertTrue(Sentry .getCurrentHub() is NoOpHub )
252+
253+ val capturedEvents = mutableListOf<SentryEvent >()
254+
255+ // init Sentry in another thread
256+ val thread = Thread () {
257+ Sentry .init {
258+ it.dsn = dsn
259+ it.isDebug = true
260+ it.beforeSend = SentryOptions .BeforeSendCallback { event, hint ->
261+ capturedEvents.add(event)
262+ event
263+ }
264+ }
265+ }
266+ thread.start()
267+ thread.join()
268+
269+ Sentry .addBreadcrumb(" breadcrumbCurrent" )
270+
271+ val hub = Sentry .getCurrentHub()
272+ assertNotNull(hub)
273+ assertFalse(hub is NoOpHub )
274+
275+ val newMainHubClone = Sentry .cloneMainHub()
276+ newMainHubClone.addBreadcrumb(" breadcrumbMainClone" )
277+
278+ hub.captureMessage(" messageCurrent" )
279+ newMainHubClone.captureMessage(" messageMainClone" )
280+
281+ assertEquals(2 , capturedEvents.size)
282+ val mainCloneEvent = capturedEvents.firstOrNull { it.message?.formatted == " messageMainClone" }
283+ val currentHubEvent = capturedEvents.firstOrNull { it.message?.formatted == " messageCurrent" }
284+
285+ assertNotNull(mainCloneEvent)
286+ assertNotNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == " breadcrumbMainClone" })
287+ assertNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == " breadcrumbCurrent" })
288+ assertNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == " breadcrumbNoOp" })
289+
290+ assertNotNull(currentHubEvent)
291+ assertNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == " breadcrumbMainClone" })
292+ assertNotNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == " breadcrumbCurrent" })
293+ assertNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == " breadcrumbNoOp" })
294+ }
295+
296+ @Test
297+ fun `main hub is not cloned in global hub mode and shares scope with current hub` () {
298+ // noop as not yet initialized, caches NoOpHub in ThreadLocal
299+ Sentry .addBreadcrumb(" breadcrumbNoOp" )
300+ Sentry .captureMessage(" messageNoOp" )
301+
302+ assertTrue(Sentry .getCurrentHub() is NoOpHub )
303+
304+ val capturedEvents = mutableListOf<SentryEvent >()
305+
306+ // init Sentry in another thread
307+ val thread = Thread () {
308+ Sentry .init ({
309+ it.dsn = dsn
310+ it.isDebug = true
311+ it.beforeSend = SentryOptions .BeforeSendCallback { event, hint ->
312+ capturedEvents.add(event)
313+ event
314+ }
315+ }, true )
316+ }
317+ thread.start()
318+ thread.join()
319+
320+ Sentry .addBreadcrumb(" breadcrumbCurrent" )
321+
322+ val hub = Sentry .getCurrentHub()
323+ assertNotNull(hub)
324+ assertFalse(hub is NoOpHub )
325+
326+ val newMainHubClone = Sentry .cloneMainHub()
327+ newMainHubClone.addBreadcrumb(" breadcrumbMainClone" )
328+
329+ hub.captureMessage(" messageCurrent" )
330+ newMainHubClone.captureMessage(" messageMainClone" )
331+
332+ assertEquals(2 , capturedEvents.size)
333+ val mainCloneEvent = capturedEvents.firstOrNull { it.message?.formatted == " messageMainClone" }
334+ val currentHubEvent = capturedEvents.firstOrNull { it.message?.formatted == " messageCurrent" }
335+
336+ assertNotNull(mainCloneEvent)
337+ assertNotNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == " breadcrumbMainClone" })
338+ assertNotNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == " breadcrumbCurrent" })
339+ assertNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == " breadcrumbNoOp" })
340+
341+ assertNotNull(currentHubEvent)
342+ assertNotNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == " breadcrumbMainClone" })
343+ assertNotNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == " breadcrumbCurrent" })
344+ assertNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == " breadcrumbNoOp" })
345+ }
346+
244347 @Test
245348 fun `when init is called and configure throws an exception then an error is logged` () {
246349 val logger = mock<ILogger >()
0 commit comments