All Downloads are FREE. Search and download functionalities are using the official Maven repository.

spock.util.concurrent.AsyncConditions Maven / Gradle / Ivy

Go to download

Spock is a testing and specification framework for Java and Groovy applications. What makes it stand out from the crowd is its beautiful and highly expressive specification language. Thanks to its JUnit runner, Spock is compatible with most IDEs, build tools, and continuous integration servers. Spock is inspired from JUnit, jMock, RSpec, Groovy, Scala, Vulcans, and other fascinating life forms.

There is a newer version: 2.4-M4-groovy-4.0
Show newest version
/*
 * Copyright 2010 the original author or authors.
 *
 * Licensed 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
 *     https://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.
 */

package spock.util.concurrent;

import org.spockframework.lang.ConditionBlock;
import org.spockframework.runtime.SpockTimeoutError;
import org.spockframework.util.*;

import java.util.concurrent.*;

/**
 * Alternative to class BlockingVariable(s) that allows to evaluate conditions
 * in a thread other than the spec runner's thread(s). Rather than transferring
 * state to an expect- or then-block, it is verified right where it is captured.
 * On the upside, this can result in a more informative stack trace if the
 * evaluation of a condition fails. On the downside, the coordination between
 * threads is more explicit (number of evaluate blocks has to be specified if
 * greater than one), and the usual structure of a feature method cannot be
 * preserved (conditions are no longer located in expect-/then-block).
 *
 * 

Example: *

 * // create object under specification
 * def machine = new Machine()
 *
 * def conds = new AsyncConditions()
 *
 * // register async callback
 * machine.workDone >> { result ->
 *   conds.evaluate {
 *     assert result == WorkResult.OK
 *     // could add more explicit conditions here
 *   }
 * }
 *
 * when:
 * machine.start()
 *
 * then:
 * // wait for the evaluation to complete
 * // any exception thrown in the evaluate block will be rethrown from this method
 * conds.await()
 *
 * cleanup:
 * // shut down all threads
 * machine?.shutdown()
 * 
 *
 * @author Peter Niederwieser
 */
@ThreadSafe
public class AsyncConditions {
  private final int numEvalBlocks;
  private final CountDownLatch latch;
  private final ConcurrentLinkedQueue exceptions = new ConcurrentLinkedQueue<>();

  /**
   * Same as AsyncConditions(1).
   */
  public AsyncConditions() {
    this(1);
  }

  /**
   * Instantiates an AsyncConditions instance with the specified number
   * of evaluate blocks. await() will block until all evaluate blocks
   * have completed or a timeout expires.
   *
   * 

Note: One evaluate block may contain multiple conditions. * * @param numEvalBlocks the number of evaluate blocks that await() * should wait for */ public AsyncConditions(int numEvalBlocks) { this.numEvalBlocks = numEvalBlocks; latch = new CountDownLatch(numEvalBlocks); } /** * Evaluates the specified block, which is expected to contain * one or more explicit conditions (i.e. assert statements). * Any caught exception will be rethrown from await(). * * @param block the code block to evaluate */ @ConditionBlock public void evaluate(Runnable block) { try { block.run(); } catch (Throwable t) { exceptions.add(t); wakeUp(); } latch.countDown(); } private void wakeUp() { // this will definitely wake us up; it doesn't matter that countDown() // might get called too often long pendingEvalBlocks = latch.getCount(); for (int i = 0; i < pendingEvalBlocks; i++) latch.countDown(); } /** * Same as {@code await(1)}. */ public void await() throws Throwable { await(1); } /** * Waits until all evaluate blocks have completed or the specified timeout * (in seconds) expires. If one of the evaluate blocks throws an exception, it is rethrown * from this method. * * @param seconds the timeout (in seconds) * @throws InterruptedException if the calling thread is interrupted * @throws Throwable the first exception thrown by an evaluate block */ public void await(double seconds) throws Throwable { latch.await((long) (seconds * 1000), TimeUnit.MILLISECONDS); if (!exceptions.isEmpty()) throw exceptions.poll(); long pendingEvalBlocks = latch.getCount(); if (pendingEvalBlocks > 0) { String msg = String.format("Async conditions timed out " + "after %1.2f seconds; %d out of %d evaluate blocks did not complete in time", seconds, pendingEvalBlocks, numEvalBlocks); throw new SpockTimeoutError(seconds, msg); } } /** * Waits until all evaluate blocks have completed or the specified timeout * expires. If one of the evaluate blocks throws an exception, it is rethrown * from this method. * * @throws InterruptedException if the calling thread is interrupted * * @throws Throwable the first exception thrown by an evaluate block * * @deprecated use {@link #await(double)} instead */ @Deprecated public void await(int value, TimeUnit unit) throws Throwable { await(TimeUtil.toSeconds(value, unit)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy