Fixing context timeout regression
At some point in the past, a context timeout would fail futures with a
V23 Timeout exception. However, recently it fails with a
CancellationException. This restores the previous behavior.
Change-Id: I010cbd83ceee44300c6329fec10ba238d09d05d0
diff --git a/android-lib/build.gradle b/android-lib/build.gradle
index a7fb4a2..8480bc0 100644
--- a/android-lib/build.gradle
+++ b/android-lib/build.gradle
@@ -21,7 +21,7 @@
// You should change this after releasing a new version of the library. See the
// list of published versions at https://repo1.maven.org/maven2/io/v/vanadium-android.
-def releaseVersion = '1.12'
+def releaseVersion = '1.13'
android {
buildToolsVersion '23.0.1'
diff --git a/lib/build.gradle b/lib/build.gradle
index 9587223..6791cdf 100644
--- a/lib/build.gradle
+++ b/lib/build.gradle
@@ -23,7 +23,7 @@
// You should change this after releasing a new version of the library. See the
// list of published versions at https://repo1.maven.org/maven2/io/v/vanadium.
-def releaseVersion = '1.12'
+def releaseVersion = '1.13'
dependencies {
compile group: 'joda-time', name: 'joda-time', version: '2.7'
diff --git a/lib/src/main/java/io/v/v23/VFutures.java b/lib/src/main/java/io/v/v23/VFutures.java
index e682b10..13d5d5d 100644
--- a/lib/src/main/java/io/v/v23/VFutures.java
+++ b/lib/src/main/java/io/v/v23/VFutures.java
@@ -5,7 +5,6 @@
package io.v.v23;
import com.google.common.util.concurrent.AsyncFunction;
-import com.google.common.util.concurrent.FutureFallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -136,7 +135,8 @@
/**
* Returns a new {@link ListenableFuture} that runs on an {@link Executor} specified in the
- * given {@code context} and is canceled iff the given {@code context} has been canceled.
+ * given {@code context}. If the future completes but the given {@code context} has been
+ * canceled, the returned future is canceled as well.
*/
public static <T> ListenableFuture<T> withUserLandChecks(
final VContext context, final ListenableFuture<T> future) {
@@ -145,7 +145,7 @@
throw new RuntimeException("NULL executor in context: did you derive this context " +
"from the context returned by V.init()?");
}
- return Futures.withFallback(Futures.transform(future, new AsyncFunction<T, T>() {
+ return Futures.transform(future, new AsyncFunction<T, T>() {
@Override
public ListenableFuture<T> apply(T input) throws Exception {
if (context.isCanceled()) {
@@ -153,14 +153,6 @@
}
return Futures.immediateFuture(input);
}
- }, executor), new FutureFallback<T>() {
- @Override
- public ListenableFuture<T> create(Throwable t) throws Exception {
- if (context.isCanceled()) {
- return Futures.immediateCancelledFuture();
- }
- return Futures.immediateFailedFuture(t);
- }
}, executor);
}
}
diff --git a/lib/src/test/java/io/v/v23/VFuturesTest.java b/lib/src/test/java/io/v/v23/VFuturesTest.java
new file mode 100644
index 0000000..01e81f3
--- /dev/null
+++ b/lib/src/test/java/io/v/v23/VFuturesTest.java
@@ -0,0 +1,71 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.v23;
+
+import org.joda.time.Duration;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+
+import io.v.v23.context.VContext;
+import io.v.v23.verror.NoServersException;
+import io.v.v23.verror.TimeoutException;
+import io.v.x.jni.test.fortune.FortuneClient;
+import io.v.x.jni.test.fortune.FortuneClientFactory;
+import io.v.x.jni.test.fortune.FortuneServer;
+import io.v.x.jni.test.fortune.FortuneServerImpl;
+
+import static io.v.v23.VFutures.sync;
+
+/**
+ * The {@link VFutures} class is responsible for wrapping Vanadium asyncs in managed
+ * {@link com.google.common.util.concurrent.ListenableFuture}s. These tests ensure that any errors
+ * raised by the V23 Go layer are surfaced to the client as reasonable exceptions.
+ */
+public class VFuturesTest {
+ private VContext ctx;
+
+ @Before
+ public void setup() throws Exception {
+ ctx = V.init();
+ }
+
+ @After
+ public void teardown() {
+ ctx.cancel();
+ }
+
+ /**
+ * When the context times out and the name was resolvable but not a Vanadium server, we expect a
+ * {@link NoServersException}.
+ */
+ @Test(expected = NoServersException.class)
+ public void testNoServers() throws Exception {
+ final FortuneClient client = FortuneClientFactory.getFortuneClient("/127.0.0.1");
+ final VContext timeout = ctx.withTimeout(Duration.millis(100));
+ sync(client.get(timeout));
+ }
+
+ /**
+ * When the context times out while a Vanadium server is handling the request, we expect a
+ * {@link TimeoutException}.
+ */
+ @Test(expected = TimeoutException.class)
+ public void testTimeout() throws Exception {
+ // Make the server hang forever; never count down
+ final CountDownLatch callLatch = new CountDownLatch(1);
+ final FortuneServer server = new FortuneServerImpl(callLatch, null);
+ final VContext serverContext = V.withNewServer(ctx, "", server, null);
+
+ final FortuneClient client = FortuneClientFactory.getFortuneClient(
+ "/" + V23TestUtil.getServerEndpoint(serverContext));
+
+ final VContext timeout = ctx.withTimeout(Duration.millis(100));
+
+ sync(client.get(timeout));
+ }
+}