providerClass, String providerAuthority) {
+ mProviderClass = providerClass;
+ mProviderAuthority = providerAuthority;
+ }
+
+ private T mProvider;
+
+ /**
+ * Returns the content provider created by this class in the {@link #setUp()} method.
+ * @return T An instance of the provider class given as a parameter to the test case class.
+ */
+ public T getProvider() {
+ return mProvider;
+ }
+
+ abstract protected Context createMockContext(Context delegate);
+
+ /**
+ * Sets up the environment for the test fixture.
+ *
+ * Creates a new
+ * {@link android.test.mock.MockContentResolver}, a new IsolatedContext
+ * that isolates the provider's file operations, and a new instance of
+ * the provider under test within the isolated environment.
+ *
+ *
+ * @throws Exception
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mResolver = new MockContentResolver();
+ final String filenamePrefix = "test.";
+ RenamingDelegatingContext targetContextWrapper = new
+ RenamingDelegatingContext(
+ createMockContext(new MockContext2()), // The context that most methods are delegated to
+ getContext(), // The context that file methods are delegated to
+ filenamePrefix);
+ mProviderContext = new IsolatedContext(mResolver, targetContextWrapper);
+
+ mProvider = mProviderClass.newInstance();
+ mProvider.attachInfo(mProviderContext, null);
+ assertNotNull(mProvider);
+ mResolver.addProvider(mProviderAuthority, getProvider());
+ }
+
+ /**
+ * Tears down the environment for the test fixture.
+ *
+ * Calls {@link android.content.ContentProvider#shutdown()} on the
+ * {@link android.content.ContentProvider} represented by mProvider.
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ mProvider.shutdown();
+ super.tearDown();
+ }
+
+ /**
+ * Gets the {@link MockContentResolver} created by this class during initialization. You
+ * must use the methods of this resolver to access the provider under test.
+ *
+ * @return A {@link MockContentResolver} instance.
+ */
+ public MockContentResolver getMockContentResolver() {
+ return mResolver;
+ }
+
+ /**
+ * Gets the {@link IsolatedContext} created by this class during initialization.
+ * @return The {@link IsolatedContext} instance
+ */
+ public IsolatedContext getMockContext() {
+ return mProviderContext;
+ }
+
+ /**
+ *
+ * Creates a new content provider of the same type as that passed to the test case class,
+ * with an authority name set to the authority parameter, and using an SQLite database as
+ * the underlying data source. The SQL statement parameter is used to create the database.
+ * This method also creates a new {@link MockContentResolver} and adds the provider to it.
+ *
+ *
+ * Both the new provider and the new resolver are put into an {@link IsolatedContext}
+ * that uses the targetContext parameter for file operations and a {@link MockContext}
+ * for everything else. The IsolatedContext prepends the filenamePrefix parameter to
+ * file, database, and directory names.
+ *
+ *
+ * This is a convenience method for creating a "mock" provider that can contain test data.
+ *
+ *
+ * @param targetContext The context to use as the basis of the IsolatedContext
+ * @param filenamePrefix A string that is prepended to file, database, and directory names
+ * @param providerClass The type of the provider being tested
+ * @param authority The authority string to associated with the test provider
+ * @param databaseName The name assigned to the database
+ * @param databaseVersion The version assigned to the database
+ * @param sql A string containing the SQL statements that are needed to create the desired
+ * database and its tables. The format is the same as that generated by the
+ * sqlite3 tool's .dump
command.
+ * @return ContentResolver A new {@link MockContentResolver} linked to the provider
+ *
+ * @throws IllegalAccessException
+ * @throws InstantiationException
+ */
+ public static ContentResolver newResolverWithContentProviderFromSql(
+ Context targetContext, String filenamePrefix, Class providerClass, String authority,
+ String databaseName, int databaseVersion, String sql)
+ throws IllegalAccessException, InstantiationException {
+ MockContentResolver resolver = new MockContentResolver();
+ RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
+ new MockContext(), // The context that most methods are delegated to
+ targetContext, // The context that file methods are delegated to
+ filenamePrefix);
+ Context context = new IsolatedContext(resolver, targetContextWrapper);
+ DatabaseUtils.createDbFromSqlStatements(context, databaseName, databaseVersion, sql);
+
+ T provider = providerClass.newInstance();
+ provider.attachInfo(context, null);
+ resolver.addProvider(authority, provider);
+
+ return resolver;
+ }
+}
diff --git a/test/src/mock/MockContextEmptyComponents.java b/test/src/mock/MockContextEmptyComponents.java
new file mode 100644
index 000000000..eb962bbe9
--- /dev/null
+++ b/test/src/mock/MockContextEmptyComponents.java
@@ -0,0 +1,14 @@
+package mock;
+
+/**
+ * As more components are required to test different parts of F-Droid, we can
+ * create them and add them here (and accessors to the parent class).
+ */
+public class MockContextEmptyComponents extends MockContextSwappableComponents {
+
+ public MockContextEmptyComponents() {
+ setPackageManager(new MockEmptyPackageManager());
+ setResources(new MockEmptyResources());
+ }
+
+}
diff --git a/test/src/mock/MockContextSwappableComponents.java b/test/src/mock/MockContextSwappableComponents.java
new file mode 100644
index 000000000..7fcbbf971
--- /dev/null
+++ b/test/src/mock/MockContextSwappableComponents.java
@@ -0,0 +1,32 @@
+package mock;
+
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.test.mock.MockContext;
+
+public class MockContextSwappableComponents extends MockContext {
+
+ private PackageManager packageManager;
+
+ private Resources resources;
+
+ public MockContextSwappableComponents setPackageManager(PackageManager pm) {
+ packageManager = pm;
+ return this;
+ }
+
+ public MockContextSwappableComponents setResources(Resources resources) {
+ this.resources = resources;
+ return this;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return packageManager;
+ }
+
+ @Override
+ public Resources getResources() {
+ return resources;
+ }
+}
diff --git a/test/src/mock/MockEmptyPackageManager.java b/test/src/mock/MockEmptyPackageManager.java
new file mode 100644
index 000000000..39fdee310
--- /dev/null
+++ b/test/src/mock/MockEmptyPackageManager.java
@@ -0,0 +1,16 @@
+package mock;
+
+import android.content.pm.PackageInfo;
+import android.test.mock.MockPackageManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MockEmptyPackageManager extends MockPackageManager {
+
+ @Override
+ public List getInstalledPackages(int flags) {
+ return new ArrayList();
+ }
+
+}
diff --git a/test/src/mock/MockEmptyResources.java b/test/src/mock/MockEmptyResources.java
new file mode 100644
index 000000000..fdc06e47f
--- /dev/null
+++ b/test/src/mock/MockEmptyResources.java
@@ -0,0 +1,12 @@
+package mock;
+
+import android.test.mock.MockResources;
+
+public class MockEmptyResources extends MockResources {
+
+ @Override
+ public String getString(int id) {
+ return "";
+ }
+
+}
diff --git a/test/src/mock/MockInstallablePackageManager.java b/test/src/mock/MockInstallablePackageManager.java
new file mode 100644
index 000000000..bd47d86f4
--- /dev/null
+++ b/test/src/mock/MockInstallablePackageManager.java
@@ -0,0 +1,26 @@
+package mock;
+
+import android.content.pm.PackageInfo;
+import android.test.mock.MockPackageManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MockInstallablePackageManager extends MockPackageManager {
+
+ private List info = new ArrayList();
+
+ @Override
+ public List getInstalledPackages(int flags) {
+ return info;
+ }
+
+ public void install(String id, int version, String versionName) {
+ PackageInfo p = new PackageInfo();
+ p.packageName = id;
+ p.versionCode = version;
+ p.versionName = versionName;
+ info.add(p);
+ }
+
+}
diff --git a/test/src/org/fdroid/fdroid/AppProviderTest.java b/test/src/org/fdroid/fdroid/AppProviderTest.java
new file mode 100644
index 000000000..c1ccb7b9c
--- /dev/null
+++ b/test/src/org/fdroid/fdroid/AppProviderTest.java
@@ -0,0 +1,138 @@
+package org.fdroid.fdroid;
+
+import android.content.ContentValues;
+import android.content.pm.PackageInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import mock.MockInstallablePackageManager;
+import org.fdroid.fdroid.data.ApkProvider;
+import org.fdroid.fdroid.data.App;
+import org.fdroid.fdroid.data.AppProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class AppProviderTest extends FDroidProviderTest {
+
+ public AppProviderTest() {
+ super(AppProvider.class, AppProvider.getAuthority());
+ }
+
+ protected String[] getMinimalProjection() {
+ return new String[] {
+ AppProvider.DataColumns.APP_ID,
+ AppProvider.DataColumns.NAME
+ };
+ }
+
+ public void testUris() {
+ assertInvalidUri(AppProvider.getAuthority());
+ assertInvalidUri(ApkProvider.getContentUri());
+
+ assertValidUri(AppProvider.getContentUri());
+ assertValidUri(AppProvider.getSearchUri("'searching!'"));
+ assertValidUri(AppProvider.getNoApksUri());
+ assertValidUri(AppProvider.getInstalledUri());
+ assertValidUri(AppProvider.getCanUpdateUri());
+
+ App app = new App();
+ app.id = "org.fdroid.fdroid";
+
+ List apps = new ArrayList(1);
+ apps.add(app);
+
+ assertValidUri(AppProvider.getContentUri(app));
+ assertValidUri(AppProvider.getContentUri(apps));
+ assertValidUri(AppProvider.getContentUri("org.fdroid.fdroid"));
+ }
+
+ public void testQuery() {
+ Cursor cursor = queryAllApps();
+ assertNotNull(cursor);
+ }
+
+ public void testInstalled() {
+
+ Utils.clearInstalledApksCache();
+
+ MockInstallablePackageManager pm = new MockInstallablePackageManager();
+ getSwappableContext().setPackageManager(pm);
+
+ for (int i = 0; i < 100; i ++) {
+ insertApp("com.example.test." + i, "Test app " + i);
+ }
+
+ assertAppCount(100, AppProvider.getContentUri());
+ assertAppCount(0, AppProvider.getInstalledUri());
+
+ for (int i = 10; i < 20; i ++) {
+ pm.install("com.example.test." + i, i, "v1");
+ }
+
+ assertAppCount(10, AppProvider.getInstalledUri());
+ }
+
+ private void assertAppCount(int expectedCount, Uri uri) {
+ Cursor cursor = getProvider().query(uri, getMinimalProjection(), null, null, null);
+ assertNotNull(cursor);
+ assertEquals(expectedCount, cursor.getCount());
+ }
+
+ public void testInsert() {
+
+ // Start with an empty database...
+ Cursor cursor = queryAllApps();
+ assertNotNull(cursor);
+ assertEquals(0, cursor.getCount());
+
+ // Insert a new record...
+ insertApp("org.fdroid.fdroid", "F-Droid");
+ cursor = queryAllApps();
+ assertNotNull(cursor);
+ assertEquals(1, cursor.getCount());
+
+ // We intentionally throw an IllegalArgumentException if you haven't
+ // yet called cursor.move*()...
+ try {
+ new App(cursor);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // Success!
+ } catch (Exception e) {
+ fail();
+ }
+
+ // And now we should be able to recover these values from the app
+ // value object (because the queryAllApps() helper asks for NAME and
+ // APP_ID.
+ cursor.moveToFirst();
+ App app = new App(cursor);
+ assertEquals("org.fdroid.fdroid", app.id);
+ assertEquals("F-Droid", app.name);
+ }
+
+ private Cursor queryAllApps() {
+ return getProvider().query(AppProvider.getContentUri(), getMinimalProjection(), null, null, null);
+ }
+
+ private void insertApp(String id, String name) {
+ ContentValues values = new ContentValues(2);
+ values.put(AppProvider.DataColumns.APP_ID, id);
+ values.put(AppProvider.DataColumns.NAME, name);
+
+ // Required fields (NOT NULL in the database).
+ values.put(AppProvider.DataColumns.SUMMARY, "test summary");
+ values.put(AppProvider.DataColumns.DESCRIPTION, "test description");
+ values.put(AppProvider.DataColumns.LICENSE, "GPL?");
+ values.put(AppProvider.DataColumns.IS_COMPATIBLE, 1);
+ values.put(AppProvider.DataColumns.IGNORE_ALLUPDATES, 0);
+ values.put(AppProvider.DataColumns.IGNORE_THISUPDATE, 0);
+
+ Uri uri = AppProvider.getContentUri();
+
+ getProvider().insert(uri, values);
+ }
+
+}
diff --git a/test/src/org/fdroid/fdroid/FDroidProviderTest.java b/test/src/org/fdroid/fdroid/FDroidProviderTest.java
new file mode 100644
index 000000000..eaaa84e57
--- /dev/null
+++ b/test/src/org/fdroid/fdroid/FDroidProviderTest.java
@@ -0,0 +1,73 @@
+package org.fdroid.fdroid;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.ContactsContract;
+import android.test.ProviderTestCase2MockContext;
+import mock.MockContextEmptyComponents;
+import mock.MockContextSwappableComponents;
+import org.fdroid.fdroid.data.FDroidProvider;
+import org.fdroid.fdroid.mock.MockInstalledApkCache;
+
+public abstract class FDroidProviderTest extends ProviderTestCase2MockContext {
+
+ private MockContextSwappableComponents swappableContext;
+
+ public FDroidProviderTest(Class providerClass, String providerAuthority) {
+ super(providerClass, providerAuthority);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ Utils.setupInstalledApkCache(new MockInstalledApkCache());
+ }
+
+ @TargetApi(Build.VERSION_CODES.ECLAIR)
+ public void testObviouslyInvalidUris() {
+ assertInvalidUri("http://www.google.com");
+ assertInvalidUri(ContactsContract.AUTHORITY_URI);
+ assertInvalidUri("junk");
+ }
+
+ @Override
+ protected Context createMockContext(Context delegate) {
+ swappableContext = new MockContextEmptyComponents();
+ return swappableContext;
+ }
+
+ public MockContextSwappableComponents getSwappableContext() {
+ return swappableContext;
+ }
+
+ protected void assertInvalidUri(String uri) {
+ assertInvalidUri(Uri.parse(uri));
+ }
+
+ protected void assertValidUri(String uri) {
+ assertValidUri(Uri.parse(uri));
+ }
+
+ protected void assertInvalidUri(Uri uri) {
+ try {
+ getProvider().query(uri, getMinimalProjection(), null, null, null);
+ fail();
+ } catch (UnsupportedOperationException e) {}
+ }
+
+ protected void assertValidUri(Uri uri) {
+ Cursor cursor = getProvider().query(uri, getMinimalProjection(), null, null, null);
+ assertNotNull(cursor);
+ }
+
+ /**
+ * Many queries need at least some sort of projection in order to produce
+ * valid SQL. As such, we also need to know about that, so we can provide
+ * helper functions that revolve around the contnet provider under test.
+ */
+ protected abstract String[] getMinimalProjection();
+
+}
diff --git a/test/src/org/fdroid/fdroid/FDroidTest.java b/test/src/org/fdroid/fdroid/FDroidTest.java
new file mode 100644
index 000000000..07d9f3ee3
--- /dev/null
+++ b/test/src/org/fdroid/fdroid/FDroidTest.java
@@ -0,0 +1,14 @@
+package org.fdroid.fdroid;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.test.ActivityInstrumentationTestCase2;
+
+@TargetApi(Build.VERSION_CODES.CUPCAKE)
+public class FDroidTest extends ActivityInstrumentationTestCase2 {
+
+ public FDroidTest() {
+ super("org.fdroid.fdroid", FDroid.class);
+ }
+
+}
diff --git a/test/src/org/fdroid/fdroid/mock/MockInstalledApkCache.java b/test/src/org/fdroid/fdroid/mock/MockInstalledApkCache.java
new file mode 100644
index 000000000..acac7557f
--- /dev/null
+++ b/test/src/org/fdroid/fdroid/mock/MockInstalledApkCache.java
@@ -0,0 +1,16 @@
+package org.fdroid.fdroid.mock;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import org.fdroid.fdroid.Utils;
+
+import java.util.Map;
+
+public class MockInstalledApkCache extends Utils.InstalledApkCache {
+
+ @Override
+ public Map getApks(Context context) {
+ return buildAppList(context);
+ }
+
+}
diff --git a/tests/gen/org/fdroid/fdroid/tests/BuildConfig.java b/tests/gen/org/fdroid/fdroid/tests/BuildConfig.java
deleted file mode 100644
index 2892c52fb..000000000
--- a/tests/gen/org/fdroid/fdroid/tests/BuildConfig.java
+++ /dev/null
@@ -1,8 +0,0 @@
-/*___Generated_by_IDEA___*/
-
-/** Automatically generated file. DO NOT MODIFY */
-package org.fdroid.fdroid.tests;
-
-public final class BuildConfig {
- public final static boolean DEBUG = true;
-}
\ No newline at end of file
diff --git a/tests/gen/org/fdroid/fdroid/tests/Manifest.java b/tests/gen/org/fdroid/fdroid/tests/Manifest.java
deleted file mode 100644
index 15e60435f..000000000
--- a/tests/gen/org/fdroid/fdroid/tests/Manifest.java
+++ /dev/null
@@ -1,7 +0,0 @@
-/*___Generated_by_IDEA___*/
-
-package org.fdroid.fdroid.tests;
-
-/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
-public final class Manifest {
-}
\ No newline at end of file
diff --git a/tests/gen/org/fdroid/fdroid/tests/R.java b/tests/gen/org/fdroid/fdroid/tests/R.java
deleted file mode 100644
index 40f6d3574..000000000
--- a/tests/gen/org/fdroid/fdroid/tests/R.java
+++ /dev/null
@@ -1,7 +0,0 @@
-/*___Generated_by_IDEA___*/
-
-package org.fdroid.fdroid.tests;
-
-/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
-public final class R {
-}
\ No newline at end of file
diff --git a/tests/local.properties b/tests/local.properties
deleted file mode 100644
index 12a01149a..000000000
--- a/tests/local.properties
+++ /dev/null
@@ -1,10 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must *NOT* be checked into Version Control Systems,
-# as it contains information specific to your local configuration.
-
-# location of the SDK. This is only used by Ant
-# For customization when using a Version Control System, please read the
-# header note.
-sdk.dir=/opt/android-sdk