1/*
2 * Copyright 2001-2011 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.FixtureFunSuite
20import org.scalatest.matchers.ShouldMatchers
21import _root_.java.util.concurrent.{Callable, CountDownLatch}
22import java.lang.Thread.State._
23
24// On Mac got: "ABCFEDGHI" was not equal to "ABCDEFGHI"
25// Finally Got: "ABDEFGHI" was not equal to "ABCDEFGHI" Didn't get a C, so that means
26// one thread is not seeing the other thread's changes. A concurrency bug in the test.
27/*
28class VolatileString {
29  @volatile private var value = ""
30  def s = value
31  def s_=(newValue: String) { value = newValue }
32}
33*/
34
35class ConductorFixtureSuite extends FixtureFunSuite with ConductorFixture with ShouldMatchers {
36
37  @volatile var aa = false
38  @volatile var bb = false
39  @volatile var cc = false
40  @volatile var dd = false
41  @volatile var ee = false
42  @volatile var ff = false
43  @volatile var gg = false
44  @volatile var hh = false
45  @volatile var ii = false
46
47  // On Mac, got "BACDEFGHI" was not equal to "ABCDEFGHI"
48  // And got: "ABDCEFGHI" was not equal to "ABCDEFGHI"
49  // And "ABCFDEGHI" was not equal to "ABCDEFGHI"
50  test("metronome order") { conductor => import conductor._
51
52    thread("t1") {
53      waitForBeat(1)
54      aa should be (false)
55      bb should be (false)
56      cc should be (false)
57      dd should be (false)
58      ee should be (false)
59      ff should be (false)
60      gg should be (false)
61      hh should be (false)
62      ii should be (false)
63      aa = true
64
65      waitForBeat(3)
66      aa should be (true)
67      bb should be (true)
68      cc should be (false)
69      dd should be (false)
70      ee should be (false)
71      ff should be (false)
72      gg should be (false)
73      hh should be (false)
74      ii should be (false)
75      cc = true
76
77      waitForBeat(6)
78      aa should be (true)
79      bb should be (true)
80      cc should be (true)
81      dd should be (true)
82      ee should be (true)
83      ff should be (false)
84      gg should be (false)
85      hh should be (false)
86      ii should be (false)
87      ff = true
88    }
89
90    thread("t2") {
91      waitForBeat(2)
92      aa should be (true)
93      bb should be (false)
94      cc should be (false)
95      dd should be (false)
96      ee should be (false)
97      ff should be (false)
98      gg should be (false)
99      hh should be (false)
100      ii should be (false)
101      bb = true
102
103      waitForBeat(5)
104      aa should be (true)
105      bb should be (true)
106      cc should be (true)
107      dd should be (true)
108      ee should be (false)
109      ff should be (false)
110      gg should be (false)
111      hh should be (false)
112      ii should be (false)
113      ee = true
114
115      waitForBeat(8)
116      aa should be (true)
117      bb should be (true)
118      cc should be (true)
119      dd should be (true)
120      ee should be (true)
121      ff should be (true)
122      gg should be (true)
123      hh should be (false)
124      ii should be (false)
125      hh = true
126    }
127
128    thread("t3") {
129      waitForBeat(4)
130      aa should be (true)
131      bb should be (true)
132      cc should be (true) // this failed once
133      dd should be (false)
134      ee should be (false)
135      ff should be (false)
136      gg should be (false)
137      hh should be (false)
138      ii should be (false)
139      dd = true
140
141      waitForBeat(7)
142      aa should be (true)
143      bb should be (true)
144      cc should be (true)
145      dd should be (true)
146      ee should be (true)
147      ff should be (true)
148      gg should be (false)
149      hh should be (false)
150      ii should be (false)
151      gg = true
152
153      waitForBeat(9)
154      aa should be (true)
155      bb should be (true)
156      cc should be (true)
157      dd should be (true)
158      ee should be (true)
159      ff should be (true)
160      gg should be (true)
161      hh should be (true)
162      ii should be (false)
163      ii = true
164    }
165
166    whenFinished {
167      aa should be (true)
168      bb should be (true)
169      cc should be (true)
170      dd should be (true)
171      ee should be (true)
172      ff should be (true)
173      gg should be (true)
174      hh should be (true)
175      ii should be (true)
176    }
177  }
178
179  test("wait for tick advances when threads are blocked") { conductor => import conductor._
180    var c = new CountDownLatch(3)
181
182    thread {
183      c.countDown()
184      c.await()
185    }
186
187    thread {
188      c.countDown()
189      c.await()
190    }
191
192    thread {
193      waitForBeat(1)
194      c.getCount should be (1) // Failed with 2 was not equal to 1
195      waitForBeat(2) // advances quickly
196      c.getCount should be (1)
197      c.countDown()
198    }
199
200    whenFinished {
201      c.getCount() should be (0)
202    }
203  }
204
205  // TODO: t1.getState should (be(WAITING) or be(BLOCKED)) failed with:
206  // RUNNABLE was not equal to WAITING, and RUNNABLE was not equal to BLOCKED
207  // A thread in a wait set is allowed to show up as RUNNABLE. It could be spin waiting
208  // or just wake up temporarily. So even though this fails very occasionally, it probably
209  // doesn't indicate a bug. (The ConductorMethodsSuite version of this failed on the
210  // Azul server after I cleaned up the bugs in Conductor.)
211  // I got it again on the Mac. Same error message. Decided to go ahead and allow RUNNABLE
212  // in the test, because it is actually possible. - bv 4/8/11
213  test("wait for beat blocks thread") { conductor => import conductor._
214
215    val t1 = thread {waitForBeat(2)}
216
217    thread {
218      waitForBeat(1)
219      t1.getState should (be (WAITING) or be (BLOCKED) or be (RUNNABLE))
220    }
221  }
222
223  // On Mac, failed with RUNNABLE was not equal to TERMINATED
224  // Same thing. Things can show up as RUNNABLE spuriously, so allow it in the test - bv 4/8/11
225  test("thread terminates before finish called") { conductor => import conductor._
226
227    val t1 = thread {1 should be (1)}
228    val t2 = thread {1 should be (1)}
229
230    whenFinished {
231      t1.getState should (be (TERMINATED) or be (RUNNABLE))
232      t2.getState should (be (TERMINATED) or be (RUNNABLE))
233    }
234  }
235
236  test("two thread calls return threads that both are in the same thread group") { conductor => import conductor._
237
238    val t1 = thread {waitForBeat(2)}
239    val t2 = thread {waitForBeat(2)}
240
241    thread {
242      waitForBeat(1)
243      t1.getThreadGroup should be (t2.getThreadGroup)
244    }
245  }
246
247  test("if a thread call is nested inside another thread call, both threads are in the same thread group") { conductor => import conductor._
248    thread {
249      val t2 = thread {waitForBeat(2)}
250      waitForBeat(1)
251      t2.getThreadGroup should be (currentThread.getThreadGroup)
252    }
253  }
254
255  test("whenFinished can only be called by thread that created Conductor.") { conductor => import conductor._
256    thread {
257      intercept[NotAllowedException] {
258        whenFinished {1 should be (1)}
259      }.getMessage should be ("whenFinished can only be called by the thread that created Conductor.")
260    }
261    whenFinished {1 should be (1)}
262  }
263
264
265  // TODO: I don't understand this test. Josh, can you clarify?
266  test("top level thread calls result in a running thread that is blocked such that it doesn't execute " +
267       "prior to conduct being called.") { conductor => import conductor._
268    val anotherConductor = new Conductor
269    val t = anotherConductor.thread{ 1 should be (1) }
270    thread{ t.getState should (be (WAITING) or be (RUNNABLE)) } // Got RUNNABLE. Decided to accept it.
271  }
272
273  test("nested thread calls result in a running thread that is allowed to execute immediately") (pending)
274
275  /////////////////////////////////////////////////
276
277  test("if conduct is not called from within the test itself, the test still executes (because " +
278         "ConductorFixture calls it given testWasConducted is false") (pending)
279
280  test("waitForBeat throws IllegalArgumentException if is called with a negative number") (pending)
281
282  test("calling withConductorFrozen by two threads or twice will not screw things up. In other words, " +
283          "whether or not the conductor is frozen should probably be a counting semaphor, not a flag") (pending)
284
285  test("whenFinished throws an IllegalStateException if it is invoked during the running or" +
286          "defunct phases,") (pending)
287  test("conduct throws an IllegalStateException if it is invoked more than once.") (pending)
288  // TODO: Should we have whenFinished just conduct the test and invoke the passed function when it is
289  // done conducting the test? If so, then there's no need to "register" the finish function.
290}
291