]> git.basschouten.com Git - openhab-addons.git/blob
895abf473107543af66b46bc0aae70c4682f019c
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.bluetooth.util;
14
15 import static org.junit.jupiter.api.Assertions.*;
16
17 import java.util.concurrent.CompletableFuture;
18 import java.util.concurrent.CountDownLatch;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.Future;
21 import java.util.concurrent.ScheduledExecutorService;
22 import java.util.concurrent.ScheduledThreadPoolExecutor;
23 import java.util.concurrent.TimeUnit;
24 import java.util.concurrent.TimeoutException;
25 import java.util.concurrent.atomic.AtomicInteger;
26
27 import org.junit.jupiter.api.AfterEach;
28 import org.junit.jupiter.api.BeforeEach;
29 import org.junit.jupiter.api.Test;
30 import org.openhab.core.common.NamedThreadFactory;
31
32 /**
33  * @author Connor Petty - Initial contribution
34  *
35  */
36 class RetryFutureTest {
37
38     private static final int TIMEOUT_MS = 1000;
39     private ScheduledExecutorService scheduler;
40
41     @BeforeEach
42     public void init() {
43         ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1,
44                 new NamedThreadFactory("RetryFutureTest", true));
45         scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
46         scheduler.setRemoveOnCancelPolicy(true);
47         this.scheduler = scheduler;
48     }
49
50     @AfterEach
51     public void cleanup() {
52         scheduler.shutdownNow();
53     }
54
55     @Test
56     void callWithRetryNormal() {
57         Future<String> retryFuture = RetryFuture.callWithRetry(() -> "test", scheduler);
58         try {
59             assertEquals("test", retryFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
60         } catch (InterruptedException | ExecutionException | TimeoutException e) {
61             fail(e);
62         }
63     }
64
65     @Test
66     void callWithRetry1() {
67         AtomicInteger visitCount = new AtomicInteger();
68         Future<String> retryFuture = RetryFuture.callWithRetry(() -> {
69             if (visitCount.getAndIncrement() == 0) {
70                 throw new RetryException(0, TimeUnit.SECONDS);
71             }
72             return "test";
73         }, scheduler);
74         try {
75             assertEquals("test", retryFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
76         } catch (InterruptedException | ExecutionException | TimeoutException e) {
77             fail(e);
78         }
79     }
80
81     @Test
82     void composeWithRetryNormal() {
83         CompletableFuture<?> composedFuture = new CompletableFuture<>();
84
85         Future<?> retryFuture = RetryFuture.composeWithRetry(() -> {
86             composedFuture.complete(null);
87             return composedFuture;
88         }, scheduler);
89
90         try {
91             retryFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
92         } catch (InterruptedException | ExecutionException | TimeoutException e) {
93             fail(e);
94         }
95         assertTrue(composedFuture.isDone());
96     }
97
98     @Test
99     void composeWithRetryThrow() {
100         CompletableFuture<?> composedFuture = new CompletableFuture<>();
101
102         Future<?> retryFuture = RetryFuture.composeWithRetry(() -> {
103             composedFuture.completeExceptionally(new DummyException());
104             return composedFuture;
105         }, scheduler);
106
107         try {
108             retryFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
109         } catch (InterruptedException | TimeoutException e) {
110             fail(e);
111         } catch (ExecutionException ex) {
112             assertTrue(ex.getCause() instanceof DummyException);
113         }
114         assertTrue(composedFuture.isDone());
115     }
116
117     @Test
118     void composeWithRetry1() {
119         AtomicInteger visitCount = new AtomicInteger();
120         CompletableFuture<String> composedFuture = new CompletableFuture<>();
121         Future<String> retryFuture = RetryFuture.composeWithRetry(() -> {
122             if (visitCount.getAndIncrement() == 0) {
123                 return CompletableFuture.failedFuture(new RetryException(0, TimeUnit.SECONDS));
124             }
125             composedFuture.complete("test");
126             return composedFuture;
127         }, scheduler);
128
129         try {
130             assertEquals("test", retryFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
131         } catch (InterruptedException | ExecutionException | TimeoutException e) {
132             fail(e);
133         }
134         assertEquals(2, visitCount.get());
135         assertTrue(composedFuture.isDone());
136     }
137
138     @Test
139     void composeWithRetry1Cancel() {
140         CountDownLatch latch = new CountDownLatch(1);
141         AtomicInteger visitCount = new AtomicInteger();
142         CompletableFuture<String> composedFuture = new CompletableFuture<>();
143         Future<String> retryFuture = RetryFuture.composeWithRetry(() -> {
144             if (visitCount.getAndIncrement() == 0) {
145                 return CompletableFuture.failedFuture(new RetryException(0, TimeUnit.SECONDS));
146             }
147             latch.countDown();
148             return composedFuture;
149         }, scheduler);
150
151         try {
152             if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
153                 fail("Timeout while waiting for latch");
154             }
155             Future<Boolean> future = scheduler.submit(() -> {
156                 retryFuture.cancel(false);
157                 return composedFuture.isCancelled();
158             });
159             assertTrue(future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
160         } catch (InterruptedException | ExecutionException | TimeoutException e) {
161             fail(e);
162         }
163         assertEquals(2, visitCount.get());
164         assertTrue(composedFuture.isDone());
165     }
166
167     private static class DummyException extends Exception {
168
169     }
170 }