@@ -19,6 +19,8 @@ import org.mockito.kotlin.whenever
1919import java.io.ByteArrayOutputStream
2020import java.io.PrintStream
2121import java.nio.file.Files
22+ import java.util.concurrent.CompletableFuture
23+ import java.util.concurrent.Executors
2224import kotlin.concurrent.thread
2325import kotlin.test.Test
2426import kotlin.test.assertEquals
@@ -313,7 +315,7 @@ class UncaughtExceptionHandlerIntegrationTest {
313315 val integration2 = UncaughtExceptionHandlerIntegration (handler)
314316 integration2.register(fixture.scopes, fixture.options)
315317
316- assertEquals(currentDefaultHandler, integration2 )
318+ assertEquals(integration2, currentDefaultHandler )
317319 integration2.close()
318320
319321 assertEquals(null , currentDefaultHandler)
@@ -344,4 +346,125 @@ class UncaughtExceptionHandlerIntegrationTest {
344346
345347 assertEquals(initialUncaughtExceptionHandler, currentDefaultHandler)
346348 }
349+
350+ @Test
351+ fun `multiple registrations with different global scopes allowed` () {
352+ val scopes2 = mock<IScopes >()
353+ val initialUncaughtExceptionHandler = Thread .UncaughtExceptionHandler { _, _ -> }
354+
355+ var currentDefaultHandler: Thread .UncaughtExceptionHandler ? = initialUncaughtExceptionHandler
356+
357+ val handler = mock<UncaughtExceptionHandler >()
358+ whenever(handler.defaultUncaughtExceptionHandler).thenAnswer { currentDefaultHandler }
359+
360+ whenever(handler.setDefaultUncaughtExceptionHandler(anyOrNull<Thread .UncaughtExceptionHandler >())).then {
361+ currentDefaultHandler = it.getArgument(0 )
362+ null
363+ }
364+
365+ whenever(scopes2.globalScope).thenReturn(mock<IScope >())
366+
367+ val integration1 = UncaughtExceptionHandlerIntegration (handler)
368+ integration1.register(fixture.scopes, fixture.options)
369+
370+ val integration2 = UncaughtExceptionHandlerIntegration (handler)
371+ integration2.register(scopes2, fixture.options)
372+
373+ assertEquals(currentDefaultHandler, integration2)
374+ integration2.close()
375+
376+ assertEquals(integration1, currentDefaultHandler)
377+ integration1.close()
378+
379+ assertEquals(initialUncaughtExceptionHandler, currentDefaultHandler)
380+ }
381+
382+ @Test
383+ fun `multiple registrations with different global scopes allowed, closed out of order` () {
384+ fixture.getSut()
385+ val scopes2 = mock<IScopes >()
386+ val initialUncaughtExceptionHandler = Thread .UncaughtExceptionHandler { _, _ -> }
387+
388+ var currentDefaultHandler: Thread .UncaughtExceptionHandler ? = initialUncaughtExceptionHandler
389+
390+ val handler = mock<UncaughtExceptionHandler >()
391+ whenever(handler.defaultUncaughtExceptionHandler).thenAnswer { currentDefaultHandler }
392+
393+ whenever(handler.setDefaultUncaughtExceptionHandler(anyOrNull<Thread .UncaughtExceptionHandler >())).then {
394+ currentDefaultHandler = it.getArgument(0 )
395+ null
396+ }
397+
398+ whenever(scopes2.globalScope).thenReturn(mock<IScope >())
399+
400+ val integration1 = UncaughtExceptionHandlerIntegration (handler)
401+ integration1.register(fixture.scopes, fixture.options)
402+
403+ val integration2 = UncaughtExceptionHandlerIntegration (handler)
404+ integration2.register(scopes2, fixture.options)
405+
406+ assertEquals(currentDefaultHandler, integration2)
407+ integration1.close()
408+
409+ assertEquals(integration2, currentDefaultHandler)
410+ integration2.close()
411+
412+ assertEquals(initialUncaughtExceptionHandler, currentDefaultHandler)
413+ }
414+
415+ @Test
416+ fun `multiple registrations async, closed async, one remains` () {
417+ val executor = Executors .newFixedThreadPool(4 )
418+ fixture.getSut()
419+ val scopes2 = mock<IScopes >()
420+ val scopes3 = mock<IScopes >()
421+ val scopes4 = mock<IScopes >()
422+ val scopes5 = mock<IScopes >()
423+
424+ val scopesList = listOf (fixture.scopes, scopes2, scopes3, scopes4, scopes5)
425+
426+ val initialUncaughtExceptionHandler = Thread .UncaughtExceptionHandler { _, _ -> }
427+
428+ var currentDefaultHandler: Thread .UncaughtExceptionHandler ? = initialUncaughtExceptionHandler
429+
430+ val handler = mock<UncaughtExceptionHandler >()
431+ whenever(handler.defaultUncaughtExceptionHandler).thenAnswer { currentDefaultHandler }
432+
433+ whenever(handler.setDefaultUncaughtExceptionHandler(anyOrNull<Thread .UncaughtExceptionHandler >())).then {
434+ currentDefaultHandler = it.getArgument(0 )
435+ null
436+ }
437+
438+ whenever(scopes2.globalScope).thenReturn(mock<IScope >())
439+ whenever(scopes3.globalScope).thenReturn(mock<IScope >())
440+ whenever(scopes4.globalScope).thenReturn(mock<IScope >())
441+ whenever(scopes5.globalScope).thenReturn(mock<IScope >())
442+
443+ val integrations = scopesList.map { scope ->
444+ CompletableFuture .supplyAsync(
445+ {
446+ UncaughtExceptionHandlerIntegration (handler).apply {
447+ register(scope, fixture.options)
448+ }
449+ },
450+ executor
451+ )
452+ }
453+
454+ CompletableFuture .allOf(* integrations.toTypedArray()).get()
455+
456+ val futures = integrations.minus(integrations[2 ]).reversed().map { integration ->
457+ CompletableFuture .supplyAsync(
458+ {
459+ integration.get().close()
460+ println (Thread .currentThread().name)
461+ },
462+ executor
463+ )
464+ }
465+
466+ CompletableFuture .allOf(* futures.toTypedArray()).get()
467+
468+ assertEquals(integrations[2 ].get(), currentDefaultHandler)
469+ }
347470}
0 commit comments