Wait for all build jobs to finish before running unit tests
Brought to you by:
raner
There is an intermittent problem with launching JUnit tests when build jobs are still running. This causes a NullPointerException in the DebugUIPlugin because of no workbench window being active. Setting waitForBuilds=false can be used as a work-around in some cases but is not always an option because this work-around causes build steps to happen asynchronously/simultaneously, which is not always desirable.
Alternatively to setting waitForBuilds=false the NullPointerExceptions can also be avoided by explicitly waiting for all jobs to complete before JUnit tests are launched.
This was fixed in revision 35.
It's actually a bug, because the functionally was there in the first place. It just didn't get activated for individual project builds (only for complete workspace builds).
Apparently, this bug is still present in one form or another:
[exec] [ebuilder] Ebuilder 1.0.0.r0048
[exec] [ebuilder] Build script: build.com.intuit.interview.iedit.ebuild
[exec] [ebuilder] waitForBuild=always
[exec] [ebuilder] Importing workspace from c:\users\hudson\workspace\iEdit\src
...
[exec] java.lang.NullPointerException
[exec] at org.eclipse.debug.internal.ui.DebugUIPlugin.launchInBackground(DebugUIPlugin.java:1160)
[exec] at org.eclipse.debug.ui.DebugUITools.launch(DebugUITools.java:551)
[exec] at org.eclipse.jdt.junit.launcher.JUnitLaunchShortcut.performLaunch(JUnitLaunchShortcut.java:223)
[exec] at org.eclipse.jdt.junit.launcher.JUnitLaunchShortcut.launch(JUnitLaunchShortcut.java:187)
[exec] at org.eclipse.jdt.junit.launcher.JUnitLaunchShortcut.launch(JUnitLaunchShortcut.java:149)
[exec] at net.sf.ebuilder.Ebuilder$3.launch(Ebuilder.java:432)
[exec] at net.sf.ebuilder.utilities.SynchronizedLaunch.<init>(SynchronizedLaunch.java:74)
[exec] at net.sf.ebuilder.utilities.SynchronizedLaunch.<init>(SynchronizedLaunch.java:51)
[exec] at net.sf.ebuilder.Ebuilder$3.<init>(Ebuilder.java:427)
[exec] at net.sf.ebuilder.Ebuilder.runAllUnitTests(Ebuilder.java:427)
[exec] at net.sf.ebuilder.Ebuilder.build(Ebuilder.java:199)
[exec] at net.sf.ebuilder.Ebuilder.earlyStartup(Ebuilder.java:133)
[exec] at org.eclipse.ui.internal.EarlyStartupRunnable.runEarlyStartup(EarlyStartupRunnable.java:87)
[exec] at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:66)
[exec] at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
[exec] at org.eclipse.ui.internal.Workbench$59.run(Workbench.java:2409)
[exec] at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)
From DebugUIPlugin.java, rev. 1.309:
1062 public static void launchInBackground(final ILaunchConfiguration configuration, final String mode) {
1063 final IJobManager jobManager = Job.getJobManager();
1064 IPreferenceStore store = DebugUIPlugin.getDefault().getPreferenceStore();
1065 boolean wait = (jobManager.find(ResourcesPlugin.FAMILY_AUTO_BUILD).length > 0) || (jobManager.find(ResourcesPlugin.FAMILY_MANUAL_BUILD).length > 0);
1066 String waitPref = store.getString(IInternalDebugUIConstants.PREF_WAIT_FOR_BUILD);
1067 if (wait) { // if there are build jobs running, do we wait or not??
1068 if (waitPref.equals(MessageDialogWithToggle.PROMPT)) {
1069 MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoCancelQuestion(getShell(), DebugUIMessages.DebugUIPlugin_23, DebugUIMessages.DebugUIPlugin_24, null, false, store, IInternalDebugUIConstants.PREF_WAIT_FOR_BUILD); //
1070 switch (dialog.getReturnCode()) {
1071 case IDialogConstants.CANCEL_ID:
1072 return;
1073 case IDialogConstants.YES_ID:
1074 wait = true;
1075 break;
1076 case IDialogConstants.NO_ID:
1077 wait = false;
1078 break;
1079 }
1080 }
1081 else {
1082 wait = waitPref.equals(MessageDialogWithToggle.ALWAYS);
1083 }
1084 }
1085 final boolean waitInJob = wait;
1086 Job job = new Job(MessageFormat.format(DebugUIMessages.DebugUIPlugin_25, new Object[] {configuration.getName()})) {
1087 public IStatus run(final IProgressMonitor monitor) {
1088 /* Setup progress monitor
1089 * - Waiting for jobs to finish (2)
1090 * - Build & launch (98) */
1091 monitor.beginTask(DebugUIMessages.DebugUITools_3, 100);
1092 try {
1093 if(waitInJob) {
1094 StringBuffer buffer = new StringBuffer(configuration.getName());
1095 buffer.append(DebugUIMessages.DebugUIPlugin_0);
1096 ILaunchConfigurationWorkingCopy workingCopy = configuration.copy(buffer.toString());
1097 workingCopy.setAttribute(ATTR_LAUNCHING_CONFIG_HANDLE, configuration.getMemento());
1098 final ILaunch pendingLaunch = new PendingLaunch(workingCopy, mode, this);
1099 DebugPlugin.getDefault().getLaunchManager().addLaunch(pendingLaunch);
1100 IJobChangeListener listener= new IJobChangeListener() {
1101 public void sleeping(IJobChangeEvent event) {}
1102 public void scheduled(IJobChangeEvent event) {}
1103 public void running(IJobChangeEvent event) {}
1104 public void awake(IJobChangeEvent event) {}
1105 public void aboutToRun(IJobChangeEvent event) {}
1106 public void done(IJobChangeEvent event) {
1107 DebugPlugin dp = DebugPlugin.getDefault();
1108 if (dp != null) {
1109 dp.getLaunchManager().removeLaunch(pendingLaunch);
1110 }
1111 removeJobChangeListener(this);
1112 }
1113 };
1114 addJobChangeListener(listener);
1115 try {
1116 jobManager.join(ResourcesPlugin.FAMILY_MANUAL_BUILD, new SubProgressMonitor(monitor, 1));
1117 jobManager.join(ResourcesPlugin.FAMILY_AUTO_BUILD, new SubProgressMonitor(monitor, 1));
1118 }
1119 catch (InterruptedException e) {/*just continue.*/}
1120 DebugPlugin.getDefault().getLaunchManager().removeLaunch(pendingLaunch);
1121 }
1122 else {
1123 monitor.worked(2); /* don't wait for jobs to finish */
1124 }
1125 if (!monitor.isCanceled()) {
1126 buildAndLaunch(configuration, mode, new SubProgressMonitor(monitor, 98));
1127 }
1128 } catch (CoreException e) {
1129 final IStatus status = e.getStatus();
1130 IStatusHandler handler = DebugPlugin.getDefault().getStatusHandler(status);
1131 if (handler == null) {
1132 return status;
1133 }
1134 final ILaunchGroup group = DebugUITools.getLaunchGroup(configuration, mode);
1135 if (group == null) {
1136 return status;
1137 }
1138 Runnable r = new Runnable() {
1139 public void run() {
1140 DebugUITools.openLaunchConfigurationDialogOnGroup(DebugUIPlugin.getShell(), new StructuredSelection(configuration), group.getIdentifier(), status);
1141 }
1142 };
1143 DebugUIPlugin.getStandardDisplay().asyncExec(r);
1144 }
1145 finally {
1146 monitor.done();
1147 }
1148
1149 return Status.OK_STATUS;
1150 }
1151 };
1152
1153 IWorkbench workbench = DebugUIPlugin.getDefault().getWorkbench();
1154 IProgressService progressService = workbench.getProgressService();
1155
1156 job.setPriority(Job.INTERACTIVE);
1157 job.setName(MessageFormat.format(DebugUIMessages.DebugUIPlugin_25, new Object[] {configuration.getName()}));
1158
1159 if (wait) {
1160 progressService.showInDialog(workbench.getActiveWorkbenchWindow().getShell(), job);
1161 }
1162 job.schedule();
1163 }
The problem may also be related to the "Build (if required) before launching" option, which is turned on by default. IDebugUIConstants.PREF_BUILD_BEFORE_LAUNCH is set to false in Ebuilder.java, line 167 (rev. 51), but it is possible that the setting is no longer in effect during unit test launch.
The interesting thing is that this NPE does not necessarily happen on the first test run after all the build steps were completed. It can happen after several other test runs have completed without any issues. This raises the question which build jobs are actually running in between test runs. Could this be yet another problem with the "Build before launch" setting?
This problem is still present in version 1.0.55.r0077, where it actually happened in the second test launch after the build was completed.