diff --git a/app/build.gradle b/app/build.gradle
index cdb397eb..a6fffa64 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -73,7 +73,9 @@ dependencies {
compile 'com.github.yukuku:ambilwarna:2.0.1'
compile project(':MemorizingTrustManager')
- androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.2.0'
compile 'dnsjava:dnsjava:2.1.7'
compile 'org.apache.commons:commons-lang3:3.4'
+
+ // for tests
+ androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.2.0'
}
diff --git a/app/src/androidTest/java/at/bitfire/davdroid/SSLSocketFactoryCompatTest.java b/app/src/androidTest/java/at/bitfire/davdroid/SSLSocketFactoryCompatTest.java
index a9def171..feb5e768 100644
--- a/app/src/androidTest/java/at/bitfire/davdroid/SSLSocketFactoryCompatTest.java
+++ b/app/src/androidTest/java/at/bitfire/davdroid/SSLSocketFactoryCompatTest.java
@@ -11,15 +11,12 @@ package at.bitfire.davdroid;
import android.os.Build;
import android.test.InstrumentationTestCase;
-import junit.framework.TestCase;
-
import java.io.IOException;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import de.duenndns.ssl.MemorizingTrustManager;
-import okhttp3.OkHttpClient;
import okhttp3.mockwebserver.MockWebServer;
public class SSLSocketFactoryCompatTest extends InstrumentationTestCase {
diff --git a/app/src/androidTest/java/at/bitfire/davdroid/TestConstants.java b/app/src/androidTest/java/at/bitfire/davdroid/TestConstants.java
deleted file mode 100644
index 28836104..00000000
--- a/app/src/androidTest/java/at/bitfire/davdroid/TestConstants.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright © 2013 – 2015 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- */
-
-package at.bitfire.davdroid;
-
-import android.util.Log;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-
-public class TestConstants {
- public static final String ROBOHYDRA_BASE = "http://192.168.0.11:3000/";
-
- public static URI roboHydra;
- static {
- try {
- roboHydra = new URI(ROBOHYDRA_BASE);
- } catch(URISyntaxException e) {
- Log.wtf("davdroid.test.Constants", "Invalid RoboHydra base URL");
- }
- }
-}
diff --git a/app/src/androidTest/java/at/bitfire/davdroid/TestDavUtils.java b/app/src/androidTest/java/at/bitfire/davdroid/TestDavUtils.java
new file mode 100644
index 00000000..92549697
--- /dev/null
+++ b/app/src/androidTest/java/at/bitfire/davdroid/TestDavUtils.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v3.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+package at.bitfire.davdroid;
+
+import junit.framework.TestCase;
+
+public class TestDavUtils extends TestCase {
+
+ private static final String exampleURL = "http://example.com/";
+
+ public void testLastSegmentOfUrl() {
+ assertEquals("/", DavUtils.lastSegmentOfUrl(exampleURL));
+ assertEquals("dir", DavUtils.lastSegmentOfUrl(exampleURL + "dir"));
+ assertEquals("dir", DavUtils.lastSegmentOfUrl(exampleURL + "dir/"));
+ assertEquals("file.html", DavUtils.lastSegmentOfUrl(exampleURL + "dir/file.html"));
+ }
+
+}
diff --git a/app/src/androidTest/java/at/bitfire/davdroid/model/CollectionInfoTest.java b/app/src/androidTest/java/at/bitfire/davdroid/model/CollectionInfoTest.java
new file mode 100644
index 00000000..a4789b94
--- /dev/null
+++ b/app/src/androidTest/java/at/bitfire/davdroid/model/CollectionInfoTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v3.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+package at.bitfire.davdroid.model;
+
+import android.content.ContentValues;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+
+import at.bitfire.dav4android.DavResource;
+import at.bitfire.dav4android.exception.DavException;
+import at.bitfire.dav4android.exception.HttpException;
+import at.bitfire.dav4android.property.ResourceType;
+import at.bitfire.davdroid.HttpClient;
+import at.bitfire.davdroid.model.ServiceDB.Collections;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+
+public class CollectionInfoTest extends TestCase {
+
+ MockWebServer server = new MockWebServer();
+
+ public void testFromDavResource() throws IOException, HttpException, DavException {
+ // r/w address book
+ server.enqueue(new MockResponse()
+ .setResponseCode(207)
+ .setBody("" +
+ "" +
+ " /" +
+ " " +
+ " " +
+ " My Contacts" +
+ " My Contacts Description" +
+ " " +
+ "" +
+ ""));
+
+ DavResource dav = new DavResource(HttpClient.create(), server.url("/"));
+ dav.propfind(0, ResourceType.NAME);
+ CollectionInfo info = CollectionInfo.fromDavResource(dav);
+ assertEquals(CollectionInfo.Type.ADDRESS_BOOK, info.type);
+ assertFalse(info.readOnly);
+ assertEquals("My Contacts", info.displayName);
+ assertEquals("My Contacts Description", info.description);
+
+ // read-only calendar, no display name
+ server.enqueue(new MockResponse()
+ .setResponseCode(207)
+ .setBody("" +
+ "" +
+ " /" +
+ " " +
+ " " +
+ " " +
+ " My Calendar" +
+ " tzdata" +
+ " #ff0000" +
+ " " +
+ "" +
+ ""));
+
+ dav = new DavResource(HttpClient.create(), server.url("/"));
+ dav.propfind(0, ResourceType.NAME);
+ info = CollectionInfo.fromDavResource(dav);
+ assertEquals(CollectionInfo.Type.CALENDAR, info.type);
+ assertTrue(info.readOnly);
+ assertNull(info.displayName);
+ assertEquals("My Calendar", info.description);
+ assertEquals(0xFFFF0000, (int)info.color);
+ assertEquals("tzdata", info.timeZone);
+ assertTrue(info.supportsVEVENT);
+ assertTrue(info.supportsVTODO);
+ }
+
+ public void testFromDB() {
+ ContentValues values = new ContentValues();
+ values.put(Collections.ID, 1);
+ values.put(Collections.SERVICE_ID, 1);
+ values.put(Collections.URL, "http://example.com");
+ values.put(Collections.READ_ONLY, 1);
+ values.put(Collections.DISPLAY_NAME, "display name");
+ values.put(Collections.DESCRIPTION, "description");
+ values.put(Collections.COLOR, 0xFFFF0000);
+ values.put(Collections.TIME_ZONE, "tzdata");
+ values.put(Collections.SUPPORTS_VEVENT, 1);
+ values.put(Collections.SUPPORTS_VTODO, 1);
+ values.put(Collections.SYNC, 1);
+
+ CollectionInfo info = CollectionInfo.fromDB(values);
+ assertEquals(1, info.id);
+ assertEquals(1, (long)info.serviceID);
+ assertEquals("http://example.com", info.url);
+ assertTrue(info.readOnly);
+ assertEquals("display name", info.displayName);
+ assertEquals("description", info.description);
+ assertEquals(0xFFFF0000, (int)info.color);
+ assertEquals("tzdata", info.timeZone);
+ assertTrue(info.supportsVEVENT);
+ assertTrue(info.supportsVTODO);
+ assertTrue(info.selected);
+ }
+
+}
diff --git a/app/src/androidTest/java/at/bitfire/davdroid/resource/DavResourceFinderTest.java b/app/src/androidTest/java/at/bitfire/davdroid/resource/DavResourceFinderTest.java
deleted file mode 100644
index 752c3e06..00000000
--- a/app/src/androidTest/java/at/bitfire/davdroid/resource/DavResourceFinderTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package at.bitfire.davdroid.resource;
-
-import android.test.InstrumentationTestCase;
-
-import java.io.IOException;
-import java.net.URI;
-
-import at.bitfire.dav4android.exception.DavException;
-import at.bitfire.dav4android.exception.HttpException;
-import at.bitfire.davdroid.ui.setup.DavResourceFinder;
-import at.bitfire.davdroid.ui.setup.LoginCredentials;
-import okhttp3.HttpUrl;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.MockWebServer;
-
-public class DavResourceFinderTest extends InstrumentationTestCase {
-
- MockWebServer server = new MockWebServer();
-
- @Override
- protected void setUp() throws Exception {
- server.start();
- }
-
- @Override
- protected void tearDown() throws Exception {
- server.shutdown();
- }
-
-
- public void testGetCurrentUserPrincipal() throws IOException, HttpException, DavException {
- HttpUrl url = server.url("/dav");
- LoginCredentials credentials = new LoginCredentials(url.uri(), "admin", "12345", true);
- DavResourceFinder finder = new DavResourceFinder(getInstrumentation().getTargetContext().getApplicationContext(), credentials);
-
- // positive test case
- server.enqueue(new MockResponse() // PROPFIND response
- .setResponseCode(207)
- .setHeader("Content-Type", "application/xml;charset=utf-8")
- .setBody("" +
- " " +
- " /dav" +
- " " +
- " " +
- " /principals/myself" +
- " " +
- " HTTP/1.0 200 OK" +
- " " +
- " " +
- ""));
- server.enqueue(new MockResponse() // OPTIONS response
- .setResponseCode(200)
- .setHeader("DAV", "addressbook"));
- URI principal = finder.getCurrentUserPrincipal(url, DavResourceFinder.Service.CARDDAV);
- assertEquals(url.resolve("/principals/myself").uri(), principal);
-
- // negative test case: no current-user-principal
- server.enqueue(new MockResponse()
- .setResponseCode(207)
- .setHeader("Content-Type", "application/xml;charset=utf-8")
- .setBody("" +
- " " +
- " /dav" +
- " HTTP/1.0 200 OK" +
- " " +
- ""));
- assertNull(finder.getCurrentUserPrincipal(url, DavResourceFinder.Service.CARDDAV));
-
- // negative test case: requested service not available
- server.enqueue(new MockResponse() // PROPFIND response
- .setResponseCode(207)
- .setHeader("Content-Type", "application/xml;charset=utf-8")
- .setBody("" +
- " " +
- " /dav" +
- " " +
- " " +
- " /principals/myself" +
- " " +
- " HTTP/1.0 200 OK" +
- " " +
- " " +
- ""));
- server.enqueue(new MockResponse() // OPTIONS response
- .setResponseCode(200)
- .setHeader("DAV", "addressbook"));
- assertNull(finder.getCurrentUserPrincipal(url, DavResourceFinder.Service.CALDAV));
- }
-
-}
diff --git a/app/src/androidTest/java/at/bitfire/davdroid/ui/setup/DavResourceFinderTest.java b/app/src/androidTest/java/at/bitfire/davdroid/ui/setup/DavResourceFinderTest.java
new file mode 100644
index 00000000..4070c44e
--- /dev/null
+++ b/app/src/androidTest/java/at/bitfire/davdroid/ui/setup/DavResourceFinderTest.java
@@ -0,0 +1,179 @@
+package at.bitfire.davdroid.ui.setup;
+
+import android.test.InstrumentationTestCase;
+
+import java.io.IOException;
+import java.net.URI;
+
+import at.bitfire.dav4android.DavResource;
+import at.bitfire.dav4android.exception.DavException;
+import at.bitfire.dav4android.exception.HttpException;
+import at.bitfire.dav4android.property.AddressbookHomeSet;
+import at.bitfire.dav4android.property.ResourceType;
+import at.bitfire.davdroid.App;
+import at.bitfire.davdroid.HttpClient;
+import at.bitfire.davdroid.ui.setup.DavResourceFinder.Configuration.ServiceInfo;
+import okhttp3.OkHttpClient;
+import okhttp3.mockwebserver.Dispatcher;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+
+public class DavResourceFinderTest extends InstrumentationTestCase {
+
+ MockWebServer server = new MockWebServer();
+
+ DavResourceFinder finder;
+ OkHttpClient client;
+ LoginCredentials credentials;
+
+ private static final String
+ PATH_NO_DAV = "/nodav",
+
+ PATH_CALDAV = "/caldav",
+ PATH_CARDDAV = "/carddav",
+ PATH_CALDAV_AND_CARDDAV = "/both-caldav-carddav",
+
+ SUBPATH_PRINCIPAL = "/principal",
+ SUBPATH_ADDRESSBOOK_HOMESET = "/addressbooks",
+ SUBPATH_ADDRESSBOOK = "/addressbooks/private-contacts";
+
+ @Override
+ protected void setUp() throws Exception {
+ server.setDispatcher(new TestDispatcher());
+ server.start();
+
+ credentials = new LoginCredentials(URI.create("/"), "mock", "12345", true);
+ finder = new DavResourceFinder(getInstrumentation().getContext(), credentials);
+
+ client = HttpClient.create();
+ client = HttpClient.addAuthentication(client, credentials.userName, credentials.password, credentials.authPreemptive);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ server.shutdown();
+ }
+
+
+ public void testRememberIfAddressBookOrHomeset() throws IOException, HttpException, DavException {
+ ServiceInfo info;
+
+ // before dav.propfind(), no info is available
+ DavResource dav = new DavResource(client, server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL));
+ finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
+ assertEquals(0, info.collections.size());
+ assertEquals(0, info.homeSets.size());
+
+ // recognize home set
+ dav.propfind(0, AddressbookHomeSet.NAME);
+ finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
+ assertEquals(0, info.collections.size());
+ assertEquals(1, info.homeSets.size());
+ assertEquals(server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK_HOMESET + "/").uri(), info.homeSets.iterator().next());
+
+ // recognize address book
+ dav = new DavResource(client, server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK));
+ dav.propfind(0, ResourceType.NAME);
+ finder.rememberIfAddressBookOrHomeset(dav, info = new ServiceInfo());
+ assertEquals(1, info.collections.size());
+ assertEquals(server.url(PATH_CARDDAV + SUBPATH_ADDRESSBOOK + "/").uri(), info.collections.keySet().iterator().next());
+ assertEquals(0, info.homeSets.size());
+ }
+
+ public void testProvidesService() throws IOException {
+ assertFalse(finder.providesService(server.url(PATH_NO_DAV), DavResourceFinder.Service.CALDAV));
+ assertFalse(finder.providesService(server.url(PATH_NO_DAV), DavResourceFinder.Service.CARDDAV));
+
+ assertTrue(finder.providesService(server.url(PATH_CALDAV), DavResourceFinder.Service.CALDAV));
+ assertFalse(finder.providesService(server.url(PATH_CALDAV), DavResourceFinder.Service.CARDDAV));
+
+ assertTrue(finder.providesService(server.url(PATH_CARDDAV), DavResourceFinder.Service.CARDDAV));
+ assertFalse(finder.providesService(server.url(PATH_CARDDAV), DavResourceFinder.Service.CALDAV));
+
+ assertTrue(finder.providesService(server.url(PATH_CALDAV_AND_CARDDAV), DavResourceFinder.Service.CALDAV));
+ assertTrue(finder.providesService(server.url(PATH_CALDAV_AND_CARDDAV), DavResourceFinder.Service.CARDDAV));
+ }
+
+ public void testGetCurrentUserPrincipal() throws IOException, HttpException, DavException {
+ assertNull(finder.getCurrentUserPrincipal(server.url(PATH_NO_DAV), DavResourceFinder.Service.CALDAV));
+ assertNull(finder.getCurrentUserPrincipal(server.url(PATH_NO_DAV), DavResourceFinder.Service.CARDDAV));
+
+ assertEquals(
+ server.url(PATH_CALDAV + SUBPATH_PRINCIPAL).uri(),
+ finder.getCurrentUserPrincipal(server.url(PATH_CALDAV), DavResourceFinder.Service.CALDAV)
+ );
+ assertNull(finder.getCurrentUserPrincipal(server.url(PATH_CALDAV), DavResourceFinder.Service.CARDDAV));
+
+ assertEquals(
+ server.url(PATH_CARDDAV + SUBPATH_PRINCIPAL).uri(),
+ finder.getCurrentUserPrincipal(server.url(PATH_CARDDAV), DavResourceFinder.Service.CARDDAV)
+ );
+ assertNull(finder.getCurrentUserPrincipal(server.url(PATH_CARDDAV), DavResourceFinder.Service.CALDAV));
+ }
+
+
+ // mock server
+
+ public class TestDispatcher extends Dispatcher {
+
+ @Override
+ public MockResponse dispatch(RecordedRequest rq) throws InterruptedException {
+ if (!checkAuth(rq))
+ return new MockResponse().setResponseCode(401);
+
+ String path = rq.getPath();
+
+ if ("OPTIONS".equalsIgnoreCase(rq.getMethod())) {
+ String dav = null;
+ if (path.startsWith(PATH_CALDAV))
+ dav = "calendar-access";
+ else if (path.startsWith(PATH_CARDDAV))
+ dav = "addressbook";
+ else if (path.startsWith(PATH_CALDAV_AND_CARDDAV))
+ dav = "calendar-access, addressbook";
+ MockResponse response = new MockResponse().setResponseCode(200);
+ if (dav != null)
+ response.addHeader("DAV", dav);
+ return response;
+
+ } else if ("PROPFIND".equalsIgnoreCase(rq.getMethod())) {
+ String props = null;
+ switch (path) {
+ case PATH_CALDAV:
+ case PATH_CARDDAV:
+ props = "" + path + SUBPATH_PRINCIPAL + "";
+ break;
+
+ case PATH_CARDDAV + SUBPATH_PRINCIPAL:
+ props = "" +
+ " " + PATH_CARDDAV + SUBPATH_ADDRESSBOOK_HOMESET + "" +
+ "";
+ break;
+ case PATH_CARDDAV + SUBPATH_ADDRESSBOOK:
+ props = "" +
+ " " +
+ " " +
+ "";
+ break;
+ }
+ App.log.info("Sending props: " + props);
+ return new MockResponse()
+ .setResponseCode(207)
+ .setBody("" +
+ "" +
+ " " + rq.getPath() + "" +
+ " " + props + "" +
+ "" +
+ "");
+ }
+
+ return new MockResponse().setResponseCode(404);
+ }
+
+ private boolean checkAuth(RecordedRequest rq) {
+ return "Basic bW9jazoxMjM0NQ==".equals(rq.getHeader("Authorization"));
+ }
+ }
+
+}
diff --git a/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.java b/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.java
index 2ac5b5f1..86d623a4 100644
--- a/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.java
+++ b/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.java
@@ -122,7 +122,7 @@ public class ServiceDB {
Collections.DISPLAY_NAME + " TEXT NULL," +
Collections.DESCRIPTION + " TEXT NULL," +
Collections.COLOR + " INTEGER NULL," +
- Collections.TIME_ZONE + " TEXt NULL," +
+ Collections.TIME_ZONE + " TEXT NULL," +
Collections.SUPPORTS_VEVENT + " INTEGER NULL," +
Collections.SUPPORTS_VTODO + " INTEGER NULL," +
Collections.SYNC + " INTEGER DEFAULT 0 NOT NULL" +
diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/DavResourceFinder.java b/app/src/main/java/at/bitfire/davdroid/ui/setup/DavResourceFinder.java
index bf208585..ffb12036 100644
--- a/app/src/main/java/at/bitfire/davdroid/ui/setup/DavResourceFinder.java
+++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/DavResourceFinder.java
@@ -190,6 +190,13 @@ public class DavResourceFinder {
}
}
+ /**
+ * If #dav is an address book or an address book home set, it will added to
+ * config.collections or config.homesets. Only evaluates already known properties,
+ * does not call dav.propfind()! URLs will be stored with trailing "/".
+ * @param dav resource whose properties are evaluated
+ * @param config structure where the address book (collection) and/or home set is stored into (if found)
+ */
protected void rememberIfAddressBookOrHomeset(@NonNull DavResource dav, @NonNull Configuration.ServiceInfo config) {
// Is the collection an address book?
ResourceType resourceType = (ResourceType)dav.properties.get(ResourceType.NAME);
@@ -223,7 +230,7 @@ public class DavResourceFinder {
}
- boolean providesService(HttpUrl url, Service service) throws IOException {
+ protected boolean providesService(HttpUrl url, Service service) throws IOException {
DavResource davPrincipal = new DavResource(httpClient, url, log);
try {
davPrincipal.options();