[macOS] NPE in Menu.setVisible(boolean) despite the menu showing up
Describe the bug
I think this is closely related to #922 and #923.
We are running our integration tests with the latest RedDeer against the latest Eclipse release. At first, I thought it might be an issue with RedDeer itself, but a closer look reveals it seems to be the underlying SWT that is used, and that is the core of the issue here. When running tests from the IDE that invoke the UI for some context menu options (more I couldn't test yet) the test will fail with a stack trace similar to this one:
org.eclipse.reddeer.common.exception.RedDeerException: Exception during sync execution in UI thread
at org.eclipse.reddeer.common.util.Display.handleErrorOccured(Display.java:112)
at org.eclipse.reddeer.common.util.Display.syncExec(Display.java:84)
at org.eclipse.reddeer.core.lookup.MenuLookup.getToolItemMenu(MenuLookup.java:98)
at org.eclipse.reddeer.swt.impl.menu.ToolItemMenuItem.<init>(ToolItemMenuItem.java:50)
at org.eclipse.reddeer.swt.impl.menu.ToolItemMenuItem.<init>(ToolItemMenuItem.java:38)
at org.sonarlint.eclipse.its.reddeer.views.SonarLintConsole.enableVerboseOutput(SonarLintConsole.java:59)
at org.sonarlint.eclipse.its.AbstractSonarLintTest.beforeClass(AbstractSonarLintTest.java:241)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.eclipse.reddeer.junit.internal.runner.statement.RunBeforeClasses.evaluate(RunBeforeClasses.java:68)
at org.eclipse.reddeer.junit.internal.runner.statement.FulfillRequirementsStatement.evaluate(FulfillRequirementsStatement.java:46)
at org.eclipse.reddeer.junit.internal.runner.statement.RunIBeforeClassExtensions.evaluate(RunIBeforeClassExtensions.java:72)
at org.eclipse.reddeer.junit.internal.runner.statement.RunAfterClasses.evaluate(RunAfterClasses.java:68)
at org.eclipse.reddeer.junit.internal.runner.statement.CleanUpRequirementStatement.evaluate(CleanUpRequirementStatement.java:45)
at org.eclipse.reddeer.junit.internal.runner.statement.RunIAfterClassExtensions.evaluate(RunIAfterClassExtensions.java:57)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:54)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:54)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.eclipse.reddeer.junit.internal.runner.RequirementsRunner.run(RequirementsRunner.java:153)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:93)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:529)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:757)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452)
at org.eclipse.reddeer.jdt.junit.RemotePluginTestRunner.main(RemotePluginTestRunner.java:71)
at org.eclipse.reddeer.jdt.junit.UITestApplication.runTests(UITestApplication.java:133)
at org.eclipse.e4.ui.internal.workbench.swt.E4Testable.lambda$0(E4Testable.java:79)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NullPointerException: Cannot invoke "org.eclipse.swt.widgets.Menu.setVisible(boolean)" because the return value of "org.eclipse.reddeer.core.lookup.MenuLookup$ShowMenuListener.getMenu()" is null
at org.eclipse.reddeer.core.lookup.MenuLookup$1.run(MenuLookup.java:101)
at org.eclipse.reddeer.core.lookup.MenuLookup$1.run(MenuLookup.java:1)
at org.eclipse.reddeer.common.util.Display$ErrorHandlingRunnable.run(Display.java:162)
at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:40)
at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:132)
at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:4365)
at org.eclipse.swt.widgets.Display.observerProc(Display.java:3901)
at org.eclipse.swt.internal.cocoa.OS.objc_msgSendSuper(Native Method)
at org.eclipse.swt.widgets.Display.applicationNextEventMatchingMask(Display.java:5561)
at org.eclipse.swt.widgets.Display.applicationProc(Display.java:5962)
at org.eclipse.swt.internal.cocoa.OS.objc_msgSend(Native Method)
at org.eclipse.swt.internal.cocoa.NSMenu.popUpContextMenu(NSMenu.java:80)
at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:273)
at org.eclipse.swt.widgets.Display.runPopups(Display.java:4487)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3979)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1151)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1042)
at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:152)
at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:639)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:546)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:152)
at org.eclipse.reddeer.jdt.junit.UITestApplication.start(UITestApplication.java:69)
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:208)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:143)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:109)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:439)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:271)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:651)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:588)
at org.eclipse.equinox.launcher.Main.run(Main.java:1459)
at org.eclipse.equinox.launcher.Main.main(Main.java:1432)
java.lang.NullPointerException: Cannot invoke "org.eclipse.swt.widgets.Menu.setVisible(boolean)" because the return value of "org.eclipse.reddeer.core.lookup.MenuLookup$ShowMenuListener.getMenu()" is null
at org.eclipse.reddeer.core.lookup.MenuLookup$1.run(MenuLookup.java:101)
at org.eclipse.reddeer.core.lookup.MenuLookup$1.run(MenuLookup.java:1)
at org.eclipse.reddeer.common.util.Display$ErrorHandlingRunnable.run(Display.java:162)
at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:40)
at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:132)
at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:4365)
at org.eclipse.swt.widgets.Display.observerProc(Display.java:3901)
at org.eclipse.swt.internal.cocoa.OS.objc_msgSendSuper(Native Method)
at org.eclipse.swt.widgets.Display.applicationNextEventMatchingMask(Display.java:5561)
at org.eclipse.swt.widgets.Display.applicationProc(Display.java:5962)
at org.eclipse.swt.internal.cocoa.OS.objc_msgSend(Native Method)
at org.eclipse.swt.internal.cocoa.NSMenu.popUpContextMenu(NSMenu.java:80)
at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:273)
at org.eclipse.swt.widgets.Display.runPopups(Display.java:4487)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3979)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1151)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1042)
at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:152)
at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:639)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:546)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:152)
at org.eclipse.reddeer.jdt.junit.UITestApplication.start(UITestApplication.java:69)
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:208)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:143)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:109)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:439)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:271)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:651)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:588)
at org.eclipse.equinox.launcher.Main.run(Main.java:1459)
at org.eclipse.equinox.launcher.Main.main(Main.java:1432)
HERE is the class with the method that is failing.
The error message indicates that there is an NPE and that the menu couldn't be found. While watching the test run I see that the menu is there and stays there even after the test instance of Eclipse is killed and until I manually click somewhere else. My guess is that the OS is not fast enough to "open" the context menu and the SWT code in question is already trying to access it without even waiting if it is slow starting up.
To Reproduce
In order to reproduce it, the user must have macOS Sonoma installed:
- clone the repository at tag 10.0.1.81733
- once cloned, run
mvn clean verify -DskipTestsin the repository main directory - importing the Maven projects into Eclipse RCP (with all sub-modules, including the one in the
itssub-folder) with RedDeer installed - set the
target-platforms/dev.targetas an active target platform - inside
SonarQubeConnectedModeTestreplace the following lines (sorry, a bit tedious)
@ClassRule
public static final OrchestratorRule orchestrator = OrchestratorRule.builderEnv()
.defaultForceAuthentication()
.useDefaultAdminCredentialsForBuilds(true)
.keepBundledPlugins()
.setEdition(Edition.DEVELOPER)
.activateLicense()
.setSonarVersion(System.getProperty("sonar.runtimeVersion", "LATEST_RELEASE"))
// Ensure SSE are processed correctly just after SQ startup
.setServerProperty("sonar.pushevents.polling.initial.delay", "2")
.setServerProperty("sonar.pushevents.polling.period", "1")
.setServerProperty("sonar.pushevents.polling.last.timestamp", "1")
.build();
with
@ClassRule
public static final OrchestratorRule orchestrator = OrchestratorRule.builderEnv()
.defaultForceAuthentication()
.useDefaultAdminCredentialsForBuilds(true)
.keepBundledPlugins()
.setEdition(Edition.COMMUNITY)
.setOrchestratorProperty("orchestrator.artifactory.url", "https://repo1.maven.org/maven2/")
.setSonarVersion(System.getProperty("sonar.runtimeVersion", "LATEST_RELEASE"))
// Ensure SSE are processed correctly just after SQ startup
.setServerProperty("sonar.pushevents.polling.initial.delay", "2")
.setServerProperty("sonar.pushevents.polling.period", "1")
.setServerProperty("sonar.pushevents.polling.last.timestamp", "1")
.build();
- Right-click on the
check_groupingtest and Run As -> RedDeer Test - The test should start up, taking some time for SQ to initialize, and then the test should fail, Eclipse test instance will be killed and the menu will still be visible (see the screenshot below for reference) and the stack trace should appear
If I have some time and you require it for testing on your side, I try to create a simple reproducer project, I just haven't done that yet.
Expected behavior
The same behavior as for macOS Ventura, with no errors when running the test. Strictly speaking, SWT should take into account if the context menu is a bit slower starting up (what I guess is happening here).
Screenshots
This is the context menu, that is (despite what the error message says) shown but with a small delay and should, therefore, be found by SWT. It stays open after the Eclipse test instance is killed:
Environment:
- Select the platform(s) on which the behavior is seen:
-
- [ ] All OS
-
- [ ] Windows
-
- [ ] Linux
-
- [x] macOS
- Additional OS info (e.g. OS version, Linux Desktop, etc)
The issue started after updating to the newest macOS Sonoma (specifically 14.4.1) from macOS Ventura 13.6.6 this Monday.
- JRE/JDK version
Eclipse RCP 2024-03 is fully updated, running on the current JustJ plug-in bundled with the installation:
- JustJ JRE for IDE Packages
- 17.0.0.v20240120-1430
- org.eclipse.justj.epp.feature.group
Workaround (or) Additional context
EDIT: Tried to workaround this by using RedDeer, but it didn't work out.
Maybe by using RedDeer *WaitConditions, I could work around this. But we have a lot of different UI interactions, and it is just not feasible to implement it for every UI interaction, not only because it is an overhead to implement but because it will slow down everything eventually.
I would only do so in order to check if it only related to the context menu options or other UI interactions as well.
The issue is independent of RedDeer / SWTBot; I've created a reproducer project that shows it failing on macOS Sonoma.
Hello, I can also reproduce this on MacOS Sequoia 15.3.1
Would you be able to provide a small snippet to help reproduce the problem?
@elsazac See the comment above for the reproducer project. I created it a while back, it might not work with RedDeer anymore after it was archived, just change the dev.target to point to another mirror of Eclipse RedDeer. We have one internally, that I'm using but cannot share.