syncbase high-level API: add basic test

Change-Id: I3c0ee8aa29b8f148779d6cc7afcc11716e2d773a
diff --git a/syncbase/Makefile b/syncbase/Makefile
index 19f86e7..85aa5a4 100644
--- a/syncbase/Makefile
+++ b/syncbase/Makefile
@@ -3,8 +3,25 @@
 
 SHELL := /bin/bash -euo pipefail
 
+.DELETE_ON_ERROR:
+
+.PHONY: build
+build:
+	./gradlew build
+
+# TODO(sadovsky): Avoid the "cp" hack.
+.PHONY: test
+test:
+	cp ../lib/build/libs/libv23.dylib ./build/libs/
+	./gradlew testDebug --tests=io.v.syncbase.SyncbaseTest --info
+
+.PHONY: clean
+clean:
+	./gradlew clean
+
 # Note, javadoc complains about the self-closing <p/> tags that Android Studio
 # creates.
+.PHONY: javadoc
 javadoc:
 	javadoc -d /tmp/javadocs -sourcepath src/main/java -subpackages io || true
 	@echo "open file:///tmp/javadocs/index.html"
diff --git a/syncbase/build.gradle b/syncbase/build.gradle
index abc7bff..5953a04 100644
--- a/syncbase/build.gradle
+++ b/syncbase/build.gradle
@@ -18,13 +18,13 @@
 }
 
 // You should update this after releasing a new version of the Syncbase API. See the
-// list of published versions at https://repo1.maven.org/maven2/io/v/syncbase
-version = '0.1.3'
+// list of published versions at: https://repo1.maven.org/maven2/io/v/syncbase
+version = '0.1.4'
 group = 'io.v'
 
-def siteUrl = 'https://github.com/vanadium/java'
+def siteUrl = 'https://github.com/vanadium/java/syncbase'
 def gitUrl = 'https://github.com/vanadium/java.git'
-def pkgDesc = 'Storage system with peer-to-peer, secure, offline data synchronization'
+def pkgDesc = 'Offline-capable storage system with secure peer-to-peer data synchronization'
 
 apply plugin: 'com.android.library'
 apply plugin: 'com.github.dcendents.android-maven'
@@ -120,6 +120,10 @@
         unitTests.all {
             systemProperty 'java.library.path', 'build/libs'
         }
+        // http://tools.android.com/tech-docs/unit-testing-support#TOC-Method-...-not-mocked.-
+        // TODO(sadovsky): Seems dangerous to set this. We should probably mock out the relevant
+        // Android methods, e.g. android.util.Log.
+        unitTests.returnDefaultValues = true
     }
 }
 
@@ -150,7 +154,7 @@
                     + 'https://vanadium.github.io/installation/')
         }
 
-        result.jiriRoot = new File(System.getenv()['JIRI_ROOT'])
+        result.jiriRoot = new File(System.getenv('JIRI_ROOT'))
         return result
     }
 }
diff --git a/syncbase/src/main/java/io/v/syncbase/Id.java b/syncbase/src/main/java/io/v/syncbase/Id.java
index b363875..0f8efa3 100644
--- a/syncbase/src/main/java/io/v/syncbase/Id.java
+++ b/syncbase/src/main/java/io/v/syncbase/Id.java
@@ -18,6 +18,7 @@
 
     // TODO(sadovsky): Replace encode and decode method implementations with calls to Cgo.
     private static final String SEPARATOR = ",";
+
     public static Id decode(String encodedId) {
         String[] parts = encodedId.split(SEPARATOR);
         if (parts.length != 2) {
@@ -41,7 +42,7 @@
     @Override
     public boolean equals(Object other) {
         if (other instanceof Id && other != null) {
-            Id otherId = (Id)other;
+            Id otherId = (Id) other;
             return mBlessing.equals(otherId.getBlessing()) && mName.equals(otherId.getName());
         }
         return false;
diff --git a/syncbase/src/main/java/io/v/syncbase/Syncbase.java b/syncbase/src/main/java/io/v/syncbase/Syncbase.java
index d1bb49f..0971e53 100644
--- a/syncbase/src/main/java/io/v/syncbase/Syncbase.java
+++ b/syncbase/src/main/java/io/v/syncbase/Syncbase.java
@@ -4,15 +4,15 @@
 
 package io.v.syncbase;
 
-import android.os.Handler;
 import android.util.Log;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 import java.io.File;
+import java.util.Timer;
+import java.util.TimerTask;
 
-import io.v.android.VAndroidContext;
 import io.v.android.v23.V;
 import io.v.impl.google.services.syncbase.SyncbaseServer;
 import io.v.v23.context.VContext;
@@ -49,7 +49,7 @@
         // FOR ADVANCED USERS. If true, the user's data will not be synced across their devices.
         public boolean disableUserdataSyncgroup;
         // TODO(sadovsky): Drop this once we switch from io.v.v23.syncbase to io.v.syncbase.core.
-        public VAndroidContext vAndroidContext;
+        public VContext vContext;
     }
 
     private static DatabaseOptions sOpts;
@@ -65,7 +65,15 @@
             USERDATA_SYNCGROUP_NAME = "userdata";
 
     protected static void enqueue(final Runnable r) {
-        new Handler().post(r);
+        // Note, we use Timer rather than Handler because the latter must be mocked out for tests,
+        // which is rather annoying.
+        //new Handler().post(r);
+        new Timer().schedule(new TimerTask() {
+            @Override
+            public void run() {
+                r.run();
+            }
+        }, 0);
     }
 
     public static abstract class DatabaseCallback {
@@ -95,6 +103,7 @@
                     cb.onSuccess(sDatabase);
                 }
             });
+            return;
         }
         sOpts = opts;
         // TODO(sadovsky): Call ctx.cancel in sDatabase destructor?
@@ -109,6 +118,7 @@
                     cb.onError(e);
                 }
             });
+            return;
         }
         if (sOpts.disableUserdataSyncgroup) {
             Database.CollectionOptions cxOpts = new DatabaseHandle.CollectionOptions();
@@ -132,7 +142,7 @@
     // io.v.syncbase.core. Note, much of this code was copied from the Todos app.
 
     protected static VContext getVContext() {
-        return sOpts.vAndroidContext.getVContext();
+        return sOpts.vContext;
     }
 
     // TODO(sadovsky): Some of these constants should become fields in DatabaseOptions.
diff --git a/syncbase/src/test/java/io/v/syncbase/SyncbaseTest.java b/syncbase/src/test/java/io/v/syncbase/SyncbaseTest.java
index 123aeb3..a48f52a 100644
--- a/syncbase/src/test/java/io/v/syncbase/SyncbaseTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/SyncbaseTest.java
@@ -4,14 +4,51 @@
 
 package io.v.syncbase;
 
+import com.google.common.util.concurrent.SettableFuture;
+
+import org.junit.Before;
 import org.junit.Test;
 
+import java.util.concurrent.TimeUnit;
+
+import io.v.v23.V;
+import io.v.v23.context.VContext;
+import io.v.v23.rpc.ListenSpec;
+
 public class SyncbaseTest {
+    private VContext ctx;
+
+    // To run these tests from Android Studio, add the following VM option to the default JUnit
+    // build configuration, via Run > Edit Configurations... > Defaults > JUnit > VM options:
+    // -Djava.library.path=/Users/sadovsky/vanadium/release/java/syncbase/build/libs
+    @Before
+    public void setUp() throws Exception {
+        ctx = V.init();
+        ctx = V.withListenSpec(ctx, V.getListenSpec(ctx).withAddress(
+                new ListenSpec.Address("tcp", "localhost:0")));
+    }
+
     @Test
-    public void createDatabase() {
+    public void createDatabase() throws Exception {
         Syncbase.DatabaseOptions opts = new Syncbase.DatabaseOptions();
         opts.rootDir = "/tmp";
-        // TODO(sadovsky): Restore this once we figure out what Vanadium context to pass.
-        // Syncbase.database(opts);
+        opts.disableUserdataSyncgroup = true;
+        opts.vContext = ctx;
+
+        final SettableFuture<Database> future = SettableFuture.create();
+
+        Syncbase.database(new Syncbase.DatabaseCallback() {
+            @Override
+            public void onSuccess(Database db) {
+                future.set(db);
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                future.setException(e);
+            }
+        }, opts);
+
+        future.get(5, TimeUnit.SECONDS);
     }
 }
\ No newline at end of file