From 09fd3d188c1b56488c0e2b11dbad596fefb51ee9 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Sun, 5 Jun 2016 09:36:26 +1000 Subject: [PATCH] Robolectric testing support + InstallAppProvider testing now run in JVM. Robolectric provides testing support for Android via the JVM, including testing of content providers. In order to get these tests to work, we need to avoid the default behaviour of starting up FDroidApp.onCreate(). This method has a lot of static state which fails if set multiple times. Instead of trying to ensure we correctly zero out that state each test, it is preferable to instead never bother with that in the first place. Expecially when that is not what is under test (as is the case with content provider tests). --- app/build.gradle | 3 + .../fdroid/fdroid/data/AppProviderTest.java | 4 +- .../fdroid/data/InstalledAppProviderTest.java | 167 --------------- .../fdroid/data/InstalledAppTestUtils.java | 24 +++ .../java/org/fdroid/fdroid/TestFDroidApp.java | 11 + .../fdroid/data/InstalledAppProviderTest.java | 200 ++++++++++++++++++ .../fdroid/fdroid/data/ProviderTestUtils.java | 103 +++++++++ .../fdroid/fdroid/data/ProviderUriTests.java | 69 ++++++ 8 files changed, 412 insertions(+), 169 deletions(-) delete mode 100644 app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java create mode 100644 app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppTestUtils.java create mode 100644 app/src/test/java/org/fdroid/fdroid/TestFDroidApp.java create mode 100644 app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java create mode 100644 app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java create mode 100644 app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java diff --git a/app/build.gradle b/app/build.gradle index 8fd669d5d..bb41a86ed 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -41,6 +41,9 @@ dependencies { testCompile 'junit:junit:4.12' + // 3.1-rc1 is required because it is the first to implements API v23. + testCompile "org.robolectric:robolectric:3.1-rc1" + androidTestCompile 'com.android.support:support-annotations:23.4.0' androidTestCompile 'com.android.support.test:runner:0.5' androidTestCompile 'com.android.support.test:rules:0.5' diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/AppProviderTest.java b/app/src/androidTest/java/org/fdroid/fdroid/data/AppProviderTest.java index 454759c0e..0bce53b68 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/AppProviderTest.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/data/AppProviderTest.java @@ -131,7 +131,7 @@ public class AppProviderTest extends FDroidProviderTest { values.put(AppProvider.DataColumns.IGNORE_THISUPDATE, ignoreVercode); insertApp(id, "App: " + id, values); - InstalledAppProviderTest.install(getSwappableContext(), packageManager, id, installedVercode, "v" + installedVercode); + InstalledAppTestUtils.install(getSwappableContext(), packageManager, id, installedVercode, "v" + installedVercode); } public void testCanUpdate() { @@ -251,7 +251,7 @@ public class AppProviderTest extends FDroidProviderTest { assertResultCount(0, AppProvider.getInstalledUri()); for (int i = 10; i < 20; i++) { - InstalledAppProviderTest.install(getSwappableContext(), pm, "com.example.test." + i, i, "v1"); + InstalledAppTestUtils.install(getSwappableContext(), pm, "com.example.test." + i, i, "v1"); } assertResultCount(10, AppProvider.getInstalledUri()); diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java b/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java deleted file mode 100644 index 7da03fcd0..000000000 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java +++ /dev/null @@ -1,167 +0,0 @@ -package org.fdroid.fdroid.data; - -import android.content.ContentValues; -import android.content.pm.PackageInfo; -import android.database.Cursor; -import android.net.Uri; - -import mock.MockContextSwappableComponents; -import mock.MockInstallablePackageManager; - -@SuppressWarnings("PMD") // TODO port this to JUnit 4 semantics -public class InstalledAppProviderTest extends FDroidProviderTest { - - public InstalledAppProviderTest() { - super(InstalledAppProvider.class, InstalledAppProvider.getAuthority()); - } - - public void testUris() { - assertInvalidUri(InstalledAppProvider.getAuthority()); - assertInvalidUri(RepoProvider.getContentUri()); - assertInvalidUri(AppProvider.getContentUri()); - assertInvalidUri(ApkProvider.getContentUri()); - assertInvalidUri("blah"); - - assertValidUri(InstalledAppProvider.getContentUri()); - assertValidUri(InstalledAppProvider.getAppUri("com.example.com")); - assertValidUri(InstalledAppProvider.getAppUri("blah")); - } - - public void testInsert() { - - assertResultCount(0, InstalledAppProvider.getContentUri()); - - insertInstalledApp("com.example.com1", 1, "v1"); - insertInstalledApp("com.example.com2", 2, "v2"); - insertInstalledApp("com.example.com3", 3, "v3"); - - assertResultCount(3, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb("com.example.com1", 1, "v1"); - assertIsInstalledVersionInDb("com.example.com2", 2, "v2"); - assertIsInstalledVersionInDb("com.example.com3", 3, "v3"); - } - - public void testUpdate() { - - insertInstalledApp("com.example.app1", 10, "1.0"); - insertInstalledApp("com.example.app2", 10, "1.0"); - - assertResultCount(2, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb("com.example.app2", 10, "1.0"); - - try { - getMockContentResolver().update( - InstalledAppProvider.getAppUri("com.example.app2"), - createContentValues(11, "1.1"), - null, null - ); - fail(); - } catch (UnsupportedOperationException e) { - // We expect this to happen, because we should be using insert() instead. - } - - getMockContentResolver().insert( - InstalledAppProvider.getContentUri(), - createContentValues("com.example.app2", 11, "1.1") - ); - - assertResultCount(2, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb("com.example.app2", 11, "1.1"); - - } - - public void testLastUpdateTime() { - String packageName = "com.example.app"; - - insertInstalledApp(packageName, 10, "1.0"); - assertResultCount(1, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb(packageName, 10, "1.0"); - - Uri uri = InstalledAppProvider.getAppUri(packageName); - - String[] projection = { - InstalledAppProvider.DataColumns.PACKAGE_NAME, - InstalledAppProvider.DataColumns.LAST_UPDATE_TIME, - }; - - Cursor cursor = getMockContentResolver().query(uri, projection, null, null, null); - assertNotNull(cursor); - assertEquals("App \"" + packageName + "\" not installed", 1, cursor.getCount()); - cursor.moveToFirst(); - assertEquals(packageName, cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.PACKAGE_NAME))); - long lastUpdateTime = cursor.getLong(cursor.getColumnIndex(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME)); - assertTrue(lastUpdateTime > 0); - assertTrue(lastUpdateTime < System.currentTimeMillis()); - cursor.close(); - - insertInstalledApp(packageName, 11, "1.1"); - cursor = getMockContentResolver().query(uri, projection, null, null, null); - assertNotNull(cursor); - assertEquals("App \"" + packageName + "\" not installed", 1, cursor.getCount()); - cursor.moveToFirst(); - assertTrue(lastUpdateTime < cursor.getLong(cursor.getColumnIndex(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME))); - cursor.close(); - } - - public void testDelete() { - - insertInstalledApp("com.example.app1", 10, "1.0"); - insertInstalledApp("com.example.app2", 10, "1.0"); - - assertResultCount(2, InstalledAppProvider.getContentUri()); - - getMockContentResolver().delete(InstalledAppProvider.getAppUri("com.example.app1"), null, null); - - assertResultCount(1, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb("com.example.app2", 10, "1.0"); - - } - - @Override - protected String[] getMinimalProjection() { - return new String[]{ - InstalledAppProvider.DataColumns.PACKAGE_NAME, - InstalledAppProvider.DataColumns.VERSION_CODE, - InstalledAppProvider.DataColumns.VERSION_NAME, - }; - } - - private ContentValues createContentValues(int versionCode, String versionNumber) { - return createContentValues(null, versionCode, versionNumber); - } - - private ContentValues createContentValues(String appId, int versionCode, String versionNumber) { - ContentValues values = new ContentValues(3); - if (appId != null) { - values.put(InstalledAppProvider.DataColumns.PACKAGE_NAME, appId); - } - values.put(InstalledAppProvider.DataColumns.APPLICATION_LABEL, "Mock app: " + appId); - values.put(InstalledAppProvider.DataColumns.VERSION_CODE, versionCode); - values.put(InstalledAppProvider.DataColumns.VERSION_NAME, versionNumber); - values.put(InstalledAppProvider.DataColumns.SIGNATURE, ""); - values.put(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME, System.currentTimeMillis()); - values.put(InstalledAppProvider.DataColumns.HASH_TYPE, "sha256"); - values.put(InstalledAppProvider.DataColumns.HASH, "cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe"); - return values; - } - - private void insertInstalledApp(String appId, int versionCode, String versionNumber) { - ContentValues values = createContentValues(appId, versionCode, versionNumber); - getMockContentResolver().insert(InstalledAppProvider.getContentUri(), values); - } - - /** - * Will tell {@code pm} that we are installing {@code packageName}, and then update the - * "installed apps" table in the database. - */ - public static void install(MockContextSwappableComponents context, - MockInstallablePackageManager pm, String packageName, - int versionCode, String versionName) { - - context.setPackageManager(pm); - pm.install(packageName, versionCode, versionName); - PackageInfo packageInfo = pm.getPackageInfo(packageName, 0); - InstalledAppProviderService.insertAppIntoDb(context, packageName, packageInfo); - } - -} diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppTestUtils.java b/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppTestUtils.java new file mode 100644 index 000000000..77959530c --- /dev/null +++ b/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppTestUtils.java @@ -0,0 +1,24 @@ +package org.fdroid.fdroid.data; + +import android.content.pm.PackageInfo; + +import mock.MockContextSwappableComponents; +import mock.MockInstallablePackageManager; + +public class InstalledAppTestUtils { + + /** + * Will tell {@code pm} that we are installing {@code packageName}, and then update the + * "installed apps" table in the database. + */ + public static void install(MockContextSwappableComponents context, + MockInstallablePackageManager pm, String packageName, + int versionCode, String versionName) { + + context.setPackageManager(pm); + pm.install(packageName, versionCode, versionName); + PackageInfo packageInfo = pm.getPackageInfo(packageName, 0); + InstalledAppProviderService.insertAppIntoDb(context, packageName, packageInfo); + } + +} diff --git a/app/src/test/java/org/fdroid/fdroid/TestFDroidApp.java b/app/src/test/java/org/fdroid/fdroid/TestFDroidApp.java new file mode 100644 index 000000000..b0fcd8319 --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/TestFDroidApp.java @@ -0,0 +1,11 @@ +package org.fdroid.fdroid; + +import android.app.Application; + +/** + * Due to there being so much static initialization in the main FDroidApp, it becomes hard to reset + * that state between Robolectric test runs. Therefore, robolectric tests will default to this + * {@link Application} instead of {@link FDroidApp}. It intentionally doesn't extends {@link FDroidApp} + * so that the static initialization in {@link FDroidApp#onCreate()} is not executed. + */ +public class TestFDroidApp extends Application {} diff --git a/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java new file mode 100644 index 000000000..75a4dcded --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java @@ -0,0 +1,200 @@ +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.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowContentResolver; + +import static org.fdroid.fdroid.data.ProviderTestUtils.assertIsInstalledVersionInDb; +import static org.fdroid.fdroid.data.ProviderTestUtils.assertResultCount; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Map; + +@Config(constants = BuildConfig.class, application = Application.class) +@RunWith(RobolectricGradleTestRunner.class) +public class InstalledAppProviderTest { + + private ShadowContentResolver contentResolver; + + @Before + public void setup() { + contentResolver = Shadows.shadowOf(RuntimeEnvironment.application.getContentResolver()); + ShadowContentResolver.registerProvider(InstalledAppProvider.getAuthority(), new InstalledAppProvider()); + } + + @After + public void teardown() { + FDroidProvider.clearDbHelperSingleton(); + } + + @Test + public void insertSingleApp() { + Map foundBefore = InstalledAppProvider.Helper.all(RuntimeEnvironment.application); + assertEquals(foundBefore.size(), 0); + + ContentValues values = new ContentValues(); + values.put(InstalledAppProvider.DataColumns.PACKAGE_NAME, "org.example.test-app"); + values.put(InstalledAppProvider.DataColumns.APPLICATION_LABEL, "Test App"); + values.put(InstalledAppProvider.DataColumns.VERSION_CODE, 1021); + values.put(InstalledAppProvider.DataColumns.VERSION_NAME, "Longhorn"); + values.put(InstalledAppProvider.DataColumns.HASH, "has of test app"); + values.put(InstalledAppProvider.DataColumns.HASH_TYPE, "fake hash type"); + values.put(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME, 100000000L); + values.put(InstalledAppProvider.DataColumns.SIGNATURE, "000111222333444555666777888999aaabbbcccdddeeefff"); + contentResolver.insert(InstalledAppProvider.getContentUri(), values); + + Map foundAfter = InstalledAppProvider.Helper.all(RuntimeEnvironment.application); + assertEquals(1, foundAfter.size()); + assertEquals(100000000L, foundAfter.get("org.example.test-app").longValue()); + + Cursor cursor = contentResolver.query(InstalledAppProvider.getAppUri("org.example.test-app"), InstalledAppProvider.DataColumns.ALL, null, null, null); + assertEquals(cursor.getCount(), 1); + + cursor.moveToFirst(); + assertEquals("org.example.test-app", cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.PACKAGE_NAME))); + assertEquals("Test App", cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.APPLICATION_LABEL))); + assertEquals(1021, cursor.getInt(cursor.getColumnIndex(InstalledAppProvider.DataColumns.VERSION_CODE))); + assertEquals("Longhorn", cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.VERSION_NAME))); + assertEquals("has of test app", cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.HASH))); + assertEquals("fake hash type", cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.HASH_TYPE))); + assertEquals(100000000L, cursor.getLong(cursor.getColumnIndex(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME))); + assertEquals("000111222333444555666777888999aaabbbcccdddeeefff", cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.SIGNATURE))); + + cursor.close(); + } + + @Test + public void testInsert() { + + assertResultCount(contentResolver, 0, InstalledAppProvider.getContentUri()); + + insertInstalledApp("com.example.com1", 1, "v1"); + insertInstalledApp("com.example.com2", 2, "v2"); + insertInstalledApp("com.example.com3", 3, "v3"); + + assertResultCount(contentResolver, 3, InstalledAppProvider.getContentUri()); + assertIsInstalledVersionInDb(contentResolver, "com.example.com1", 1, "v1"); + assertIsInstalledVersionInDb(contentResolver, "com.example.com2", 2, "v2"); + assertIsInstalledVersionInDb(contentResolver, "com.example.com3", 3, "v3"); + } + + @Test + public void testUpdate() { + + insertInstalledApp("com.example.app1", 10, "1.0"); + insertInstalledApp("com.example.app2", 10, "1.0"); + + assertResultCount(contentResolver, 2, InstalledAppProvider.getContentUri()); + assertIsInstalledVersionInDb(contentResolver, "com.example.app2", 10, "1.0"); + + try { + contentResolver.update( + InstalledAppProvider.getAppUri("com.example.app2"), + createContentValues(11, "1.1"), + null, null + ); + fail(); + } catch (UnsupportedOperationException e) { + // We expect this to happen, because we should be using insert() instead. + } + + contentResolver.insert( + InstalledAppProvider.getContentUri(), + createContentValues("com.example.app2", 11, "1.1") + ); + + assertResultCount(contentResolver, 2, InstalledAppProvider.getContentUri()); + assertIsInstalledVersionInDb(contentResolver, "com.example.app2", 11, "1.1"); + + } + + @Test + public void testLastUpdateTime() { + String packageName = "com.example.app"; + + insertInstalledApp(packageName, 10, "1.0"); + assertResultCount(contentResolver, 1, InstalledAppProvider.getContentUri()); + assertIsInstalledVersionInDb(contentResolver, packageName, 10, "1.0"); + + Uri uri = InstalledAppProvider.getAppUri(packageName); + + String[] projection = { + InstalledAppProvider.DataColumns.PACKAGE_NAME, + InstalledAppProvider.DataColumns.LAST_UPDATE_TIME, + }; + + Cursor cursor = contentResolver.query(uri, projection, null, null, null); + assertNotNull(cursor); + assertEquals("App \"" + packageName + "\" not installed", 1, cursor.getCount()); + cursor.moveToFirst(); + assertEquals(packageName, cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.PACKAGE_NAME))); + long lastUpdateTime = cursor.getLong(cursor.getColumnIndex(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME)); + assertTrue(lastUpdateTime > 0); + assertTrue(lastUpdateTime < System.currentTimeMillis()); + cursor.close(); + + insertInstalledApp(packageName, 11, "1.1"); + cursor = contentResolver.query(uri, projection, null, null, null); + assertNotNull(cursor); + assertEquals("App \"" + packageName + "\" not installed", 1, cursor.getCount()); + cursor.moveToFirst(); + assertTrue(lastUpdateTime < cursor.getLong(cursor.getColumnIndex(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME))); + cursor.close(); + } + + @Test + public void testDelete() { + + insertInstalledApp("com.example.app1", 10, "1.0"); + insertInstalledApp("com.example.app2", 10, "1.0"); + + assertResultCount(contentResolver, 2, InstalledAppProvider.getContentUri()); + + contentResolver.delete(InstalledAppProvider.getAppUri("com.example.app1"), null, null); + + assertResultCount(contentResolver, 1, InstalledAppProvider.getContentUri()); + assertIsInstalledVersionInDb(contentResolver, "com.example.app2", 10, "1.0"); + + } + + private ContentValues createContentValues(int versionCode, String versionNumber) { + return createContentValues(null, versionCode, versionNumber); + } + + private ContentValues createContentValues(String appId, int versionCode, String versionNumber) { + ContentValues values = new ContentValues(3); + if (appId != null) { + values.put(InstalledAppProvider.DataColumns.PACKAGE_NAME, appId); + } + values.put(InstalledAppProvider.DataColumns.APPLICATION_LABEL, "Mock app: " + appId); + values.put(InstalledAppProvider.DataColumns.VERSION_CODE, versionCode); + values.put(InstalledAppProvider.DataColumns.VERSION_NAME, versionNumber); + values.put(InstalledAppProvider.DataColumns.SIGNATURE, ""); + values.put(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME, System.currentTimeMillis()); + values.put(InstalledAppProvider.DataColumns.HASH_TYPE, "sha256"); + values.put(InstalledAppProvider.DataColumns.HASH, "cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe"); + return values; + } + + private void insertInstalledApp(String appId, int versionCode, String versionNumber) { + ContentValues values = createContentValues(appId, versionCode, versionNumber); + contentResolver.insert(InstalledAppProvider.getContentUri(), values); + } +} + +// https://github.com/robolectric/robolectric/wiki/2.4-to-3.0-Upgrade-Guide diff --git a/app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java b/app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java new file mode 100644 index 000000000..9c3b2cc4b --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java @@ -0,0 +1,103 @@ +package org.fdroid.fdroid.data; + +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +import org.robolectric.shadows.ShadowContentResolver; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +public class ProviderTestUtils { + + public static void assertCantDelete(ShadowContentResolver resolver, Uri uri) { + try { + resolver.delete(uri, null, null); + fail(); + } catch (UnsupportedOperationException e) { + // Successful condition + } catch (Exception e) { + fail(); + } + } + + public static void assertCantUpdate(ShadowContentResolver resolver, Uri uri) { + try { + resolver.update(uri, new ContentValues(), null, null); + fail(); + } catch (UnsupportedOperationException e) { + // Successful condition + } catch (Exception e) { + fail(); + } + } + + public static void assertInvalidUri(ShadowContentResolver resolver, String uri) { + assertInvalidUri(resolver, Uri.parse(uri)); + } + + public static void assertValidUri(ShadowContentResolver resolver, String uri, String[] projection) { + assertValidUri(resolver, Uri.parse(uri), projection); + } + + public static void assertInvalidUri(ShadowContentResolver resolver, Uri uri) { + Cursor cursor = resolver.query(uri, new String[] {}, null, null, null); + assertNull(cursor); + } + + public static void assertValidUri(ShadowContentResolver resolver, Uri uri, String[] projection) { + Cursor cursor = resolver.query(uri, projection, null, null, null); + assertNotNull(cursor); + cursor.close(); + } + + public static void assertValidUri(ShadowContentResolver resolver, Uri actualUri, String expectedUri, String[] projection) { + assertValidUri(resolver, actualUri, projection); + assertEquals(expectedUri, actualUri.toString()); + } + + public static void assertResultCount(ShadowContentResolver resolver, int expectedCount, Uri uri) { + Cursor cursor = resolver.query(uri, new String[] {}, null, null, null); + assertResultCount(expectedCount, cursor); + cursor.close(); + } + + public static void assertResultCount(int expectedCount, List items) { + assertNotNull(items); + assertEquals(expectedCount, items.size()); + } + + public static void assertResultCount(int expectedCount, Cursor result) { + assertNotNull(result); + assertEquals(expectedCount, result.getCount()); + } + + public static void assertIsInstalledVersionInDb(ShadowContentResolver resolver, String appId, int versionCode, String versionName) { + Uri uri = InstalledAppProvider.getAppUri(appId); + + String[] projection = { + InstalledAppProvider.DataColumns.PACKAGE_NAME, + InstalledAppProvider.DataColumns.VERSION_CODE, + InstalledAppProvider.DataColumns.VERSION_NAME, + InstalledAppProvider.DataColumns.APPLICATION_LABEL, + }; + + Cursor cursor = resolver.query(uri, projection, null, null, null); + + assertNotNull(cursor); + assertEquals("App \"" + appId + "\" not installed", 1, cursor.getCount()); + + cursor.moveToFirst(); + + assertEquals(appId, cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.PACKAGE_NAME))); + assertEquals(versionCode, cursor.getInt(cursor.getColumnIndex(InstalledAppProvider.DataColumns.VERSION_CODE))); + assertEquals(versionName, cursor.getString(cursor.getColumnIndex(InstalledAppProvider.DataColumns.VERSION_NAME))); + cursor.close(); + } + +} diff --git a/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java b/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java new file mode 100644 index 000000000..c62aa465b --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java @@ -0,0 +1,69 @@ +package org.fdroid.fdroid.data; + +import org.fdroid.fdroid.BuildConfig; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowContentResolver; + +import static org.fdroid.fdroid.data.ProviderTestUtils.assertInvalidUri; +import static org.fdroid.fdroid.data.ProviderTestUtils.assertValidUri; + +@Config(constants = BuildConfig.class) +@RunWith(RobolectricGradleTestRunner.class) +public class ProviderUriTests { + + private ShadowContentResolver resolver; + + @Before + public void setup() { + resolver = Shadows.shadowOf(RuntimeEnvironment.application.getContentResolver()); + } + + @After + public void teardown() { + FDroidProvider.clearDbHelperSingleton(); + } + + @Test + public void invalidInstalledAppProviderUris() { + ShadowContentResolver.registerProvider(InstalledAppProvider.getAuthority(), new InstalledAppProvider()); + assertInvalidUri(resolver, InstalledAppProvider.getAuthority()); + assertInvalidUri(resolver, "blah"); + } + + @Test + public void validInstalledAppProviderUris() { + ShadowContentResolver.registerProvider(InstalledAppProvider.getAuthority(), new InstalledAppProvider()); + String[] projection = new String[] { InstalledAppProvider.DataColumns._ID }; + assertValidUri(resolver, InstalledAppProvider.getContentUri(), projection); + assertValidUri(resolver, InstalledAppProvider.getAppUri("org.example.app"), projection); + assertValidUri(resolver, InstalledAppProvider.getSearchUri("blah"), projection); + assertValidUri(resolver, InstalledAppProvider.getSearchUri("\"blah\""), projection); + assertValidUri(resolver, InstalledAppProvider.getSearchUri("blah & sneh"), projection); + assertValidUri(resolver, InstalledAppProvider.getSearchUri("http://blah.example.com?sneh=\"sneh\""), projection); + } + + @Test + public void invalidRepoProviderUris() { + ShadowContentResolver.registerProvider(RepoProvider.getAuthority(), new RepoProvider()); + assertInvalidUri(resolver, RepoProvider.getAuthority()); + assertInvalidUri(resolver, "blah"); + } + + @Test + public void validRepoProviderUris() { + ShadowContentResolver.registerProvider(RepoProvider.getAuthority(), new RepoProvider()); + String[] projection = new String[] { RepoProvider.DataColumns._ID }; + assertValidUri(resolver, RepoProvider.getContentUri(), projection); + assertValidUri(resolver, RepoProvider.getContentUri(10000L), projection); + assertValidUri(resolver, RepoProvider.allExceptSwapUri(), projection); + } + } + +}