2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.bluetooth.util;
15 import static org.junit.jupiter.api.Assertions.*;
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;
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;
33 * @author Connor Petty - Initial contribution
36 class RetryFutureTest {
38 private static final int TIMEOUT_MS = 1000;
39 private ScheduledExecutorService scheduler;
43 ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1,
44 new NamedThreadFactory("RetryFutureTest", true));
45 scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
46 scheduler.setRemoveOnCancelPolicy(true);
47 this.scheduler = scheduler;
51 public void cleanup() {
52 scheduler.shutdownNow();
56 void callWithRetryNormal() {
57 Future<String> retryFuture = RetryFuture.callWithRetry(() -> "test", scheduler);
59 assertEquals("test", retryFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
60 } catch (InterruptedException | ExecutionException | TimeoutException e) {
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);
75 assertEquals("test", retryFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
76 } catch (InterruptedException | ExecutionException | TimeoutException e) {
82 void composeWithRetryNormal() {
83 CompletableFuture<?> composedFuture = new CompletableFuture<>();
85 Future<?> retryFuture = RetryFuture.composeWithRetry(() -> {
86 composedFuture.complete(null);
87 return composedFuture;
91 retryFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
92 } catch (InterruptedException | ExecutionException | TimeoutException e) {
95 assertTrue(composedFuture.isDone());
99 void composeWithRetryThrow() {
100 CompletableFuture<?> composedFuture = new CompletableFuture<>();
102 Future<?> retryFuture = RetryFuture.composeWithRetry(() -> {
103 composedFuture.completeExceptionally(new DummyException());
104 return composedFuture;
108 retryFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
109 } catch (InterruptedException | TimeoutException e) {
111 } catch (ExecutionException ex) {
112 assertTrue(ex.getCause() instanceof DummyException);
114 assertTrue(composedFuture.isDone());
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));
125 composedFuture.complete("test");
126 return composedFuture;
130 assertEquals("test", retryFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
131 } catch (InterruptedException | ExecutionException | TimeoutException e) {
134 assertEquals(2, visitCount.get());
135 assertTrue(composedFuture.isDone());
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));
148 return composedFuture;
152 if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
153 fail("Timeout while waiting for latch");
155 Future<Boolean> future = scheduler.submit(() -> {
156 retryFuture.cancel(false);
157 return composedFuture.isCancelled();
159 assertTrue(future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
160 } catch (InterruptedException | ExecutionException | TimeoutException e) {
163 assertEquals(2, visitCount.get());
164 assertTrue(composedFuture.isDone());
167 private static class DummyException extends Exception {