### Eclipse Workspace Patch 1.0 #P maven-plugin Index: src/main/java/hudson/maven/MavenModuleSetBuild.java =================================================================== --- src/main/java/hudson/maven/MavenModuleSetBuild.java (revision 34055) +++ src/main/java/hudson/maven/MavenModuleSetBuild.java (working copy) @@ -57,6 +57,8 @@ import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.text.html.HTMLDocument.HTMLReader.IsindexAction; + import static hudson.model.Result.FAILURE; /** @@ -209,7 +211,65 @@ return r; } + + /** + * Returns the estimated duration for this builds. + * Takes only the modules into account which are actually being build in + * case of incremental builds. + */ + @Override + public long getEstimatedDuration() { + + if (!project.isIncrementalBuild()) { + return super.getEstimatedDuration(); + } + long result = 0; + + Map> moduleBuilds = getModuleBuilds(); + for (List builds : moduleBuilds.values()) { + if (!builds.isEmpty()) { + MavenBuild build = builds.get(0); + if (build.getResult() != Result.NOT_BUILT) { + if (build.getEstimatedDuration() != -1) { + result += build.getEstimatedDuration(); + } + } + } + } + + result += estimateModuleSetBuildDurationOverhead(3); + + return result != 0 ? result : -1; + } + + /** + * Estimates the duration overhead the {@link MavenModuleSetBuild} itself adds + * to the sum of duration of the module builds. + */ + private long estimateModuleSetBuildDurationOverhead(int numberOfBuilds) { + List moduleSetBuilds = getPreviousBuildsOverThreshold(numberOfBuilds, Result.UNSTABLE); + + if (moduleSetBuilds.isEmpty()) { + return 0; + } + + long overhead = 0; + for(MavenModuleSetBuild moduleSetBuild : moduleSetBuilds) { + long sumOfModuleBuilds = 0; + for (List builds : moduleSetBuild.getModuleBuilds().values()) { + if (!builds.isEmpty()) { + MavenBuild moduleBuild = builds.get(0); + sumOfModuleBuilds += moduleBuild.getDuration(); + } + } + + overhead += Math.max(0, moduleSetBuild.getDuration() - sumOfModuleBuilds); + } + + return Math.round((double)overhead / moduleSetBuilds.size()); + } + @Override public synchronized void delete() throws IOException { super.delete(); #P hudson-core Index: src/main/java/hudson/model/Executor.java =================================================================== --- src/main/java/hudson/model/Executor.java (revision 34172) +++ src/main/java/hudson/model/Executor.java (working copy) @@ -245,7 +245,7 @@ public int getProgress() { Queue.Executable e = executable; if(e==null) return -1; - long d = e.getParent().getEstimatedDuration(); + long d = e.getEstimatedDuration(); if(d<0) return -1; int num = (int)(getElapsedTime()*100/d); @@ -266,7 +266,7 @@ if(e==null) return false; long elapsed = getElapsedTime(); - long d = e.getParent().getEstimatedDuration(); + long d = e.getEstimatedDuration(); if(d>=0) { // if it's taking 10 times longer than ETA, consider it stuck return d*10 < elapsed; @@ -298,7 +298,7 @@ Queue.Executable e = executable; if(e==null) return Messages.Executor_NotAvailable(); - long d = e.getParent().getEstimatedDuration(); + long d = e.getEstimatedDuration(); if(d<0) return Messages.Executor_NotAvailable(); long eta = d-getElapsedTime(); @@ -315,7 +315,7 @@ Queue.Executable e = executable; if(e==null) return -1; - long d = e.getParent().getEstimatedDuration(); + long d = e.getEstimatedDuration(); if(d<0) return -1; long eta = d-getElapsedTime(); @@ -355,7 +355,7 @@ if (isIdle()) return Math.max(finishTime, owner.getConnectTime()); else { - return Math.max(startTime + Math.max(0, executable.getParent().getEstimatedDuration()), + return Math.max(startTime + Math.max(0, executable.getEstimatedDuration()), System.currentTimeMillis() + 15000); } } Index: src/main/java/hudson/model/Queue.java =================================================================== --- src/main/java/hudson/model/Queue.java (revision 34172) +++ src/main/java/hudson/model/Queue.java (working copy) @@ -1131,6 +1131,15 @@ * Called by {@link Executor} to perform the task */ void run(); + + /** + * Estimate of how long will it take to execute this executable. + * Measured in milliseconds. + * + * @return -1 if it's impossible to estimate. + * @since 1.375 + */ + long getEstimatedDuration(); /** * Used to render the HTML. Should be a human readable text of what this executable is. Index: src/main/java/hudson/model/Run.java =================================================================== --- src/main/java/hudson/model/Run.java (revision 34172) +++ src/main/java/hudson/model/Run.java (working copy) @@ -681,6 +681,29 @@ r=r.previousBuild; return r; } + + /** + * Returns the last 'numberOfBuilds' builds with a build result >= 'threshold' + * + * @return a list with the builds (youngest build first). + * May be smaller than 'numberOfBuilds' or even empty + * if not enough builds satisfying the threshold have been found. Never null. + * @since 1.375 + */ + public List getPreviousBuildsOverThreshold(int numberOfBuilds, Result threshold) { + List result = new ArrayList(numberOfBuilds); + + RunT r = getPreviousBuild(); + while (r != null && result.size() < numberOfBuilds) { + if (!r.isBuilding() && + (r.getResult() != null && r.getResult().isBetterOrEqualTo(threshold))) { + result.add(r); + } + r = r.getPreviousBuild(); + } + + return result; + } public RunT getNextBuild() { return nextBuild; @@ -1723,6 +1746,17 @@ return job.getBuildByNumber(number); } + /** + * Returns the estimated duration for this run if it is currently running. + * Default to {@link Job#getEstimatedDuration()}, may be overridden in subclasses + * if duration may depend on run specific parameters (like incremental Maven builds). + * + * @return the estimated duration in milliseconds + * @since 1.375 + */ + public long getEstimatedDuration() { + return project.getEstimatedDuration(); + } public static final XStream XSTREAM = new XStream2(); static { #P hudson-test-harness Index: src/test/java/hudson/maven/MavenMultiModuleTest.java =================================================================== --- src/test/java/hudson/maven/MavenMultiModuleTest.java (revision 34058) +++ src/test/java/hudson/maven/MavenMultiModuleTest.java (working copy) @@ -71,7 +71,34 @@ assertTrue("duration of moduleset build should be greater-equal than sum of the module builds", pBuild.getDuration() >= summedModuleDuration); } + + @Bug(6544) + public void testEstimatedDurationForIncrementalMultiModMaven() + throws Exception { + configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); + MavenModuleSet m = createMavenProject(); + m.getReporters().add(new TestReporter()); + m.setScm(new ExtractResourceWithChangesSCM(getClass().getResource( + "maven-multimod.zip"), getClass().getResource( + "maven-multimod-changes.zip"))); + buildAndAssertSuccess(m); + + // Now run a second, incremental build with the changes. + m.setIncrementalBuild(true); + buildAndAssertSuccess(m); + + MavenModuleSetBuild lastBuild = m.getLastBuild(); + MavenModuleSetBuild previousBuild = lastBuild.getPreviousBuild(); + assertNull("There should be only one previous build", previousBuild.getPreviousBuild()); + + // since the estimated duration is calculated based on the previous builds + // and there was only one previous build (which built all modules) and this build + // did only build one module, the estimated duration of this build must be + // smaller than the duration of the previous build. + assertTrue(lastBuild.getEstimatedDuration() < previousBuild.getDuration()); + } + /** * NPE in {@code getChangeSetFor(m)} in {@link MavenModuleSetBuild} when incremental build is * enabled and a new module is added.