PageRenderTime 56ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/server/DefaultDaemonConnectionTest.groovy

https://github.com/andrewhj-mn/gradle
Groovy | 393 lines | 310 code | 68 blank | 15 comment | 10 complexity | 8b447b6652150be6be10545014d04c5c MD5 | raw file
  1. /*
  2. * Copyright 2012 the original author or authors.
  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. */
  16. package org.gradle.launcher.daemon.server
  17. import org.gradle.launcher.daemon.protocol.CloseInput
  18. import org.gradle.launcher.daemon.protocol.ForwardInput
  19. import org.gradle.launcher.daemon.server.api.StdinHandler
  20. import org.gradle.messaging.remote.internal.Connection
  21. import org.gradle.util.ConcurrentSpecification
  22. import java.util.concurrent.CountDownLatch
  23. import java.util.concurrent.TimeUnit
  24. class DefaultDaemonConnectionTest extends ConcurrentSpecification {
  25. final TestConnection connection = new TestConnection()
  26. final DefaultDaemonConnection daemonConnection = new DefaultDaemonConnection(connection, executorFactory)
  27. def cleanup() {
  28. connection.disconnect()
  29. daemonConnection.stop()
  30. }
  31. def "forwards queued input events to stdin handler until end of input received"() {
  32. StdinHandler handler = Mock()
  33. def input1 = new ForwardInput(1, "hello".bytes)
  34. def input2 = new ForwardInput(2, "hello".bytes)
  35. def closeInput = new CloseInput(3)
  36. def received = new CountDownLatch(1)
  37. when:
  38. daemonConnection.onStdin(handler)
  39. connection.queueIncoming(input1)
  40. connection.queueIncoming(input2)
  41. connection.queueIncoming(closeInput)
  42. received.await()
  43. daemonConnection.stop()
  44. then:
  45. 1 * handler.onInput(input1)
  46. 1 * handler.onInput(input2)
  47. 1 * handler.onEndOfInput() >> { received.countDown() }
  48. 0 * handler._
  49. }
  50. def "generates end of stdin event when connection disconnects"() {
  51. StdinHandler handler = Mock()
  52. def input1 = new ForwardInput(1, "hello".bytes)
  53. def received = new CountDownLatch(1)
  54. when:
  55. daemonConnection.onStdin(handler)
  56. connection.queueIncoming(input1)
  57. received.await()
  58. daemonConnection.stop()
  59. then:
  60. 1 * handler.onInput(input1) >> { received.countDown() }
  61. 1 * handler.onEndOfInput()
  62. 0 * handler._
  63. }
  64. def "generates end of stdin event when connection stopped"() {
  65. StdinHandler handler = Mock()
  66. when:
  67. daemonConnection.onStdin(handler)
  68. daemonConnection.stop()
  69. then:
  70. 1 * handler.onEndOfInput()
  71. 0 * handler._
  72. }
  73. def "buffers stdin events"() {
  74. StdinHandler handler = Mock()
  75. def input1 = new ForwardInput(1, "hello".bytes)
  76. def input2 = new ForwardInput(2, "hello".bytes)
  77. def closeInput = new CloseInput(3)
  78. def received = new CountDownLatch(1)
  79. when:
  80. connection.queueIncoming(input1)
  81. connection.queueIncoming(input2)
  82. connection.queueIncoming(closeInput)
  83. daemonConnection.onStdin(handler)
  84. received.await()
  85. daemonConnection.stop()
  86. then:
  87. 1 * handler.onInput(input1)
  88. 1 * handler.onInput(input2)
  89. 1 * handler.onEndOfInput() >> { received.countDown() }
  90. 0 * handler._
  91. }
  92. def "does not notify stdin handler once it is removed"() {
  93. StdinHandler handler = Mock()
  94. when:
  95. daemonConnection.onStdin(handler)
  96. daemonConnection.onStdin(null)
  97. connection.disconnect()
  98. daemonConnection.stop()
  99. then:
  100. 0 * handler._
  101. }
  102. def "discards queued messages on stop"() {
  103. when:
  104. connection.queueIncoming("incoming")
  105. connection.queueIncoming(new ForwardInput(1, "hello".bytes))
  106. connection.disconnect()
  107. daemonConnection.stop()
  108. then:
  109. notThrown()
  110. }
  111. def "handles case where cannot receive from connection"() {
  112. when:
  113. connection.queueBroken()
  114. daemonConnection.stop()
  115. then:
  116. notThrown()
  117. }
  118. def "handles failure to notify stdin handler"() {
  119. StdinHandler handler = Mock()
  120. def input1 = new ForwardInput(1, "hello".bytes)
  121. def input2 = new ForwardInput(2, "hello".bytes)
  122. def closeInput = new CloseInput(3)
  123. def received = new CountDownLatch(1)
  124. when:
  125. connection.queueIncoming(input1)
  126. connection.queueIncoming(input2)
  127. connection.queueIncoming(closeInput)
  128. daemonConnection.onStdin(handler)
  129. received.await()
  130. daemonConnection.stop()
  131. then:
  132. 1 * handler.onInput(input1) >> { received.countDown(); throw new RuntimeException() }
  133. 0 * handler._
  134. }
  135. def "notifies disconnect handler on disconnect"() {
  136. Runnable handler = Mock()
  137. def received = new CountDownLatch(1)
  138. when:
  139. daemonConnection.onDisconnect(handler)
  140. connection.disconnect()
  141. received.await()
  142. daemonConnection.stop()
  143. then:
  144. 1 * handler.run() >> { received.countDown() }
  145. 0 * handler._
  146. }
  147. def "notifies disconnect handler when already disconnected"() {
  148. Runnable handler = Mock()
  149. def received = new CountDownLatch(1)
  150. when:
  151. connection.disconnect()
  152. daemonConnection.onDisconnect(handler)
  153. received.await()
  154. daemonConnection.stop()
  155. then:
  156. 1 * handler.run() >> { received.countDown() }
  157. 0 * handler._
  158. }
  159. def "does not notify disconnect handler once it has been removed"() {
  160. Runnable handler = Mock()
  161. when:
  162. daemonConnection.onDisconnect(handler)
  163. daemonConnection.onDisconnect(null)
  164. connection.disconnect()
  165. daemonConnection.stop()
  166. then:
  167. 0 * handler._
  168. }
  169. def "does not notify disconnect handler on stop"() {
  170. Runnable handler = Mock()
  171. when:
  172. daemonConnection.onDisconnect(handler)
  173. daemonConnection.stop()
  174. then:
  175. 0 * handler._
  176. }
  177. def "can stop after disconnect handler fails"() {
  178. Runnable handler = Mock()
  179. def received = new CountDownLatch(1)
  180. when:
  181. connection.disconnect()
  182. daemonConnection.onDisconnect(handler)
  183. received.await()
  184. daemonConnection.stop()
  185. then:
  186. 1 * handler.run() >> { received.countDown(); throw new RuntimeException() }
  187. 0 * handler._
  188. }
  189. def "receive queues incoming messages"() {
  190. when:
  191. connection.queueIncoming("incoming1")
  192. connection.queueIncoming("incoming2")
  193. connection.queueIncoming("incoming3")
  194. def result = []
  195. result << daemonConnection.receive(20, TimeUnit.SECONDS)
  196. result << daemonConnection.receive(20, TimeUnit.SECONDS)
  197. result << daemonConnection.receive(20, TimeUnit.SECONDS)
  198. daemonConnection.stop()
  199. then:
  200. result == ["incoming1", "incoming2", "incoming3"]
  201. }
  202. def "receive blocks until message available"() {
  203. def waiting = new CountDownLatch(1)
  204. def received = new CountDownLatch(1)
  205. def result = null
  206. when:
  207. start {
  208. waiting.countDown()
  209. result = daemonConnection.receive(20, TimeUnit.SECONDS)
  210. received.countDown()
  211. }
  212. waiting.await()
  213. Thread.sleep(500)
  214. connection.queueIncoming("incoming")
  215. received.await()
  216. then:
  217. result == "incoming"
  218. }
  219. def "receive blocks until connection stopped"() {
  220. def waiting = new CountDownLatch(1)
  221. def result = null
  222. when:
  223. start {
  224. waiting.countDown()
  225. result = daemonConnection.receive(20, TimeUnit.SECONDS)
  226. }
  227. waiting.await()
  228. Thread.sleep(500)
  229. daemonConnection.stop()
  230. finished()
  231. then:
  232. result == null
  233. }
  234. def "receive blocks until connection disconnected"() {
  235. def waiting = new CountDownLatch(1)
  236. def result = null
  237. when:
  238. start {
  239. waiting.countDown()
  240. result = daemonConnection.receive(20, TimeUnit.SECONDS)
  241. }
  242. waiting.await()
  243. Thread.sleep(500)
  244. connection.disconnect()
  245. finished()
  246. then:
  247. result == null
  248. }
  249. def "receive blocks until timeout"() {
  250. when:
  251. def result = daemonConnection.receive(100, TimeUnit.MILLISECONDS)
  252. then:
  253. result == null
  254. }
  255. def "receive rethrows failure to receive from connection"() {
  256. def waiting = new CountDownLatch(1)
  257. def failure = new RuntimeException()
  258. def result = null
  259. when:
  260. start {
  261. waiting.countDown()
  262. try {
  263. daemonConnection.receive(20, TimeUnit.SECONDS)
  264. } catch (RuntimeException e) {
  265. result = e
  266. }
  267. }
  268. waiting.await()
  269. Thread.sleep(500)
  270. connection.queueBroken(failure)
  271. finished()
  272. then:
  273. result == failure
  274. }
  275. def "receive ignores stdin messages"() {
  276. when:
  277. connection.queueIncoming("incoming1")
  278. connection.queueIncoming(new ForwardInput(12, "yo".bytes))
  279. connection.queueIncoming(new CloseInput(44))
  280. connection.queueIncoming("incoming2")
  281. def result = []
  282. result << daemonConnection.receive(20, TimeUnit.SECONDS)
  283. result << daemonConnection.receive(20, TimeUnit.SECONDS)
  284. daemonConnection.stop()
  285. then:
  286. result == ["incoming1", "incoming2"]
  287. }
  288. static class TestConnection implements Connection<Object> {
  289. final Object lock = new Object()
  290. final Object endInput = new Object()
  291. final LinkedList<Object> receiveQueue = new LinkedList<Object>()
  292. void requestStop() {
  293. }
  294. void dispatch(Object message) {
  295. }
  296. void queueIncoming(Object message) {
  297. synchronized (lock) {
  298. receiveQueue << message
  299. lock.notifyAll()
  300. }
  301. }
  302. void queueBroken(Throwable failure = new RuntimeException()) {
  303. queueIncoming(failure)
  304. }
  305. Object receive() {
  306. synchronized (lock) {
  307. while (receiveQueue.empty) {
  308. lock.wait()
  309. }
  310. def message = receiveQueue.removeFirst()
  311. if (message instanceof Throwable) {
  312. message.fillInStackTrace()
  313. throw message
  314. }
  315. return message == endInput ? null : message
  316. }
  317. }
  318. void stop() {
  319. disconnect()
  320. }
  321. void disconnect() {
  322. queueIncoming(endInput)
  323. }
  324. }
  325. }