From 55ad833fd397a33db99c1dac39d9112ca6fa84cb Mon Sep 17 00:00:00 2001 From: Tibor Digana Date: Tue, 2 Jul 2013 00:12:35 +0200 Subject: [PATCH 01/11] PC --- .../pc/AbstractThreadPoolStrategy.java | 111 ++++++ .../maven/surefire/junitcore/pc/Balancer.java | 98 +++++ .../junitcore/pc/InvokerStrategy.java | 61 +++ .../pc/NonSharedThreadPoolStrategy.java | 54 +++ .../junitcore/pc/ParallelComputerBuilder.java | 350 ++++++++++++++++++ .../surefire/junitcore/pc/Scheduler.java | 335 +++++++++++++++++ .../junitcore/pc/SchedulingStrategies.java | 73 ++++ .../junitcore/pc/SchedulingStrategy.java | 105 ++++++ .../pc/SharedThreadPoolStrategy.java | 84 +++++ 9 files changed, 1271 insertions(+) create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/AbstractThreadPoolStrategy.java create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/InvokerStrategy.java create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NonSharedThreadPoolStrategy.java create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategies.java create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategy.java create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SharedThreadPoolStrategy.java diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/AbstractThreadPoolStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/AbstractThreadPoolStrategy.java new file mode 100644 index 0000000000..fea07013ab --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/AbstractThreadPoolStrategy.java @@ -0,0 +1,111 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.Collection; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Abstract parallel scheduling strategy in private package. + * The remaining abstract methods have to be implemented differently + * depending if the thread pool is shared with other strategies or not. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + * + * @see SchedulingStrategy + * @see SharedThreadPoolStrategy + * @see NonSharedThreadPoolStrategy + */ +abstract class AbstractThreadPoolStrategy extends SchedulingStrategy { + private final ExecutorService threadPool; + private final Collection> futureResults; + private final AtomicBoolean canSchedule = new AtomicBoolean(true); + + AbstractThreadPoolStrategy(ExecutorService threadPool) { + this(threadPool, null); + } + + AbstractThreadPoolStrategy(ExecutorService threadPool, Collection> futureResults) { + this.threadPool = threadPool; + this.futureResults = futureResults; + } + + protected final ExecutorService getThreadPool() { + return threadPool; + } + + protected final Collection> getFutureResults() { + return futureResults; + } + + protected final void disable() { + canSchedule.set(false); + } + + @Override + public void schedule(Runnable task) { + if (canSchedule()) { + Future futureResult = threadPool.submit(task); + if (futureResults != null) { + futureResults.add(futureResult); + } + } + } + + @Override + protected boolean stop() { + boolean wasRunning = canSchedule.getAndSet(false); + if (threadPool.isShutdown()) { + wasRunning = false; + } else { + threadPool.shutdown(); + } + return wasRunning; + } + + @Override + protected boolean stopNow() { + boolean wasRunning = canSchedule.getAndSet(false); + if (threadPool.isShutdown()) { + wasRunning = false; + } else { + threadPool.shutdownNow(); + } + return wasRunning; + } + + @Override + protected void setDefaultShutdownHandler(Scheduler.ShutdownHandler handler) { + if (threadPool instanceof ThreadPoolExecutor) { + ThreadPoolExecutor pool = (ThreadPoolExecutor) threadPool; + handler.setRejectedExecutionHandler(pool.getRejectedExecutionHandler()); + pool.setRejectedExecutionHandler(handler); + } + } + + @Override + public final boolean canSchedule() { + return canSchedule.get(); + } +} \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java new file mode 100644 index 0000000000..59eed26c53 --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java @@ -0,0 +1,98 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.concurrent.Semaphore; + +/** + * The Balancer controls the maximum of concurrent threads in the current Scheduler(s) and prevents + * from own thread resources exhaustion if other group of schedulers share the same pool of threads. + *

+ * If a permit is available, {@link #acquirePermit()} simply returns and a new test is scheduled + * by {@link Scheduler#schedule(Runnable)} in the current runner. Otherwise waiting for a release. + * One permit is released as soon as the child thread has finished. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class Balancer { + private final Semaphore balancer; + private final int maxPermits; + + /** + * Infinite permits. + */ + protected Balancer() { + this(0); + } + + /** + * fair set to false. + * @see #Balancer(int, boolean) + */ + public Balancer(int numPermits) { + this(numPermits, false); + } + + /** + * @param numPermits number of permits to acquire when maintaining concurrency on tests + * @param fair true guarantees the waiting schedulers to wake up in order they acquired a permit + */ + public Balancer(int numPermits, boolean fair) { + boolean maintainTests = numPermits > 0 && numPermits < Integer.MAX_VALUE; + balancer = maintainTests ? new Semaphore(numPermits, fair) : null; + maxPermits = numPermits; + } + + /** + * Acquires a permit from this balancer, blocking until one is available. + * + * @return true if current thread is NOT interrupted + * while waiting for a permit. + */ + public boolean acquirePermit() { + Semaphore balancer = this.balancer; + if (balancer != null) { + try { + balancer.acquire(); + } catch (InterruptedException e) { + return false; + } + } + return true; + } + + /** + * Releases a permit, returning it to the balancer. + */ + public void releasePermit() { + Semaphore balancer = this.balancer; + if (balancer != null) { + balancer.release(); + } + } + + public void releaseAllPermits() { + Semaphore balancer = this.balancer; + if (balancer != null) { + balancer.release(maxPermits); + } + } +} diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/InvokerStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/InvokerStrategy.java new file mode 100644 index 0000000000..dc1e5b3fe4 --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/InvokerStrategy.java @@ -0,0 +1,61 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * The sequentially executing strategy in private package. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + * + * @see SchedulingStrategy + */ +final class InvokerStrategy extends SchedulingStrategy { + private final AtomicBoolean canSchedule = new AtomicBoolean(true); + + @Override + public void schedule(Runnable task) { + if (canSchedule()) { + task.run(); + } + } + + @Override + protected boolean stop() { + return canSchedule.getAndSet(false); + } + + @Override + public boolean hasSharedThreadPool() { + return false; + } + + @Override + public boolean canSchedule() { + return canSchedule.get(); + } + + @Override + public boolean finished() throws InterruptedException { + return stop(); + } +} diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NonSharedThreadPoolStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NonSharedThreadPoolStrategy.java new file mode 100644 index 0000000000..b463605f84 --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NonSharedThreadPoolStrategy.java @@ -0,0 +1,54 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Parallel strategy for non-shared thread pool in private package. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + * + * @see AbstractThreadPoolStrategy + */ +final class NonSharedThreadPoolStrategy extends AbstractThreadPoolStrategy { + NonSharedThreadPoolStrategy(ExecutorService threadPool) { + super(threadPool); + } + + @Override + public boolean hasSharedThreadPool() { + return false; + } + + @Override + public boolean finished() throws InterruptedException { + boolean wasRunning = canSchedule(); + getThreadPool().shutdown(); + try { + getThreadPool().awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + return wasRunning; + } finally { + disable(); + } + } +} diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java new file mode 100644 index 0000000000..67a6d0c0bc --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java @@ -0,0 +1,350 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.internal.runners.ErrorReportingRunner; +import org.junit.runner.Computer; +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runners.ParentRunner; +import org.junit.runners.Suite; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Executing suites, classes and methods with defined concurrency. In this example the threads which completed + * the suites and classes can be reused in parallel methods. + *

+ * ParallelComputerBuilder builder = new ParallelComputerBuilder();
+ * builder.useOnePool(8).parallelSuites(2).parallelClasses(4).parallelMethods();
+ * ParallelComputerBuilder.ParallelComputer computer = builder.buildComputer();
+ * Class[] tests = {...};
+ * new JUnitCore().run(computer, tests);
+ * 
+ * Note that the type has always at least one thread even if unspecified. The capacity in + * {@link ParallelComputerBuilder#useOnePool(int)} must be greater than the number of concurrent suites and classes altogether. + *

+ * The Computer can be shutdown in a separate thread. Pending tests will be interrupted if the argument is true. + *

+ * computer.shutdown(true);
+ * 
+ * + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class ParallelComputerBuilder { + private static enum Type { + SUITES, CLASSES, METHODS + } + + static final int TOTAL_POOL_SIZE_UNDEFINED = 0; + private final Map parallelGroups = new HashMap(3); + private boolean useSeparatePools; + private int totalPoolSize; + + /** + * Calling {@link #useSeparatePools()}. + */ + public ParallelComputerBuilder() { + useSeparatePools(); + parallelGroups.put(Type.SUITES, 0); + parallelGroups.put(Type.CLASSES, 0); + parallelGroups.put(Type.METHODS, 0); + } + + public ParallelComputerBuilder useSeparatePools() { + totalPoolSize = TOTAL_POOL_SIZE_UNDEFINED; + useSeparatePools = true; + return this; + } + + public ParallelComputerBuilder useOnePool() { + totalPoolSize = TOTAL_POOL_SIZE_UNDEFINED; + useSeparatePools = false; + return this; + } + + /** + * @param totalPoolSize Pool size where suites, classes and methods are executed in parallel. + * @throws IllegalArgumentException If totalPoolSize is < 1. + */ + public ParallelComputerBuilder useOnePool(int totalPoolSize) { + if (totalPoolSize < 1) { + throw new IllegalArgumentException("Size of common pool is less than 1."); + } + this.totalPoolSize = totalPoolSize; + useSeparatePools = false; + return this; + } + + public ParallelComputerBuilder parallelSuites() { + return parallel(Type.SUITES); + } + + public ParallelComputerBuilder parallelSuites(int nThreads) { + return parallel(nThreads, Type.SUITES); + } + + public ParallelComputerBuilder parallelClasses() { + return parallel(Type.CLASSES); + } + + public ParallelComputerBuilder parallelClasses(int nThreads) { + return parallel(nThreads, Type.CLASSES); + } + + public ParallelComputerBuilder parallelMethods() { + return parallel(Type.METHODS); + } + + public ParallelComputerBuilder parallelMethods(int nThreads) { + return parallel(nThreads, Type.METHODS); + } + + private ParallelComputerBuilder parallel(int nThreads, Type parallelType) { + if (nThreads < 0) { + throw new IllegalArgumentException("negative nThreads " + nThreads); + } + + if (parallelType == null) { + throw new NullPointerException("null parallelType"); + } + + parallelGroups.put(parallelType, nThreads); + return this; + } + + private ParallelComputerBuilder parallel(Type parallelType) { + return parallel(Integer.MAX_VALUE, parallelType); + } + + public ParallelComputer buildComputer() { + return new ParallelComputer(); + } + + private static class RunnersSuite extends Suite { + protected RunnersSuite(Collection runners) throws InitializationError { + super(null, new ArrayList(runners)); + } + + protected RunnersSuite(Runner... runners) throws InitializationError { + super(null, Arrays.asList(runners)); + } + } + + public final class ParallelComputer extends Computer { + final Collection suites = new ArrayList(); + final Collection nestedSuites = new ArrayList(); + final Collection classes = new ArrayList(); + final Collection nestedClasses = new ArrayList(); + final int poolCapacity; + final boolean splitPool; + private final Map allGroups; + private volatile Scheduler master; + + private ParallelComputer() { + allGroups = new HashMap(ParallelComputerBuilder.this.parallelGroups); + poolCapacity = ParallelComputerBuilder.this.totalPoolSize; + splitPool = ParallelComputerBuilder.this.useSeparatePools; + } + + public Collection shutdown(boolean shutdownNow) { + final Scheduler master = this.master; + return master == null ? Collections.emptyList() : master.shutdown(shutdownNow); + } + + @Override + public Runner getSuite(RunnerBuilder builder, Class[] cls) throws InitializationError { + super.getSuite(builder, cls); + populateChildrenFromSuites(); + return setSchedulers(); + } + + @Override + protected Runner getRunner(RunnerBuilder builder, Class testClass) throws Throwable { + Runner runner = super.getRunner(builder, testClass); + if (canUse(runner)) { + if (runner instanceof Suite) { + suites.add((Suite) runner); + } else { + classes.add((ParentRunner) runner); + } + } + return runner; + } + + private class SuiteFilter extends Filter { + @Override + public boolean shouldRun(Description description) { + return true; + } + + @Override + public void apply(Object child) throws NoTestsRemainException { + super.apply(child); + if (child instanceof Suite) { + nestedSuites.add((Suite) child); + } else if (child instanceof ParentRunner) { + nestedClasses.add((ParentRunner) child); + } + } + + @Override + public String describe() { + return ""; + } + } + + private ExecutorService createPool(int poolSize) { + return poolSize < Integer.MAX_VALUE ? Executors.newFixedThreadPool(poolSize) : Executors.newCachedThreadPool(); + } + + private Scheduler createMaster(ExecutorService pool, int poolSize) { + if (!areSuitesAndClassesParallel() || poolSize <= 1) { + return new Scheduler(null, new InvokerStrategy()); + } else if (pool != null && poolSize == Integer.MAX_VALUE) { + return new Scheduler(null, new SharedThreadPoolStrategy(pool)); + } else { + return new Scheduler(null, SchedulingStrategies.createParallelStrategy(2)); + } + } + + private boolean areSuitesAndClassesParallel() { + return !suites.isEmpty() && allGroups.get(Type.SUITES) > 0 && !classes.isEmpty() && allGroups.get(Type.CLASSES) > 0; + } + + private void populateChildrenFromSuites() { + Filter filter = new SuiteFilter(); + for (Iterator it = suites.iterator(); it.hasNext();) { + Suite suite = it.next(); + try { + suite.filter(filter); + } catch (NoTestsRemainException e) { + it.remove(); + } + } + } + + private int totalPoolSize() { + if (poolCapacity == TOTAL_POOL_SIZE_UNDEFINED) { + int total = 0; + for (int nThreads : allGroups.values()) { + total += nThreads; + if (total < 0) { + total = Integer.MAX_VALUE; + break; + } + } + return total; + } else { + return poolCapacity; + } + } + + private Runner setSchedulers() throws InitializationError { + int parallelSuites = allGroups.get(Type.SUITES); + int parallelClasses = allGroups.get(Type.CLASSES); + int parallelMethods = allGroups.get(Type.METHODS); + int poolSize = totalPoolSize(); + ExecutorService commonPool = splitPool || poolSize == 0 ? null : createPool(poolSize); + master = createMaster(commonPool, poolSize); + + // a scheduler for parallel suites + final Scheduler suitesScheduler; + if (commonPool != null && parallelSuites > 0) { + suitesScheduler = createScheduler(null, commonPool, true, new Balancer(parallelSuites, true)); + } else { + suitesScheduler = createScheduler(parallelSuites); + } + Suite suiteSuites = new RunnersSuite(suites); + suiteSuites.setScheduler(suitesScheduler); + + // schedulers for parallel classes + Suite suiteClasses = new RunnersSuite(classes); + ArrayList allSuites = new ArrayList(suites); + allSuites.addAll(nestedSuites); + allSuites.add(suiteClasses); + setSchedulers(allSuites, parallelClasses, commonPool); + + // schedulers for parallel methods + ArrayList allClasses = new ArrayList(classes); + allClasses.addAll(nestedClasses); + setSchedulers(allClasses, parallelMethods, commonPool); + + // resulting runner for Computer#getSuite() scheduled by master scheduler + Suite all = new RunnersSuite(suiteSuites, suiteClasses); + all.setScheduler(master); + return all; + } + + private void setSchedulers(Iterable runners, int poolSize, ExecutorService commonPool) { + if (commonPool != null) { + Balancer concurrencyLimit = new Balancer(poolSize, true); + boolean doParallel = poolSize > 0; + for (ParentRunner runner : runners) { + runner.setScheduler(createScheduler(runner.getDescription(), commonPool, doParallel, concurrencyLimit)); + } + } else { + ExecutorService pool = null; + if (poolSize == Integer.MAX_VALUE) { + pool = Executors.newCachedThreadPool(); + } else if (poolSize > 0) { + pool = Executors.newFixedThreadPool(poolSize); + } + boolean doParallel = pool != null; + for (ParentRunner runner : runners) { + runner.setScheduler(createScheduler(runner.getDescription(), pool, doParallel, new Balancer())); + } + } + } + + private Scheduler createScheduler(Description desc, ExecutorService pool, boolean doParallel, Balancer concurrency) { + doParallel &= pool != null; + SchedulingStrategy strategy = doParallel ? new SharedThreadPoolStrategy(pool) : new InvokerStrategy(); + return new Scheduler(desc, master, strategy, concurrency); + } + + private Scheduler createScheduler(int poolSize) { + if (poolSize == Integer.MAX_VALUE) { + return new Scheduler(null, master, SchedulingStrategies.createParallelStrategyUnbounded()); + } else if (poolSize == 0) { + return new Scheduler(null, master, new InvokerStrategy()); + } else { + return new Scheduler(null, master, SchedulingStrategies.createParallelStrategy(poolSize)); + } + } + + private boolean canUse(Runner runner) { + return !(runner instanceof ErrorReportingRunner) && runner instanceof ParentRunner; + } + } +} diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java new file mode 100644 index 0000000000..241c71bd97 --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java @@ -0,0 +1,335 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.runner.Description; +import org.junit.runners.model.RunnerScheduler; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * + * Schedules tests, controls thread resources, awaiting tests and other schedulers finished, and + * a master scheduler can shutdown slaves. + *

+ * The scheduler objects should be first created (and wired) and set in runners + * {@link org.junit.runners.ParentRunner#setScheduler(org.junit.runners.model.RunnerScheduler)}. + *

+ * A new instance of scheduling strategy should be passed to the constructor of this scheduler. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class Scheduler implements RunnerScheduler { + private final Balancer balancer; + private final SchedulingStrategy strategy; + private final Set slaves = new CopyOnWriteArraySet(); + private final Description description; + private volatile boolean shutdown = false; + private volatile boolean started = false; + private volatile Controller masterController; + + /** + * Use e.g. parallel classes have own non-shared thread pool, and methods another pool. + *

+ * You can use it with one infinite thread pool shared in strategies across all + * suites, class runners, etc. + */ + public Scheduler(Description description, SchedulingStrategy strategy) { + this(description, strategy, -1); + } + + /** + * Should be used if schedulers in parallel children and parent use one instance of bounded thread pool. + *

+ * Set this scheduler in a e.g. one suite of classes, then every individual class runner should reference + * {@link #Scheduler(org.junit.runner.Description, Scheduler, SchedulingStrategy)} + * or {@link #Scheduler(org.junit.runner.Description, Scheduler, SchedulingStrategy, int)}. + * + * @param description description of current runner + * @param strategy scheduling strategy with a shared thread pool + * @param concurrency determines maximum concurrent children scheduled a time via {@link #schedule(Runnable)} + * @throws NullPointerException if null strategy + */ + public Scheduler(Description description, SchedulingStrategy strategy, int concurrency) { + this(description, strategy, new Balancer(concurrency)); + } + + /** + * New instances should be used by schedulers with limited concurrency by balancer + * against other groups of schedulers. The schedulers share one pool. + *

+ * Unlike in {@link #Scheduler(org.junit.runner.Description, SchedulingStrategy, int)} which was limiting + * the concurrency of children of a runner where this scheduler was set, this balancer + * is limiting the concurrency of all children in runners having schedulers created by this constructor. + * + * @param description description of current runner + * @param strategy scheduling strategy which may share threads with other strategy + * @param balancer determines maximum concurrent children scheduled a time via {@link #schedule(Runnable)} + * @throws NullPointerException if null strategy or balancer + */ + public Scheduler(Description description, SchedulingStrategy strategy, Balancer balancer) { + strategy.setDefaultShutdownHandler(newShutdownHandler()); + this.description = description; + this.strategy = strategy; + this.balancer = balancer; + masterController = null; + } + /** + * Can be used by e.g. a runner having parallel classes in use case with parallel + * suites, classes and methods sharing the same thread pool. + * + * @param description description of current runner + * @param masterScheduler scheduler sharing own threads with this slave + * @param strategy scheduling strategy for this scheduler + * @param balancer determines maximum concurrent children scheduled a time via {@link #schedule(Runnable)} + * @throws NullPointerException if null masterScheduler, strategy or balancer + */ + public Scheduler(Description description, Scheduler masterScheduler, SchedulingStrategy strategy, Balancer balancer) { + this(description, strategy, balancer); + strategy.setDefaultShutdownHandler(newShutdownHandler()); + masterScheduler.register(this); + } + + /** + * @param masterScheduler a reference to {@link #Scheduler(org.junit.runner.Description, SchedulingStrategy, int)} + * or {@link #Scheduler(org.junit.runner.Description, SchedulingStrategy)} + * @see #Scheduler(org.junit.runner.Description, SchedulingStrategy) + * @see #Scheduler(org.junit.runner.Description, SchedulingStrategy, int) + */ + public Scheduler(Description description, Scheduler masterScheduler, SchedulingStrategy strategy, int concurrency) { + this(description, strategy, concurrency); + strategy.setDefaultShutdownHandler(newShutdownHandler()); + masterScheduler.register(this); + } + + /** + * Should be used with individual pools on suites, classes and methods, see + * {@link org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder#useSeparatePools()}. + *

+ * Cached thread pool is infinite and can be always shared. + */ + public Scheduler(Description description, Scheduler masterScheduler, SchedulingStrategy strategy) { + this(description, masterScheduler, strategy, 0); + } + + private void setController(Controller masterController) { + if (masterController == null) { + throw new NullPointerException("null ExecutionController"); + } + this.masterController = masterController; + } + + /** + * @param slave a slave scheduler to register + * @return true if successfully registered the slave. + */ + private boolean register(Scheduler slave) { + boolean canRegister = slave != null && slave != this; + if (canRegister) { + Controller controller = new Controller(slave); + canRegister = !slaves.contains(controller); + if (canRegister) { + slaves.add(controller); + slave.setController(controller); + } + } + return canRegister; + } + + /** + * @return true if new tasks can be scheduled. + */ + private boolean canSchedule() { + return !shutdown && (masterController == null || masterController.canSchedule()); + } + + protected void logQuietly(Throwable t) { + t.printStackTrace(System.err); + } + + protected void logQuietly(String msg) { + System.err.println(msg); + } + + /** + * Attempts to stop all actively executing tasks and immediately returns a collection + * of descriptions of those tasks which have started prior to this call. + *

+ * This scheduler and other registered schedulers will shutdown, see {@link #register(Scheduler)}. + * If shutdownNow is set, waiting methods will cancel via {@link Thread#interrupt}. + * + * @param shutdownNow if true interrupts waiting methods + * @return collection of descriptions started before shutting down + */ + public Collection shutdown(boolean shutdownNow) { + shutdown = true; + ArrayList activeChildren = new ArrayList(); + + if (started && description != null) { + activeChildren.add(description); + } + + for (Controller slave : slaves) { + try { + activeChildren.addAll(slave.shutdown(shutdownNow)); + } catch (Throwable t) { + logQuietly(t); + } + } + + try { + balancer.releaseAllPermits(); + } finally { + if (shutdownNow) { + strategy.stopNow(); + } else { + strategy.stop(); + } + } + + return activeChildren; + } + + protected void beforeExecute() { + } + + protected void afterExecute() { + } + + public void schedule(Runnable childStatement) { + if (childStatement == null) { + logQuietly("cannot schedule null"); + } else if (canSchedule() && strategy.canSchedule()) { + try { + balancer.acquirePermit(); + Runnable task = wrapTask(childStatement); + strategy.schedule(task); + started = true; + } catch (RejectedExecutionException e) { + shutdown(false); + } catch (Throwable t) { + balancer.releasePermit(); + logQuietly(t); + } + } + } + + public void finished() { + try { + strategy.finished(); + } catch (InterruptedException e) { + logQuietly(e); + } finally { + for (Controller slave : slaves) { + slave.awaitFinishedQuietly(); + } + } + } + + private Runnable wrapTask(final Runnable task) { + return new Runnable() { + public void run() { + try { + beforeExecute(); + task.run(); + } finally { + try { + afterExecute(); + } finally { + balancer.releasePermit(); + } + } + } + }; + } + + protected ShutdownHandler newShutdownHandler() { + return new ShutdownHandler(); + } + + /** + * If this is a master scheduler, the slaves can stop scheduling by the master through the controller. + */ + private final class Controller { + private final Scheduler slave; + + private Controller(Scheduler slave) { + this.slave = slave; + } + + /** + * @return true if new children can be scheduled. + */ + boolean canSchedule() { + return Scheduler.this.canSchedule(); + } + + void awaitFinishedQuietly() { + try { + slave.finished(); + } catch(Throwable t) { + slave.logQuietly(t); + } + } + + Collection shutdown(boolean shutdownNow) { + return slave.shutdown(shutdownNow); + } + + @Override + public int hashCode() { + return slave.hashCode(); + } + + @Override + public boolean equals(Object o) { + return o == this || (o instanceof Controller) && slave.equals(((Controller) o).slave); + } + } + + public class ShutdownHandler implements RejectedExecutionHandler { + private volatile RejectedExecutionHandler poolHandler; + + protected ShutdownHandler() { + poolHandler = null; + } + + public void setRejectedExecutionHandler(RejectedExecutionHandler poolHandler) { + this.poolHandler = poolHandler; + } + + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + if (executor.isShutdown()) { + shutdown(false); + } + final RejectedExecutionHandler poolHandler = this.poolHandler; + if (poolHandler != null) { + poolHandler.rejectedExecution(r, executor); + } + } + } +} diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategies.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategies.java new file mode 100644 index 0000000000..4d3c6a6484 --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategies.java @@ -0,0 +1,73 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * The factory of {@link SchedulingStrategy}. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class SchedulingStrategies { + + /** + * @return sequentially executing strategy + */ + public static SchedulingStrategy createInvokerStrategy() { + return new InvokerStrategy(); + } + + /** + * @param nThreads fixed pool capacity + * @return parallel scheduling strategy + */ + public static SchedulingStrategy createParallelStrategy(int nThreads) { + return new NonSharedThreadPoolStrategy(Executors.newFixedThreadPool(nThreads)); + } + + /** + * @return parallel scheduling strategy with unbounded capacity + */ + public static SchedulingStrategy createParallelStrategyUnbounded() { + return new NonSharedThreadPoolStrategy(Executors.newCachedThreadPool()); + } + + /** + * The threadPool passed to this strategy can be shared in other strategies. + *

+ * The call {@link SchedulingStrategy#finished()} is waiting until own tasks have finished. + * New tasks will not be scheduled by this call in this strategy. This strategy is not + * waiting for other strategies to finish. The {@link org.junit.runners.model.RunnerScheduler#finished()} may + * freely use {@link SchedulingStrategy#finished()}. + * + * @param threadPool thread pool possibly shared with other strategies + * @return parallel strategy with shared thread pool + * @throws NullPointerException if threadPool is null + */ + public static SchedulingStrategy createParallelSharedStrategy(ExecutorService threadPool) { + if (threadPool == null) { + throw new NullPointerException("null threadPool in #createParallelSharedStrategy"); + } + return new SharedThreadPoolStrategy(threadPool); + } +} diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategy.java new file mode 100644 index 0000000000..c9da764b68 --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategy.java @@ -0,0 +1,105 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.runners.model.RunnerScheduler; + +import java.util.concurrent.RejectedExecutionException; + +/** + * Specifies the strategy of scheduling whether sequential, or parallel. + * The strategy may use a thread pool shared with other strategies. + *

+ * One instance of strategy can be used just by one {@link Scheduler}. + *

+ * The strategy is scheduling tasks in {@link #schedule(Runnable)} and awaiting them + * completed in {@link #finished()}. Both methods should be used in one thread. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public abstract class SchedulingStrategy { + + /** + * Schedules tasks if {@link #canSchedule()}. + * + * @param task runnable to schedule in a thread pool or invoke + * @throws RejectedExecutionException if task + * cannot be scheduled for execution + * @throws NullPointerException if task is null + * @see RunnerScheduler#schedule(Runnable) + * @see java.util.concurrent.Executor#execute(Runnable) + */ + protected abstract void schedule(Runnable task); + + /** + * Waiting for scheduled tasks to finish. + * New tasks will not be scheduled by calling this method. + * + * @return true if successfully stopped the scheduler, else + * false if already stopped (a shared thread + * pool was shutdown externally). + * @throws InterruptedException if interrupted while waiting + * for scheduled tasks to finish + * @see RunnerScheduler#finished() + */ + protected abstract boolean finished() throws InterruptedException; + + /** + * Stops scheduling new tasks (e.g. by {@link java.util.concurrent.ExecutorService#shutdown()} + * on a private thread pool which cannot be shared with other strategy). + * + * @return true if successfully stopped the scheduler, else + * false if already stopped (a shared thread + * pool was shutdown externally). + * @see java.util.concurrent.ExecutorService#shutdown() + */ + protected abstract boolean stop(); + + /** + * Stops scheduling new tasks and interrupts running tasks + * (e.g. by {@link java.util.concurrent.ExecutorService#shutdownNow()} on a private thread pool + * which cannot be shared with other strategy). + *

+ * This method calls {@link #stop()} by default. + * + * @return true if successfully stopped the scheduler, else + * false if already stopped (a shared thread + * pool was shutdown externally). + * @see java.util.concurrent.ExecutorService#shutdownNow() + */ + protected boolean stopNow() { + return stop(); + } + + protected void setDefaultShutdownHandler(Scheduler.ShutdownHandler handler) { + } + + /** + * @return true if a thread pool associated with this strategy + * can be shared with other strategies. + */ + protected abstract boolean hasSharedThreadPool(); + + /** + * @return true unless stopped or finished. + */ + protected abstract boolean canSchedule(); +} diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SharedThreadPoolStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SharedThreadPoolStrategy.java new file mode 100644 index 0000000000..53082c424e --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SharedThreadPoolStrategy.java @@ -0,0 +1,84 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +/** + * Parallel strategy for shared thread pool in private package. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + * + * @see AbstractThreadPoolStrategy + */ +final class SharedThreadPoolStrategy extends AbstractThreadPoolStrategy { + SharedThreadPoolStrategy(ExecutorService threadPool) { + super(threadPool, new ConcurrentLinkedQueue>()); + } + + @Override + public boolean hasSharedThreadPool() { + return true; + } + + @Override + public boolean finished() throws InterruptedException { + boolean wasRunningAll = canSchedule(); + for (Future futureResult : getFutureResults()) { + try { + futureResult.get(); + } catch (InterruptedException e) { + // after called external ExecutorService#shutdownNow() + // or ExecutorService#shutdown() + wasRunningAll = false; + } catch (ExecutionException e) { + // test throws exception + } catch (CancellationException e) { + // cannot happen because not calling Future#cancel() + } + } + disable(); + return wasRunningAll; + } + + @Override + protected final boolean stop() { + return stop(false); + } + + @Override + protected final boolean stopNow() { + return stop(true); + } + + private boolean stop(boolean interrupt) { + final boolean wasRunning = canSchedule(); + for (Future futureResult : getFutureResults()) { + futureResult.cancel(interrupt); + } + disable(); + return wasRunning; + } +} \ No newline at end of file From 7aafcf3e6d57e74c3c326fe53555a35ebaa7e9cb Mon Sep 17 00:00:00 2001 From: Tibor Digana Date: Tue, 2 Jul 2013 01:32:42 +0200 Subject: [PATCH 02/11] unit tests of pc --- .../junitcore/pc/ParallelComputerBuilder.java | 9 +- .../pc/ParallelComputerBuilderTest.java | 426 ++++++++++++++++++ .../surefire/junitcore/pc/RangeMatcher.java | 56 +++ .../pc/SchedulingStrategiesTest.java | 155 +++++++ .../surefire/junitcore/pc/Stopwatch.java | 45 ++ 5 files changed, 687 insertions(+), 4 deletions(-) create mode 100644 surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java create mode 100644 surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/RangeMatcher.java create mode 100644 surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategiesTest.java create mode 100644 surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/Stopwatch.java diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java index 67a6d0c0bc..7223690892 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java @@ -36,6 +36,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -162,10 +163,10 @@ protected RunnersSuite(Runner... runners) throws InitializationError { } public final class ParallelComputer extends Computer { - final Collection suites = new ArrayList(); - final Collection nestedSuites = new ArrayList(); - final Collection classes = new ArrayList(); - final Collection nestedClasses = new ArrayList(); + final Collection suites = new LinkedHashSet(); + final Collection nestedSuites = new LinkedHashSet(); + final Collection classes = new LinkedHashSet(); + final Collection nestedClasses = new LinkedHashSet(); final int poolCapacity; final boolean splitPool; private final Map allGroups; diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java new file mode 100644 index 0000000000..f6a280f5a5 --- /dev/null +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java @@ -0,0 +1,426 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.ConcurrentLinkedQueue; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.hamcrest.core.AnyOf.anyOf; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.apache.maven.surefire.junitcore.pc.RangeMatcher.between; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class ParallelComputerBuilderTest { + @Rule + public final Stopwatch runtime = new Stopwatch(); + + @Before + public void beforeTest() { + Class1.maxConcurrentMethods = 0; + Class1.concurrentMethods = 0; + shutdownTask = null; + } + + @Test + public void parallelMethodsReuseOneOrTwoThreads() { + ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); + parallelComputerBuilder.useOnePool(4); + + // One thread because one suite: TestSuite, however the capacity is 5. + parallelComputerBuilder.parallelSuites(5); + + // Two threads because TestSuite has two classes, however the capacity is 5. + parallelComputerBuilder.parallelClasses(5); + + // One or two threads because one threads comes from '#useOnePool(4)' + // and next thread may be reused from finished class, however the capacity is 3. + parallelComputerBuilder.parallelMethods(3); + + ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + Result result = new JUnitCore().run(computer, TestSuite.class); + long timeSpent = runtime.stop(); + + assertThat(computer.suites.size(), is(1)); + assertThat(computer.classes.size(), is(0)); + assertThat(computer.nestedClasses.size(), is(2)); + assertThat(computer.nestedSuites.size(), is(0)); + assertFalse(computer.splitPool); + assertThat(computer.poolCapacity, is(4)); + assertTrue(result.wasSuccessful()); + if (Class1.maxConcurrentMethods == 1) { + assertThat(timeSpent, between(1950, 2250)); + } else if (Class1.maxConcurrentMethods == 2) { + assertThat(timeSpent, between(1450, 1750)); + } else { + fail(); + } + } + + @Test + public void suiteAndClassInOnePool() { + ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); + parallelComputerBuilder.useOnePool(5); + parallelComputerBuilder.parallelSuites(5); + parallelComputerBuilder.parallelClasses(5); + parallelComputerBuilder.parallelMethods(3); + + ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class); + long timeSpent = runtime.stop(); + + assertThat(computer.suites.size(), is(1)); + assertThat(computer.classes.size(), is(1)); + assertThat(computer.nestedClasses.size(), is(2)); + assertThat(computer.nestedSuites.size(), is(0)); + assertFalse(computer.splitPool); + assertThat(computer.poolCapacity, is(5)); + assertTrue(result.wasSuccessful()); + assertThat(Class1.maxConcurrentMethods, is(2)); + assertThat(timeSpent, anyOf(between(1450, 1750), between(1950, 2250), between(2450, 2750))); + } + + @Test + public void onePoolWithUnlimitedParallelMethods() { + // see ParallelComputerBuilder Javadoc + ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); + parallelComputerBuilder.useOnePool(8); + parallelComputerBuilder.parallelSuites(2); + parallelComputerBuilder.parallelClasses(4); + parallelComputerBuilder.parallelMethods(); + + ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class); + long timeSpent = runtime.stop(); + + assertThat(computer.suites.size(), is(1)); + assertThat(computer.classes.size(), is(1)); + assertThat(computer.nestedClasses.size(), is(2)); + assertThat(computer.nestedSuites.size(), is(0)); + assertFalse(computer.splitPool); + assertThat(computer.poolCapacity, is(8)); + assertTrue(result.wasSuccessful()); + assertThat(Class1.maxConcurrentMethods, is(4)); + assertThat(timeSpent, between(950, 1250)); + } + + @Test + public void underflowParallelism() { + ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); + parallelComputerBuilder.useOnePool(3); + + // One thread because one suite: TestSuite. + parallelComputerBuilder.parallelSuites(5); + + // One thread because of the limitation which is bottleneck. + parallelComputerBuilder.parallelClasses(1); + + // One thread remains from '#useOnePool(3)'. + parallelComputerBuilder.parallelMethods(3); + + ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + Result result = new JUnitCore().run(computer, TestSuite.class); + long timeSpent = runtime.stop(); + + assertThat(computer.suites.size(), is(1)); + assertThat(computer.classes.size(), is(0)); + assertThat(computer.nestedClasses.size(), is(2)); + assertThat(computer.nestedSuites.size(), is(0)); + assertFalse(computer.splitPool); + assertThat(computer.poolCapacity, is(3)); + assertTrue(result.wasSuccessful()); + assertThat(Class1.maxConcurrentMethods, is(1)); + assertThat(timeSpent, between(1950, 2250)); + } + + @Test + public void separatePoolsWithSuite() { + ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); + parallelComputerBuilder.parallelSuites(5); + parallelComputerBuilder.parallelClasses(5); + parallelComputerBuilder.parallelMethods(3); + + ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + Result result = new JUnitCore().run(computer, TestSuite.class); + long timeSpent = runtime.stop(); + + assertThat(computer.suites.size(), is(1)); + assertThat(computer.classes.size(), is(0)); + assertThat(computer.nestedClasses.size(), is(2)); + assertThat(computer.nestedSuites.size(), is(0)); + assertTrue(computer.splitPool); + assertThat(computer.poolCapacity, is(ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED)); + assertTrue(result.wasSuccessful()); + assertThat(Class1.maxConcurrentMethods, is(3)); + assertThat(timeSpent, between(950, 1250)); + } + + @Test + public void separatePoolsWithSuiteAndClass() { + ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); + parallelComputerBuilder.parallelSuites(5); + parallelComputerBuilder.parallelClasses(5); + parallelComputerBuilder.parallelMethods(3); + + // 6 methods altogether. + // 2 groups with 3 threads. + // Each group takes 0.5s. + ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class); + long timeSpent = runtime.stop(); + + assertThat(computer.suites.size(), is(1)); + assertThat(computer.classes.size(), is(1)); + assertThat(computer.nestedClasses.size(), is(2)); + assertThat(computer.nestedSuites.size(), is(0)); + assertTrue(computer.splitPool); + assertThat(computer.poolCapacity, is(ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED)); + assertTrue(result.wasSuccessful()); + assertThat(Class1.maxConcurrentMethods, is(3)); + assertThat(timeSpent, between(950, 1250)); + } + + @Test + public void separatePoolsWithSuiteAndSequentialClasses() { + ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); + parallelComputerBuilder.parallelSuites(5); + parallelComputerBuilder.parallelClasses(1); + parallelComputerBuilder.parallelMethods(3); + + ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class); + long timeSpent = runtime.stop(); + + assertThat(computer.suites.size(), is(1)); + assertThat(computer.classes.size(), is(1)); + assertThat(computer.nestedClasses.size(), is(2)); + assertThat(computer.nestedSuites.size(), is(0)); + assertTrue(computer.splitPool); + assertThat(computer.poolCapacity, is(ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED)); + assertTrue(result.wasSuccessful()); + assertThat(Class1.maxConcurrentMethods, is(2)); + assertThat(timeSpent, between(1450, 1750)); + } + + private static class ShutdownTest { + Result run(final boolean useInterrupt) { + ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder().useOnePool(8); + parallelComputerBuilder.parallelSuites(2); + parallelComputerBuilder.parallelClasses(3); + parallelComputerBuilder.parallelMethods(3); + + final ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + shutdownTask = new Runnable() { + public void run() { + Collection startedTests = computer.shutdown(useInterrupt); + assertThat(startedTests.size(), is(not(0))); + } + }; + return new JUnitCore().run(computer, TestSuite.class, Class2.class, Class3.class); + } + } + + @Test(timeout = 2000) + public void shutdown() { + Result result = new ShutdownTest().run(false); + long timeSpent = runtime.stop(); + assertTrue(result.wasSuccessful()); + assertTrue(beforeShutdown); + assertThat(timeSpent, between(450, 1250)); + } + + @Test(timeout = 2000) + public void shutdownWithInterrupt() { + new ShutdownTest().run(true); + long timeSpent = runtime.stop(); + assertTrue(beforeShutdown); + assertThat(timeSpent, between(450, 1250)); + } + + @Test + public void nothingParallel() { + JUnitCore core = new JUnitCore(); + ParallelComputerBuilder builder = new ParallelComputerBuilder(); + + Result result = core.run(builder.buildComputer(), NothingDoingTest1.class, NothingDoingTest2.class); + assertTrue(result.wasSuccessful()); + + result = core.run(builder.buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class); + assertTrue(result.wasSuccessful()); + + result = core.run(builder.useOnePool(1).buildComputer(), NothingDoingTest1.class, NothingDoingTest2.class); + assertTrue(result.wasSuccessful()); + + result = core.run(builder.useOnePool(1).buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class); + assertTrue(result.wasSuccessful()); + + result = core.run(builder.useOnePool(2).buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class); + assertTrue(result.wasSuccessful()); + + Class[] classes = {NothingDoingTest1.class, NothingDoingSuite.class}; + + result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses(1).buildComputer(), classes); + assertTrue(result.wasSuccessful()); + + result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses().buildComputer(), classes); + assertTrue(result.wasSuccessful()); + } + + private static void testKeepBeforeAfter(ParallelComputerBuilder builder, Class... classes) { + JUnitCore core = new JUnitCore(); + for (int round = 0; round < 5; round++) { + NothingDoingTest1.methods.clear(); + Result result = core.run(builder.buildComputer(), classes); + assertTrue(result.wasSuccessful()); + Iterator methods = NothingDoingTest1.methods.iterator(); + for (Class clazz : classes) { + String a = clazz.getName() + "#a()"; + String b = clazz.getName() + "#b()"; + assertThat(methods.next(), is("init")); + assertThat(methods.next(), anyOf(is(a), is(b))); + assertThat(methods.next(), anyOf(is(a), is(b))); + assertThat(methods.next(), is("deinit")); + } + } + } + + @Test + public void keepBeforeAfterOneClass() { + ParallelComputerBuilder builder = new ParallelComputerBuilder(); + builder.parallelMethods(); + testKeepBeforeAfter(builder, NothingDoingTest1.class); + } + + @Test + public void keepBeforeAfterTwoClasses() { + ParallelComputerBuilder builder = new ParallelComputerBuilder(); + builder.useOnePool(5).parallelClasses(1).parallelMethods(2); + testKeepBeforeAfter(builder, NothingDoingTest1.class, NothingDoingTest2.class); + } + + @Test + public void keepBeforeAfterTwoParallelClasses() { + ParallelComputerBuilder builder = new ParallelComputerBuilder(); + builder.useOnePool(8).parallelClasses(2).parallelMethods(2); + JUnitCore core = new JUnitCore(); + NothingDoingTest1.methods.clear(); + Class[] classes = {NothingDoingTest1.class, NothingDoingTest2.class, NothingDoingTest3.class}; + Result result = core.run(builder.buildComputer(), classes); + assertTrue(result.wasSuccessful()); + ArrayList methods = new ArrayList(NothingDoingTest1.methods); + assertThat(methods.size(), is(12)); + assertThat(methods.subList(9, 12), is(not(Arrays.asList("deinit", "deinit", "deinit")))); + } + + private static volatile boolean beforeShutdown; + private static volatile Runnable shutdownTask; + + public static class Class1 { + static volatile int concurrentMethods = 0; + static volatile int maxConcurrentMethods = 0; + + @Test + public void test1() throws InterruptedException { + synchronized (Class1.class) { + ++concurrentMethods; + Class1.class.wait(500); + maxConcurrentMethods = Math.max(maxConcurrentMethods, concurrentMethods--); + } + } + + @Test + public void test2() throws InterruptedException { + test1(); + Runnable shutdownTask = ParallelComputerBuilderTest.shutdownTask; + if (shutdownTask != null) { + beforeShutdown = true; + shutdownTask.run(); + } + } + } + + public static class Class2 extends Class1 { + } + + public static class Class3 extends Class1 { + } + + @RunWith(Suite.class) + @Suite.SuiteClasses({Class2.class, Class1.class}) + public class TestSuite { + } + + public static class NothingDoingTest1 { + static final Collection methods = new ConcurrentLinkedQueue(); + + @BeforeClass + public static void init() { + methods.add("init"); + } + + @Test + public void a() throws InterruptedException { + Thread.sleep(5); + methods.add(getClass().getName() + "#a()"); + } + + @Test + public void b() throws InterruptedException { + Thread.sleep(5); + methods.add(getClass().getName() + "#b()"); + } + + @AfterClass + public static void deinit() { + methods.add("deinit"); + } + } + + public static class NothingDoingTest2 extends NothingDoingTest1 { + } + + public static class NothingDoingTest3 extends NothingDoingTest1 { + } + + @RunWith(Suite.class) + @Suite.SuiteClasses({NothingDoingTest1.class, NothingDoingTest2.class}) + public static class NothingDoingSuite { + } +} diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/RangeMatcher.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/RangeMatcher.java new file mode 100644 index 0000000000..ad04d6b9db --- /dev/null +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/RangeMatcher.java @@ -0,0 +1,56 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +final class RangeMatcher extends BaseMatcher +{ + private final long from; + private final long to; + + private RangeMatcher( long from, long to ) + { + this.from = from; + this.to = to; + } + + public void describeTo( Description description ) + { + description.appendValueList( "between ", " and ", "", from, to ); + } + + public static Matcher between( long from, long to ) + { + return new RangeMatcher( from, to ); + } + + public boolean matches( Object o ) + { + long actual = (Long) o; + return actual >= from && actual <= to; + } +} \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategiesTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategiesTest.java new file mode 100644 index 0000000000..485b63c3b8 --- /dev/null +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategiesTest.java @@ -0,0 +1,155 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.Test; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Tests the factories in SchedulingStrategy. + * + * Th point of these tests is to check {@link Task#result} if changed + * from false to true after all scheduled tasks + * have finished. + * The call {@link SchedulingStrategy#finished()} is waiting until the + * strategy has finished. + * Then {@link Task#result} should be asserted that is true. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + * + * @see SchedulingStrategy + */ +public class SchedulingStrategiesTest { + static class Task implements Runnable { + volatile boolean result = false; + + public void run() { + result = true; + } + } + + @Test + public void invokerStrategy() throws InterruptedException { + SchedulingStrategy strategy = SchedulingStrategies.createInvokerStrategy(); + assertFalse(strategy.hasSharedThreadPool()); + assertTrue(strategy.canSchedule()); + + Task task = new Task(); + + strategy.schedule(task); + + assertTrue(strategy.canSchedule()); + + assertTrue(task.result); + + assertTrue(strategy.finished()); + assertFalse(strategy.canSchedule()); + } + + @Test + public void nonSharedPoolStrategy() throws InterruptedException { + SchedulingStrategy strategy = SchedulingStrategies.createParallelStrategy(2); + assertFalse(strategy.hasSharedThreadPool()); + assertTrue(strategy.canSchedule()); + + Task task1 = new Task(); + Task task2 = new Task(); + + strategy.schedule(task1); + strategy.schedule(task2); + + assertTrue(strategy.canSchedule()); + + assertTrue(strategy.finished()); + assertFalse(strategy.canSchedule()); + + assertTrue(task1.result); + assertTrue(task2.result); + } + + @Test(expected = NullPointerException.class) + public void sharedPoolStrategyNullPool() { + SchedulingStrategies.createParallelSharedStrategy(null); + } + + @Test + public void sharedPoolStrategy() throws InterruptedException { + ExecutorService sharedPool = Executors.newCachedThreadPool(); + + SchedulingStrategy strategy1 = SchedulingStrategies.createParallelSharedStrategy(sharedPool); + assertTrue(strategy1.hasSharedThreadPool()); + assertTrue(strategy1.canSchedule()); + + SchedulingStrategy strategy2 = SchedulingStrategies.createParallelSharedStrategy(sharedPool); + assertTrue(strategy2.hasSharedThreadPool()); + assertTrue(strategy2.canSchedule()); + + Task task1 = new Task(); + Task task2 = new Task(); + Task task3 = new Task(); + Task task4 = new Task(); + + strategy1.schedule(task1); + strategy2.schedule(task2); + strategy1.schedule(task3); + strategy2.schedule(task4); + + assertTrue(strategy1.canSchedule()); + assertTrue(strategy2.canSchedule()); + + assertTrue(strategy1.finished()); + assertFalse(strategy1.canSchedule()); + + assertTrue(strategy2.finished()); + assertFalse(strategy2.canSchedule()); + + assertTrue(task1.result); + assertTrue(task2.result); + assertTrue(task3.result); + assertTrue(task4.result); + } + + @Test + public void infinitePoolStrategy() throws InterruptedException { + SchedulingStrategy strategy = SchedulingStrategies.createParallelStrategyUnbounded(); + assertFalse(strategy.hasSharedThreadPool()); + assertTrue(strategy.canSchedule()); + + Task task1 = new Task(); + Task task2 = new Task(); + + strategy.schedule(task1); + strategy.schedule(task2); + + assertTrue(strategy.canSchedule()); + + assertTrue(strategy.finished()); + assertFalse(strategy.canSchedule()); + + assertTrue(task1.result); + assertTrue(task2.result); + } +} diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/Stopwatch.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/Stopwatch.java new file mode 100644 index 0000000000..32b056c009 --- /dev/null +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/Stopwatch.java @@ -0,0 +1,45 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.rules.TestWatchman; +import org.junit.runners.model.FrameworkMethod; + +import java.util.concurrent.TimeUnit; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +final class Stopwatch extends TestWatchman +{ + private long startNanos; + + long stop() + { + return TimeUnit.MILLISECONDS.convert(System.nanoTime() - startNanos, TimeUnit.NANOSECONDS); + } + + @Override + public void starting(FrameworkMethod method) + { + startNanos = System.nanoTime(); + } +} From fd4788585c8322f8e683e3c00788fab353799918 Mon Sep 17 00:00:00 2001 From: Tibor Digana Date: Sat, 6 Jul 2013 11:43:43 +0200 Subject: [PATCH 03/11] added tests with sharing threads --- .../junitcore/pc/ParallelComputerBuilderTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java index f6a280f5a5..4b55f888c5 100644 --- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java @@ -301,6 +301,15 @@ public void nothingParallel() { result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses().buildComputer(), classes); assertTrue(result.wasSuccessful()); + + classes = new Class[]{NothingDoingSuite.class, NothingDoingSuite.class, + NothingDoingTest1.class, NothingDoingTest2.class, NothingDoingTest3.class}; + + result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses(1).buildComputer(), classes); + assertTrue(result.wasSuccessful()); + + result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses().buildComputer(), classes); + assertTrue(result.wasSuccessful()); } private static void testKeepBeforeAfter(ParallelComputerBuilder builder, Class... classes) { From cc3bc2c8ad6bbbcd00863b72d23f3f392cc4132e Mon Sep 17 00:00:00 2001 From: Tibor Digana Date: Sat, 6 Jul 2013 11:57:44 +0200 Subject: [PATCH 04/11] thread-count entities and reused threads + fixed tests for threadCount = 0 --- .../plugin/surefire/AbstractSurefireMojo.java | 257 +++++++++++++++++- .../surefire/booter/BaseProviderFactory.java | 2 +- .../booter/ProviderParameterNames.java | 6 + .../surefire/its/Junit47WithCucumberIT.java | 7 +- .../surefire/its/TestSingleMethodIT.java | 6 +- .../its/jiras/Surefire943ReportContentIT.java | 9 +- .../src/test/resources/junit44-dep/pom.xml | 1 + .../src/test/java/junit44Dep/BasicTest.java | 1 + .../junitcore/JUnitCoreParameters.java | 48 +++- .../surefire/junitcore/JUnitCoreProvider.java | 3 +- 10 files changed, 319 insertions(+), 21 deletions(-) diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java index 677920e6c2..b5de7a5bfb 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java @@ -483,13 +483,85 @@ public abstract class AbstractSurefireMojo * respect their order of execution. *

* (JUnit 4.7 provider) Supports values "classes"/"methods"/"both" to run in separate threads, as controlled by - * threadCount. + * threadCount.
+ *
+ * Since version 2.16 (JUnit 4.7 provider), the value "both" is DEPRECATED. + * Use "classesAndMethods" instead.
+ *
+ * Since version 2.16 (JUnit 4.7 provider), additional vales are available + * "suites"/"suitesAndClasses"/"suitesAndMethods"/"classesAndMethods"/"all". * * @since 2.2 */ @Parameter( property = "parallel" ) protected String parallel; + /** + * (JUnit 4.7 provider) The attribute thread-count-suites allows you to specify the concurrency in test suites, i.e.: + *

    + *
  • number of threads executing JUnit test suites if threadCount is 0 or unspecified
  • + *
  • In a special case threadCountSuites and threadCount are specified + * without threadCountMethods for parallel=suitesAndMethods. + *
    Example1: threadCount=8 and threadCountSuites=3, the number of parallel methods is varying from 5 to 7 in your tests. + *
    In another special case when parallel=all and the only threadCountMethods + * is unspecified, then threads from suites and classes are reused in favor of methods. + *
    Example2: parallel=all, threadCount=16 , threadCountSuites=2 , threadCountClasses=5, + * the number of parallel methods is varying from 9 to 14 in your tests. + *
  • + *
  • integer number which represents the weight in ratio between + * threadCountSuites:threadCountClasses:threadCountMethods. + * As an example 2 is 20% of threadCount if the ratio is 2:3:5
  • + *
+ * + * Only makes sense to use in conjunction with the parallel parameter. + * The default value 0 behaves same as unspecified one. + * + * @since 2.16 + */ + @Parameter( property = "threadCountSuites", defaultValue = "0" ) + protected int threadCountSuites; + + /** + * (JUnit 4.7 provider) The attribute thread-count-classes allows you to specify the concurrency in test classes, i.e.: + *
    + *
  • number of threads executing JUnit test classes if threadCount is 0 or unspecified
  • + *
  • In a special case threadCountClasses and threadCount are specified + * without threadCountMethods for parallel=classesAndMethods. + *
    Example1: threadCount=8 and threadCountClasses=3, the number of parallel methods is varying from 5 to 7 in your tests. + *
    In another special case when parallel=all and the only threadCountMethods + * is unspecified, then threads from suites and classes are reused in favor of methods. + *
    Example2: parallel=all, threadCount=16 , threadCountSuites=2 , threadCountClasses=5, + * the number of parallel methods is varying from 9 to 14 in your tests. + *
  • + *
  • integer number which represents the weight in ratio between + * threadCountSuites:threadCountClasses:threadCountMethods. + * As an example 3 is 30% of threadCount if the ratio is 2:3:5
  • + *
+ * + * Only makes sense to use in conjunction with the parallel parameter. + * The default value 0 behaves same as unspecified one. + * + * @since 2.16 + */ + @Parameter( property = "threadCountClasses", defaultValue = "0" ) + protected int threadCountClasses; + + /** + * (JUnit 4.7 provider) The attribute thread-count-methods allows you to specify the concurrency in test methods, i.e.: + *
    + *
  • number of threads executing JUnit test methods if threadCount is 0 or unspecified;
  • + *
  • integer number which represents the weight in ratio between threadCountSuites:threadCountClasses:threadCountMethods. + * As an example 5 is 50% of threadCount if the ratio is 2:3:5.
  • + *
+ * + * Only makes sense to use in conjunction with the parallel parameter. + * The default value 0 behaves same as unspecified one. + * + * @since 2.16 + */ + @Parameter( property = "threadCountMethods", defaultValue = "0" ) + protected int threadCountMethods; + /** * Whether to trim the stack trace in the reports to just the lines within the test, or show the full trace. * @@ -1046,23 +1118,158 @@ protected boolean isAnyGroupsSelected() * Converts old JUnit configuration parameters over to new properties based configuration * method. (if any are defined the old way) */ - private void convertJunitCoreParameters() + private void convertJunitCoreParameters() throws MojoExecutionException { + checkThreadCountEntity( getThreadCountSuites(), "suites" ); + checkThreadCountEntity( getThreadCountClasses(), "classes" ); + checkThreadCountEntity( getThreadCountMethods(), "methods" ); + String usedParallel = ( getParallel() != null ) ? getParallel() : "none"; - String usedThreadCount = ( getThreadCount() > 0 ) ? Integer.toString( getThreadCount() ) : "2"; + if ( !"none".equals( usedParallel )) + { + checkNonForkedThreads( parallel ); + } + + String usedThreadCount = Integer.toString( getThreadCount() ); getProperties().setProperty( ProviderParameterNames.PARALLEL_PROP, usedParallel ); getProperties().setProperty( ProviderParameterNames.THREADCOUNT_PROP, usedThreadCount ); getProperties().setProperty( "perCoreThreadCount", Boolean.toString( getPerCoreThreadCount() ) ); getProperties().setProperty( "useUnlimitedThreads", Boolean.toString( getUseUnlimitedThreads() ) ); + getProperties().setProperty( ProviderParameterNames.THREADCOUNTSUITES_PROP, Integer.toString( getThreadCountSuites() ) ); + getProperties().setProperty( ProviderParameterNames.THREADCOUNTCLASSES_PROP, Integer.toString( getThreadCountClasses() ) ); + getProperties().setProperty( ProviderParameterNames.THREADCOUNTMETHODS_PROP, Integer.toString( getThreadCountMethods() ) ); String message = "parallel='" + usedParallel + '\'' + ", perCoreThreadCount=" + getPerCoreThreadCount() + ", threadCount=" - + usedThreadCount + ", useUnlimitedThreads=" + getUseUnlimitedThreads(); + + usedThreadCount + ", useUnlimitedThreads=" + getUseUnlimitedThreads() + + ", threadCountSuites=" + getThreadCountSuites() + ", threadCountClasses=" + getThreadCountClasses() + + ", threadCountMethods=" + getThreadCountMethods(); getLog().info( message ); } + private void checkNonForkedThreads( String parallel ) throws MojoExecutionException + { + if ( "suites".equals( parallel ) ) + { + if ( !( getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountSuites() > 0 ) ) + { + throw new MojoExecutionException( "Use threadCount or threadCountSuites > 0 or useUnlimitedThreads=true " + + "for parallel='suites'" ); + } + } + else if ( "classes".equals( parallel ) ) + { + if ( !( getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountClasses() > 0 ) ) + { + throw new MojoExecutionException( "Use threadCount or threadCountClasses > 0 or useUnlimitedThreads=true " + + "for parallel='classes'" ); + } + } + else if ( "methods".equals( parallel ) ) + { + if ( !( getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountMethods() > 0 ) ) + { + throw new MojoExecutionException( "Use threadCount or threadCountMethods > 0 or useUnlimitedThreads=true " + + "for parallel='methods'" ); + } + } + else if ( "suitesAndClasses".equals( parallel ) ) + { + if ( !( getUseUnlimitedThreads() + || onlyThreadCount() + || getThreadCountSuites() > 0 && getThreadCountClasses() > 0 + && getThreadCount() == 0 && getThreadCountMethods() == 0 + || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCountClasses() > 0 + && getThreadCountMethods() == 0 + || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCount() > getThreadCountSuites() + && getThreadCountClasses() == 0 && getThreadCountMethods() == 0 ) ) + { + throw new MojoExecutionException( "Use useUnlimitedThreads=true, " + + "or only threadCount > 0, " + + "or (threadCountSuites > 0 and threadCountClasses > 0), " + + "or (threadCount > 0 and threadCountSuites > 0 and threadCountClasses > 0) " + + "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) " + + "for parallel='suitesAndClasses' or 'both'" ); + } + } + else if ( "suitesAndMethods".equals( parallel ) ) + { + if ( !( getUseUnlimitedThreads() + || onlyThreadCount() + || getThreadCountSuites() > 0 && getThreadCountMethods() > 0 + && getThreadCount() == 0 && getThreadCountClasses() == 0 + || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCountMethods() > 0 + && getThreadCountClasses() == 0 + || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCount() > getThreadCountSuites() + && getThreadCountClasses() == 0 && getThreadCountMethods() == 0 ) ) + { + throw new MojoExecutionException( "Use useUnlimitedThreads=true, " + + "or only threadCount > 0, " + + "or (threadCountSuites > 0 and threadCountMethods > 0), " + + "or (threadCount > 0 and threadCountSuites > 0 and threadCountMethods > 0), " + + "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) " + + "for parallel='suitesAndMethods'" ); + } + } + else if ( "both".equals( parallel ) || "classesAndMethods".equals( parallel ) ) + { + if ( !( getUseUnlimitedThreads() + || onlyThreadCount() + || getThreadCountClasses() > 0 && getThreadCountMethods() > 0 + && getThreadCount() == 0 && getThreadCountSuites() == 0 + || getThreadCount() > 0 && getThreadCountClasses() > 0 && getThreadCountMethods() > 0 + && getThreadCountSuites() == 0 + || getThreadCount() > 0 && getThreadCountClasses() > 0 && getThreadCount() > getThreadCountClasses() + && getThreadCountSuites() == 0 && getThreadCountMethods() == 0 ) ) + { + throw new MojoExecutionException( "Use useUnlimitedThreads=true, " + + "or only threadCount > 0, " + + "or (threadCountClasses > 0 and threadCountMethods > 0), " + + "or (threadCount > 0 and threadCountClasses > 0 and threadCountMethods > 0), " + + "or (threadCount > 0 and threadCountClasses > 0 and threadCount > threadCountClasses) " + + "for parallel='both' or parallel='classesAndMethods'" ); + } + } + else if ( "all".equals( parallel ) ) + { + if ( !( getUseUnlimitedThreads() + || onlyThreadCount() + || getThreadCountSuites() > 0 && getThreadCountClasses() > 0 && getThreadCountMethods() > 0 + || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCountClasses() > 0 + && getThreadCountMethods() == 0 + && getThreadCount() > ( getThreadCountSuites() + getThreadCountClasses() ) ) ) + { + throw new MojoExecutionException( "Use useUnlimitedThreads=true, " + + "or only threadCount > 0, " + + "or (threadCountSuites > 0 and threadCountClasses > 0 and threadCountMethods > 0), " + + "or every thread-count is specified, " + + "or (threadCount > 0 and threadCountSuites > 0 and threadCountClasses > 0 " + + "and threadCount > threadCountSuites + threadCountClasses) " + + "for parallel='all'" ); + } + } + else + { + throw new MojoExecutionException( "Illegal parallel='" + parallel + "'" ); + } + } + + private boolean onlyThreadCount() + { + return getThreadCount() > 0 && getThreadCountSuites() == 0 && getThreadCountClasses() == 0 + && getThreadCountMethods() == 0; + } + + private static void checkThreadCountEntity(int count, String entity) throws MojoExecutionException + { + if ( count < 0 ) + { + throw new MojoExecutionException("parallel maven execution does not allow negative thread-count" + entity); + } + } + private boolean isJunit47Compatible( Artifact artifact ) { return dependencyResolver.isWithinVersionSpec( artifact, "[4.7,)" ); @@ -1677,6 +1884,9 @@ private String getConfigChecksum() checksum.add( getJunitArtifact() ); checksum.add( getTestNGArtifactName() ); checksum.add( getThreadCount() ); + checksum.add( getThreadCountSuites() ); + checksum.add( getThreadCountClasses() ); + checksum.add( getThreadCountMethods() ); checksum.add( getPerCoreThreadCount() ); checksum.add( getUseUnlimitedThreads() ); checksum.add( getParallel() ); @@ -2054,7 +2264,7 @@ public boolean isApplicable() return true; } - public void addProviderProperties() + public void addProviderProperties() throws MojoExecutionException { } @@ -2093,7 +2303,7 @@ public boolean isApplicable() return junitDepArtifact != null || isAnyJunit4( junitArtifact ); } - public void addProviderProperties() + public void addProviderProperties() throws MojoExecutionException { } @@ -2137,7 +2347,7 @@ public boolean isApplicable() return isAny47ProvidersForcers && ( isJunitArtifact47 || is47CompatibleJunitDep() ); } - public void addProviderProperties() + public void addProviderProperties() throws MojoExecutionException { convertJunitCoreParameters(); convertGroupParameters(); @@ -2494,6 +2704,39 @@ public void setParallel( String parallel ) this.parallel = parallel; } + public int getThreadCountSuites() + { + return threadCountSuites; + } + + @SuppressWarnings( "UnusedDeclaration" ) + public void setThreadCountSuites( int threadCountSuites ) + { + this.threadCountSuites = threadCountSuites; + } + + public int getThreadCountClasses() + { + return threadCountClasses; + } + + @SuppressWarnings( "UnusedDeclaration" ) + public void setThreadCountClasses( int threadCountClasses ) + { + this.threadCountClasses = threadCountClasses; + } + + public int getThreadCountMethods() + { + return threadCountMethods; + } + + @SuppressWarnings( "UnusedDeclaration" ) + public void setThreadCountMethods( int threadCountMethods ) + { + this.threadCountMethods = threadCountMethods; + } + public boolean isTrimStackTrace() { return trimStackTrace; diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java index a5e7daff17..dee42aa244 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java @@ -92,7 +92,7 @@ public ScanResult getScanResult() private int getThreadCount() { final String threadcount = (String) providerProperties.get( ProviderParameterNames.THREADCOUNT_PROP ); - return threadcount == null ? 1 : Integer.parseInt( threadcount ); + return threadcount == null ? 1 : Math.max( Integer.parseInt( threadcount ), 1 ); } public RunOrderCalculator getRunOrderCalculator() diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java index b70fbc9601..1407cabe76 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java @@ -31,4 +31,10 @@ public class ProviderParameterNames public static final String PARALLEL_PROP = "parallel"; + public static final String THREADCOUNTSUITES_PROP = "threadcountsuites"; + + public static final String THREADCOUNTCLASSES_PROP = "threadcountclasses"; + + public static final String THREADCOUNTMETHODS_PROP = "threadcountmethods"; + } diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/Junit47WithCucumberIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/Junit47WithCucumberIT.java index 08f6f6c976..09e857ab9d 100644 --- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/Junit47WithCucumberIT.java +++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/Junit47WithCucumberIT.java @@ -60,7 +60,10 @@ public void testWithParallelClasses() private void doTest( String parallel, int total ) { - unpack( "junit47-cucumber" ).sysProp( "parallel", parallel ).executeTest().assertTestSuiteResults( total, 0, 2, - 0 ); + unpack( "junit47-cucumber" ) + .sysProp( "parallel", parallel ) + .sysProp( "threadCount", "2" ) + .executeTest() + .assertTestSuiteResults( total, 0, 2, 0 ); } } diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/TestSingleMethodIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/TestSingleMethodIT.java index d96f4804e5..39d2ab9875 100644 --- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/TestSingleMethodIT.java +++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/TestSingleMethodIT.java @@ -54,8 +54,10 @@ public void testJunit48() public void testJunit48parallel() throws Exception { - unpack( "junit48-single-method" ).parallelClasses().executeTest().verifyErrorFreeLog().assertTestSuiteResults( - 1, 0, 0, 0 ); + unpack( "junit48-single-method" ) + .executeTest() + .verifyErrorFreeLog() + .assertTestSuiteResults( 1, 0, 0, 0 ); } @Test diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire943ReportContentIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire943ReportContentIT.java index fe72087211..32aedbf75c 100644 --- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire943ReportContentIT.java +++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire943ReportContentIT.java @@ -49,8 +49,13 @@ public void test_parallelBoth() private void doTest( String parallelMode ) throws Exception { - OutputValidator validator = - unpack( "surefire-943-report-content" ).maven().sysProp( "parallel", parallelMode ).withFailure().executeTest(); + OutputValidator validator = unpack( "surefire-943-report-content" ) + .maven() + .sysProp( "parallel", parallelMode ) + .sysProp( "threadCount", 4 ) + .withFailure() + .executeTest(); + validator.assertTestSuiteResults( 9, 0, 3, 3 ); validate( validator, "org.sample.module.My1Test", 1 ); diff --git a/surefire-integration-tests/src/test/resources/junit44-dep/pom.xml b/surefire-integration-tests/src/test/resources/junit44-dep/pom.xml index 7f778728e7..6e6d476b74 100644 --- a/surefire-integration-tests/src/test/resources/junit44-dep/pom.xml +++ b/surefire-integration-tests/src/test/resources/junit44-dep/pom.xml @@ -66,6 +66,7 @@ ${surefire.version} classes + 1 diff --git a/surefire-integration-tests/src/test/resources/junit44-dep/src/test/java/junit44Dep/BasicTest.java b/surefire-integration-tests/src/test/resources/junit44-dep/src/test/java/junit44Dep/BasicTest.java index 6c31e20423..dc0e71af0d 100644 --- a/surefire-integration-tests/src/test/resources/junit44-dep/src/test/java/junit44Dep/BasicTest.java +++ b/surefire-integration-tests/src/test/resources/junit44-dep/src/test/java/junit44Dep/BasicTest.java @@ -34,6 +34,7 @@ public void tearDown() public void testSetUp() { Assert.assertTrue( "setUp was not called", setUpCalled ); + Assert.assertFalse( "tearDown was called", tearDownCalled ); Assert.assertThat( true, Is.is( true ) ); } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java index a2dcea2d32..3b637f2a90 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java @@ -33,6 +33,12 @@ class JUnitCoreParameters private final int threadCount; + private final int threadCountSuites; + + private final int threadCountClasses; + + private final int threadCountMethods; + private final Boolean useUnlimitedThreads; public static final String PARALLEL_KEY = ProviderParameterNames.PARALLEL_PROP; @@ -41,29 +47,43 @@ class JUnitCoreParameters public static final String THREADCOUNT_KEY = ProviderParameterNames.THREADCOUNT_PROP; + public static final String THREADCOUNTSUITES_KEY = ProviderParameterNames.THREADCOUNTSUITES_PROP; + + public static final String THREADCOUNTCLASSES_KEY = ProviderParameterNames.THREADCOUNTCLASSES_PROP; + + public static final String THREADCOUNTMETHODS_KEY = ProviderParameterNames.THREADCOUNTMETHODS_PROP; + public static final String USEUNLIMITEDTHREADS_KEY = "useUnlimitedThreads"; public JUnitCoreParameters( Properties properties ) { this.parallel = properties.getProperty( PARALLEL_KEY, "none" ).toLowerCase(); this.perCoreThreadCount = Boolean.valueOf( properties.getProperty( PERCORETHREADCOUNT_KEY, "true" ) ); - this.threadCount = Integer.valueOf( properties.getProperty( THREADCOUNT_KEY, "2" ) ); + this.threadCount = Integer.valueOf( properties.getProperty( THREADCOUNT_KEY, "2" ) );//tibor daj "0" + this.threadCountMethods = Integer.valueOf( properties.getProperty( THREADCOUNTMETHODS_KEY, "0" ) ); + this.threadCountClasses = Integer.valueOf( properties.getProperty( THREADCOUNTCLASSES_KEY, "0" ) ); + this.threadCountSuites = Integer.valueOf(properties.getProperty( THREADCOUNTSUITES_KEY, "0" ) ); this.useUnlimitedThreads = Boolean.valueOf( properties.getProperty( USEUNLIMITEDTHREADS_KEY, "false" ) ); } public boolean isParallelMethod() { - return "methods".equals( parallel ); + return "methods".equals( parallel ) || "suitesAndMethods".equals( parallel ) || "classesAndMethods".equals( parallel ); } public boolean isParallelClasses() { - return "classes".equals( parallel ); + return "classes".equals( parallel ) || "suitesAndClasses".equals( parallel ) || "classesAndMethods".equals( parallel ); + } + + public boolean isParallelSuites() + { + return "suites".equals( parallel ) || "suitesAndClasses".equals( parallel ) || "suitesAndMethods".equals( parallel ); } public boolean isParallelBoth() { - return "both".equals( parallel ); + return "both".equals( parallel ) || "all".equals( parallel ); } public Boolean isPerCoreThreadCount() @@ -76,6 +96,21 @@ public int getThreadCount() return threadCount; } + public int getThreadCountMethods() + { + return threadCountMethods; + } + + public int getThreadCountClasses() + { + return threadCountClasses; + } + + public int getThreadCountSuites() + { + return threadCountSuites; + } + public Boolean isUseUnlimitedThreads() { return useUnlimitedThreads; @@ -83,7 +118,7 @@ public Boolean isUseUnlimitedThreads() public boolean isNoThreading() { - return !( isParallelClasses() || isParallelMethod() || isParallelBoth() ); + return !( isParallelSuites() || isParallelClasses() || isParallelMethod() || isParallelBoth() ); } public boolean isAnyParallelitySelected() @@ -95,6 +130,7 @@ public boolean isAnyParallelitySelected() public String toString() { return "parallel='" + parallel + '\'' + ", perCoreThreadCount=" + perCoreThreadCount + ", threadCount=" - + threadCount + ", useUnlimitedThreads=" + useUnlimitedThreads; + + threadCount + ", useUnlimitedThreads=" + useUnlimitedThreads + ", threadCountSuites=" + threadCountSuites + + ", threadCountClasses=" + threadCountClasses + ", threadCountMethods=" + threadCountMethods; } } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java index 14af56cf45..08ae0d01c2 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java @@ -153,7 +153,8 @@ private org.junit.runner.notification.RunListener getRunListener( ReporterFactor RunListener listener = ConcurrentRunListener.createInstance( testSetMap, reporterFactory, - jUnitCoreParameters.isParallelClasses(), + jUnitCoreParameters.isParallelClasses() + || jUnitCoreParameters.isParallelSuites(), jUnitCoreParameters.isParallelBoth(), consoleLogger ); ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) listener ); From c0c348851191650a40f10e99fdb93916a992988b Mon Sep 17 00:00:00 2001 From: Tibor Digana Date: Sun, 7 Jul 2013 11:20:42 +0200 Subject: [PATCH 05/11] refactoring in Balancer --- .../org/apache/maven/surefire/junitcore/pc/Balancer.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java index 59eed26c53..26feb869b1 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java @@ -56,8 +56,8 @@ public Balancer(int numPermits) { * @param fair true guarantees the waiting schedulers to wake up in order they acquired a permit */ public Balancer(int numPermits, boolean fair) { - boolean maintainTests = numPermits > 0 && numPermits < Integer.MAX_VALUE; - balancer = maintainTests ? new Semaphore(numPermits, fair) : null; + boolean shouldBalance = numPermits > 0 && numPermits < Integer.MAX_VALUE; + balancer = shouldBalance ? new Semaphore(numPermits, fair) : null; maxPermits = numPermits; } @@ -68,7 +68,6 @@ public Balancer(int numPermits, boolean fair) { * while waiting for a permit. */ public boolean acquirePermit() { - Semaphore balancer = this.balancer; if (balancer != null) { try { balancer.acquire(); @@ -83,14 +82,12 @@ public boolean acquirePermit() { * Releases a permit, returning it to the balancer. */ public void releasePermit() { - Semaphore balancer = this.balancer; if (balancer != null) { balancer.release(); } } public void releaseAllPermits() { - Semaphore balancer = this.balancer; if (balancer != null) { balancer.release(maxPermits); } From 9b8437c39c4332a1c6761d97836e578062bbd22d Mon Sep 17 00:00:00 2001 From: Tibor Digana Date: Sun, 7 Jul 2013 22:54:53 +0200 Subject: [PATCH 06/11] refactor Balancer with Factory --- .../maven/surefire/junitcore/pc/Balancer.java | 54 +---------- .../junitcore/pc/BalancerFactory.java | 68 ++++++++++++++ .../surefire/junitcore/pc/NullBalancer.java | 44 +++++++++ .../junitcore/pc/ParallelComputerBuilder.java | 8 +- .../surefire/junitcore/pc/Scheduler.java | 2 +- .../junitcore/pc/ThreadResourcesBalancer.java | 90 +++++++++++++++++++ 6 files changed, 212 insertions(+), 54 deletions(-) create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/BalancerFactory.java create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NullBalancer.java create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ThreadResourcesBalancer.java diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java index 26feb869b1..1b28309b5d 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java @@ -19,8 +19,6 @@ * under the License. */ -import java.util.concurrent.Semaphore; - /** * The Balancer controls the maximum of concurrent threads in the current Scheduler(s) and prevents * from own thread resources exhaustion if other group of schedulers share the same pool of threads. @@ -32,34 +30,7 @@ * @author Tibor Digana (tibor17) * @since 2.16 */ -public class Balancer { - private final Semaphore balancer; - private final int maxPermits; - - /** - * Infinite permits. - */ - protected Balancer() { - this(0); - } - - /** - * fair set to false. - * @see #Balancer(int, boolean) - */ - public Balancer(int numPermits) { - this(numPermits, false); - } - - /** - * @param numPermits number of permits to acquire when maintaining concurrency on tests - * @param fair true guarantees the waiting schedulers to wake up in order they acquired a permit - */ - public Balancer(int numPermits, boolean fair) { - boolean shouldBalance = numPermits > 0 && numPermits < Integer.MAX_VALUE; - balancer = shouldBalance ? new Semaphore(numPermits, fair) : null; - maxPermits = numPermits; - } +public interface Balancer { /** * Acquires a permit from this balancer, blocking until one is available. @@ -67,29 +38,12 @@ public Balancer(int numPermits, boolean fair) { * @return true if current thread is NOT interrupted * while waiting for a permit. */ - public boolean acquirePermit() { - if (balancer != null) { - try { - balancer.acquire(); - } catch (InterruptedException e) { - return false; - } - } - return true; - } + public boolean acquirePermit(); /** * Releases a permit, returning it to the balancer. */ - public void releasePermit() { - if (balancer != null) { - balancer.release(); - } - } + public void releasePermit(); - public void releaseAllPermits() { - if (balancer != null) { - balancer.release(maxPermits); - } - } + public void releaseAllPermits(); } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/BalancerFactory.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/BalancerFactory.java new file mode 100644 index 0000000000..e7db197497 --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/BalancerFactory.java @@ -0,0 +1,68 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + * + * @see Balancer + */ +public class BalancerFactory { + private BalancerFactory() + { + } + + /** + * Infinite permits. + */ + public static Balancer createInfinitePermitsBalancer() + { + return balancer( 0, false ); + } + + /** + * Balancer without fairness. + * Fairness guarantees the waiting schedulers to wake up in order they acquired a permit. + * + * @param concurrency number of permits to acquire when maintaining concurrency on tests + */ + public static Balancer createBalancer( int concurrency ) + { + return balancer( concurrency, false ); + } + + /** + * Balancer with fairness. + * Fairness guarantees the waiting schedulers to wake up in order they acquired a permit. + * + * @param concurrency number of permits to acquire when maintaining concurrency on tests + */ + public static Balancer createBalancerWithFairness( int concurrency ) + { + return balancer( concurrency, true ); + } + + private static Balancer balancer( int concurrency, boolean fairness ) + { + boolean shouldBalance = concurrency > 0 && concurrency < Integer.MAX_VALUE; + return shouldBalance ? new ThreadResourcesBalancer( concurrency, fairness ) : new NullBalancer(); + } +} \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NullBalancer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NullBalancer.java new file mode 100644 index 0000000000..eec375912b --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NullBalancer.java @@ -0,0 +1,44 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * This balancer implements {@link Balancer} and does not do anything -no blocking operation. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + * + * @see Balancer + */ +final class NullBalancer implements Balancer +{ + public boolean acquirePermit() + { + return true; + } + + public void releasePermit() + { + } + + public void releaseAllPermits() + { + } +} \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java index 7223690892..7ac2b8a02a 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java @@ -282,7 +282,8 @@ private Runner setSchedulers() throws InitializationError { // a scheduler for parallel suites final Scheduler suitesScheduler; if (commonPool != null && parallelSuites > 0) { - suitesScheduler = createScheduler(null, commonPool, true, new Balancer(parallelSuites, true)); + Balancer balancer = BalancerFactory.createBalancerWithFairness(parallelSuites); + suitesScheduler = createScheduler(null, commonPool, true, balancer); } else { suitesScheduler = createScheduler(parallelSuites); } @@ -309,7 +310,7 @@ private Runner setSchedulers() throws InitializationError { private void setSchedulers(Iterable runners, int poolSize, ExecutorService commonPool) { if (commonPool != null) { - Balancer concurrencyLimit = new Balancer(poolSize, true); + Balancer concurrencyLimit = BalancerFactory.createBalancerWithFairness(poolSize); boolean doParallel = poolSize > 0; for (ParentRunner runner : runners) { runner.setScheduler(createScheduler(runner.getDescription(), commonPool, doParallel, concurrencyLimit)); @@ -323,7 +324,8 @@ private void setSchedulers(Iterable runners, int poolSiz } boolean doParallel = pool != null; for (ParentRunner runner : runners) { - runner.setScheduler(createScheduler(runner.getDescription(), pool, doParallel, new Balancer())); + runner.setScheduler(createScheduler(runner.getDescription(), pool, doParallel, + BalancerFactory.createInfinitePermitsBalancer())); } } } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java index 241c71bd97..d4f49fab91 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java @@ -75,7 +75,7 @@ public Scheduler(Description description, SchedulingStrategy strategy) { * @throws NullPointerException if null strategy */ public Scheduler(Description description, SchedulingStrategy strategy, int concurrency) { - this(description, strategy, new Balancer(concurrency)); + this(description, strategy, BalancerFactory.createBalancer(concurrency)); } /** diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ThreadResourcesBalancer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ThreadResourcesBalancer.java new file mode 100644 index 0000000000..80d116d7ce --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ThreadResourcesBalancer.java @@ -0,0 +1,90 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.concurrent.Semaphore; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + * + * @see Balancer + */ +final class ThreadResourcesBalancer implements Balancer +{ + private final Semaphore balancer; + private final int numPermits; + + /** + * fair set to false. + * + * @param numPermits number of permits to acquire when maintaining concurrency on tests. + * Must be >0 and < {@link Integer#MAX_VALUE}. + * + * @see #ThreadResourcesBalancer(int, boolean) + */ + ThreadResourcesBalancer( int numPermits ) + { + this( numPermits, false ); + } + + /** + * @param numPermits number of permits to acquire when maintaining concurrency on tests. + * Must be >0 and < {@link Integer#MAX_VALUE}. + * @param fair true guarantees the waiting schedulers to wake up in order they acquired a permit + */ + ThreadResourcesBalancer( int numPermits, boolean fair ) + { + balancer = new Semaphore( numPermits, fair ); + this.numPermits = numPermits; + } + + /** + * Acquires a permit from this balancer, blocking until one is available. + * + * @return true if current thread is NOT interrupted + * while waiting for a permit. + */ + public boolean acquirePermit() + { + try + { + balancer.acquire(); + return true; + } + catch ( InterruptedException e ) + { + return false; + } + } + + /** + * Releases a permit, returning it to the balancer. + */ + public void releasePermit() + { + balancer.release(); + } + + public void releaseAllPermits() + { + balancer.release( numPermits ); + } +} \ No newline at end of file From 551a42b96ed2055bb9cd5f550db2ffc618e00099 Mon Sep 17 00:00:00 2001 From: Tibor Digana Date: Sun, 7 Jul 2013 22:59:34 +0200 Subject: [PATCH 07/11] threadCountSuites/Classes/Methods if useUnlimitedThreads is specified --- .../org/apache/maven/plugin/surefire/AbstractSurefireMojo.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java index b5de7a5bfb..d4f472397f 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java @@ -511,6 +511,7 @@ public abstract class AbstractSurefireMojo *
  • integer number which represents the weight in ratio between * threadCountSuites:threadCountClasses:threadCountMethods. * As an example 2 is 20% of threadCount if the ratio is 2:3:5
  • + *
  • You can impose limitation on parallel suites if useUnlimitedThreads is specified.
  • * * * Only makes sense to use in conjunction with the parallel parameter. @@ -536,6 +537,7 @@ public abstract class AbstractSurefireMojo *
  • integer number which represents the weight in ratio between * threadCountSuites:threadCountClasses:threadCountMethods. * As an example 3 is 30% of threadCount if the ratio is 2:3:5
  • + *
  • You can impose limitation on parallel classes if useUnlimitedThreads is specified.
  • * * * Only makes sense to use in conjunction with the parallel parameter. @@ -552,6 +554,7 @@ public abstract class AbstractSurefireMojo *
  • number of threads executing JUnit test methods if threadCount is 0 or unspecified;
  • *
  • integer number which represents the weight in ratio between threadCountSuites:threadCountClasses:threadCountMethods. * As an example 5 is 50% of threadCount if the ratio is 2:3:5.
  • + *
  • You can impose limitation on parallel methods if useUnlimitedThreads is specified.
  • * * * Only makes sense to use in conjunction with the parallel parameter. From 961a82381407caa9e31b7f61ac2cd5c32ef3ab20 Mon Sep 17 00:00:00 2001 From: Tibor Digana Date: Mon, 15 Jul 2013 23:51:28 +0200 Subject: [PATCH 08/11] AbstractSurefireMojo, SurefirePlugin, IntegrationTestMojo, Parameters --- .../plugin/failsafe/IntegrationTestMojo.java | 37 ++++++++++++ .../plugin/surefire/AbstractSurefireMojo.java | 15 +++++ .../surefire/SurefireExecutionParameters.java | 8 +++ .../maven/plugin/surefire/SurefirePlugin.java | 37 ++++++++++++ .../booter/ProviderParameterNames.java | 4 ++ .../its/fixture/SurefireLauncher.java | 5 ++ .../junitcore/JUnitCoreParameters.java | 56 +++++++++++++++---- .../surefire/junitcore/JUnitCoreProvider.java | 18 ++++-- .../junitcore/JUnitCoreParametersTest.java | 15 +++-- 9 files changed, 173 insertions(+), 22 deletions(-) diff --git a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java index 23f8dc1c01..41278c3748 100644 --- a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java +++ b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java @@ -137,6 +137,27 @@ public class IntegrationTestMojo */ @Parameter( property = "failsafe.timeout" ) private int forkedProcessTimeoutInSeconds; + + /** + * Stop executing queued parallel JUnit tests after a certain number of seconds. + * If set to 0, wait forever, never timing out. + * Makes sense with specified parallel different from "none". + * + * @since 2.16 + */ + @Parameter( property = "failsafe.parallel.timeout" ) + private int parallelTestsTimeoutInSeconds; + + /** + * Stop executing queued parallel JUnit tests + * and interrupt currently running tests after a certain number of seconds. + * If set to 0, wait forever, never timing out. + * Makes sense with specified parallel different from "none". + * + * @since 2.16 + */ + @Parameter( property = "failsafe.parallel.forcedTimeout" ) + private int parallelTestsTimeoutForcedInSeconds; /** * A list of <include> elements specifying the tests (by pattern) that should be included in testing. When not @@ -433,6 +454,22 @@ public void setForkedProcessTimeoutInSeconds( int forkedProcessTimeoutInSeconds this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds; } + public int getParallelTestsTimeoutInSeconds() { + return parallelTestsTimeoutInSeconds; + } + + public void setParallelTestsTimeoutInSeconds( int parallelTestsTimeoutInSeconds ) { + this.parallelTestsTimeoutInSeconds = parallelTestsTimeoutInSeconds; + } + + public int getParallelTestsTimeoutForcedInSeconds() { + return parallelTestsTimeoutForcedInSeconds; + } + + public void setParallelTestsTimeoutForcedInSeconds( int parallelTestsTimeoutForcedInSeconds ) { + this.parallelTestsTimeoutForcedInSeconds = parallelTestsTimeoutForcedInSeconds; + } + public boolean isUseSystemClassLoader() { return useSystemClassLoader; diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java index d4f472397f..5975d7d23e 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java @@ -1142,6 +1142,10 @@ private void convertJunitCoreParameters() throws MojoExecutionException getProperties().setProperty( ProviderParameterNames.THREADCOUNTSUITES_PROP, Integer.toString( getThreadCountSuites() ) ); getProperties().setProperty( ProviderParameterNames.THREADCOUNTCLASSES_PROP, Integer.toString( getThreadCountClasses() ) ); getProperties().setProperty( ProviderParameterNames.THREADCOUNTMETHODS_PROP, Integer.toString( getThreadCountMethods() ) ); + getProperties().setProperty( ProviderParameterNames.PARALLEL_TIMEOUT_PROP, + Integer.toString( getParallelTestsTimeoutInSeconds() ) ); + getProperties().setProperty( ProviderParameterNames.PARALLEL_TIMEOUTFORCED_PROP, + Integer.toString( getParallelTestsTimeoutForcedInSeconds() ) ); String message = "parallel='" + usedParallel + '\'' + ", perCoreThreadCount=" + getPerCoreThreadCount() + ", threadCount=" @@ -1161,6 +1165,8 @@ private void checkNonForkedThreads( String parallel ) throws MojoExecutionExcept throw new MojoExecutionException( "Use threadCount or threadCountSuites > 0 or useUnlimitedThreads=true " + "for parallel='suites'" ); } + setThreadCountClasses( 0 ); + setThreadCountMethods( 0 ); } else if ( "classes".equals( parallel ) ) { @@ -1169,6 +1175,8 @@ else if ( "classes".equals( parallel ) ) throw new MojoExecutionException( "Use threadCount or threadCountClasses > 0 or useUnlimitedThreads=true " + "for parallel='classes'" ); } + setThreadCountSuites( 0 ); + setThreadCountMethods( 0 ); } else if ( "methods".equals( parallel ) ) { @@ -1177,6 +1185,8 @@ else if ( "methods".equals( parallel ) ) throw new MojoExecutionException( "Use threadCount or threadCountMethods > 0 or useUnlimitedThreads=true " + "for parallel='methods'" ); } + setThreadCountSuites( 0 ); + setThreadCountClasses( 0 ); } else if ( "suitesAndClasses".equals( parallel ) ) { @@ -1196,6 +1206,7 @@ && getThreadCountClasses() == 0 && getThreadCountMethods() == 0 ) ) "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) " + "for parallel='suitesAndClasses' or 'both'" ); } + setThreadCountMethods( 0 ); } else if ( "suitesAndMethods".equals( parallel ) ) { @@ -1215,6 +1226,7 @@ && getThreadCountClasses() == 0 && getThreadCountMethods() == 0 ) ) "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) " + "for parallel='suitesAndMethods'" ); } + setThreadCountClasses( 0 ); } else if ( "both".equals( parallel ) || "classesAndMethods".equals( parallel ) ) { @@ -1234,6 +1246,7 @@ && getThreadCountSuites() == 0 && getThreadCountMethods() == 0 ) ) "or (threadCount > 0 and threadCountClasses > 0 and threadCount > threadCountClasses) " + "for parallel='both' or parallel='classesAndMethods'" ); } + setThreadCountSuites( 0 ); } else if ( "all".equals( parallel ) ) { @@ -1878,6 +1891,8 @@ private String getConfigChecksum() checksum.add( getArgLine() ); checksum.add( getDebugForkedProcess() ); checksum.add( getForkedProcessTimeoutInSeconds() ); + checksum.add( getParallelTestsTimeoutInSeconds() ); + checksum.add( getParallelTestsTimeoutForcedInSeconds() ); checksum.add( getEnvironmentVariables() ); checksum.add( getWorkingDirectory() ); checksum.add( isChildDelegation() ); diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java index 44c7d197bd..0b5a1af431 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java @@ -101,6 +101,14 @@ public interface SurefireExecutionParameters void setForkedProcessTimeoutInSeconds( int forkedProcessTimeoutInSeconds ); + int getParallelTestsTimeoutInSeconds(); + + void setParallelTestsTimeoutInSeconds( int parallelTestsTimeoutInSeconds ); + + int getParallelTestsTimeoutForcedInSeconds(); + + void setParallelTestsTimeoutForcedInSeconds( int parallelTestsTimeoutForcedInSeconds ); + boolean isUseSystemClassLoader(); void setUseSystemClassLoader( boolean useSystemClassLoader ); diff --git a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java index f0ac255039..189161b409 100644 --- a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java +++ b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java @@ -119,6 +119,27 @@ public class SurefirePlugin */ @Parameter( property = "surefire.timeout" ) private int forkedProcessTimeoutInSeconds; + + /** + * Stop executing queued parallel JUnit tests after a certain number of seconds. + * If set to 0, wait forever, never timing out. + * Makes sense with specified parallel different from "none". + * + * @since 2.16 + */ + @Parameter( property = "surefire.parallel.timeout" ) + private int parallelTestsTimeoutInSeconds; + + /** + * Stop executing queued parallel JUnit tests + * and interrupt currently running tests after a certain number of seconds. + * If set to 0, wait forever, never timing out. + * Makes sense with specified parallel different from "none". + * + * @since 2.16 + */ + @Parameter( property = "surefire.parallel.forcedTimeout" ) + private int parallelTestsTimeoutForcedInSeconds; /** * A list of <include> elements specifying the tests (by pattern) that should be included in testing. When not @@ -426,6 +447,22 @@ public void setForkedProcessTimeoutInSeconds( int forkedProcessTimeoutInSeconds this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds; } + public int getParallelTestsTimeoutInSeconds() { + return parallelTestsTimeoutInSeconds; + } + + public void setParallelTestsTimeoutInSeconds( int parallelTestsTimeoutInSeconds ) { + this.parallelTestsTimeoutInSeconds = parallelTestsTimeoutInSeconds; + } + + public int getParallelTestsTimeoutForcedInSeconds() { + return parallelTestsTimeoutForcedInSeconds; + } + + public void setParallelTestsTimeoutForcedInSeconds( int parallelTestsTimeoutForcedInSeconds ) { + this.parallelTestsTimeoutForcedInSeconds = parallelTestsTimeoutForcedInSeconds; + } + public void setTest( String test ) { this.test = test; diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java index 1407cabe76..72c16ac21e 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java @@ -37,4 +37,8 @@ public class ProviderParameterNames public static final String THREADCOUNTMETHODS_PROP = "threadcountmethods"; + public static final String PARALLEL_TIMEOUT_PROP = "paralleltimeout"; + + public static final String PARALLEL_TIMEOUTFORCED_PROP = "paralleltimeoutforced"; + } diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java index fffde85262..7b65783800 100755 --- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java +++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java @@ -321,6 +321,11 @@ public SurefireLauncher parallel( String parallel ) } + public SurefireLauncher parallelSuites() + { + return parallel( "suites" ); + } + public SurefireLauncher parallelClasses() { return parallel( "classes" ); diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java index 3b637f2a90..24fe299682 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java @@ -19,6 +19,7 @@ * under the License. */ +import java.util.Arrays; import java.util.Properties; import org.apache.maven.surefire.booter.ProviderParameterNames; @@ -39,6 +40,10 @@ class JUnitCoreParameters private final int threadCountMethods; + private final int parallelTestsTimeoutInSeconds; + + private final int parallelTestsTimeoutForcedInSeconds; + private final Boolean useUnlimitedThreads; public static final String PARALLEL_KEY = ProviderParameterNames.PARALLEL_PROP; @@ -55,35 +60,52 @@ class JUnitCoreParameters public static final String USEUNLIMITEDTHREADS_KEY = "useUnlimitedThreads"; + public static final String PARALLEL_TIMEOUT_KEY = ProviderParameterNames.PARALLEL_TIMEOUT_PROP; + + public static final String PARALLEL_TIMEOUTFORCED_KEY = ProviderParameterNames.PARALLEL_TIMEOUTFORCED_PROP; + public JUnitCoreParameters( Properties properties ) { - this.parallel = properties.getProperty( PARALLEL_KEY, "none" ).toLowerCase(); - this.perCoreThreadCount = Boolean.valueOf( properties.getProperty( PERCORETHREADCOUNT_KEY, "true" ) ); - this.threadCount = Integer.valueOf( properties.getProperty( THREADCOUNT_KEY, "2" ) );//tibor daj "0" - this.threadCountMethods = Integer.valueOf( properties.getProperty( THREADCOUNTMETHODS_KEY, "0" ) ); - this.threadCountClasses = Integer.valueOf( properties.getProperty( THREADCOUNTCLASSES_KEY, "0" ) ); - this.threadCountSuites = Integer.valueOf(properties.getProperty( THREADCOUNTSUITES_KEY, "0" ) ); - this.useUnlimitedThreads = Boolean.valueOf( properties.getProperty( USEUNLIMITEDTHREADS_KEY, "false" ) ); + parallel = properties.getProperty( PARALLEL_KEY, "none" ).toLowerCase(); + perCoreThreadCount = Boolean.valueOf( properties.getProperty( PERCORETHREADCOUNT_KEY, "true" ) ); + threadCount = Integer.valueOf( properties.getProperty( THREADCOUNT_KEY, "2" ) ); + threadCountMethods = Integer.valueOf( properties.getProperty( THREADCOUNTMETHODS_KEY, "0" ) ); + threadCountClasses = Integer.valueOf( properties.getProperty( THREADCOUNTCLASSES_KEY, "0" ) ); + threadCountSuites = Integer.valueOf( properties.getProperty( THREADCOUNTSUITES_KEY, "0" ) ); + useUnlimitedThreads = Boolean.valueOf( properties.getProperty( USEUNLIMITEDTHREADS_KEY, "false" ) ); + parallelTestsTimeoutInSeconds = Integer.valueOf( properties.getProperty( PARALLEL_TIMEOUT_KEY, "0" ) ); + parallelTestsTimeoutForcedInSeconds = Integer.valueOf( properties.getProperty( PARALLEL_TIMEOUTFORCED_KEY, "0" ) ); + } + + private boolean isAllParallel() + { + return "all".equals( parallel ); } public boolean isParallelMethod() { - return "methods".equals( parallel ) || "suitesAndMethods".equals( parallel ) || "classesAndMethods".equals( parallel ); + return isAllParallel() + || Arrays.asList( "both", "methods", "suitesAndMethods", "classesAndMethods" ).contains( parallel ); } public boolean isParallelClasses() { - return "classes".equals( parallel ) || "suitesAndClasses".equals( parallel ) || "classesAndMethods".equals( parallel ); + return isAllParallel() + || Arrays.asList( "both", "classes", "suitesAndClasses", "classesAndMethods" ).contains( parallel ); } public boolean isParallelSuites() { - return "suites".equals( parallel ) || "suitesAndClasses".equals( parallel ) || "suitesAndMethods".equals( parallel ); + return isAllParallel() || Arrays.asList( "suites", "suitesAndClasses", "suitesAndMethods" ).contains( parallel ); } + /** + * @deprecated Instead use the expression ( {@link #isParallelMethod()} && {@link #isParallelClasses()} ). + */ + @Deprecated public boolean isParallelBoth() { - return "both".equals( parallel ) || "all".equals( parallel ); + return isParallelMethod() && isParallelClasses(); } public Boolean isPerCoreThreadCount() @@ -116,9 +138,19 @@ public Boolean isUseUnlimitedThreads() return useUnlimitedThreads; } + public int getParallelTestsTimeoutInSeconds() + { + return parallelTestsTimeoutInSeconds; + } + + public int getParallelTestsTimeoutForcedInSeconds() + { + return parallelTestsTimeoutForcedInSeconds; + } + public boolean isNoThreading() { - return !( isParallelSuites() || isParallelClasses() || isParallelMethod() || isParallelBoth() ); + return !( isParallelSuites() || isParallelClasses() || isParallelMethod() ); } public boolean isAnyParallelitySelected() diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java index 08ae0d01c2..de0673c7d4 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java @@ -100,8 +100,7 @@ public Iterator getSuites() private boolean isSingleThreaded() { - return !jUnitCoreParameters.isAnyParallelitySelected() - || ( testsToRun.containsExactly( 1 ) && !jUnitCoreParameters.isParallelMethod() ); + return jUnitCoreParameters.isNoThreading(); } public RunResult invoke( Object forkTestSet ) @@ -153,9 +152,8 @@ private org.junit.runner.notification.RunListener getRunListener( ReporterFactor RunListener listener = ConcurrentRunListener.createInstance( testSetMap, reporterFactory, - jUnitCoreParameters.isParallelClasses() - || jUnitCoreParameters.isParallelSuites(), - jUnitCoreParameters.isParallelBoth(), consoleLogger ); + isParallelTypes(), + isParallelMethodsAndTypes(), consoleLogger ); ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) listener ); jUnit4RunListener = new JUnitCoreRunListener( listener, testSetMap ); @@ -163,6 +161,16 @@ private org.junit.runner.notification.RunListener getRunListener( ReporterFactor return jUnit4RunListener; } + private boolean isParallelMethodsAndTypes() + { + return jUnitCoreParameters.isParallelMethod() && isParallelTypes(); + } + + private boolean isParallelTypes() + { + return jUnitCoreParameters.isParallelClasses() || jUnitCoreParameters.isParallelSuites(); + } + private Filter createJUnit48Filter() { final FilterFactory filterFactory = new FilterFactory( testClassLoader ); diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreParametersTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreParametersTest.java index 0201d90952..e100357d09 100644 --- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreParametersTest.java +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreParametersTest.java @@ -34,7 +34,7 @@ public void testIsParallelMethod() { assertFalse( getTestSetClasses().isParallelMethod() ); assertTrue( getTestSetMethods().isParallelMethod() ); - assertFalse( getTestSetBoth().isParallelMethod() ); + assertTrue( getTestSetBoth().isParallelMethod() ); } public void testIsParallelClasses() @@ -42,15 +42,15 @@ public void testIsParallelClasses() { assertTrue( getTestSetClasses().isParallelClasses() ); assertFalse( getTestSetMethods().isParallelClasses() ); - assertFalse( getTestSetBoth().isParallelClasses() ); + assertTrue( getTestSetBoth().isParallelClasses() ); } public void testIsParallelBoth() throws Exception { - assertFalse( getTestSetClasses().isParallelBoth() ); - assertFalse( getTestSetMethods().isParallelBoth() ); - assertTrue( getTestSetBoth().isParallelBoth() ); + assertFalse( isParallelMethodsAndClasses( getTestSetClasses() ) ); + assertFalse( isParallelMethodsAndClasses( getTestSetMethods() ) ); + assertTrue( isParallelMethodsAndClasses( getTestSetBoth() ) ); } public void testIsPerCoreThreadCount() @@ -145,4 +145,9 @@ private JUnitCoreParameters getTestSetMethods() { return new JUnitCoreParameters( getPropsetMethods() ); } + + private boolean isParallelMethodsAndClasses( JUnitCoreParameters jUnitCoreParameters ) + { + return jUnitCoreParameters.isParallelMethod() && jUnitCoreParameters.isParallelClasses(); + } } From 68b0f09cb00334fd8ab36ad25a35c84cf6f047fc Mon Sep 17 00:00:00 2001 From: Tibor Digana Date: Wed, 17 Jul 2013 23:31:28 +0200 Subject: [PATCH 09/11] Fix: surefire-junit47 module did not run JUnit4 tests using annotations --- .../surefire/junitcore/JUnit4SuiteTest.java | 49 +++++++++++++++++++ .../surefire/junitcore/Surefire746Test.java | 25 +++++++--- 2 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnit4SuiteTest.java diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnit4SuiteTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnit4SuiteTest.java new file mode 100644 index 0000000000..455f28032d --- /dev/null +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnit4SuiteTest.java @@ -0,0 +1,49 @@ +package org.apache.maven.surefire.junitcore; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import junit.framework.JUnit4TestAdapter; +import junit.framework.Test; +import org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilderTest; +import org.apache.maven.surefire.junitcore.pc.SchedulingStrategiesTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/** + * Adapt the JUnit4 tests which use only annotations to the JUnit3 test suite. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +@Suite.SuiteClasses( { + Surefire746Test.class, + Surefire813IncorrectResultTest.class, + ParallelComputerFactoryTest.class, + ParallelComputerBuilderTest.class, + SchedulingStrategiesTest.class +} ) +@RunWith( Suite.class ) +public class JUnit4SuiteTest +{ + public static Test suite() + { + return new JUnit4TestAdapter( JUnit4SuiteTest.class ); + } +} \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java index 190a280896..ab312afc90 100644 --- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Surefire746Test.java @@ -33,10 +33,13 @@ import org.apache.maven.surefire.report.ReporterFactory; import org.apache.maven.surefire.report.RunListener; import org.apache.maven.surefire.suite.RunResult; +import org.apache.maven.surefire.testset.TestSetFailedException; import org.apache.maven.surefire.util.TestsToRun; import junit.framework.Assert; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.Description; import org.junit.runner.RunWith; import org.junit.runner.notification.RunNotifier; @@ -77,6 +80,8 @@ */ public class Surefire746Test { + @Rule + public final ExpectedException exception = ExpectedException.none(); @Test public void surefireIsConfused_ByMultipleIgnore_OnClassLevel() @@ -105,13 +110,19 @@ public void surefireIsConfused_ByMultipleIgnore_OnClassLevel() new ArrayList(); customRunListeners.add( 0, jUnit4RunListener ); - JUnitCoreWrapper.execute( testsToRun, jUnitCoreParameters, customRunListeners, null ); - - RunResult result = reporterFactory.close(); - - Assert.assertEquals( "JUnit should report correctly number of test ran(Finished)", 1, - result.getCompletedCount() ); - + try + { + // JUnitCoreWrapper#execute() is calling JUnit4RunListener#rethrowAnyTestMechanismFailures() + // and rethrows a failure which happened in listener + exception.expect( TestSetFailedException.class ); + JUnitCoreWrapper.execute( testsToRun, jUnitCoreParameters, customRunListeners, null ); + } + finally + { + RunResult result = reporterFactory.close(); + Assert.assertEquals( "JUnit should report correctly number of test ran(Finished)", + 1, result.getCompletedCount() ); + } } @RunWith( TestCaseRunner.class ) From 4b733ef78f5ce68bfff81b6d1bac33da4b212f0d Mon Sep 17 00:00:00 2001 From: Tibor Digana Date: Thu, 18 Jul 2013 07:53:18 +0200 Subject: [PATCH 10/11] PC + successful unit tests --- .../junitcore/JUnitCoreParameters.java | 25 +- .../surefire/junitcore/JUnitCoreWrapper.java | 166 +++- .../junitcore/ParallelComputerFactory.java | 368 +++++++ .../junitcore/pc/ParallelComputer.java | 83 ++ .../junitcore/pc/ParallelComputerBuilder.java | 155 ++- .../surefire/junitcore/pc/Scheduler.java | 2 +- .../ParallelComputerFactoryTest.java | 907 ++++++++++++++++++ .../pc/ParallelComputerBuilderTest.java | 47 +- 8 files changed, 1626 insertions(+), 127 deletions(-) create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ParallelComputerFactory.java create mode 100644 surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputer.java create mode 100644 surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/ParallelComputerFactoryTest.java diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java index 24fe299682..157ed582f1 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java @@ -19,7 +19,8 @@ * under the License. */ -import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collection; import java.util.Properties; import org.apache.maven.surefire.booter.ProviderParameterNames; @@ -68,7 +69,7 @@ public JUnitCoreParameters( Properties properties ) { parallel = properties.getProperty( PARALLEL_KEY, "none" ).toLowerCase(); perCoreThreadCount = Boolean.valueOf( properties.getProperty( PERCORETHREADCOUNT_KEY, "true" ) ); - threadCount = Integer.valueOf( properties.getProperty( THREADCOUNT_KEY, "2" ) ); + threadCount = Integer.valueOf( properties.getProperty( THREADCOUNT_KEY, "0" ) ); threadCountMethods = Integer.valueOf( properties.getProperty( THREADCOUNTMETHODS_KEY, "0" ) ); threadCountClasses = Integer.valueOf( properties.getProperty( THREADCOUNTCLASSES_KEY, "0" ) ); threadCountSuites = Integer.valueOf( properties.getProperty( THREADCOUNTSUITES_KEY, "0" ) ); @@ -77,6 +78,16 @@ public JUnitCoreParameters( Properties properties ) parallelTestsTimeoutForcedInSeconds = Integer.valueOf( properties.getProperty( PARALLEL_TIMEOUTFORCED_KEY, "0" ) ); } + private static Collection lowerCase( String... elements ) + { + ArrayList lowerCase = new ArrayList(); + for ( String element : elements ) + { + lowerCase.add( element.toLowerCase() ); + } + return lowerCase; + } + private boolean isAllParallel() { return "all".equals( parallel ); @@ -85,18 +96,18 @@ private boolean isAllParallel() public boolean isParallelMethod() { return isAllParallel() - || Arrays.asList( "both", "methods", "suitesAndMethods", "classesAndMethods" ).contains( parallel ); + || lowerCase( "both", "methods", "suitesAndMethods", "classesAndMethods" ).contains( parallel ); } public boolean isParallelClasses() { return isAllParallel() - || Arrays.asList( "both", "classes", "suitesAndClasses", "classesAndMethods" ).contains( parallel ); + || lowerCase( "both", "classes", "suitesAndClasses", "classesAndMethods" ).contains( parallel ); } public boolean isParallelSuites() { - return isAllParallel() || Arrays.asList( "suites", "suitesAndClasses", "suitesAndMethods" ).contains( parallel ); + return isAllParallel() || lowerCase( "suites", "suitesAndClasses", "suitesAndMethods" ).contains( parallel ); } /** @@ -150,12 +161,12 @@ public int getParallelTestsTimeoutForcedInSeconds() public boolean isNoThreading() { - return !( isParallelSuites() || isParallelClasses() || isParallelMethod() ); + return !isAnyParallelitySelected(); } public boolean isAnyParallelitySelected() { - return !isNoThreading(); + return isParallelSuites() || isParallelClasses() || isParallelMethod(); } @Override diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java index 4473063fc1..45cf34c18c 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java @@ -19,14 +19,20 @@ * under the License. */ -import java.util.Iterator; +import java.util.Collection; +import java.util.Collections; import java.util.List; -import java.util.concurrent.ExecutionException; +import java.util.TreeSet; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + import org.apache.maven.surefire.common.junit4.JUnit4RunListener; +import org.apache.maven.surefire.junitcore.pc.ParallelComputer; import org.apache.maven.surefire.testset.TestSetFailedException; import org.apache.maven.surefire.util.TestsToRun; import org.junit.runner.Computer; +import org.junit.runner.Description; import org.junit.runner.JUnitCore; import org.junit.runner.Request; import org.junit.runner.Result; @@ -73,24 +79,21 @@ public static void execute( TestsToRun testsToRun, JUnitCoreParameters jUnitCore List listeners, Filter filter ) throws TestSetFailedException { - Computer computer = getComputer( jUnitCoreParameters ); - + ComputerWrapper computerWrapper = createComputer( jUnitCoreParameters ); JUnitCore junitCore = createJUnitCore( listeners ); - - try + if ( testsToRun.allowEagerReading() ) { - if ( testsToRun.allowEagerReading() ) - { - executeEager( testsToRun, filter, computer, junitCore ); - } - else - { - exeuteLazy( testsToRun, filter, computer, junitCore ); - } + executeEager( testsToRun, filter, computerWrapper.getComputer(), junitCore ); } - finally + else + { + exeuteLazy( testsToRun, filter, computerWrapper.getComputer(), junitCore ); + } + + String timeoutMessage = computerWrapper.describeElapsedTimeout(); + if ( timeoutMessage.length() != 0 ) { - closeIfConfigurable( computer ); + throw new TestSetFailedException( timeoutMessage ); } } @@ -108,21 +111,20 @@ private static void executeEager(TestsToRun testsToRun, Filter filter, Computer throws TestSetFailedException { Class[] tests = testsToRun.getLocatedClasses(); - createReqestAndRun( filter, computer, junitCore, tests ); + createRequestAndRun( filter, computer, junitCore, tests ); } private static void exeuteLazy(TestsToRun testsToRun, Filter filter, Computer computer, JUnitCore junitCore) throws TestSetFailedException { // in order to support LazyTestsToRun, the iterator must be used - Iterator classIter = testsToRun.iterator(); - while ( classIter.hasNext() ) + for ( Class clazz : testsToRun ) { - createReqestAndRun( filter, computer, junitCore, new Class[]{ (Class) classIter.next() } ); + createRequestAndRun( filter, computer, junitCore, clazz ); } } - private static void createReqestAndRun( Filter filter, Computer computer, JUnitCore junitCore, Class[] classesToRun ) + private static void createRequestAndRun( Filter filter, Computer computer, JUnitCore junitCore, Class... classesToRun ) throws TestSetFailedException { Request req = Request.classes( computer, classesToRun ); @@ -140,46 +142,110 @@ private static void createReqestAndRun( Filter filter, Computer computer, JUnitC JUnit4RunListener.rethrowAnyTestMechanismFailures( run ); } - private static void closeIfConfigurable( Computer computer ) + private static ComputerWrapper createComputer( JUnitCoreParameters parameters ) throws TestSetFailedException { - if ( computer instanceof ConfigurableParallelComputer ) - { - try - { - ( (ConfigurableParallelComputer) computer ).close(); - } - catch ( ExecutionException e ) - { - throw new TestSetFailedException( e ); - } - } + return parameters.isNoThreading() ? new ComputerWrapper( Computer.serial() ) : createParallelComputer( parameters ); } - private static Computer getComputer( JUnitCoreParameters jUnitCoreParameters ) - throws TestSetFailedException + private static ComputerWrapper createParallelComputer( JUnitCoreParameters parameters ) + throws TestSetFailedException { - if ( jUnitCoreParameters.isNoThreading() ) - { - return new Computer(); - } - return getConfigurableParallelComputer( jUnitCoreParameters ); + ParallelComputer pc = ParallelComputerFactory.createParallelComputer( parameters ); + + int timeout = parameters.getParallelTestsTimeoutInSeconds(); + + int timeoutForced = parameters.getParallelTestsTimeoutForcedInSeconds(); + + Future> testsBeforeShutdown = + timeout > 0 ? pc.scheduleShutdown( timeout, TimeUnit.SECONDS ) : null; + + Future> testsBeforeForcedShutdown = + timeoutForced > 0 ? pc.scheduleForcedShutdown( timeoutForced, TimeUnit.SECONDS ) : null; + + return new ComputerWrapper( pc, timeout, testsBeforeShutdown, timeoutForced, testsBeforeForcedShutdown ); } - private static Computer getConfigurableParallelComputer( JUnitCoreParameters jUnitCoreParameters ) - throws TestSetFailedException + private static class ComputerWrapper { - if ( jUnitCoreParameters.isUseUnlimitedThreads() ) + private final Computer computer; + private final int timeout; + private final int timeoutForced; + private final Future> testsBeforeShutdown; + private final Future> testsBeforeForcedShutdown; + + ComputerWrapper( Computer computer ) { - return new ConfigurableParallelComputer(); + this( computer, 0, null, 0, null ); } - else + + ComputerWrapper( Computer computer, + int timeout, Future> testsBeforeShutdown, + int timeoutForced, Future> testsBeforeForcedShutdown ) { - return new ConfigurableParallelComputer( - jUnitCoreParameters.isParallelClasses() | jUnitCoreParameters.isParallelBoth(), - jUnitCoreParameters.isParallelMethod() | jUnitCoreParameters.isParallelBoth(), - jUnitCoreParameters.getThreadCount(), jUnitCoreParameters.isPerCoreThreadCount() ); + this.computer = computer; + this.timeout = timeout; + this.testsBeforeShutdown = testsBeforeShutdown; + this.timeoutForced = timeoutForced; + this.testsBeforeForcedShutdown = testsBeforeForcedShutdown; } - } + Computer getComputer() + { + return computer; + } + + String describeElapsedTimeout() throws TestSetFailedException + { + TreeSet executedTests = new TreeSet(); + if ( timeout > 0 ) + { + executedTests.addAll( printShutdownHook( testsBeforeShutdown ) ); + } + + if ( timeoutForced > 0 ) + { + executedTests.addAll( printShutdownHook( testsBeforeForcedShutdown ) ); + } + + StringBuilder msg = new StringBuilder(); + if ( !executedTests.isEmpty() ) + { + msg.append( "The test run has finished abruptly after timeout of " ); + msg.append( Math.min( timeout, timeoutForced ) ); + msg.append( " seconds.\n" ); + msg.append( "These tests were executed in prior of the shutdown operation:\n" ); + for ( String executedTest : executedTests ) + { + msg.append( executedTest ).append( "\n" ); + } + } + return msg.toString(); + } + + static Collection printShutdownHook( Future> future ) + throws TestSetFailedException + { + if ( !future.isCancelled() && future.isDone() ) + { + try + { + TreeSet executedTests = new TreeSet(); + for ( Description executedTest : future.get() ) + { + if ( executedTest != null && executedTest.getDisplayName() != null ) + { + executedTests.add( executedTest.getDisplayName() ); + } + } + return executedTests; + } + catch ( Exception e ) + { + throw new TestSetFailedException( e ); + } + } + return Collections.emptySet(); + } + } } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ParallelComputerFactory.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ParallelComputerFactory.java new file mode 100644 index 0000000000..3937cd4855 --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ParallelComputerFactory.java @@ -0,0 +1,368 @@ +package org.apache.maven.surefire.junitcore; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.surefire.junitcore.pc.ParallelComputer; +import org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder; +import org.apache.maven.surefire.testset.TestSetFailedException; + +/** + * An algorithm which configures {@link ParallelComputer} with allocated thread resources by given {@link JUnitCoreParameters}. + * The AbstractSurefireMojo has to provide correct combinations of thread-counts and parallel. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + * + * @see org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder + */ +final class ParallelComputerFactory +{ + private static int availableProcessors = Runtime.getRuntime().availableProcessors(); + + static class Concurrency + { + int suites, classes, methods, capacity; + } + + private ParallelComputerFactory() + { + throw new IllegalStateException("Suppresses calling constructor, ensuring non-instantiability."); + } + + /* + * For testing purposes. + */ + static void overrideAvailableProcessors( int availableProcessors ) + { + ParallelComputerFactory.availableProcessors = availableProcessors; + } + + /* + * For testing purposes. + */ + static void setDefaultAvailableProcessors() + { + ParallelComputerFactory.availableProcessors = Runtime.getRuntime().availableProcessors(); + } + + static ParallelComputer createParallelComputer( JUnitCoreParameters params ) throws TestSetFailedException + { + Concurrency concurrency = resolveConcurrency( params ); + ParallelComputerBuilder builder = new ParallelComputerBuilder(); + + if ( params.isParallelSuites() ) + { + resolveSuitesConcurrency( builder, concurrency.suites ); + } + + if ( params.isParallelClasses() ) + { + resolveClassesConcurrency( builder, concurrency.classes ); + } + + if ( params.isParallelMethod() ) + { + resolveMethodsConcurrency( builder, concurrency.methods ); + } + + resolveCapacity( builder, concurrency.capacity ); + return builder.buildComputer(); + } + + static Concurrency resolveConcurrency( JUnitCoreParameters params ) throws TestSetFailedException + { + if ( !params.isAnyParallelitySelected() ) + { + throw new TestSetFailedException( "Unspecified parameter '" + JUnitCoreParameters.PARALLEL_KEY + "'." ); + } + + if ( !params.isUseUnlimitedThreads() && !hasThreadCount( params ) && !hasThreadCounts( params ) ) + { + throw new TestSetFailedException( "Unspecified thread-count(s). " + + "See the parameters " + JUnitCoreParameters.USEUNLIMITEDTHREADS_KEY + ", " + + JUnitCoreParameters.THREADCOUNT_KEY + ", " + JUnitCoreParameters.THREADCOUNTSUITES_KEY + ", " + + JUnitCoreParameters.THREADCOUNTCLASSES_KEY + ", " + JUnitCoreParameters.THREADCOUNTMETHODS_KEY + "."); + } + + if ( params.isUseUnlimitedThreads() ) + { + return concurrencyForUnlimitedThreads( params ); + } + else + { + if ( hasThreadCount( params ) ) + { + if ( hasThreadCounts( params ) ) + { + return isLeafUnspecified( params ) ? + concurrencyFromAllThreadCountsButUnspecifiedLeafCount( params ) : + concurrencyFromAllThreadCounts( params ); + } + else + { + return estimateConcurrency( params ); + } + } + else + { + return concurrencyFromThreadCounts( params ); + } + } + } + + private static void resolveSuitesConcurrency( ParallelComputerBuilder builder, int concurrency ) + { + if ( concurrency > 0 ) + { + if ( concurrency == Integer.MAX_VALUE ) + { + builder.parallelSuites(); + } + else + { + builder.parallelSuites( concurrency ); + } + } + } + + private static void resolveClassesConcurrency( ParallelComputerBuilder builder, int concurrency ) + { + if ( concurrency > 0 ) + { + if ( concurrency == Integer.MAX_VALUE ) + { + builder.parallelClasses(); + } + else + { + builder.parallelClasses( concurrency ); + } + } + } + + private static void resolveMethodsConcurrency( ParallelComputerBuilder builder, int concurrency ) + { + if ( concurrency > 0 ) + { + if ( concurrency == Integer.MAX_VALUE ) + { + builder.parallelMethods(); + } + else + { + builder.parallelMethods( concurrency ); + } + } + } + + private static void resolveCapacity( ParallelComputerBuilder builder, int capacity ) + { + if ( capacity > 0 ) + { + builder.useOnePool( capacity ); + } + } + + private static Concurrency concurrencyForUnlimitedThreads( JUnitCoreParameters params ) + { + Concurrency concurrency = new Concurrency(); + concurrency.suites = params.isParallelSuites() ? threadCountSuites( params ) : 0; + concurrency.classes = params.isParallelClasses() ? threadCountClasses( params ) : 0; + concurrency.methods = params.isParallelMethod() ? threadCountMethods( params ) : 0; + concurrency.capacity = Integer.MAX_VALUE; + return concurrency; + } + + private static Concurrency estimateConcurrency( JUnitCoreParameters params ) + { + Concurrency concurrency = new Concurrency(); + concurrency.suites = params.isParallelSuites() ? params.getThreadCountSuites() : 0; + concurrency.classes = params.isParallelClasses() ? params.getThreadCountClasses() : 0; + concurrency.methods = params.isParallelMethod() ? params.getThreadCountMethods() : 0; + concurrency.capacity = params.getThreadCount(); + + // estimate parallel thread counts + double ratio = 1d / countParallelEntities( params ); + int threads = multiplyByCoreCount( params, ratio * concurrency.capacity ); + concurrency.suites = params.isParallelSuites() ? threads : 0; + concurrency.classes = params.isParallelClasses() ? threads : 0; + concurrency.methods = params.isParallelMethod() ? threads : 0; + if ( countParallelEntities( params ) == 1 ) + { + concurrency.capacity = 0; + } + else + { + concurrency.capacity = multiplyByCoreCount( params, concurrency.capacity ); + adjustLeaf( params, concurrency ); + } + return concurrency; + } + + private static Concurrency concurrencyFromAllThreadCountsButUnspecifiedLeafCount( JUnitCoreParameters params ) + { + Concurrency concurrency = new Concurrency(); + concurrency.suites = params.isParallelSuites() ? params.getThreadCountSuites() : 0; + concurrency.classes = params.isParallelClasses() ? params.getThreadCountClasses() : 0; + concurrency.methods = params.isParallelMethod() ? params.getThreadCountMethods() : 0; + concurrency.capacity = params.getThreadCount(); + + setLeafInfinite( params, concurrency ); + concurrency.suites = params.isParallelSuites() ? multiplyByCoreCount( params, concurrency.suites ) : 0; + concurrency.classes = params.isParallelClasses() ? multiplyByCoreCount( params, concurrency.classes ) : 0; + concurrency.methods = params.isParallelMethod() ? multiplyByCoreCount( params, concurrency.methods ) : 0; + concurrency.capacity = multiplyByCoreCount( params, concurrency.capacity ); + + return concurrency; + } + + private static Concurrency concurrencyFromAllThreadCounts( JUnitCoreParameters params ) + { + Concurrency concurrency = new Concurrency(); + concurrency.suites = params.isParallelSuites() ? params.getThreadCountSuites() : 0; + concurrency.classes = params.isParallelClasses() ? params.getThreadCountClasses() : 0; + concurrency.methods = params.isParallelMethod() ? params.getThreadCountMethods() : 0; + concurrency.capacity = params.getThreadCount(); + double all = sumThreadCounts( concurrency ); + + concurrency.suites = params.isParallelSuites() ? + multiplyByCoreCount( params, concurrency.capacity * ( concurrency.suites / all ) ) : 0; + + concurrency.classes = params.isParallelClasses() ? + multiplyByCoreCount( params, concurrency.capacity * ( concurrency.classes / all ) ) : 0; + + concurrency.methods = params.isParallelMethod() ? + multiplyByCoreCount( params, concurrency.capacity * ( concurrency.methods / all ) ) : 0; + + concurrency.capacity = multiplyByCoreCount( params, concurrency.capacity ); + adjustPrecisionInLeaf( params, concurrency ); + return concurrency; + } + + private static Concurrency concurrencyFromThreadCounts( JUnitCoreParameters params ) + { + Concurrency concurrency = new Concurrency(); + concurrency.suites = params.isParallelSuites() ? threadCountSuites( params ) : 0; + concurrency.classes = params.isParallelClasses() ? threadCountClasses( params ) : 0; + concurrency.methods = params.isParallelMethod() ? threadCountMethods( params ) : 0 ; + concurrency.capacity = (int) Math.min( sumThreadCounts( concurrency ), Integer.MAX_VALUE ); + return concurrency; + } + + private static int countParallelEntities( JUnitCoreParameters params ) + { + int count = 0; + if ( params.isParallelSuites() ) count++; + if ( params.isParallelClasses() ) count++; + if ( params.isParallelMethod() ) count++; + return count; + } + + private static void adjustPrecisionInLeaf( JUnitCoreParameters params, Concurrency concurrency ) + { + if ( params.isParallelMethod() ) + { + concurrency.methods = concurrency.capacity - concurrency.suites - concurrency.classes; + } + else if ( params.isParallelClasses() ) + { + concurrency.classes = concurrency.capacity - concurrency.suites; + } + } + + private static void adjustLeaf( JUnitCoreParameters params, Concurrency concurrency ) + { + if ( params.isParallelMethod() ) + { + concurrency.methods = Integer.MAX_VALUE; + } + else if ( params.isParallelClasses() ) + { + concurrency.classes = Integer.MAX_VALUE; + } + } + + private static void setLeafInfinite( JUnitCoreParameters params, Concurrency concurrency ) + { + if ( params.isParallelMethod() ) concurrency.methods = Integer.MAX_VALUE; + else if ( params.isParallelClasses() ) concurrency.classes = Integer.MAX_VALUE; + else if ( params.isParallelSuites() ) concurrency.suites = Integer.MAX_VALUE; + } + + private static boolean isLeafUnspecified( JUnitCoreParameters params ) + { + int maskOfParallel = params.isParallelSuites() ? 4: 0; + maskOfParallel |= params.isParallelClasses() ? 2 : 0; + maskOfParallel |= params.isParallelMethod() ? 1 : 0; + + int maskOfConcurrency = params.getThreadCountSuites() > 0 ? 4 : 0; + maskOfConcurrency |= params.getThreadCountClasses() > 0 ? 2 : 0; + maskOfConcurrency |= params.getThreadCountMethods() > 0 ? 1 : 0; + + maskOfConcurrency &= maskOfParallel; + + int leaf = Integer.lowestOneBit( maskOfParallel ); + return maskOfConcurrency == maskOfParallel - leaf; + } + + private static double sumThreadCounts( Concurrency concurrency ) + { + double sum = concurrency.suites; + sum += concurrency.classes; + sum += concurrency.methods; + return sum; + } + + private static boolean hasThreadCounts( JUnitCoreParameters jUnitCoreParameters ) + { + return jUnitCoreParameters.getThreadCountSuites() > 0 || + jUnitCoreParameters.getThreadCountClasses() > 0 || + jUnitCoreParameters.getThreadCountMethods() > 0; + } + + private static boolean hasThreadCount ( JUnitCoreParameters jUnitCoreParameters ) + { + return jUnitCoreParameters.getThreadCount() > 0; + } + + private static int threadCountMethods( JUnitCoreParameters jUnitCoreParameters ) + { + return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountMethods() ); + } + + private static int threadCountClasses( JUnitCoreParameters jUnitCoreParameters ) + { + return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountClasses() ); + } + + private static int threadCountSuites( JUnitCoreParameters jUnitCoreParameters ) + { + return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountSuites() ); + } + + private static int multiplyByCoreCount( JUnitCoreParameters jUnitCoreParameters, double threadsPerCore ) + { + double numberOfThreads = + jUnitCoreParameters.isPerCoreThreadCount() ? + threadsPerCore * (double) availableProcessors : threadsPerCore; + + return numberOfThreads > 0 ? (int) Math.min( numberOfThreads, Integer.MAX_VALUE ) : Integer.MAX_VALUE; + } +} \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputer.java new file mode 100644 index 0000000000..ef4fc94267 --- /dev/null +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputer.java @@ -0,0 +1,83 @@ +package org.apache.maven.surefire.junitcore.pc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.runner.Computer; +import org.junit.runner.Description; + +import java.util.Collection; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * ParallelComputer extends JUnit {@link Computer} and has a shutdown functionality. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + * + * @see ParallelComputerBuilder + */ +public abstract class ParallelComputer extends Computer +{ + private ScheduledExecutorService shutdownScheduler; + + public abstract Collection shutdown( boolean shutdownNow ); + + protected final void afterRunQuietly() + { + if ( shutdownScheduler != null ) + { + shutdownScheduler.shutdownNow(); + } + } + + public Future> scheduleShutdown( int timeout, TimeUnit unit ) + { + return getShutdownScheduler().schedule( createShutdownTask( false ), timeout, unit ); + } + + public Future> scheduleForcedShutdown( int timeout, TimeUnit unit ) + { + return getShutdownScheduler().schedule( createShutdownTask( true ), timeout, unit ); + } + + private ScheduledExecutorService getShutdownScheduler() + { + if ( shutdownScheduler == null ) + { + shutdownScheduler = Executors.newScheduledThreadPool( 2 ); + } + return shutdownScheduler; + } + + private Callable> createShutdownTask( final boolean isForced ) + { + return new Callable>() + { + public Collection call() throws Exception + { + return shutdown( isForced ); + } + }; + } +} \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java index 7ac2b8a02a..ef2c05e7e5 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java @@ -20,18 +20,17 @@ */ import org.junit.internal.runners.ErrorReportingRunner; -import org.junit.runner.Computer; import org.junit.runner.Description; import org.junit.runner.Runner; import org.junit.runner.manipulation.Filter; import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.notification.RunNotifier; import org.junit.runners.ParentRunner; import org.junit.runners.Suite; import org.junit.runners.model.InitializationError; import org.junit.runners.model.RunnerBuilder; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -96,6 +95,7 @@ public ParallelComputerBuilder useOnePool() { /** * @param totalPoolSize Pool size where suites, classes and methods are executed in parallel. + * If the totalPoolSize is {@link Integer#MAX_VALUE}, the pool capacity is not limited. * @throws IllegalArgumentException If totalPoolSize is < 1. */ public ParallelComputerBuilder useOnePool(int totalPoolSize) { @@ -149,35 +149,28 @@ private ParallelComputerBuilder parallel(Type parallelType) { } public ParallelComputer buildComputer() { - return new ParallelComputer(); + return new PC(); } - private static class RunnersSuite extends Suite { - protected RunnersSuite(Collection runners) throws InitializationError { - super(null, new ArrayList(runners)); - } - - protected RunnersSuite(Runner... runners) throws InitializationError { - super(null, Arrays.asList(runners)); - } - } - - public final class ParallelComputer extends Computer { - final Collection suites = new LinkedHashSet(); - final Collection nestedSuites = new LinkedHashSet(); + final class PC extends ParallelComputer + { + final Collection suites = new LinkedHashSet(); + final Collection nestedSuites = new LinkedHashSet(); final Collection classes = new LinkedHashSet(); final Collection nestedClasses = new LinkedHashSet(); + final Collection unscheduledRunners = new LinkedHashSet(); final int poolCapacity; final boolean splitPool; private final Map allGroups; private volatile Scheduler master; - private ParallelComputer() { + private PC() { allGroups = new HashMap(ParallelComputerBuilder.this.parallelGroups); poolCapacity = ParallelComputerBuilder.this.totalPoolSize; splitPool = ParallelComputerBuilder.this.useSeparatePools; } + @Override public Collection shutdown(boolean shutdownNow) { final Scheduler master = this.master; return master == null ? Collections.emptyList() : master.shutdown(shutdownNow); @@ -191,14 +184,23 @@ public Runner getSuite(RunnerBuilder builder, Class[] cls) throws Initializat } @Override - protected Runner getRunner(RunnerBuilder builder, Class testClass) throws Throwable { - Runner runner = super.getRunner(builder, testClass); - if (canUse(runner)) { - if (runner instanceof Suite) { - suites.add((Suite) runner); - } else { - classes.add((ParentRunner) runner); + protected Runner getRunner( RunnerBuilder builder, Class testClass ) throws Throwable + { + Runner runner = super.getRunner( builder, testClass ); + if ( canSchedule(runner) ) + { + if ( runner instanceof Suite ) + { + suites.add( (Suite) runner ); } + else + { + classes.add( (ParentRunner) runner ); + } + } + else + { + unscheduledRunners.add( runner ); } return runner; } @@ -225,6 +227,26 @@ public String describe() { } } + private ParentRunner wrapRunners( Collection runners ) throws InitializationError { + ArrayList runs = new ArrayList(); + for ( T runner : runners ) + { + if ( runner != null && hasChildren( runner ) ) + { + runs.add( runner ); + } + } + + return runs.isEmpty() ? null : new Suite( null, runs ) {}; + } + + private boolean hasChildren( Runner runner ) + { + Description description = runner.getDescription(); + Collection children = description == null ? null : description.getChildren(); + return children != null && !children.isEmpty(); + } + private ExecutorService createPool(int poolSize) { return poolSize < Integer.MAX_VALUE ? Executors.newFixedThreadPool(poolSize) : Executors.newCachedThreadPool(); } @@ -245,8 +267,8 @@ private boolean areSuitesAndClassesParallel() { private void populateChildrenFromSuites() { Filter filter = new SuiteFilter(); - for (Iterator it = suites.iterator(); it.hasNext();) { - Suite suite = it.next(); + for (Iterator it = suites.iterator(); it.hasNext();) { + ParentRunner suite = it.next(); try { suite.filter(filter); } catch (NoTestsRemainException e) { @@ -279,35 +301,76 @@ private Runner setSchedulers() throws InitializationError { ExecutorService commonPool = splitPool || poolSize == 0 ? null : createPool(poolSize); master = createMaster(commonPool, poolSize); - // a scheduler for parallel suites - final Scheduler suitesScheduler; - if (commonPool != null && parallelSuites > 0) { - Balancer balancer = BalancerFactory.createBalancerWithFairness(parallelSuites); - suitesScheduler = createScheduler(null, commonPool, true, balancer); - } else { - suitesScheduler = createScheduler(parallelSuites); + ParentRunner suiteSuites = wrapRunners( suites ); + if ( suiteSuites != null ) + { + // a scheduler for parallel suites + if ( commonPool != null && parallelSuites > 0 ) + { + Balancer balancer = BalancerFactory.createBalancerWithFairness( parallelSuites ); + suiteSuites.setScheduler( createScheduler( null, commonPool, true, balancer ) ); + } + else + { + suiteSuites.setScheduler( createScheduler( parallelSuites ) ); + } } - Suite suiteSuites = new RunnersSuite(suites); - suiteSuites.setScheduler(suitesScheduler); // schedulers for parallel classes - Suite suiteClasses = new RunnersSuite(classes); - ArrayList allSuites = new ArrayList(suites); - allSuites.addAll(nestedSuites); - allSuites.add(suiteClasses); - setSchedulers(allSuites, parallelClasses, commonPool); + ParentRunner suiteClasses = wrapRunners( classes ); + ArrayList allSuites = new ArrayList( suites ); + allSuites.addAll( nestedSuites ); + if ( suiteClasses != null ) + { + allSuites.add( suiteClasses ); + } + if ( !allSuites.isEmpty() ) + { + setSchedulers( allSuites, parallelClasses, commonPool ); + } // schedulers for parallel methods - ArrayList allClasses = new ArrayList(classes); - allClasses.addAll(nestedClasses); - setSchedulers(allClasses, parallelMethods, commonPool); + ArrayList allClasses = new ArrayList( classes ); + allClasses.addAll( nestedClasses ); + if ( !allClasses.isEmpty() ) + { + setSchedulers( allClasses, parallelMethods, commonPool ); + } // resulting runner for Computer#getSuite() scheduled by master scheduler - Suite all = new RunnersSuite(suiteSuites, suiteClasses); - all.setScheduler(master); + ParentRunner all = createFinalRunner( suiteSuites, suiteClasses ); + all.setScheduler( master ); return all; } + private ParentRunner createFinalRunner( Runner... runners ) throws InitializationError + { + ArrayList all = new ArrayList( unscheduledRunners ); + for ( Runner runner : runners ) + { + if ( runner != null ) + { + all.add( runner ); + } + } + + return new Suite( null, all ) + { + @Override + public void run( RunNotifier notifier ) + { + try + { + super.run( notifier ); + } + finally + { + afterRunQuietly(); + } + } + }; + } + private void setSchedulers(Iterable runners, int poolSize, ExecutorService commonPool) { if (commonPool != null) { Balancer concurrencyLimit = BalancerFactory.createBalancerWithFairness(poolSize); @@ -346,7 +409,7 @@ private Scheduler createScheduler(int poolSize) { } } - private boolean canUse(Runner runner) { + private boolean canSchedule(Runner runner) { return !(runner instanceof ErrorReportingRunner) && runner instanceof ParentRunner; } } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java index d4f49fab91..7b287e5314 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java @@ -180,7 +180,7 @@ protected void logQuietly(String msg) { * of descriptions of those tasks which have started prior to this call. *

    * This scheduler and other registered schedulers will shutdown, see {@link #register(Scheduler)}. - * If shutdownNow is set, waiting methods will cancel via {@link Thread#interrupt}. + * If shutdownNow is set, waiting methods will be interrupted via {@link Thread#interrupt}. * * @param shutdownNow if true interrupts waiting methods * @return collection of descriptions started before shutting down diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/ParallelComputerFactoryTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/ParallelComputerFactoryTest.java new file mode 100644 index 0000000000..90d773e63b --- /dev/null +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/ParallelComputerFactoryTest.java @@ -0,0 +1,907 @@ +package org.apache.maven.surefire.junitcore; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.surefire.testset.TestSetFailedException; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.theories.DataPoint; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; + +import java.util.Properties; + +import static org.apache.maven.surefire.junitcore.ParallelComputerFactory.*; +import static org.apache.maven.surefire.junitcore.JUnitCoreParameters.*; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.*; + +/** + * Testing an algorithm in {@link ParallelComputerFactory} which configures + * allocated thread resources in ParallelComputer by given {@link JUnitCoreParameters}. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + * + * @see org.apache.maven.surefire.junitcore.ParallelComputerFactory + */ +@RunWith(Theories.class) +public final class ParallelComputerFactoryTest +{ + @Rule + public final ExpectedException exception = ExpectedException.none(); + + @DataPoint + public static final int CPU_1 = 1; + + @DataPoint + public static final int CPU_4 = 4; + + @BeforeClass + public static void beforeClass() + { + ParallelComputerFactory.overrideAvailableProcessors( 1 ); + } + + @AfterClass + public static void afterClass() + { + ParallelComputerFactory.setDefaultAvailableProcessors(); + } + + @Test + public void unknownParallel() throws TestSetFailedException + { + Properties properties = new Properties(); + exception.expect( TestSetFailedException.class ); + resolveConcurrency( new JUnitCoreParameters( properties ) ); + } + + @Test + public void unknownThreadCountSuites() throws TestSetFailedException + { + JUnitCoreParameters params = new JUnitCoreParameters( parallel( "suites" ) ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + exception.expect( TestSetFailedException.class ); + resolveConcurrency( params ); + } + + @Test + public void unknownThreadCountClasses() throws TestSetFailedException + { + JUnitCoreParameters params = new JUnitCoreParameters( parallel( "classes" ) ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + exception.expect( TestSetFailedException.class ); + resolveConcurrency( params ); + } + + @Test + public void unknownThreadCountMethods() throws TestSetFailedException + { + JUnitCoreParameters params = new JUnitCoreParameters( parallel( "methods" ) ); + assertFalse( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + exception.expect( TestSetFailedException.class ); + resolveConcurrency( params ); + } + + @Test + public void unknownThreadCountBoth() throws TestSetFailedException + { + JUnitCoreParameters params = new JUnitCoreParameters( parallel( "both" ) ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + exception.expect( TestSetFailedException.class ); + resolveConcurrency( params ); + } + + @Test + public void unknownThreadCountAll() throws TestSetFailedException + { + JUnitCoreParameters params = new JUnitCoreParameters( parallel( "all" ) ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + exception.expect( TestSetFailedException.class ); + resolveConcurrency( params ); + } + + @Test + public void unknownThreadCountSuitesAndClasses() throws TestSetFailedException + { + JUnitCoreParameters params = new JUnitCoreParameters( parallel( "suitesAndClasses" ) ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + exception.expect( TestSetFailedException.class ); + resolveConcurrency( params ); + } + + @Test + public void unknownThreadCountSuitesAndMethods() throws TestSetFailedException + { + JUnitCoreParameters params = new JUnitCoreParameters( parallel( "suitesAndMethods" ) ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + exception.expect( TestSetFailedException.class ); + resolveConcurrency( params ); + } + + @Test + public void unknownThreadCountClassesAndMethods() throws TestSetFailedException + { + JUnitCoreParameters params = new JUnitCoreParameters( parallel( "classesAndMethods" ) ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + exception.expect( TestSetFailedException.class ); + resolveConcurrency( params ); + } + + @Theory + public void useUnlimitedThreadsSuites( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suites" ); + properties.setProperty( USEUNLIMITEDTHREADS_KEY, "true" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( 0 ) ); + + properties.setProperty( THREADCOUNTSUITES_KEY, "5" ); + params = new JUnitCoreParameters( properties ); + concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 5 * cpu ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( 0 ) ); + } + + @Theory + public void useUnlimitedThreadsClasses( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "classes" ); + properties.setProperty( USEUNLIMITEDTHREADS_KEY, "true" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.methods, is( 0 ) ); + + properties.setProperty( THREADCOUNTCLASSES_KEY, "5" ); + params = new JUnitCoreParameters( properties ); + concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( 5 * cpu ) ); + assertThat( concurrency.methods, is( 0 ) ); + } + + @Theory + public void unlimitedThreadsMethods( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "methods" ); + properties.setProperty( USEUNLIMITEDTHREADS_KEY, "true" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + + properties.setProperty( THREADCOUNTMETHODS_KEY, "5" ); + params = new JUnitCoreParameters( properties ); + concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( 5 * cpu ) ); + } + + @Theory + public void unlimitedThreadsSuitesAndClasses( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suitesAndClasses" ); + properties.setProperty( USEUNLIMITEDTHREADS_KEY, "true" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.classes, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.methods, is( 0 ) ); + + properties.setProperty( THREADCOUNTSUITES_KEY, "5" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "15" ); + params = new JUnitCoreParameters( properties ); + concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 5 * cpu ) ); + assertThat( concurrency.classes, is( 15 * cpu ) ); + assertThat( concurrency.methods, is( 0 ) ); + } + + @Theory + public void unlimitedThreadsSuitesAndMethods( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suitesAndMethods" ); + properties.setProperty( USEUNLIMITEDTHREADS_KEY, "true" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + + properties.setProperty( THREADCOUNTSUITES_KEY, "5" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "15" ); + params = new JUnitCoreParameters( properties ); + concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 5 * cpu ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( 15 * cpu ) ); + } + + @Theory + public void unlimitedThreadsClassesAndMethods( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "classesAndMethods" ); + properties.setProperty( USEUNLIMITEDTHREADS_KEY, "true" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + + properties.setProperty( THREADCOUNTCLASSES_KEY, "5" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "15" ); + params = new JUnitCoreParameters( properties ); + concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( 5 * cpu ) ); + assertThat( concurrency.methods, is( 15 * cpu ) ); + } + + @Theory + public void unlimitedThreadsAll( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "all" ); + properties.setProperty( USEUNLIMITEDTHREADS_KEY, "true" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.classes, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + + properties.setProperty( THREADCOUNTSUITES_KEY, "5" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "15" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "30" ); + params = new JUnitCoreParameters( properties ); + concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 5 * cpu ) ); + assertThat( concurrency.classes, is( 15 * cpu ) ); + assertThat( concurrency.methods, is( 30 * cpu ) ); + } + + @Theory + public void threadCountSuites( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suites" ); + properties.setProperty( THREADCOUNT_KEY, "3" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 0 ) ); + assertThat( concurrency.suites, is( 3 * cpu ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( 0 ) ); + } + + @Theory + public void threadCountClasses( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "classes" ); + properties.setProperty( THREADCOUNT_KEY, "3" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 0 ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( 3 * cpu ) ); + assertThat( concurrency.methods, is( 0 ) ); + } + + @Theory + public void threadCountMethods( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "methods" ); + properties.setProperty( THREADCOUNT_KEY, "3" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 0 ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( 3 * cpu ) ); + } + + @Theory + public void threadCountBoth( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "both" ); + properties.setProperty( THREADCOUNT_KEY, "3" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 3 * cpu ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( (int) ( ( 3d / 2 ) * cpu ) ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + } + + @Theory + public void threadCountClassesAndMethods( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "classesAndMethods" ); + properties.setProperty( THREADCOUNT_KEY, "3" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 3 * cpu ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( (int) ( ( 3d / 2 ) * cpu ) ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + } + + @Theory + public void threadCountSuitesAndMethods( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suitesAndMethods" ); + properties.setProperty( THREADCOUNT_KEY, "3" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 3 * cpu ) ); + assertThat( concurrency.suites, is( (int) ( ( 3d / 2 ) * cpu ) ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + } + + @Theory + public void threadCountSuitesAndClasses( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suitesAndClasses" ); + properties.setProperty( THREADCOUNT_KEY, "3" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 3 * cpu ) ); + assertThat( concurrency.suites, is( (int) ( ( 3d / 2 ) * cpu ) ) ); + assertThat( concurrency.classes, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.methods, is( 0 ) ); + } + + @Theory + public void threadCountAll( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "all" ); + properties.setProperty( THREADCOUNT_KEY, "3" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 3 * cpu ) ); + assertThat( concurrency.suites, is( cpu ) ); + assertThat( concurrency.classes, is( cpu ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + } + + @Theory + public void everyThreadCountSuitesAndClasses( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suitesAndClasses" ); + properties.setProperty( THREADCOUNT_KEY, "3" ); + // % percentage ratio + properties.setProperty( THREADCOUNTSUITES_KEY, "34" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "66" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is(3 * cpu) ); + int concurrentSuites = (int) ( 0.34d * concurrency.capacity ); + assertThat( concurrency.suites, is( concurrentSuites ) ); + assertThat( concurrency.classes, is( concurrency.capacity - concurrentSuites ) ); + assertThat( concurrency.methods, is( 0 ) ); + } + + @Theory + public void everyThreadCountSuitesAndMethods( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suitesAndMethods" ); + properties.setProperty( THREADCOUNT_KEY, "3" ); + // % percentage ratio + properties.setProperty( THREADCOUNTSUITES_KEY, "34" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "66" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 3 * cpu ) ); + int concurrentSuites = (int) ( 0.34d * concurrency.capacity ); + assertThat( concurrency.suites, is( concurrentSuites ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( concurrency.capacity - concurrentSuites ) ); + } + + @Theory + public void everyThreadCountClassesAndMethods( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "classesAndMethods" ); + properties.setProperty( THREADCOUNT_KEY, "3" ); + // % percentage ratio + properties.setProperty( THREADCOUNTCLASSES_KEY, "34" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "66" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 3 * cpu ) ); + assertThat( concurrency.suites, is( 0 ) ); + int concurrentClasses = (int) ( 0.34d * concurrency.capacity ); + assertThat( concurrency.classes, is( concurrentClasses ) ); + assertThat( concurrency.methods, is( concurrency.capacity - concurrentClasses ) ); + } + + @Theory + public void everyThreadCountAll( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "all" ); + properties.setProperty( THREADCOUNT_KEY, "3" ); + // % percentage ratio + properties.setProperty( THREADCOUNTSUITES_KEY, "17" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "34" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "49" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 3 * cpu ) ); + int concurrentSuites = (int) ( 0.17d * concurrency.capacity ); + int concurrentClasses = (int) ( 0.34d * concurrency.capacity ); + assertThat( concurrency.suites, is( concurrentSuites ) ); + assertThat( concurrency.classes, is( concurrentClasses ) ); + assertThat( concurrency.methods, is( concurrency.capacity - concurrentSuites - concurrentClasses ) ); + } + + @Theory + public void reusableThreadCountSuitesAndClasses( int cpu ) throws TestSetFailedException + { + // 4 * cpu to 5 * cpu threads to run test classes + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suitesAndClasses" ); + properties.setProperty( THREADCOUNT_KEY, "6" ); + properties.setProperty( THREADCOUNTSUITES_KEY, "2" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 6 * cpu ) ); + assertThat( concurrency.suites, is( 2 * cpu ) ); + assertThat( concurrency.classes, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.methods, is( 0 ) ); + } + + @Theory + public void reusableThreadCountSuitesAndMethods( int cpu ) throws TestSetFailedException + { + // 4 * cpu to 5 * cpu threads to run test methods + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suitesAndMethods" ); + properties.setProperty( THREADCOUNT_KEY, "6" ); + properties.setProperty( THREADCOUNTSUITES_KEY, "2" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 6 * cpu ) ); + assertThat( concurrency.suites, is( 2 * cpu ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + } + + @Theory + public void reusableThreadCountClassesAndMethods( int cpu ) throws TestSetFailedException + { + // 4 * cpu to 5 * cpu threads to run test methods + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "classesAndMethods" ); + properties.setProperty( THREADCOUNT_KEY, "6" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "2" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 6 * cpu ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( 2 * cpu ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + } + + @Theory + public void reusableThreadCountAll( int cpu ) throws TestSetFailedException + { + // 8 * cpu to 13 * cpu threads to run test methods + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "all" ); + properties.setProperty( THREADCOUNT_KEY, "14" ); + properties.setProperty( THREADCOUNTSUITES_KEY, "2" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "4" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 14 * cpu ) ); + assertThat( concurrency.suites, is( 2 * cpu ) ); + assertThat( concurrency.classes, is( 4 * cpu ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + } + + @Theory + public void suites( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suites" ); + properties.setProperty( THREADCOUNTSUITES_KEY, "5" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 5 * cpu ) ); + assertThat( concurrency.suites, is( 5 * cpu ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( 0 ) ); + } + + @Theory + public void classes( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "classes" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "5" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 5 * cpu ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( 5 * cpu ) ); + assertThat( concurrency.methods, is( 0 ) ); + } + + @Theory + public void methods( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "methods" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "5" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 5 * cpu ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( 5 * cpu ) ); + } + + @Theory + public void suitesAndClasses( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + + properties.setProperty( PARALLEL_KEY, "suitesAndClasses" ); + properties.setProperty( THREADCOUNTSUITES_KEY, "5" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "15" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 20 * cpu ) ); + assertThat( concurrency.suites, is( 5 * cpu ) ); + assertThat( concurrency.classes, is( 15 * cpu ) ); + assertThat( concurrency.methods, is( 0 ) ); + + // Warning: this case works but is not enabled in AbstractSurefireMojo + // Instead use the 'useUnlimitedThreads' parameter. + properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suitesAndClasses" ); + properties.setProperty( THREADCOUNTSUITES_KEY, "5" ); + params = new JUnitCoreParameters( properties ); + concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertFalse( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 5 * cpu ) ); + assertThat( concurrency.classes, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.methods, is( 0 ) ); + } + + @Theory + public void suitesAndMethods( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + + properties.setProperty( PARALLEL_KEY, "suitesAndMethods" ); + properties.setProperty( THREADCOUNTSUITES_KEY, "5" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "15" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 20 * cpu ) ); + assertThat( concurrency.suites, is( 5 * cpu ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( 15 * cpu ) ); + + // Warning: this case works but is not enabled in AbstractSurefireMojo + // Instead use the 'useUnlimitedThreads' parameter. + properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "suitesAndMethods" ); + properties.setProperty( THREADCOUNTSUITES_KEY, "5" ); + params = new JUnitCoreParameters( properties ); + concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertFalse( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 5 * cpu ) ); + assertThat( concurrency.classes, is( 0 ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + } + + @Theory + public void classesAndMethods( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + + properties.setProperty( PARALLEL_KEY, "classesAndMethods" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "5" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "15" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 20 * cpu ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( 5 * cpu ) ); + assertThat( concurrency.methods, is( 15 * cpu ) ); + + // Warning: this case works but is not enabled in AbstractSurefireMojo + // Instead use the 'useUnlimitedThreads' parameter. + properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "classesAndMethods" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "5" ); + params = new JUnitCoreParameters( properties ); + concurrency = resolveConcurrency( params ); + assertFalse( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 0 ) ); + assertThat( concurrency.classes, is( 5 * cpu ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + } + + @Theory + public void all( int cpu ) throws TestSetFailedException + { + ParallelComputerFactory.overrideAvailableProcessors( cpu ); + Properties properties = new Properties(); + + properties.setProperty( PARALLEL_KEY, "all" ); + properties.setProperty( THREADCOUNTSUITES_KEY, "5" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "15" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "30" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + Concurrency concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( 50 * cpu ) ); + assertThat( concurrency.suites, is( 5 * cpu ) ); + assertThat( concurrency.classes, is( 15 * cpu ) ); + assertThat( concurrency.methods, is( 30 * cpu ) ); + + // Warning: these cases work but they are not enabled in AbstractSurefireMojo + // Instead use the 'useUnlimitedThreads' parameter. + properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "all" ); + properties.setProperty( THREADCOUNTSUITES_KEY, "5" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "15" ); + params = new JUnitCoreParameters( properties ); + concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( 5 * cpu ) ); + assertThat( concurrency.classes, is( 15 * cpu ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + + properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "all" ); + properties.setProperty( THREADCOUNTCLASSES_KEY, "15" ); + params = new JUnitCoreParameters( properties ); + concurrency = resolveConcurrency( params ); + assertTrue( params.isParallelSuites() ); + assertTrue( params.isParallelClasses() ); + assertTrue( params.isParallelMethod() ); + assertThat( concurrency.capacity, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.suites, is( Integer.MAX_VALUE ) ); + assertThat( concurrency.classes, is( 15 * cpu ) ); + assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); + } + + private static Properties parallel( String parallel ) + { + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, parallel ); + return properties; + } +} \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java index 4b55f888c5..c34056ac0a 100644 --- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java @@ -74,7 +74,7 @@ public void parallelMethodsReuseOneOrTwoThreads() { // and next thread may be reused from finished class, however the capacity is 3. parallelComputerBuilder.parallelMethods(3); - ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); Result result = new JUnitCore().run(computer, TestSuite.class); long timeSpent = runtime.stop(); @@ -102,7 +102,7 @@ public void suiteAndClassInOnePool() { parallelComputerBuilder.parallelClasses(5); parallelComputerBuilder.parallelMethods(3); - ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class); long timeSpent = runtime.stop(); @@ -126,7 +126,7 @@ public void onePoolWithUnlimitedParallelMethods() { parallelComputerBuilder.parallelClasses(4); parallelComputerBuilder.parallelMethods(); - ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class); long timeSpent = runtime.stop(); @@ -142,32 +142,33 @@ public void onePoolWithUnlimitedParallelMethods() { } @Test - public void underflowParallelism() { + public void underflowParallelism() + { ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); - parallelComputerBuilder.useOnePool(3); + parallelComputerBuilder.useOnePool( 3 ); // One thread because one suite: TestSuite. - parallelComputerBuilder.parallelSuites(5); + parallelComputerBuilder.parallelSuites( 5 ); // One thread because of the limitation which is bottleneck. - parallelComputerBuilder.parallelClasses(1); + parallelComputerBuilder.parallelClasses( 1 ); // One thread remains from '#useOnePool(3)'. - parallelComputerBuilder.parallelMethods(3); + parallelComputerBuilder.parallelMethods( 3 ); - ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); - Result result = new JUnitCore().run(computer, TestSuite.class); + ParallelComputerBuilder.PC computer = ( ParallelComputerBuilder.PC ) parallelComputerBuilder.buildComputer(); + Result result = new JUnitCore().run( computer, TestSuite.class ); long timeSpent = runtime.stop(); - assertThat(computer.suites.size(), is(1)); - assertThat(computer.classes.size(), is(0)); - assertThat(computer.nestedClasses.size(), is(2)); - assertThat(computer.nestedSuites.size(), is(0)); - assertFalse(computer.splitPool); - assertThat(computer.poolCapacity, is(3)); - assertTrue(result.wasSuccessful()); - assertThat(Class1.maxConcurrentMethods, is(1)); - assertThat(timeSpent, between(1950, 2250)); + assertThat( computer.suites.size(), is( 1 ) ); + assertThat( computer.classes.size(), is( 0 ) ); + assertThat( computer.nestedClasses.size(), is( 2 ) ); + assertThat( computer.nestedSuites.size(), is( 0 ) ); + assertFalse( computer.splitPool ); + assertThat( computer.poolCapacity, is( 3 ) ); + assertTrue( result.wasSuccessful() ); + assertThat( Class1.maxConcurrentMethods, is( 1 ) ); + assertThat( timeSpent, between( 1950, 2250 ) ); } @Test @@ -177,7 +178,7 @@ public void separatePoolsWithSuite() { parallelComputerBuilder.parallelClasses(5); parallelComputerBuilder.parallelMethods(3); - ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); Result result = new JUnitCore().run(computer, TestSuite.class); long timeSpent = runtime.stop(); @@ -202,7 +203,7 @@ public void separatePoolsWithSuiteAndClass() { // 6 methods altogether. // 2 groups with 3 threads. // Each group takes 0.5s. - ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class); long timeSpent = runtime.stop(); @@ -224,7 +225,7 @@ public void separatePoolsWithSuiteAndSequentialClasses() { parallelComputerBuilder.parallelClasses(1); parallelComputerBuilder.parallelMethods(3); - ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class); long timeSpent = runtime.stop(); @@ -246,7 +247,7 @@ Result run(final boolean useInterrupt) { parallelComputerBuilder.parallelClasses(3); parallelComputerBuilder.parallelMethods(3); - final ParallelComputerBuilder.ParallelComputer computer = parallelComputerBuilder.buildComputer(); + final ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); shutdownTask = new Runnable() { public void run() { Collection startedTests = computer.shutdown(useInterrupt); From bd6ad1baa8d375228d23ba230c89ffc64258ebb4 Mon Sep 17 00:00:00 2001 From: Tibor Digana Date: Thu, 15 Aug 2013 07:20:41 +0200 Subject: [PATCH 11/11] reformat code + IT + junit.apt.vm + fork-options-and-parllel-execution.apt.vm --- .../plugin/failsafe/IntegrationTestMojo.java | 18 +- .../plugin/surefire/AbstractSurefireMojo.java | 6 +- .../surefire/SurefireExecutionParameters.java | 8 +- .../booterclient/ChecksumCalculator.java | 5 + .../maven/plugin/surefire/SurefirePlugin.java | 18 +- ...fork-options-and-parallel-execution.apt.vm | 51 +- .../src/site/apt/examples/junit.apt.vm | 4 + .../maven/surefire/its/JUnit47ParallelIT.java | 495 ++++++++++++++++++ .../surefire/its/fixture/MavenLauncher.java | 5 + .../its/fixture/SurefireLauncher.java | 68 ++- .../test/resources/junit47-parallel/pom.xml | 33 ++ .../java/surefireparallel/Suite1Test.java | 39 ++ .../java/surefireparallel/Suite2Test.java | 39 ++ .../test/java/surefireparallel/TestClass.java | 53 ++ .../java/surefireparallel/Waiting1Test.java | 50 ++ .../java/surefireparallel/Waiting2Test.java | 50 ++ .../java/surefireparallel/Waiting3Test.java | 50 ++ .../java/surefireparallel/Waiting4Test.java | 50 ++ .../java/surefireparallel/Waiting5Test.java | 50 ++ .../java/surefireparallel/Waiting6Test.java | 50 ++ .../java/surefireparallel/Waiting7Test.java | 50 ++ .../java/surefireparallel/Waiting8Test.java | 50 ++ .../junitcore/JUnitCoreParameters.java | 57 +- .../surefire/junitcore/JUnitCoreWrapper.java | 167 ++---- .../junitcore/NonConcurrentRunListener.java | 6 +- .../junitcore/ParallelComputerFactory.java | 107 ++-- .../pc/AbstractThreadPoolStrategy.java | 80 ++- .../maven/surefire/junitcore/pc/Balancer.java | 5 +- .../junitcore/pc/BalancerFactory.java | 6 +- .../junitcore/pc/InvokerStrategy.java | 30 +- .../pc/NonSharedThreadPoolStrategy.java | 28 +- .../surefire/junitcore/pc/NullBalancer.java | 6 +- .../junitcore/pc/ParallelComputer.java | 117 ++++- .../junitcore/pc/ParallelComputerBuilder.java | 357 ++++++++----- .../surefire/junitcore/pc/Scheduler.java | 299 +++++++---- .../junitcore/pc/SchedulingStrategies.java | 28 +- .../junitcore/pc/SchedulingStrategy.java | 24 +- .../pc/SharedThreadPoolStrategy.java | 55 +- .../junitcore/pc/ThreadResourcesBalancer.java | 10 +- .../ParallelComputerFactoryTest.java | 256 +++++++-- .../junitcore/{pc => }/Stopwatch.java | 11 +- .../pc/ParallelComputerBuilderTest.java | 470 +++++++++-------- .../surefire/junitcore/pc/RangeMatcher.java | 12 +- .../pc/SchedulingStrategiesTest.java | 140 ++--- 44 files changed, 2608 insertions(+), 905 deletions(-) create mode 100644 surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/JUnit47ParallelIT.java create mode 100644 surefire-integration-tests/src/test/resources/junit47-parallel/pom.xml create mode 100644 surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Suite1Test.java create mode 100644 surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Suite2Test.java create mode 100644 surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/TestClass.java create mode 100644 surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting1Test.java create mode 100644 surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting2Test.java create mode 100644 surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting3Test.java create mode 100644 surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting4Test.java create mode 100644 surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting5Test.java create mode 100644 surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting6Test.java create mode 100644 surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting7Test.java create mode 100644 surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting8Test.java rename surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/{pc => }/Stopwatch.java (80%) diff --git a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java index 41278c3748..3329bcb5e1 100644 --- a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java +++ b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java @@ -140,24 +140,30 @@ public class IntegrationTestMojo /** * Stop executing queued parallel JUnit tests after a certain number of seconds. + *
    + * Example values: "3.5", "4"
    + *
    * If set to 0, wait forever, never timing out. * Makes sense with specified parallel different from "none". * * @since 2.16 */ @Parameter( property = "failsafe.parallel.timeout" ) - private int parallelTestsTimeoutInSeconds; + private double parallelTestsTimeoutInSeconds; /** * Stop executing queued parallel JUnit tests * and interrupt currently running tests after a certain number of seconds. + *
    + * Example values: "3.5", "4"
    + *
    * If set to 0, wait forever, never timing out. * Makes sense with specified parallel different from "none". * * @since 2.16 */ @Parameter( property = "failsafe.parallel.forcedTimeout" ) - private int parallelTestsTimeoutForcedInSeconds; + private double parallelTestsTimeoutForcedInSeconds; /** * A list of <include> elements specifying the tests (by pattern) that should be included in testing. When not @@ -454,19 +460,19 @@ public void setForkedProcessTimeoutInSeconds( int forkedProcessTimeoutInSeconds this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds; } - public int getParallelTestsTimeoutInSeconds() { + public double getParallelTestsTimeoutInSeconds() { return parallelTestsTimeoutInSeconds; } - public void setParallelTestsTimeoutInSeconds( int parallelTestsTimeoutInSeconds ) { + public void setParallelTestsTimeoutInSeconds( double parallelTestsTimeoutInSeconds ) { this.parallelTestsTimeoutInSeconds = parallelTestsTimeoutInSeconds; } - public int getParallelTestsTimeoutForcedInSeconds() { + public double getParallelTestsTimeoutForcedInSeconds() { return parallelTestsTimeoutForcedInSeconds; } - public void setParallelTestsTimeoutForcedInSeconds( int parallelTestsTimeoutForcedInSeconds ) { + public void setParallelTestsTimeoutForcedInSeconds( double parallelTestsTimeoutForcedInSeconds ) { this.parallelTestsTimeoutForcedInSeconds = parallelTestsTimeoutForcedInSeconds; } diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java index 5975d7d23e..d8b9f9dcca 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java @@ -1143,9 +1143,9 @@ private void convertJunitCoreParameters() throws MojoExecutionException getProperties().setProperty( ProviderParameterNames.THREADCOUNTCLASSES_PROP, Integer.toString( getThreadCountClasses() ) ); getProperties().setProperty( ProviderParameterNames.THREADCOUNTMETHODS_PROP, Integer.toString( getThreadCountMethods() ) ); getProperties().setProperty( ProviderParameterNames.PARALLEL_TIMEOUT_PROP, - Integer.toString( getParallelTestsTimeoutInSeconds() ) ); + Double.toString( getParallelTestsTimeoutInSeconds() ) ); getProperties().setProperty( ProviderParameterNames.PARALLEL_TIMEOUTFORCED_PROP, - Integer.toString( getParallelTestsTimeoutForcedInSeconds() ) ); + Double.toString( getParallelTestsTimeoutForcedInSeconds() ) ); String message = "parallel='" + usedParallel + '\'' + ", perCoreThreadCount=" + getPerCoreThreadCount() + ", threadCount=" @@ -2224,7 +2224,7 @@ void warnIfDefunctGroupsCombinations() { return; } - if ( junitArtifact != null && !junit47Compatible ) + if ( junitArtifact != null ) { throw new MojoFailureException( "groups/excludedGroups are specified but JUnit version on classpath" + " is too old to support groups. Check your dependency:tree to see if your project is picking up an old junit version" ); diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java index 0b5a1af431..22152f0b43 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java @@ -101,13 +101,13 @@ public interface SurefireExecutionParameters void setForkedProcessTimeoutInSeconds( int forkedProcessTimeoutInSeconds ); - int getParallelTestsTimeoutInSeconds(); + double getParallelTestsTimeoutInSeconds(); - void setParallelTestsTimeoutInSeconds( int parallelTestsTimeoutInSeconds ); + void setParallelTestsTimeoutInSeconds( double parallelTestsTimeoutInSeconds ); - int getParallelTestsTimeoutForcedInSeconds(); + double getParallelTestsTimeoutForcedInSeconds(); - void setParallelTestsTimeoutForcedInSeconds( int parallelTestsTimeoutForcedInSeconds ); + void setParallelTestsTimeoutForcedInSeconds( double parallelTestsTimeoutForcedInSeconds ); boolean isUseSystemClassLoader(); diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ChecksumCalculator.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ChecksumCalculator.java index 03cbd4cfb4..29fbf11958 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ChecksumCalculator.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ChecksumCalculator.java @@ -54,6 +54,11 @@ public void add( int value ) checksumItems.add( value ); } + public void add( double value ) + { + checksumItems.add( value ); + } + public void add( Map map ) { if ( map != null ) diff --git a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java index 189161b409..1477b23d51 100644 --- a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java +++ b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java @@ -122,24 +122,30 @@ public class SurefirePlugin /** * Stop executing queued parallel JUnit tests after a certain number of seconds. + *
    + * Example values: "3.5", "4"
    + *
    * If set to 0, wait forever, never timing out. * Makes sense with specified parallel different from "none". * * @since 2.16 */ @Parameter( property = "surefire.parallel.timeout" ) - private int parallelTestsTimeoutInSeconds; + private double parallelTestsTimeoutInSeconds; /** * Stop executing queued parallel JUnit tests * and interrupt currently running tests after a certain number of seconds. + *
    + * Example values: "3.5", "4"
    + *
    * If set to 0, wait forever, never timing out. * Makes sense with specified parallel different from "none". * * @since 2.16 */ @Parameter( property = "surefire.parallel.forcedTimeout" ) - private int parallelTestsTimeoutForcedInSeconds; + private double parallelTestsTimeoutForcedInSeconds; /** * A list of <include> elements specifying the tests (by pattern) that should be included in testing. When not @@ -447,19 +453,19 @@ public void setForkedProcessTimeoutInSeconds( int forkedProcessTimeoutInSeconds this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds; } - public int getParallelTestsTimeoutInSeconds() { + public double getParallelTestsTimeoutInSeconds() { return parallelTestsTimeoutInSeconds; } - public void setParallelTestsTimeoutInSeconds( int parallelTestsTimeoutInSeconds ) { + public void setParallelTestsTimeoutInSeconds( double parallelTestsTimeoutInSeconds ) { this.parallelTestsTimeoutInSeconds = parallelTestsTimeoutInSeconds; } - public int getParallelTestsTimeoutForcedInSeconds() { + public double getParallelTestsTimeoutForcedInSeconds() { return parallelTestsTimeoutForcedInSeconds; } - public void setParallelTestsTimeoutForcedInSeconds( int parallelTestsTimeoutForcedInSeconds ) { + public void setParallelTestsTimeoutForcedInSeconds( double parallelTestsTimeoutForcedInSeconds ) { this.parallelTestsTimeoutForcedInSeconds = parallelTestsTimeoutForcedInSeconds; } diff --git a/maven-surefire-plugin/src/site/apt/examples/fork-options-and-parallel-execution.apt.vm b/maven-surefire-plugin/src/site/apt/examples/fork-options-and-parallel-execution.apt.vm index 5e47b0dbbc..c6682bb2a5 100644 --- a/maven-surefire-plugin/src/site/apt/examples/fork-options-and-parallel-execution.apt.vm +++ b/maven-surefire-plugin/src/site/apt/examples/fork-options-and-parallel-execution.apt.vm @@ -42,17 +42,58 @@ Fork Options and Parallel Test Execution * Parallel Test Execution Basically, there are two ways in Surefire to achieve parallel test execution. + The most obvious one is by using the <<>> parameter. The possible values depend on the test provider used. For JUnit 4.7 and onwards, this may - be <<>>, <<>>, or <<>>. + be <<>>, <<>>, <<>>, <<>>, + <<>>, <<>>, <<>>, + <<>>. + As of surefire 2.16, the value "both" is deprecated but it still can be + used and behaves same as <<>>. See the example pages for {{{./junit.html#Running_tests_in_parallel}JUnit}} and {{{./testng.html#Running_tests_in_parallel}TestNG}} for details. - The of the parallelism is configured using the parameters - <<>>, and optionally <<>>, or - <<>>. - + The of the parallelism is configured using the following parameters. + The parameter <<>> declares the unlimited number of + threads. Unless <<>> is set to "true", the parameter + <<>> can be used with the optional parameter + <<>>. + The parameters <<>>, <<>> make sense with + thereinbefore value specified in the parameter <<>>. + + You can impose thread-count limitations on suites, classes or methods if you + configure some of the parameters <<>>, + <<>> or <<>>. + If the only <<>> is specified, the surefire attempts to estimate + thread-counts for suites, classes and methods and reuse the threads in favor + of parallel methods (possibly increasing concurrent methods). + + As an example with unlimited number of threads, there is maximum of three + concurrent threads to execute suites: + parallel = all, useUnlimitedThreads = true, threadCountSuites = 3. + + In the second example, the number of concurrent methods is not strictly + limited: + parallel = classesAndMethods, threadCount = 8, threadCountClasses = 3. + Here the number of parallel methods is varying from 5 to 7. + Similarily with parallel = all, but the sum of <<>> and + <<>> must not excit certain <<>> - 1. + Other combinations are possible with unspecified thread-count leaf. Make sure + that the leaf is last from the order suites-classes-methods in <<>>. + + In the third example the thread-counts represent a ratio, e.g. for + parallel = all, threadCount = 16, threadCountSuites = 2, + threadCountClasses = 3, threadCountMethods = 5. Thus the concurrent suites + will be 20%, concurrent classes 30%, and concurrent methods 50%. + + Finally, the <<>> and <<>> may not be + necessarily configured if the equivalent thread-counts are specified for the + value in <<>>. + + The surefire is always trying to reuse threads, optimize the thread-counts, + and prefers thread fairness. + <> with the <<>> option is: the concurrency happens within the same JVM process. That is efficient in terms of memory and execution time, but you may be more vulnerable towards race diff --git a/maven-surefire-plugin/src/site/apt/examples/junit.apt.vm b/maven-surefire-plugin/src/site/apt/examples/junit.apt.vm index 74e32cb1c0..980779a0ac 100644 --- a/maven-surefire-plugin/src/site/apt/examples/junit.apt.vm +++ b/maven-surefire-plugin/src/site/apt/examples/junit.apt.vm @@ -142,6 +142,10 @@ else This is particularly useful for slow tests that can have high concurrency. As of surefire 2.7, no additional dependencies are needed to use the full set of options with parallel. + As of surefire 2.16, new thread-count attributes are introduced, namely <<>>, <<>> and + <<>>. Additionally new attributes, <<>> and + <<>>, are used to shutdown the parallel execution after an elapsed timeout, and + the attribute "parallel" specifies new values. See also {{{./fork-options-and-parallel-execution.html}Fork Options and Parallel Test Execution}}. diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/JUnit47ParallelIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/JUnit47ParallelIT.java new file mode 100644 index 0000000000..e236e5487e --- /dev/null +++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/JUnit47ParallelIT.java @@ -0,0 +1,495 @@ +package org.apache.maven.surefire.its; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase; +import org.apache.maven.surefire.its.fixture.SurefireLauncher; +import org.junit.Test; + +/** + * Testing JUnitCoreWrapper with ParallelComputerBuilder. + * + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class JUnit47ParallelIT + extends SurefireJUnit4IntegrationTestCase +{ + + @Test + public void unknownThreadCountSuites() + { + unpack().parallelSuites().setTestToRun( "TestClass" ).failNever().executeTest().verifyTextInLog( + "Use threadCount or threadCountSuites > 0 or useUnlimitedThreads=true for parallel='suites'" ); + } + + @Test + public void unknownThreadCountClasses() + { + unpack().parallelClasses().setTestToRun( "TestClass" ).failNever().executeTest().verifyTextInLog( + "Use threadCount or threadCountClasses > 0 or useUnlimitedThreads=true for parallel='classes'" ); + } + + @Test + public void unknownThreadCountMethods() + { + unpack().parallelMethods().setTestToRun( "TestClass" ).failNever().executeTest().verifyTextInLog( + "Use threadCount or threadCountMethods > 0 or useUnlimitedThreads=true for parallel='methods'" ); + + } + + @Test + public void unknownThreadCountBoth() + { + unpack().parallelBoth().setTestToRun( "TestClass" ).failNever().executeTest().verifyTextInLog( + "Use useUnlimitedThreads=true, " + + "or only threadCount > 0, " + + "or (threadCountClasses > 0 and threadCountMethods > 0), " + + "or (threadCount > 0 and threadCountClasses > 0 and threadCountMethods > 0), " + + "or (threadCount > 0 and threadCountClasses > 0 and threadCount > threadCountClasses) " + + "for parallel='both' or parallel='classesAndMethods'" ); + } + + @Test + public void unknownThreadCountAll() + { + unpack().parallelAll().setTestToRun( "TestClass" ).failNever().executeTest().verifyTextInLog( + "Use useUnlimitedThreads=true, " + + "or only threadCount > 0, " + + "or (threadCountSuites > 0 and threadCountClasses > 0 and threadCountMethods > 0), " + + "or every thread-count is specified, " + + "or (threadCount > 0 and threadCountSuites > 0 and threadCountClasses > 0 " + + "and threadCount > threadCountSuites + threadCountClasses) " + + "for parallel='all'" ); + } + + @Test + public void unknownThreadCountSuitesAndClasses() + { + unpack().parallelSuitesAndClasses().setTestToRun( "TestClass" ).failNever().executeTest().verifyTextInLog( + "Use useUnlimitedThreads=true, " + + "or only threadCount > 0, " + + "or (threadCountSuites > 0 and threadCountClasses > 0), " + + "or (threadCount > 0 and threadCountSuites > 0 and threadCountClasses > 0) " + + "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) " + + "for parallel='suitesAndClasses' or 'both'" ); + } + + @Test + public void unknownThreadCountSuitesAndMethods() + { + unpack().parallelSuitesAndMethods().setTestToRun( "TestClass" ).failNever().executeTest().verifyTextInLog( + "Use useUnlimitedThreads=true, " + + "or only threadCount > 0, " + + "or (threadCountSuites > 0 and threadCountMethods > 0), " + + "or (threadCount > 0 and threadCountSuites > 0 and threadCountMethods > 0), " + + "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) " + + "for parallel='suitesAndMethods'" ); + } + + @Test + public void unknownThreadCountClassesAndMethods() + { + unpack().parallelClassesAndMethods().setTestToRun( "TestClass" ).failNever().executeTest().verifyTextInLog( + "Use useUnlimitedThreads=true, " + + "or only threadCount > 0, " + + "or (threadCountClasses > 0 and threadCountMethods > 0), " + + "or (threadCount > 0 and threadCountClasses > 0 and threadCountMethods > 0), " + + "or (threadCount > 0 and threadCountClasses > 0 and threadCount > threadCountClasses) " + + "for parallel='both' or parallel='classesAndMethods'" ); + } + + @Test + public void serial() + { + // takes 7.2 sec + unpack().setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void useUnlimitedThreadsSuites1() + { + // takes 3.6 sec + unpack().parallelSuites().useUnlimitedThreads().setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void useUnlimitedThreadsSuites2() + { + // takes 3.6 sec + unpack().parallelSuites().useUnlimitedThreads().threadCountSuites( 5 ).setTestToRun( "Suite*Test" ) + .executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void useUnlimitedThreadsClasses1() + { + // takes 1.8 sec + unpack().parallelClasses().useUnlimitedThreads().setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void useUnlimitedThreadsClasses2() + { + // takes 1.8 sec + unpack().parallelClasses().useUnlimitedThreads().threadCountClasses( 5 ).setTestToRun( "Suite*Test" ) + .executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void useUnlimitedThreadsMethods1() + { + // takes 2.4 sec + unpack().parallelMethods().useUnlimitedThreads().setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void useUnlimitedThreadsMethods2() + { + // takes 2.4 sec + unpack().parallelMethods().useUnlimitedThreads().threadCountMethods( 5 ).setTestToRun( "Suite*Test" ) + .executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void unlimitedThreadsSuitesAndClasses1() + { + // takes 0.9 sec + unpack().parallelSuitesAndClasses().useUnlimitedThreads().setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void unlimitedThreadsSuitesAndClasses2() + { + // takes 0.9 sec + // 1.8 sec with 4 parallel classes + unpack().parallelSuitesAndClasses().useUnlimitedThreads().threadCountSuites( 5 ).threadCountClasses( 15 ) + .setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void unlimitedThreadsSuitesAndMethods1() + { + // takes 1.2 sec + unpack().parallelSuitesAndMethods().useUnlimitedThreads().setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void unlimitedThreadsSuitesAndMethods2() + { + // takes 1.2 sec + unpack().parallelSuitesAndMethods().useUnlimitedThreads().threadCountSuites( 5 ).threadCountMethods( 15 ) + .setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void unlimitedThreadsClassesAndMethods1() + { + // takes 0.6 sec + unpack().parallelClassesAndMethods().useUnlimitedThreads().setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void unlimitedThreadsClassesAndMethods2() + { + // takes 0.6 sec + unpack().parallelClassesAndMethods().useUnlimitedThreads().threadCountClasses( 5 ).threadCountMethods( 15 ) + .setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void unlimitedThreadsAll1() + { + // takes 0.3 sec + unpack().parallelAll().useUnlimitedThreads().setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void unlimitedThreadsAll2() + { + // takes 0.3 sec + unpack().parallelAll().useUnlimitedThreads().threadCountSuites( 5 ).threadCountClasses( 15 ) + .threadCountMethods( 30 ).setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void threadCountSuites() + { + // takes 3.6 sec + unpack().parallelSuites().threadCount( 3 ).setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void threadCountClasses() + { + // takes 3.6 sec for single core + // takes 1.8 sec for double core + unpack().parallelClasses().threadCount( 3 ).setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void threadCountMethods() + { + // takes 2.4 sec + unpack().parallelMethods().threadCount( 3 ).setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void threadCountClassesAndMethodsOneCore() + { + // takes 4.8 sec + unpack().disablePerCoreThreadCount().parallelClassesAndMethods().threadCount( 3 ).setTestToRun( "Suite*Test" ) + .executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void threadCountClassesAndMethods() + { + // takes 2.4 sec for double core CPU + unpack().parallelClassesAndMethods().threadCount( 3 ).setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void threadCountSuitesAndMethods() + { + // usually 24 times 0.3 sec = 7.2 sec with one core CPU + // takes 1.8 sec for double core CPU + unpack().parallelSuitesAndMethods().threadCount( 3 ).setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void threadCountSuitesAndClasses() + { + unpack().parallelSuitesAndClasses().threadCount( 3 ).setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void threadCountAll() + { + unpack().parallelAll().threadCount( 3 ).setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void everyThreadCountSuitesAndClasses() + { + // takes 1.8 sec for double core CPU + unpack().parallelSuitesAndClasses().threadCount( 3 ).threadCountSuites( 34 ).threadCountClasses( 66 ) + .setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void everyThreadCountSuitesAndMethods() + { + // takes 1.8 sec for double core CPU + unpack().parallelSuitesAndMethods().threadCount( 3 ).threadCountSuites( 34 ).threadCountMethods( 66 ) + .setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void everyThreadCountClassesAndMethods() + { + // takes 1.8 sec for double core CPU + unpack().parallelClassesAndMethods().threadCount( 3 ).threadCountClasses( 34 ).threadCountMethods( 66 ) + .setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void everyThreadCountAll() + { + // takes 2.4 sec for double core CPU + unpack().parallelAll().threadCount( 3 ).threadCountSuites( 17 ).threadCountClasses( 34 ).threadCountMethods( + 49 ).setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void reusableThreadCountSuitesAndClasses() + { + // 4 * cpu to 5 * cpu threads to run test classes + // takes cca 1.8 sec + unpack().parallelSuitesAndClasses().disablePerCoreThreadCount().threadCount( 6 ).threadCountSuites( 2 ) + .setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void reusableThreadCountSuitesAndMethods() + { + // 4 * cpu to 5 * cpu threads to run test methods + // takes cca 1.8 sec + unpack().parallelSuitesAndMethods().disablePerCoreThreadCount().threadCount( 6 ).threadCountSuites( 2 ) + .setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void reusableThreadCountClassesAndMethods() + { + // 4 * cpu to 5 * cpu threads to run test methods + // takes cca 1.8 sec + unpack().parallelClassesAndMethods().disablePerCoreThreadCount().threadCount( 6 ).threadCountClasses( 2 ) + .setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void reusableThreadCountAll() + { + // 8 * cpu to 13 * cpu threads to run test methods + // takes 0.9 sec + unpack().parallelAll().disablePerCoreThreadCount().threadCount( 14 ).threadCountSuites( 2 ).threadCountClasses( + 4 ).setTestToRun( "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void suites() + { + // takes 3.6 sec + unpack().parallelSuites().threadCountSuites( 5 ).setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void classes() + { + // takes 1.8 sec on any CPU because the suites are running in a sequence + unpack().parallelClasses().threadCountClasses( 5 ).setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void methods() + { + // takes 2.4 sec on any CPU because every class has only three methods + // and the suites and classes are running in a sequence + unpack().parallelMethods().threadCountMethods( 5 ).setTestToRun( "Suite*Test" ).executeTest() + //.verifyErrorFree( 24 ) + ; + } + + @Test + public void suitesAndClasses() + { + // takes 0.9 sec + unpack().parallelSuitesAndClasses().threadCountSuites( 5 ).threadCountClasses( 15 ).setTestToRun( + "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void suitesAndMethods() + { + // takes 1.2 sec on any CPU + unpack().parallelSuitesAndMethods().threadCountSuites( 5 ).threadCountMethods( 15 ).setTestToRun( + "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void classesAndMethods() + { + // takes 0.6 sec on any CPU + unpack().parallelClassesAndMethods().threadCountClasses( 5 ).threadCountMethods( 15 ).setTestToRun( + "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void all() + { + // takes 0.3 sec on any CPU + unpack().parallelAll().threadCountSuites( 5 ).threadCountClasses( 15 ).threadCountMethods( 30 ).setTestToRun( + "Suite*Test" ).executeTest()//.verifyErrorFree( 24 ) + ; + } + + @Test + public void shutdown() + { + // executes for 2.5 sec until timeout has elapsed + unpack().parallelMethods().threadCountMethods( 2 ).parallelTestsTimeoutInSeconds( 2.5d ).setTestToRun( + "TestClass" ).failNever().executeTest().verifyTextInLog( + "The test run has finished abruptly after timeout of 2.5 seconds." ); + } + + @Test + public void forcedShutdown() + { + // executes for 2.5 sec until timeout has elapsed + unpack().parallelMethods().threadCountMethods( 2 ).parallelTestsTimeoutForcedInSeconds( 2.5d ).setTestToRun( + "TestClass" ).failNever().executeTest().verifyTextInLog( + "The test run has finished abruptly after timeout of 2.5 seconds." ); + } + + @Test + public void timeoutAndForcedShutdown() + { + // executes for one sec until timeout has elapsed + unpack().parallelMethods().threadCountMethods( 2 ).parallelTestsTimeoutInSeconds( 1 ) + .parallelTestsTimeoutForcedInSeconds( 2.5d ).setTestToRun( "TestClass" ).failNever().executeTest() + .verifyTextInLog( "The test run has finished abruptly after timeout of 1.0 seconds." ); + } + + private SurefireLauncher unpack() + { + return unpack( "junit47-parallel" ); + } +} \ No newline at end of file diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java index e0befccafa..8d4a38544d 100755 --- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java +++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java @@ -339,6 +339,11 @@ public MavenLauncher sysProp( String variable, int value ) return addGoal( "-D" + variable + "=" + value ); } + public MavenLauncher sysProp( String variable, double value ) + { + return addGoal( "-D" + variable + "=" + value ); + } + public MavenLauncher showExceptionMessages() { addCliOption( "-e" ); diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java index 7b65783800..3ac3e20bba 100755 --- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java +++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java @@ -313,14 +313,18 @@ protected String getSurefireVersion() return surefireVersion; } - public SurefireLauncher parallel( String parallel ) + public SurefireLauncher disablePerCoreThreadCount() { + mavenLauncher.sysProp( "perCoreThreadCount", false ); + return this; + } + public SurefireLauncher parallel( String parallel ) + { mavenLauncher.sysProp( "parallel", parallel ); return this; } - public SurefireLauncher parallelSuites() { return parallel( "suites" ); @@ -336,6 +340,66 @@ public SurefireLauncher parallelMethods() return parallel( "methods" ); } + public SurefireLauncher parallelBoth() + { + return parallel( "both" ); + } + + public SurefireLauncher parallelSuitesAndClasses() + { + return parallel( "suitesAndClasses" ); + } + + public SurefireLauncher parallelSuitesAndMethods() + { + return parallel( "suitesAndMethods" ); + } + + public SurefireLauncher parallelClassesAndMethods() + { + return parallel( "classesAndMethods" ); + } + + public SurefireLauncher parallelAll() + { + return parallel( "all" ); + } + + public SurefireLauncher useUnlimitedThreads() + { + mavenLauncher.sysProp( "useUnlimitedThreads", true ); + return this; + } + + public SurefireLauncher threadCountSuites( int count ) + { + mavenLauncher.sysProp( "threadCountSuites", count ); + return this; + } + + public SurefireLauncher threadCountClasses( int count ) + { + mavenLauncher.sysProp( "threadCountClasses", count ); + return this; + } + + public SurefireLauncher threadCountMethods( int count ) + { + mavenLauncher.sysProp( "threadCountMethods", count ); + return this; + } + + public SurefireLauncher parallelTestsTimeoutInSeconds( double timeout ) + { + mavenLauncher.sysProp( "surefire.parallel.timeout", timeout ); + return this; + } + + public SurefireLauncher parallelTestsTimeoutForcedInSeconds( double timeout ) + { + mavenLauncher.sysProp( "surefire.parallel.forcedTimeout", timeout ); + return this; + } public SurefireLauncher sysProp( String variable, String value ) { diff --git a/surefire-integration-tests/src/test/resources/junit47-parallel/pom.xml b/surefire-integration-tests/src/test/resources/junit47-parallel/pom.xml new file mode 100644 index 0000000000..d8de300e16 --- /dev/null +++ b/surefire-integration-tests/src/test/resources/junit47-parallel/pom.xml @@ -0,0 +1,33 @@ + + 4.0.0 + org.apache.maven.plugins.surefire + junit47-parallel + 1.0-SNAPSHOT + junit47-parallel + http://maven.apache.org + + + junit + junit + 4.8.1 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.version} + + + + diff --git a/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Suite1Test.java b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Suite1Test.java new file mode 100644 index 0000000000..99419fe04a --- /dev/null +++ b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Suite1Test.java @@ -0,0 +1,39 @@ +package surefireparallel; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +@RunWith( Suite.class ) +@Suite.SuiteClasses( + { + Waiting1Test.class, + Waiting2Test.class, + Waiting3Test.class, + Waiting4Test.class + }) +public class Suite1Test +{ +} diff --git a/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Suite2Test.java b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Suite2Test.java new file mode 100644 index 0000000000..32814ff724 --- /dev/null +++ b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Suite2Test.java @@ -0,0 +1,39 @@ +package surefireparallel; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +@RunWith( Suite.class ) +@Suite.SuiteClasses( + { + Waiting5Test.class, + Waiting6Test.class, + Waiting7Test.class, + Waiting8Test.class + }) +public class Suite2Test +{ +} diff --git a/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/TestClass.java b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/TestClass.java new file mode 100644 index 0000000000..3b1f8422f5 --- /dev/null +++ b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/TestClass.java @@ -0,0 +1,53 @@ +package surefireparallel; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runners.BlockJUnit4ClassRunner; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class TestClass +{ + @Test + public void a() + throws InterruptedException + { + Thread.sleep( 5000L ); + } + + @Test + public void b() + throws InterruptedException + { + Thread.sleep( 5000L ); + } + + @Test + public void c() + throws InterruptedException + { + Thread.sleep( 5000L ); + } +} \ No newline at end of file diff --git a/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting1Test.java b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting1Test.java new file mode 100644 index 0000000000..1d58841281 --- /dev/null +++ b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting1Test.java @@ -0,0 +1,50 @@ +package surefireparallel; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.Test; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class Waiting1Test +{ + @Test + public void a() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void b() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void c() + throws InterruptedException + { + Thread.sleep( 300L ); + } +} \ No newline at end of file diff --git a/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting2Test.java b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting2Test.java new file mode 100644 index 0000000000..55da77266d --- /dev/null +++ b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting2Test.java @@ -0,0 +1,50 @@ +package surefireparallel; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.Test; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class Waiting2Test +{ + @Test + public void a() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void b() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void c() + throws InterruptedException + { + Thread.sleep( 300L ); + } +} \ No newline at end of file diff --git a/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting3Test.java b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting3Test.java new file mode 100644 index 0000000000..5098fd452b --- /dev/null +++ b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting3Test.java @@ -0,0 +1,50 @@ +package surefireparallel; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.Test; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class Waiting3Test +{ + @Test + public void a() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void b() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void c() + throws InterruptedException + { + Thread.sleep( 300L ); + } +} \ No newline at end of file diff --git a/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting4Test.java b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting4Test.java new file mode 100644 index 0000000000..8418448636 --- /dev/null +++ b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting4Test.java @@ -0,0 +1,50 @@ +package surefireparallel; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.Test; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class Waiting4Test +{ + @Test + public void a() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void b() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void c() + throws InterruptedException + { + Thread.sleep( 300L ); + } +} \ No newline at end of file diff --git a/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting5Test.java b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting5Test.java new file mode 100644 index 0000000000..2b9916061b --- /dev/null +++ b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting5Test.java @@ -0,0 +1,50 @@ +package surefireparallel; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.Test; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class Waiting5Test +{ + @Test + public void a() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void b() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void c() + throws InterruptedException + { + Thread.sleep( 300L ); + } +} \ No newline at end of file diff --git a/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting6Test.java b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting6Test.java new file mode 100644 index 0000000000..9ae9c012aa --- /dev/null +++ b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting6Test.java @@ -0,0 +1,50 @@ +package surefireparallel; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.Test; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class Waiting6Test +{ + @Test + public void a() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void b() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void c() + throws InterruptedException + { + Thread.sleep( 300L ); + } +} \ No newline at end of file diff --git a/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting7Test.java b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting7Test.java new file mode 100644 index 0000000000..ca00a6af86 --- /dev/null +++ b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting7Test.java @@ -0,0 +1,50 @@ +package surefireparallel; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.Test; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class Waiting7Test +{ + @Test + public void a() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void b() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void c() + throws InterruptedException + { + Thread.sleep( 300L ); + } +} \ No newline at end of file diff --git a/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting8Test.java b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting8Test.java new file mode 100644 index 0000000000..96d1c668b3 --- /dev/null +++ b/surefire-integration-tests/src/test/resources/junit47-parallel/src/test/java/surefireparallel/Waiting8Test.java @@ -0,0 +1,50 @@ +package surefireparallel; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.Test; + +/** + * @author Tibor Digana (tibor17) + * @since 2.16 + */ +public class Waiting8Test +{ + @Test + public void a() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void b() + throws InterruptedException + { + Thread.sleep( 300L ); + } + + @Test + public void c() + throws InterruptedException + { + Thread.sleep( 300L ); + } +} \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java index 157ed582f1..8421751a43 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Properties; + import org.apache.maven.surefire.booter.ProviderParameterNames; /** @@ -29,24 +30,6 @@ */ class JUnitCoreParameters { - private final String parallel; - - private final Boolean perCoreThreadCount; - - private final int threadCount; - - private final int threadCountSuites; - - private final int threadCountClasses; - - private final int threadCountMethods; - - private final int parallelTestsTimeoutInSeconds; - - private final int parallelTestsTimeoutForcedInSeconds; - - private final Boolean useUnlimitedThreads; - public static final String PARALLEL_KEY = ProviderParameterNames.PARALLEL_PROP; public static final String PERCORETHREADCOUNT_KEY = "perCoreThreadCount"; @@ -65,6 +48,24 @@ class JUnitCoreParameters public static final String PARALLEL_TIMEOUTFORCED_KEY = ProviderParameterNames.PARALLEL_TIMEOUTFORCED_PROP; + private final String parallel; + + private final Boolean perCoreThreadCount; + + private final int threadCount; + + private final int threadCountSuites; + + private final int threadCountClasses; + + private final int threadCountMethods; + + private final double parallelTestsTimeoutInSeconds; + + private final double parallelTestsTimeoutForcedInSeconds; + + private final Boolean useUnlimitedThreads; + public JUnitCoreParameters( Properties properties ) { parallel = properties.getProperty( PARALLEL_KEY, "none" ).toLowerCase(); @@ -74,8 +75,10 @@ public JUnitCoreParameters( Properties properties ) threadCountClasses = Integer.valueOf( properties.getProperty( THREADCOUNTCLASSES_KEY, "0" ) ); threadCountSuites = Integer.valueOf( properties.getProperty( THREADCOUNTSUITES_KEY, "0" ) ); useUnlimitedThreads = Boolean.valueOf( properties.getProperty( USEUNLIMITEDTHREADS_KEY, "false" ) ); - parallelTestsTimeoutInSeconds = Integer.valueOf( properties.getProperty( PARALLEL_TIMEOUT_KEY, "0" ) ); - parallelTestsTimeoutForcedInSeconds = Integer.valueOf( properties.getProperty( PARALLEL_TIMEOUTFORCED_KEY, "0" ) ); + parallelTestsTimeoutInSeconds = + Math.max( Double.valueOf( properties.getProperty( PARALLEL_TIMEOUT_KEY, "0" ) ), 0 ); + parallelTestsTimeoutForcedInSeconds = + Math.max( Double.valueOf( properties.getProperty( PARALLEL_TIMEOUTFORCED_KEY, "0" ) ), 0 ); } private static Collection lowerCase( String... elements ) @@ -95,14 +98,14 @@ private boolean isAllParallel() public boolean isParallelMethod() { - return isAllParallel() - || lowerCase( "both", "methods", "suitesAndMethods", "classesAndMethods" ).contains( parallel ); + return isAllParallel() || lowerCase( "both", "methods", "suitesAndMethods", "classesAndMethods" ).contains( + parallel ); } public boolean isParallelClasses() { - return isAllParallel() - || lowerCase( "both", "classes", "suitesAndClasses", "classesAndMethods" ).contains( parallel ); + return isAllParallel() || lowerCase( "both", "classes", "suitesAndClasses", "classesAndMethods" ).contains( + parallel ); } public boolean isParallelSuites() @@ -149,12 +152,12 @@ public Boolean isUseUnlimitedThreads() return useUnlimitedThreads; } - public int getParallelTestsTimeoutInSeconds() + public double getParallelTestsTimeoutInSeconds() { return parallelTestsTimeoutInSeconds; } - public int getParallelTestsTimeoutForcedInSeconds() + public double getParallelTestsTimeoutForcedInSeconds() { return parallelTestsTimeoutForcedInSeconds; } @@ -174,6 +177,6 @@ public String toString() { return "parallel='" + parallel + '\'' + ", perCoreThreadCount=" + perCoreThreadCount + ", threadCount=" + threadCount + ", useUnlimitedThreads=" + useUnlimitedThreads + ", threadCountSuites=" + threadCountSuites - + ", threadCountClasses=" + threadCountClasses + ", threadCountMethods=" + threadCountMethods; + + ", threadCountClasses=" + threadCountClasses + ", threadCountMethods=" + threadCountMethods; } } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java index 45cf34c18c..e124f6e7dd 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java @@ -19,12 +19,7 @@ * under the License. */ -import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.TreeSet; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; import org.apache.maven.surefire.common.junit4.JUnit4RunListener; import org.apache.maven.surefire.junitcore.pc.ParallelComputer; @@ -32,7 +27,6 @@ import org.apache.maven.surefire.util.TestsToRun; import org.junit.runner.Computer; -import org.junit.runner.Description; import org.junit.runner.JUnitCore; import org.junit.runner.Request; import org.junit.runner.Result; @@ -49,51 +43,28 @@ class JUnitCoreWrapper { - private static class FilteringRequest - extends Request - { - private Runner filteredRunner; - - public FilteringRequest( Request req, Filter filter ) - { - try - { - Runner runner = req.getRunner(); - filter.apply( runner ); - filteredRunner = runner; - } - catch ( NoTestsRemainException e ) - { - filteredRunner = null; - } - } - - @Override - public Runner getRunner() - { - return filteredRunner; - } - } - public static void execute( TestsToRun testsToRun, JUnitCoreParameters jUnitCoreParameters, List listeners, Filter filter ) throws TestSetFailedException { - ComputerWrapper computerWrapper = createComputer( jUnitCoreParameters ); + Computer computer = createComputer( jUnitCoreParameters ); JUnitCore junitCore = createJUnitCore( listeners ); if ( testsToRun.allowEagerReading() ) { - executeEager( testsToRun, filter, computerWrapper.getComputer(), junitCore ); + executeEager( testsToRun, filter, computer, junitCore ); } else { - exeuteLazy( testsToRun, filter, computerWrapper.getComputer(), junitCore ); + exeuteLazy( testsToRun, filter, computer, junitCore ); } - String timeoutMessage = computerWrapper.describeElapsedTimeout(); - if ( timeoutMessage.length() != 0 ) + if ( computer instanceof ParallelComputer ) { - throw new TestSetFailedException( timeoutMessage ); + String timeoutMessage = ( (ParallelComputer) computer ).describeElapsedTimeout(); + if ( timeoutMessage.length() != 0 ) + { + throw new TestSetFailedException( timeoutMessage ); + } } } @@ -107,15 +78,15 @@ private static JUnitCore createJUnitCore( List listeners ) return junitCore; } - private static void executeEager(TestsToRun testsToRun, Filter filter, Computer computer, JUnitCore junitCore) - throws TestSetFailedException + private static void executeEager( TestsToRun testsToRun, Filter filter, Computer computer, JUnitCore junitCore ) + throws TestSetFailedException { Class[] tests = testsToRun.getLocatedClasses(); createRequestAndRun( filter, computer, junitCore, tests ); } - private static void exeuteLazy(TestsToRun testsToRun, Filter filter, Computer computer, JUnitCore junitCore) - throws TestSetFailedException + private static void exeuteLazy( TestsToRun testsToRun, Filter filter, Computer computer, JUnitCore junitCore ) + throws TestSetFailedException { // in order to support LazyTestsToRun, the iterator must be used for ( Class clazz : testsToRun ) @@ -124,8 +95,9 @@ private static void exeuteLazy(TestsToRun testsToRun, Filter filter, Computer co } } - private static void createRequestAndRun( Filter filter, Computer computer, JUnitCore junitCore, Class... classesToRun ) - throws TestSetFailedException + private static void createRequestAndRun( Filter filter, Computer computer, JUnitCore junitCore, + Class... classesToRun ) + throws TestSetFailedException { Request req = Request.classes( computer, classesToRun ); if ( filter != null ) @@ -142,110 +114,37 @@ private static void createRequestAndRun( Filter filter, Computer computer, JUnit JUnit4RunListener.rethrowAnyTestMechanismFailures( run ); } - private static ComputerWrapper createComputer( JUnitCoreParameters parameters ) + private static Computer createComputer( JUnitCoreParameters parameters ) throws TestSetFailedException { - return parameters.isNoThreading() ? new ComputerWrapper( Computer.serial() ) : createParallelComputer( parameters ); + return parameters.isNoThreading() + ? Computer.serial() + : ParallelComputerFactory.createParallelComputer( parameters ); } - private static ComputerWrapper createParallelComputer( JUnitCoreParameters parameters ) - throws TestSetFailedException - { - ParallelComputer pc = ParallelComputerFactory.createParallelComputer( parameters ); - - int timeout = parameters.getParallelTestsTimeoutInSeconds(); - - int timeoutForced = parameters.getParallelTestsTimeoutForcedInSeconds(); - - Future> testsBeforeShutdown = - timeout > 0 ? pc.scheduleShutdown( timeout, TimeUnit.SECONDS ) : null; - - Future> testsBeforeForcedShutdown = - timeoutForced > 0 ? pc.scheduleForcedShutdown( timeoutForced, TimeUnit.SECONDS ) : null; - - return new ComputerWrapper( pc, timeout, testsBeforeShutdown, timeoutForced, testsBeforeForcedShutdown ); - } - - private static class ComputerWrapper + private static class FilteringRequest + extends Request { - private final Computer computer; - private final int timeout; - private final int timeoutForced; - private final Future> testsBeforeShutdown; - private final Future> testsBeforeForcedShutdown; - - ComputerWrapper( Computer computer ) - { - this( computer, 0, null, 0, null ); - } - - ComputerWrapper( Computer computer, - int timeout, Future> testsBeforeShutdown, - int timeoutForced, Future> testsBeforeForcedShutdown ) - { - this.computer = computer; - this.timeout = timeout; - this.testsBeforeShutdown = testsBeforeShutdown; - this.timeoutForced = timeoutForced; - this.testsBeforeForcedShutdown = testsBeforeForcedShutdown; - } - - Computer getComputer() - { - return computer; - } + private Runner filteredRunner; - String describeElapsedTimeout() throws TestSetFailedException + public FilteringRequest( Request req, Filter filter ) { - TreeSet executedTests = new TreeSet(); - if ( timeout > 0 ) - { - executedTests.addAll( printShutdownHook( testsBeforeShutdown ) ); - } - - if ( timeoutForced > 0 ) + try { - executedTests.addAll( printShutdownHook( testsBeforeForcedShutdown ) ); + Runner runner = req.getRunner(); + filter.apply( runner ); + filteredRunner = runner; } - - StringBuilder msg = new StringBuilder(); - if ( !executedTests.isEmpty() ) + catch ( NoTestsRemainException e ) { - msg.append( "The test run has finished abruptly after timeout of " ); - msg.append( Math.min( timeout, timeoutForced ) ); - msg.append( " seconds.\n" ); - msg.append( "These tests were executed in prior of the shutdown operation:\n" ); - for ( String executedTest : executedTests ) - { - msg.append( executedTest ).append( "\n" ); - } + filteredRunner = null; } - return msg.toString(); } - static Collection printShutdownHook( Future> future ) - throws TestSetFailedException + @Override + public Runner getRunner() { - if ( !future.isCancelled() && future.isDone() ) - { - try - { - TreeSet executedTests = new TreeSet(); - for ( Description executedTest : future.get() ) - { - if ( executedTest != null && executedTest.getDisplayName() != null ) - { - executedTests.add( executedTest.getDisplayName() ); - } - } - return executedTests; - } - catch ( Exception e ) - { - throw new TestSetFailedException( e ); - } - } - return Collections.emptySet(); + return filteredRunner; } } } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/NonConcurrentRunListener.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/NonConcurrentRunListener.java index dcf861512e..db2ff05c61 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/NonConcurrentRunListener.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/NonConcurrentRunListener.java @@ -76,11 +76,11 @@ protected SimpleReportEntry createReportEntryForTestSet( Description description public void testStarted( Description description ) throws Exception { - finishLastTestSetIfNeccessary( description ); + finishLastTestSetIfNecessary( description ); super.testStarted( description ); } - private void finishLastTestSetIfNeccessary( Description description ) + private void finishLastTestSetIfNecessary( Description description ) { if ( describesNewTestSet( description ) ) { @@ -125,7 +125,7 @@ public void testFinished( Description description ) public void testIgnored( Description description ) throws Exception { - finishLastTestSetIfNeccessary( description ); + finishLastTestSetIfNecessary( description ); super.testIgnored( description ); this.lastFinishedDescription = description; diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ParallelComputerFactory.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ParallelComputerFactory.java index 3937cd4855..d7fe2d149e 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ParallelComputerFactory.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ParallelComputerFactory.java @@ -23,27 +23,24 @@ import org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder; import org.apache.maven.surefire.testset.TestSetFailedException; +import java.util.concurrent.TimeUnit; + /** - * An algorithm which configures {@link ParallelComputer} with allocated thread resources by given {@link JUnitCoreParameters}. + * An algorithm which configures {@link ParallelComputer} with allocated thread resources by given + * {@link JUnitCoreParameters}. * The AbstractSurefireMojo has to provide correct combinations of thread-counts and parallel. * * @author Tibor Digana (tibor17) - * @since 2.16 - * * @see org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder + * @since 2.16 */ final class ParallelComputerFactory { private static int availableProcessors = Runtime.getRuntime().availableProcessors(); - static class Concurrency - { - int suites, classes, methods, capacity; - } - private ParallelComputerFactory() { - throw new IllegalStateException("Suppresses calling constructor, ensuring non-instantiability."); + throw new IllegalStateException( "Suppresses calling constructor, ensuring non-instantiability." ); } /* @@ -62,7 +59,8 @@ static void setDefaultAvailableProcessors() ParallelComputerFactory.availableProcessors = Runtime.getRuntime().availableProcessors(); } - static ParallelComputer createParallelComputer( JUnitCoreParameters params ) throws TestSetFailedException + static ParallelComputer createParallelComputer( JUnitCoreParameters params ) + throws TestSetFailedException { Concurrency concurrency = resolveConcurrency( params ); ParallelComputerBuilder builder = new ParallelComputerBuilder(); @@ -82,11 +80,14 @@ static ParallelComputer createParallelComputer( JUnitCoreParameters params ) thr resolveMethodsConcurrency( builder, concurrency.methods ); } + long timeout = secondsToNanos( params.getParallelTestsTimeoutInSeconds() ); + long timeoutForced = secondsToNanos( params.getParallelTestsTimeoutForcedInSeconds() ); resolveCapacity( builder, concurrency.capacity ); - return builder.buildComputer(); + return builder.buildComputer( timeout, timeoutForced, TimeUnit.NANOSECONDS ); } - static Concurrency resolveConcurrency( JUnitCoreParameters params ) throws TestSetFailedException + static Concurrency resolveConcurrency( JUnitCoreParameters params ) + throws TestSetFailedException { if ( !params.isAnyParallelitySelected() ) { @@ -96,9 +97,11 @@ static Concurrency resolveConcurrency( JUnitCoreParameters params ) throws TestS if ( !params.isUseUnlimitedThreads() && !hasThreadCount( params ) && !hasThreadCounts( params ) ) { throw new TestSetFailedException( "Unspecified thread-count(s). " + - "See the parameters " + JUnitCoreParameters.USEUNLIMITEDTHREADS_KEY + ", " - + JUnitCoreParameters.THREADCOUNT_KEY + ", " + JUnitCoreParameters.THREADCOUNTSUITES_KEY + ", " - + JUnitCoreParameters.THREADCOUNTCLASSES_KEY + ", " + JUnitCoreParameters.THREADCOUNTMETHODS_KEY + "."); + "See the parameters " + JUnitCoreParameters.USEUNLIMITEDTHREADS_KEY + + ", " + JUnitCoreParameters.THREADCOUNT_KEY + ", " + + JUnitCoreParameters.THREADCOUNTSUITES_KEY + ", " + + JUnitCoreParameters.THREADCOUNTCLASSES_KEY + ", " + + JUnitCoreParameters.THREADCOUNTMETHODS_KEY + "." ); } if ( params.isUseUnlimitedThreads() ) @@ -111,9 +114,9 @@ static Concurrency resolveConcurrency( JUnitCoreParameters params ) throws TestS { if ( hasThreadCounts( params ) ) { - return isLeafUnspecified( params ) ? - concurrencyFromAllThreadCountsButUnspecifiedLeafCount( params ) : - concurrencyFromAllThreadCounts( params ); + return isLeafUnspecified( params ) + ? concurrencyFromAllThreadCountsButUnspecifiedLeafCount( params ) + : concurrencyFromAllThreadCounts( params ); } else { @@ -127,6 +130,12 @@ static Concurrency resolveConcurrency( JUnitCoreParameters params ) throws TestS } } + private static long secondsToNanos( double seconds ) + { + double nanos = seconds > 0 ? seconds * 1E9 : 0; + return Double.isInfinite( nanos ) || nanos >= Long.MAX_VALUE ? 0 : (long) nanos; + } + private static void resolveSuitesConcurrency( ParallelComputerBuilder builder, int concurrency ) { if ( concurrency > 0 ) @@ -242,14 +251,14 @@ private static Concurrency concurrencyFromAllThreadCounts( JUnitCoreParameters p concurrency.capacity = params.getThreadCount(); double all = sumThreadCounts( concurrency ); - concurrency.suites = params.isParallelSuites() ? - multiplyByCoreCount( params, concurrency.capacity * ( concurrency.suites / all ) ) : 0; + concurrency.suites = params.isParallelSuites() ? multiplyByCoreCount( params, concurrency.capacity * ( + concurrency.suites / all ) ) : 0; - concurrency.classes = params.isParallelClasses() ? - multiplyByCoreCount( params, concurrency.capacity * ( concurrency.classes / all ) ) : 0; + concurrency.classes = params.isParallelClasses() ? multiplyByCoreCount( params, concurrency.capacity * ( + concurrency.classes / all ) ) : 0; - concurrency.methods = params.isParallelMethod() ? - multiplyByCoreCount( params, concurrency.capacity * ( concurrency.methods / all ) ) : 0; + concurrency.methods = params.isParallelMethod() ? multiplyByCoreCount( params, concurrency.capacity * ( + concurrency.methods / all ) ) : 0; concurrency.capacity = multiplyByCoreCount( params, concurrency.capacity ); adjustPrecisionInLeaf( params, concurrency ); @@ -261,7 +270,7 @@ private static Concurrency concurrencyFromThreadCounts( JUnitCoreParameters para Concurrency concurrency = new Concurrency(); concurrency.suites = params.isParallelSuites() ? threadCountSuites( params ) : 0; concurrency.classes = params.isParallelClasses() ? threadCountClasses( params ) : 0; - concurrency.methods = params.isParallelMethod() ? threadCountMethods( params ) : 0 ; + concurrency.methods = params.isParallelMethod() ? threadCountMethods( params ) : 0; concurrency.capacity = (int) Math.min( sumThreadCounts( concurrency ), Integer.MAX_VALUE ); return concurrency; } @@ -269,9 +278,20 @@ private static Concurrency concurrencyFromThreadCounts( JUnitCoreParameters para private static int countParallelEntities( JUnitCoreParameters params ) { int count = 0; - if ( params.isParallelSuites() ) count++; - if ( params.isParallelClasses() ) count++; - if ( params.isParallelMethod() ) count++; + if ( params.isParallelSuites() ) + { + count++; + } + + if ( params.isParallelClasses() ) + { + count++; + } + + if ( params.isParallelMethod() ) + { + count++; + } return count; } @@ -301,14 +321,23 @@ else if ( params.isParallelClasses() ) private static void setLeafInfinite( JUnitCoreParameters params, Concurrency concurrency ) { - if ( params.isParallelMethod() ) concurrency.methods = Integer.MAX_VALUE; - else if ( params.isParallelClasses() ) concurrency.classes = Integer.MAX_VALUE; - else if ( params.isParallelSuites() ) concurrency.suites = Integer.MAX_VALUE; + if ( params.isParallelMethod() ) + { + concurrency.methods = Integer.MAX_VALUE; + } + else if ( params.isParallelClasses() ) + { + concurrency.classes = Integer.MAX_VALUE; + } + else if ( params.isParallelSuites() ) + { + concurrency.suites = Integer.MAX_VALUE; + } } private static boolean isLeafUnspecified( JUnitCoreParameters params ) { - int maskOfParallel = params.isParallelSuites() ? 4: 0; + int maskOfParallel = params.isParallelSuites() ? 4 : 0; maskOfParallel |= params.isParallelClasses() ? 2 : 0; maskOfParallel |= params.isParallelMethod() ? 1 : 0; @@ -333,11 +362,11 @@ private static double sumThreadCounts( Concurrency concurrency ) private static boolean hasThreadCounts( JUnitCoreParameters jUnitCoreParameters ) { return jUnitCoreParameters.getThreadCountSuites() > 0 || - jUnitCoreParameters.getThreadCountClasses() > 0 || - jUnitCoreParameters.getThreadCountMethods() > 0; + jUnitCoreParameters.getThreadCountClasses() > 0 || + jUnitCoreParameters.getThreadCountMethods() > 0; } - private static boolean hasThreadCount ( JUnitCoreParameters jUnitCoreParameters ) + private static boolean hasThreadCount( JUnitCoreParameters jUnitCoreParameters ) { return jUnitCoreParameters.getThreadCount() > 0; } @@ -360,9 +389,13 @@ private static int threadCountSuites( JUnitCoreParameters jUnitCoreParameters ) private static int multiplyByCoreCount( JUnitCoreParameters jUnitCoreParameters, double threadsPerCore ) { double numberOfThreads = - jUnitCoreParameters.isPerCoreThreadCount() ? - threadsPerCore * (double) availableProcessors : threadsPerCore; + jUnitCoreParameters.isPerCoreThreadCount() ? threadsPerCore * (double) availableProcessors : threadsPerCore; return numberOfThreads > 0 ? (int) Math.min( numberOfThreads, Integer.MAX_VALUE ) : Integer.MAX_VALUE; } + + static class Concurrency + { + int suites, classes, methods, capacity; + } } \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/AbstractThreadPoolStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/AbstractThreadPoolStrategy.java index fea07013ab..56621d50ba 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/AbstractThreadPoolStrategy.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/AbstractThreadPoolStrategy.java @@ -31,81 +31,103 @@ * depending if the thread pool is shared with other strategies or not. * * @author Tibor Digana (tibor17) - * @since 2.16 - * * @see SchedulingStrategy * @see SharedThreadPoolStrategy * @see NonSharedThreadPoolStrategy + * @since 2.16 */ -abstract class AbstractThreadPoolStrategy extends SchedulingStrategy { +abstract class AbstractThreadPoolStrategy + extends SchedulingStrategy +{ private final ExecutorService threadPool; + private final Collection> futureResults; - private final AtomicBoolean canSchedule = new AtomicBoolean(true); - AbstractThreadPoolStrategy(ExecutorService threadPool) { - this(threadPool, null); + private final AtomicBoolean canSchedule = new AtomicBoolean( true ); + + AbstractThreadPoolStrategy( ExecutorService threadPool ) + { + this( threadPool, null ); } - AbstractThreadPoolStrategy(ExecutorService threadPool, Collection> futureResults) { + AbstractThreadPoolStrategy( ExecutorService threadPool, Collection> futureResults ) + { this.threadPool = threadPool; this.futureResults = futureResults; } - protected final ExecutorService getThreadPool() { + protected final ExecutorService getThreadPool() + { return threadPool; } - protected final Collection> getFutureResults() { + protected final Collection> getFutureResults() + { return futureResults; } - protected final void disable() { - canSchedule.set(false); + protected final void disable() + { + canSchedule.set( false ); } @Override - public void schedule(Runnable task) { - if (canSchedule()) { - Future futureResult = threadPool.submit(task); - if (futureResults != null) { - futureResults.add(futureResult); + public void schedule( Runnable task ) + { + if ( canSchedule() ) + { + Future futureResult = threadPool.submit( task ); + if ( futureResults != null ) + { + futureResults.add( futureResult ); } } } @Override - protected boolean stop() { - boolean wasRunning = canSchedule.getAndSet(false); - if (threadPool.isShutdown()) { + protected boolean stop() + { + boolean wasRunning = canSchedule.getAndSet( false ); + if ( threadPool.isShutdown() ) + { wasRunning = false; - } else { + } + else + { threadPool.shutdown(); } return wasRunning; } @Override - protected boolean stopNow() { - boolean wasRunning = canSchedule.getAndSet(false); - if (threadPool.isShutdown()) { + protected boolean stopNow() + { + boolean wasRunning = canSchedule.getAndSet( false ); + if ( threadPool.isShutdown() ) + { wasRunning = false; - } else { + } + else + { threadPool.shutdownNow(); } return wasRunning; } @Override - protected void setDefaultShutdownHandler(Scheduler.ShutdownHandler handler) { - if (threadPool instanceof ThreadPoolExecutor) { + protected void setDefaultShutdownHandler( Scheduler.ShutdownHandler handler ) + { + if ( threadPool instanceof ThreadPoolExecutor ) + { ThreadPoolExecutor pool = (ThreadPoolExecutor) threadPool; - handler.setRejectedExecutionHandler(pool.getRejectedExecutionHandler()); - pool.setRejectedExecutionHandler(handler); + handler.setRejectedExecutionHandler( pool.getRejectedExecutionHandler() ); + pool.setRejectedExecutionHandler( handler ); } } @Override - public final boolean canSchedule() { + public final boolean canSchedule() + { return canSchedule.get(); } } \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java index 1b28309b5d..2277bd490b 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java @@ -22,7 +22,7 @@ /** * The Balancer controls the maximum of concurrent threads in the current Scheduler(s) and prevents * from own thread resources exhaustion if other group of schedulers share the same pool of threads. - *

    + *

    * If a permit is available, {@link #acquirePermit()} simply returns and a new test is scheduled * by {@link Scheduler#schedule(Runnable)} in the current runner. Otherwise waiting for a release. * One permit is released as soon as the child thread has finished. @@ -30,7 +30,8 @@ * @author Tibor Digana (tibor17) * @since 2.16 */ -public interface Balancer { +public interface Balancer +{ /** * Acquires a permit from this balancer, blocking until one is available. diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/BalancerFactory.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/BalancerFactory.java index e7db197497..e4c36e344a 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/BalancerFactory.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/BalancerFactory.java @@ -21,11 +21,11 @@ /** * @author Tibor Digana (tibor17) - * @since 2.16 - * * @see Balancer + * @since 2.16 */ -public class BalancerFactory { +public class BalancerFactory +{ private BalancerFactory() { } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/InvokerStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/InvokerStrategy.java index dc1e5b3fe4..29a6624368 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/InvokerStrategy.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/InvokerStrategy.java @@ -25,37 +25,45 @@ * The sequentially executing strategy in private package. * * @author Tibor Digana (tibor17) - * @since 2.16 - * * @see SchedulingStrategy + * @since 2.16 */ -final class InvokerStrategy extends SchedulingStrategy { - private final AtomicBoolean canSchedule = new AtomicBoolean(true); +final class InvokerStrategy + extends SchedulingStrategy +{ + private final AtomicBoolean canSchedule = new AtomicBoolean( true ); @Override - public void schedule(Runnable task) { - if (canSchedule()) { + public void schedule( Runnable task ) + { + if ( canSchedule() ) + { task.run(); } } @Override - protected boolean stop() { - return canSchedule.getAndSet(false); + protected boolean stop() + { + return canSchedule.getAndSet( false ); } @Override - public boolean hasSharedThreadPool() { + public boolean hasSharedThreadPool() + { return false; } @Override - public boolean canSchedule() { + public boolean canSchedule() + { return canSchedule.get(); } @Override - public boolean finished() throws InterruptedException { + public boolean finished() + throws InterruptedException + { return stop(); } } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NonSharedThreadPoolStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NonSharedThreadPoolStrategy.java index b463605f84..df80ad917b 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NonSharedThreadPoolStrategy.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NonSharedThreadPoolStrategy.java @@ -26,28 +26,36 @@ * Parallel strategy for non-shared thread pool in private package. * * @author Tibor Digana (tibor17) - * @since 2.16 - * * @see AbstractThreadPoolStrategy + * @since 2.16 */ -final class NonSharedThreadPoolStrategy extends AbstractThreadPoolStrategy { - NonSharedThreadPoolStrategy(ExecutorService threadPool) { - super(threadPool); +final class NonSharedThreadPoolStrategy + extends AbstractThreadPoolStrategy +{ + NonSharedThreadPoolStrategy( ExecutorService threadPool ) + { + super( threadPool ); } @Override - public boolean hasSharedThreadPool() { + public boolean hasSharedThreadPool() + { return false; } @Override - public boolean finished() throws InterruptedException { + public boolean finished() + throws InterruptedException + { boolean wasRunning = canSchedule(); getThreadPool().shutdown(); - try { - getThreadPool().awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + try + { + getThreadPool().awaitTermination( Long.MAX_VALUE, TimeUnit.NANOSECONDS ); return wasRunning; - } finally { + } + finally + { disable(); } } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NullBalancer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NullBalancer.java index eec375912b..56b95220b2 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NullBalancer.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NullBalancer.java @@ -23,11 +23,11 @@ * This balancer implements {@link Balancer} and does not do anything -no blocking operation. * * @author Tibor Digana (tibor17) - * @since 2.16 - * * @see Balancer + * @since 2.16 */ -final class NullBalancer implements Balancer +final class NullBalancer + implements Balancer { public boolean acquirePermit() { diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputer.java index ef4fc94267..242df2ee88 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputer.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputer.java @@ -19,10 +19,13 @@ * under the License. */ +import org.apache.maven.surefire.testset.TestSetFailedException; import org.junit.runner.Computer; import org.junit.runner.Description; import java.util.Collection; +import java.util.Collections; +import java.util.TreeSet; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -33,16 +36,77 @@ * ParallelComputer extends JUnit {@link Computer} and has a shutdown functionality. * * @author Tibor Digana (tibor17) - * @since 2.16 - * * @see ParallelComputerBuilder + * @since 2.16 */ -public abstract class ParallelComputer extends Computer +public abstract class ParallelComputer + extends Computer { + private final long timeoutNanos; + + private final long timeoutForcedNanos; + private ScheduledExecutorService shutdownScheduler; + private Future> testsBeforeShutdown; + + private Future> testsBeforeForcedShutdown; + + public ParallelComputer( long timeout, long timeoutForced, TimeUnit timeoutUnit ) + { + this.timeoutNanos = timeoutUnit.toNanos( timeout ); + this.timeoutForcedNanos = timeoutUnit.toNanos( timeoutForced ); + } + + private static long minTimeout( long timeout1, long timeout2 ) + { + if ( timeout1 == 0 ) + { + return timeout2; + } + else if ( timeout2 == 0 ) + { + return timeout1; + } + else + { + return Math.min( timeout1, timeout2 ); + } + } + + private static Collection printShutdownHook( Future> future ) + throws TestSetFailedException + { + if ( !future.isCancelled() && future.isDone() ) + { + try + { + TreeSet executedTests = new TreeSet(); + for ( Description executedTest : future.get() ) + { + if ( executedTest != null && executedTest.getDisplayName() != null ) + { + executedTests.add( executedTest.getDisplayName() ); + } + } + return executedTests; + } + catch ( Exception e ) + { + throw new TestSetFailedException( e ); + } + } + return Collections.emptySet(); + } + public abstract Collection shutdown( boolean shutdownNow ); + protected final void beforeRunQuietly() + { + testsBeforeShutdown = timeoutNanos > 0 ? scheduleShutdown() : null; + testsBeforeForcedShutdown = timeoutForcedNanos > 0 ? scheduleForcedShutdown() : null; + } + protected final void afterRunQuietly() { if ( shutdownScheduler != null ) @@ -51,14 +115,43 @@ protected final void afterRunQuietly() } } - public Future> scheduleShutdown( int timeout, TimeUnit unit ) + public String describeElapsedTimeout() + throws TestSetFailedException { - return getShutdownScheduler().schedule( createShutdownTask( false ), timeout, unit ); + TreeSet executedTests = new TreeSet(); + if ( testsBeforeShutdown != null ) + { + executedTests.addAll( printShutdownHook( testsBeforeShutdown ) ); + } + + if ( testsBeforeForcedShutdown != null ) + { + executedTests.addAll( printShutdownHook( testsBeforeForcedShutdown ) ); + } + + StringBuilder msg = new StringBuilder(); + if ( !executedTests.isEmpty() ) + { + msg.append( "The test run has finished abruptly after timeout of " ); + msg.append( nanosToSeconds( minTimeout( timeoutNanos, timeoutForcedNanos ) ) ); + msg.append( " seconds.\n" ); + msg.append( "These tests were executed in prior of the shutdown operation:\n" ); + for ( String executedTest : executedTests ) + { + msg.append( executedTest ).append( '\n' ); + } + } + return msg.toString(); } - public Future> scheduleForcedShutdown( int timeout, TimeUnit unit ) + private Future> scheduleShutdown() { - return getShutdownScheduler().schedule( createShutdownTask( true ), timeout, unit ); + return getShutdownScheduler().schedule( createShutdownTask( false ), timeoutNanos, TimeUnit.NANOSECONDS ); + } + + private Future> scheduleForcedShutdown() + { + return getShutdownScheduler().schedule( createShutdownTask( true ), timeoutForcedNanos, TimeUnit.NANOSECONDS ); } private ScheduledExecutorService getShutdownScheduler() @@ -74,10 +167,16 @@ private Callable> createShutdownTask( final boolean isFo { return new Callable>() { - public Collection call() throws Exception + public Collection call() + throws Exception { - return shutdown( isForced ); + return ParallelComputer.this.shutdown( isForced ); } }; } + + private double nanosToSeconds( long nanos ) + { + return (double) nanos / 1E9; + } } \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java index ef2c05e7e5..8e7f5b0de3 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java @@ -39,6 +39,7 @@ import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; /** * Executing suites, classes and methods with defined concurrency. In this example the threads which completed @@ -51,9 +52,11 @@ * new JUnitCore().run(computer, tests); * * Note that the type has always at least one thread even if unspecified. The capacity in - * {@link ParallelComputerBuilder#useOnePool(int)} must be greater than the number of concurrent suites and classes altogether. - *

    - * The Computer can be shutdown in a separate thread. Pending tests will be interrupted if the argument is true. + * {@link ParallelComputerBuilder#useOnePool(int)} must be greater than the number of concurrent suites and classes + * altogether. + *

    + * The Computer can be shutdown in a separate thread. Pending tests will be interrupted if the argument is + * true. *

      * computer.shutdown(true);
      * 
    @@ -61,33 +64,36 @@ * @author Tibor Digana (tibor17) * @since 2.16 */ -public class ParallelComputerBuilder { - private static enum Type { - SUITES, CLASSES, METHODS - } - +public class ParallelComputerBuilder +{ static final int TOTAL_POOL_SIZE_UNDEFINED = 0; - private final Map parallelGroups = new HashMap(3); + + private final Map parallelGroups = new HashMap( 3 ); + private boolean useSeparatePools; + private int totalPoolSize; /** * Calling {@link #useSeparatePools()}. */ - public ParallelComputerBuilder() { + public ParallelComputerBuilder() + { useSeparatePools(); - parallelGroups.put(Type.SUITES, 0); - parallelGroups.put(Type.CLASSES, 0); - parallelGroups.put(Type.METHODS, 0); + parallelGroups.put( Type.SUITES, 0 ); + parallelGroups.put( Type.CLASSES, 0 ); + parallelGroups.put( Type.METHODS, 0 ); } - public ParallelComputerBuilder useSeparatePools() { + public ParallelComputerBuilder useSeparatePools() + { totalPoolSize = TOTAL_POOL_SIZE_UNDEFINED; useSeparatePools = true; return this; } - public ParallelComputerBuilder useOnePool() { + public ParallelComputerBuilder useOnePool() + { totalPoolSize = TOTAL_POOL_SIZE_UNDEFINED; useSeparatePools = false; return this; @@ -95,99 +101,140 @@ public ParallelComputerBuilder useOnePool() { /** * @param totalPoolSize Pool size where suites, classes and methods are executed in parallel. - * If the totalPoolSize is {@link Integer#MAX_VALUE}, the pool capacity is not limited. + * If the totalPoolSize is {@link Integer#MAX_VALUE}, the pool capacity is not + * limited. * @throws IllegalArgumentException If totalPoolSize is < 1. */ - public ParallelComputerBuilder useOnePool(int totalPoolSize) { - if (totalPoolSize < 1) { - throw new IllegalArgumentException("Size of common pool is less than 1."); + public ParallelComputerBuilder useOnePool( int totalPoolSize ) + { + if ( totalPoolSize < 1 ) + { + throw new IllegalArgumentException( "Size of common pool is less than 1." ); } this.totalPoolSize = totalPoolSize; useSeparatePools = false; return this; } - public ParallelComputerBuilder parallelSuites() { - return parallel(Type.SUITES); + public ParallelComputerBuilder parallelSuites() + { + return parallel( Type.SUITES ); } - public ParallelComputerBuilder parallelSuites(int nThreads) { - return parallel(nThreads, Type.SUITES); + public ParallelComputerBuilder parallelSuites( int nThreads ) + { + return parallel( nThreads, Type.SUITES ); } - public ParallelComputerBuilder parallelClasses() { - return parallel(Type.CLASSES); + public ParallelComputerBuilder parallelClasses() + { + return parallel( Type.CLASSES ); } - public ParallelComputerBuilder parallelClasses(int nThreads) { - return parallel(nThreads, Type.CLASSES); + public ParallelComputerBuilder parallelClasses( int nThreads ) + { + return parallel( nThreads, Type.CLASSES ); } - public ParallelComputerBuilder parallelMethods() { - return parallel(Type.METHODS); + public ParallelComputerBuilder parallelMethods() + { + return parallel( Type.METHODS ); } - public ParallelComputerBuilder parallelMethods(int nThreads) { - return parallel(nThreads, Type.METHODS); + public ParallelComputerBuilder parallelMethods( int nThreads ) + { + return parallel( nThreads, Type.METHODS ); } - private ParallelComputerBuilder parallel(int nThreads, Type parallelType) { - if (nThreads < 0) { - throw new IllegalArgumentException("negative nThreads " + nThreads); + private ParallelComputerBuilder parallel( int nThreads, Type parallelType ) + { + if ( nThreads < 0 ) + { + throw new IllegalArgumentException( "negative nThreads " + nThreads ); } - if (parallelType == null) { - throw new NullPointerException("null parallelType"); + if ( parallelType == null ) + { + throw new NullPointerException( "null parallelType" ); } - parallelGroups.put(parallelType, nThreads); + parallelGroups.put( parallelType, nThreads ); return this; } - private ParallelComputerBuilder parallel(Type parallelType) { - return parallel(Integer.MAX_VALUE, parallelType); + private ParallelComputerBuilder parallel( Type parallelType ) + { + return parallel( Integer.MAX_VALUE, parallelType ); + } + + public ParallelComputer buildComputer() + { + return buildComputer( 0, 0, TimeUnit.NANOSECONDS ); } - public ParallelComputer buildComputer() { - return new PC(); + public ParallelComputer buildComputer( long timeout, long timeoutForced, TimeUnit timeUnit ) + { + return new PC( timeout, timeoutForced, timeUnit ); } - final class PC extends ParallelComputer + private static enum Type + { + SUITES, + CLASSES, + METHODS + } + + final class PC + extends ParallelComputer { final Collection suites = new LinkedHashSet(); + final Collection nestedSuites = new LinkedHashSet(); + final Collection classes = new LinkedHashSet(); + final Collection nestedClasses = new LinkedHashSet(); + final Collection unscheduledRunners = new LinkedHashSet(); + final int poolCapacity; + final boolean splitPool; + private final Map allGroups; + private volatile Scheduler master; - private PC() { - allGroups = new HashMap(ParallelComputerBuilder.this.parallelGroups); + private PC( long timeout, long timeoutForced, TimeUnit timeoutUnit ) + { + super( timeout, timeoutForced, timeoutUnit ); + allGroups = new HashMap( ParallelComputerBuilder.this.parallelGroups ); poolCapacity = ParallelComputerBuilder.this.totalPoolSize; splitPool = ParallelComputerBuilder.this.useSeparatePools; } @Override - public Collection shutdown(boolean shutdownNow) { + public Collection shutdown( boolean shutdownNow ) + { final Scheduler master = this.master; - return master == null ? Collections.emptyList() : master.shutdown(shutdownNow); + return master == null ? Collections.emptyList() : master.shutdown( shutdownNow ); } @Override - public Runner getSuite(RunnerBuilder builder, Class[] cls) throws InitializationError { - super.getSuite(builder, cls); + public Runner getSuite( RunnerBuilder builder, Class[] cls ) + throws InitializationError + { + super.getSuite( builder, cls ); populateChildrenFromSuites(); return setSchedulers(); } @Override - protected Runner getRunner( RunnerBuilder builder, Class testClass ) throws Throwable + protected Runner getRunner( RunnerBuilder builder, Class testClass ) + throws Throwable { Runner runner = super.getRunner( builder, testClass ); - if ( canSchedule(runner) ) + if ( canSchedule( runner ) ) { if ( runner instanceof Suite ) { @@ -205,29 +252,9 @@ protected Runner getRunner( RunnerBuilder builder, Class testClass ) throws T return runner; } - private class SuiteFilter extends Filter { - @Override - public boolean shouldRun(Description description) { - return true; - } - - @Override - public void apply(Object child) throws NoTestsRemainException { - super.apply(child); - if (child instanceof Suite) { - nestedSuites.add((Suite) child); - } else if (child instanceof ParentRunner) { - nestedClasses.add((ParentRunner) child); - } - } - - @Override - public String describe() { - return ""; - } - } - - private ParentRunner wrapRunners( Collection runners ) throws InitializationError { + private ParentRunner wrapRunners( Collection runners ) + throws InitializationError + { ArrayList runs = new ArrayList(); for ( T runner : runners ) { @@ -237,7 +264,9 @@ private ParentRunner wrapRunners( Collection runners ) thr } } - return runs.isEmpty() ? null : new Suite( null, runs ) {}; + return runs.isEmpty() ? null : new Suite( null, runs ) + { + }; } private boolean hasChildren( Runner runner ) @@ -247,59 +276,83 @@ private boolean hasChildren( Runner runner ) return children != null && !children.isEmpty(); } - private ExecutorService createPool(int poolSize) { - return poolSize < Integer.MAX_VALUE ? Executors.newFixedThreadPool(poolSize) : Executors.newCachedThreadPool(); + private ExecutorService createPool( int poolSize ) + { + return poolSize < Integer.MAX_VALUE + ? Executors.newFixedThreadPool( poolSize ) + : Executors.newCachedThreadPool(); } - private Scheduler createMaster(ExecutorService pool, int poolSize) { - if (!areSuitesAndClassesParallel() || poolSize <= 1) { - return new Scheduler(null, new InvokerStrategy()); - } else if (pool != null && poolSize == Integer.MAX_VALUE) { - return new Scheduler(null, new SharedThreadPoolStrategy(pool)); - } else { - return new Scheduler(null, SchedulingStrategies.createParallelStrategy(2)); + private Scheduler createMaster( ExecutorService pool, int poolSize ) + { + if ( !areSuitesAndClassesParallel() || poolSize <= 1 ) + { + return new Scheduler( null, new InvokerStrategy() ); + } + else if ( pool != null && poolSize == Integer.MAX_VALUE ) + { + return new Scheduler( null, new SharedThreadPoolStrategy( pool ) ); + } + else + { + return new Scheduler( null, SchedulingStrategies.createParallelStrategy( 2 ) ); } } - private boolean areSuitesAndClassesParallel() { - return !suites.isEmpty() && allGroups.get(Type.SUITES) > 0 && !classes.isEmpty() && allGroups.get(Type.CLASSES) > 0; + private boolean areSuitesAndClassesParallel() + { + return !suites.isEmpty() && allGroups.get( Type.SUITES ) > 0 && !classes.isEmpty() + && allGroups.get( Type.CLASSES ) > 0; } - private void populateChildrenFromSuites() { + private void populateChildrenFromSuites() + { Filter filter = new SuiteFilter(); - for (Iterator it = suites.iterator(); it.hasNext();) { + for ( Iterator it = suites.iterator(); it.hasNext(); ) + { ParentRunner suite = it.next(); - try { - suite.filter(filter); - } catch (NoTestsRemainException e) { + try + { + suite.filter( filter ); + } + catch ( NoTestsRemainException e ) + { it.remove(); } } } - private int totalPoolSize() { - if (poolCapacity == TOTAL_POOL_SIZE_UNDEFINED) { + private int totalPoolSize() + { + if ( poolCapacity == TOTAL_POOL_SIZE_UNDEFINED ) + { int total = 0; - for (int nThreads : allGroups.values()) { + for ( int nThreads : allGroups.values() ) + { total += nThreads; - if (total < 0) { + if ( total < 0 ) + { total = Integer.MAX_VALUE; break; } } return total; - } else { + } + else + { return poolCapacity; } } - private Runner setSchedulers() throws InitializationError { - int parallelSuites = allGroups.get(Type.SUITES); - int parallelClasses = allGroups.get(Type.CLASSES); - int parallelMethods = allGroups.get(Type.METHODS); + private Runner setSchedulers() + throws InitializationError + { + int parallelSuites = allGroups.get( Type.SUITES ); + int parallelClasses = allGroups.get( Type.CLASSES ); + int parallelMethods = allGroups.get( Type.METHODS ); int poolSize = totalPoolSize(); - ExecutorService commonPool = splitPool || poolSize == 0 ? null : createPool(poolSize); - master = createMaster(commonPool, poolSize); + ExecutorService commonPool = splitPool || poolSize == 0 ? null : createPool( poolSize ); + master = createMaster( commonPool, poolSize ); ParentRunner suiteSuites = wrapRunners( suites ); if ( suiteSuites != null ) @@ -343,7 +396,8 @@ private Runner setSchedulers() throws InitializationError { return all; } - private ParentRunner createFinalRunner( Runner... runners ) throws InitializationError + private ParentRunner createFinalRunner( Runner... runners ) + throws InitializationError { ArrayList all = new ArrayList( unscheduledRunners ); for ( Runner runner : runners ) @@ -361,6 +415,7 @@ public void run( RunNotifier notifier ) { try { + beforeRunQuietly(); super.run( notifier ); } finally @@ -371,46 +426,96 @@ public void run( RunNotifier notifier ) }; } - private void setSchedulers(Iterable runners, int poolSize, ExecutorService commonPool) { - if (commonPool != null) { - Balancer concurrencyLimit = BalancerFactory.createBalancerWithFairness(poolSize); + private void setSchedulers( Iterable runners, int poolSize, ExecutorService commonPool ) + { + if ( commonPool != null ) + { + Balancer concurrencyLimit = BalancerFactory.createBalancerWithFairness( poolSize ); boolean doParallel = poolSize > 0; - for (ParentRunner runner : runners) { - runner.setScheduler(createScheduler(runner.getDescription(), commonPool, doParallel, concurrencyLimit)); + for ( ParentRunner runner : runners ) + { + runner.setScheduler( + createScheduler( runner.getDescription(), commonPool, doParallel, concurrencyLimit ) ); } - } else { + } + else + { ExecutorService pool = null; - if (poolSize == Integer.MAX_VALUE) { + if ( poolSize == Integer.MAX_VALUE ) + { pool = Executors.newCachedThreadPool(); - } else if (poolSize > 0) { - pool = Executors.newFixedThreadPool(poolSize); + } + else if ( poolSize > 0 ) + { + pool = Executors.newFixedThreadPool( poolSize ); } boolean doParallel = pool != null; - for (ParentRunner runner : runners) { - runner.setScheduler(createScheduler(runner.getDescription(), pool, doParallel, - BalancerFactory.createInfinitePermitsBalancer())); + for ( ParentRunner runner : runners ) + { + runner.setScheduler( createScheduler( runner.getDescription(), pool, doParallel, + BalancerFactory.createInfinitePermitsBalancer() ) ); } } } - private Scheduler createScheduler(Description desc, ExecutorService pool, boolean doParallel, Balancer concurrency) { + private Scheduler createScheduler( Description desc, ExecutorService pool, boolean doParallel, + Balancer concurrency ) + { doParallel &= pool != null; - SchedulingStrategy strategy = doParallel ? new SharedThreadPoolStrategy(pool) : new InvokerStrategy(); - return new Scheduler(desc, master, strategy, concurrency); + SchedulingStrategy strategy = doParallel ? new SharedThreadPoolStrategy( pool ) : new InvokerStrategy(); + return new Scheduler( desc, master, strategy, concurrency ); } - private Scheduler createScheduler(int poolSize) { - if (poolSize == Integer.MAX_VALUE) { - return new Scheduler(null, master, SchedulingStrategies.createParallelStrategyUnbounded()); - } else if (poolSize == 0) { - return new Scheduler(null, master, new InvokerStrategy()); - } else { - return new Scheduler(null, master, SchedulingStrategies.createParallelStrategy(poolSize)); + private Scheduler createScheduler( int poolSize ) + { + if ( poolSize == Integer.MAX_VALUE ) + { + return new Scheduler( null, master, SchedulingStrategies.createParallelStrategyUnbounded() ); + } + else if ( poolSize == 0 ) + { + return new Scheduler( null, master, new InvokerStrategy() ); + } + else + { + return new Scheduler( null, master, SchedulingStrategies.createParallelStrategy( poolSize ) ); } } - private boolean canSchedule(Runner runner) { - return !(runner instanceof ErrorReportingRunner) && runner instanceof ParentRunner; + private boolean canSchedule( Runner runner ) + { + return !( runner instanceof ErrorReportingRunner ) && runner instanceof ParentRunner; + } + + private class SuiteFilter + extends Filter + { + @Override + public boolean shouldRun( Description description ) + { + return true; + } + + @Override + public void apply( Object child ) + throws NoTestsRemainException + { + super.apply( child ); + if ( child instanceof Suite ) + { + nestedSuites.add( (Suite) child ); + } + else if ( child instanceof ParentRunner ) + { + nestedClasses.add( (ParentRunner) child ); + } + } + + @Override + public String describe() + { + return ""; + } } } } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java index 7b287e5314..d3c41337b9 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Scheduler.java @@ -31,87 +31,100 @@ import java.util.concurrent.ThreadPoolExecutor; /** - * * Schedules tests, controls thread resources, awaiting tests and other schedulers finished, and * a master scheduler can shutdown slaves. - *

    + *

    * The scheduler objects should be first created (and wired) and set in runners * {@link org.junit.runners.ParentRunner#setScheduler(org.junit.runners.model.RunnerScheduler)}. - *

    + *

    * A new instance of scheduling strategy should be passed to the constructor of this scheduler. * * @author Tibor Digana (tibor17) * @since 2.16 */ -public class Scheduler implements RunnerScheduler { +public class Scheduler + implements RunnerScheduler +{ private final Balancer balancer; + private final SchedulingStrategy strategy; + private final Set slaves = new CopyOnWriteArraySet(); + private final Description description; + private volatile boolean shutdown = false; + private volatile boolean started = false; + private volatile Controller masterController; /** * Use e.g. parallel classes have own non-shared thread pool, and methods another pool. - *

    + *

    * You can use it with one infinite thread pool shared in strategies across all * suites, class runners, etc. */ - public Scheduler(Description description, SchedulingStrategy strategy) { - this(description, strategy, -1); + public Scheduler( Description description, SchedulingStrategy strategy ) + { + this( description, strategy, -1 ); } /** * Should be used if schedulers in parallel children and parent use one instance of bounded thread pool. - *

    + *

    * Set this scheduler in a e.g. one suite of classes, then every individual class runner should reference * {@link #Scheduler(org.junit.runner.Description, Scheduler, SchedulingStrategy)} * or {@link #Scheduler(org.junit.runner.Description, Scheduler, SchedulingStrategy, int)}. * * @param description description of current runner - * @param strategy scheduling strategy with a shared thread pool + * @param strategy scheduling strategy with a shared thread pool * @param concurrency determines maximum concurrent children scheduled a time via {@link #schedule(Runnable)} * @throws NullPointerException if null strategy */ - public Scheduler(Description description, SchedulingStrategy strategy, int concurrency) { - this(description, strategy, BalancerFactory.createBalancer(concurrency)); + public Scheduler( Description description, SchedulingStrategy strategy, int concurrency ) + { + this( description, strategy, BalancerFactory.createBalancer( concurrency ) ); } /** * New instances should be used by schedulers with limited concurrency by balancer * against other groups of schedulers. The schedulers share one pool. - *

    + *

    * Unlike in {@link #Scheduler(org.junit.runner.Description, SchedulingStrategy, int)} which was limiting * the concurrency of children of a runner where this scheduler was set, this balancer * is limiting the concurrency of all children in runners having schedulers created by this constructor. * * @param description description of current runner - * @param strategy scheduling strategy which may share threads with other strategy - * @param balancer determines maximum concurrent children scheduled a time via {@link #schedule(Runnable)} + * @param strategy scheduling strategy which may share threads with other strategy + * @param balancer determines maximum concurrent children scheduled a time via {@link #schedule(Runnable)} * @throws NullPointerException if null strategy or balancer */ - public Scheduler(Description description, SchedulingStrategy strategy, Balancer balancer) { - strategy.setDefaultShutdownHandler(newShutdownHandler()); + public Scheduler( Description description, SchedulingStrategy strategy, Balancer balancer ) + { + strategy.setDefaultShutdownHandler( newShutdownHandler() ); this.description = description; this.strategy = strategy; this.balancer = balancer; masterController = null; } + /** * Can be used by e.g. a runner having parallel classes in use case with parallel * suites, classes and methods sharing the same thread pool. * - * @param description description of current runner + * @param description description of current runner * @param masterScheduler scheduler sharing own threads with this slave - * @param strategy scheduling strategy for this scheduler - * @param balancer determines maximum concurrent children scheduled a time via {@link #schedule(Runnable)} + * @param strategy scheduling strategy for this scheduler + * @param balancer determines maximum concurrent children scheduled a time via {@link #schedule(Runnable)} * @throws NullPointerException if null masterScheduler, strategy or balancer */ - public Scheduler(Description description, Scheduler masterScheduler, SchedulingStrategy strategy, Balancer balancer) { - this(description, strategy, balancer); - strategy.setDefaultShutdownHandler(newShutdownHandler()); - masterScheduler.register(this); + public Scheduler( Description description, Scheduler masterScheduler, SchedulingStrategy strategy, + Balancer balancer ) + { + this( description, strategy, balancer ); + strategy.setDefaultShutdownHandler( newShutdownHandler() ); + masterScheduler.register( this ); } /** @@ -120,25 +133,29 @@ public Scheduler(Description description, Scheduler masterScheduler, SchedulingS * @see #Scheduler(org.junit.runner.Description, SchedulingStrategy) * @see #Scheduler(org.junit.runner.Description, SchedulingStrategy, int) */ - public Scheduler(Description description, Scheduler masterScheduler, SchedulingStrategy strategy, int concurrency) { - this(description, strategy, concurrency); - strategy.setDefaultShutdownHandler(newShutdownHandler()); - masterScheduler.register(this); + public Scheduler( Description description, Scheduler masterScheduler, SchedulingStrategy strategy, int concurrency ) + { + this( description, strategy, concurrency ); + strategy.setDefaultShutdownHandler( newShutdownHandler() ); + masterScheduler.register( this ); } /** * Should be used with individual pools on suites, classes and methods, see * {@link org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder#useSeparatePools()}. - *

    + *

    * Cached thread pool is infinite and can be always shared. */ - public Scheduler(Description description, Scheduler masterScheduler, SchedulingStrategy strategy) { - this(description, masterScheduler, strategy, 0); + public Scheduler( Description description, Scheduler masterScheduler, SchedulingStrategy strategy ) + { + this( description, masterScheduler, strategy, 0 ); } - private void setController(Controller masterController) { - if (masterController == null) { - throw new NullPointerException("null ExecutionController"); + private void setController( Controller masterController ) + { + if ( masterController == null ) + { + throw new NullPointerException( "null ExecutionController" ); } this.masterController = masterController; } @@ -147,14 +164,17 @@ private void setController(Controller masterController) { * @param slave a slave scheduler to register * @return true if successfully registered the slave. */ - private boolean register(Scheduler slave) { + private boolean register( Scheduler slave ) + { boolean canRegister = slave != null && slave != this; - if (canRegister) { - Controller controller = new Controller(slave); - canRegister = !slaves.contains(controller); - if (canRegister) { - slaves.add(controller); - slave.setController(controller); + if ( canRegister ) + { + Controller controller = new Controller( slave ); + canRegister = !slaves.contains( controller ); + if ( canRegister ) + { + slaves.add( controller ); + slave.setController( controller ); } } return canRegister; @@ -163,50 +183,65 @@ private boolean register(Scheduler slave) { /** * @return true if new tasks can be scheduled. */ - private boolean canSchedule() { - return !shutdown && (masterController == null || masterController.canSchedule()); + private boolean canSchedule() + { + return !shutdown && ( masterController == null || masterController.canSchedule() ); } - protected void logQuietly(Throwable t) { - t.printStackTrace(System.err); + protected void logQuietly( Throwable t ) + { + t.printStackTrace( System.err ); } - protected void logQuietly(String msg) { - System.err.println(msg); + protected void logQuietly( String msg ) + { + System.err.println( msg ); } /** * Attempts to stop all actively executing tasks and immediately returns a collection * of descriptions of those tasks which have started prior to this call. - *

    + *

    * This scheduler and other registered schedulers will shutdown, see {@link #register(Scheduler)}. * If shutdownNow is set, waiting methods will be interrupted via {@link Thread#interrupt}. * * @param shutdownNow if true interrupts waiting methods * @return collection of descriptions started before shutting down */ - public Collection shutdown(boolean shutdownNow) { + public Collection shutdown( boolean shutdownNow ) + { shutdown = true; ArrayList activeChildren = new ArrayList(); - if (started && description != null) { - activeChildren.add(description); + if ( started && description != null ) + { + activeChildren.add( description ); } - for (Controller slave : slaves) { - try { - activeChildren.addAll(slave.shutdown(shutdownNow)); - } catch (Throwable t) { - logQuietly(t); + for ( Controller slave : slaves ) + { + try + { + activeChildren.addAll( slave.shutdown( shutdownNow ) ); + } + catch ( Throwable t ) + { + logQuietly( t ); } } - try { + try + { balancer.releaseAllPermits(); - } finally { - if (shutdownNow) { + } + finally + { + if ( shutdownNow ) + { strategy.stopNow(); - } else { + } + else + { strategy.stop(); } } @@ -214,52 +249,82 @@ public Collection shutdown(boolean shutdownNow) { return activeChildren; } - protected void beforeExecute() { + protected void beforeExecute() + { } - protected void afterExecute() { + protected void afterExecute() + { } - public void schedule(Runnable childStatement) { - if (childStatement == null) { - logQuietly("cannot schedule null"); - } else if (canSchedule() && strategy.canSchedule()) { - try { - balancer.acquirePermit(); - Runnable task = wrapTask(childStatement); - strategy.schedule(task); - started = true; - } catch (RejectedExecutionException e) { - shutdown(false); - } catch (Throwable t) { + public void schedule( Runnable childStatement ) + { + if ( childStatement == null ) + { + logQuietly( "cannot schedule null" ); + } + else if ( canSchedule() && strategy.canSchedule() ) + { + try + { + boolean isNotInterrupted = balancer.acquirePermit(); + if ( isNotInterrupted && !shutdown ) + { + Runnable task = wrapTask( childStatement ); + strategy.schedule( task ); + started = true; + } + } + catch ( RejectedExecutionException e ) + { + shutdown( false ); + } + catch ( Throwable t ) + { balancer.releasePermit(); - logQuietly(t); + logQuietly( t ); } } } - public void finished() { - try { + public void finished() + { + try + { strategy.finished(); - } catch (InterruptedException e) { - logQuietly(e); - } finally { - for (Controller slave : slaves) { + } + catch ( InterruptedException e ) + { + logQuietly( e ); + } + finally + { + for ( Controller slave : slaves ) + { slave.awaitFinishedQuietly(); } } } - private Runnable wrapTask(final Runnable task) { - return new Runnable() { - public void run() { - try { + private Runnable wrapTask( final Runnable task ) + { + return new Runnable() + { + public void run() + { + try + { beforeExecute(); task.run(); - } finally { - try { + } + finally + { + try + { afterExecute(); - } finally { + } + finally + { balancer.releasePermit(); } } @@ -267,68 +332,86 @@ public void run() { }; } - protected ShutdownHandler newShutdownHandler() { + protected ShutdownHandler newShutdownHandler() + { return new ShutdownHandler(); } /** * If this is a master scheduler, the slaves can stop scheduling by the master through the controller. */ - private final class Controller { + private final class Controller + { private final Scheduler slave; - private Controller(Scheduler slave) { + private Controller( Scheduler slave ) + { this.slave = slave; } /** * @return true if new children can be scheduled. */ - boolean canSchedule() { + boolean canSchedule() + { return Scheduler.this.canSchedule(); } - void awaitFinishedQuietly() { - try { + void awaitFinishedQuietly() + { + try + { slave.finished(); - } catch(Throwable t) { - slave.logQuietly(t); + } + catch ( Throwable t ) + { + slave.logQuietly( t ); } } - Collection shutdown(boolean shutdownNow) { - return slave.shutdown(shutdownNow); + Collection shutdown( boolean shutdownNow ) + { + return slave.shutdown( shutdownNow ); } @Override - public int hashCode() { + public int hashCode() + { return slave.hashCode(); } @Override - public boolean equals(Object o) { - return o == this || (o instanceof Controller) && slave.equals(((Controller) o).slave); + public boolean equals( Object o ) + { + return o == this || ( o instanceof Controller ) && slave.equals( ( (Controller) o ).slave ); } } - public class ShutdownHandler implements RejectedExecutionHandler { + public class ShutdownHandler + implements RejectedExecutionHandler + { private volatile RejectedExecutionHandler poolHandler; - protected ShutdownHandler() { + protected ShutdownHandler() + { poolHandler = null; } - public void setRejectedExecutionHandler(RejectedExecutionHandler poolHandler) { + public void setRejectedExecutionHandler( RejectedExecutionHandler poolHandler ) + { this.poolHandler = poolHandler; } - public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { - if (executor.isShutdown()) { - shutdown(false); + public void rejectedExecution( Runnable r, ThreadPoolExecutor executor ) + { + if ( executor.isShutdown() ) + { + shutdown( false ); } final RejectedExecutionHandler poolHandler = this.poolHandler; - if (poolHandler != null) { - poolHandler.rejectedExecution(r, executor); + if ( poolHandler != null ) + { + poolHandler.rejectedExecution( r, executor ); } } } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategies.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategies.java index 4d3c6a6484..475f4c0bb0 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategies.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategies.java @@ -28,12 +28,14 @@ * @author Tibor Digana (tibor17) * @since 2.16 */ -public class SchedulingStrategies { +public class SchedulingStrategies +{ /** * @return sequentially executing strategy */ - public static SchedulingStrategy createInvokerStrategy() { + public static SchedulingStrategy createInvokerStrategy() + { return new InvokerStrategy(); } @@ -41,20 +43,22 @@ public static SchedulingStrategy createInvokerStrategy() { * @param nThreads fixed pool capacity * @return parallel scheduling strategy */ - public static SchedulingStrategy createParallelStrategy(int nThreads) { - return new NonSharedThreadPoolStrategy(Executors.newFixedThreadPool(nThreads)); + public static SchedulingStrategy createParallelStrategy( int nThreads ) + { + return new NonSharedThreadPoolStrategy( Executors.newFixedThreadPool( nThreads ) ); } /** * @return parallel scheduling strategy with unbounded capacity */ - public static SchedulingStrategy createParallelStrategyUnbounded() { - return new NonSharedThreadPoolStrategy(Executors.newCachedThreadPool()); + public static SchedulingStrategy createParallelStrategyUnbounded() + { + return new NonSharedThreadPoolStrategy( Executors.newCachedThreadPool() ); } /** * The threadPool passed to this strategy can be shared in other strategies. - *

    + *

    * The call {@link SchedulingStrategy#finished()} is waiting until own tasks have finished. * New tasks will not be scheduled by this call in this strategy. This strategy is not * waiting for other strategies to finish. The {@link org.junit.runners.model.RunnerScheduler#finished()} may @@ -64,10 +68,12 @@ public static SchedulingStrategy createParallelStrategyUnbounded() { * @return parallel strategy with shared thread pool * @throws NullPointerException if threadPool is null */ - public static SchedulingStrategy createParallelSharedStrategy(ExecutorService threadPool) { - if (threadPool == null) { - throw new NullPointerException("null threadPool in #createParallelSharedStrategy"); + public static SchedulingStrategy createParallelSharedStrategy( ExecutorService threadPool ) + { + if ( threadPool == null ) + { + throw new NullPointerException( "null threadPool in #createParallelSharedStrategy" ); } - return new SharedThreadPoolStrategy(threadPool); + return new SharedThreadPoolStrategy( threadPool ); } } diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategy.java index c9da764b68..f419cb7f61 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategy.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategy.java @@ -35,19 +35,20 @@ * @author Tibor Digana (tibor17) * @since 2.16 */ -public abstract class SchedulingStrategy { +public abstract class SchedulingStrategy +{ /** * Schedules tasks if {@link #canSchedule()}. * * @param task runnable to schedule in a thread pool or invoke * @throws RejectedExecutionException if task - * cannot be scheduled for execution - * @throws NullPointerException if task is null + * cannot be scheduled for execution + * @throws NullPointerException if task is null * @see RunnerScheduler#schedule(Runnable) * @see java.util.concurrent.Executor#execute(Runnable) */ - protected abstract void schedule(Runnable task); + protected abstract void schedule( Runnable task ); /** * Waiting for scheduled tasks to finish. @@ -57,10 +58,11 @@ public abstract class SchedulingStrategy { * false if already stopped (a shared thread * pool was shutdown externally). * @throws InterruptedException if interrupted while waiting - * for scheduled tasks to finish + * for scheduled tasks to finish * @see RunnerScheduler#finished() */ - protected abstract boolean finished() throws InterruptedException; + protected abstract boolean finished() + throws InterruptedException; /** * Stops scheduling new tasks (e.g. by {@link java.util.concurrent.ExecutorService#shutdown()} @@ -77,7 +79,7 @@ public abstract class SchedulingStrategy { * Stops scheduling new tasks and interrupts running tasks * (e.g. by {@link java.util.concurrent.ExecutorService#shutdownNow()} on a private thread pool * which cannot be shared with other strategy). - *

    + *

    * This method calls {@link #stop()} by default. * * @return true if successfully stopped the scheduler, else @@ -85,16 +87,18 @@ public abstract class SchedulingStrategy { * pool was shutdown externally). * @see java.util.concurrent.ExecutorService#shutdownNow() */ - protected boolean stopNow() { + protected boolean stopNow() + { return stop(); } - protected void setDefaultShutdownHandler(Scheduler.ShutdownHandler handler) { + protected void setDefaultShutdownHandler( Scheduler.ShutdownHandler handler ) + { } /** * @return true if a thread pool associated with this strategy - * can be shared with other strategies. + * can be shared with other strategies. */ protected abstract boolean hasSharedThreadPool(); diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SharedThreadPoolStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SharedThreadPoolStrategy.java index 53082c424e..88907e69e5 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SharedThreadPoolStrategy.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/SharedThreadPoolStrategy.java @@ -29,33 +29,46 @@ * Parallel strategy for shared thread pool in private package. * * @author Tibor Digana (tibor17) - * @since 2.16 - * * @see AbstractThreadPoolStrategy + * @since 2.16 */ -final class SharedThreadPoolStrategy extends AbstractThreadPoolStrategy { - SharedThreadPoolStrategy(ExecutorService threadPool) { - super(threadPool, new ConcurrentLinkedQueue>()); +final class SharedThreadPoolStrategy + extends AbstractThreadPoolStrategy +{ + SharedThreadPoolStrategy( ExecutorService threadPool ) + { + super( threadPool, new ConcurrentLinkedQueue>() ); } @Override - public boolean hasSharedThreadPool() { + public boolean hasSharedThreadPool() + { return true; } @Override - public boolean finished() throws InterruptedException { + public boolean finished() + throws InterruptedException + { boolean wasRunningAll = canSchedule(); - for (Future futureResult : getFutureResults()) { - try { + for ( Future futureResult : getFutureResults() ) + { + try + { futureResult.get(); - } catch (InterruptedException e) { + } + catch ( InterruptedException e ) + { // after called external ExecutorService#shutdownNow() // or ExecutorService#shutdown() wasRunningAll = false; - } catch (ExecutionException e) { + } + catch ( ExecutionException e ) + { // test throws exception - } catch (CancellationException e) { + } + catch ( CancellationException e ) + { // cannot happen because not calling Future#cancel() } } @@ -64,19 +77,23 @@ public boolean finished() throws InterruptedException { } @Override - protected final boolean stop() { - return stop(false); + protected final boolean stop() + { + return stop( false ); } @Override - protected final boolean stopNow() { - return stop(true); + protected final boolean stopNow() + { + return stop( true ); } - private boolean stop(boolean interrupt) { + private boolean stop( boolean interrupt ) + { final boolean wasRunning = canSchedule(); - for (Future futureResult : getFutureResults()) { - futureResult.cancel(interrupt); + for ( Future futureResult : getFutureResults() ) + { + futureResult.cancel( interrupt ); } disable(); return wasRunning; diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ThreadResourcesBalancer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ThreadResourcesBalancer.java index 80d116d7ce..322d44344e 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ThreadResourcesBalancer.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ThreadResourcesBalancer.java @@ -23,13 +23,14 @@ /** * @author Tibor Digana (tibor17) - * @since 2.16 - * * @see Balancer + * @since 2.16 */ -final class ThreadResourcesBalancer implements Balancer +final class ThreadResourcesBalancer + implements Balancer { private final Semaphore balancer; + private final int numPermits; /** @@ -37,7 +38,6 @@ final class ThreadResourcesBalancer implements Balancer * * @param numPermits number of permits to acquire when maintaining concurrency on tests. * Must be >0 and < {@link Integer#MAX_VALUE}. - * * @see #ThreadResourcesBalancer(int, boolean) */ ThreadResourcesBalancer( int numPermits ) @@ -48,7 +48,7 @@ final class ThreadResourcesBalancer implements Balancer /** * @param numPermits number of permits to acquire when maintaining concurrency on tests. * Must be >0 and < {@link Integer#MAX_VALUE}. - * @param fair true guarantees the waiting schedulers to wake up in order they acquired a permit + * @param fair true guarantees the waiting schedulers to wake up in order they acquired a permit */ ThreadResourcesBalancer( int numPermits, boolean fair ) { diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/ParallelComputerFactoryTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/ParallelComputerFactoryTest.java index 90d773e63b..c3682ae068 100644 --- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/ParallelComputerFactoryTest.java +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/ParallelComputerFactoryTest.java @@ -19,6 +19,7 @@ * under the License. */ +import org.apache.maven.surefire.junitcore.pc.ParallelComputer; import org.apache.maven.surefire.testset.TestSetFailedException; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -28,9 +29,12 @@ import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.rules.ExpectedException; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; import org.junit.runner.RunWith; import java.util.Properties; +import java.util.concurrent.ExecutionException; import static org.apache.maven.surefire.junitcore.ParallelComputerFactory.*; import static org.apache.maven.surefire.junitcore.JUnitCoreParameters.*; @@ -42,22 +46,24 @@ * allocated thread resources in ParallelComputer by given {@link JUnitCoreParameters}. * * @author Tibor Digana (tibor17) - * @since 2.16 - * * @see org.apache.maven.surefire.junitcore.ParallelComputerFactory + * @since 2.16 */ -@RunWith(Theories.class) +@RunWith( Theories.class ) public final class ParallelComputerFactoryTest { - @Rule - public final ExpectedException exception = ExpectedException.none(); - @DataPoint public static final int CPU_1 = 1; @DataPoint public static final int CPU_4 = 4; + @Rule + public final ExpectedException exception = ExpectedException.none(); + + @Rule + public final Stopwatch runtime = new Stopwatch(); + @BeforeClass public static void beforeClass() { @@ -70,16 +76,25 @@ public static void afterClass() ParallelComputerFactory.setDefaultAvailableProcessors(); } + private static Properties parallel( String parallel ) + { + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, parallel ); + return properties; + } + @Test - public void unknownParallel() throws TestSetFailedException + public void unknownParallel() + throws TestSetFailedException { Properties properties = new Properties(); - exception.expect( TestSetFailedException.class ); + exception.expect( TestSetFailedException.class ); resolveConcurrency( new JUnitCoreParameters( properties ) ); } @Test - public void unknownThreadCountSuites() throws TestSetFailedException + public void unknownThreadCountSuites() + throws TestSetFailedException { JUnitCoreParameters params = new JUnitCoreParameters( parallel( "suites" ) ); assertTrue( params.isParallelSuites() ); @@ -90,7 +105,8 @@ public void unknownThreadCountSuites() throws TestSetFailedException } @Test - public void unknownThreadCountClasses() throws TestSetFailedException + public void unknownThreadCountClasses() + throws TestSetFailedException { JUnitCoreParameters params = new JUnitCoreParameters( parallel( "classes" ) ); assertFalse( params.isParallelSuites() ); @@ -101,7 +117,8 @@ public void unknownThreadCountClasses() throws TestSetFailedException } @Test - public void unknownThreadCountMethods() throws TestSetFailedException + public void unknownThreadCountMethods() + throws TestSetFailedException { JUnitCoreParameters params = new JUnitCoreParameters( parallel( "methods" ) ); assertFalse( params.isParallelSuites() ); @@ -112,7 +129,8 @@ public void unknownThreadCountMethods() throws TestSetFailedException } @Test - public void unknownThreadCountBoth() throws TestSetFailedException + public void unknownThreadCountBoth() + throws TestSetFailedException { JUnitCoreParameters params = new JUnitCoreParameters( parallel( "both" ) ); assertFalse( params.isParallelSuites() ); @@ -123,7 +141,8 @@ public void unknownThreadCountBoth() throws TestSetFailedException } @Test - public void unknownThreadCountAll() throws TestSetFailedException + public void unknownThreadCountAll() + throws TestSetFailedException { JUnitCoreParameters params = new JUnitCoreParameters( parallel( "all" ) ); assertTrue( params.isParallelSuites() ); @@ -134,7 +153,8 @@ public void unknownThreadCountAll() throws TestSetFailedException } @Test - public void unknownThreadCountSuitesAndClasses() throws TestSetFailedException + public void unknownThreadCountSuitesAndClasses() + throws TestSetFailedException { JUnitCoreParameters params = new JUnitCoreParameters( parallel( "suitesAndClasses" ) ); assertTrue( params.isParallelSuites() ); @@ -145,7 +165,8 @@ public void unknownThreadCountSuitesAndClasses() throws TestSetFailedException } @Test - public void unknownThreadCountSuitesAndMethods() throws TestSetFailedException + public void unknownThreadCountSuitesAndMethods() + throws TestSetFailedException { JUnitCoreParameters params = new JUnitCoreParameters( parallel( "suitesAndMethods" ) ); assertTrue( params.isParallelSuites() ); @@ -156,7 +177,8 @@ public void unknownThreadCountSuitesAndMethods() throws TestSetFailedException } @Test - public void unknownThreadCountClassesAndMethods() throws TestSetFailedException + public void unknownThreadCountClassesAndMethods() + throws TestSetFailedException { JUnitCoreParameters params = new JUnitCoreParameters( parallel( "classesAndMethods" ) ); assertFalse( params.isParallelSuites() ); @@ -167,7 +189,8 @@ public void unknownThreadCountClassesAndMethods() throws TestSetFailedException } @Theory - public void useUnlimitedThreadsSuites( int cpu ) throws TestSetFailedException + public void useUnlimitedThreadsSuites( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -196,7 +219,8 @@ public void useUnlimitedThreadsSuites( int cpu ) throws TestSetFailedException } @Theory - public void useUnlimitedThreadsClasses( int cpu ) throws TestSetFailedException + public void useUnlimitedThreadsClasses( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -225,7 +249,8 @@ public void useUnlimitedThreadsClasses( int cpu ) throws TestSetFailedException } @Theory - public void unlimitedThreadsMethods( int cpu ) throws TestSetFailedException + public void unlimitedThreadsMethods( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -254,7 +279,8 @@ public void unlimitedThreadsMethods( int cpu ) throws TestSetFailedException } @Theory - public void unlimitedThreadsSuitesAndClasses( int cpu ) throws TestSetFailedException + public void unlimitedThreadsSuitesAndClasses( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -284,7 +310,8 @@ public void unlimitedThreadsSuitesAndClasses( int cpu ) throws TestSetFailedExce } @Theory - public void unlimitedThreadsSuitesAndMethods( int cpu ) throws TestSetFailedException + public void unlimitedThreadsSuitesAndMethods( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -314,7 +341,8 @@ public void unlimitedThreadsSuitesAndMethods( int cpu ) throws TestSetFailedExce } @Theory - public void unlimitedThreadsClassesAndMethods( int cpu ) throws TestSetFailedException + public void unlimitedThreadsClassesAndMethods( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -344,7 +372,8 @@ public void unlimitedThreadsClassesAndMethods( int cpu ) throws TestSetFailedExc } @Theory - public void unlimitedThreadsAll( int cpu ) throws TestSetFailedException + public void unlimitedThreadsAll( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -375,7 +404,8 @@ public void unlimitedThreadsAll( int cpu ) throws TestSetFailedException } @Theory - public void threadCountSuites( int cpu ) throws TestSetFailedException + public void threadCountSuites( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -393,7 +423,8 @@ public void threadCountSuites( int cpu ) throws TestSetFailedException } @Theory - public void threadCountClasses( int cpu ) throws TestSetFailedException + public void threadCountClasses( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -411,7 +442,8 @@ public void threadCountClasses( int cpu ) throws TestSetFailedException } @Theory - public void threadCountMethods( int cpu ) throws TestSetFailedException + public void threadCountMethods( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -429,7 +461,8 @@ public void threadCountMethods( int cpu ) throws TestSetFailedException } @Theory - public void threadCountBoth( int cpu ) throws TestSetFailedException + public void threadCountBoth( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -447,7 +480,8 @@ public void threadCountBoth( int cpu ) throws TestSetFailedException } @Theory - public void threadCountClassesAndMethods( int cpu ) throws TestSetFailedException + public void threadCountClassesAndMethods( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -465,7 +499,8 @@ public void threadCountClassesAndMethods( int cpu ) throws TestSetFailedExceptio } @Theory - public void threadCountSuitesAndMethods( int cpu ) throws TestSetFailedException + public void threadCountSuitesAndMethods( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -483,7 +518,8 @@ public void threadCountSuitesAndMethods( int cpu ) throws TestSetFailedException } @Theory - public void threadCountSuitesAndClasses( int cpu ) throws TestSetFailedException + public void threadCountSuitesAndClasses( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -501,7 +537,8 @@ public void threadCountSuitesAndClasses( int cpu ) throws TestSetFailedException } @Theory - public void threadCountAll( int cpu ) throws TestSetFailedException + public void threadCountAll( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -519,7 +556,8 @@ public void threadCountAll( int cpu ) throws TestSetFailedException } @Theory - public void everyThreadCountSuitesAndClasses( int cpu ) throws TestSetFailedException + public void everyThreadCountSuitesAndClasses( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -533,7 +571,7 @@ public void everyThreadCountSuitesAndClasses( int cpu ) throws TestSetFailedExce assertTrue( params.isParallelSuites() ); assertTrue( params.isParallelClasses() ); assertFalse( params.isParallelMethod() ); - assertThat( concurrency.capacity, is(3 * cpu) ); + assertThat( concurrency.capacity, is( 3 * cpu ) ); int concurrentSuites = (int) ( 0.34d * concurrency.capacity ); assertThat( concurrency.suites, is( concurrentSuites ) ); assertThat( concurrency.classes, is( concurrency.capacity - concurrentSuites ) ); @@ -541,7 +579,8 @@ public void everyThreadCountSuitesAndClasses( int cpu ) throws TestSetFailedExce } @Theory - public void everyThreadCountSuitesAndMethods( int cpu ) throws TestSetFailedException + public void everyThreadCountSuitesAndMethods( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -563,7 +602,8 @@ public void everyThreadCountSuitesAndMethods( int cpu ) throws TestSetFailedExce } @Theory - public void everyThreadCountClassesAndMethods( int cpu ) throws TestSetFailedException + public void everyThreadCountClassesAndMethods( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -585,7 +625,8 @@ public void everyThreadCountClassesAndMethods( int cpu ) throws TestSetFailedExc } @Theory - public void everyThreadCountAll( int cpu ) throws TestSetFailedException + public void everyThreadCountAll( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -609,7 +650,8 @@ public void everyThreadCountAll( int cpu ) throws TestSetFailedException } @Theory - public void reusableThreadCountSuitesAndClasses( int cpu ) throws TestSetFailedException + public void reusableThreadCountSuitesAndClasses( int cpu ) + throws TestSetFailedException { // 4 * cpu to 5 * cpu threads to run test classes ParallelComputerFactory.overrideAvailableProcessors( cpu ); @@ -629,7 +671,8 @@ public void reusableThreadCountSuitesAndClasses( int cpu ) throws TestSetFailedE } @Theory - public void reusableThreadCountSuitesAndMethods( int cpu ) throws TestSetFailedException + public void reusableThreadCountSuitesAndMethods( int cpu ) + throws TestSetFailedException { // 4 * cpu to 5 * cpu threads to run test methods ParallelComputerFactory.overrideAvailableProcessors( cpu ); @@ -649,7 +692,8 @@ public void reusableThreadCountSuitesAndMethods( int cpu ) throws TestSetFailedE } @Theory - public void reusableThreadCountClassesAndMethods( int cpu ) throws TestSetFailedException + public void reusableThreadCountClassesAndMethods( int cpu ) + throws TestSetFailedException { // 4 * cpu to 5 * cpu threads to run test methods ParallelComputerFactory.overrideAvailableProcessors( cpu ); @@ -669,7 +713,8 @@ public void reusableThreadCountClassesAndMethods( int cpu ) throws TestSetFailed } @Theory - public void reusableThreadCountAll( int cpu ) throws TestSetFailedException + public void reusableThreadCountAll( int cpu ) + throws TestSetFailedException { // 8 * cpu to 13 * cpu threads to run test methods ParallelComputerFactory.overrideAvailableProcessors( cpu ); @@ -690,7 +735,8 @@ public void reusableThreadCountAll( int cpu ) throws TestSetFailedException } @Theory - public void suites( int cpu ) throws TestSetFailedException + public void suites( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -708,7 +754,8 @@ public void suites( int cpu ) throws TestSetFailedException } @Theory - public void classes( int cpu ) throws TestSetFailedException + public void classes( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -726,7 +773,8 @@ public void classes( int cpu ) throws TestSetFailedException } @Theory - public void methods( int cpu ) throws TestSetFailedException + public void methods( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -744,7 +792,8 @@ public void methods( int cpu ) throws TestSetFailedException } @Theory - public void suitesAndClasses( int cpu ) throws TestSetFailedException + public void suitesAndClasses( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -779,7 +828,8 @@ public void suitesAndClasses( int cpu ) throws TestSetFailedException } @Theory - public void suitesAndMethods( int cpu ) throws TestSetFailedException + public void suitesAndMethods( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -814,7 +864,8 @@ public void suitesAndMethods( int cpu ) throws TestSetFailedException } @Theory - public void classesAndMethods( int cpu ) throws TestSetFailedException + public void classesAndMethods( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -849,7 +900,8 @@ public void classesAndMethods( int cpu ) throws TestSetFailedException } @Theory - public void all( int cpu ) throws TestSetFailedException + public void all( int cpu ) + throws TestSetFailedException { ParallelComputerFactory.overrideAvailableProcessors( cpu ); Properties properties = new Properties(); @@ -898,10 +950,114 @@ public void all( int cpu ) throws TestSetFailedException assertThat( concurrency.methods, is( Integer.MAX_VALUE ) ); } - private static Properties parallel( String parallel ) + @Test + public void withoutShutdown() + throws TestSetFailedException, ExecutionException, InterruptedException { Properties properties = new Properties(); - properties.setProperty( PARALLEL_KEY, parallel ); - return properties; + properties.setProperty( PARALLEL_KEY, "methods" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "2" ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + ParallelComputer pc = createParallelComputer( params ); + + Result result = new JUnitCore().run( pc, TestClass.class ); + long timeSpent = runtime.stop(); + long deltaTime = 500L; + + assertTrue( result.wasSuccessful() ); + assertThat( result.getRunCount(), is( 3 ) ); + assertThat( result.getFailureCount(), is( 0 ) ); + assertThat( result.getIgnoreCount(), is( 0 ) ); + assertEquals( 10000L, timeSpent, deltaTime ); + } + + @Test + public void shutdown() + throws TestSetFailedException, ExecutionException, InterruptedException + { + // The JUnitCore returns after 2.5s. + // The test-methods in TestClass are NOT interrupted, and return normally after 5s. + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "methods" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "2" ); + properties.setProperty( PARALLEL_TIMEOUT_KEY, Double.toString( 2.5d ) ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + ParallelComputer pc = createParallelComputer( params ); + + new JUnitCore().run( pc, TestClass.class ); + long timeSpent = runtime.stop(); + long deltaTime = 500L; + + assertEquals( 2500L, timeSpent, deltaTime ); + assertTrue( pc.describeElapsedTimeout().contains( + "The test run has finished abruptly after timeout of 2.5 seconds." ) ); + assertTrue( pc.describeElapsedTimeout().contains( TestClass.class.getName() ) ); + } + + @Test + public void forcedShutdown() + throws TestSetFailedException, ExecutionException, InterruptedException + { + // The JUnitCore returns after 2.5s, and the test-methods in TestClass are interrupted. + Properties properties = new Properties(); + properties.setProperty( PARALLEL_KEY, "methods" ); + properties.setProperty( THREADCOUNTMETHODS_KEY, "2" ); + properties.setProperty( PARALLEL_TIMEOUTFORCED_KEY, Double.toString( 2.5d ) ); + JUnitCoreParameters params = new JUnitCoreParameters( properties ); + ParallelComputer pc = createParallelComputer( params ); + + new JUnitCore().run( pc, TestClass.class ); + long timeSpent = runtime.stop(); + long deltaTime = 500L; + + assertEquals( 2500L, timeSpent, deltaTime ); + assertTrue( pc.describeElapsedTimeout().contains( + "The test run has finished abruptly after timeout of 2.5 seconds." ) ); + assertTrue( pc.describeElapsedTimeout().contains( TestClass.class.getName() ) ); + } + + public static class TestClass + { + @Test + public void a() + throws InterruptedException + { + long t1 = System.currentTimeMillis(); + try{ + Thread.sleep( 5000L ); + } + finally + { + System.out.println( getClass().getSimpleName() + "#a() spent " + ( System.currentTimeMillis() - t1 ) ); + } + } + + @Test + public void b() + throws InterruptedException + { + long t1 = System.currentTimeMillis(); + try{ + Thread.sleep( 5000L ); + } + finally + { + System.out.println( getClass().getSimpleName() + "#b() spent " + ( System.currentTimeMillis() - t1 ) ); + } + } + + @Test + public void c() + throws InterruptedException + { + long t1 = System.currentTimeMillis(); + try{ + Thread.sleep( 5000L ); + } + finally + { + System.out.println( getClass().getSimpleName() + "#c() spent " + ( System.currentTimeMillis() - t1 ) ); + } + } } } \ No newline at end of file diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/Stopwatch.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Stopwatch.java similarity index 80% rename from surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/Stopwatch.java rename to surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Stopwatch.java index 32b056c009..558fc068d6 100644 --- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/Stopwatch.java +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/Stopwatch.java @@ -1,4 +1,4 @@ -package org.apache.maven.surefire.junitcore.pc; +package org.apache.maven.surefire.junitcore; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -28,17 +28,18 @@ * @author Tibor Digana (tibor17) * @since 2.16 */ -final class Stopwatch extends TestWatchman +public final class Stopwatch + extends TestWatchman { private long startNanos; - long stop() + public long stop() { - return TimeUnit.MILLISECONDS.convert(System.nanoTime() - startNanos, TimeUnit.NANOSECONDS); + return TimeUnit.MILLISECONDS.convert( System.nanoTime() - startNanos, TimeUnit.NANOSECONDS ); } @Override - public void starting(FrameworkMethod method) + public void starting( FrameworkMethod method ) { startNanos = System.nanoTime(); } diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java index c34056ac0a..4dda4654ac 100644 --- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilderTest.java @@ -19,6 +19,7 @@ * under the License. */ +import org.apache.maven.surefire.junitcore.Stopwatch; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -48,97 +49,132 @@ * @author Tibor Digana (tibor17) * @since 2.16 */ -public class ParallelComputerBuilderTest { +public class ParallelComputerBuilderTest +{ + private static volatile boolean beforeShutdown; + + private static volatile Runnable shutdownTask; + @Rule public final Stopwatch runtime = new Stopwatch(); + private static void testKeepBeforeAfter( ParallelComputerBuilder builder, Class... classes ) + { + JUnitCore core = new JUnitCore(); + for ( int round = 0; round < 5; round++ ) + { + NothingDoingTest1.methods.clear(); + Result result = core.run( builder.buildComputer(), classes ); + assertTrue( result.wasSuccessful() ); + Iterator methods = NothingDoingTest1.methods.iterator(); + for ( Class clazz : classes ) + { + String a = clazz.getName() + "#a()"; + String b = clazz.getName() + "#b()"; + assertThat( methods.next(), is( "init" ) ); + assertThat( methods.next(), anyOf( is( a ), is( b ) ) ); + assertThat( methods.next(), anyOf( is( a ), is( b ) ) ); + assertThat( methods.next(), is( "deinit" ) ); + } + } + } + @Before - public void beforeTest() { + public void beforeTest() + { Class1.maxConcurrentMethods = 0; Class1.concurrentMethods = 0; shutdownTask = null; } @Test - public void parallelMethodsReuseOneOrTwoThreads() { + public void parallelMethodsReuseOneOrTwoThreads() + { ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); - parallelComputerBuilder.useOnePool(4); + parallelComputerBuilder.useOnePool( 4 ); // One thread because one suite: TestSuite, however the capacity is 5. - parallelComputerBuilder.parallelSuites(5); + parallelComputerBuilder.parallelSuites( 5 ); // Two threads because TestSuite has two classes, however the capacity is 5. - parallelComputerBuilder.parallelClasses(5); + parallelComputerBuilder.parallelClasses( 5 ); // One or two threads because one threads comes from '#useOnePool(4)' // and next thread may be reused from finished class, however the capacity is 3. - parallelComputerBuilder.parallelMethods(3); + parallelComputerBuilder.parallelMethods( 3 ); ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); - Result result = new JUnitCore().run(computer, TestSuite.class); + Result result = new JUnitCore().run( computer, TestSuite.class ); long timeSpent = runtime.stop(); - assertThat(computer.suites.size(), is(1)); - assertThat(computer.classes.size(), is(0)); - assertThat(computer.nestedClasses.size(), is(2)); - assertThat(computer.nestedSuites.size(), is(0)); - assertFalse(computer.splitPool); - assertThat(computer.poolCapacity, is(4)); - assertTrue(result.wasSuccessful()); - if (Class1.maxConcurrentMethods == 1) { - assertThat(timeSpent, between(1950, 2250)); - } else if (Class1.maxConcurrentMethods == 2) { - assertThat(timeSpent, between(1450, 1750)); - } else { + assertThat( computer.suites.size(), is( 1 ) ); + assertThat( computer.classes.size(), is( 0 ) ); + assertThat( computer.nestedClasses.size(), is( 2 ) ); + assertThat( computer.nestedSuites.size(), is( 0 ) ); + assertFalse( computer.splitPool ); + assertThat( computer.poolCapacity, is( 4 ) ); + assertTrue( result.wasSuccessful() ); + if ( Class1.maxConcurrentMethods == 1 ) + { + assertThat( timeSpent, between( 1950, 2250 ) ); + } + else if ( Class1.maxConcurrentMethods == 2 ) + { + assertThat( timeSpent, between( 1450, 1750 ) ); + } + else + { fail(); } } @Test - public void suiteAndClassInOnePool() { + public void suiteAndClassInOnePool() + { ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); - parallelComputerBuilder.useOnePool(5); - parallelComputerBuilder.parallelSuites(5); - parallelComputerBuilder.parallelClasses(5); - parallelComputerBuilder.parallelMethods(3); + parallelComputerBuilder.useOnePool( 5 ); + parallelComputerBuilder.parallelSuites( 5 ); + parallelComputerBuilder.parallelClasses( 5 ); + parallelComputerBuilder.parallelMethods( 3 ); ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); - Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class); + Result result = new JUnitCore().run( computer, TestSuite.class, Class1.class ); long timeSpent = runtime.stop(); - assertThat(computer.suites.size(), is(1)); - assertThat(computer.classes.size(), is(1)); - assertThat(computer.nestedClasses.size(), is(2)); - assertThat(computer.nestedSuites.size(), is(0)); - assertFalse(computer.splitPool); - assertThat(computer.poolCapacity, is(5)); - assertTrue(result.wasSuccessful()); - assertThat(Class1.maxConcurrentMethods, is(2)); - assertThat(timeSpent, anyOf(between(1450, 1750), between(1950, 2250), between(2450, 2750))); + assertThat( computer.suites.size(), is( 1 ) ); + assertThat( computer.classes.size(), is( 1 ) ); + assertThat( computer.nestedClasses.size(), is( 2 ) ); + assertThat( computer.nestedSuites.size(), is( 0 ) ); + assertFalse( computer.splitPool ); + assertThat( computer.poolCapacity, is( 5 ) ); + assertTrue( result.wasSuccessful() ); + assertThat( Class1.maxConcurrentMethods, is( 2 ) ); + assertThat( timeSpent, anyOf( between( 1450, 1750 ), between( 1950, 2250 ), between( 2450, 2750 ) ) ); } @Test - public void onePoolWithUnlimitedParallelMethods() { + public void onePoolWithUnlimitedParallelMethods() + { // see ParallelComputerBuilder Javadoc ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); - parallelComputerBuilder.useOnePool(8); - parallelComputerBuilder.parallelSuites(2); - parallelComputerBuilder.parallelClasses(4); + parallelComputerBuilder.useOnePool( 8 ); + parallelComputerBuilder.parallelSuites( 2 ); + parallelComputerBuilder.parallelClasses( 4 ); parallelComputerBuilder.parallelMethods(); ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); - Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class); + Result result = new JUnitCore().run( computer, TestSuite.class, Class1.class ); long timeSpent = runtime.stop(); - assertThat(computer.suites.size(), is(1)); - assertThat(computer.classes.size(), is(1)); - assertThat(computer.nestedClasses.size(), is(2)); - assertThat(computer.nestedSuites.size(), is(0)); - assertFalse(computer.splitPool); - assertThat(computer.poolCapacity, is(8)); - assertTrue(result.wasSuccessful()); - assertThat(Class1.maxConcurrentMethods, is(4)); - assertThat(timeSpent, between(950, 1250)); + assertThat( computer.suites.size(), is( 1 ) ); + assertThat( computer.classes.size(), is( 1 ) ); + assertThat( computer.nestedClasses.size(), is( 2 ) ); + assertThat( computer.nestedSuites.size(), is( 0 ) ); + assertFalse( computer.splitPool ); + assertThat( computer.poolCapacity, is( 8 ) ); + assertTrue( result.wasSuccessful() ); + assertThat( Class1.maxConcurrentMethods, is( 4 ) ); + assertThat( timeSpent, between( 950, 1250 ) ); } @Test @@ -156,7 +192,7 @@ public void underflowParallelism() // One thread remains from '#useOnePool(3)'. parallelComputerBuilder.parallelMethods( 3 ); - ParallelComputerBuilder.PC computer = ( ParallelComputerBuilder.PC ) parallelComputerBuilder.buildComputer(); + ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); Result result = new JUnitCore().run( computer, TestSuite.class ); long timeSpent = runtime.stop(); @@ -172,265 +208,283 @@ public void underflowParallelism() } @Test - public void separatePoolsWithSuite() { + public void separatePoolsWithSuite() + { ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); - parallelComputerBuilder.parallelSuites(5); - parallelComputerBuilder.parallelClasses(5); - parallelComputerBuilder.parallelMethods(3); + parallelComputerBuilder.parallelSuites( 5 ); + parallelComputerBuilder.parallelClasses( 5 ); + parallelComputerBuilder.parallelMethods( 3 ); ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); - Result result = new JUnitCore().run(computer, TestSuite.class); + Result result = new JUnitCore().run( computer, TestSuite.class ); long timeSpent = runtime.stop(); - assertThat(computer.suites.size(), is(1)); - assertThat(computer.classes.size(), is(0)); - assertThat(computer.nestedClasses.size(), is(2)); - assertThat(computer.nestedSuites.size(), is(0)); - assertTrue(computer.splitPool); - assertThat(computer.poolCapacity, is(ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED)); - assertTrue(result.wasSuccessful()); - assertThat(Class1.maxConcurrentMethods, is(3)); - assertThat(timeSpent, between(950, 1250)); + assertThat( computer.suites.size(), is( 1 ) ); + assertThat( computer.classes.size(), is( 0 ) ); + assertThat( computer.nestedClasses.size(), is( 2 ) ); + assertThat( computer.nestedSuites.size(), is( 0 ) ); + assertTrue( computer.splitPool ); + assertThat( computer.poolCapacity, is( ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED ) ); + assertTrue( result.wasSuccessful() ); + assertThat( Class1.maxConcurrentMethods, is( 3 ) ); + assertThat( timeSpent, between( 950, 1250 ) ); } @Test - public void separatePoolsWithSuiteAndClass() { + public void separatePoolsWithSuiteAndClass() + { ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); - parallelComputerBuilder.parallelSuites(5); - parallelComputerBuilder.parallelClasses(5); - parallelComputerBuilder.parallelMethods(3); + parallelComputerBuilder.parallelSuites( 5 ); + parallelComputerBuilder.parallelClasses( 5 ); + parallelComputerBuilder.parallelMethods( 3 ); // 6 methods altogether. // 2 groups with 3 threads. // Each group takes 0.5s. ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); - Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class); + Result result = new JUnitCore().run( computer, TestSuite.class, Class1.class ); long timeSpent = runtime.stop(); - assertThat(computer.suites.size(), is(1)); - assertThat(computer.classes.size(), is(1)); - assertThat(computer.nestedClasses.size(), is(2)); - assertThat(computer.nestedSuites.size(), is(0)); - assertTrue(computer.splitPool); - assertThat(computer.poolCapacity, is(ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED)); - assertTrue(result.wasSuccessful()); - assertThat(Class1.maxConcurrentMethods, is(3)); - assertThat(timeSpent, between(950, 1250)); + assertThat( computer.suites.size(), is( 1 ) ); + assertThat( computer.classes.size(), is( 1 ) ); + assertThat( computer.nestedClasses.size(), is( 2 ) ); + assertThat( computer.nestedSuites.size(), is( 0 ) ); + assertTrue( computer.splitPool ); + assertThat( computer.poolCapacity, is( ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED ) ); + assertTrue( result.wasSuccessful() ); + assertThat( Class1.maxConcurrentMethods, is( 3 ) ); + assertThat( timeSpent, between( 950, 1250 ) ); } @Test - public void separatePoolsWithSuiteAndSequentialClasses() { + public void separatePoolsWithSuiteAndSequentialClasses() + { ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder(); - parallelComputerBuilder.parallelSuites(5); - parallelComputerBuilder.parallelClasses(1); - parallelComputerBuilder.parallelMethods(3); + parallelComputerBuilder.parallelSuites( 5 ); + parallelComputerBuilder.parallelClasses( 1 ); + parallelComputerBuilder.parallelMethods( 3 ); ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); - Result result = new JUnitCore().run(computer, TestSuite.class, Class1.class); + Result result = new JUnitCore().run( computer, TestSuite.class, Class1.class ); long timeSpent = runtime.stop(); - assertThat(computer.suites.size(), is(1)); - assertThat(computer.classes.size(), is(1)); - assertThat(computer.nestedClasses.size(), is(2)); - assertThat(computer.nestedSuites.size(), is(0)); - assertTrue(computer.splitPool); - assertThat(computer.poolCapacity, is(ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED)); - assertTrue(result.wasSuccessful()); - assertThat(Class1.maxConcurrentMethods, is(2)); - assertThat(timeSpent, between(1450, 1750)); - } - - private static class ShutdownTest { - Result run(final boolean useInterrupt) { - ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder().useOnePool(8); - parallelComputerBuilder.parallelSuites(2); - parallelComputerBuilder.parallelClasses(3); - parallelComputerBuilder.parallelMethods(3); - - final ParallelComputerBuilder.PC computer = (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); - shutdownTask = new Runnable() { - public void run() { - Collection startedTests = computer.shutdown(useInterrupt); - assertThat(startedTests.size(), is(not(0))); - } - }; - return new JUnitCore().run(computer, TestSuite.class, Class2.class, Class3.class); - } + assertThat( computer.suites.size(), is( 1 ) ); + assertThat( computer.classes.size(), is( 1 ) ); + assertThat( computer.nestedClasses.size(), is( 2 ) ); + assertThat( computer.nestedSuites.size(), is( 0 ) ); + assertTrue( computer.splitPool ); + assertThat( computer.poolCapacity, is( ParallelComputerBuilder.TOTAL_POOL_SIZE_UNDEFINED ) ); + assertTrue( result.wasSuccessful() ); + assertThat( Class1.maxConcurrentMethods, is( 2 ) ); + assertThat( timeSpent, between( 1450, 1750 ) ); } - @Test(timeout = 2000) - public void shutdown() { - Result result = new ShutdownTest().run(false); + @Test( timeout = 2000 ) + public void shutdown() + { + Result result = new ShutdownTest().run( false ); long timeSpent = runtime.stop(); - assertTrue(result.wasSuccessful()); - assertTrue(beforeShutdown); - assertThat(timeSpent, between(450, 1250)); + assertTrue( result.wasSuccessful() ); + assertTrue( beforeShutdown ); + assertThat( timeSpent, between( 450, 1250 ) ); } - @Test(timeout = 2000) - public void shutdownWithInterrupt() { - new ShutdownTest().run(true); + @Test( timeout = 2000 ) + public void shutdownWithInterrupt() + { + new ShutdownTest().run( true ); long timeSpent = runtime.stop(); - assertTrue(beforeShutdown); - assertThat(timeSpent, between(450, 1250)); + assertTrue( beforeShutdown ); + assertThat( timeSpent, between( 450, 1250 ) ); } @Test - public void nothingParallel() { + public void nothingParallel() + { JUnitCore core = new JUnitCore(); ParallelComputerBuilder builder = new ParallelComputerBuilder(); - Result result = core.run(builder.buildComputer(), NothingDoingTest1.class, NothingDoingTest2.class); - assertTrue(result.wasSuccessful()); - - result = core.run(builder.buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class); - assertTrue(result.wasSuccessful()); + Result result = core.run( builder.buildComputer(), NothingDoingTest1.class, NothingDoingTest2.class ); + assertTrue( result.wasSuccessful() ); - result = core.run(builder.useOnePool(1).buildComputer(), NothingDoingTest1.class, NothingDoingTest2.class); - assertTrue(result.wasSuccessful()); + result = core.run( builder.buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class ); + assertTrue( result.wasSuccessful() ); - result = core.run(builder.useOnePool(1).buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class); - assertTrue(result.wasSuccessful()); + result = core.run( builder.useOnePool( 1 ).buildComputer(), NothingDoingTest1.class, NothingDoingTest2.class ); + assertTrue( result.wasSuccessful() ); - result = core.run(builder.useOnePool(2).buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class); - assertTrue(result.wasSuccessful()); + result = core.run( builder.useOnePool( 1 ).buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class ); + assertTrue( result.wasSuccessful() ); - Class[] classes = {NothingDoingTest1.class, NothingDoingSuite.class}; + result = core.run( builder.useOnePool( 2 ).buildComputer(), NothingDoingTest1.class, NothingDoingSuite.class ); + assertTrue( result.wasSuccessful() ); - result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses(1).buildComputer(), classes); - assertTrue(result.wasSuccessful()); + Class[] classes = { NothingDoingTest1.class, NothingDoingSuite.class }; - result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses().buildComputer(), classes); - assertTrue(result.wasSuccessful()); + result = core.run( builder.useOnePool( 2 ).parallelSuites( 1 ).parallelClasses( 1 ).buildComputer(), classes ); + assertTrue( result.wasSuccessful() ); - classes = new Class[]{NothingDoingSuite.class, NothingDoingSuite.class, - NothingDoingTest1.class, NothingDoingTest2.class, NothingDoingTest3.class}; + result = core.run( builder.useOnePool( 2 ).parallelSuites( 1 ).parallelClasses().buildComputer(), classes ); + assertTrue( result.wasSuccessful() ); - result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses(1).buildComputer(), classes); - assertTrue(result.wasSuccessful()); + classes = new Class[]{ NothingDoingSuite.class, NothingDoingSuite.class, NothingDoingTest1.class, + NothingDoingTest2.class, NothingDoingTest3.class }; - result = core.run(builder.useOnePool(2).parallelSuites(1).parallelClasses().buildComputer(), classes); - assertTrue(result.wasSuccessful()); - } + result = core.run( builder.useOnePool( 2 ).parallelSuites( 1 ).parallelClasses( 1 ).buildComputer(), classes ); + assertTrue( result.wasSuccessful() ); - private static void testKeepBeforeAfter(ParallelComputerBuilder builder, Class... classes) { - JUnitCore core = new JUnitCore(); - for (int round = 0; round < 5; round++) { - NothingDoingTest1.methods.clear(); - Result result = core.run(builder.buildComputer(), classes); - assertTrue(result.wasSuccessful()); - Iterator methods = NothingDoingTest1.methods.iterator(); - for (Class clazz : classes) { - String a = clazz.getName() + "#a()"; - String b = clazz.getName() + "#b()"; - assertThat(methods.next(), is("init")); - assertThat(methods.next(), anyOf(is(a), is(b))); - assertThat(methods.next(), anyOf(is(a), is(b))); - assertThat(methods.next(), is("deinit")); - } - } + result = core.run( builder.useOnePool( 2 ).parallelSuites( 1 ).parallelClasses().buildComputer(), classes ); + assertTrue( result.wasSuccessful() ); } @Test - public void keepBeforeAfterOneClass() { + public void keepBeforeAfterOneClass() + { ParallelComputerBuilder builder = new ParallelComputerBuilder(); builder.parallelMethods(); - testKeepBeforeAfter(builder, NothingDoingTest1.class); + testKeepBeforeAfter( builder, NothingDoingTest1.class ); } @Test - public void keepBeforeAfterTwoClasses() { + public void keepBeforeAfterTwoClasses() + { ParallelComputerBuilder builder = new ParallelComputerBuilder(); - builder.useOnePool(5).parallelClasses(1).parallelMethods(2); - testKeepBeforeAfter(builder, NothingDoingTest1.class, NothingDoingTest2.class); + builder.useOnePool( 5 ).parallelClasses( 1 ).parallelMethods( 2 ); + testKeepBeforeAfter( builder, NothingDoingTest1.class, NothingDoingTest2.class ); } @Test - public void keepBeforeAfterTwoParallelClasses() { + public void keepBeforeAfterTwoParallelClasses() + { ParallelComputerBuilder builder = new ParallelComputerBuilder(); - builder.useOnePool(8).parallelClasses(2).parallelMethods(2); + builder.useOnePool( 8 ).parallelClasses( 2 ).parallelMethods( 2 ); JUnitCore core = new JUnitCore(); NothingDoingTest1.methods.clear(); - Class[] classes = {NothingDoingTest1.class, NothingDoingTest2.class, NothingDoingTest3.class}; - Result result = core.run(builder.buildComputer(), classes); - assertTrue(result.wasSuccessful()); - ArrayList methods = new ArrayList(NothingDoingTest1.methods); - assertThat(methods.size(), is(12)); - assertThat(methods.subList(9, 12), is(not(Arrays.asList("deinit", "deinit", "deinit")))); + Class[] classes = { NothingDoingTest1.class, NothingDoingTest2.class, NothingDoingTest3.class }; + Result result = core.run( builder.buildComputer(), classes ); + assertTrue( result.wasSuccessful() ); + ArrayList methods = new ArrayList( NothingDoingTest1.methods ); + assertThat( methods.size(), is( 12 ) ); + assertThat( methods.subList( 9, 12 ), is( not( Arrays.asList( "deinit", "deinit", "deinit" ) ) ) ); } - private static volatile boolean beforeShutdown; - private static volatile Runnable shutdownTask; + private static class ShutdownTest + { + Result run( final boolean useInterrupt ) + { + ParallelComputerBuilder parallelComputerBuilder = new ParallelComputerBuilder().useOnePool( 8 ); + parallelComputerBuilder.parallelSuites( 2 ); + parallelComputerBuilder.parallelClasses( 3 ); + parallelComputerBuilder.parallelMethods( 3 ); + + final ParallelComputerBuilder.PC computer = + (ParallelComputerBuilder.PC) parallelComputerBuilder.buildComputer(); + shutdownTask = new Runnable() + { + public void run() + { + Collection startedTests = computer.shutdown( useInterrupt ); + assertThat( startedTests.size(), is( not( 0 ) ) ); + } + }; + return new JUnitCore().run( computer, TestSuite.class, Class2.class, Class3.class ); + } + } - public static class Class1 { + public static class Class1 + { static volatile int concurrentMethods = 0; + static volatile int maxConcurrentMethods = 0; @Test - public void test1() throws InterruptedException { - synchronized (Class1.class) { + public void test1() + throws InterruptedException + { + synchronized ( Class1.class ) + { ++concurrentMethods; - Class1.class.wait(500); - maxConcurrentMethods = Math.max(maxConcurrentMethods, concurrentMethods--); + Class1.class.wait( 500 ); + maxConcurrentMethods = Math.max( maxConcurrentMethods, concurrentMethods-- ); } } @Test - public void test2() throws InterruptedException { + public void test2() + throws InterruptedException + { test1(); Runnable shutdownTask = ParallelComputerBuilderTest.shutdownTask; - if (shutdownTask != null) { + if ( shutdownTask != null ) + { beforeShutdown = true; shutdownTask.run(); } } } - public static class Class2 extends Class1 { - } - - public static class Class3 extends Class1 { + public static class Class2 + extends Class1 + { } - @RunWith(Suite.class) - @Suite.SuiteClasses({Class2.class, Class1.class}) - public class TestSuite { + public static class Class3 + extends Class1 + { } - public static class NothingDoingTest1 { + public static class NothingDoingTest1 + { static final Collection methods = new ConcurrentLinkedQueue(); @BeforeClass - public static void init() { - methods.add("init"); + public static void init() + { + methods.add( "init" ); } - @Test - public void a() throws InterruptedException { - Thread.sleep(5); - methods.add(getClass().getName() + "#a()"); + @AfterClass + public static void deinit() + { + methods.add( "deinit" ); } @Test - public void b() throws InterruptedException { - Thread.sleep(5); - methods.add(getClass().getName() + "#b()"); + public void a() + throws InterruptedException + { + Thread.sleep( 5 ); + methods.add( getClass().getName() + "#a()" ); } - @AfterClass - public static void deinit() { - methods.add("deinit"); + @Test + public void b() + throws InterruptedException + { + Thread.sleep( 5 ); + methods.add( getClass().getName() + "#b()" ); } } - public static class NothingDoingTest2 extends NothingDoingTest1 { + public static class NothingDoingTest2 + extends NothingDoingTest1 + { } - public static class NothingDoingTest3 extends NothingDoingTest1 { + public static class NothingDoingTest3 + extends NothingDoingTest1 + { } - @RunWith(Suite.class) - @Suite.SuiteClasses({NothingDoingTest1.class, NothingDoingTest2.class}) - public static class NothingDoingSuite { + @RunWith( Suite.class ) + @Suite.SuiteClasses( { NothingDoingTest1.class, NothingDoingTest2.class } ) + public static class NothingDoingSuite + { + } + + @RunWith( Suite.class ) + @Suite.SuiteClasses( { Class2.class, Class1.class } ) + public class TestSuite + { } } diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/RangeMatcher.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/RangeMatcher.java index ad04d6b9db..a71d853e3a 100644 --- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/RangeMatcher.java +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/RangeMatcher.java @@ -27,9 +27,11 @@ * @author Tibor Digana (tibor17) * @since 2.16 */ -final class RangeMatcher extends BaseMatcher +final class RangeMatcher + extends BaseMatcher { private final long from; + private final long to; private RangeMatcher( long from, long to ) @@ -38,14 +40,14 @@ private RangeMatcher( long from, long to ) this.to = to; } - public void describeTo( Description description ) + public static Matcher between( long from, long to ) { - description.appendValueList( "between ", " and ", "", from, to ); + return new RangeMatcher( from, to ); } - public static Matcher between( long from, long to ) + public void describeTo( Description description ) { - return new RangeMatcher( from, to ); + description.appendValueList( "between ", " and ", "", from, to ); } public boolean matches( Object o ) diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategiesTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategiesTest.java index 485b63c3b8..d8d5d33b42 100644 --- a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategiesTest.java +++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/pc/SchedulingStrategiesTest.java @@ -29,7 +29,7 @@ /** * Tests the factories in SchedulingStrategy. - * + *

    * Th point of these tests is to check {@link Task#result} if changed * from false to true after all scheduled tasks * have finished. @@ -38,118 +38,130 @@ * Then {@link Task#result} should be asserted that is true. * * @author Tibor Digana (tibor17) - * @since 2.16 - * * @see SchedulingStrategy + * @since 2.16 */ -public class SchedulingStrategiesTest { - static class Task implements Runnable { - volatile boolean result = false; - - public void run() { - result = true; - } - } - +public class SchedulingStrategiesTest +{ @Test - public void invokerStrategy() throws InterruptedException { + public void invokerStrategy() + throws InterruptedException + { SchedulingStrategy strategy = SchedulingStrategies.createInvokerStrategy(); - assertFalse(strategy.hasSharedThreadPool()); - assertTrue(strategy.canSchedule()); + assertFalse( strategy.hasSharedThreadPool() ); + assertTrue( strategy.canSchedule() ); Task task = new Task(); - strategy.schedule(task); + strategy.schedule( task ); - assertTrue(strategy.canSchedule()); + assertTrue( strategy.canSchedule() ); - assertTrue(task.result); + assertTrue( task.result ); - assertTrue(strategy.finished()); - assertFalse(strategy.canSchedule()); + assertTrue( strategy.finished() ); + assertFalse( strategy.canSchedule() ); } @Test - public void nonSharedPoolStrategy() throws InterruptedException { - SchedulingStrategy strategy = SchedulingStrategies.createParallelStrategy(2); - assertFalse(strategy.hasSharedThreadPool()); - assertTrue(strategy.canSchedule()); + public void nonSharedPoolStrategy() + throws InterruptedException + { + SchedulingStrategy strategy = SchedulingStrategies.createParallelStrategy( 2 ); + assertFalse( strategy.hasSharedThreadPool() ); + assertTrue( strategy.canSchedule() ); Task task1 = new Task(); Task task2 = new Task(); - strategy.schedule(task1); - strategy.schedule(task2); + strategy.schedule( task1 ); + strategy.schedule( task2 ); - assertTrue(strategy.canSchedule()); + assertTrue( strategy.canSchedule() ); - assertTrue(strategy.finished()); - assertFalse(strategy.canSchedule()); + assertTrue( strategy.finished() ); + assertFalse( strategy.canSchedule() ); - assertTrue(task1.result); - assertTrue(task2.result); + assertTrue( task1.result ); + assertTrue( task2.result ); } @Test(expected = NullPointerException.class) - public void sharedPoolStrategyNullPool() { - SchedulingStrategies.createParallelSharedStrategy(null); + public void sharedPoolStrategyNullPool() + { + SchedulingStrategies.createParallelSharedStrategy( null ); } @Test - public void sharedPoolStrategy() throws InterruptedException { + public void sharedPoolStrategy() + throws InterruptedException + { ExecutorService sharedPool = Executors.newCachedThreadPool(); - SchedulingStrategy strategy1 = SchedulingStrategies.createParallelSharedStrategy(sharedPool); - assertTrue(strategy1.hasSharedThreadPool()); - assertTrue(strategy1.canSchedule()); + SchedulingStrategy strategy1 = SchedulingStrategies.createParallelSharedStrategy( sharedPool ); + assertTrue( strategy1.hasSharedThreadPool() ); + assertTrue( strategy1.canSchedule() ); - SchedulingStrategy strategy2 = SchedulingStrategies.createParallelSharedStrategy(sharedPool); - assertTrue(strategy2.hasSharedThreadPool()); - assertTrue(strategy2.canSchedule()); + SchedulingStrategy strategy2 = SchedulingStrategies.createParallelSharedStrategy( sharedPool ); + assertTrue( strategy2.hasSharedThreadPool() ); + assertTrue( strategy2.canSchedule() ); Task task1 = new Task(); Task task2 = new Task(); Task task3 = new Task(); Task task4 = new Task(); - strategy1.schedule(task1); - strategy2.schedule(task2); - strategy1.schedule(task3); - strategy2.schedule(task4); + strategy1.schedule( task1 ); + strategy2.schedule( task2 ); + strategy1.schedule( task3 ); + strategy2.schedule( task4 ); - assertTrue(strategy1.canSchedule()); - assertTrue(strategy2.canSchedule()); + assertTrue( strategy1.canSchedule() ); + assertTrue( strategy2.canSchedule() ); - assertTrue(strategy1.finished()); - assertFalse(strategy1.canSchedule()); + assertTrue( strategy1.finished() ); + assertFalse( strategy1.canSchedule() ); - assertTrue(strategy2.finished()); - assertFalse(strategy2.canSchedule()); + assertTrue( strategy2.finished() ); + assertFalse( strategy2.canSchedule() ); - assertTrue(task1.result); - assertTrue(task2.result); - assertTrue(task3.result); - assertTrue(task4.result); + assertTrue( task1.result ); + assertTrue( task2.result ); + assertTrue( task3.result ); + assertTrue( task4.result ); } @Test - public void infinitePoolStrategy() throws InterruptedException { + public void infinitePoolStrategy() + throws InterruptedException + { SchedulingStrategy strategy = SchedulingStrategies.createParallelStrategyUnbounded(); - assertFalse(strategy.hasSharedThreadPool()); - assertTrue(strategy.canSchedule()); + assertFalse( strategy.hasSharedThreadPool() ); + assertTrue( strategy.canSchedule() ); Task task1 = new Task(); Task task2 = new Task(); - strategy.schedule(task1); - strategy.schedule(task2); + strategy.schedule( task1 ); + strategy.schedule( task2 ); + + assertTrue( strategy.canSchedule() ); + + assertTrue( strategy.finished() ); + assertFalse( strategy.canSchedule() ); - assertTrue(strategy.canSchedule()); + assertTrue( task1.result ); + assertTrue( task2.result ); + } - assertTrue(strategy.finished()); - assertFalse(strategy.canSchedule()); + static class Task + implements Runnable + { + volatile boolean result = false; - assertTrue(task1.result); - assertTrue(task2.result); + public void run() + { + result = true; + } } }