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