1/* 2 * Copyright 2001-2008 Artima, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package org.scalatest.concurrent 17 18import org.scalatest._ 19import org.scalatest.fixture.FixtureSuite 20 21/** 22 * Trait that can pass a new <code>Conductor</code> fixture into tests. 23 * 24 * <p> 25 * Here's an example of the use of this trait to test the <code>ArrayBlockingQueue</code> 26 * class from <code>java.util.concurrent</code>: 27 * </p> 28 * 29 * <pre class="stHighlight"> 30 * import org.scalatest.fixture.FixtureFunSuite 31 * import org.scalatest.concurrent.ConductorFixture 32 * import org.scalatest.matchers.ShouldMatchers 33 * import java.util.concurrent.ArrayBlockingQueue 34 * 35 * class ArrayBlockingQueueSuite extends FixtureFunSuite with ConductorFixture with ShouldMatchers { 36 * 37 * test("calling put on a full queue blocks the producer thread") { conductor => import conductor._ 38 * 39 * val buf = new ArrayBlockingQueue[Int](1) 40 * 41 * thread("producer") { 42 * buf put 42 43 * buf put 17 44 * beat should be (1) 45 * } 46 * 47 * thread("consumer") { 48 * waitForBeat(1) 49 * buf.take should be (42) 50 * buf.take should be (17) 51 * } 52 * 53 * whenFinished { 54 * buf should be ('empty) 55 * } 56 * } 57 * 58 * test("calling take on an empty queue blocks the consumer thread") { conductor => import conductor._ 59 * 60 * val buf = new ArrayBlockingQueue[Int](1) 61 * 62 * thread("producer") { 63 * waitForBeat(1) 64 * buf put 42 65 * buf put 17 66 * } 67 * 68 * thread("consumer") { 69 * buf.take should be (42) 70 * buf.take should be (17) 71 * beat should be (1) 72 * } 73 * 74 * whenFinished { 75 * buf should be ('empty) 76 * } 77 * } 78 * } 79 * </pre> 80 * 81 * <p> 82 * For an explanation of how these tests work, see the documentation for <a href="Conductor.html"><code>Conductor</code></a>. 83 * </p> 84 * 85 * @author Bill Venners 86 */ 87trait ConductorFixture { this: FixtureSuite => 88 89 /** 90 * Defines type <code>Fixture</code> to be <code>Conductor</code>. 91 */ 92 type FixtureParam = Conductor 93 94 /** 95 * Creates a new <code>Conductor</code>, passes the <code>Conductor</code> to the 96 * specified test function, and ensures that <code>conduct</code> gets invoked 97 * on the <code>Conductor</code>. 98 * 99 * <p> 100 * After the test function returns (so long as it returns normally and doesn't 101 * complete abruptly with an exception), this method will determine whether the 102 * <code>conduct</code> method has already been called (by invoking 103 * <code>conductingHasBegun</code> on the <code>Conductor</code>). If not, 104 * this method will invoke <code>conduct</code> to ensure that the 105 * multi-threaded scenario is actually conducted. 106 * </p> 107 */ 108 def withFixture(test: OneArgTest) { 109 val conductor = new Conductor 110 test(conductor) 111 if (!conductor.conductingHasBegun) 112 conductor.conduct() 113 } 114} 115