package book.example.threading.executor; import book.example.common.searching.AuctionDescription; import book.example.common.searching.AuctionHouse; import book.example.common.searching.StubAuctionHouse; import book.example.common.searching.async.AuctionSearchConsumer; import org.hamcrest.Matcher; import org.jmock.Expectations; import org.jmock.Mockery; import org.jmock.States; import org.jmock.integration.junit4.JMock; import org.jmock.integration.junit4.JUnit4Mockery; import org.jmock.lib.concurrent.Synchroniser; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static java.util.Arrays.asList; import static java.util.concurrent.TimeUnit.SECONDS; import static org.hamcrest.Matchers.anything; @RunWith(JMock.class) public class AuctionSearchStressTests { private static final HashSet<String> KEYWORDS = new HashSet<String>(asList("sheep", "cheese")); private static final int NUMBER_OF_AUCTION_HOUSES = 80; private static final int NUMBER_OF_SEARCHES = 20; Synchroniser synchroniser = new Synchroniser(); Mockery context = new JUnit4Mockery() {{ setThreadingPolicy(synchroniser); }}; final AuctionSearchConsumer consumer = context.mock(AuctionSearchConsumer.class, "consumer"); final States searching = context.states("searching"); final ExecutorService executor = Executors.newCachedThreadPool(); // Change to v2, v3, v4 to test different versions... AuctionSearch_v4 search = new AuctionSearch_v4(executor, auctionHouses(), consumer); @Test(timeout = 500) public void onlyOneAuctionSearchFinishedNotificationPerSearch() throws InterruptedException { context.checking(new Expectations() {{ ignoring(consumer).auctionSearchFound(with(anyResults())); }}); for (int i = 0; i < NUMBER_OF_SEARCHES; i++) { completeASearch(); } } private void completeASearch() throws InterruptedException { searching.startsAs("in progress"); context.checking(new Expectations() {{ exactly(1).of(consumer).auctionSearchFinished(); then(searching.is("done")); }}); search.search(KEYWORDS); synchroniser.waitUntil(searching.is("done")); } private List<AuctionHouse> auctionHouses() { ArrayList<AuctionHouse> auctionHouses = new ArrayList<AuctionHouse>(); for (int i = 0; i < NUMBER_OF_AUCTION_HOUSES; i++) { auctionHouses.add(stubbedAuctionHouse(i)); } return auctionHouses; } private AuctionHouse stubbedAuctionHouse(int i) { StubAuctionHouse house = new StubAuctionHouse("house" + i); house.willReturnSearchResults( KEYWORDS, asList(new AuctionDescription(house, "id" + i, "description"))); return house; } @After public void cleanUp() throws InterruptedException { executor.shutdown(); executor.awaitTermination(1, SECONDS); } private Matcher<List<AuctionDescription>> anyResults() { return anything(); } }