|
16 | 16 |
|
17 | 17 | package com.vaadin.flow.component; |
18 | 18 |
|
| 19 | +import java.lang.reflect.Field; |
19 | 20 | import java.util.ArrayList; |
20 | 21 | import java.util.Arrays; |
21 | 22 | import java.util.List; |
@@ -617,6 +618,32 @@ public void constructedRegistration_lifecycleOnwerHasInvisibleParent_shorcutEven |
617 | 618 | Assert.assertNull(event.get()); |
618 | 619 | } |
619 | 620 |
|
| 621 | + @Test |
| 622 | + public void constructedRegistration_lifecycleOwnerNulledBeforeKeyEvent_noNPE() |
| 623 | + throws Exception { |
| 624 | + AtomicReference<ShortcutEvent> event = new AtomicReference<>(); |
| 625 | + |
| 626 | + ShortcutRegistration registration = new ShortcutRegistration( |
| 627 | + lifecycleOwner, () -> listenOn, event::set, Key.KEY_A); |
| 628 | + |
| 629 | + mockLifecycle(true); |
| 630 | + clientResponse(); |
| 631 | + |
| 632 | + // Simulate the race condition: null out lifecycleOwner while |
| 633 | + // the KeyDown listener is still registered |
| 634 | + Field field = ShortcutRegistration.class |
| 635 | + .getDeclaredField("lifecycleOwner"); |
| 636 | + field.setAccessible(true); |
| 637 | + field.set(registration, null); |
| 638 | + |
| 639 | + // Fire KeyDown event — should not throw NPE |
| 640 | + listenOn[0].getEventBus() |
| 641 | + .fireEvent(new KeyDownEvent(listenOn[0], Key.KEY_A.toString())); |
| 642 | + |
| 643 | + // Shortcut event should not have been fired |
| 644 | + Assert.assertNull(event.get()); |
| 645 | + } |
| 646 | + |
620 | 647 | @Test |
621 | 648 | public void constructedRegistration_lifeCycleOwnerIsDetached_detachListenerIsDeregisteredFromListenOnComponents() { |
622 | 649 | AtomicReference<ComponentEventListener> detachListener = new AtomicReference<>(); |
|
0 commit comments