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