2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2023 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.eventmanager;
24 import static org.assertj.core.api.Assertions.assertThat;
25 import static org.assertj.core.api.Assertions.assertThatCode;
26 import static org.junit.jupiter.api.Assertions.assertEquals;
27 import static org.junit.jupiter.api.Assertions.assertFalse;
28 import static org.junit.jupiter.api.Assertions.assertNotNull;
29 import static org.junit.jupiter.api.Assertions.assertSame;
30 import static org.junit.jupiter.api.Assertions.assertTrue;
31 import static org.mockito.Mockito.mock;
32 import static org.mockito.Mockito.verify;
33 import static org.mockito.Mockito.when;
35 import java.io.IOException;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.UUID;
39 import java.util.concurrent.ExecutorService;
40 import org.junit.jupiter.api.BeforeEach;
41 import org.junit.jupiter.api.Test;
42 import org.mockito.ArgumentCaptor;
43 import org.onap.policy.common.utils.coder.Coder;
44 import org.onap.policy.common.utils.coder.CoderException;
45 import org.onap.policy.common.utils.coder.StandardYamlCoder;
46 import org.onap.policy.common.utils.io.Serializer;
47 import org.onap.policy.common.utils.resources.ResourceUtils;
48 import org.onap.policy.controlloop.ControlLoopException;
49 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
50 import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
51 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
52 import org.onap.policy.controlloop.ophistory.OperationHistoryDataManager;
53 import org.onap.policy.controlloop.ophistory.OperationHistoryDataManagerStub;
54 import org.onap.policy.drools.core.lock.LockCallback;
55 import org.onap.policy.drools.core.lock.LockImpl;
56 import org.onap.policy.drools.core.lock.LockState;
57 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
58 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
60 class ControlLoopEventManagerTest {
61 private static final UUID REQ_ID = UUID.randomUUID();
62 private static final String EXPECTED_EXCEPTION = "expected exception";
63 private static final String CL_NAME = "my-closed-loop-name";
64 private static final String POLICY_NAME = "my-policy-name";
65 private static final String POLICY_SCOPE = "my-scope";
66 private static final String POLICY_VERSION = "1.2.3";
67 private static final String LOCK1 = "my-lock-A";
68 private static final String LOCK2 = "my-lock-B";
69 private static final Coder yamlCoder = new StandardYamlCoder();
70 private static final String MY_KEY = "def";
72 private final ExecutorService executor = mock(ExecutorService.class);
73 private final EventManagerServices services = mock(EventManagerServices.class);
74 private final OperationHistoryDataManager dataMgr = mock(OperationHistoryDataManager.class);
76 private long preCreateTimeMs;
77 private List<LockImpl> locks;
78 private ToscaPolicy tosca;
79 private ControlLoopParams params;
80 private ControlLoopEventManager mgr;
86 public void setUp() throws ControlLoopException, CoderException {
87 when(services.getDataManager()).thenReturn(dataMgr);
89 params = new ControlLoopParams();
90 params.setClosedLoopControlName(CL_NAME);
91 params.setPolicyName(POLICY_NAME);
92 params.setPolicyScope(POLICY_SCOPE);
93 params.setPolicyVersion(POLICY_VERSION);
95 loadPolicy("eventManager/event-mgr-simple.yaml");
97 locks = new ArrayList<>();
99 preCreateTimeMs = System.currentTimeMillis();
101 MyManager.executor = executor;
102 MyManager.locks = locks;
104 mgr = new MyManager(services, params, REQ_ID);
108 void testConstructor() {
109 assertEquals(POLICY_NAME, mgr.getPolicyName());
111 assertTrue(mgr.isActive());
112 assertEquals(CL_NAME, mgr.getClosedLoopControlName());
113 assertSame(REQ_ID, mgr.getRequestId());
114 assertEquals(POLICY_NAME, mgr.getPolicyName());
115 assertEquals(POLICY_VERSION, mgr.getPolicyVersion());
116 assertNotNull(mgr.getProcessor());
117 assertThat(mgr.getEndTimeMs()).isGreaterThanOrEqualTo(preCreateTimeMs);
121 void testGetCreateCount() throws ControlLoopException {
122 long original = ControlLoopEventManager.getCreateCount();
124 new MyManager(services, params, REQ_ID);
125 assertEquals(original + 1, ControlLoopEventManager.getCreateCount());
127 new MyManager(services, params, REQ_ID);
128 assertEquals(original + 2, ControlLoopEventManager.getCreateCount());
132 void testIsActive() throws Exception {
133 mgr = new ControlLoopEventManager(services, params, REQ_ID);
134 assertTrue(mgr.isActive());
136 var mgr2 = Serializer.roundTrip(mgr);
137 assertFalse(mgr2.isActive());
141 void testDestroy() throws IOException {
142 mgr.requestLock(LOCK1);
143 mgr.requestLock(LOCK2);
144 mgr.requestLock(LOCK1);
146 // ensure destroy() doesn't throw an exception if the object is deserialized
147 var mgr2 = Serializer.roundTrip(mgr);
148 assertThatCode(() -> mgr2.destroy()).doesNotThrowAnyException();
150 // locks should not have been freed
151 for (var lock : locks) {
152 assertFalse(lock.isUnavailable());
159 for (var lock : locks) {
160 assertTrue(lock.isUnavailable());
165 void testDetmControlLoopTimeoutMs() throws Exception {
166 long timeMs = 1200 * 1000L;
167 long end = mgr.getEndTimeMs();
168 assertThat(end).isGreaterThanOrEqualTo(preCreateTimeMs + timeMs).isLessThan(preCreateTimeMs + timeMs + 5000);
172 void testRequestLock() {
173 final var future1 = mgr.requestLock(LOCK1);
174 assertTrue(mgr.getOutcomes().isEmpty());
176 final var future2 = mgr.requestLock(LOCK2);
177 assertTrue(mgr.getOutcomes().isEmpty());
179 assertSame(future1, mgr.requestLock(LOCK1));
180 assertTrue(mgr.getOutcomes().isEmpty());
182 assertEquals(2, locks.size());
184 assertTrue(future1.isDone());
185 assertTrue(future2.isDone());
187 // indicate that the first lock failed
188 locks.get(0).notifyUnavailable();
190 verifyLock(OperationResult.FAILURE, ActorConstants.LOCK_OPERATION);
191 assertTrue(mgr.getOutcomes().isEmpty());
195 void testReleaseLock() {
196 mgr.requestLock(LOCK1);
197 mgr.requestLock(LOCK2);
200 final var future = mgr.releaseLock(LOCK1);
202 // asynchronous, thus should not have executed yet
203 assertThat(future.isDone()).isFalse();
205 // asynchronous, thus everything should still be locked
206 for (var lock : locks) {
207 assertThat(lock.isUnavailable()).isFalse();
212 verifyLock(OperationResult.SUCCESS, ActorConstants.UNLOCK_OPERATION);
213 assertThat(mgr.getOutcomes()).isEmpty();
215 // first lock should have been released, thus no longer available to the manager
216 assertThat(locks.get(0).isUnavailable()).isTrue();
218 // second should still be locked
219 assertThat(locks.get(1).isUnavailable()).isFalse();
223 * Tests releaseLock() when there is no lock.
226 void testReleaseLockNotLocked() {
227 final var future = mgr.releaseLock(LOCK1);
229 // lock didn't exist, so the request should already be complete
230 assertThat(future.isDone()).isTrue();
232 verifyLock(OperationResult.SUCCESS, ActorConstants.UNLOCK_OPERATION);
233 assertThat(mgr.getOutcomes()).isEmpty();
237 * Tests releaseLock() when lock.free() throws an exception.
240 void testReleaseLockException() throws ControlLoopException {
241 mgr = new MyManager(services, params, REQ_ID) {
242 private static final long serialVersionUID = 1L;
245 protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
247 var lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback) {
248 private static final long serialVersionUID = 1L;
251 public boolean free() {
252 throw new RuntimeException(EXPECTED_EXCEPTION);
257 callback.lockAvailable(lock);
261 mgr.requestLock(LOCK1);
264 final var future = mgr.releaseLock(LOCK1);
266 // asynchronous, thus should not have executed yet
267 assertThat(future.isDone()).isFalse();
271 verifyLock(OperationResult.FAILURE_EXCEPTION, ActorConstants.UNLOCK_OPERATION);
272 assertThat(mgr.getOutcomes()).isEmpty();
275 private void verifyLock(OperationResult result, String lockOperation) {
276 var outcome = mgr.getOutcomes().poll();
277 assertNotNull(outcome);
278 assertEquals(ActorConstants.LOCK_ACTOR, outcome.getActor());
279 assertEquals(lockOperation, outcome.getOperation());
280 assertNotNull(outcome.getEnd());
281 assertTrue(outcome.isFinalOutcome());
282 assertEquals(result, outcome.getResult());
287 var outcome1 = new OperationOutcome();
288 var outcome2 = new OperationOutcome();
290 mgr.onStart(outcome1);
291 mgr.onStart(outcome2);
293 assertSame(outcome1, mgr.getOutcomes().poll());
294 assertSame(outcome2, mgr.getOutcomes().poll());
295 assertTrue(mgr.getOutcomes().isEmpty());
299 void testOnComplete() {
300 var outcome1 = new OperationOutcome();
301 var outcome2 = new OperationOutcome();
303 mgr.onComplete(outcome1);
304 mgr.onComplete(outcome2);
306 assertSame(outcome1, mgr.getOutcomes().poll());
307 assertSame(outcome2, mgr.getOutcomes().poll());
308 assertTrue(mgr.getOutcomes().isEmpty());
312 void testContains_testGetProperty_testSetProperty_testRemoveProperty() {
313 mgr.setProperty("abc", "a string");
314 mgr.setProperty(MY_KEY, 100);
316 assertTrue(mgr.contains(MY_KEY));
317 assertFalse(mgr.contains("ghi"));
319 var strValue = mgr.getProperty("abc");
320 assertEquals("a string", strValue);
322 int intValue = mgr.getProperty(MY_KEY);
323 assertEquals(100, intValue);
325 mgr.removeProperty(MY_KEY);
326 assertFalse(mgr.contains(MY_KEY));
330 * Tests getDataManager() when not disabled.
333 void testGetDataManagerNotDisabled() throws ControlLoopException {
334 assertThat(mgr.getDataManager()).isSameAs(dataMgr);
338 * Tests getDataManager() when guard.disabled=true.
341 void testGetDataManagerDisabled() throws ControlLoopException {
342 mgr = new MyManager(services, params, REQ_ID) {
343 private static final long serialVersionUID = 1L;
346 protected String getEnvironmentProperty(String propName) {
347 return ("guard.disabled".equals(propName) ? "true" : null);
351 assertThat(mgr.getDataManager()).isInstanceOf(OperationHistoryDataManagerStub.class);
355 void testToString() {
356 assertNotNull(mgr.toString());
359 private void loadPolicy(String fileName) throws CoderException {
360 var template = yamlCoder.decode(ResourceUtils.getResourceAsString(fileName), ToscaServiceTemplate.class);
361 tosca = template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next();
363 params.setToscaPolicy(tosca);
366 private void runExecutor() {
367 var runCaptor = ArgumentCaptor.forClass(Runnable.class);
368 verify(executor).execute(runCaptor.capture());
370 runCaptor.getValue().run();
374 private static class MyManager extends ControlLoopEventManager {
375 private static final long serialVersionUID = 1L;
377 private static ExecutorService executor;
378 private static List<LockImpl> locks;
380 public MyManager(EventManagerServices services, ControlLoopParams params, UUID requestId)
381 throws ControlLoopException {
382 super(services, params, requestId);
386 protected ExecutorService getBlockingExecutor() {
391 protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
392 var lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback);
394 callback.lockAvailable(lock);