2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019-2021 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2023-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.drools.system.internal;
24 import static org.assertj.core.api.Assertions.assertThat;
25 import static org.assertj.core.api.Assertions.assertThatCode;
26 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
27 import static org.junit.jupiter.api.Assertions.assertEquals;
28 import static org.junit.jupiter.api.Assertions.assertFalse;
29 import static org.junit.jupiter.api.Assertions.assertNotNull;
30 import static org.junit.jupiter.api.Assertions.assertNull;
31 import static org.junit.jupiter.api.Assertions.assertSame;
32 import static org.junit.jupiter.api.Assertions.assertTrue;
33 import static org.mockito.ArgumentMatchers.any;
34 import static org.mockito.Mockito.mock;
35 import static org.mockito.Mockito.never;
36 import static org.mockito.Mockito.times;
37 import static org.mockito.Mockito.verify;
39 import java.io.ByteArrayInputStream;
40 import java.io.ByteArrayOutputStream;
41 import java.io.ObjectInputStream;
42 import java.io.ObjectOutputStream;
43 import java.io.Serial;
44 import java.util.concurrent.ScheduledExecutorService;
45 import org.junit.jupiter.api.AfterAll;
46 import org.junit.jupiter.api.AfterEach;
47 import org.junit.jupiter.api.BeforeAll;
48 import org.junit.jupiter.api.BeforeEach;
49 import org.junit.jupiter.api.Test;
50 import org.junit.jupiter.api.extension.ExtendWith;
51 import org.mockito.ArgumentCaptor;
52 import org.mockito.Mock;
53 import org.mockito.MockitoAnnotations;
54 import org.mockito.junit.jupiter.MockitoExtension;
55 import org.onap.policy.drools.core.DroolsRunnable;
56 import org.onap.policy.drools.core.PolicySession;
57 import org.onap.policy.drools.core.lock.LockCallback;
58 import org.onap.policy.drools.core.lock.LockState;
59 import org.onap.policy.drools.system.PolicyEngineConstants;
60 import org.springframework.test.util.ReflectionTestUtils;
62 @ExtendWith(MockitoExtension.class)
63 class FeatureLockImplTest {
64 private static final String POLICY_ENGINE_EXECUTOR_FIELD = "executorService";
65 private static final String OWNER_KEY = "my key";
66 private static final String RESOURCE = "my resource";
67 private static final int HOLD_SEC = 100;
68 private static final int HOLD_SEC2 = 120;
70 private static ScheduledExecutorService saveExec;
73 private ScheduledExecutorService exsvc;
76 private LockCallback callback;
78 AutoCloseable closeable;
81 * Saves static fields and configures the location of the property files.
84 static void setUpBeforeClass() {
85 saveExec = (ScheduledExecutorService) ReflectionTestUtils.getField(PolicyEngineConstants.getManager(),
86 POLICY_ENGINE_EXECUTOR_FIELD);
90 * Restores static fields.
93 static void tearDownAfterClass() {
94 ReflectionTestUtils.setField(PolicyEngineConstants.getManager(), POLICY_ENGINE_EXECUTOR_FIELD, saveExec);
98 * Initializes the mocks and creates a feature that uses {@link #exsvc} to execute
103 closeable = MockitoAnnotations.openMocks(this);
104 ReflectionTestUtils.setField(PolicyEngineConstants.getManager(), POLICY_ENGINE_EXECUTOR_FIELD, exsvc);
108 void closeMocks() throws Exception {
114 MyLock lock = new MyLock();
115 assertNull(lock.getResourceId());
116 assertNull(lock.getOwnerKey());
117 assertNull(lock.getCallback());
118 assertEquals(0, lock.getHoldSec());
122 void testFeatureLockImpl() {
123 MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
124 assertTrue(lock.isWaiting());
125 assertEquals(RESOURCE, lock.getResourceId());
126 assertEquals(OWNER_KEY, lock.getOwnerKey());
127 assertSame(callback, lock.getCallback());
128 assertEquals(HOLD_SEC, lock.getHoldSec());
132 void testSerializable() throws Exception {
133 MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
134 lock = roundTrip(lock);
136 assertTrue(lock.isWaiting());
138 assertEquals(RESOURCE, lock.getResourceId());
139 assertEquals(OWNER_KEY, lock.getOwnerKey());
140 assertNull(lock.getCallback());
141 assertEquals(HOLD_SEC, lock.getHoldSec());
146 MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
149 assertTrue(lock.isActive());
150 assertEquals(1, lock.nupdates);
153 verify(callback).lockAvailable(any());
154 verify(callback, never()).lockUnavailable(any());
158 * Tests grant() when the lock is already unavailable.
161 void testGrantUnavailable() {
162 MyLock lock = new MyLock(LockState.UNAVAILABLE, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
163 lock.setState(LockState.UNAVAILABLE);
166 assertTrue(lock.isUnavailable());
167 assertEquals(0, lock.nupdates);
169 verify(exsvc, never()).execute(any());
174 MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
175 lock.deny("my reason");
177 assertTrue(lock.isUnavailable());
180 verify(callback, never()).lockAvailable(any());
181 verify(callback).lockUnavailable(any());
185 * Tests doNotify() when a session exists.
188 void testDoNotifySession() {
189 PolicySession session = mock(PolicySession.class);
191 MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback) {
193 private static final long serialVersionUID = 1L;
196 protected PolicySession getSession() {
203 assertTrue(lock.isActive());
204 assertEquals(1, lock.nupdates);
206 verify(exsvc, never()).execute(any());
208 ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);
209 verify(session).insertDrools(captor.capture());
211 DroolsRunnable runner = (DroolsRunnable) captor.getValue();
214 verify(callback).lockAvailable(any());
215 verify(callback, never()).lockUnavailable(any());
219 * Tests doNotify() when there is no session.
222 void testDoNotifyNoSession() {
223 MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
226 assertTrue(lock.isActive());
227 assertEquals(1, lock.nupdates);
230 verify(callback).lockAvailable(any());
231 verify(callback, never()).lockUnavailable(any());
235 void testFreeAllowed() {
236 MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
237 assertTrue(lock.freeAllowed());
241 * Tests freeAllowed() when the lock is unavailable.
244 void testFreeAllowedUnavailable() {
245 MyLock lock = new MyLock(LockState.UNAVAILABLE, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
246 assertFalse(lock.freeAllowed());
247 assertTrue(lock.isUnavailable());
251 * Tests that free() works on a serialized lock with a new feature.
253 * @throws Exception if an error occurs
256 void testFreeAllowedSerialized() throws Exception {
257 MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
259 lock = roundTrip(lock);
260 assertTrue(lock.freeAllowed());
264 * Tests free() on a serialized lock without a feature.
266 * @throws Exception if an error occurs
269 void testFreeAllowedNoFeature() throws Exception {
270 MyLock lock = new MyLockNoFeature(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
272 lock = roundTrip(lock);
273 assertFalse(lock.freeAllowed());
274 assertTrue(lock.isUnavailable());
278 void testExtendAllowed() {
279 MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
281 LockCallback scallback = mock(LockCallback.class);
282 assertTrue(lock.extendAllowed(HOLD_SEC2, scallback));
283 assertTrue(lock.isWaiting());
284 assertEquals(HOLD_SEC2, lock.getHoldSec());
285 assertSame(scallback, lock.getCallback());
287 verify(exsvc, never()).execute(any());
292 assertThatIllegalArgumentException().isThrownBy(
293 () -> new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback)
294 .extendAllowed(-1, callback))
295 .withMessageContaining("holdSec");
300 * Tests extendAllowed() when the lock is unavailable.
303 void testExtendAllowedUnavailable() {
304 MyLock lock = new MyLock(LockState.UNAVAILABLE, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
306 LockCallback scallback = mock(LockCallback.class);
307 assertFalse(lock.extendAllowed(HOLD_SEC2, scallback));
308 assertTrue(lock.isUnavailable());
309 assertEquals(HOLD_SEC2, lock.getHoldSec());
310 assertSame(scallback, lock.getCallback());
313 verify(scallback, never()).lockAvailable(lock);
314 verify(scallback).lockUnavailable(lock);
318 * Tests that extendAllowed() works on a serialized lock with a new feature.
320 * @throws Exception if an error occurs
323 void testExtendAllowedSerialized() throws Exception {
324 MyLock lock = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
326 lock = roundTrip(lock);
328 LockCallback scallback = mock(LockCallback.class);
329 assertTrue(lock.extendAllowed(HOLD_SEC2, scallback));
330 assertTrue(lock.isWaiting());
331 assertEquals(HOLD_SEC2, lock.getHoldSec());
332 assertSame(scallback, lock.getCallback());
334 verify(exsvc, never()).execute(any());
338 * Tests extendAllowed() on a serialized lock without a feature.
340 * @throws Exception if an error occurs
343 void testExtendAllowedNoFeature() throws Exception {
344 MyLock lock = new MyLockNoFeature(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
346 lock = roundTrip(lock);
348 LockCallback scallback = mock(LockCallback.class);
349 assertFalse(lock.extendAllowed(HOLD_SEC2, scallback));
350 assertTrue(lock.isUnavailable());
351 assertEquals(HOLD_SEC2, lock.getHoldSec());
352 assertSame(scallback, lock.getCallback());
355 verify(scallback, never()).lockAvailable(lock);
356 verify(scallback).lockUnavailable(lock);
360 void testGetSession() {
361 MyLockStdSession lock = new MyLockStdSession(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
363 // this should invoke the real policy session without throwing an exception
364 assertThatCode(lock::grant).doesNotThrowAnyException();
368 void testToString() {
369 String text = new MyLock(LockState.WAITING, RESOURCE, OWNER_KEY, HOLD_SEC, callback).toString();
371 assertThat(text).contains("LockImpl");
374 private MyLock roundTrip(MyLock lock) throws Exception {
375 ByteArrayOutputStream baos = new ByteArrayOutputStream();
376 try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
377 oos.writeObject(lock);
380 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
381 try (ObjectInputStream ois = new ObjectInputStream(bais)) {
382 return (MyLock) ois.readObject();
387 * Invokes the last call-back in the work queue.
390 private void invokeCallback() {
391 ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
392 verify(exsvc, times(1)).execute(captor.capture());
393 captor.getAllValues().get(0).run();
397 * Lock that inherits the normal getSession() method.
399 public static class MyLockStdSession extends FeatureLockImpl {
401 private static final long serialVersionUID = 1L;
402 protected int nupdates = 0;
404 public MyLockStdSession() {
408 public MyLockStdSession(LockState state, String resourceId, String ownerKey, int holdSec,
409 LockCallback callback) {
410 super(state, resourceId, ownerKey, holdSec, callback);
414 protected void updateGrant() {
420 public boolean free() {
425 public void extend(int holdSec, LockCallback callback) {
430 protected boolean addToFeature() {
435 public static class MyLock extends MyLockStdSession {
437 private static final long serialVersionUID = 1L;
443 public MyLock(LockState state, String resourceId, String ownerKey, int holdSec, LockCallback callback) {
444 super(state, resourceId, ownerKey, holdSec, callback);
448 protected PolicySession getSession() {
453 public static class MyLockNoFeature extends MyLock {
455 private static final long serialVersionUID = 1L;
457 public MyLockNoFeature() {
461 public MyLockNoFeature(LockState state, String resourceId, String ownerKey, int holdSec,
462 LockCallback callback) {
463 super(state, resourceId, ownerKey, holdSec, callback);
467 protected boolean addToFeature() {