4f26419f74facadbef77232378e56388090c37e0
[policy/drools-pdp.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.drools.system.internal;
22
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertTrue;
27 import static org.mockito.Matchers.any;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.times;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33 import static org.onap.policy.common.utils.test.PolicyAssert.assertThrows;
34
35 import java.util.Arrays;
36 import java.util.List;
37 import java.util.Properties;
38 import java.util.function.BiConsumer;
39 import java.util.function.Consumer;
40 import org.junit.AfterClass;
41 import org.junit.Before;
42 import org.junit.BeforeClass;
43 import org.junit.Test;
44 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
45 import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
46 import org.onap.policy.common.endpoints.event.comm.TopicSink;
47 import org.onap.policy.common.endpoints.event.comm.TopicSource;
48 import org.onap.policy.drools.controller.DroolsController;
49 import org.onap.policy.drools.controller.DroolsControllerFactory;
50 import org.onap.policy.drools.features.PolicyControllerFeatureAPI;
51 import org.onap.policy.drools.persistence.SystemPersistence;
52 import org.onap.policy.drools.protocol.configuration.DroolsConfiguration;
53 import org.onap.policy.drools.system.internal.AggregatedPolicyController.Factory;
54 import org.powermock.reflect.Whitebox;
55
56 public class AggregatedPolicyControllerTest {
57
58     /**
59      * Name of the "factory" field within the {@link AggregatedPolicyController} class.
60      */
61     private static final String FACTORY_FIELD = "factory";
62
63     private static final String AGG_NAME = "agg-name";
64     private static final String SINK_TOPIC1 = "sink-a";
65     private static final String SINK_TOPIC2 = "sink-b";
66     private static final String SOURCE_TOPIC1 = "source-a";
67     private static final String SOURCE_TOPIC2 = "source-b";
68
69     private static final String EXPECTED = "expected exception";
70
71     private static final String MY_EVENT = "my-event";
72
73     private static final String ARTIFACT1 = "artifact-a";
74     private static final String GROUP1 = "group-a";
75     private static final String VERSION1 = "version-a";
76
77     private static final String ARTIFACT2 = "artifact-b";
78     private static final String GROUP2 = "group-b";
79     private static final String VERSION2 = "version-b";
80
81     private static Factory savedFactory;
82
83     private Properties properties;
84     private Factory factory;
85     private TopicEndpoint endpointMgr;
86     private List<TopicSource> sources;
87     private TopicSource source1;
88     private TopicSource source2;
89     private List<TopicSink> sinks;
90     private TopicSink sink1;
91     private TopicSink sink2;
92     private SystemPersistence persist;
93     private DroolsControllerFactory droolsFactory;
94     private DroolsController drools;
95     private DroolsConfiguration config;
96     private List<PolicyControllerFeatureAPI> providers;
97     private PolicyControllerFeatureAPI prov1;
98     private PolicyControllerFeatureAPI prov2;
99     private AggregatedPolicyController apc;
100
101     @BeforeClass
102     public static void setUpBeforeClass() {
103         savedFactory = Whitebox.getInternalState(AggregatedPolicyController.class, FACTORY_FIELD);
104     }
105
106     @AfterClass
107     public static void tearDownAfterClass() {
108         Whitebox.setInternalState(AggregatedPolicyController.class, FACTORY_FIELD, savedFactory);
109     }
110
111     /**
112      * Initializes the object to be tested.
113      */
114     @Before
115     public void setUp() {
116         properties = new Properties();
117
118         source1 = mock(TopicSource.class);
119         source2 = mock(TopicSource.class);
120         when(source1.getTopic()).thenReturn(SOURCE_TOPIC1);
121         when(source2.getTopic()).thenReturn(SOURCE_TOPIC2);
122
123         sink1 = mock(TopicSink.class);
124         sink2 = mock(TopicSink.class);
125         when(sink1.getTopic()).thenReturn(SINK_TOPIC1);
126         when(sink2.getTopic()).thenReturn(SINK_TOPIC2);
127
128         sources = Arrays.asList(source1, source2);
129         sinks = Arrays.asList(sink1, sink2);
130
131         endpointMgr = mock(TopicEndpoint.class);
132         when(endpointMgr.addTopicSources(any())).thenReturn(sources);
133         when(endpointMgr.addTopicSinks(any())).thenReturn(sinks);
134
135         persist = mock(SystemPersistence.class);
136
137         drools = mock(DroolsController.class);
138         when(drools.start()).thenReturn(true);
139         when(drools.stop()).thenReturn(true);
140         when(drools.offer(any(), any())).thenReturn(true);
141         when(drools.deliver(any(), any())).thenReturn(true);
142         when(drools.lock()).thenReturn(true);
143         when(drools.unlock()).thenReturn(true);
144         when(drools.getArtifactId()).thenReturn(ARTIFACT1);
145         when(drools.getGroupId()).thenReturn(GROUP1);
146         when(drools.getVersion()).thenReturn(VERSION1);
147
148         config = mock(DroolsConfiguration.class);
149         when(config.getArtifactId()).thenReturn(ARTIFACT2);
150         when(config.getGroupId()).thenReturn(GROUP2);
151         when(config.getVersion()).thenReturn(VERSION2);
152
153         droolsFactory = mock(DroolsControllerFactory.class);
154         when(droolsFactory.build(any(), any(), any())).thenReturn(drools);
155
156         prov1 = mock(PolicyControllerFeatureAPI.class);
157         prov2 = mock(PolicyControllerFeatureAPI.class);
158
159         providers = Arrays.asList(prov1, prov2);
160
161         factory = mock(Factory.class);
162         Whitebox.setInternalState(AggregatedPolicyController.class, FACTORY_FIELD, factory);
163
164         when(factory.getEndpointManager()).thenReturn(endpointMgr);
165         when(factory.getPersistenceManager()).thenReturn(persist);
166         when(factory.getDroolsFactory()).thenReturn(droolsFactory);
167         when(factory.getFeatureProviders()).thenReturn(providers);
168
169         apc = new AggregatedPolicyController(AGG_NAME, properties);
170     }
171
172     @Test
173     public void testFactory() {
174         assertNotNull(savedFactory);
175
176         Factory factory = new Factory();
177         assertNotNull(factory.getDroolsFactory());
178         assertNotNull(factory.getEndpointManager());
179         assertNotNull(factory.getFeatureProviders());
180         assertNotNull(factory.getPersistenceManager());
181     }
182
183     @Test
184     public void testAggregatedPolicyController_() {
185         verify(persist).storeController(AGG_NAME, properties);
186     }
187
188     @Test(expected = IllegalArgumentException.class)
189     public void testInitDrools_Ex() {
190         when(factory.getDroolsFactory()).thenThrow(new RuntimeException(EXPECTED));
191         new AggregatedPolicyController(AGG_NAME, properties);
192     }
193
194     @Test(expected = IllegalArgumentException.class)
195     public void testInitDrools_Error() {
196         when(factory.getDroolsFactory()).thenThrow(new LinkageError(EXPECTED));
197         new AggregatedPolicyController(AGG_NAME, properties);
198     }
199
200     @Test
201     public void testUpdateDrools_ConfigVariations() {
202
203         // config should return same values as current controller
204         when(config.getArtifactId()).thenReturn(ARTIFACT1.toUpperCase());
205         when(config.getGroupId()).thenReturn(GROUP1.toUpperCase());
206         when(config.getVersion()).thenReturn(VERSION1.toUpperCase());
207
208         assertTrue(apc.updateDrools(config));
209
210         // number of times store should have been called
211         int count = 0;
212
213         // invoked once during construction, but shouldn't be invoked during update
214         verify(persist, times(++count)).storeController(any(), any());
215
216
217         // different artifact
218         when(config.getArtifactId()).thenReturn(ARTIFACT2);
219
220         assertTrue(apc.updateDrools(config));
221
222         // should be invoked during update
223         verify(persist, times(++count)).storeController(any(), any());
224
225
226         // different group
227         when(config.getArtifactId()).thenReturn(ARTIFACT1);
228         when(config.getGroupId()).thenReturn(GROUP2);
229
230         assertTrue(apc.updateDrools(config));
231
232         // should be invoked during update
233         verify(persist, times(++count)).storeController(any(), any());
234
235
236         // different version
237         when(config.getGroupId()).thenReturn(GROUP1);
238         when(config.getVersion()).thenReturn(VERSION2);
239
240         assertTrue(apc.updateDrools(config));
241
242         // should be invoked during update
243         verify(persist, times(++count)).storeController(any(), any());
244
245
246         /*
247          * Exception case.
248          */
249         when(drools.lock()).thenThrow(new IllegalArgumentException(EXPECTED));
250         when(drools.unlock()).thenThrow(new IllegalArgumentException(EXPECTED));
251
252         assertFalse(apc.updateDrools(config));
253     }
254
255     @Test
256     public void testUpdateDrools_LockVariations() {
257         // not locked
258         apc.updateDrools(config);
259         verify(drools, never()).lock();
260         verify(drools).unlock();
261
262         // locked
263         setUp();
264         apc.lock();
265         apc.updateDrools(config);
266         verify(drools, times(2)).lock();
267         verify(drools, never()).unlock();
268     }
269
270     @Test
271     public void testUpdateDrools_AliveVariations() {
272         // not started
273         apc.updateDrools(config);
274         verify(drools, never()).start();
275         verify(drools).stop();
276
277         // started
278         setUp();
279         apc.start();
280         apc.updateDrools(config);
281         verify(drools, times(2)).start();
282         verify(drools, never()).stop();
283     }
284
285     @Test
286     public void testGetName() {
287         assertEquals(AGG_NAME, apc.getName());
288     }
289
290     @Test
291     public void testStart() {
292         // arrange for first provider to throw exceptions
293         when(prov1.beforeStart(any())).thenThrow(new RuntimeException(EXPECTED));
294         when(prov1.afterStart(any())).thenThrow(new RuntimeException(EXPECTED));
295
296         // arrange for first sink to throw exception
297         when(sink1.start()).thenThrow(new RuntimeException(EXPECTED));
298
299         // start it
300         assertTrue(apc.start());
301
302         assertTrue(apc.isAlive());
303
304         verify(prov1).beforeStart(apc);
305         verify(prov2).beforeStart(apc);
306
307         verify(source1).register(apc);
308         verify(source2).register(apc);
309
310         verify(sink1).start();
311         verify(sink2).start();
312
313         verify(prov1).afterStart(apc);
314         verify(prov2).afterStart(apc);
315
316         checkBeforeAfter(
317             (prov, flag) -> when(prov.beforeStart(apc)).thenReturn(flag),
318             (prov, flag) -> when(prov.afterStart(apc)).thenReturn(flag),
319             () -> apc.start(),
320             prov -> verify(prov).beforeStart(apc),
321             () -> verify(source1).register(apc),
322             prov -> verify(prov).afterStart(apc));
323     }
324
325     @Test
326     public void testStart_AlreadyStarted() {
327         apc.start();
328
329         // re-start it
330         assertTrue(apc.start());
331
332         assertTrue(apc.isAlive());
333
334         // these should now have been called twice
335         verify(prov1, times(2)).beforeStart(apc);
336         verify(prov2, times(2)).beforeStart(apc);
337
338         // these should still only have been called once
339         verify(source1).register(apc);
340         verify(sink1).start();
341         verify(prov1).afterStart(apc);
342     }
343
344     @Test
345     public void testStart_Locked() {
346         apc.lock();
347
348         // start it
349         assertThrows(IllegalStateException.class, () -> apc.start());
350
351         assertFalse(apc.isAlive());
352
353         // should call beforeStart(), but stop after that
354         verify(prov1).beforeStart(apc);
355         verify(prov2).beforeStart(apc);
356
357         verify(source1, never()).register(apc);
358         verify(sink1, never()).start();
359         verify(prov1, never()).afterStart(apc);
360     }
361
362     @Test
363     public void testStop() {
364         // arrange for first provider to throw exceptions
365         when(prov1.beforeStop(any())).thenThrow(new RuntimeException(EXPECTED));
366         when(prov1.afterStop(any())).thenThrow(new RuntimeException(EXPECTED));
367
368         // start it
369         apc.start();
370
371         // now stop it
372         assertTrue(apc.stop());
373
374         assertFalse(apc.isAlive());
375
376         verify(prov1).beforeStop(apc);
377         verify(prov2).beforeStop(apc);
378
379         verify(source1).unregister(apc);
380         verify(source2).unregister(apc);
381
382         verify(prov1).afterStop(apc);
383         verify(prov2).afterStop(apc);
384
385         // ensure no shutdown operations were called
386         verify(prov1, never()).beforeShutdown(apc);
387         verify(droolsFactory, never()).shutdown(drools);
388         verify(prov2, never()).afterShutdown(apc);
389
390         checkBeforeAfter(
391             (prov, flag) -> when(prov.beforeStop(apc)).thenReturn(flag),
392             (prov, flag) -> when(prov.afterStop(apc)).thenReturn(flag),
393             () -> {
394                 apc.start();
395                 apc.stop();
396             },
397             prov -> verify(prov).beforeStop(apc),
398             () -> verify(source1).unregister(apc),
399             prov -> verify(prov).afterStop(apc));
400     }
401
402     @Test
403     public void testStop_AlreadyStopped() {
404         apc.start();
405         apc.stop();
406
407         // now re-stop it
408         assertTrue(apc.stop());
409
410         // called again
411         verify(prov1, times(2)).beforeStop(apc);
412         verify(prov2, times(2)).beforeStop(apc);
413
414         // should NOT be called again
415         verify(source1).unregister(apc);
416         verify(prov1).afterStop(apc);
417     }
418
419     @Test
420     public void testShutdown() {
421         // arrange for first provider to throw exceptions
422         when(prov1.beforeShutdown(any())).thenThrow(new RuntimeException(EXPECTED));
423         when(prov1.afterShutdown(any())).thenThrow(new RuntimeException(EXPECTED));
424
425         // start it
426         apc.start();
427
428         // now shut it down
429         apc.shutdown();
430
431         verify(prov1).beforeShutdown(apc);
432         verify(prov2).beforeShutdown(apc);
433
434         assertFalse(apc.isAlive());
435
436         verify(prov1).afterStop(apc);
437         verify(prov2).afterStop(apc);
438
439         verify(droolsFactory).shutdown(drools);
440
441         verify(prov1).afterShutdown(apc);
442         verify(prov2).afterShutdown(apc);
443
444         // ensure no halt operation was called
445         verify(prov1, never()).beforeHalt(apc);
446
447         checkBeforeAfter(
448             (prov, flag) -> when(prov.beforeShutdown(apc)).thenReturn(flag),
449             (prov, flag) -> when(prov.afterShutdown(apc)).thenReturn(flag),
450             () -> {
451                 apc.start();
452                 apc.shutdown();
453             },
454             prov -> verify(prov).beforeShutdown(apc),
455             () -> verify(source1).unregister(apc),
456             prov -> verify(prov).afterShutdown(apc));
457     }
458
459     @Test
460     public void testHalt() {
461         // arrange for first provider to throw exceptions
462         when(prov1.beforeHalt(any())).thenThrow(new RuntimeException(EXPECTED));
463         when(prov1.afterHalt(any())).thenThrow(new RuntimeException(EXPECTED));
464
465         // start it
466         apc.start();
467
468         // now halt it
469         apc.halt();
470
471         verify(prov1).beforeHalt(apc);
472         verify(prov2).beforeHalt(apc);
473
474         assertFalse(apc.isAlive());
475
476         verify(prov1).beforeStop(apc);
477         verify(prov2).beforeStop(apc);
478
479         verify(droolsFactory).destroy(drools);
480         verify(persist).deleteController(AGG_NAME);
481
482         verify(prov1).afterHalt(apc);
483         verify(prov2).afterHalt(apc);
484
485         // ensure no shutdown operation was called
486         verify(prov1, never()).beforeShutdown(apc);
487
488         checkBeforeAfter(
489             (prov, flag) -> when(prov.beforeHalt(apc)).thenReturn(flag),
490             (prov, flag) -> when(prov.afterHalt(apc)).thenReturn(flag),
491             () -> {
492                 apc.start();
493                 apc.halt();
494             },
495             prov -> verify(prov).beforeHalt(apc),
496             () -> verify(source1).unregister(apc),
497             prov -> verify(prov).afterHalt(apc));
498     }
499
500     @Test
501     public void testOnTopicEvent() {
502         // arrange for first provider to throw exceptions
503         when(prov1.beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT))
504                         .thenThrow(new RuntimeException(EXPECTED));
505         when(prov1.afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true))
506                         .thenThrow(new RuntimeException(EXPECTED));
507
508         // start it
509         apc.start();
510
511         // now offer it
512         apc.onTopicEvent(CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
513
514         verify(prov1).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
515         verify(prov2).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
516
517         verify(drools).offer(SOURCE_TOPIC1, MY_EVENT);
518
519         verify(prov1).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true);
520         verify(prov2).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true);
521
522         checkBeforeAfter(
523             (prov, flag) -> when(prov.beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT))
524                             .thenReturn(flag),
525             (prov, flag) -> when(
526                             prov.afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true))
527                                             .thenReturn(flag),
528             () -> {
529                 apc.start();
530                 apc.onTopicEvent(CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
531             },
532             prov -> verify(prov).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT),
533             () -> verify(drools).offer(SOURCE_TOPIC1, MY_EVENT),
534             prov -> verify(prov).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true));
535     }
536
537     @Test
538     public void testOnTopicEvent_Locked() {
539         // start it
540         apc.start();
541
542         apc.lock();
543
544         // now offer it
545         apc.onTopicEvent(CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
546
547         verify(prov1).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
548         verify(prov2).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
549
550         // never gets this far
551         verify(drools, never()).offer(SOURCE_TOPIC1, MY_EVENT);
552         verify(prov1, never()).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true);
553     }
554
555     @Test
556     public void testOnTopicEvent_NotStarted() {
557
558         // offer it
559         apc.onTopicEvent(CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
560
561         verify(prov1).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
562         verify(prov2).beforeOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT);
563
564         // never gets this far
565         verify(drools, never()).offer(SOURCE_TOPIC1, MY_EVENT);
566         verify(prov1, never()).afterOffer(apc, CommInfrastructure.NOOP, SOURCE_TOPIC1, MY_EVENT, true);
567     }
568
569     @Test
570     public void testDeliver_testInitSinks() {
571         // arrange for first provider to throw exceptions
572         when(prov1.beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT))
573                         .thenThrow(new RuntimeException(EXPECTED));
574         when(prov1.afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true))
575                         .thenThrow(new RuntimeException(EXPECTED));
576
577         // start it
578         apc.start();
579
580         // now offer it
581         assertTrue(apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT));
582
583         verify(prov1).beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT);
584         verify(prov2).beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT);
585
586         verify(drools).deliver(sink1, MY_EVENT);
587         verify(drools, never()).deliver(sink2, MY_EVENT);
588
589         verify(prov1).afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true);
590         verify(prov2).afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true);
591
592         // offer to the other topic
593         assertTrue(apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC2, MY_EVENT));
594
595         // now both topics should show one message delivered
596         verify(drools).deliver(sink1, MY_EVENT);
597         verify(drools).deliver(sink2, MY_EVENT);
598
599         checkBeforeAfter(
600             (prov, flag) -> when(prov.beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT))
601                             .thenReturn(flag),
602             (prov, flag) -> when(
603                             prov.afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true))
604                                             .thenReturn(flag),
605             () -> {
606                 apc.start();
607                 apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT);
608             },
609             prov -> verify(prov).beforeDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT),
610             () -> verify(drools).deliver(sink1, MY_EVENT),
611             prov -> verify(prov).afterDeliver(apc, CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT, true));
612     }
613
614     @Test(expected = IllegalArgumentException.class)
615     public void testDeliver_NullTopic() {
616         apc.start();
617         apc.deliver(CommInfrastructure.NOOP, null, MY_EVENT);
618     }
619
620     @Test(expected = IllegalArgumentException.class)
621     public void testDeliver_EmptyTopic() {
622         apc.start();
623         apc.deliver(CommInfrastructure.NOOP, "", MY_EVENT);
624     }
625
626     @Test(expected = IllegalArgumentException.class)
627     public void testDeliver_NullEvent() {
628         apc.start();
629         apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, null);
630     }
631
632     @Test(expected = IllegalStateException.class)
633     public void testDeliver_NotStarted() {
634         // do NOT start
635         apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT);
636     }
637
638     @Test(expected = IllegalStateException.class)
639     public void testDeliver_Locked() {
640         apc.start();
641         apc.lock();
642         apc.deliver(CommInfrastructure.NOOP, SINK_TOPIC1, MY_EVENT);
643     }
644
645     @Test(expected = IllegalArgumentException.class)
646     public void testDeliver_UnknownTopic() {
647         apc.start();
648         apc.deliver(CommInfrastructure.NOOP, "unknown-topic", MY_EVENT);
649     }
650
651     @Test
652     public void testIsAlive() {
653         assertFalse(apc.isAlive());
654
655         apc.start();
656         assertTrue(apc.isAlive());
657
658         apc.stop();
659         assertFalse(apc.isAlive());
660     }
661
662     @Test
663     public void testLock() {
664         // arrange for first provider to throw exceptions
665         when(prov1.beforeLock(any())).thenThrow(new RuntimeException(EXPECTED));
666         when(prov1.afterLock(any())).thenThrow(new RuntimeException(EXPECTED));
667
668         // start it
669         apc.start();
670
671         // now lock it
672         assertTrue(apc.lock());
673
674         verify(prov1).beforeLock(apc);
675         verify(prov2).beforeLock(apc);
676
677         assertTrue(apc.isLocked());
678
679         verify(drools).lock();
680
681         verify(prov1).afterLock(apc);
682         verify(prov2).afterLock(apc);
683
684         checkBeforeAfter(
685             (prov, flag) -> when(prov.beforeLock(apc)).thenReturn(flag),
686             (prov, flag) -> when(prov.afterLock(apc)).thenReturn(flag),
687             () -> {
688                 apc.start();
689                 apc.lock();
690             },
691             prov -> verify(prov).beforeLock(apc),
692             () -> verify(drools).lock(),
693             prov -> verify(prov).afterLock(apc));
694     }
695
696     @Test
697     public void testLock_AlreadyLocked() {
698         apc.start();
699         apc.lock();
700
701         // now re-lock it
702         assertTrue(apc.lock());
703
704         // these should be invoked a second time
705         verify(prov1, times(2)).beforeLock(apc);
706         verify(prov2, times(2)).beforeLock(apc);
707
708         assertTrue(apc.isLocked());
709
710         // these shouldn't be invoked a second time
711         verify(drools).lock();
712         verify(prov1).afterLock(apc);
713     }
714
715     @Test
716     public void testUnlock() {
717         // arrange for first provider to throw exceptions
718         when(prov1.beforeUnlock(any())).thenThrow(new RuntimeException(EXPECTED));
719         when(prov1.afterUnlock(any())).thenThrow(new RuntimeException(EXPECTED));
720
721         // start it
722         apc.start();
723         apc.lock();
724
725         // now unlock it
726         assertTrue(apc.unlock());
727
728         verify(prov1).beforeUnlock(apc);
729         verify(prov2).beforeUnlock(apc);
730
731         assertFalse(apc.isLocked());
732
733         verify(drools).unlock();
734
735         verify(prov1).afterUnlock(apc);
736         verify(prov2).afterUnlock(apc);
737
738         checkBeforeAfter(
739             (prov, flag) -> when(prov.beforeUnlock(apc)).thenReturn(flag),
740             (prov, flag) -> when(prov.afterUnlock(apc)).thenReturn(flag),
741             () -> {
742                 apc.start();
743                 apc.lock();
744                 apc.unlock();
745             },
746             prov -> verify(prov).beforeUnlock(apc),
747             () -> verify(drools).unlock(),
748             prov -> verify(prov).afterUnlock(apc));
749     }
750
751     @Test
752     public void testUnlock_NotLocked() {
753         apc.start();
754
755         // now unlock it
756         assertTrue(apc.unlock());
757
758         verify(prov1).beforeUnlock(apc);
759         verify(prov2).beforeUnlock(apc);
760
761         assertFalse(apc.isLocked());
762
763         // these shouldn't be invoked
764         verify(drools, never()).unlock();
765         verify(prov1, never()).afterLock(apc);
766     }
767
768     @Test
769     public void testIsLocked() {
770         assertFalse(apc.isLocked());
771
772         apc.lock();
773         assertTrue(apc.isLocked());
774
775         apc.unlock();
776         assertFalse(apc.isLocked());
777     }
778
779     @Test
780     public void testGetTopicSources() {
781         assertEquals(sources, apc.getTopicSources());
782     }
783
784     @Test
785     public void testGetTopicSinks() {
786         assertEquals(sinks, apc.getTopicSinks());
787     }
788
789     @Test
790     public void testGetDrools() {
791         assertEquals(drools, apc.getDrools());
792     }
793
794     @Test
795     public void testGetProperties() {
796         assertEquals(properties, apc.getProperties());
797     }
798
799     @Test
800     public void testToString() {
801         assertTrue(apc.toString().startsWith("AggregatedPolicyController ["));
802     }
803
804     /**
805      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries
806      * combinations where beforeXxx and afterXxx return {@code true} and {@code false}.
807      *
808      * @param setBefore function to set the return value of a provider's beforeXxx method
809      * @param setAfter function to set the return value of a provider's afterXxx method
810      * @param action invokes the operation
811      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
812      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
813      *        and the afterXxx loop was invoked
814      * @param verifyAfter verifies that a provider's afterXxx method was invoked
815      */
816     private void checkBeforeAfter(BiConsumer<PolicyControllerFeatureAPI, Boolean> setBefore,
817                     BiConsumer<PolicyControllerFeatureAPI, Boolean> setAfter, Runnable action,
818                     Consumer<PolicyControllerFeatureAPI> verifyBefore, Runnable verifyMiddle,
819                     Consumer<PolicyControllerFeatureAPI> verifyAfter) {
820
821         checkBeforeAfter_FalseFalse(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
822         checkBeforeAfter_FalseTrue(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
823         checkBeforeAfter_TrueFalse(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
824
825         // don't need to test true-true, as it's behavior is a subset of true-false
826     }
827
828     /**
829      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
830      * case where both the beforeXxx and afterXxx methods return {@code false}.
831      *
832      * @param setBefore function to set the return value of a provider's beforeXxx method
833      * @param setAfter function to set the return value of a provider's afterXxx method
834      * @param action invokes the operation
835      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
836      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
837      *        and the afterXxx loop was invoked
838      * @param verifyAfter verifies that a provider's afterXxx method was invoked
839      */
840     private void checkBeforeAfter_FalseFalse(BiConsumer<PolicyControllerFeatureAPI, Boolean> setBefore,
841                     BiConsumer<PolicyControllerFeatureAPI, Boolean> setAfter, Runnable action,
842                     Consumer<PolicyControllerFeatureAPI> verifyBefore, Runnable verifyMiddle,
843                     Consumer<PolicyControllerFeatureAPI> verifyAfter) {
844
845         setUp();
846
847         // configure for the test
848         setBefore.accept(prov1, false);
849         setBefore.accept(prov2, false);
850
851         setAfter.accept(prov1, false);
852         setAfter.accept(prov2, false);
853
854         // run the action
855         action.run();
856
857         // verify that various methods were invoked
858         verifyBefore.accept(prov1);
859         verifyBefore.accept(prov2);
860
861         verifyMiddle.run();
862
863         verifyAfter.accept(prov1);
864         verifyAfter.accept(prov2);
865     }
866
867     /**
868      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
869      * case where the first provider's afterXxx returns {@code true}, while the others
870      * return {@code false}.
871      *
872      * @param setBefore function to set the return value of a provider's beforeXxx method
873      * @param setAfter function to set the return value of a provider's afterXxx method
874      * @param action invokes the operation
875      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
876      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
877      *        and the afterXxx loop was invoked
878      * @param verifyAfter verifies that a provider's afterXxx method was invoked
879      */
880     private void checkBeforeAfter_FalseTrue(BiConsumer<PolicyControllerFeatureAPI, Boolean> setBefore,
881                     BiConsumer<PolicyControllerFeatureAPI, Boolean> setAfter, Runnable action,
882                     Consumer<PolicyControllerFeatureAPI> verifyBefore, Runnable verifyMiddle,
883                     Consumer<PolicyControllerFeatureAPI> verifyAfter) {
884
885         setUp();
886
887         // configure for the test
888         setBefore.accept(prov1, false);
889         setBefore.accept(prov2, false);
890
891         setAfter.accept(prov1, true);
892         setAfter.accept(prov2, false);
893
894         // run the action
895         action.run();
896
897         // verify that various methods were invoked
898         verifyBefore.accept(prov1);
899         verifyBefore.accept(prov2);
900
901         verifyMiddle.run();
902
903         verifyAfter.accept(prov1);
904         assertThrows(AssertionError.class, () -> verifyAfter.accept(prov2));
905     }
906
907     /**
908      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
909      * case where the first provider's beforeXxx returns {@code true}, while the others
910      * return {@code false}.
911      *
912      * @param setBefore function to set the return value of a provider's beforeXxx method
913      * @param setAfter function to set the return value of a provider's afterXxx method
914      * @param action invokes the operation
915      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
916      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
917      *        and the afterXxx loop was invoked
918      * @param verifyAfter verifies that a provider's afterXxx method was invoked
919      */
920     private void checkBeforeAfter_TrueFalse(BiConsumer<PolicyControllerFeatureAPI, Boolean> setBefore,
921                     BiConsumer<PolicyControllerFeatureAPI, Boolean> setAfter, Runnable action,
922                     Consumer<PolicyControllerFeatureAPI> verifyBefore, Runnable verifyMiddle,
923                     Consumer<PolicyControllerFeatureAPI> verifyAfter) {
924
925         setUp();
926
927         // configure for the test
928         setBefore.accept(prov1, true);
929         setBefore.accept(prov2, false);
930
931         setAfter.accept(prov1, false);
932         setAfter.accept(prov2, false);
933
934         // run the action
935         action.run();
936
937         // verify that various methods were invoked
938         verifyBefore.accept(prov1);
939
940         // remaining methods should not have been invoked
941         assertThrows(AssertionError.class, () -> verifyBefore.accept(prov2));
942
943         assertThrows(AssertionError.class, () -> verifyMiddle.run());
944
945         assertThrows(AssertionError.class, () -> verifyAfter.accept(prov1));
946         assertThrows(AssertionError.class, () -> verifyAfter.accept(prov2));
947     }
948 }