Commit | Line | Data |
---|---|---|
d91063d0 BH |
1 | /******************************************************************************* |
2 | * Copyright (c) 2014 Ericsson | |
3 | * | |
4 | * All rights reserved. This program and the accompanying materials are | |
5 | * made available under the terms of the Eclipse Public License v1.0 which | |
6 | * accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
8 | * | |
9 | * Contributors: | |
10 | * Bernd Hufmann - Initial API and implementation | |
11 | *******************************************************************************/ | |
12 | ||
13 | package org.eclipse.linuxtools.tmf.core.tests.signal; | |
14 | ||
15 | import static org.junit.Assert.assertEquals; | |
16 | import static org.junit.Assert.fail; | |
17 | ||
18 | import java.util.ArrayList; | |
19 | import java.util.List; | |
20 | import java.util.concurrent.CountDownLatch; | |
21 | ||
22 | import org.eclipse.linuxtools.tmf.core.component.TmfComponent; | |
23 | import org.eclipse.linuxtools.tmf.core.signal.TmfEndSynchSignal; | |
24 | import org.eclipse.linuxtools.tmf.core.signal.TmfSignal; | |
25 | import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler; | |
26 | import org.eclipse.linuxtools.tmf.core.signal.TmfSignalManager; | |
27 | import org.eclipse.linuxtools.tmf.core.signal.TmfStartSynchSignal; | |
28 | import org.junit.After; | |
29 | import org.junit.Before; | |
30 | import org.junit.Test; | |
31 | ||
32 | /** | |
33 | * Test suite for {@link TmfSignalManager} | |
34 | * | |
35 | * @author Bernd Hufmann | |
36 | */ | |
37 | public class TmfSignalManagerTest { | |
38 | ||
39 | private TestSignalSender signalSender; | |
40 | ||
41 | /** | |
42 | * Pre-test setup | |
43 | */ | |
44 | @Before | |
45 | public void setUp() { | |
46 | signalSender = new TestSignalSender(); | |
47 | } | |
48 | ||
49 | /** | |
50 | * After-test cleanup | |
51 | */ | |
52 | @After | |
53 | public void tearDown() { | |
54 | signalSender.dispose(); | |
55 | } | |
56 | ||
57 | // ------------------------------------------------------------------------ | |
58 | // Test cases | |
59 | // ------------------------------------------------------------------------ | |
60 | ||
61 | /** | |
62 | * Test send and receive including synch signals. | |
63 | */ | |
64 | @Test | |
65 | public void testSendReceive() { | |
66 | final int NB_HANDLERS = 10; | |
67 | TestSignalHandler[] signalReceivers = new TestSignalHandler[NB_HANDLERS]; | |
68 | for (int i = 0; i < NB_HANDLERS; i++) { | |
69 | signalReceivers[i] = new TestSignalHandler(); | |
70 | } | |
71 | final TestSignal1 firstSignal = new TestSignal1(signalSender); | |
72 | final TestSignal2 secondSignal = new TestSignal2(signalSender); | |
73 | ||
74 | final Class<?>[] expectedOrder = new Class[] { | |
75 | TmfStartSynchSignal.class, TestSignal1.class, TmfEndSynchSignal.class, | |
76 | TmfStartSynchSignal.class, TestSignal2.class, TmfEndSynchSignal.class }; | |
77 | ||
78 | try { | |
79 | signalSender.sendSignal(firstSignal); | |
80 | signalSender.sendSignal(secondSignal); | |
81 | ||
82 | for (int i = 0; i < NB_HANDLERS; i++) { | |
83 | assertEquals(expectedOrder.length, signalReceivers[i].receivedSignals.size()); | |
84 | ||
85 | for (int k = 0; k < expectedOrder.length; k++) { | |
86 | assertEquals(signalReceivers[i].receivedSignals.get(k).getClass(), expectedOrder[k]); | |
87 | } | |
88 | ||
89 | for (int k = 0; k < expectedOrder.length; k += 3) { | |
90 | // Verify signal IDs | |
91 | int startSyncId = signalReceivers[i].receivedSignals.get(k).getReference(); | |
92 | int signalId = signalReceivers[i].receivedSignals.get(k + 1).getReference(); | |
93 | int endSyncId = signalReceivers[i].receivedSignals.get(k + 2).getReference(); | |
94 | ||
95 | assertEquals(startSyncId, signalId); | |
96 | assertEquals(startSyncId, endSyncId); | |
97 | } | |
98 | } | |
99 | } finally { | |
100 | // Make sure that handlers are disposed in any case (success or not success) | |
101 | for (int i = 0; i < NB_HANDLERS; i++) { | |
102 | signalReceivers[i].dispose(); | |
103 | } | |
104 | } | |
105 | } | |
106 | ||
107 | /** | |
108 | * Test nesting signals. Verify that they are handled in the same thread. | |
109 | */ | |
110 | @Test | |
111 | public void testNestedSignals() { | |
112 | TestSignalHandlerNested signalResender = new TestSignalHandlerNested(); | |
113 | TestSignalHandler signalReceiver = new TestSignalHandler(); | |
114 | ||
115 | final TestSignal1 mainSignal = new TestSignal1(signalSender); | |
116 | ||
117 | final Class<?>[] expectedOrder = new Class[] { | |
118 | TmfStartSynchSignal.class, | |
119 | TmfStartSynchSignal.class, | |
120 | TestSignal2.class, | |
121 | TmfEndSynchSignal.class, | |
122 | TmfStartSynchSignal.class, | |
123 | TmfStartSynchSignal.class, | |
124 | TestSignal4.class, | |
125 | TmfEndSynchSignal.class, | |
126 | TestSignal3.class, | |
127 | TmfEndSynchSignal.class, | |
128 | TestSignal1.class, | |
129 | TmfEndSynchSignal.class | |
130 | }; | |
131 | ||
132 | /* | |
133 | * Index of signals in array signalReceiver.receivedSignals which have | |
134 | * to have the same signal ID. | |
135 | */ | |
136 | ||
137 | final int[] sameSigNoIndex = new int[] { | |
138 | 0, 10, 11, 1, 2, 3, 4, 8, 9, 5, 6, 7 | |
139 | }; | |
140 | ||
141 | try { | |
142 | signalSender.sendSignal(mainSignal); | |
143 | ||
144 | assertEquals(expectedOrder.length, signalReceiver.receivedSignals.size()); | |
145 | ||
146 | for (int i = 0; i < expectedOrder.length; i++) { | |
147 | assertEquals(signalReceiver.receivedSignals.get(i).getClass(), expectedOrder[i]); | |
148 | } | |
149 | ||
150 | for (int i = 0; i < sameSigNoIndex.length; i+=3) { | |
151 | // Verify signal IDs | |
152 | int startSyncId = signalReceiver.receivedSignals.get(sameSigNoIndex[i]).getReference(); | |
153 | int signalId = signalReceiver.receivedSignals.get(sameSigNoIndex[i + 1]).getReference(); | |
154 | int endSyncId = signalReceiver.receivedSignals.get(sameSigNoIndex[i + 2]).getReference(); | |
155 | assertEquals(startSyncId, signalId); | |
156 | assertEquals(startSyncId, endSyncId); | |
157 | } | |
158 | } finally { | |
159 | // Make sure that handlers are disposed in any case (success or not success) | |
160 | signalResender.dispose(); | |
161 | signalReceiver.dispose(); | |
162 | } | |
163 | } | |
164 | ||
165 | /** | |
166 | * Test concurrent signals. Verify that they are handled one after each | |
167 | * other. | |
168 | */ | |
169 | @Test | |
170 | public void testConcurrentSignals() { | |
171 | ||
172 | TestSignalHandlerNested signalResender = new TestSignalHandlerNested(); | |
173 | TestSignalHandler signalReceiver = new TestSignalHandler(false, null); | |
174 | ||
175 | /* | |
176 | * Test of synchronization of signal manager. | |
177 | * | |
178 | * The order of received signals is either set of signals triggered by | |
179 | * thread1 before set of signals triggered by thread2 or the other way | |
180 | * around. | |
181 | * | |
182 | * If both received sets were interleaved then the synchronization of | |
183 | * the signal manager would be not working. | |
184 | */ | |
185 | ||
186 | final Class<?>[] expectedOrder1 = new Class[] { | |
187 | TestSignal2.class, TestSignal4.class, TestSignal3.class, TestSignal1.class, // signals triggered by thread 1 | |
188 | TestSignal4.class // signal triggered by thread2 | |
189 | }; | |
190 | ||
191 | final Class<?>[] expectedOrder2 = new Class[] { | |
192 | TestSignal4.class, // signal triggered by thread2 | |
193 | TestSignal2.class, TestSignal4.class, TestSignal3.class, TestSignal1.class // signals triggered by thread 1 | |
194 | }; | |
195 | ||
196 | /* | |
197 | * Run it multiple times so that both expected order are triggered | |
198 | */ | |
199 | try { | |
200 | for (int k = 0; k < 10; k++) { | |
201 | // Latch to ensure that both threads are started | |
202 | final CountDownLatch startLatch = new CountDownLatch(2); | |
203 | // Latch to ensure that signals are send roughly at the same time | |
204 | final CountDownLatch sendLatch = new CountDownLatch(1); | |
205 | // Latch to ensure that both treads are finished | |
206 | final CountDownLatch endLatch = new CountDownLatch(2); | |
207 | ||
208 | signalReceiver.receivedSignals.clear(); | |
209 | ||
210 | Thread senderThread1 = new Thread() { | |
211 | @Override | |
212 | public void run() { | |
213 | startLatch.countDown(); | |
214 | try { | |
215 | sendLatch.await(); | |
216 | } catch (InterruptedException e) { | |
217 | } | |
218 | signalSender.sendSignal(new TestSignal1(signalSender)); | |
219 | endLatch.countDown(); | |
220 | } | |
221 | }; | |
222 | ||
223 | Thread senderThread2 = new Thread() { | |
224 | @Override | |
225 | public void run() { | |
226 | startLatch.countDown(); | |
227 | try { | |
228 | sendLatch.await(); | |
229 | } catch (InterruptedException e) { | |
230 | } | |
231 | signalSender.sendSignal(new TestSignal4(signalSender)); | |
232 | endLatch.countDown(); | |
233 | } | |
234 | }; | |
235 | ||
236 | senderThread1.start(); | |
237 | senderThread2.start(); | |
238 | try { | |
239 | startLatch.await(); | |
240 | } catch (InterruptedException e) { | |
241 | } | |
242 | sendLatch.countDown(); | |
243 | ||
244 | try { | |
245 | endLatch.await(); | |
246 | } catch (InterruptedException e) { | |
247 | } | |
248 | ||
249 | assertEquals(expectedOrder1.length, signalReceiver.receivedSignals.size()); | |
250 | boolean pass = true; | |
251 | for (int i = 0; i < expectedOrder1.length; i++) { | |
252 | if (!signalReceiver.receivedSignals.get(i).getClass().equals(expectedOrder1[i])) { | |
253 | pass = false; | |
254 | break; | |
255 | } | |
256 | } | |
257 | ||
258 | if (!pass) { | |
259 | for (int i = 0; i < expectedOrder2.length; i++) { | |
260 | if (!signalReceiver.receivedSignals.get(i).getClass().equals(expectedOrder2[i])) { | |
261 | fail("Concurrent signal test failure!"); | |
262 | } | |
263 | } | |
264 | } | |
265 | } | |
266 | } finally { | |
267 | // Make sure that handlers are disposed in any case (success or not success) | |
268 | signalResender.dispose(); | |
269 | signalReceiver.dispose(); | |
270 | } | |
271 | } | |
272 | ||
273 | /** | |
274 | * Test broadcastAsync() | |
275 | */ | |
276 | @Test | |
277 | public void testBroadcastAsync() { | |
278 | TestSignalHandlerNested signalResender = new TestSignalHandlerNested(false); | |
279 | ||
280 | final int NB_HANDLERS = 10; | |
281 | final CountDownLatch latch = new CountDownLatch(NB_HANDLERS); | |
282 | TestSignalHandler[] signalReceivers = new TestSignalHandler[NB_HANDLERS]; | |
283 | for (int i = 0; i < NB_HANDLERS; i++) { | |
284 | signalReceivers[i] = new TestSignalHandler(false, latch); | |
285 | } | |
286 | ||
287 | final Class<?>[] expectedOrder = new Class[] { | |
288 | TestSignal1.class, TestSignal2.class, TestSignal3.class, TestSignal4.class | |
289 | }; | |
290 | ||
291 | try { | |
292 | final TestSignal1 mainSignal = new TestSignal1(signalSender); | |
293 | signalSender.sendSignal(mainSignal); | |
294 | ||
295 | try { | |
296 | latch.await(); | |
297 | } catch (InterruptedException e) { | |
298 | } | |
299 | ||
300 | for (int i = 0; i < NB_HANDLERS; i++) { | |
301 | assertEquals(expectedOrder.length, signalReceivers[i].receivedSignals.size()); | |
302 | for (int k = 0; k < expectedOrder.length; k++) { | |
303 | assertEquals(signalReceivers[i].receivedSignals.get(k).getClass(), expectedOrder[k]); | |
304 | } | |
305 | } | |
306 | } finally { | |
307 | // Make sure that handlers are disposed in any case (success or not success) | |
308 | for (int i = 0; i < NB_HANDLERS; i++) { | |
309 | signalReceivers[i].dispose(); | |
310 | } | |
311 | signalResender.dispose(); | |
312 | } | |
313 | } | |
314 | ||
315 | // ------------------------------------------------------------------------ | |
316 | // Helper classes | |
317 | // ------------------------------------------------------------------------ | |
318 | ||
319 | /** | |
320 | * Signal sender | |
321 | */ | |
322 | private class TestSignalSender extends TmfComponent { | |
323 | ||
324 | TestSignalSender() { | |
325 | super("TestSignalSender"); | |
326 | } | |
327 | ||
328 | /** | |
329 | * Send a signal | |
330 | * | |
331 | * @param signal | |
332 | * main signal to send | |
333 | */ | |
334 | public void sendSignal(TmfSignal signal) { | |
335 | broadcast(signal); | |
336 | } | |
337 | } | |
338 | ||
339 | /** | |
340 | * Signal handler implementation for testing nested signals. | |
341 | * Needs to be public so TmfSignalManager can see it. | |
342 | */ | |
343 | public class TestSignalHandlerNested extends AbstractBaseSignalHandler { | |
344 | ||
345 | private boolean sync; | |
346 | ||
347 | /** | |
348 | * Constructor | |
349 | */ | |
350 | private TestSignalHandlerNested() { | |
351 | this(true); | |
352 | } | |
353 | ||
354 | /** | |
355 | * Constructor | |
356 | * | |
357 | * @param sync | |
358 | * log sync signals | |
359 | * | |
360 | */ | |
361 | private TestSignalHandlerNested(boolean sync) { | |
362 | super("TestSignalHandlerNested", false); | |
363 | this.sync = sync; | |
364 | TmfSignalManager.deregister(this); | |
365 | TmfSignalManager.registerVIP(this); | |
366 | } | |
367 | ||
368 | /** | |
369 | * Receive a signal of type TestSignal1. | |
370 | * | |
371 | * @param signal | |
372 | * Signal received | |
373 | */ | |
374 | @TmfSignalHandler | |
375 | public void receiveSignal1(final TestSignal1 signal) { | |
376 | if (sync) { | |
377 | broadcast(new TestSignal2(signal.getSource())); | |
378 | broadcast(new TestSignal3(signal.getSource())); | |
379 | } else { | |
380 | broadcastAsync(new TestSignal2(signal.getSource())); | |
381 | broadcastAsync(new TestSignal3(signal.getSource())); | |
382 | } | |
383 | } | |
384 | ||
385 | /** | |
386 | * Receive a signal of type TestSignal3. | |
387 | * | |
388 | * @param signal | |
389 | * Signal received | |
390 | */ | |
391 | @TmfSignalHandler | |
392 | public void receiveSignal3(final TestSignal3 signal) { | |
393 | if (sync) { | |
394 | broadcast(new TestSignal4(signal.getSource())); | |
395 | } else { | |
396 | broadcastAsync(new TestSignal4(signal.getSource())); | |
397 | } | |
398 | } | |
399 | } | |
400 | ||
401 | /** | |
402 | * Signal handler implementation for testing of sending and receiving | |
403 | * signals. | |
404 | */ | |
405 | public class TestSignalHandler extends AbstractBaseSignalHandler { | |
406 | ||
407 | private CountDownLatch latch; | |
408 | ||
409 | /** | |
410 | * Constructor | |
411 | * | |
412 | */ | |
413 | private TestSignalHandler() { | |
414 | this(true, null); | |
415 | } | |
416 | ||
417 | /** | |
418 | * Constructor | |
419 | * | |
420 | * @param logSyncSigs | |
421 | * log sync signals | |
422 | * @param latch | |
423 | * latch to count down when receiving last signal | |
424 | * (TmfSingal4) | |
425 | */ | |
426 | private TestSignalHandler(boolean logSyncSigs, CountDownLatch latch) { | |
427 | super("TestSignalHandler", logSyncSigs); | |
428 | this.latch = latch; | |
429 | } | |
430 | ||
431 | /** | |
432 | * Receive a signal of type TestSignal1. | |
433 | * | |
434 | * @param signal | |
435 | * Signal received | |
436 | */ | |
437 | @TmfSignalHandler | |
438 | public void receiveSignal1(final TestSignal1 signal) { | |
439 | receivedSignals.add(signal); | |
440 | } | |
441 | ||
442 | /** | |
443 | * Receive a signal of type TestSignal2. | |
444 | * | |
445 | * @param signal | |
446 | * Signal received | |
447 | */ | |
448 | @TmfSignalHandler | |
449 | public void receiveSignal2(final TestSignal2 signal) { | |
450 | receivedSignals.add(signal); | |
451 | } | |
452 | ||
453 | /** | |
454 | * Receive a signal of type TestSignal3. | |
455 | * | |
456 | * @param signal | |
457 | * Signal received | |
458 | */ | |
459 | @TmfSignalHandler | |
460 | public void receiveSignal3(final TestSignal3 signal) { | |
461 | receivedSignals.add(signal); | |
462 | } | |
463 | ||
464 | /** | |
465 | * Receive a signal of type TestSignal4. | |
466 | * | |
467 | * @param signal | |
468 | * Signal received | |
469 | */ | |
470 | @TmfSignalHandler | |
471 | public void receiveSignal4(final TestSignal4 signal) { | |
472 | receivedSignals.add(signal); | |
473 | if (latch != null) { | |
474 | latch.countDown(); | |
475 | } | |
476 | } | |
477 | } | |
478 | ||
479 | /** | |
480 | * Base signal handler for start and end sync signals. | |
481 | */ | |
482 | public abstract class AbstractBaseSignalHandler extends TmfComponent { | |
483 | List<TmfSignal> receivedSignals = new ArrayList<>(); | |
484 | private boolean logSyncSigs; | |
485 | ||
486 | private AbstractBaseSignalHandler(String name, boolean logSyncSignal) { | |
487 | super(name); | |
488 | this.logSyncSigs = logSyncSignal; | |
489 | } | |
490 | ||
491 | /** | |
492 | * Receive a signal of type TmfStartSynchSignal. | |
493 | * | |
494 | * @param signal | |
495 | * Signal received | |
496 | */ | |
497 | @TmfSignalHandler | |
498 | public void receiveStartSynch(final TmfStartSynchSignal signal) { | |
499 | if (logSyncSigs) { | |
500 | receivedSignals.add(signal); | |
501 | } | |
502 | } | |
503 | ||
504 | /** | |
505 | * Receive a signal of type TmfEndSynchSignal. | |
506 | * | |
507 | * @param signal | |
508 | * Signal received | |
509 | */ | |
510 | @TmfSignalHandler | |
511 | public void receiveEndSynch(final TmfEndSynchSignal signal) { | |
512 | if (logSyncSigs) { | |
513 | receivedSignals.add(signal); | |
514 | } | |
515 | } | |
516 | } | |
517 | ||
518 | ||
519 | /** | |
520 | * Test Signal object | |
521 | */ | |
522 | private class TestSignal1 extends TmfSignal { | |
523 | ||
524 | public TestSignal1(Object source) { | |
525 | super(source); | |
526 | } | |
527 | } | |
528 | ||
529 | /** | |
530 | * Test Signal object | |
531 | */ | |
532 | private class TestSignal2 extends TmfSignal { | |
533 | ||
534 | public TestSignal2(Object source) { | |
535 | super(source); | |
536 | } | |
537 | } | |
538 | ||
539 | /** | |
540 | * Test Signal object | |
541 | */ | |
542 | private class TestSignal3 extends TmfSignal { | |
543 | ||
544 | public TestSignal3(Object source) { | |
545 | super(source); | |
546 | } | |
547 | } | |
548 | ||
549 | /** | |
550 | * Test Signal object | |
551 | */ | |
552 | private class TestSignal4 extends TmfSignal { | |
553 | ||
554 | public TestSignal4(Object source) { | |
555 | super(source); | |
556 | } | |
557 | } | |
558 | } |