Hi Helena, Looks great! I think this will make writing multithreaded tests a lot less error prone. A couple of minor comments below:
I'm not sure we need runInSeries, someone could do that without using this test helper. Could we make the default timeout something more like 5 minutes? See this thread https://markmail.org/thread/yrcvdlbixqojl6oh -Dan On Wed, Jul 18, 2018 at 12:16 PM, Dale Emery <dem...@pivotal.io> wrote: > Hi Helena, > > I like it! > > A few suggestions: > > - For the durations (repeatForDuration() and setTimeout()), consider using > a java.time.Duration rather than an int to express the duration. The > Duration class lets most of the code not have to keep track of the time > units. The caller creates the Duration using whatever time unit they want. > When some other code needs to convert it to a specific time unit, there are > methods for that. The rest of the code doesn’t have to care about the time > units. And there are methods for adding, subtracting, and comparing > Durations and Instants, all handling the time units nicely. > > - Consider adding a version of runAndExpectValue that takes a Predicate > instead of a value. And maybe one taking a Hamcrest Matcher. > > - Consider specifying the type of the Callable, and preferably for the > value passed to runAndExpectValue()). And definitely for Predicates and > Matchers, if you add those methods. > > Cheers, > Dale > > > On Jul 18, 2018, at 11:53 AM, Helena Bales <hba...@pivotal.io> wrote: > > > > Hello All, > > > > Below I have included a proposed API for a concurrency test helper. The > > goal of this test helper is to make it easier for tests to use multiple > > threads. I believe providing a helper for this will reduce the number of > > tests that incorrectly use threads, and help make our DUnit tests more > > readable. > > > > I would like feedback on the API below. I have provided the method > > signatures that I plan on implementing, as well as an example usage. > Please > > let me know if there are any methods that seem unnecessary or if there > are > > any important use cases that I missed here. > > > > Thank you, > > Helena Bales > > > > public class ConcurrencyTestHelper { > > private List<Callable<T>> toRun; > > private Integer timeout; > > > > private final Integer DEFAULT_TIMEOUT = 30; > > > > /** > > * A default constructor that sets the timeout to a default of 30 > seconds > > */ > > public ConcurrencyTestHelper() { > > toRun = new ArrayList<Callable<T>>(); > > timeout = DEFAULT_TIMEOUT; > > } > > > > /** > > * Run the given Runnable and assert that an exception of the given > > type is thrown. The thread > > * will be cancelled when the class timeout is reached. > > * @param runnable a Runnable that throws an exception and responds > > to cancellation signal > > * @param exceptionType a Class of Exception that is expected to be > > thrown by the runnable > > */ > > public void runAndExpectException(Runnable runnable, Class > exceptionType); > > > > /** > > * Run the given Runnable and assert that no exceptions are thrown. > > The thread will be cancelled > > * when the class timeout is reached. > > * @param runnable a Runnable that does not throw an exception and > > responds to cancellation signal > > */ > > public void runAndExpectNoException(Runnable runnable); > > > > /** > > * Run the given Callable and assert that the result of that > > callable is the given expected value. > > * The thread will be cancelled when the class timeout is reached. > > * @param callable a Callable that returns an object, does not throw > > exceptions and responds to > > * cancellation signal > > * @param value an Object that is the expected return value from the > Callable. > > */ > > public void runAndExpectValue(Callable callable, Object value); > > > > /** > > * Run the given runnable in a loop until the rest of the threads > > complete. Fails if the runnable > > * throws an exception during any iteration. After the rest of the > > threads complete the loop will > > * not restart the runnable. The running iteration will be cancelled > > when the timeout set on the > > * class is reached. A cancellation will result in a test failure. > > * @param runnable a Runnable that does not throw an exception and > > responds to cancellation signal > > */ > > public void repeatUntilAllThreadsComplete(Runnable runnable); > > > > /** > > * Run the given runnable in a loop for some number of iterations. > > Fails if the runnable throws an > > * exception in any iteration. After the number of iterations has > > been met, the loop will not > > * restart the runnable. The running iteration of the Runnable will > > be cancelled when the > > * timeout set on the class is reached, regardless of if the number > > of desired iterations has > > * been reached. A cancellation will result in a test failure. > > * @param runnable a Runnable that does not throw an exception and > > responds to cancellation signal > > * @param iterations a maximum number of iterations to repeat for > > */ > > public void repeatForIterations(Runnable runnable, Integer iterations); > > > > /** > > * Run the given runnable in a loop for a certain duration. Fails if > > the runnable throws an > > * exception in any iteration. After the duration has been exceeded, > > the loop will not restart > > * the runnable, however the current iteration will not be cancelled > > until the timeout value for > > * the class has been reached. A cancellation will result in a test > failure. > > * @param runnable a Runnable that does not throw exceptions and > > responds to cancellation signal > > * @param duration a maximum amount of time to repeat for > > */ > > public void repeatForDuration(Runnable runnable, Integer duration); > > > > /** > > * Set the timeout for the threads. After the timeout is reached, > > the threads will be interrupted > > * and will throw a CancellationException > > */ > > public void setTimeout(Integer seconds) { > > timeout = seconds; > > } > > > > /** > > * Runs all callables in toRun in parallel and fail if threads' > > conditions were not met. Runs > > * until timeout is reached. > > * @throws InterruptedException if interrupted before timeout > > */ > > public void executeInParallel(); > > > > /** > > * Runs all callables in toRun in the order that they were added and > > fail if threads' conditions > > * were not met. Runs until timeout is reached. > > * @throws InterruptedException if interrupted before timeout > > */ > > public void executeInSeries(); > > } > > > > // example usage: > > new ConcurrentTestHelper() > > .runAndExpectException(runnable, UnsupportedOperationException. > class) > > .runAndExpectNoException(runnable) > > .runAndExpectValue(callable, result) > > .repeatUntilAllOtherRunnablesFinish(runnable) > > .repeatFor(runnable, time) //keep doing this for time amount of > time > > .repeat(runnable, iterations) //keep doing this for N times > > .setTimeout(seconds) //Throws exception if test takes too long > > .executeInParallel(); > > — > Dale Emery > dem...@pivotal.io > > > > >