2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2024 Nordix Foundation
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.controlloop.actorserviceprovider;
24 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
25 import static org.junit.jupiter.api.Assertions.assertEquals;
26 import static org.junit.jupiter.api.Assertions.assertFalse;
27 import static org.junit.jupiter.api.Assertions.assertNotNull;
28 import static org.junit.jupiter.api.Assertions.assertSame;
29 import static org.junit.jupiter.api.Assertions.assertTrue;
30 import static org.mockito.ArgumentMatchers.any;
31 import static org.mockito.Mockito.doThrow;
32 import static org.mockito.Mockito.never;
33 import static org.mockito.Mockito.spy;
34 import static org.mockito.Mockito.times;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.Collections;
41 import java.util.Iterator;
43 import java.util.TreeMap;
44 import java.util.function.Consumer;
45 import java.util.stream.Collectors;
46 import org.junit.jupiter.api.BeforeEach;
47 import org.junit.jupiter.api.Test;
48 import org.onap.policy.common.parameters.ObjectValidationResult;
49 import org.onap.policy.common.parameters.ValidationStatus;
50 import org.onap.policy.controlloop.actorserviceprovider.impl.ActorImpl;
51 import org.onap.policy.controlloop.actorserviceprovider.parameters.ActorParams;
52 import org.onap.policy.controlloop.actorserviceprovider.parameters.ParameterValidationRuntimeException;
53 import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
55 class ActorServiceTest {
56 static final String EXPECTED_EXCEPTION = "expected exception";
57 static final String ACTOR1 = "actor A";
58 static final String ACTOR2 = "actor B";
59 static final String ACTOR3 = "actor C";
60 static final String ACTOR4 = "actor D";
67 Map<String, Object> sub1;
68 Map<String, Object> sub2;
69 Map<String, Object> sub3;
70 Map<String, Object> sub4;
71 Map<String, Object> params;
77 * Initializes the fields, including a fully populated {@link #service}.
81 actor1 = spy(new ActorImpl(ACTOR1));
82 actor2 = spy(new ActorImpl(ACTOR2));
83 actor3 = spy(new ActorImpl(ACTOR3));
84 actor4 = spy(new ActorImpl(ACTOR4));
86 sub1 = Map.of("sub A", "value A", ActorParams.OPERATIONS_FIELD, Collections.emptyMap());
87 sub2 = Map.of("sub B", "value B", ActorParams.OPERATIONS_FIELD, Collections.emptyMap());
88 sub3 = Map.of("sub C", "value C", ActorParams.OPERATIONS_FIELD, Collections.emptyMap());
89 sub4 = Map.of("sub D", "value D", ActorParams.OPERATIONS_FIELD, Collections.emptyMap());
91 params = Map.of(ACTOR1, sub1, ACTOR2, sub2, ACTOR3, sub3, ACTOR4, sub4);
93 service = makeService(actor1, actor2, actor3, actor4);
97 void testActorService_testBuildList() {
99 * make a service where actors two and four have names that are duplicates of the
104 * actor0 has a higher sequence number than actor1, so it should be discarded,
105 * even though it will be examined first
107 Actor actor0 = spy(new ActorImpl(ACTOR1) {
109 public int getSequenceNumber() {
114 actor2 = spy(new ActorImpl(ACTOR1));
115 actor4 = spy(new ActorImpl(ACTOR3));
117 service = makeService(actor0, actor1, actor2, actor3, actor4);
119 assertEquals(2, service.getActorNames().size());
121 assertSame(actor1, service.getActor(ACTOR1));
122 assertSame(actor3, service.getActor(ACTOR3));
127 service.configure(params);
129 setUpOp("testDoStart", actor -> when(actor.isConfigured()).thenReturn(false), Actor::start);
135 assertTrue(service.isAlive());
137 Iterator<Actor> iter = service.getActors().iterator();
138 verify(iter.next()).start();
139 verify(iter.next(), never()).start();
140 verify(iter.next()).start();
141 verify(iter.next()).start();
143 // no additional types of operations
144 verify(actor1).configure(any());
146 // no other types of operations
147 verify(actor1, never()).stop();
148 verify(actor1, never()).shutdown();
153 service.configure(params);
156 setUpOp("testDoStop", Actor::stop, Actor::stop);
162 assertFalse(service.isAlive());
164 Iterator<Actor> iter = service.getActors().iterator();
165 verify(iter.next()).stop();
166 verify(iter.next(), times(2)).stop();
167 verify(iter.next()).stop();
168 verify(iter.next()).stop();
170 // no additional types of operations
171 verify(actor1).configure(any());
172 verify(actor1).start();
174 // no other types of operation
175 verify(actor1, never()).shutdown();
179 void testDoShutdown() {
180 service.configure(params);
183 setUpOp("testDoShutdown", Actor::shutdown, Actor::shutdown);
186 * Shut down the service.
189 assertFalse(service.isAlive());
191 Iterator<Actor> iter = service.getActors().iterator();
192 verify(iter.next()).shutdown();
193 verify(iter.next(), times(2)).shutdown();
194 verify(iter.next()).shutdown();
195 verify(iter.next()).shutdown();
197 // no additional types of operations
198 verify(actor1).configure(any());
199 verify(actor1).start();
201 // no other types of operation
202 verify(actor1, never()).stop();
206 * Applies an operation to the second actor, and then arranges for the third actor to
207 * throw an exception when its operation is performed.
209 * @param testName test name
210 * @param oper2 operation to apply to the second actor
211 * @param oper3 operation to apply to the third actor
213 private void setUpOp(String testName, Consumer<Actor> oper2, Consumer<Actor> oper3) {
214 Collection<Actor> actors = service.getActors();
215 assertEquals(4, actors.size(), testName);
217 Iterator<Actor> iter = actors.iterator();
219 // leave the first alone
222 // apply oper2 to the second actor
223 oper2.accept(iter.next());
225 // throw an exception in the third
226 oper3.accept(doThrow(new IllegalStateException(EXPECTED_EXCEPTION)).when(iter.next()));
228 // leave the fourth alone
233 void testGetActor() {
234 assertSame(actor1, service.getActor(ACTOR1));
235 assertSame(actor3, service.getActor(ACTOR3));
237 assertThatIllegalArgumentException().isThrownBy(() -> service.getActor("unknown actor"));
241 void testGetActors() {
243 assertEquals("[actor A, actor B, actor C, actor D]",
244 service.getActors().stream()
247 .collect(Collectors.toList())
253 void testGetActorNames() {
255 assertEquals("[actor A, actor B, actor C, actor D]",
256 service.getActorNames().stream()
258 .collect(Collectors.toList())
264 void testDoConfigure() {
265 service.configure(params);
266 assertTrue(service.isConfigured());
268 verify(actor1).configure(sub1);
269 verify(actor2).configure(sub2);
270 verify(actor3).configure(sub3);
271 verify(actor4).configure(sub4);
273 // no other types of operations
274 verify(actor1, never()).start();
275 verify(actor1, never()).stop();
276 verify(actor1, never()).shutdown();
280 * Tests doConfigure() where actors throw parameter validation and runtime exceptions.
283 void testDoConfigureExceptions() {
284 makeValidException(actor1);
285 makeRuntimeException(actor2);
286 makeValidException(actor3);
288 service.configure(params);
289 assertTrue(service.isConfigured());
293 * Tests doConfigure(). Arranges for the following:
295 * <li>one actor is configured, but has parameters</li>
296 * <li>another actor is configured, but has no parameters</li>
297 * <li>another actor has no parameters and is not configured</li>
301 void testDoConfigureConfigure() {
302 // need mutable parameters
303 params = new TreeMap<>(params);
305 // configure one actor
306 actor1.configure(sub1);
308 // configure another and remove its parameters
309 actor2.configure(sub2);
310 params.remove(ACTOR2);
312 // create a new, unconfigured actor
313 ActorImpl actor5 = spy(new ActorImpl("UNCONFIGURED"));
314 service = makeService(actor1, actor2, actor3, actor4, actor5);
319 service.configure(params);
320 assertTrue(service.isConfigured());
322 // this should have been configured again
323 verify(actor1, times(2)).configure(sub1);
325 // no parameters, so this should not have been configured again
326 verify(actor2).configure(sub2);
328 // these were only configured once
329 verify(actor3).configure(sub3);
330 verify(actor4).configure(sub4);
333 verify(actor5, never()).configure(any());
334 assertFalse(actor5.isConfigured());
336 // start and verify that all are started except for the last
338 verify(actor1).start();
339 verify(actor2).start();
340 verify(actor3).start();
341 verify(actor4).start();
342 verify(actor5, never()).start();
346 * Arranges for an actor to throw a validation exception when
347 * {@link Actor#configure(Map)} is invoked.
349 * @param actor actor of interest
351 private void makeValidException(Actor actor) {
352 ParameterValidationRuntimeException ex = new ParameterValidationRuntimeException(
353 new ObjectValidationResult(actor.getName(), null, ValidationStatus.INVALID, "null"));
354 doThrow(ex).when(actor).configure(any());
358 * Arranges for an actor to throw a runtime exception when
359 * {@link Actor#configure(Map)} is invoked.
361 * @param actor actor of interest
363 private void makeRuntimeException(Actor actor) {
364 IllegalStateException ex = new IllegalStateException(EXPECTED_EXCEPTION);
365 doThrow(ex).when(actor).configure(any());
369 void testLoadActors() {
370 ActorService service = new ActorService();
371 assertFalse(service.getActors().isEmpty());
372 assertNotNull(service.getActor(DummyActor.class.getSimpleName()));
376 * Makes an actor service whose {@link ActorService#loadActors()} method returns the
379 * @param actors actors to be returned
380 * @return a new actor service
382 private ActorService makeService(Actor... actors) {
383 return new ActorService() {
385 protected Iterable<Actor> loadActors() {
386 return Arrays.asList(actors);