diff --git a/app/src/androidTest/java/org/fdroid/fdroid/RepoXMLHandlerTest.java b/app/src/androidTest/java/org/fdroid/fdroid/RepoXMLHandlerTest.java index 8b2b7f6f1..d5bc8c74f 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/RepoXMLHandlerTest.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/RepoXMLHandlerTest.java @@ -9,7 +9,7 @@ import android.util.Log; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.Repo; -import org.fdroid.fdroid.mock.MockRepo; +import org.fdroid.fdroid.mock.MockRepoOld; import org.junit.Test; import org.junit.runner.RunWith; import org.xml.sax.InputSource; @@ -678,7 +678,7 @@ public class RepoXMLHandlerTest { parser = SAXParserFactory.newInstance().newSAXParser(); XMLReader reader = parser.getXMLReader(); RepoDetails repoDetails = new RepoDetails(); - RepoXMLHandler handler = new RepoXMLHandler(new MockRepo(100), repoDetails); + RepoXMLHandler handler = new RepoXMLHandler(new MockRepoOld(100), repoDetails); reader.setContentHandler(handler); String resName = "assets/" + indexFilename; Log.i(TAG, "test file: " + getClass().getClassLoader().getResource(resName)); diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/ApkProviderHelperTest.java b/app/src/androidTest/java/org/fdroid/fdroid/data/ApkProviderHelperTest.java deleted file mode 100644 index 307655a72..000000000 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/ApkProviderHelperTest.java +++ /dev/null @@ -1,216 +0,0 @@ -package org.fdroid.fdroid.data; - -import android.content.ContentValues; -import android.database.Cursor; -import android.net.Uri; - -import org.fdroid.fdroid.TestUtils; -import org.fdroid.fdroid.Utils; -import org.fdroid.fdroid.mock.MockApk; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -@SuppressWarnings("PMD") // TODO port this to JUnit 4 semantics -public class ApkProviderHelperTest extends BaseApkProviderTest { - - public void testKnownApks() { - - for (int i = 0; i < 7; i++) { - TestUtils.insertApk(this, "org.fdroid.fdroid", i); - } - - for (int i = 0; i < 9; i++) { - TestUtils.insertApk(this, "org.example", i); - } - - for (int i = 0; i < 3; i++) { - TestUtils.insertApk(this, "com.example", i); - } - - TestUtils.insertApk(this, "com.apk.thingo", 1); - - Apk[] known = { - new MockApk("org.fdroid.fdroid", 1), - new MockApk("org.fdroid.fdroid", 3), - new MockApk("org.fdroid.fdroid", 5), - - new MockApk("com.example", 1), - new MockApk("com.example", 2), - }; - - Apk[] unknown = { - new MockApk("org.fdroid.fdroid", 7), - new MockApk("org.fdroid.fdroid", 9), - new MockApk("org.fdroid.fdroid", 11), - new MockApk("org.fdroid.fdroid", 13), - - new MockApk("com.example", 3), - new MockApk("com.example", 4), - new MockApk("com.example", 5), - - new MockApk("info.example", 1), - new MockApk("info.example", 2), - }; - - List apksToCheck = new ArrayList<>(known.length + unknown.length); - Collections.addAll(apksToCheck, known); - Collections.addAll(apksToCheck, unknown); - - String[] projection = { - ApkProvider.DataColumns.PACKAGE_NAME, - ApkProvider.DataColumns.VERSION_CODE, - }; - - List knownApks = ApkProvider.Helper.knownApks(getMockContext(), apksToCheck, projection); - - assertResultCount(known.length, knownApks); - - for (Apk knownApk : knownApks) { - assertContains(knownApks, knownApk); - } - } - - public void testFindByApp() { - - for (int i = 0; i < 7; i++) { - TestUtils.insertApk(this, "org.fdroid.fdroid", i); - } - - for (int i = 0; i < 9; i++) { - TestUtils.insertApk(this, "org.example", i); - } - - for (int i = 0; i < 3; i++) { - TestUtils.insertApk(this, "com.example", i); - } - - TestUtils.insertApk(this, "com.apk.thingo", 1); - - assertTotalApkCount(7 + 9 + 3 + 1); - - List fdroidApks = ApkProvider.Helper.findByPackageName(getMockContext(), "org.fdroid.fdroid"); - assertResultCount(7, fdroidApks); - assertBelongsToApp(fdroidApks, "org.fdroid.fdroid"); - - List exampleApks = ApkProvider.Helper.findByPackageName(getMockContext(), "org.example"); - assertResultCount(9, exampleApks); - assertBelongsToApp(exampleApks, "org.example"); - - List exampleApks2 = ApkProvider.Helper.findByPackageName(getMockContext(), "com.example"); - assertResultCount(3, exampleApks2); - assertBelongsToApp(exampleApks2, "com.example"); - - List thingoApks = ApkProvider.Helper.findByPackageName(getMockContext(), "com.apk.thingo"); - assertResultCount(1, thingoApks); - assertBelongsToApp(thingoApks, "com.apk.thingo"); - } - - public void testUpdate() { - - Uri apkUri = TestUtils.insertApk(this, "com.example", 10); - - String[] allFields = ApkProvider.DataColumns.ALL; - Cursor cursor = getMockContentResolver().query(apkUri, allFields, null, null, null); - assertResultCount(1, cursor); - - cursor.moveToFirst(); - Apk apk = new Apk(cursor); - cursor.close(); - - assertEquals("com.example", apk.packageName); - assertEquals(10, apk.versionCode); - - assertNull(apk.features); - assertNull(apk.added); - assertNull(apk.hashType); - - apk.features = Utils.CommaSeparatedList.make("one,two,three"); - long dateTimestamp = System.currentTimeMillis(); - apk.added = new Date(dateTimestamp); - apk.hashType = "i'm a hash type"; - - ApkProvider.Helper.update(getMockContext(), apk); - - // Should not have inserted anything else, just updated the already existing apk. - Cursor allCursor = getMockContentResolver().query(ApkProvider.getContentUri(), allFields, null, null, null); - assertResultCount(1, allCursor); - allCursor.close(); - - Cursor updatedCursor = getMockContentResolver().query(apkUri, allFields, null, null, null); - assertResultCount(1, updatedCursor); - - updatedCursor.moveToFirst(); - Apk updatedApk = new Apk(updatedCursor); - updatedCursor.close(); - - assertEquals("com.example", updatedApk.packageName); - assertEquals(10, updatedApk.versionCode); - - assertNotNull(updatedApk.features); - assertNotNull(updatedApk.added); - assertNotNull(updatedApk.hashType); - - assertEquals("one,two,three", updatedApk.features.toString()); - assertEquals(new Date(dateTimestamp).getYear(), updatedApk.added.getYear()); - assertEquals(new Date(dateTimestamp).getMonth(), updatedApk.added.getMonth()); - assertEquals(new Date(dateTimestamp).getDay(), updatedApk.added.getDay()); - assertEquals("i'm a hash type", updatedApk.hashType); - } - - public void testFind() { - - // Insert some random apks either side of the "com.example", so that - // the Helper.find() method doesn't stumble upon the app we are interested - // in by shear dumb luck... - for (int i = 0; i < 10; i++) { - TestUtils.insertApk(this, "org.fdroid.apk." + i, i); - } - - ContentValues values = new ContentValues(); - values.put(ApkProvider.DataColumns.VERSION_NAME, "v1.1"); - values.put(ApkProvider.DataColumns.HASH, "xxxxyyyy"); - values.put(ApkProvider.DataColumns.HASH_TYPE, "a hash type"); - TestUtils.insertApk(this, "com.example", 11, values); - - // ...and a few more for good measure... - for (int i = 15; i < 20; i++) { - TestUtils.insertApk(this, "com.other.thing." + i, i); - } - - Apk apk = ApkProvider.Helper.find(getMockContext(), "com.example", 11); - - assertNotNull(apk); - - // The find() method populates ALL fields if you don't specify any, - // so we expect to find each of the ones we inserted above... - assertEquals("com.example", apk.packageName); - assertEquals(11, apk.versionCode); - assertEquals("v1.1", apk.versionName); - assertEquals("xxxxyyyy", apk.hash); - assertEquals("a hash type", apk.hashType); - - String[] projection = { - ApkProvider.DataColumns.PACKAGE_NAME, - ApkProvider.DataColumns.HASH, - }; - - Apk apkLessFields = ApkProvider.Helper.find(getMockContext(), "com.example", 11, projection); - - assertNotNull(apkLessFields); - - assertEquals("com.example", apkLessFields.packageName); - assertEquals("xxxxyyyy", apkLessFields.hash); - - // Didn't ask for these fields, so should be their default values... - assertNull(apkLessFields.hashType); - assertNull(apkLessFields.versionName); - assertEquals(0, apkLessFields.versionCode); - - Apk notFound = ApkProvider.Helper.find(getMockContext(), "com.doesnt.exist", 1000); - assertNull(notFound); - } - -} diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/ApkProviderTest.java b/app/src/androidTest/java/org/fdroid/fdroid/data/ApkProviderTest.java deleted file mode 100644 index 4dced204f..000000000 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/ApkProviderTest.java +++ /dev/null @@ -1,334 +0,0 @@ -package org.fdroid.fdroid.data; - -import android.content.ContentValues; -import android.database.Cursor; -import android.net.Uri; - -import org.fdroid.fdroid.TestUtils; -import org.fdroid.fdroid.mock.MockApk; -import org.fdroid.fdroid.mock.MockApp; -import org.fdroid.fdroid.mock.MockRepo; - -import java.util.ArrayList; -import java.util.List; - -@SuppressWarnings("PMD") // TODO port this to JUnit 4 semantics -public class ApkProviderTest extends BaseApkProviderTest { - - /** - * I want to test the protected {@link org.fdroid.fdroid.data.ApkProvider#getContentUri(java.util.List)} - * method, but don't want to make it public. This exposes it. - */ - private static class PublicApkProvider extends ApkProvider { - - public static final int MAX_APKS_TO_QUERY = ApkProvider.MAX_APKS_TO_QUERY; - - public static Uri getContentUri(List apks) { - return ApkProvider.getContentUri(apks); - } - } - - public void testUris() { - assertInvalidUri(ApkProvider.getAuthority()); - assertInvalidUri(RepoProvider.getContentUri()); - - List apks = new ArrayList<>(3); - for (int i = 0; i < 10; i++) { - apks.add(new MockApk("com.example." + i, i)); - } - - assertValidUri(ApkProvider.getContentUri()); - assertValidUri(ApkProvider.getAppUri("org.fdroid.fdroid")); - assertValidUri(ApkProvider.getContentUri(new MockApk("org.fdroid.fdroid", 100))); - assertValidUri(ApkProvider.getContentUri()); - assertValidUri(PublicApkProvider.getContentUri(apks)); - assertValidUri(ApkProvider.getContentUri("org.fdroid.fdroid", 100)); - assertValidUri(ApkProvider.getRepoUri(1000)); - - List manyApks = new ArrayList<>(PublicApkProvider.MAX_APKS_TO_QUERY - 5); - for (int i = 0; i < PublicApkProvider.MAX_APKS_TO_QUERY - 1; i++) { - manyApks.add(new MockApk("com.example." + i, i)); - } - assertValidUri(PublicApkProvider.getContentUri(manyApks)); - - manyApks.add(new MockApk("org.fdroid.fdroid.1", 1)); - manyApks.add(new MockApk("org.fdroid.fdroid.2", 2)); - try { - // Technically, it is a valid URI, because it doesn't - // throw an UnsupportedOperationException. However it - // is still not okay (we run out of bindable parameters - // in the sqlite query. - assertValidUri(PublicApkProvider.getContentUri(manyApks)); - fail(); - } catch (IllegalArgumentException e) { - // This is the expected error behaviour. - } catch (Exception e) { - fail(); - } - } - - public void testAppApks() { - for (int i = 1; i <= 10; i++) { - TestUtils.insertApk(this, "org.fdroid.fdroid", i); - TestUtils.insertApk(this, "com.example", i); - } - - assertTotalApkCount(20); - - Cursor fdroidApks = getMockContentResolver().query( - ApkProvider.getAppUri("org.fdroid.fdroid"), - getMinimalProjection(), - null, null, null); - assertResultCount(10, fdroidApks); - assertBelongsToApp(fdroidApks, "org.fdroid.fdroid"); - fdroidApks.close(); - - Cursor exampleApks = getMockContentResolver().query( - ApkProvider.getAppUri("com.example"), - getMinimalProjection(), - null, null, null); - assertResultCount(10, exampleApks); - assertBelongsToApp(exampleApks, "com.example"); - exampleApks.close(); - - ApkProvider.Helper.deleteApksByApp(getMockContext(), new MockApp("com.example")); - - Cursor all = queryAllApks(); - assertResultCount(10, all); - assertBelongsToApp(all, "org.fdroid.fdroid"); - all.close(); - } - - public void testInvalidUpdateUris() { - Apk apk = new MockApk("org.fdroid.fdroid", 10); - - List apks = new ArrayList<>(); - apks.add(apk); - - assertCantUpdate(ApkProvider.getContentUri()); - assertCantUpdate(ApkProvider.getAppUri("org.fdroid.fdroid")); - assertCantUpdate(ApkProvider.getRepoUri(1)); - assertCantUpdate(PublicApkProvider.getContentUri(apks)); - assertCantUpdate(Uri.withAppendedPath(ApkProvider.getContentUri(), "some-random-path")); - - // The only valid ones are: - // ApkProvider.getContentUri(apk) - // ApkProvider.getContentUri(id, version) - // which are tested elsewhere. - } - - public void testDeleteArbitraryApks() { - Apk one = insertApkForRepo("com.example.one", 1, 10); - Apk two = insertApkForRepo("com.example.two", 1, 10); - Apk three = insertApkForRepo("com.example.three", 1, 10); - Apk four = insertApkForRepo("com.example.four", 1, 10); - Apk five = insertApkForRepo("com.example.five", 1, 10); - - assertTotalApkCount(5); - - assertEquals("com.example.one", one.packageName); - assertEquals("com.example.two", two.packageName); - assertEquals("com.example.five", five.packageName); - - String[] expectedIds = { - "com.example.one", - "com.example.two", - "com.example.three", - "com.example.four", - "com.example.five", - }; - - List all = ApkProvider.Helper.findByRepo(getSwappableContext(), new MockRepo(10), ApkProvider.DataColumns.ALL); - List actualIds = new ArrayList<>(); - for (Apk apk : all) { - actualIds.add(apk.packageName); - } - - TestUtils.assertContainsOnly(actualIds, expectedIds); - - List toDelete = new ArrayList<>(3); - toDelete.add(two); - toDelete.add(three); - toDelete.add(four); - ApkProvider.Helper.deleteApks(getSwappableContext(), toDelete); - - assertTotalApkCount(2); - - List allRemaining = ApkProvider.Helper.findByRepo(getSwappableContext(), new MockRepo(10), ApkProvider.DataColumns.ALL); - List actualRemainingIds = new ArrayList<>(); - for (Apk apk : allRemaining) { - actualRemainingIds.add(apk.packageName); - } - - String[] expectedRemainingIds = { - "com.example.one", - "com.example.five", - }; - - TestUtils.assertContainsOnly(actualRemainingIds, expectedRemainingIds); - } - - public void testInvalidDeleteUris() { - Apk apk = new MockApk("org.fdroid.fdroid", 10); - - assertCantDelete(ApkProvider.getContentUri()); - assertCantDelete(ApkProvider.getContentUri("org.fdroid.fdroid", 10)); - assertCantDelete(ApkProvider.getContentUri(apk)); - assertCantDelete(Uri.withAppendedPath(ApkProvider.getContentUri(), "some-random-path")); - } - - private static final long REPO_KEEP = 1; - private static final long REPO_DELETE = 2; - - public void testRepoApks() { - - // Insert apks into two repos, one of which we will later purge the - // the apks from. - for (int i = 1; i <= 5; i++) { - insertApkForRepo("org.fdroid.fdroid", i, REPO_KEEP); - insertApkForRepo("com.example." + i, 1, REPO_DELETE); - } - for (int i = 6; i <= 10; i++) { - insertApkForRepo("org.fdroid.fdroid", i, REPO_DELETE); - insertApkForRepo("com.example." + i, 1, REPO_KEEP); - } - - assertTotalApkCount(20); - - Cursor cursor = getMockContentResolver().query( - ApkProvider.getRepoUri(REPO_DELETE), getMinimalProjection(), null, null, null); - assertResultCount(10, cursor); - assertBelongsToRepo(cursor, REPO_DELETE); - cursor.close(); - - int count = ApkProvider.Helper.deleteApksByRepo(getMockContext(), new MockRepo(REPO_DELETE)); - assertEquals(10, count); - - assertTotalApkCount(10); - cursor = getMockContentResolver().query( - ApkProvider.getRepoUri(REPO_DELETE), getMinimalProjection(), null, null, null); - assertResultCount(0, cursor); - cursor.close(); - - // The only remaining apks should be those from REPO_KEEP. - assertBelongsToRepo(queryAllApks(), REPO_KEEP); - } - - public void testQuery() { - Cursor cursor = queryAllApks(); - assertNotNull(cursor); - cursor.close(); - } - - public void testInsert() { - - // Start with an empty database... - Cursor cursor = queryAllApks(); - assertNotNull(cursor); - assertEquals(0, cursor.getCount()); - cursor.close(); - - Apk apk = new MockApk("org.fdroid.fdroid", 13); - - // Insert a new record... - Uri newUri = TestUtils.insertApk(this, apk.packageName, apk.versionCode); - assertEquals(ApkProvider.getContentUri(apk).toString(), newUri.toString()); - cursor = queryAllApks(); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - - // We intentionally throw an IllegalArgumentException if you haven't - // yet called cursor.move*()... - try { - new Apk(cursor); - fail(); - } catch (IllegalArgumentException e) { - // Success! - } catch (Exception e) { - fail(); - } - - // And now we should be able to recover these values from the apk - // value object (because the queryAllApks() helper asks for VERSION_CODE and - // PACKAGE_NAME. - cursor.moveToFirst(); - Apk toCheck = new Apk(cursor); - cursor.close(); - assertEquals("org.fdroid.fdroid", toCheck.packageName); - assertEquals(13, toCheck.versionCode); - } - - public void testCount() { - String[] projectionFields = getMinimalProjection(); - String[] projectionCount = new String[] {ApkProvider.DataColumns._COUNT}; - - for (int i = 0; i < 13; i++) { - TestUtils.insertApk(this, "com.example", i); - } - - Uri all = ApkProvider.getContentUri(); - Cursor allWithFields = getMockContentResolver().query(all, projectionFields, null, null, null); - Cursor allWithCount = getMockContentResolver().query(all, projectionCount, null, null, null); - - assertResultCount(13, allWithFields); - allWithFields.close(); - assertResultCount(1, allWithCount); - - allWithCount.moveToFirst(); - int countColumn = allWithCount.getColumnIndex(ApkProvider.DataColumns._COUNT); - assertEquals(13, allWithCount.getInt(countColumn)); - allWithCount.close(); - } - - public void testInsertWithExtraFields() { - - assertResultCount(0, queryAllApks()); - - String[] repoFields = new String[] { - RepoProvider.DataColumns.DESCRIPTION, - RepoProvider.DataColumns.ADDRESS, - RepoProvider.DataColumns.FINGERPRINT, - RepoProvider.DataColumns.NAME, - RepoProvider.DataColumns.SIGNING_CERT, - }; - - for (String field : repoFields) { - ContentValues invalidRepo = new ContentValues(); - invalidRepo.put(field, "Test data"); - try { - TestUtils.insertApk(this, "org.fdroid.fdroid", 10, invalidRepo); - fail(); - } catch (IllegalArgumentException e) { - } catch (Exception e) { - fail(); - } - assertResultCount(0, queryAllApks()); - } - - ContentValues values = new ContentValues(); - values.put(ApkProvider.DataColumns.REPO_ID, 10); - values.put(ApkProvider.DataColumns.REPO_ADDRESS, "http://example.com"); - values.put(ApkProvider.DataColumns.REPO_VERSION, 3); - values.put(ApkProvider.DataColumns.FEATURES, "Some features"); - Uri uri = TestUtils.insertApk(this, "com.example.com", 1, values); - - assertResultCount(1, queryAllApks()); - - String[] projections = ApkProvider.DataColumns.ALL; - Cursor cursor = getMockContentResolver().query(uri, projections, null, null, null); - cursor.moveToFirst(); - Apk apk = new Apk(cursor); - cursor.close(); - - // These should have quietly been dropped when we tried to save them, - // because the provider only knows how to query them (not update them). - assertEquals(null, apk.repoAddress); - assertEquals(0, apk.repoVersion); - - // But this should have saved correctly... - assertEquals("Some features", apk.features.toString()); - assertEquals("com.example.com", apk.packageName); - assertEquals(1, apk.versionCode); - assertEquals(10, apk.repo); - } -} diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/BaseApkProviderTest.java b/app/src/androidTest/java/org/fdroid/fdroid/data/BaseApkProviderTest.java deleted file mode 100644 index 41d6b0a6c..000000000 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/BaseApkProviderTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.fdroid.fdroid.data; - -import android.content.ContentValues; -import android.database.Cursor; -import android.net.Uri; - -import org.fdroid.fdroid.TestUtils; - -import java.util.List; - -/** - * Provides helper methods that can be used by both Helper and plain old - * Provider tests. Allows the test classes to contain only test methods, - * hopefully making them easier to understand. - * - * This should not contain any test methods, or else they get executed - * once for every concrete subclass. - */ -abstract class BaseApkProviderTest extends FDroidProviderTestOld { - - BaseApkProviderTest() { - super(ApkProvider.class, ApkProvider.getAuthority()); - } - - @Override - protected String[] getMinimalProjection() { - return new String[] { - ApkProvider.DataColumns.PACKAGE_NAME, - ApkProvider.DataColumns.VERSION_CODE, - ApkProvider.DataColumns.NAME, - ApkProvider.DataColumns.REPO_ID, - }; - } - - protected final Cursor queryAllApks() { - return getMockContentResolver().query(ApkProvider.getContentUri(), getMinimalProjection(), null, null, null); - } - - protected void assertContains(List apks, Apk apk) { - boolean found = false; - for (Apk a : apks) { - if (a.versionCode == apk.versionCode && a.packageName.equals(apk.packageName)) { - found = true; - break; - } - } - if (!found) { - fail("Apk [" + apk + "] not found in " + TestUtils.listToString(apks)); - } - } - - protected void assertBelongsToApp(Cursor apks, String appId) { - assertBelongsToApp(ApkProvider.Helper.cursorToList(apks), appId); - } - - protected void assertBelongsToApp(List apks, String appId) { - for (Apk apk : apks) { - assertEquals(appId, apk.packageName); - } - } - - protected void assertTotalApkCount(int expected) { - assertResultCount(expected, queryAllApks()); - } - - protected void assertBelongsToRepo(Cursor apkCursor, long repoId) { - for (Apk apk : ApkProvider.Helper.cursorToList(apkCursor)) { - assertEquals(repoId, apk.repo); - } - } - - protected Apk insertApkForRepo(String id, int versionCode, long repoId) { - ContentValues additionalValues = new ContentValues(); - additionalValues.put(ApkProvider.DataColumns.REPO_ID, repoId); - Uri uri = TestUtils.insertApk(this, id, versionCode, additionalValues); - return ApkProvider.Helper.get(getSwappableContext(), uri); - } -} diff --git a/app/src/androidTest/java/org/fdroid/fdroid/mock/MockRepoOld.java b/app/src/androidTest/java/org/fdroid/fdroid/mock/MockRepoOld.java new file mode 100644 index 000000000..43fec57cf --- /dev/null +++ b/app/src/androidTest/java/org/fdroid/fdroid/mock/MockRepoOld.java @@ -0,0 +1,11 @@ +package org.fdroid.fdroid.mock; + +import org.fdroid.fdroid.data.Repo; + +public class MockRepoOld extends Repo { + + public MockRepoOld(long repoId) { + id = repoId; + } + +} diff --git a/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java b/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java index e1395f92d..70b43b739 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java @@ -26,7 +26,7 @@ public class ApkProvider extends FDroidProvider { * we may want to add additional constraints, so we give our self some * room by saying only 450 apks can be queried at once. */ - protected static final int MAX_APKS_TO_QUERY = 450; + static final int MAX_APKS_TO_QUERY = 450; public static final class Helper { @@ -319,7 +319,7 @@ public class ApkProvider extends FDroidProvider { * this directly, think about using * {@link org.fdroid.fdroid.data.ApkProvider.Helper#knownApks(android.content.Context, java.util.List, String[])} */ - protected static Uri getContentUri(List apks) { + static Uri getContentUri(List apks) { return getContentUri().buildUpon() .appendPath(PATH_APKS) .appendPath(buildApkString(apks)) diff --git a/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java new file mode 100644 index 000000000..02a0e379c --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java @@ -0,0 +1,535 @@ +package org.fdroid.fdroid.data; + +import android.app.Application; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +import org.fdroid.fdroid.BuildConfig; +import org.fdroid.fdroid.Utils; +import org.fdroid.fdroid.mock.MockApk; +import org.fdroid.fdroid.mock.MockApp; +import org.fdroid.fdroid.mock.MockRepo; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.annotation.Config; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import static org.fdroid.fdroid.data.ProviderTestUtils.assertCantDelete; +import static org.fdroid.fdroid.data.ProviderTestUtils.assertContainsOnly; +import static org.fdroid.fdroid.data.ProviderTestUtils.assertResultCount; +import static org.fdroid.fdroid.data.ProviderTestUtils.insertApk; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +@Config(constants = BuildConfig.class, application = Application.class) +@RunWith(RobolectricGradleTestRunner.class) +public class ApkProviderTest extends FDroidProviderTest { + + private static final String[] PROJ = ApkProvider.DataColumns.ALL; + + @Test + public void testAppApks() { + for (int i = 1; i <= 10; i++) { + insertApk(contentResolver, "org.fdroid.fdroid", i); + insertApk(contentResolver, "com.example", i); + } + + assertTotalApkCount(20); + + Cursor fdroidApks = contentResolver.query( + ApkProvider.getAppUri("org.fdroid.fdroid"), + PROJ, + null, null, null); + assertResultCount(10, fdroidApks); + assertBelongsToApp(fdroidApks, "org.fdroid.fdroid"); + fdroidApks.close(); + + Cursor exampleApks = contentResolver.query( + ApkProvider.getAppUri("com.example"), + PROJ, + null, null, null); + assertResultCount(10, exampleApks); + assertBelongsToApp(exampleApks, "com.example"); + exampleApks.close(); + + ApkProvider.Helper.deleteApksByApp(context, new MockApp("com.example")); + + Cursor all = queryAllApks(); + assertResultCount(10, all); + assertBelongsToApp(all, "org.fdroid.fdroid"); + all.close(); + } + + @Test + public void testDeleteArbitraryApks() { + Apk one = insertApkForRepo("com.example.one", 1, 10); + Apk two = insertApkForRepo("com.example.two", 1, 10); + Apk three = insertApkForRepo("com.example.three", 1, 10); + Apk four = insertApkForRepo("com.example.four", 1, 10); + Apk five = insertApkForRepo("com.example.five", 1, 10); + + assertTotalApkCount(5); + + assertEquals("com.example.one", one.packageName); + assertEquals("com.example.two", two.packageName); + assertEquals("com.example.five", five.packageName); + + String[] expectedIds = { + "com.example.one", + "com.example.two", + "com.example.three", + "com.example.four", + "com.example.five", + }; + + List all = ApkProvider.Helper.findByRepo(context, new MockRepo(10), ApkProvider.DataColumns.ALL); + List actualIds = new ArrayList<>(); + for (Apk apk : all) { + actualIds.add(apk.packageName); + } + + assertContainsOnly(actualIds, expectedIds); + + List toDelete = new ArrayList<>(3); + toDelete.add(two); + toDelete.add(three); + toDelete.add(four); + ApkProvider.Helper.deleteApks(context, toDelete); + + assertTotalApkCount(2); + + List allRemaining = ApkProvider.Helper.findByRepo(context, new MockRepo(10), ApkProvider.DataColumns.ALL); + List actualRemainingIds = new ArrayList<>(); + for (Apk apk : allRemaining) { + actualRemainingIds.add(apk.packageName); + } + + String[] expectedRemainingIds = { + "com.example.one", + "com.example.five", + }; + + assertContainsOnly(actualRemainingIds, expectedRemainingIds); + } + + @Test + public void testInvalidDeleteUris() { + Apk apk = new MockApk("org.fdroid.fdroid", 10); + + assertCantDelete(contentResolver, ApkProvider.getContentUri()); + assertCantDelete(contentResolver, ApkProvider.getContentUri("org.fdroid.fdroid", 10)); + assertCantDelete(contentResolver, ApkProvider.getContentUri(apk)); + assertCantDelete(contentResolver, Uri.withAppendedPath(ApkProvider.getContentUri(), "some-random-path")); + } + + private static final long REPO_KEEP = 1; + private static final long REPO_DELETE = 2; + + @Test + public void testRepoApks() { + + // Insert apks into two repos, one of which we will later purge the + // the apks from. + for (int i = 1; i <= 5; i++) { + insertApkForRepo("org.fdroid.fdroid", i, REPO_KEEP); + insertApkForRepo("com.example." + i, 1, REPO_DELETE); + } + for (int i = 6; i <= 10; i++) { + insertApkForRepo("org.fdroid.fdroid", i, REPO_DELETE); + insertApkForRepo("com.example." + i, 1, REPO_KEEP); + } + + assertTotalApkCount(20); + + Cursor cursor = contentResolver.query( + ApkProvider.getRepoUri(REPO_DELETE), PROJ, null, null, null); + assertResultCount(10, cursor); + assertBelongsToRepo(cursor, REPO_DELETE); + cursor.close(); + + int count = ApkProvider.Helper.deleteApksByRepo(context, new MockRepo(REPO_DELETE)); + assertEquals(10, count); + + assertTotalApkCount(10); + cursor = contentResolver.query( + ApkProvider.getRepoUri(REPO_DELETE), PROJ, null, null, null); + assertResultCount(0, cursor); + cursor.close(); + + // The only remaining apks should be those from REPO_KEEP. + assertBelongsToRepo(queryAllApks(), REPO_KEEP); + } + + @Test + public void testQuery() { + Cursor cursor = queryAllApks(); + assertNotNull(cursor); + cursor.close(); + } + + @Test + public void testInsert() { + + // Start with an empty database... + Cursor cursor = queryAllApks(); + assertNotNull(cursor); + assertEquals(0, cursor.getCount()); + cursor.close(); + + Apk apk = new MockApk("org.fdroid.fdroid", 13); + + // Insert a new record... + Uri newUri = insertApk(contentResolver, apk.packageName, apk.versionCode); + assertEquals(ApkProvider.getContentUri(apk).toString(), newUri.toString()); + cursor = queryAllApks(); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + + // We intentionally throw an IllegalArgumentException if you haven't + // yet called cursor.move*()... + try { + new Apk(cursor); + fail(); + } catch (IllegalArgumentException e) { + // Success! + } catch (Exception e) { + fail(); + } + + // And now we should be able to recover these values from the apk + // value object (because the queryAllApks() helper asks for VERSION_CODE and + // PACKAGE_NAME. + cursor.moveToFirst(); + Apk toCheck = new Apk(cursor); + cursor.close(); + assertEquals("org.fdroid.fdroid", toCheck.packageName); + assertEquals(13, toCheck.versionCode); + } + + @Test + public void testCount() { + String[] projectionCount = new String[] {ApkProvider.DataColumns._COUNT}; + + for (int i = 0; i < 13; i++) { + insertApk(contentResolver, "com.example", i); + } + + Uri all = ApkProvider.getContentUri(); + Cursor allWithFields = contentResolver.query(all, PROJ, null, null, null); + Cursor allWithCount = contentResolver.query(all, projectionCount, null, null, null); + + assertResultCount(13, allWithFields); + allWithFields.close(); + assertResultCount(1, allWithCount); + + allWithCount.moveToFirst(); + int countColumn = allWithCount.getColumnIndex(ApkProvider.DataColumns._COUNT); + assertEquals(13, allWithCount.getInt(countColumn)); + allWithCount.close(); + } + + @Test + public void testInsertWithExtraFields() { + + assertResultCount(0, queryAllApks()); + + String[] repoFields = new String[] { + RepoProvider.DataColumns.DESCRIPTION, + RepoProvider.DataColumns.ADDRESS, + RepoProvider.DataColumns.FINGERPRINT, + RepoProvider.DataColumns.NAME, + RepoProvider.DataColumns.SIGNING_CERT, + }; + + for (String field : repoFields) { + ContentValues invalidRepo = new ContentValues(); + invalidRepo.put(field, "Test data"); + try { + insertApk(contentResolver, "org.fdroid.fdroid", 10, invalidRepo); + fail(); + } catch (IllegalArgumentException e) { + } catch (Exception e) { + fail(); + } + assertResultCount(0, queryAllApks()); + } + + ContentValues values = new ContentValues(); + values.put(ApkProvider.DataColumns.REPO_ID, 10); + values.put(ApkProvider.DataColumns.REPO_ADDRESS, "http://example.com"); + values.put(ApkProvider.DataColumns.REPO_VERSION, 3); + values.put(ApkProvider.DataColumns.FEATURES, "Some features"); + Uri uri = insertApk(contentResolver, "com.example.com", 1, values); + + assertResultCount(1, queryAllApks()); + + String[] projections = ApkProvider.DataColumns.ALL; + Cursor cursor = contentResolver.query(uri, projections, null, null, null); + cursor.moveToFirst(); + Apk apk = new Apk(cursor); + cursor.close(); + + // These should have quietly been dropped when we tried to save them, + // because the provider only knows how to query them (not update them). + assertEquals(null, apk.repoAddress); + assertEquals(0, apk.repoVersion); + + // But this should have saved correctly... + assertEquals("Some features", apk.features.toString()); + assertEquals("com.example.com", apk.packageName); + assertEquals(1, apk.versionCode); + assertEquals(10, apk.repo); + } + + @Test + public void testKnownApks() { + + for (int i = 0; i < 7; i++) { + insertApk(contentResolver, "org.fdroid.fdroid", i); + } + + for (int i = 0; i < 9; i++) { + insertApk(contentResolver, "org.example", i); + } + + for (int i = 0; i < 3; i++) { + insertApk(contentResolver, "com.example", i); + } + + insertApk(contentResolver, "com.apk.thingo", 1); + + Apk[] known = { + new MockApk("org.fdroid.fdroid", 1), + new MockApk("org.fdroid.fdroid", 3), + new MockApk("org.fdroid.fdroid", 5), + + new MockApk("com.example", 1), + new MockApk("com.example", 2), + }; + + Apk[] unknown = { + new MockApk("org.fdroid.fdroid", 7), + new MockApk("org.fdroid.fdroid", 9), + new MockApk("org.fdroid.fdroid", 11), + new MockApk("org.fdroid.fdroid", 13), + + new MockApk("com.example", 3), + new MockApk("com.example", 4), + new MockApk("com.example", 5), + + new MockApk("info.example", 1), + new MockApk("info.example", 2), + }; + + List apksToCheck = new ArrayList<>(known.length + unknown.length); + Collections.addAll(apksToCheck, known); + Collections.addAll(apksToCheck, unknown); + + String[] projection = { + ApkProvider.DataColumns.PACKAGE_NAME, + ApkProvider.DataColumns.VERSION_CODE, + }; + + List knownApks = ApkProvider.Helper.knownApks(context, apksToCheck, projection); + + assertResultCount(known.length, knownApks); + + for (Apk knownApk : knownApks) { + assertContains(knownApks, knownApk); + } + } + + @Test + public void testFindByApp() { + + for (int i = 0; i < 7; i++) { + insertApk(contentResolver, "org.fdroid.fdroid", i); + } + + for (int i = 0; i < 9; i++) { + insertApk(contentResolver, "org.example", i); + } + + for (int i = 0; i < 3; i++) { + insertApk(contentResolver, "com.example", i); + } + + insertApk(contentResolver, "com.apk.thingo", 1); + + assertTotalApkCount(7 + 9 + 3 + 1); + + List fdroidApks = ApkProvider.Helper.findByPackageName(context, "org.fdroid.fdroid"); + assertResultCount(7, fdroidApks); + assertBelongsToApp(fdroidApks, "org.fdroid.fdroid"); + + List exampleApks = ApkProvider.Helper.findByPackageName(context, "org.example"); + assertResultCount(9, exampleApks); + assertBelongsToApp(exampleApks, "org.example"); + + List exampleApks2 = ApkProvider.Helper.findByPackageName(context, "com.example"); + assertResultCount(3, exampleApks2); + assertBelongsToApp(exampleApks2, "com.example"); + + List thingoApks = ApkProvider.Helper.findByPackageName(context, "com.apk.thingo"); + assertResultCount(1, thingoApks); + assertBelongsToApp(thingoApks, "com.apk.thingo"); + } + + @Test + public void testUpdate() { + + Uri apkUri = insertApk(contentResolver, "com.example", 10); + + String[] allFields = ApkProvider.DataColumns.ALL; + Cursor cursor = contentResolver.query(apkUri, allFields, null, null, null); + assertResultCount(1, cursor); + + cursor.moveToFirst(); + Apk apk = new Apk(cursor); + cursor.close(); + + assertEquals("com.example", apk.packageName); + assertEquals(10, apk.versionCode); + + assertNull(apk.features); + assertNull(apk.added); + assertNull(apk.hashType); + + apk.features = Utils.CommaSeparatedList.make("one,two,three"); + long dateTimestamp = System.currentTimeMillis(); + apk.added = new Date(dateTimestamp); + apk.hashType = "i'm a hash type"; + + ApkProvider.Helper.update(context, apk); + + // Should not have inserted anything else, just updated the already existing apk. + Cursor allCursor = contentResolver.query(ApkProvider.getContentUri(), allFields, null, null, null); + assertResultCount(1, allCursor); + allCursor.close(); + + Cursor updatedCursor = contentResolver.query(apkUri, allFields, null, null, null); + assertResultCount(1, updatedCursor); + + updatedCursor.moveToFirst(); + Apk updatedApk = new Apk(updatedCursor); + updatedCursor.close(); + + assertEquals("com.example", updatedApk.packageName); + assertEquals(10, updatedApk.versionCode); + + assertNotNull(updatedApk.features); + assertNotNull(updatedApk.added); + assertNotNull(updatedApk.hashType); + + assertEquals("one,two,three", updatedApk.features.toString()); + assertEquals(new Date(dateTimestamp).getYear(), updatedApk.added.getYear()); + assertEquals(new Date(dateTimestamp).getMonth(), updatedApk.added.getMonth()); + assertEquals(new Date(dateTimestamp).getDay(), updatedApk.added.getDay()); + assertEquals("i'm a hash type", updatedApk.hashType); + } + + @Test + public void testFind() { + // Insert some random apks either side of the "com.example", so that + // the Helper.find() method doesn't stumble upon the app we are interested + // in by shear dumb luck... + for (int i = 0; i < 10; i++) { + insertApk(contentResolver, "org.fdroid.apk." + i, i); + } + + ContentValues values = new ContentValues(); + values.put(ApkProvider.DataColumns.VERSION_NAME, "v1.1"); + values.put(ApkProvider.DataColumns.HASH, "xxxxyyyy"); + values.put(ApkProvider.DataColumns.HASH_TYPE, "a hash type"); + insertApk(contentResolver, "com.example", 11, values); + + // ...and a few more for good measure... + for (int i = 15; i < 20; i++) { + insertApk(contentResolver, "com.other.thing." + i, i); + } + + Apk apk = ApkProvider.Helper.find(context, "com.example", 11); + + assertNotNull(apk); + + // The find() method populates ALL fields if you don't specify any, + // so we expect to find each of the ones we inserted above... + assertEquals("com.example", apk.packageName); + assertEquals(11, apk.versionCode); + assertEquals("v1.1", apk.versionName); + assertEquals("xxxxyyyy", apk.hash); + assertEquals("a hash type", apk.hashType); + + String[] projection = { + ApkProvider.DataColumns.PACKAGE_NAME, + ApkProvider.DataColumns.HASH, + }; + + Apk apkLessFields = ApkProvider.Helper.find(context, "com.example", 11, projection); + + assertNotNull(apkLessFields); + + assertEquals("com.example", apkLessFields.packageName); + assertEquals("xxxxyyyy", apkLessFields.hash); + + // Didn't ask for these fields, so should be their default values... + assertNull(apkLessFields.hashType); + assertNull(apkLessFields.versionName); + assertEquals(0, apkLessFields.versionCode); + + Apk notFound = ApkProvider.Helper.find(context, "com.doesnt.exist", 1000); + assertNull(notFound); + } + + protected final Cursor queryAllApks() { + return contentResolver.query(ApkProvider.getContentUri(), PROJ, null, null, null); + } + + protected void assertContains(List apks, Apk apk) { + boolean found = false; + for (Apk a : apks) { + if (a.versionCode == apk.versionCode && a.packageName.equals(apk.packageName)) { + found = true; + break; + } + } + if (!found) { + fail("Apk [" + apk + "] not found in " + ProviderTestUtils.listToString(apks)); + } + } + + protected void assertBelongsToApp(Cursor apks, String appId) { + assertBelongsToApp(ApkProvider.Helper.cursorToList(apks), appId); + } + + protected void assertBelongsToApp(List apks, String appId) { + for (Apk apk : apks) { + assertEquals(appId, apk.packageName); + } + } + + protected void assertTotalApkCount(int expected) { + assertResultCount(expected, queryAllApks()); + } + + protected void assertBelongsToRepo(Cursor apkCursor, long repoId) { + for (Apk apk : ApkProvider.Helper.cursorToList(apkCursor)) { + assertEquals(repoId, apk.repo); + } + } + + protected Apk insertApkForRepo(String id, int versionCode, long repoId) { + ContentValues additionalValues = new ContentValues(); + additionalValues.put(ApkProvider.DataColumns.REPO_ID, repoId); + Uri uri = insertApk(contentResolver, id, versionCode, additionalValues); + return ApkProvider.Helper.get(context, uri); + } +} diff --git a/app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java b/app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java index 97a5481b2..ce277c31a 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java @@ -168,4 +168,55 @@ public class ProviderTestUtils { cursor.close(); } + public static void insertApp(ShadowContentResolver resolver, String appId, String name) { + insertApp(resolver, appId, name, new ContentValues()); + } + + public static void insertApp(ShadowContentResolver resolver, String id, String name, ContentValues additionalValues) { + + ContentValues values = new ContentValues(); + values.put(AppProvider.DataColumns.PACKAGE_NAME, 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); + + values.putAll(additionalValues); + + Uri uri = AppProvider.getContentUri(); + + resolver.insert(uri, values); + } + + public static Uri insertApk(ShadowContentResolver resolver, String id, int versionCode) { + return insertApk(resolver, id, versionCode, new ContentValues()); + } + + public static Uri insertApk(ShadowContentResolver resolver, String id, int versionCode, ContentValues additionalValues) { + + ContentValues values = new ContentValues(); + + values.put(ApkProvider.DataColumns.PACKAGE_NAME, id); + values.put(ApkProvider.DataColumns.VERSION_CODE, versionCode); + + // Required fields (NOT NULL in the database). + values.put(ApkProvider.DataColumns.REPO_ID, 1); + values.put(ApkProvider.DataColumns.VERSION_NAME, "The good one"); + values.put(ApkProvider.DataColumns.HASH, "11111111aaaaaaaa"); + values.put(ApkProvider.DataColumns.NAME, "Test Apk"); + values.put(ApkProvider.DataColumns.SIZE, 10000); + values.put(ApkProvider.DataColumns.IS_COMPATIBLE, 1); + + values.putAll(additionalValues); + + Uri uri = ApkProvider.getContentUri(); + + return resolver.insert(uri, values); + } + } diff --git a/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java b/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java index 57e68624a..9fa7d9d9a 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java @@ -1,6 +1,7 @@ package org.fdroid.fdroid.data; import org.fdroid.fdroid.BuildConfig; +import org.fdroid.fdroid.mock.MockApk; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -16,6 +17,7 @@ import java.util.List; import static org.fdroid.fdroid.data.ProviderTestUtils.assertInvalidUri; import static org.fdroid.fdroid.data.ProviderTestUtils.assertValidUri; +import static org.junit.Assert.fail; @Config(constants = BuildConfig.class) @RunWith(RobolectricGradleTestRunner.class) @@ -99,4 +101,50 @@ public class ProviderUriTests { assertValidUri(resolver, AppProvider.getContentUri("org.fdroid.fdroid"), "content://org.fdroid.fdroid.data.AppProvider/org.fdroid.fdroid", projection); } + @Test + public void invalidApkProviderUris() { + ShadowContentResolver.registerProvider(ApkProvider.getAuthority(), new ApkProvider()); + assertInvalidUri(resolver, ApkProvider.getAuthority()); + assertInvalidUri(resolver, "blah"); + } + + @Test + public void validApkProviderUris() { + ShadowContentResolver.registerProvider(ApkProvider.getAuthority(), new ApkProvider()); + String[] projection = new String[] { ApkProvider.DataColumns._ID }; + + List apks = new ArrayList<>(10); + for (int i = 0; i < 10; i++) { + apks.add(new MockApk("com.example." + i, i)); + } + + assertValidUri(resolver, ApkProvider.getContentUri(), "content://org.fdroid.fdroid.data.ApkProvider", projection); + assertValidUri(resolver, ApkProvider.getAppUri("org.fdroid.fdroid"), "content://org.fdroid.fdroid.data.ApkProvider/app/org.fdroid.fdroid", projection); + assertValidUri(resolver, ApkProvider.getContentUri(new MockApk("org.fdroid.fdroid", 100)), "content://org.fdroid.fdroid.data.ApkProvider/apk/100/org.fdroid.fdroid", projection); + assertValidUri(resolver, ApkProvider.getContentUri(apks), projection); + assertValidUri(resolver, ApkProvider.getContentUri("org.fdroid.fdroid", 100), "content://org.fdroid.fdroid.data.ApkProvider/apk/100/org.fdroid.fdroid", projection); + assertValidUri(resolver, ApkProvider.getRepoUri(1000), "content://org.fdroid.fdroid.data.ApkProvider/repo/1000", projection); + + List manyApks = new ArrayList<>(ApkProvider.MAX_APKS_TO_QUERY - 5); + for (int i = 0; i < ApkProvider.MAX_APKS_TO_QUERY - 1; i++) { + manyApks.add(new MockApk("com.example." + i, i)); + } + assertValidUri(resolver, ApkProvider.getContentUri(manyApks), projection); + + manyApks.add(new MockApk("org.fdroid.fdroid.1", 1)); + manyApks.add(new MockApk("org.fdroid.fdroid.2", 2)); + try { + // Technically, it is a valid URI, because it doesn't + // throw an UnsupportedOperationException. However it + // is still not okay (we run out of bindable parameters + // in the sqlite query. + assertValidUri(resolver, ApkProvider.getContentUri(manyApks), projection); + fail(); + } catch (IllegalArgumentException e) { + // This is the expected error behaviour. + } catch (Exception e) { + fail(); + } + } + } diff --git a/app/src/androidTest/java/org/fdroid/fdroid/mock/MockApk.java b/app/src/test/java/org/fdroid/fdroid/mock/MockApk.java similarity index 100% rename from app/src/androidTest/java/org/fdroid/fdroid/mock/MockApk.java rename to app/src/test/java/org/fdroid/fdroid/mock/MockApk.java diff --git a/app/src/androidTest/java/org/fdroid/fdroid/mock/MockApp.java b/app/src/test/java/org/fdroid/fdroid/mock/MockApp.java similarity index 100% rename from app/src/androidTest/java/org/fdroid/fdroid/mock/MockApp.java rename to app/src/test/java/org/fdroid/fdroid/mock/MockApp.java diff --git a/app/src/androidTest/java/org/fdroid/fdroid/mock/MockRepo.java b/app/src/test/java/org/fdroid/fdroid/mock/MockRepo.java similarity index 100% rename from app/src/androidTest/java/org/fdroid/fdroid/mock/MockRepo.java rename to app/src/test/java/org/fdroid/fdroid/mock/MockRepo.java