2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019 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.pap.main.comm;
24 import static org.junit.jupiter.api.Assertions.assertEquals;
25 import static org.junit.jupiter.api.Assertions.assertNotNull;
26 import static org.junit.jupiter.api.Assertions.assertNull;
27 import static org.junit.jupiter.api.Assertions.assertTrue;
28 import static org.junit.jupiter.api.Assertions.fail;
30 import java.util.concurrent.LinkedBlockingQueue;
31 import java.util.concurrent.Semaphore;
32 import java.util.concurrent.TimeUnit;
33 import java.util.function.Consumer;
34 import org.junit.jupiter.api.AfterEach;
35 import org.junit.jupiter.api.BeforeEach;
36 import org.junit.jupiter.api.Test;
37 import org.onap.policy.pap.main.comm.TimerManager.Timer;
39 class TimerManagerTest extends Threaded {
40 private static final String EXPECTED_EXCEPTION = "expected exception";
41 private static final String MGR_NAME = "my-manager";
42 private static final String NAME1 = "timer-A";
43 private static final String NAME2 = "timer-B";
44 private static final String NAME3 = "timer-C";
46 private static final long MGR_TIMEOUT_MS = 10000;
48 private MyManager mgr;
51 * This is a field rather than a local variable to prevent checkstyle from complaining
52 * about the distance between its assignment and its use.
59 * @throws Exception if an error occurs
62 public void setUp() throws Exception {
65 mgr = new MyManager(MGR_NAME, MGR_TIMEOUT_MS);
69 public void tearDown() throws Exception {
74 protected void stopThread() throws Exception {
82 void testTimerManager_testStop() throws Exception {
86 assertTrue(waitStop());
88 // ensure we can call "stop" a second time
93 void testRegister() throws Exception {
94 mgr.register(NAME2, mgr::addToQueue);
95 mgr.registerNewTime(NAME1, mgr::addToQueue);
97 // goes to the end of the queue
98 mgr.registerNewTime(NAME2, mgr::addToQueue);
104 assertEquals(NAME1, mgr.awaitTimer());
105 assertEquals(NAME2, mgr.awaitTimer());
109 void testRun_Ex() throws Exception {
111 mgr.register(NAME1, mgr::addToQueue);
115 // background thread is "sleeping" - now we can interrupt it
118 assertTrue(waitStop());
122 void testProcessTimers() throws Exception {
124 mgr.register(NAME1, mgr::addToQueue);
128 mgr.registerNewTime(NAME2, mgr::addToQueue);
131 // tell it to stop before returning from "sleep"
135 assertTrue(waitStop());
137 assertEquals(NAME1, mgr.pollTimer());
138 assertNull(mgr.pollTimer());
142 void testGetNextTimer() throws Exception {
144 mgr.register(NAME1, mgr::addToQueue);
148 mgr.registerNewTime(NAME2, mgr::addToQueue);
153 void testProcessTimer_StopWhileWaiting() throws Exception {
155 mgr.register(NAME1, mgr::addToQueue);
159 mgr.registerNewTime(NAME2, mgr::addToQueue);
165 assertTrue(waitStop());
167 // should have stopped after processing the first timer
168 assertEquals(NAME1, mgr.pollTimer());
169 assertNull(mgr.pollTimer());
173 void testProcessTimer_CancelWhileWaiting() throws Exception {
175 Timer timer = mgr.register(NAME1, mgr::addToQueue);
181 mgr.registerNewTime(NAME2, mgr::addToQueue);
185 mgr.registerNewTime(NAME1, mgr::addToQueue);
188 // should have fired timer 2, but not timer 1
189 assertEquals(NAME2, mgr.pollTimer());
190 assertNull(mgr.pollTimer());
194 void testProcessTimer_TimerEx() throws Exception {
196 mgr.register(NAME1, name -> {
197 throw new RuntimeException(EXPECTED_EXCEPTION);
200 mgr.register(NAME2, mgr::addToQueue);
202 // same times, so only need one sleep
207 mgr.registerNewTime(NAME3, mgr::addToQueue);
210 // timer 1 fired but threw an exception, so only timer 2 should be in the queue
211 assertEquals(NAME2, mgr.pollTimer());
215 void testTimerAwait() throws Exception {
218 // same times - should only sleep once
219 mgr.register(NAME1, mgr::addToQueue);
220 mgr.register(NAME2, mgr::addToQueue);
223 tcur = mgr.currentTimeMillis();
227 // next one will have a new timeout, so expect to sleep again
228 mgr.registerNewTime(NAME3, mgr::addToQueue);
231 long tcur2 = mgr.currentTimeMillis();
232 assertTrue(tcur2 >= tcur + MGR_TIMEOUT_MS);
234 assertEquals(NAME1, mgr.pollTimer());
235 assertEquals(NAME2, mgr.pollTimer());
236 assertNull(mgr.pollTimer());
240 void testTimerCancel_WhileWaiting() throws Exception {
243 Timer timer = mgr.register(NAME1, mgr::addToQueue);
246 // cancel while sleeping
249 mgr.registerNewTime(NAME2, mgr::addToQueue);
251 // allow it to sleep through both timers
254 // only timer 2 should have fired
255 assertEquals(NAME2, mgr.awaitTimer());
259 void testTimerCancel_ViaReplace() throws Exception {
262 mgr.register(NAME1, name -> mgr.addToQueue("hello"));
265 // replace the timer while the background thread is sleeping
266 mgr.registerNewTime(NAME1, name -> mgr.addToQueue("world"));
268 // allow it to sleep through both timers
271 // only timer 2 should have fired
272 assertEquals("world", mgr.awaitTimer());
276 void testTimerToString() {
277 Timer timer = mgr.register(NAME1, mgr::addToQueue);
278 assertNotNull(timer.toString());
279 assertTrue(timer.toString().contains(NAME1));
283 void testCurrentTimeMillis() {
284 long tbeg = System.currentTimeMillis();
285 long tcur = new TimerManager(MGR_NAME, MGR_TIMEOUT_MS).currentTimeMillis();
286 long tend = System.currentTimeMillis();
288 assertTrue(tcur >= tbeg);
289 assertTrue(tend >= tcur);
293 void testSleep() throws Exception {
294 long tbeg = System.currentTimeMillis();
295 new TimerManager(MGR_NAME, MGR_TIMEOUT_MS).sleep(10);
296 long tend = System.currentTimeMillis();
298 assertTrue(tend >= tbeg + 10);
303 * Timer Manager whose notions of time are controlled here. It also overrides the
304 * {@link #sleep(long)} method so that the test thread can control when the background
305 * timer thread finishes sleeping.
307 private static class MyManager extends TimerManager {
308 private final Object lockit = new Object();
309 private long curTime = 1000;
310 private int offset = 0;
311 private final Semaphore sleepEntered = new Semaphore(0);
312 private final Semaphore sleepsAllowed = new Semaphore(0);
313 private final LinkedBlockingQueue<String> results = new LinkedBlockingQueue<>();
315 public MyManager(String name, long waitTimeMs) {
316 super(name, waitTimeMs);
320 * Registers a timer with a new starting time. Because the manager uses the
321 * current time when determining the expiration time, we have to temporarily
322 * fiddle with {@link #curTime}, but we leave it unchanged when we're done.
323 * Increases the {@link #offset} each time it's invoked.
325 * @return the new timer
327 public Timer registerNewTime(String timerName, Consumer<String> action) {
328 synchronized (lockit) {
332 Timer timer = super.register(timerName, action);
340 * Allows the manager to "sleep" several times.
342 * @param ntimes the number of times the manager should sleep
344 public void allowSleep(int ntimes) {
345 sleepsAllowed.release(ntimes);
349 * Waits for the manager to "sleep".
351 * @throws InterruptedException if the thread is interrupted while waiting for the
352 * background thread to sleep
354 public void awaitSleep() throws InterruptedException {
355 if (!sleepEntered.tryAcquire(MAX_WAIT_MS, TimeUnit.MILLISECONDS)) {
356 fail("background thread failed to sleep");
361 protected long currentTimeMillis() {
362 synchronized (lockit) {
368 protected void sleep(long timeMs) throws InterruptedException {
369 sleepEntered.release();
370 sleepsAllowed.acquire();
372 synchronized (lockit) {
378 * Waits for a timer to fire.
380 * @return the message the timer added to {@link #results}
381 * @throws InterruptedException if this thread is interrupted while waiting
383 private String awaitTimer() throws InterruptedException {
384 return results.poll(MAX_WAIT_MS, TimeUnit.MILLISECONDS);
388 * Adds a name to the queue.
390 * @param name the name to add
392 private void addToQueue(String name) {
397 * Polls to see if a timer has fired.
399 * @return the message the timer added to {@link #results}, or {@code null} if no
400 * timer has fired yet
402 private String pollTimer() {
403 return results.poll();