Skip to content

Commit d5665bd

Browse files
committed
- Incorporated gemini comments plus other improvements
- Added test cases
1 parent 96c0585 commit d5665bd

2 files changed

Lines changed: 128 additions & 5 deletions

File tree

sdk-platform-java/api-common-java/src/main/java/com/google/api/core/ApiFuture.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
package com.google.api.core;
3131

3232
import java.util.concurrent.CompletableFuture;
33+
import java.util.concurrent.ExecutionException;
3334
import java.util.concurrent.Executor;
3435
import java.util.concurrent.Future;
3536

@@ -45,13 +46,28 @@ default CompletableFuture<V> completable(Executor executor) {
4546
CompletableFuture<V> completableFuture = new CompletableFuture<>();
4647

4748
addListener(() -> {
48-
try {
49-
completableFuture.complete(get());
50-
} catch (Exception e) {
51-
completableFuture.completeExceptionally(e);
49+
if (isCancelled()) {
50+
completableFuture.cancel(true);
51+
} else {
52+
try {
53+
completableFuture.complete(get());
54+
} catch (ExecutionException e) {
55+
completableFuture.completeExceptionally(e.getCause() != null? e.getCause() : e);
56+
} catch (InterruptedException e) {
57+
Thread.currentThread().interrupt();
58+
completableFuture.completeExceptionally(e);
59+
} catch (Throwable t) {
60+
completableFuture.completeExceptionally(t);
61+
}
5262
}
5363
}, executor);
5464

65+
completableFuture.whenComplete((v, t) -> {
66+
if (completableFuture.isCancelled()) {
67+
cancel(true);
68+
}
69+
});
70+
5571
return completableFuture;
5672
}
5773
}

sdk-platform-java/api-common-java/src/test/java/com/google/api/core/SettableApiFutureTest.java

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,12 @@
3030
package com.google.api.core;
3131

3232
import com.google.common.truth.Truth;
33+
import java.util.concurrent.CancellationException;
34+
import java.util.concurrent.CompletableFuture;
3335
import java.util.concurrent.ExecutionException;
3436
import java.util.concurrent.Executor;
3537
import java.util.concurrent.TimeUnit;
38+
import java.util.concurrent.atomic.AtomicBoolean;
3639
import java.util.concurrent.atomic.AtomicInteger;
3740
import java.util.function.BiFunction;
3841

@@ -93,7 +96,7 @@ public void execute(Runnable r) {
9396
}
9497

9598
@Test
96-
void testCompletable() {
99+
void testCompletableApplied() {
97100
final AtomicInteger flag = new AtomicInteger();
98101
SettableApiFuture<Integer> future = SettableApiFuture.<Integer>create();
99102
future.completable(
@@ -113,4 +116,108 @@ public Object apply(Integer integer, Throwable throwable) {
113116
future.set(0);
114117
Truth.assertThat(flag.get()).isEqualTo(1);
115118
}
119+
120+
@Test
121+
void testCompletableSuccess() throws Exception {
122+
SettableApiFuture<Integer> future = SettableApiFuture.create();
123+
CompletableFuture<Integer> completable = future.completable(Runnable::run);
124+
125+
Truth.assertThat(completable.isDone()).isFalse();
126+
future.set(42);
127+
Truth.assertThat(completable.isDone()).isTrue();
128+
Truth.assertThat(completable.get()).isEqualTo(42);
129+
}
130+
131+
@Test
132+
void testCompletableException() {
133+
SettableApiFuture<Integer> future = SettableApiFuture.create();
134+
CompletableFuture<Integer> completable = future.completable(Runnable::run);
135+
136+
Exception expectedException = new IllegalArgumentException("something failed");
137+
future.setException(expectedException);
138+
139+
Truth.assertThat(completable.isDone()).isTrue();
140+
Truth.assertThat(completable.isCompletedExceptionally()).isTrue();
141+
ExecutionException thrown =
142+
Assertions.assertThrows(ExecutionException.class, completable::get);
143+
Truth.assertThat(thrown.getCause()).isSameInstanceAs(expectedException);
144+
}
145+
146+
@Test
147+
void testCompletableCancelApiFutureCancelsCompletableFuture() {
148+
SettableApiFuture<Integer> future = SettableApiFuture.create();
149+
CompletableFuture<Integer> completable = future.completable(Runnable::run);
150+
151+
future.cancel(false);
152+
153+
Truth.assertThat(completable.isDone()).isTrue();
154+
Truth.assertThat(completable.isCancelled()).isTrue();
155+
Assertions.assertThrows(CancellationException.class, completable::get);
156+
}
157+
158+
@Test
159+
void testCompletableCancelCompletableFutureCancelsApiFuture() {
160+
SettableApiFuture<Integer> future = SettableApiFuture.create();
161+
CompletableFuture<Integer> completable = future.completable(Runnable::run);
162+
163+
completable.cancel(true);
164+
165+
Truth.assertThat(future.isDone()).isTrue();
166+
Truth.assertThat(future.isCancelled()).isTrue();
167+
Assertions.assertThrows(CancellationException.class, future::get);
168+
}
169+
170+
@Test
171+
void testCompletableAlreadyCompleted() throws Exception {
172+
SettableApiFuture<Integer> future = SettableApiFuture.create();
173+
future.set(100);
174+
175+
CompletableFuture<Integer> completable = future.completable(Runnable::run);
176+
177+
Truth.assertThat(completable.isDone()).isTrue();
178+
Truth.assertThat(completable.get()).isEqualTo(100);
179+
}
180+
181+
@Test
182+
void testCompletableAlreadyFailed() {
183+
SettableApiFuture<Integer> future = SettableApiFuture.create();
184+
RuntimeException expectedException = new RuntimeException("pre-failed");
185+
future.setException(expectedException);
186+
187+
CompletableFuture<Integer> completable = future.completable(Runnable::run);
188+
189+
Truth.assertThat(completable.isDone()).isTrue();
190+
Truth.assertThat(completable.isCompletedExceptionally()).isTrue();
191+
ExecutionException thrown =
192+
Assertions.assertThrows(ExecutionException.class, completable::get);
193+
Truth.assertThat(thrown.getCause()).isSameInstanceAs(expectedException);
194+
}
195+
196+
@Test
197+
void testCompletableAlreadyCancelled() {
198+
SettableApiFuture<Integer> future = SettableApiFuture.create();
199+
future.cancel(false);
200+
201+
CompletableFuture<Integer> completable = future.completable(Runnable::run);
202+
203+
Truth.assertThat(completable.isDone()).isTrue();
204+
Truth.assertThat(completable.isCancelled()).isTrue();
205+
}
206+
207+
@Test
208+
void testCompletableUsesExecutor() {
209+
AtomicBoolean executorRan = new AtomicBoolean(false);
210+
SettableApiFuture<Integer> future = SettableApiFuture.create();
211+
CompletableFuture<Integer> completable =
212+
future.completable(
213+
command -> {
214+
executorRan.set(true);
215+
command.run();
216+
});
217+
218+
Truth.assertThat(executorRan.get()).isFalse();
219+
future.set(7);
220+
Truth.assertThat(executorRan.get()).isTrue();
221+
Truth.assertThat(completable.isDone()).isTrue();
222+
}
116223
}

0 commit comments

Comments
 (0)