From c7bb93f7433c30b4c2df98a0fb0bea607b91382f Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Mon, 6 Jun 2016 07:43:18 +1000 Subject: [PATCH 01/10] Inject hash + hashType into install method. This makes testing of the function easier, as the method previously expected a real file to exist on disk for which it could then hash. This instead allows mock hash values to be inserted when under test. Other than this, the semantics remain exactly the same as before, and the expensive hashing is still done on a worker thread as part of the `IntentService`. --- .../fdroid/data/InstalledAppProviderService.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java index f1959596f..3c54f5519 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java @@ -156,7 +156,10 @@ public class InstalledAppProviderService extends IntentService { String packageName = intent.getData().getSchemeSpecificPart(); final String action = intent.getAction(); if (ACTION_INSERT.equals(action)) { - insertAppIntoDb(this, packageName, (PackageInfo) intent.getParcelableExtra(EXTRA_PACKAGE_INFO)); + PackageInfo packageInfo = intent.getParcelableExtra(EXTRA_PACKAGE_INFO); + String hashType = "sha256"; + String hash = Utils.getBinaryHash(new File(packageInfo.applicationInfo.publicSourceDir), hashType); + insertAppIntoDb(this, packageName, packageInfo, hashType, hash); } else if (ACTION_DELETE.equals(action)) { deleteAppFromDb(this, packageName); } @@ -164,7 +167,13 @@ public class InstalledAppProviderService extends IntentService { } } - static void insertAppIntoDb(Context context, String packageName, PackageInfo packageInfo) { + /** + * @param hash Although the has could be calculated within this function, it is helpful to inject + * the hash so as to be able to use this method during testing. Otherwise, the + * hashing method will try to hash a non-existent .apk file and try to insert NULL + * into the database when under test. + */ + static void insertAppIntoDb(Context context, String packageName, PackageInfo packageInfo, String hashType, String hash) { if (packageInfo == null) { try { packageInfo = context.getPackageManager().getPackageInfo(packageName, @@ -185,8 +194,6 @@ public class InstalledAppProviderService extends IntentService { contentValues.put(InstalledAppProvider.DataColumns.SIGNATURE, getPackageSig(packageInfo)); contentValues.put(InstalledAppProvider.DataColumns.LAST_UPDATE_TIME, packageInfo.lastUpdateTime); - String hashType = "sha256"; - String hash = Utils.getBinaryHash(new File(packageInfo.applicationInfo.publicSourceDir), hashType); contentValues.put(InstalledAppProvider.DataColumns.HASH_TYPE, hashType); contentValues.put(InstalledAppProvider.DataColumns.HASH, hash); From 09fd3d188c1b56488c0e2b11dbad596fefb51ee9 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Sun, 5 Jun 2016 09:36:26 +1000 Subject: [PATCH 02/10] 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); + } + } + +} From 4e66bb810fe7ab42e65d5fda9fb4a3490a62c30c Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Mon, 6 Jun 2016 08:02:53 +1000 Subject: [PATCH 03/10] Ported `AppProvider` tests to Robolectric. Get around silly `final` methods in `ContentResolver` with Mockito and `delegatesTo`. The Robolectric library presumes that people always want to test content providers by manually invoking the `query`/`update`/`delete` methods on the `ShadowContentResolver`. While that is a great feature for testing, we have helper methods that require testing, and these methods accept either a _real_ `ContentResolver` or `Context`. Robolectric did some cool magic in terms of intercepting runtime calls to content resolvers and forwarding them to the "shadow" verison, to deal with final/package private/etc methods. However, as a side effect, the `ShadowContentProvider` _is not a `ContentProvider` as far as the Java compiler is concerned. By utilising Mockito + `delegatesTo` method, we are able to achieve what is required: * An actual `ContentProvider` instance. * It forwards calls to the `ShadowContentProvider` provided by Robolectric. --- app/build.gradle | 2 + .../java/org/fdroid/fdroid/TestUtils.java | 6 +- .../fdroid/fdroid/data/AppProviderTest.java | 389 ------------------ .../fdroid/data/BaseApkProviderTest.java | 2 +- ...erTest.java => FDroidProviderTestOld.java} | 4 +- .../fdroid/data/InstalledAppCacheTest.java | 4 +- .../fdroid/data/InstalledAppTestUtils.java | 24 -- .../fdroid/fdroid/data/AppProviderTest.java | 374 +++++++++++++++++ .../fdroid/data/FDroidProviderTest.java | 39 ++ .../fdroid/data/InstalledAppProviderTest.java | 12 +- .../fdroid/data/InstalledAppTestUtils.java | 27 ++ .../fdroid/fdroid/data/ProviderTestUtils.java | 70 +++- .../fdroid/fdroid/data/ProviderUriTests.java | 33 ++ 13 files changed, 553 insertions(+), 433 deletions(-) delete mode 100644 app/src/androidTest/java/org/fdroid/fdroid/data/AppProviderTest.java rename app/src/androidTest/java/org/fdroid/fdroid/data/{FDroidProviderTest.java => FDroidProviderTestOld.java} (96%) delete mode 100644 app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppTestUtils.java create mode 100644 app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java create mode 100644 app/src/test/java/org/fdroid/fdroid/data/FDroidProviderTest.java create mode 100644 app/src/test/java/org/fdroid/fdroid/data/InstalledAppTestUtils.java diff --git a/app/build.gradle b/app/build.gradle index bb41a86ed..9443a8917 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -44,6 +44,8 @@ dependencies { // 3.1-rc1 is required because it is the first to implements API v23. testCompile "org.robolectric:robolectric:3.1-rc1" + testCompile "org.mockito:mockito-core:1.10.19" + 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/TestUtils.java b/app/src/androidTest/java/org/fdroid/fdroid/TestUtils.java index 8b01eb630..e51790abb 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/TestUtils.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/TestUtils.java @@ -13,7 +13,7 @@ import junit.framework.AssertionFailedError; import org.fdroid.fdroid.data.ApkProvider; import org.fdroid.fdroid.data.AppProvider; -import org.fdroid.fdroid.data.FDroidProviderTest; +import org.fdroid.fdroid.data.FDroidProviderTestOld; import java.io.File; import java.io.FileOutputStream; @@ -113,11 +113,11 @@ public class TestUtils { resolver.insert(uri, values); } - public static Uri insertApk(FDroidProviderTest providerTest, String id, int versionCode) { + public static Uri insertApk(FDroidProviderTestOld providerTest, String id, int versionCode) { return insertApk(providerTest, id, versionCode, new ContentValues()); } - public static Uri insertApk(FDroidProviderTest providerTest, String id, int versionCode, ContentValues additionalValues) { + public static Uri insertApk(FDroidProviderTestOld providerTest, String id, int versionCode, ContentValues additionalValues) { ContentValues values = new ContentValues(); diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/AppProviderTest.java b/app/src/androidTest/java/org/fdroid/fdroid/data/AppProviderTest.java deleted file mode 100644 index 0bce53b68..000000000 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/AppProviderTest.java +++ /dev/null @@ -1,389 +0,0 @@ -package org.fdroid.fdroid.data; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.pm.PackageInfo; -import android.content.res.Resources; -import android.database.Cursor; - -import org.fdroid.fdroid.R; -import org.fdroid.fdroid.TestUtils; - -import java.util.ArrayList; -import java.util.List; - -import mock.MockCategoryResources; -import mock.MockContextSwappableComponents; -import mock.MockInstallablePackageManager; - -@SuppressWarnings("PMD") // TODO port this to JUnit 4 semantics -public class AppProviderTest extends FDroidProviderTest { - - public AppProviderTest() { - super(AppProvider.class, AppProvider.getAuthority()); - } - - @Override - public void setUp() throws Exception { - super.setUp(); - getSwappableContext().setResources(new MockCategoryResources(getContext())); - } - - @Override - protected Resources getMockResources() { - return new MockCategoryResources(getContext()); - } - - @Override - protected String[] getMinimalProjection() { - return new String[] { - AppProvider.DataColumns.PACKAGE_NAME, - AppProvider.DataColumns.NAME, - }; - } - - /** - * Although this doesn't directly relate to the AppProvider, it is here because - * the AppProvider used to stumble across this bug when asking for installed apps, - * and the device had over 1000 apps installed. - */ - public void testMaxSqliteParams() { - - MockInstallablePackageManager pm = new MockInstallablePackageManager(); - getSwappableContext().setPackageManager(pm); - - insertApp("com.example.app1", "App 1"); - insertApp("com.example.app100", "App 100"); - insertApp("com.example.app1000", "App 1000"); - - for (int i = 0; i < 50; i++) { - String packageName = "com.example.app" + i; - pm.install(packageName, 1, "v" + 1); - PackageInfo packageInfo = pm.getPackageInfo(packageName, 0); - InstalledAppProviderService.insertAppIntoDb(getSwappableContext(), packageName, packageInfo); - } - assertResultCount(1, AppProvider.getInstalledUri()); - - for (int i = 50; i < 500; i++) { - String packageName = "com.example.app" + i; - pm.install(packageName, 1, "v" + 1); - PackageInfo packageInfo = pm.getPackageInfo(packageName, 0); - InstalledAppProviderService.insertAppIntoDb(getSwappableContext(), packageName, packageInfo); - } - assertResultCount(2, AppProvider.getInstalledUri()); - - for (int i = 500; i < 1100; i++) { - String packageName = "com.example.app" + i; - pm.install(packageName, 1, "v" + 1); - PackageInfo packageInfo = pm.getPackageInfo(packageName, 0); - InstalledAppProviderService.insertAppIntoDb(getSwappableContext(), packageName, packageInfo); - } - assertResultCount(3, AppProvider.getInstalledUri()); - } - - public void testCantFindApp() { - assertNull(AppProvider.Helper.findByPackageName(getMockContentResolver(), "com.example.doesnt-exist")); - } - - public void testUris() { - assertInvalidUri(AppProvider.getAuthority()); - assertInvalidUri(ApkProvider.getContentUri()); - - assertValidUri(AppProvider.getContentUri(), "content://org.fdroid.fdroid.data.AppProvider"); - assertValidUri(AppProvider.getSearchUri("'searching!'"), "content://org.fdroid.fdroid.data.AppProvider/search/'searching!'"); - assertValidUri(AppProvider.getSearchUri("/"), "content://org.fdroid.fdroid.data.AppProvider/search/%2F"); - assertValidUri(AppProvider.getSearchUri(""), "content://org.fdroid.fdroid.data.AppProvider"); - assertValidUri(AppProvider.getSearchUri(null), "content://org.fdroid.fdroid.data.AppProvider"); - assertValidUri(AppProvider.getNoApksUri()); - assertValidUri(AppProvider.getInstalledUri()); - assertValidUri(AppProvider.getCanUpdateUri()); - - App app = new App(); - app.packageName = "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); - cursor.close(); - } - - private void insertApps(int count) { - for (int i = 0; i < count; i++) { - insertApp("com.example.test." + i, "Test app " + i); - } - } - - private void insertAndInstallApp( - MockInstallablePackageManager packageManager, - String id, int installedVercode, int suggestedVercode, - boolean ignoreAll, int ignoreVercode) { - ContentValues values = new ContentValues(3); - values.put(AppProvider.DataColumns.SUGGESTED_VERSION_CODE, suggestedVercode); - values.put(AppProvider.DataColumns.IGNORE_ALLUPDATES, ignoreAll); - values.put(AppProvider.DataColumns.IGNORE_THISUPDATE, ignoreVercode); - insertApp(id, "App: " + id, values); - - InstalledAppTestUtils.install(getSwappableContext(), packageManager, id, installedVercode, "v" + installedVercode); - } - - public void testCanUpdate() { - - MockContextSwappableComponents c = getSwappableContext(); - - MockInstallablePackageManager pm = new MockInstallablePackageManager(); - c.setPackageManager(pm); - - insertApp("not installed", "not installed"); - insertAndInstallApp(pm, "installed, only one version available", 1, 1, false, 0); - insertAndInstallApp(pm, "installed, already latest, no ignore", 10, 10, false, 0); - insertAndInstallApp(pm, "installed, already latest, ignore all", 10, 10, true, 0); - insertAndInstallApp(pm, "installed, already latest, ignore latest", 10, 10, false, 10); - insertAndInstallApp(pm, "installed, already latest, ignore old", 10, 10, false, 5); - insertAndInstallApp(pm, "installed, old version, no ignore", 5, 10, false, 0); - insertAndInstallApp(pm, "installed, old version, ignore all", 5, 10, true, 0); - insertAndInstallApp(pm, "installed, old version, ignore latest", 5, 10, false, 10); - insertAndInstallApp(pm, "installed, old version, ignore newer, but not latest", 5, 10, false, 8); - - ContentResolver r = getMockContentResolver(); - - // Can't "update", although can "install"... - App notInstalled = AppProvider.Helper.findByPackageName(r, "not installed"); - assertFalse(notInstalled.canAndWantToUpdate()); - - App installedOnlyOneVersionAvailable = AppProvider.Helper.findByPackageName(r, "installed, only one version available"); - App installedAlreadyLatestNoIgnore = AppProvider.Helper.findByPackageName(r, "installed, already latest, no ignore"); - App installedAlreadyLatestIgnoreAll = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore all"); - App installedAlreadyLatestIgnoreLatest = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore latest"); - App installedAlreadyLatestIgnoreOld = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore old"); - - assertFalse(installedOnlyOneVersionAvailable.canAndWantToUpdate()); - assertFalse(installedAlreadyLatestNoIgnore.canAndWantToUpdate()); - assertFalse(installedAlreadyLatestIgnoreAll.canAndWantToUpdate()); - assertFalse(installedAlreadyLatestIgnoreLatest.canAndWantToUpdate()); - assertFalse(installedAlreadyLatestIgnoreOld.canAndWantToUpdate()); - - App installedOldNoIgnore = AppProvider.Helper.findByPackageName(r, "installed, old version, no ignore"); - App installedOldIgnoreAll = AppProvider.Helper.findByPackageName(r, "installed, old version, ignore all"); - App installedOldIgnoreLatest = AppProvider.Helper.findByPackageName(r, "installed, old version, ignore latest"); - App installedOldIgnoreNewerNotLatest = AppProvider.Helper.findByPackageName(r, "installed, old version, ignore newer, but not latest"); - - assertTrue(installedOldNoIgnore.canAndWantToUpdate()); - assertFalse(installedOldIgnoreAll.canAndWantToUpdate()); - assertFalse(installedOldIgnoreLatest.canAndWantToUpdate()); - assertTrue(installedOldIgnoreNewerNotLatest.canAndWantToUpdate()); - - Cursor canUpdateCursor = r.query(AppProvider.getCanUpdateUri(), AppProvider.DataColumns.ALL, null, null, null); - canUpdateCursor.moveToFirst(); - List canUpdateIds = new ArrayList<>(canUpdateCursor.getCount()); - while (!canUpdateCursor.isAfterLast()) { - canUpdateIds.add(new App(canUpdateCursor).packageName); - canUpdateCursor.moveToNext(); - } - canUpdateCursor.close(); - - String[] expectedUpdateableIds = { - "installed, old version, no ignore", - "installed, old version, ignore newer, but not latest", - }; - - TestUtils.assertContainsOnly(expectedUpdateableIds, canUpdateIds); - } - - public void testIgnored() { - - MockInstallablePackageManager pm = new MockInstallablePackageManager(); - getSwappableContext().setPackageManager(pm); - - insertApp("not installed", "not installed"); - insertAndInstallApp(pm, "installed, only one version available", 1, 1, false, 0); - insertAndInstallApp(pm, "installed, already latest, no ignore", 10, 10, false, 0); - insertAndInstallApp(pm, "installed, already latest, ignore all", 10, 10, true, 0); - insertAndInstallApp(pm, "installed, already latest, ignore latest", 10, 10, false, 10); - insertAndInstallApp(pm, "installed, already latest, ignore old", 10, 10, false, 5); - insertAndInstallApp(pm, "installed, old version, no ignore", 5, 10, false, 0); - insertAndInstallApp(pm, "installed, old version, ignore all", 5, 10, true, 0); - insertAndInstallApp(pm, "installed, old version, ignore latest", 5, 10, false, 10); - insertAndInstallApp(pm, "installed, old version, ignore newer, but not latest", 5, 10, false, 8); - - assertResultCount(10, AppProvider.getContentUri()); - - String[] projection = {AppProvider.DataColumns.PACKAGE_NAME}; - List ignoredApps = AppProvider.Helper.findIgnored(getMockContext(), projection); - - String[] expectedIgnored = { - "installed, already latest, ignore all", - "installed, already latest, ignore latest", - // NOT "installed, already latest, ignore old" - because it - // is should only ignore if "ignored version" is >= suggested - - "installed, old version, ignore all", - "installed, old version, ignore latest", - // NOT "installed, old version, ignore newer, but not latest" - // for the same reason as above. - }; - - assertContainsOnlyIds(ignoredApps, expectedIgnored); - } - - private void assertContainsOnlyIds(List actualApps, String[] expectedIds) { - List actualIds = new ArrayList<>(actualApps.size()); - for (App app : actualApps) { - actualIds.add(app.packageName); - } - TestUtils.assertContainsOnly(actualIds, expectedIds); - } - - public void testInstalled() { - MockInstallablePackageManager pm = new MockInstallablePackageManager(); - getSwappableContext().setPackageManager(pm); - - insertApps(100); - - assertResultCount(100, AppProvider.getContentUri()); - assertResultCount(0, AppProvider.getInstalledUri()); - - for (int i = 10; i < 20; i++) { - InstalledAppTestUtils.install(getSwappableContext(), pm, "com.example.test." + i, i, "v1"); - } - - assertResultCount(10, AppProvider.getInstalledUri()); - } - - public void testInsert() { - - // Start with an empty database... - Cursor cursor = queryAllApps(); - assertNotNull(cursor); - assertEquals(0, cursor.getCount()); - cursor.close(); - - // 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 - // PACKAGE_NAME. - cursor.moveToFirst(); - App app = new App(cursor); - cursor.close(); - assertEquals("org.fdroid.fdroid", app.packageName); - assertEquals("F-Droid", app.name); - } - - private Cursor queryAllApps() { - return getMockContentResolver().query(AppProvider.getContentUri(), getMinimalProjection(), null, null, null); - } - - // ======================================================================== - // "Categories" - // (at this point) not an additional table, but we treat them sort of - // like they are. That means that if we change the implementation to - // use a separate table in the future, these should still pass. - // ======================================================================== - - public void testCategoriesSingle() { - insertAppWithCategory("com.dog", "Dog", "Animal"); - insertAppWithCategory("com.rock", "Rock", "Mineral"); - insertAppWithCategory("com.banana", "Banana", "Vegetable"); - - List categories = AppProvider.Helper.categories(getMockContext()); - String[] expected = new String[] { - getMockContext().getResources().getString(R.string.category_Whats_New), - getMockContext().getResources().getString(R.string.category_Recently_Updated), - getMockContext().getResources().getString(R.string.category_All), - "Animal", - "Mineral", - "Vegetable", - }; - TestUtils.assertContainsOnly(categories, expected); - } - - public void testCategoriesMultiple() { - insertAppWithCategory("com.rock.dog", "Rock-Dog", "Mineral,Animal"); - insertAppWithCategory("com.dog.rock.apple", "Dog-Rock-Apple", "Animal,Mineral,Vegetable"); - insertAppWithCategory("com.banana.apple", "Banana", "Vegetable,Vegetable"); - - List categories = AppProvider.Helper.categories(getMockContext()); - String[] expected = new String[] { - getMockContext().getResources().getString(R.string.category_Whats_New), - getMockContext().getResources().getString(R.string.category_Recently_Updated), - getMockContext().getResources().getString(R.string.category_All), - - "Animal", - "Mineral", - "Vegetable", - }; - TestUtils.assertContainsOnly(categories, expected); - - insertAppWithCategory("com.example.game", "Game", - "Running,Shooting,Jumping,Bleh,Sneh,Pleh,Blah,Test category," + - "The quick brown fox jumps over the lazy dog,With apostrophe's"); - - List categoriesLonger = AppProvider.Helper.categories(getMockContext()); - String[] expectedLonger = new String[] { - getMockContext().getResources().getString(R.string.category_Whats_New), - getMockContext().getResources().getString(R.string.category_Recently_Updated), - getMockContext().getResources().getString(R.string.category_All), - - "Animal", - "Mineral", - "Vegetable", - - "Running", - "Shooting", - "Jumping", - "Bleh", - "Sneh", - "Pleh", - "Blah", - "Test category", - "The quick brown fox jumps over the lazy dog", - "With apostrophe's", - }; - - TestUtils.assertContainsOnly(categoriesLonger, expectedLonger); - } - - // ======================================================================= - // Misc helper functions - // (to be used by any tests in this suite) - // ======================================================================= - - private void insertApp(String id, String name) { - insertApp(id, name, new ContentValues()); - } - - private void insertAppWithCategory(String id, String name, String categories) { - ContentValues values = new ContentValues(1); - values.put(AppProvider.DataColumns.CATEGORIES, categories); - insertApp(id, name, values); - } - - private void insertApp(String id, String name, - ContentValues additionalValues) { - TestUtils.insertApp(getMockContentResolver(), id, name, additionalValues); - } - -} diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/BaseApkProviderTest.java b/app/src/androidTest/java/org/fdroid/fdroid/data/BaseApkProviderTest.java index f5a73197c..41d6b0a6c 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/BaseApkProviderTest.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/data/BaseApkProviderTest.java @@ -16,7 +16,7 @@ import java.util.List; * This should not contain any test methods, or else they get executed * once for every concrete subclass. */ -abstract class BaseApkProviderTest extends FDroidProviderTest { +abstract class BaseApkProviderTest extends FDroidProviderTestOld { BaseApkProviderTest() { super(ApkProvider.class, ApkProvider.getAuthority()); diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/FDroidProviderTest.java b/app/src/androidTest/java/org/fdroid/fdroid/data/FDroidProviderTestOld.java similarity index 96% rename from app/src/androidTest/java/org/fdroid/fdroid/data/FDroidProviderTest.java rename to app/src/androidTest/java/org/fdroid/fdroid/data/FDroidProviderTestOld.java index 40e81fd28..4767aa4c4 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/FDroidProviderTest.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/data/FDroidProviderTestOld.java @@ -15,7 +15,7 @@ import mock.MockContextSwappableComponents; import mock.MockFDroidResources; @SuppressWarnings("PMD") // TODO port this to JUnit 4 semantics -public abstract class FDroidProviderTest extends ProviderTestCase2MockContext { +public abstract class FDroidProviderTestOld extends ProviderTestCase2MockContext { private FDroidProvider[] allProviders = { new AppProvider(), @@ -26,7 +26,7 @@ public abstract class FDroidProviderTest extends Provi private MockContextSwappableComponents swappableContext; - public FDroidProviderTest(Class providerClass, String providerAuthority) { + public FDroidProviderTestOld(Class providerClass, String providerAuthority) { super(providerClass, providerAuthority); } diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppCacheTest.java b/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppCacheTest.java index cb63f631a..f63d11b90 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppCacheTest.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppCacheTest.java @@ -5,11 +5,11 @@ import mock.MockInstallablePackageManager; /** * Tests the ability of the {@link InstalledAppCacheUpdater} to stay in sync with * the {@link android.content.pm.PackageManager}. - * For practical reasons, it extends FDroidProviderTest, although there is also a + * For practical reasons, it extends FDroidProviderTestOld, although there is also a * separate test for the InstalledAppProvider which tests the CRUD operations in more detail. */ @SuppressWarnings("PMD") // TODO port this to JUnit 4 semantics -public class InstalledAppCacheTest extends FDroidProviderTest { +public class InstalledAppCacheTest extends FDroidProviderTestOld { private MockInstallablePackageManager packageManager; diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppTestUtils.java b/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppTestUtils.java deleted file mode 100644 index 77959530c..000000000 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppTestUtils.java +++ /dev/null @@ -1,24 +0,0 @@ -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/data/AppProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java new file mode 100644 index 000000000..3aa382521 --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java @@ -0,0 +1,374 @@ +package org.fdroid.fdroid.data; + +import android.app.Application; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +import org.fdroid.fdroid.BuildConfig; +import org.fdroid.fdroid.R; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowContentResolver; + +import java.util.ArrayList; +import java.util.List; + +import static org.fdroid.fdroid.data.ProviderTestUtils.assertContainsOnly; +import static org.fdroid.fdroid.data.ProviderTestUtils.assertResultCount; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +@Config(constants = BuildConfig.class, application = Application.class) +@RunWith(RobolectricGradleTestRunner.class) +public class AppProviderTest extends FDroidProviderTest { + + private static final String[] PROJ = AppProvider.DataColumns.ALL; + + @Before + public void setup() { + ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); + } + + /** + * Although this doesn't directly relate to the {@link AppProvider}, it is here because + * the {@link AppProvider} used to stumble across this bug when asking for installed apps, + * and the device had over 1000 apps installed. + */ + @Test + public void testMaxSqliteParams() { + insertApp("com.example.app1", "App 1"); + insertApp("com.example.app100", "App 100"); + insertApp("com.example.app1000", "App 1000"); + + for (int i = 0; i < 50; i++) { + InstalledAppTestUtils.install(context, "com.example.app" + i, 1, "v1"); + } + assertResultCount(contentResolver, 1, AppProvider.getInstalledUri(), PROJ); + + for (int i = 50; i < 500; i++) { + InstalledAppTestUtils.install(context, "com.example.app" + i, 1, "v1"); + } + assertResultCount(contentResolver, 2, AppProvider.getInstalledUri(), PROJ); + + for (int i = 500; i < 1100; i++) { + InstalledAppTestUtils.install(context, "com.example.app" + i, 1, "v1"); + } + assertResultCount(contentResolver, 3, AppProvider.getInstalledUri(), PROJ); + } + + @Test + public void testCantFindApp() { + assertNull(AppProvider.Helper.findByPackageName(context.getContentResolver(), "com.example.doesnt-exist")); + } + + @Test + public void testQuery() { + Cursor cursor = queryAllApps(); + assertNotNull(cursor); + cursor.close(); + } + + private void insertApps(int count) { + for (int i = 0; i < count; i++) { + insertApp("com.example.test." + i, "Test app " + i); + } + } + + private void insertAndInstallApp( + String packageName, int installedVercode, int suggestedVercode, + boolean ignoreAll, int ignoreVercode) { + ContentValues values = new ContentValues(3); + values.put(AppProvider.DataColumns.SUGGESTED_VERSION_CODE, suggestedVercode); + values.put(AppProvider.DataColumns.IGNORE_ALLUPDATES, ignoreAll); + values.put(AppProvider.DataColumns.IGNORE_THISUPDATE, ignoreVercode); + insertApp(packageName, "App: " + packageName, values); + + InstalledAppTestUtils.install(context, packageName, installedVercode, "v" + installedVercode); + } + + @Test + public void testCanUpdate() { + insertApp("not installed", "not installed"); + insertAndInstallApp("installed, only one version available", 1, 1, false, 0); + insertAndInstallApp("installed, already latest, no ignore", 10, 10, false, 0); + insertAndInstallApp("installed, already latest, ignore all", 10, 10, true, 0); + insertAndInstallApp("installed, already latest, ignore latest", 10, 10, false, 10); + insertAndInstallApp("installed, already latest, ignore old", 10, 10, false, 5); + insertAndInstallApp("installed, old version, no ignore", 5, 10, false, 0); + insertAndInstallApp("installed, old version, ignore all", 5, 10, true, 0); + insertAndInstallApp("installed, old version, ignore latest", 5, 10, false, 10); + insertAndInstallApp("installed, old version, ignore newer, but not latest", 5, 10, false, 8); + + ContentResolver r = context.getContentResolver(); + + // Can't "update", although can "install"... + App notInstalled = AppProvider.Helper.findByPackageName(r, "not installed"); + assertFalse(notInstalled.canAndWantToUpdate()); + + App installedOnlyOneVersionAvailable = AppProvider.Helper.findByPackageName(r, "installed, only one version available"); + App installedAlreadyLatestNoIgnore = AppProvider.Helper.findByPackageName(r, "installed, already latest, no ignore"); + App installedAlreadyLatestIgnoreAll = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore all"); + App installedAlreadyLatestIgnoreLatest = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore latest"); + App installedAlreadyLatestIgnoreOld = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore old"); + + assertFalse(installedOnlyOneVersionAvailable.canAndWantToUpdate()); + assertFalse(installedAlreadyLatestNoIgnore.canAndWantToUpdate()); + assertFalse(installedAlreadyLatestIgnoreAll.canAndWantToUpdate()); + assertFalse(installedAlreadyLatestIgnoreLatest.canAndWantToUpdate()); + assertFalse(installedAlreadyLatestIgnoreOld.canAndWantToUpdate()); + + App installedOldNoIgnore = AppProvider.Helper.findByPackageName(r, "installed, old version, no ignore"); + App installedOldIgnoreAll = AppProvider.Helper.findByPackageName(r, "installed, old version, ignore all"); + App installedOldIgnoreLatest = AppProvider.Helper.findByPackageName(r, "installed, old version, ignore latest"); + App installedOldIgnoreNewerNotLatest = AppProvider.Helper.findByPackageName(r, "installed, old version, ignore newer, but not latest"); + + assertTrue(installedOldNoIgnore.canAndWantToUpdate()); + assertFalse(installedOldIgnoreAll.canAndWantToUpdate()); + assertFalse(installedOldIgnoreLatest.canAndWantToUpdate()); + assertTrue(installedOldIgnoreNewerNotLatest.canAndWantToUpdate()); + + Cursor canUpdateCursor = r.query(AppProvider.getCanUpdateUri(), AppProvider.DataColumns.ALL, null, null, null); + assertNotNull(canUpdateCursor); + canUpdateCursor.moveToFirst(); + List canUpdateIds = new ArrayList<>(canUpdateCursor.getCount()); + while (!canUpdateCursor.isAfterLast()) { + canUpdateIds.add(new App(canUpdateCursor).packageName); + canUpdateCursor.moveToNext(); + } + canUpdateCursor.close(); + + String[] expectedUpdateableIds = { + "installed, old version, no ignore", + "installed, old version, ignore newer, but not latest", + }; + + assertContainsOnly(expectedUpdateableIds, canUpdateIds); + } + + @Test + public void testIgnored() { + insertApp("not installed", "not installed"); + insertAndInstallApp("installed, only one version available", 1, 1, false, 0); + insertAndInstallApp("installed, already latest, no ignore", 10, 10, false, 0); + insertAndInstallApp("installed, already latest, ignore all", 10, 10, true, 0); + insertAndInstallApp("installed, already latest, ignore latest", 10, 10, false, 10); + insertAndInstallApp("installed, already latest, ignore old", 10, 10, false, 5); + insertAndInstallApp("installed, old version, no ignore", 5, 10, false, 0); + insertAndInstallApp("installed, old version, ignore all", 5, 10, true, 0); + insertAndInstallApp("installed, old version, ignore latest", 5, 10, false, 10); + insertAndInstallApp("installed, old version, ignore newer, but not latest", 5, 10, false, 8); + + assertResultCount(contentResolver, 10, AppProvider.getContentUri(), PROJ); + + String[] projection = {AppProvider.DataColumns.PACKAGE_NAME}; + List ignoredApps = AppProvider.Helper.findIgnored(context, projection); + + String[] expectedIgnored = { + "installed, already latest, ignore all", + "installed, already latest, ignore latest", + // NOT "installed, already latest, ignore old" - because it + // is should only ignore if "ignored version" is >= suggested + + "installed, old version, ignore all", + "installed, old version, ignore latest", + // NOT "installed, old version, ignore newer, but not latest" + // for the same reason as above. + }; + + assertContainsOnlyIds(ignoredApps, expectedIgnored); + } + + private void assertContainsOnlyIds(List actualApps, String[] expectedIds) { + List actualIds = new ArrayList<>(actualApps.size()); + for (App app : actualApps) { + actualIds.add(app.packageName); + } + assertContainsOnly(actualIds, expectedIds); + } + + @Test + public void testInstalled() { + insertApps(100); + + assertResultCount(contentResolver, 100, AppProvider.getContentUri(), PROJ); + assertResultCount(contentResolver, 0, AppProvider.getInstalledUri(), PROJ); + + for (int i = 10; i < 20; i++) { + InstalledAppTestUtils.install(context, "com.example.test." + i, i, "v1"); + } + + assertResultCount(contentResolver, 10, AppProvider.getInstalledUri(), PROJ); + } + + @Test + public void testInsert() { + + // Start with an empty database... + Cursor cursor = queryAllApps(); + assertNotNull(cursor); + assertEquals(0, cursor.getCount()); + cursor.close(); + + // 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 + // PACKAGE_NAME. + cursor.moveToFirst(); + App app = new App(cursor); + cursor.close(); + assertEquals("org.fdroid.fdroid", app.packageName); + assertEquals("F-Droid", app.name); + + App otherApp = AppProvider.Helper.findByPackageName(context.getContentResolver(), "org.fdroid.fdroid"); + assertNotNull(otherApp); + assertEquals("org.fdroid.fdroid", otherApp.packageName); + assertEquals("F-Droid", otherApp.name); + } + + private Cursor queryAllApps() { + String[] projection = new String[] { + AppProvider.DataColumns._ID, + AppProvider.DataColumns.NAME, + AppProvider.DataColumns.PACKAGE_NAME + }; + return contentResolver.query(AppProvider.getContentUri(), projection, null, null, null); + } + + + // ======================================================================== + // "Categories" + // (at this point) not an additional table, but we treat them sort of + // like they are. That means that if we change the implementation to + // use a separate table in the future, these should still pass. + // ======================================================================== + + @Test + public void testCategoriesSingle() { + insertAppWithCategory("com.dog", "Dog", "Animal"); + insertAppWithCategory("com.rock", "Rock", "Mineral"); + insertAppWithCategory("com.banana", "Banana", "Vegetable"); + + List categories = AppProvider.Helper.categories(context); + String[] expected = new String[] { + context.getResources().getString(R.string.category_Whats_New), + context.getResources().getString(R.string.category_Recently_Updated), + context.getResources().getString(R.string.category_All), + "Animal", + "Mineral", + "Vegetable", + }; + assertContainsOnly(categories, expected); + } + + @Test + public void testCategoriesMultiple() { + insertAppWithCategory("com.rock.dog", "Rock-Dog", "Mineral,Animal"); + insertAppWithCategory("com.dog.rock.apple", "Dog-Rock-Apple", "Animal,Mineral,Vegetable"); + insertAppWithCategory("com.banana.apple", "Banana", "Vegetable,Vegetable"); + + List categories = AppProvider.Helper.categories(context); + String[] expected = new String[] { + context.getResources().getString(R.string.category_Whats_New), + context.getResources().getString(R.string.category_Recently_Updated), + context.getResources().getString(R.string.category_All), + + "Animal", + "Mineral", + "Vegetable", + }; + assertContainsOnly(categories, expected); + + insertAppWithCategory("com.example.game", "Game", + "Running,Shooting,Jumping,Bleh,Sneh,Pleh,Blah,Test category," + + "The quick brown fox jumps over the lazy dog,With apostrophe's"); + + List categoriesLonger = AppProvider.Helper.categories(context); + String[] expectedLonger = new String[] { + context.getResources().getString(R.string.category_Whats_New), + context.getResources().getString(R.string.category_Recently_Updated), + context.getResources().getString(R.string.category_All), + + "Animal", + "Mineral", + "Vegetable", + + "Running", + "Shooting", + "Jumping", + "Bleh", + "Sneh", + "Pleh", + "Blah", + "Test category", + "The quick brown fox jumps over the lazy dog", + "With apostrophe's", + }; + + assertContainsOnly(categoriesLonger, expectedLonger); + } + + // ======================================================================= + // Misc helper functions + // (to be used by any tests in this suite) + // ======================================================================= + + private void insertApp(String id, String name) { + insertApp(id, name, new ContentValues()); + } + + private void insertAppWithCategory(String id, String name, String categories) { + ContentValues values = new ContentValues(1); + values.put(AppProvider.DataColumns.CATEGORIES, categories); + insertApp(id, name, values); + } + + public void insertApp(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(); + + contentResolver.insert(uri, 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/FDroidProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/FDroidProviderTest.java new file mode 100644 index 000000000..5bef2d769 --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/data/FDroidProviderTest.java @@ -0,0 +1,39 @@ +package org.fdroid.fdroid.data; + +import android.content.ContentResolver; +import android.content.ContextWrapper; + +import org.junit.After; +import org.junit.Before; +import org.mockito.AdditionalAnswers; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; +import org.robolectric.shadows.ShadowContentResolver; + +import static org.mockito.Mockito.mock; + +public abstract class FDroidProviderTest { + + protected ShadowContentResolver contentResolver; + protected ContextWrapper context; + + @Before + public final void setupBase() { + contentResolver = Shadows.shadowOf(RuntimeEnvironment.application.getContentResolver()); + final ContentResolver resolver = mock(ContentResolver.class, AdditionalAnswers.delegatesTo(contentResolver)); + context = new ContextWrapper(RuntimeEnvironment.application.getApplicationContext()) { + @Override + public ContentResolver getContentResolver() { + return resolver; + } + }; + ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); + } + + @After + public final void tearDownBase() { + FDroidProvider.clearDbHelperSingleton(); + } + + +} diff --git a/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java index 75a4dcded..1d537de18 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java @@ -6,13 +6,11 @@ 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; @@ -27,21 +25,13 @@ import java.util.Map; @Config(constants = BuildConfig.class, application = Application.class) @RunWith(RobolectricGradleTestRunner.class) -public class InstalledAppProviderTest { - - private ShadowContentResolver contentResolver; +public class InstalledAppProviderTest extends FDroidProviderTest{ @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); diff --git a/app/src/test/java/org/fdroid/fdroid/data/InstalledAppTestUtils.java b/app/src/test/java/org/fdroid/fdroid/data/InstalledAppTestUtils.java new file mode 100644 index 000000000..51fd6e905 --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/data/InstalledAppTestUtils.java @@ -0,0 +1,27 @@ +package org.fdroid.fdroid.data; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; + +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(Context context, + String packageName, + int versionCode, String versionName) { + PackageInfo info = new PackageInfo(); + info.packageName = packageName; + info.versionCode = versionCode; + info.versionName = versionName; + info.applicationInfo = new ApplicationInfo(); + info.applicationInfo.publicSourceDir = "/tmp/mock-location"; + String hashType = "sha256"; + String hash = "00112233445566778899aabbccddeeff"; + InstalledAppProviderService.insertAppIntoDb(context, packageName, info, hashType, hash); + } + +} 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 9c3b2cc4b..97a5481b2 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java @@ -4,8 +4,12 @@ import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; +import junit.framework.AssertionFailedError; + import org.robolectric.shadows.ShadowContentResolver; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static org.junit.Assert.assertEquals; @@ -15,6 +19,66 @@ import static org.junit.Assert.fail; public class ProviderTestUtils { + public static void assertContainsOnly(List actualList, T[] expectedArray) { + List expectedList = new ArrayList<>(expectedArray.length); + Collections.addAll(expectedList, expectedArray); + assertContainsOnly(actualList, expectedList); + } + + public static void assertContainsOnly(T[] actualArray, List expectedList) { + List actualList = new ArrayList<>(actualArray.length); + Collections.addAll(actualList, actualArray); + assertContainsOnly(actualList, expectedList); + } + + public static void assertContainsOnly(T[] actualArray, T[] expectedArray) { + List expectedList = new ArrayList<>(expectedArray.length); + Collections.addAll(expectedList, expectedArray); + assertContainsOnly(actualArray, expectedList); + } + + public static String listToString(List list) { + String string = "["; + for (int i = 0; i < list.size(); i++) { + if (i > 0) { + string += ", "; + } + string += "'" + list.get(i) + "'"; + } + string += "]"; + return string; + } + + public static void assertContainsOnly(List actualList, List expectedContains) { + if (actualList.size() != expectedContains.size()) { + String message = + "List sizes don't match.\n" + + "Expected: " + + listToString(expectedContains) + "\n" + + "Actual: " + + listToString(actualList); + throw new AssertionFailedError(message); + } + for (T required : expectedContains) { + boolean containsRequired = false; + for (T itemInList : actualList) { + if (required.equals(itemInList)) { + containsRequired = true; + break; + } + } + if (!containsRequired) { + String message = + "List doesn't contain \"" + required + "\".\n" + + "Expected: " + + listToString(expectedContains) + "\n" + + "Actual: " + + listToString(actualList); + throw new AssertionFailedError(message); + } + } + } + public static void assertCantDelete(ShadowContentResolver resolver, Uri uri) { try { resolver.delete(uri, null, null); @@ -62,7 +126,11 @@ public class ProviderTestUtils { } public static void assertResultCount(ShadowContentResolver resolver, int expectedCount, Uri uri) { - Cursor cursor = resolver.query(uri, new String[] {}, null, null, null); + assertResultCount(resolver, expectedCount, uri, new String[] {}); + } + + public static void assertResultCount(ShadowContentResolver resolver, int expectedCount, Uri uri, String[] projection) { + Cursor cursor = resolver.query(uri, projection, null, null, null); assertResultCount(expectedCount, cursor); 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 index c62aa465b..57e68624a 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java @@ -11,6 +11,9 @@ import org.robolectric.Shadows; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowContentResolver; +import java.util.ArrayList; +import java.util.List; + import static org.fdroid.fdroid.data.ProviderTestUtils.assertInvalidUri; import static org.fdroid.fdroid.data.ProviderTestUtils.assertValidUri; @@ -64,6 +67,36 @@ public class ProviderUriTests { assertValidUri(resolver, RepoProvider.getContentUri(10000L), projection); assertValidUri(resolver, RepoProvider.allExceptSwapUri(), projection); } + + @Test + public void invalidAppProviderUris() { + ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); + assertInvalidUri(resolver, AppProvider.getAuthority()); + assertInvalidUri(resolver, "blah"); + } + + @Test + public void validAppProviderUris() { + ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); + String[] projection = new String[] { AppProvider.DataColumns._ID }; + assertValidUri(resolver, AppProvider.getContentUri(), "content://org.fdroid.fdroid.data.AppProvider", projection); + assertValidUri(resolver, AppProvider.getSearchUri("'searching!'"), "content://org.fdroid.fdroid.data.AppProvider/search/'searching!'", projection); + assertValidUri(resolver, AppProvider.getSearchUri("/"), "content://org.fdroid.fdroid.data.AppProvider/search/%2F", projection); + assertValidUri(resolver, AppProvider.getSearchUri(""), "content://org.fdroid.fdroid.data.AppProvider", projection); + assertValidUri(resolver, AppProvider.getSearchUri(null), "content://org.fdroid.fdroid.data.AppProvider", projection); + assertValidUri(resolver, AppProvider.getNoApksUri(), "content://org.fdroid.fdroid.data.AppProvider/noApks", projection); + assertValidUri(resolver, AppProvider.getInstalledUri(), "content://org.fdroid.fdroid.data.AppProvider/installed", projection); + assertValidUri(resolver, AppProvider.getCanUpdateUri(), "content://org.fdroid.fdroid.data.AppProvider/canUpdate", projection); + + App app = new App(); + app.packageName = "org.fdroid.fdroid"; + + List apps = new ArrayList<>(1); + apps.add(app); + + assertValidUri(resolver, AppProvider.getContentUri(app), "content://org.fdroid.fdroid.data.AppProvider/org.fdroid.fdroid", projection); + assertValidUri(resolver, AppProvider.getContentUri(apps), "content://org.fdroid.fdroid.data.AppProvider/apps/org.fdroid.fdroid", projection); + assertValidUri(resolver, AppProvider.getContentUri("org.fdroid.fdroid"), "content://org.fdroid.fdroid.data.AppProvider/org.fdroid.fdroid", projection); } } From 839ebebd873ed1aa4f0e0704696f8a42e39694d3 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Mon, 6 Jun 2016 23:50:34 +1000 Subject: [PATCH 04/10] Migrated Apk tests to robolectric. Relatively straightforward port, nothing particularly special here. --- .../org/fdroid/fdroid/RepoXMLHandlerTest.java | 4 +- .../fdroid/data/ApkProviderHelperTest.java | 216 ------- .../fdroid/fdroid/data/ApkProviderTest.java | 334 ----------- .../fdroid/data/BaseApkProviderTest.java | 78 --- .../org/fdroid/fdroid/mock/MockRepoOld.java | 11 + .../org/fdroid/fdroid/data/ApkProvider.java | 4 +- .../fdroid/fdroid/data/ApkProviderTest.java | 535 ++++++++++++++++++ .../fdroid/fdroid/data/ProviderTestUtils.java | 51 ++ .../fdroid/fdroid/data/ProviderUriTests.java | 48 ++ .../java/org/fdroid/fdroid/mock/MockApk.java | 0 .../java/org/fdroid/fdroid/mock/MockApp.java | 0 .../java/org/fdroid/fdroid/mock/MockRepo.java | 0 12 files changed, 649 insertions(+), 632 deletions(-) delete mode 100644 app/src/androidTest/java/org/fdroid/fdroid/data/ApkProviderHelperTest.java delete mode 100644 app/src/androidTest/java/org/fdroid/fdroid/data/ApkProviderTest.java delete mode 100644 app/src/androidTest/java/org/fdroid/fdroid/data/BaseApkProviderTest.java create mode 100644 app/src/androidTest/java/org/fdroid/fdroid/mock/MockRepoOld.java create mode 100644 app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java rename app/src/{androidTest => test}/java/org/fdroid/fdroid/mock/MockApk.java (100%) rename app/src/{androidTest => test}/java/org/fdroid/fdroid/mock/MockApp.java (100%) rename app/src/{androidTest => test}/java/org/fdroid/fdroid/mock/MockRepo.java (100%) 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 From 60451a050f757777748b24367728ac966b28b799 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Mon, 6 Jun 2016 23:56:46 +1000 Subject: [PATCH 05/10] Remove unused test code. Many of the `Mock*` classes are there to deal with idiosyncrosies of the Android SDK, including `final`/package local/`@Hide` annotations/etc. They are no longer required with robolectric tests. --- .../java/mock/MockApplicationInfo.java | 29 --- .../java/mock/MockCategoryResources.java | 27 --- .../java/mock/MockContextEmptyComponents.java | 14 -- .../mock/MockContextSwappableComponents.java | 43 ----- .../java/mock/MockEmptyPackageManager.java | 16 -- .../java/mock/MockEmptyResources.java | 12 -- .../java/mock/MockFDroidResources.java | 37 ---- .../mock/MockInstallablePackageManager.java | 61 ------ .../java/org/fdroid/fdroid/TestUtils.java | 123 ------------ .../fdroid/data/FDroidProviderTestOld.java | 177 ------------------ .../fdroid/data/InstalledAppCacheTest.java | 177 ------------------ 11 files changed, 716 deletions(-) delete mode 100644 app/src/androidTest/java/mock/MockApplicationInfo.java delete mode 100644 app/src/androidTest/java/mock/MockCategoryResources.java delete mode 100644 app/src/androidTest/java/mock/MockContextEmptyComponents.java delete mode 100644 app/src/androidTest/java/mock/MockContextSwappableComponents.java delete mode 100644 app/src/androidTest/java/mock/MockEmptyPackageManager.java delete mode 100644 app/src/androidTest/java/mock/MockEmptyResources.java delete mode 100644 app/src/androidTest/java/mock/MockFDroidResources.java delete mode 100644 app/src/androidTest/java/mock/MockInstallablePackageManager.java delete mode 100644 app/src/androidTest/java/org/fdroid/fdroid/data/FDroidProviderTestOld.java delete mode 100644 app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppCacheTest.java diff --git a/app/src/androidTest/java/mock/MockApplicationInfo.java b/app/src/androidTest/java/mock/MockApplicationInfo.java deleted file mode 100644 index 1ab4592a4..000000000 --- a/app/src/androidTest/java/mock/MockApplicationInfo.java +++ /dev/null @@ -1,29 +0,0 @@ -package mock; - -import android.annotation.SuppressLint; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; - -import java.io.File; -import java.io.IOException; - -@SuppressLint("ParcelCreator") -public class MockApplicationInfo extends ApplicationInfo { - - private final PackageInfo info; - - public MockApplicationInfo(PackageInfo info) { - this.info = info; - try { - this.publicSourceDir = File.createTempFile(info.packageName, "apk").getAbsolutePath(); - } catch (IOException e) { - this.publicSourceDir = "/data/app/" + info.packageName + "-4.apk"; - } - } - - @Override - public CharSequence loadLabel(PackageManager pm) { - return "Mock app: " + info.packageName; - } -} diff --git a/app/src/androidTest/java/mock/MockCategoryResources.java b/app/src/androidTest/java/mock/MockCategoryResources.java deleted file mode 100644 index 0f88ade9f..000000000 --- a/app/src/androidTest/java/mock/MockCategoryResources.java +++ /dev/null @@ -1,27 +0,0 @@ -package mock; - -import android.content.Context; - -import org.fdroid.fdroid.R; - -public class MockCategoryResources extends MockFDroidResources { - - public MockCategoryResources(Context getStringDelegatingContext) { - super(getStringDelegatingContext); - } - - @Override - public String getString(int id) { - switch (id) { - case R.string.category_All: - return "All"; - case R.string.category_Recently_Updated: - return "Recently Updated"; - case R.string.category_Whats_New: - return "Whats New"; - default: - return ""; - } - } - -} diff --git a/app/src/androidTest/java/mock/MockContextEmptyComponents.java b/app/src/androidTest/java/mock/MockContextEmptyComponents.java deleted file mode 100644 index eb962bbe9..000000000 --- a/app/src/androidTest/java/mock/MockContextEmptyComponents.java +++ /dev/null @@ -1,14 +0,0 @@ -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/app/src/androidTest/java/mock/MockContextSwappableComponents.java b/app/src/androidTest/java/mock/MockContextSwappableComponents.java deleted file mode 100644 index 5d94967f2..000000000 --- a/app/src/androidTest/java/mock/MockContextSwappableComponents.java +++ /dev/null @@ -1,43 +0,0 @@ -package mock; - -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.test.mock.MockContentResolver; -import android.test.mock.MockContext; - -public class MockContextSwappableComponents extends MockContext { - - private PackageManager packageManager; - private Resources resources; - private MockContentResolver contentResolver; - - public MockContextSwappableComponents setPackageManager(PackageManager pm) { - packageManager = pm; - return this; - } - - public MockContextSwappableComponents setResources(Resources resources) { - this.resources = resources; - return this; - } - - public MockContextSwappableComponents setContentResolver(MockContentResolver contentResolver) { - this.contentResolver = contentResolver; - return this; - } - - @Override - public PackageManager getPackageManager() { - return packageManager; - } - - @Override - public Resources getResources() { - return resources; - } - - @Override - public MockContentResolver getContentResolver() { - return contentResolver; - } -} diff --git a/app/src/androidTest/java/mock/MockEmptyPackageManager.java b/app/src/androidTest/java/mock/MockEmptyPackageManager.java deleted file mode 100644 index 463864179..000000000 --- a/app/src/androidTest/java/mock/MockEmptyPackageManager.java +++ /dev/null @@ -1,16 +0,0 @@ -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/app/src/androidTest/java/mock/MockEmptyResources.java b/app/src/androidTest/java/mock/MockEmptyResources.java deleted file mode 100644 index fdc06e47f..000000000 --- a/app/src/androidTest/java/mock/MockEmptyResources.java +++ /dev/null @@ -1,12 +0,0 @@ -package mock; - -import android.test.mock.MockResources; - -public class MockEmptyResources extends MockResources { - - @Override - public String getString(int id) { - return ""; - } - -} diff --git a/app/src/androidTest/java/mock/MockFDroidResources.java b/app/src/androidTest/java/mock/MockFDroidResources.java deleted file mode 100644 index 5cb33798c..000000000 --- a/app/src/androidTest/java/mock/MockFDroidResources.java +++ /dev/null @@ -1,37 +0,0 @@ -package mock; - -import android.content.Context; -import android.test.mock.MockResources; - -import org.fdroid.fdroid.R; - -public class MockFDroidResources extends MockResources { - - private final Context getStringDelegatingContext; - - public MockFDroidResources(Context getStringDelegatingContext) { - this.getStringDelegatingContext = getStringDelegatingContext; - } - - @Override - public String getString(int id) { - return getStringDelegatingContext.getString(id); - } - - @Override - public int getInteger(int id) { - switch (id) { - case R.integer.fdroid_repo_inuse: - return 1; - case R.integer.fdroid_archive_inuse: - return 0; - case R.integer.fdroid_repo_priority: - return 10; - case R.integer.fdroid_archive_priority: - return 20; - default: - return 0; - } - } - -} diff --git a/app/src/androidTest/java/mock/MockInstallablePackageManager.java b/app/src/androidTest/java/mock/MockInstallablePackageManager.java deleted file mode 100644 index 1fcef1e86..000000000 --- a/app/src/androidTest/java/mock/MockInstallablePackageManager.java +++ /dev/null @@ -1,61 +0,0 @@ -package mock; - -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.test.mock.MockPackageManager; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -public class MockInstallablePackageManager extends MockPackageManager { - - private final List info = new ArrayList<>(); - - @Override - public List getInstalledPackages(int flags) { - return info; - } - - @Override - public PackageInfo getPackageInfo(String id, int flags) { - for (PackageInfo i : info) { - if (i.packageName.equals(id)) { - return i; - } - } - return null; - } - - public void install(String id, int version, String versionName) { - PackageInfo existing = getPackageInfo(id, 0); - if (existing != null) { - existing.versionCode = version; - existing.versionName = versionName; - } else { - PackageInfo p = new PackageInfo(); - p.packageName = id; - p.versionCode = version; - p.versionName = versionName; - p.applicationInfo = new MockApplicationInfo(p); - p.lastUpdateTime = System.currentTimeMillis(); - info.add(p); - } - } - - @Override - public ApplicationInfo getApplicationInfo(String packageName, int flags) throws NameNotFoundException { - return new MockApplicationInfo(getPackageInfo(packageName, 0)); - } - - public void remove(String id) { - for (Iterator it = info.iterator(); it.hasNext();) { - PackageInfo info = it.next(); - if (info.packageName.equals(id)) { - it.remove(); - return; - } - } - } - -} diff --git a/app/src/androidTest/java/org/fdroid/fdroid/TestUtils.java b/app/src/androidTest/java/org/fdroid/fdroid/TestUtils.java index e51790abb..e30070124 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/TestUtils.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/TestUtils.java @@ -1,144 +1,21 @@ package org.fdroid.fdroid; import android.app.Instrumentation; -import android.content.ContentResolver; -import android.content.ContentValues; import android.content.Context; -import android.net.Uri; import android.os.Environment; import android.support.annotation.Nullable; import android.util.Log; -import junit.framework.AssertionFailedError; - -import org.fdroid.fdroid.data.ApkProvider; -import org.fdroid.fdroid.data.AppProvider; -import org.fdroid.fdroid.data.FDroidProviderTestOld; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; public class TestUtils { private static final String TAG = "TestUtils"; - public static void assertContainsOnly(List actualList, T[] expectedArray) { - List expectedList = new ArrayList<>(expectedArray.length); - Collections.addAll(expectedList, expectedArray); - assertContainsOnly(actualList, expectedList); - } - - public static void assertContainsOnly(T[] actualArray, List expectedList) { - List actualList = new ArrayList<>(actualArray.length); - Collections.addAll(actualList, actualArray); - assertContainsOnly(actualList, expectedList); - } - - public static void assertContainsOnly(T[] actualArray, T[] expectedArray) { - List expectedList = new ArrayList<>(expectedArray.length); - Collections.addAll(expectedList, expectedArray); - assertContainsOnly(actualArray, expectedList); - } - - public static String listToString(List list) { - String string = "["; - for (int i = 0; i < list.size(); i++) { - if (i > 0) { - string += ", "; - } - string += "'" + list.get(i) + "'"; - } - string += "]"; - return string; - } - - public static void assertContainsOnly(List actualList, List expectedContains) { - if (actualList.size() != expectedContains.size()) { - String message = - "List sizes don't match.\n" + - "Expected: " + - listToString(expectedContains) + "\n" + - "Actual: " + - listToString(actualList); - throw new AssertionFailedError(message); - } - for (T required : expectedContains) { - boolean containsRequired = false; - for (T itemInList : actualList) { - if (required.equals(itemInList)) { - containsRequired = true; - break; - } - } - if (!containsRequired) { - String message = - "List doesn't contain \"" + required + "\".\n" + - "Expected: " + - listToString(expectedContains) + "\n" + - "Actual: " + - listToString(actualList); - throw new AssertionFailedError(message); - } - } - } - - public static void insertApp(ContentResolver resolver, String appId, String name) { - insertApp(resolver, appId, name, new ContentValues()); - } - - public static void insertApp(ContentResolver 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(FDroidProviderTestOld providerTest, String id, int versionCode) { - return insertApk(providerTest, id, versionCode, new ContentValues()); - } - - public static Uri insertApk(FDroidProviderTestOld providerTest, 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 providerTest.getMockContentResolver().insert(uri, values); - } - @Nullable public static File copyAssetToDir(Context context, String assetName, File directory) { File tempFile; diff --git a/app/src/androidTest/java/org/fdroid/fdroid/data/FDroidProviderTestOld.java b/app/src/androidTest/java/org/fdroid/fdroid/data/FDroidProviderTestOld.java deleted file mode 100644 index 4767aa4c4..000000000 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/FDroidProviderTestOld.java +++ /dev/null @@ -1,177 +0,0 @@ -package org.fdroid.fdroid.data; - -import android.content.ContentValues; -import android.content.Context; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; -import android.provider.ContactsContract; -import android.test.ProviderTestCase2MockContext; - -import java.util.List; - -import mock.MockContextEmptyComponents; -import mock.MockContextSwappableComponents; -import mock.MockFDroidResources; - -@SuppressWarnings("PMD") // TODO port this to JUnit 4 semantics -public abstract class FDroidProviderTestOld extends ProviderTestCase2MockContext { - - private FDroidProvider[] allProviders = { - new AppProvider(), - new RepoProvider(), - new ApkProvider(), - new InstalledAppProvider(), - }; - - private MockContextSwappableComponents swappableContext; - - public FDroidProviderTestOld(Class providerClass, String providerAuthority) { - super(providerClass, providerAuthority); - } - - protected Resources getMockResources() { - return new MockFDroidResources(getContext()); - } - - @Override - public void setUp() throws Exception { - super.setUp(); - - FDroidProvider.clearDbHelperSingleton(); - - // Instantiate all providers other than the one which was already created by the base class. - // This is because F-Droid providers tend to perform joins onto tables managed by other - // providers, and so we need to be able to insert into those other providers for these - // joins to be tested correctly. - for (FDroidProvider provider : allProviders) { - if (!provider.getName().equals(getProvider().getName())) { - provider.attachInfo(getMockContext(), null); - getMockContentResolver().addProvider(provider.getName(), provider); - } - } - - getSwappableContext().setResources(getMockResources()); - - // The *Provider.Helper.* functions tend to take a Context as their - // first parameter. This context is used to connect to the relevant - // content provider. Thus, we need a context that is able to connect - // to the mock content resolver, in order to reach the provider - // under test. - getSwappableContext().setContentResolver(getMockContentResolver()); - - } - - 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 assertCantDelete(Uri uri) { - try { - getMockContentResolver().delete(uri, null, null); - fail(); - } catch (UnsupportedOperationException e) { - } catch (Exception e) { - fail(); - } - } - - protected void assertCantUpdate(Uri uri) { - try { - getMockContentResolver().update(uri, new ContentValues(), null, null); - fail(); - } catch (UnsupportedOperationException e) { - } catch (Exception e) { - fail(); - } - } - - protected void assertInvalidUri(String uri) { - assertInvalidUri(Uri.parse(uri)); - } - - protected void assertValidUri(String uri) { - assertValidUri(Uri.parse(uri)); - } - - protected void assertInvalidUri(Uri uri) { - try { - // Use getProvdider instead of getContentResolver, because the mock - // content resolver wont result in the provider we are testing, and - // hence we don't get to see how our provider responds to invalid - // uris. - getProvider().query(uri, getMinimalProjection(), null, null, null); - fail(); - } catch (UnsupportedOperationException e) { } - } - - protected void assertValidUri(Uri uri) { - Cursor cursor = getMockContentResolver().query(uri, getMinimalProjection(), null, null, null); - assertNotNull(cursor); - cursor.close(); - } - - protected void assertValidUri(Uri actualUri, String expectedUri) { - assertValidUri(actualUri); - assertEquals(expectedUri, actualUri.toString()); - } - - /** - * 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(); - - protected void assertResultCount(int expectedCount, Uri uri) { - Cursor cursor = getMockContentResolver().query(uri, getMinimalProjection(), null, null, null); - assertResultCount(expectedCount, cursor); - cursor.close(); - } - - protected void assertResultCount(int expectedCount, List items) { - assertNotNull(items); - assertEquals(expectedCount, items.size()); - } - - protected void assertResultCount(int expectedCount, Cursor result) { - assertNotNull(result); - assertEquals(expectedCount, result.getCount()); - } - - protected void assertIsInstalledVersionInDb(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 = getMockContentResolver().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/androidTest/java/org/fdroid/fdroid/data/InstalledAppCacheTest.java b/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppCacheTest.java deleted file mode 100644 index f63d11b90..000000000 --- a/app/src/androidTest/java/org/fdroid/fdroid/data/InstalledAppCacheTest.java +++ /dev/null @@ -1,177 +0,0 @@ -package org.fdroid.fdroid.data; - -import mock.MockInstallablePackageManager; - -/** - * Tests the ability of the {@link InstalledAppCacheUpdater} to stay in sync with - * the {@link android.content.pm.PackageManager}. - * For practical reasons, it extends FDroidProviderTestOld, although there is also a - * separate test for the InstalledAppProvider which tests the CRUD operations in more detail. - */ -@SuppressWarnings("PMD") // TODO port this to JUnit 4 semantics -public class InstalledAppCacheTest extends FDroidProviderTestOld { - - private MockInstallablePackageManager packageManager; - - public InstalledAppCacheTest() { - super(InstalledAppProvider.class, InstalledAppProvider.getAuthority()); - } - - @Override - public void setUp() throws Exception { - super.setUp(); - packageManager = new MockInstallablePackageManager(); - getSwappableContext().setPackageManager(packageManager); - } - - @Override - protected String[] getMinimalProjection() { - return new String[] { - InstalledAppProvider.DataColumns.PACKAGE_NAME, - }; - } - - public void install(String appId, int versionCode, String versionName) { - packageManager.install(appId, versionCode, versionName); - } - - public void remove(String appId) { - packageManager.remove(appId); - } - -/* TODO fix me - public void testFromEmptyCache() { - assertResultCount(0, InstalledAppProvider.getContentUri()); - for (int i = 1; i <= 15; i ++) { - install("com.example.app" + i, 200, "2.0"); - } - InstalledAppCacheUpdater.updateInForeground(getMockContext()); - - String[] expectedInstalledIds = { - "com.example.app1", - "com.example.app2", - "com.example.app3", - "com.example.app4", - "com.example.app5", - "com.example.app6", - "com.example.app7", - "com.example.app8", - "com.example.app9", - "com.example.app10", - "com.example.app11", - "com.example.app12", - "com.example.app13", - "com.example.app14", - "com.example.app15", - }; - - TestUtils.assertContainsOnly(getInstalledAppIdsFromProvider(), expectedInstalledIds); - } - - private String[] getInstalledAppIdsFromProvider() { - Uri uri = InstalledAppProvider.getContentUri(); - String[] projection = { InstalledAppProvider.DataColumns.PACKAGE_NAME }; - Cursor result = getMockContext().getContentResolver().query(uri, projection, null, null, null); - if (result == null) { - return new String[0]; - } - - String[] installedAppIds = new String[result.getCount()]; - result.moveToFirst(); - int i = 0; - while (!result.isAfterLast()) { - installedAppIds[i] = result.getString(result.getColumnIndex(InstalledAppProvider.DataColumns.PACKAGE_NAME)); - result.moveToNext(); - i ++; - } - result.close(); - return installedAppIds; - } - - public void testAppsAdded() { - assertResultCount(0, InstalledAppProvider.getContentUri()); - - install("com.example.app1", 1, "v1"); - install("com.example.app2", 1, "v1"); - install("com.example.app3", 1, "v1"); - InstalledAppCacheUpdater.updateInForeground(getMockContext()); - - assertResultCount(3, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb("com.example.app1", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app2", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app3", 1, "v1"); - - install("com.example.app10", 1, "v1"); - install("com.example.app11", 1, "v1"); - install("com.example.app12", 1, "v1"); - InstalledAppCacheUpdater.updateInForeground(getMockContext()); - - assertResultCount(6, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb("com.example.app10", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app11", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app12", 1, "v1"); - } - - public void testAppsRemoved() { - install("com.example.app1", 1, "v1"); - install("com.example.app2", 1, "v1"); - install("com.example.app3", 1, "v1"); - InstalledAppCacheUpdater.updateInForeground(getMockContext()); - - assertResultCount(3, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb("com.example.app1", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app2", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app3", 1, "v1"); - - remove("com.example.app2"); - InstalledAppCacheUpdater.updateInForeground(getMockContext()); - - assertResultCount(2, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb("com.example.app1", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app3", 1, "v1"); - } - - public void testAppsUpdated() { - install("com.example.app1", 1, "v1"); - install("com.example.app2", 1, "v1"); - InstalledAppCacheUpdater.updateInForeground(getMockContext()); - - assertResultCount(2, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb("com.example.app1", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app2", 1, "v1"); - - install("com.example.app2", 20, "v2.0"); - InstalledAppCacheUpdater.updateInForeground(getMockContext()); - - assertResultCount(2, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb("com.example.app1", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app2", 20, "v2.0"); - } - - public void testAppsAddedRemovedAndUpdated() { - install("com.example.app1", 1, "v1"); - install("com.example.app2", 1, "v1"); - install("com.example.app3", 1, "v1"); - install("com.example.app4", 1, "v1"); - InstalledAppCacheUpdater.updateInForeground(getMockContext()); - - assertResultCount(4, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb("com.example.app1", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app2", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app3", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app4", 1, "v1"); - - install("com.example.app1", 13, "v1.3"); - remove("com.example.app2"); - remove("com.example.app3"); - install("com.example.app10", 1, "v1"); - InstalledAppCacheUpdater.updateInForeground(getMockContext()); - - assertResultCount(3, InstalledAppProvider.getContentUri()); - assertIsInstalledVersionInDb("com.example.app1", 13, "v1.3"); - assertIsInstalledVersionInDb("com.example.app4", 1, "v1"); - assertIsInstalledVersionInDb("com.example.app10", 1, "v1"); - - } -*/ -} From 660ebc5ec87a2702b5d68a07fd33c1fb1783e84f Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Tue, 7 Jun 2016 00:17:09 +1000 Subject: [PATCH 06/10] Migrated to robolectric. This required changing the SAX parser to be namespace aware, as it seems that is the default in Android, but not the default in the OpenJDK. --- .../org/fdroid/fdroid/RepoUpdaterTest.java | 175 ------------------ .../org/fdroid/fdroid/mock/MockRepoOld.java | 11 -- .../org/fdroid/fdroid/RepoXMLHandlerTest.java | 20 +- .../assets => test/resources}/largeRepo.xml | 0 .../assets => test/resources}/mediumRepo.xml | 0 .../assets => test/resources}/simpleIndex.xml | 0 .../assets => test/resources}/smallRepo.xml | 0 7 files changed, 11 insertions(+), 195 deletions(-) delete mode 100644 app/src/androidTest/java/org/fdroid/fdroid/RepoUpdaterTest.java delete mode 100644 app/src/androidTest/java/org/fdroid/fdroid/mock/MockRepoOld.java rename app/src/{androidTest => test}/java/org/fdroid/fdroid/RepoXMLHandlerTest.java (99%) rename app/src/{androidTest/assets => test/resources}/largeRepo.xml (100%) rename app/src/{androidTest/assets => test/resources}/mediumRepo.xml (100%) rename app/src/{androidTest/assets => test/resources}/simpleIndex.xml (100%) rename app/src/{androidTest/assets => test/resources}/smallRepo.xml (100%) diff --git a/app/src/androidTest/java/org/fdroid/fdroid/RepoUpdaterTest.java b/app/src/androidTest/java/org/fdroid/fdroid/RepoUpdaterTest.java deleted file mode 100644 index ac77cbd60..000000000 --- a/app/src/androidTest/java/org/fdroid/fdroid/RepoUpdaterTest.java +++ /dev/null @@ -1,175 +0,0 @@ - -package org.fdroid.fdroid; - -import android.app.Instrumentation; -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; -import android.util.Log; - -import org.fdroid.fdroid.RepoUpdater.UpdateException; -import org.fdroid.fdroid.data.Repo; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; - -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; - -@RunWith(AndroidJUnit4.class) -public class RepoUpdaterTest { - public static final String TAG = "RepoUpdaterTest"; - - private Context context; - private Repo repo; - private RepoUpdater repoUpdater; - private File testFilesDir; - - String simpleIndexSigningCert = "308201ee30820157a0030201020204300d845b300d06092a864886f70d01010b0500302a3110300e060355040b1307462d44726f6964311630140603550403130d70616c6174736368696e6b656e301e170d3134303432373030303633315a170d3431303931323030303633315a302a3110300e060355040b1307462d44726f6964311630140603550403130d70616c6174736368696e6b656e30819f300d06092a864886f70d010101050003818d0030818902818100a439472e4b6d01141bfc94ecfe131c7c728fdda670bb14c57ca60bd1c38a8b8bc0879d22a0a2d0bc0d6fdd4cb98d1d607c2caefbe250a0bd0322aedeb365caf9b236992fac13e6675d3184a6c7c6f07f73410209e399a9da8d5d7512bbd870508eebacff8b57c3852457419434d34701ccbf692267cbc3f42f1c5d1e23762d790203010001a321301f301d0603551d0e041604140b1840691dab909746fde4bfe28207d1cae15786300d06092a864886f70d01010b05000381810062424c928ffd1b6fd419b44daafef01ca982e09341f7077fb865905087aeac882534b3bd679b51fdfb98892cef38b63131c567ed26c9d5d9163afc775ac98ad88c405d211d6187bde0b0d236381cc574ba06ef9080721a92ae5a103a7301b2c397eecc141cc850dd3e123813ebc41c59d31ddbcb6e984168280c53272f6a442b"; - - /** - * Getting a writeable dir during the tests seems to be a flaky prospect. - */ - private boolean canWrite() { - if (testFilesDir.canWrite()) { - return true; - } else { - Log.e(TAG, "ERROR: " + testFilesDir + " is not writable, skipping test"); - return false; - } - } - - @Before - public void setUp() { - Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - context = instrumentation.getContext(); - testFilesDir = TestUtils.getWriteableDir(instrumentation); - repo = new Repo(); - repo.address = "https://fake.url/fdroid/repo"; - repo.signingCertificate = this.simpleIndexSigningCert; - } - - @Test - public void testExtractIndexFromJar() { - assumeTrue(canWrite()); - File simpleIndexJar = TestUtils.copyAssetToDir(context, "simpleIndex.jar", testFilesDir); - repoUpdater = new RepoUpdater(context, repo); - - // these are supposed to succeed - try { - repoUpdater.processDownloadedFile(simpleIndexJar); - } catch (UpdateException e) { - e.printStackTrace(); - fail(); - } - } - - @Test(expected = UpdateException.class) - public void testExtractIndexFromOutdatedJar() throws UpdateException { - assumeTrue(canWrite()); - File simpleIndexJar = TestUtils.copyAssetToDir(context, "simpleIndex.jar", testFilesDir); - repo.version = 10; - repo.timestamp = System.currentTimeMillis() / 1000L; - repoUpdater = new RepoUpdater(context, repo); - - // these are supposed to fail - repoUpdater.processDownloadedFile(simpleIndexJar); - fail(); - } - - @Test(expected = UpdateException.class) - public void testExtractIndexFromJarWithoutSignatureJar() throws UpdateException { - assumeTrue(canWrite()); - // this is supposed to fail - File jarFile = TestUtils.copyAssetToDir(context, "simpleIndexWithoutSignature.jar", testFilesDir); - repoUpdater = new RepoUpdater(context, repo); - repoUpdater.processDownloadedFile(jarFile); - fail(); - } - - @Test - public void testExtractIndexFromJarWithCorruptedManifestJar() { - assumeTrue(canWrite()); - // this is supposed to fail - try { - File jarFile = TestUtils.copyAssetToDir(context, "simpleIndexWithCorruptedManifest.jar", testFilesDir); - repoUpdater = new RepoUpdater(context, repo); - repoUpdater.processDownloadedFile(jarFile); - fail(); - } catch (UpdateException e) { - e.printStackTrace(); - fail(); - } catch (SecurityException e) { - // success! - } - } - - @Test - public void testExtractIndexFromJarWithCorruptedSignature() { - assumeTrue(canWrite()); - // this is supposed to fail - try { - File jarFile = TestUtils.copyAssetToDir(context, "simpleIndexWithCorruptedSignature.jar", testFilesDir); - repoUpdater = new RepoUpdater(context, repo); - repoUpdater.processDownloadedFile(jarFile); - fail(); - } catch (UpdateException e) { - e.printStackTrace(); - fail(); - } catch (SecurityException e) { - // success! - } - } - - @Test - public void testExtractIndexFromJarWithCorruptedCertificate() { - assumeTrue(canWrite()); - // this is supposed to fail - try { - File jarFile = TestUtils.copyAssetToDir(context, "simpleIndexWithCorruptedCertificate.jar", testFilesDir); - repoUpdater = new RepoUpdater(context, repo); - repoUpdater.processDownloadedFile(jarFile); - fail(); - } catch (UpdateException e) { - e.printStackTrace(); - fail(); - } catch (SecurityException e) { - // success! - } - } - - @Test - public void testExtractIndexFromJarWithCorruptedEverything() { - assumeTrue(canWrite()); - // this is supposed to fail - try { - File jarFile = TestUtils.copyAssetToDir(context, "simpleIndexWithCorruptedEverything.jar", testFilesDir); - repoUpdater = new RepoUpdater(context, repo); - repoUpdater.processDownloadedFile(jarFile); - fail(); - } catch (UpdateException e) { - e.printStackTrace(); - fail(); - } catch (SecurityException e) { - // success! - } - } - - @Test - public void testExtractIndexFromMasterKeyIndexJar() { - assumeTrue(canWrite()); - // this is supposed to fail - try { - File jarFile = TestUtils.copyAssetToDir(context, "masterKeyIndex.jar", testFilesDir); - repoUpdater = new RepoUpdater(context, repo); - repoUpdater.processDownloadedFile(jarFile); - fail(); //NOPMD - } catch (UpdateException e) { - // success! - } catch (SecurityException e) { - // success! - } - } -} diff --git a/app/src/androidTest/java/org/fdroid/fdroid/mock/MockRepoOld.java b/app/src/androidTest/java/org/fdroid/fdroid/mock/MockRepoOld.java deleted file mode 100644 index 43fec57cf..000000000 --- a/app/src/androidTest/java/org/fdroid/fdroid/mock/MockRepoOld.java +++ /dev/null @@ -1,11 +0,0 @@ -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/androidTest/java/org/fdroid/fdroid/RepoXMLHandlerTest.java b/app/src/test/java/org/fdroid/fdroid/RepoXMLHandlerTest.java similarity index 99% rename from app/src/androidTest/java/org/fdroid/fdroid/RepoXMLHandlerTest.java rename to app/src/test/java/org/fdroid/fdroid/RepoXMLHandlerTest.java index d5bc8c74f..e354320e8 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/RepoXMLHandlerTest.java +++ b/app/src/test/java/org/fdroid/fdroid/RepoXMLHandlerTest.java @@ -2,16 +2,17 @@ package org.fdroid.fdroid; import android.support.annotation.NonNull; -import android.support.test.runner.AndroidJUnit4; import android.text.TextUtils; 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.MockRepoOld; +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 org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; @@ -31,7 +32,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; -@RunWith(AndroidJUnit4.class) +@Config(constants = BuildConfig.class) +@RunWith(RobolectricGradleTestRunner.class) public class RepoXMLHandlerTest { private static final String TAG = "RepoXMLHandlerTest"; @@ -673,16 +675,16 @@ public class RepoXMLHandlerTest { @NonNull private RepoDetails getFromFile(String indexFilename) { - SAXParser parser; try { - parser = SAXParserFactory.newInstance().newSAXParser(); + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + SAXParser parser = factory.newSAXParser(); XMLReader reader = parser.getXMLReader(); RepoDetails repoDetails = new RepoDetails(); - RepoXMLHandler handler = new RepoXMLHandler(new MockRepoOld(100), repoDetails); + RepoXMLHandler handler = new RepoXMLHandler(new MockRepo(100), repoDetails); reader.setContentHandler(handler); - String resName = "assets/" + indexFilename; - Log.i(TAG, "test file: " + getClass().getClassLoader().getResource(resName)); - InputStream input = getClass().getClassLoader().getResourceAsStream(resName); + Log.i(TAG, "test file: " + getClass().getClassLoader().getResource(indexFilename)); + InputStream input = getClass().getClassLoader().getResourceAsStream(indexFilename); InputSource is = new InputSource(new BufferedInputStream(input)); reader.parse(is); return repoDetails; diff --git a/app/src/androidTest/assets/largeRepo.xml b/app/src/test/resources/largeRepo.xml similarity index 100% rename from app/src/androidTest/assets/largeRepo.xml rename to app/src/test/resources/largeRepo.xml diff --git a/app/src/androidTest/assets/mediumRepo.xml b/app/src/test/resources/mediumRepo.xml similarity index 100% rename from app/src/androidTest/assets/mediumRepo.xml rename to app/src/test/resources/mediumRepo.xml diff --git a/app/src/androidTest/assets/simpleIndex.xml b/app/src/test/resources/simpleIndex.xml similarity index 100% rename from app/src/androidTest/assets/simpleIndex.xml rename to app/src/test/resources/simpleIndex.xml diff --git a/app/src/androidTest/assets/smallRepo.xml b/app/src/test/resources/smallRepo.xml similarity index 100% rename from app/src/androidTest/assets/smallRepo.xml rename to app/src/test/resources/smallRepo.xml From 253900e927389bbdfc2a960ed2f78962db445dc5 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Wed, 8 Jun 2016 07:39:28 +1000 Subject: [PATCH 07/10] Multi-repo updater ported to robolectric. The tests pass, but there is a lingering message that gets logged: ``` Jun 08, 2016 7:31:13 AM com.almworks.sqlite4java.Internal log WARNING: [sqlite] [DETACH DATABASE temp_update_db]DB[1][C]: exception when clearing com.almworks.sqlite4java.SQLiteException: [1] DB[1] reset [no such database: temp_update_db] at com.almworks.sqlite4java.SQLiteConnection.throwResult(SQLiteConnection.java:1309) at com.almworks.sqlite4java.SQLiteConnection.throwResult(SQLiteConnection.java:1282) at com.almworks.sqlite4java.SQLiteConnection.cacheStatementHandle(SQLiteConnection.java:1211) at com.almworks.sqlite4java.SQLiteConnection.access$900(SQLiteConnection.java:54) at com.almworks.sqlite4java.SQLiteConnection$CachedController.dispose(SQLiteConnection.java:1606) at com.almworks.sqlite4java.SQLiteStatement.dispose(SQLiteStatement.java:187) at org.robolectric.shadows.ShadowSQLiteConnection$Connections$4.call(ShadowSQLiteConnection.java:421) at org.robolectric.shadows.ShadowSQLiteConnection$Connections$6.call(ShadowSQLiteConnection.java:449) at org.robolectric.shadows.ShadowSQLiteConnection$Connections$6.call(ShadowSQLiteConnection.java:443) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) ``` The `temp_update_db` is the one used for repo updates, but I thought that it correctly gets dropped/detached by the `TempAppProvider` when required. In fact, given the nature of the error message (no such database: temp_update_db), that hints at the fact that it is indeed dropped. I'm struggling to figure out what causes this, but it should not be harmful to the running of the tests. If a test actually fails, then it is picked up correctly by JUnit. --- .../org/fdroid/fdroid/FileCompatTest.java | 4 +- .../{TestUtils.java => TestUtilsOld.java} | 4 +- .../java/org/fdroid/fdroid/UtilsTest.java | 2 +- .../java/org/fdroid/fdroid/Preferences.java | 11 ++ .../java/org/fdroid/fdroid/RepoUpdater.java | 4 +- .../fdroid/fdroid/MultiRepoUpdaterTest.java | 126 +++++------------- .../java/org/fdroid/fdroid/TestUtils.java | 38 ++++++ .../resources}/multiRepo.archive.jar | Bin .../resources}/multiRepo.conflicting.jar | Bin .../resources}/multiRepo.normal.jar | Bin 10 files changed, 94 insertions(+), 95 deletions(-) rename app/src/androidTest/java/org/fdroid/fdroid/{TestUtils.java => TestUtilsOld.java} (96%) rename app/src/{androidTest => test}/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java (82%) create mode 100644 app/src/test/java/org/fdroid/fdroid/TestUtils.java rename app/src/{androidTest/assets => test/resources}/multiRepo.archive.jar (100%) rename app/src/{androidTest/assets => test/resources}/multiRepo.conflicting.jar (100%) rename app/src/{androidTest/assets => test/resources}/multiRepo.normal.jar (100%) diff --git a/app/src/androidTest/java/org/fdroid/fdroid/FileCompatTest.java b/app/src/androidTest/java/org/fdroid/fdroid/FileCompatTest.java index 819d8539b..ff7e86378 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/FileCompatTest.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/FileCompatTest.java @@ -36,8 +36,8 @@ public class FileCompatTest { @Before public void setUp() { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - File dir = TestUtils.getWriteableDir(instrumentation); - sourceFile = SanitizedFile.knownSanitized(TestUtils.copyAssetToDir(instrumentation.getContext(), "simpleIndex.jar", dir)); + File dir = TestUtilsOld.getWriteableDir(instrumentation); + sourceFile = SanitizedFile.knownSanitized(TestUtilsOld.copyAssetToDir(instrumentation.getContext(), "simpleIndex.jar", dir)); destFile = new SanitizedFile(dir, "dest-" + UUID.randomUUID() + ".testproduct"); assertFalse(destFile.exists()); assertTrue(sourceFile.getAbsolutePath() + " should exist.", sourceFile.exists()); diff --git a/app/src/androidTest/java/org/fdroid/fdroid/TestUtils.java b/app/src/androidTest/java/org/fdroid/fdroid/TestUtilsOld.java similarity index 96% rename from app/src/androidTest/java/org/fdroid/fdroid/TestUtils.java rename to app/src/androidTest/java/org/fdroid/fdroid/TestUtilsOld.java index e30070124..df8144457 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/TestUtils.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/TestUtilsOld.java @@ -12,9 +12,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -public class TestUtils { +public class TestUtilsOld { - private static final String TAG = "TestUtils"; + private static final String TAG = "TestUtilsOld"; @Nullable public static File copyAssetToDir(Context context, String assetName, File directory) { diff --git a/app/src/androidTest/java/org/fdroid/fdroid/UtilsTest.java b/app/src/androidTest/java/org/fdroid/fdroid/UtilsTest.java index 96f701328..3aadf8849 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/UtilsTest.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/UtilsTest.java @@ -146,7 +146,7 @@ public class UtilsTest { @Test public void testClearOldFiles() throws IOException, InterruptedException { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - File dir = new File(TestUtils.getWriteableDir(instrumentation), "clearOldFiles"); + File dir = new File(TestUtilsOld.getWriteableDir(instrumentation), "clearOldFiles"); FileUtils.deleteQuietly(dir); dir.mkdirs(); assertTrue(dir.isDirectory()); diff --git a/app/src/main/java/org/fdroid/fdroid/Preferences.java b/app/src/main/java/org/fdroid/fdroid/Preferences.java index 20144be5f..ff521293e 100644 --- a/app/src/main/java/org/fdroid/fdroid/Preferences.java +++ b/app/src/main/java/org/fdroid/fdroid/Preferences.java @@ -372,6 +372,17 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh private static Preferences instance; + /** + * Should only be used for unit testing, whereby separate tests are required to invoke `setup()`. + * The reason we don't instead ask for the singleton to be lazily loaded in the {@link Preferences#get()} + * method is because that would require each call to that method to require a {@link Context}. + * While it is likely that most places asking for preferences have access to a {@link Context}, + * it is a minor convenience to be able to ask for preferences without. + */ + public static void clearSingletonForTesting() { + instance = null; + } + /** * Needs to be setup before anything else tries to access it. */ diff --git a/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java b/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java index 683f39235..d72f83210 100644 --- a/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java +++ b/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java @@ -190,7 +190,9 @@ public class RepoUpdater { processXmlProgressListener, new URL(repo.address), (int) indexEntry.getSize()); // Process the index... - final SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + final SAXParser parser = factory.newSAXParser(); final XMLReader reader = parser.getXMLReader(); final RepoXMLHandler repoXMLHandler = new RepoXMLHandler(repo, createIndexReceiver()); reader.setContentHandler(repoXMLHandler); diff --git a/app/src/androidTest/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java b/app/src/test/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java similarity index 82% rename from app/src/androidTest/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java rename to app/src/test/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java index 9fc9c9443..ac77a566d 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java +++ b/app/src/test/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java @@ -1,16 +1,9 @@ package org.fdroid.fdroid; -import android.content.ContentProvider; -import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; -import android.content.res.AssetManager; -import android.content.res.Resources; import android.support.annotation.NonNull; -import android.test.InstrumentationTestCase; -import android.test.RenamingDelegatingContext; -import android.test.mock.MockContentResolver; import android.text.TextUtils; import android.util.Log; @@ -18,29 +11,37 @@ import org.fdroid.fdroid.RepoUpdater.UpdateException; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.ApkProvider; import org.fdroid.fdroid.data.AppProvider; -import org.fdroid.fdroid.data.FDroidProvider; +import org.fdroid.fdroid.data.FDroidProviderTest; import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoProvider; -import org.fdroid.fdroid.data.TempApkProvider; -import org.fdroid.fdroid.data.TempAppProvider; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.annotation.Config; import java.io.File; import java.util.List; import java.util.UUID; -@SuppressWarnings("PMD") // TODO port this to JUnit 4 semantics -public class MultiRepoUpdaterTest extends InstrumentationTestCase { +import static org.fdroid.fdroid.TestUtils.copyResourceToTempFile; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@Config(constants = BuildConfig.class) +@RunWith(RobolectricGradleTestRunner.class) +public class MultiRepoUpdaterTest extends FDroidProviderTest { private static final String TAG = "MultiRepoUpdaterTest"; private static final String REPO_MAIN = "Test F-Droid repo"; private static final String REPO_ARCHIVE = "Test F-Droid repo (Archive)"; private static final String REPO_CONFLICTING = "Test F-Droid repo with different apps"; - private Context context; private RepoUpdater conflictingRepoUpdater; private RepoUpdater mainRepoUpdater; private RepoUpdater archiveRepoUpdater; - private File testFilesDir; private static final String PUB_KEY = "3082050b308202f3a003020102020420d8f212300d06092a864886f70d01010b050030363110300e0603" + @@ -75,73 +76,8 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase { "98f848e0dbfce5a0f2da0198c47e6935a47fda12c518ef45adfb66ddf5aebaab13948a66c004b8592d22" + "e8af60597c4ae2977977cf61dc715a572e241ae717cafdb4f71781943945ac52e0f50b"; - public class TestContext extends RenamingDelegatingContext { - - private MockContentResolver resolver; - - public TestContext() { - super(getInstrumentation().getTargetContext(), "test."); - - resolver = new MockContentResolver(); - resolver.addProvider(AppProvider.getAuthority(), prepareProvider(new AppProvider())); - resolver.addProvider(ApkProvider.getAuthority(), prepareProvider(new ApkProvider())); - resolver.addProvider(RepoProvider.getAuthority(), prepareProvider(new RepoProvider())); - resolver.addProvider(TempAppProvider.getAuthority(), prepareProvider(new TempAppProvider())); - resolver.addProvider(TempApkProvider.getAuthority(), prepareProvider(new TempApkProvider())); - } - - private ContentProvider prepareProvider(ContentProvider provider) { - provider.attachInfo(this, null); - provider.onCreate(); - return provider; - } - - @Override - public File getFilesDir() { - return getInstrumentation().getTargetContext().getFilesDir(); - } - - /** - * String resources used during testing (e.g. when bootstraping the database) are from - * the real org.fdroid.fdroid app, not the test org.fdroid.fdroid.test app. - */ - @Override - public Resources getResources() { - return getInstrumentation().getTargetContext().getResources(); - } - - @Override - public ContentResolver getContentResolver() { - return resolver; - } - - @Override - public AssetManager getAssets() { - return getInstrumentation().getContext().getAssets(); - } - - @Override - public File getDatabasePath(String name) { - return new File(getInstrumentation().getContext().getFilesDir(), "fdroid_test.db"); - } - - @Override - public Context getApplicationContext() { - // Used by the DBHelper singleton instance. - return this; - } - } - - @Override - public void setUp() throws Exception { - super.setUp(); - - FDroidProvider.clearDbHelperSingleton(); - - context = new TestContext(); - - testFilesDir = TestUtils.getWriteableDir(getInstrumentation()); - + @Before + public void setup() throws Exception { // On a fresh database install, there will be F-Droid + GP repos, including their Archive // repos that we are not interested in. RepoProvider.Helper.remove(context, 1); @@ -152,6 +88,13 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase { conflictingRepoUpdater = createUpdater(REPO_CONFLICTING, context); mainRepoUpdater = createUpdater(REPO_MAIN, context); archiveRepoUpdater = createUpdater(REPO_ARCHIVE, context); + + Preferences.setup(context); + } + + @After + public void tearDown() { + Preferences.clearSingletonForTesting(); } /** @@ -169,9 +112,6 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase { assertConflictingRepo(repos); } - /** - * - */ private void assertSomewhatAcceptable() { Log.i(TAG, "Asserting at least one versions of each .apk is in index."); List repos = RepoProvider.Helper.all(context); @@ -361,6 +301,7 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase { */ + @Test public void testAcceptableConflictingThenMainThenArchive() throws UpdateException { assertEmpty(); if (updateConflicting() && updateMain() && updateArchive()) { @@ -368,6 +309,7 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase { } } + @Test public void testAcceptableConflictingThenArchiveThenMain() throws UpdateException { assertEmpty(); if (updateConflicting() && updateArchive() && updateMain()) { @@ -375,6 +317,7 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase { } } + @Test public void testAcceptableArchiveThenMainThenConflicting() throws UpdateException { assertEmpty(); if (updateArchive() && updateMain() && updateConflicting()) { @@ -382,6 +325,7 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase { } } + @Test public void testAcceptableArchiveThenConflictingThenMain() throws UpdateException { assertEmpty(); if (updateArchive() && updateConflicting() && updateMain()) { @@ -389,6 +333,7 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase { } } + @Test public void testAcceptableMainThenArchiveThenConflicting() throws UpdateException { assertEmpty(); if (updateMain() && updateArchive() && updateConflicting()) { @@ -396,6 +341,7 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase { } } + @Test public void testAcceptableMainThenConflictingThenArchive() throws UpdateException { assertEmpty(); if (updateMain() && updateConflicting() && updateArchive()) { @@ -434,12 +380,14 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase { } private boolean updateRepo(RepoUpdater updater, String indexJarPath) throws UpdateException { - if (!testFilesDir.canWrite()) { - return false; + File indexJar = copyResourceToTempFile(indexJarPath); + try { + updater.processDownloadedFile(indexJar); + } finally { + if (indexJar != null && indexJar.exists()) { + indexJar.delete(); + } } - - File indexJar = TestUtils.copyAssetToDir(context, indexJarPath, testFilesDir); - updater.processDownloadedFile(indexJar); return true; } diff --git a/app/src/test/java/org/fdroid/fdroid/TestUtils.java b/app/src/test/java/org/fdroid/fdroid/TestUtils.java new file mode 100644 index 000000000..598c60a3b --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/TestUtils.java @@ -0,0 +1,38 @@ +package org.fdroid.fdroid; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import static org.junit.Assert.fail; + +public class TestUtils { + + private static final String TAG = "TestUtils"; + + public static File copyResourceToTempFile(String resourceName) { + File tempFile = null; + InputStream input = null; + OutputStream output = null; + try { + tempFile = File.createTempFile(resourceName + "-", ".testasset"); + input = TestUtils.class.getClassLoader().getResourceAsStream(resourceName); + output = new FileOutputStream(tempFile); + Utils.copy(input, output); + } catch (IOException e) { + e.printStackTrace(); + if (tempFile != null && tempFile.exists()) { + tempFile.delete(); + } + fail(); + return null; + } finally { + Utils.closeQuietly(output); + Utils.closeQuietly(input); + } + return tempFile; + } + +} diff --git a/app/src/androidTest/assets/multiRepo.archive.jar b/app/src/test/resources/multiRepo.archive.jar similarity index 100% rename from app/src/androidTest/assets/multiRepo.archive.jar rename to app/src/test/resources/multiRepo.archive.jar diff --git a/app/src/androidTest/assets/multiRepo.conflicting.jar b/app/src/test/resources/multiRepo.conflicting.jar similarity index 100% rename from app/src/androidTest/assets/multiRepo.conflicting.jar rename to app/src/test/resources/multiRepo.conflicting.jar diff --git a/app/src/androidTest/assets/multiRepo.normal.jar b/app/src/test/resources/multiRepo.normal.jar similarity index 100% rename from app/src/androidTest/assets/multiRepo.normal.jar rename to app/src/test/resources/multiRepo.normal.jar From 4e73d1e5e6c50815bec692493e2d6ab7c525ae98 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Thu, 9 Jun 2016 09:55:12 +1000 Subject: [PATCH 08/10] Finish porting tests to Robolectric, and appease PMD. To appease PMD, we now have a three rulesets in `config/pmd/*.xml`: * `rules.xml`: The bulk of the rules, used by both main and test code. * `rules-main.xml`: Rules specific to the andoid client code. * `rules-test.xml`: Rules specific to test code. The rationale is because checkstyle by default checks for "too many static imports", which is a fair call. However in JUnit4 code, it is common to import many `assert*` static methods. --- app/build.gradle | 15 +- app/src/androidTest/assets/simpleIndex.jar | Bin 2412 -> 0 bytes .../test/ProviderTestCase2MockContext.java | 257 ------------------ .../org/fdroid/fdroid/FileCompatTest.java | 86 ++++-- .../java/org/fdroid/fdroid/TestUtilsOld.java | 73 ----- .../AcceptableMultiRepoUpdaterTest.java | 82 ++++++ .../fdroid/fdroid/MultiRepoUpdaterTest.java | 237 ++-------------- .../fdroid/ProperMultiRepoUpdaterTest.java | 155 +++++++++++ .../java/org/fdroid/fdroid/TestUtils.java | 6 +- .../java/org/fdroid/fdroid/UtilsTest.java | 28 +- .../fdroid/fdroid/data/ApkProviderTest.java | 109 ++++---- .../fdroid/fdroid/data/AppProviderTest.java | 23 +- .../fdroid/data/InstalledAppProviderTest.java | 25 +- .../fdroid/fdroid/data/ProviderUriTests.java | 24 +- .../assets => test/resources}/README.md | 0 .../resources}/masterKeyIndex.jar | Bin app/src/test/resources/simpleIndex.jar | Bin 0 -> 2412 bytes .../simpleIndexWithCorruptedCertificate.jar | Bin .../simpleIndexWithCorruptedEverything.jar | Bin .../simpleIndexWithCorruptedManifest.jar | Bin .../simpleIndexWithCorruptedSignature.jar | Bin .../simpleIndexWithoutSignature.jar | Bin config/pmd/rules-main.xml | 9 + config/pmd/rules-test.xml | 11 + config/pmd/rules.xml | 1 - 25 files changed, 474 insertions(+), 667 deletions(-) delete mode 100644 app/src/androidTest/java/android/test/ProviderTestCase2MockContext.java delete mode 100644 app/src/androidTest/java/org/fdroid/fdroid/TestUtilsOld.java create mode 100644 app/src/test/java/org/fdroid/fdroid/AcceptableMultiRepoUpdaterTest.java create mode 100644 app/src/test/java/org/fdroid/fdroid/ProperMultiRepoUpdaterTest.java rename app/src/{androidTest => test}/java/org/fdroid/fdroid/UtilsTest.java (97%) rename app/src/{androidTest/assets => test/resources}/README.md (100%) rename app/src/{androidTest/assets => test/resources}/masterKeyIndex.jar (100%) create mode 100644 app/src/test/resources/simpleIndex.jar rename app/src/{androidTest/assets => test/resources}/simpleIndexWithCorruptedCertificate.jar (100%) rename app/src/{androidTest/assets => test/resources}/simpleIndexWithCorruptedEverything.jar (100%) rename app/src/{androidTest/assets => test/resources}/simpleIndexWithCorruptedManifest.jar (100%) rename app/src/{androidTest/assets => test/resources}/simpleIndexWithCorruptedSignature.jar (100%) rename app/src/{androidTest/assets => test/resources}/simpleIndexWithoutSignature.jar (100%) create mode 100644 config/pmd/rules-main.xml create mode 100644 config/pmd/rules-test.xml diff --git a/app/build.gradle b/app/build.gradle index 9443a8917..a9bf227f2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -230,13 +230,22 @@ pmd { consoleOutput = true } -task pmd(type: Pmd, dependsOn: assembleDebug) { - ruleSetFiles = files("${project.rootDir}/config/pmd/rules.xml") +task pmdMain(type: Pmd, dependsOn: assembleDebug) { + ruleSetFiles = files("${project.rootDir}/config/pmd/rules.xml", "${project.rootDir}/config/pmd/rules-main.xml") ruleSets = [] // otherwise defaults clash with the list in rules.xml - source 'src/main/java', 'src/test/java', 'src/androidTest/java' + source 'src/main/java' include '**/*.java' } +task pmdTest(type: Pmd, dependsOn: assembleDebug) { + ruleSetFiles = files("${project.rootDir}/config/pmd/rules.xml", "${project.rootDir}/config/pmd/rules-test.xml") + ruleSets = [] // otherwise defaults clash with the list in rules.xml + source 'src/test/java', 'src/androidTest/java' + include '**/*.java' +} + +task pmd(dependsOn: [pmdMain, pmdTest]) {} + // This person took the example code below from another blogpost online, however // I lost the reference to it: // http://stackoverflow.com/questions/23297562/gradle-javadoc-and-android-documentation diff --git a/app/src/androidTest/assets/simpleIndex.jar b/app/src/androidTest/assets/simpleIndex.jar index 1c173ceb37eb2898f6dbb0df4a9dc849d943c919..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 2412 zcma)82{hDe8z0LsvXpG)VsdH7GPcYZQK@E%F-(YS8QmBRS!xiYELVM|L?jwnK9$Iq zE&GQoX_PTz#uhSCx3M(VM9me$9i4M=`@Zv?@A;qqIq!Sk^ZT9WJn!@Veh(HcA}S96 zfj|K9qMQ?eZ6PmMV~|c(`qp+R1B{iOH45qI1jV2hF$}sbS|8dYjn>y;GFVj(hW#e* z$B9rIjGi^7C;W-1MCSBfYPPZt;SZ(#kjY0nW|K+^V@mt)0y55jCN2<{_fHhM9l%c% zJaYbcW^CdU5qQ~5d8z?lDAHBq09ukfcqy4-u*gjMbTKK623jfwSu^YwW%_Pa1G!=V zELt3-pfAUY5o8xH__6aFeaUu`_i%|A92%$2*m$A6fux5%3p=Ar6`g>mtvPY z(_$^bBGQD7kAk2#WHF~@LG>+Z4l8nJq7 zhH0N>S2{sZxBhb1sL$m1e8Li_rnx!xdpXqLBe%K)rC9l&owhrF~G!z9henOzYw zk<*ZqyD1cct`;)zkk)`>Z{B?nCj|Z}R9mvnura#~eLBWB@j>Qqpi|deGwb#lX;nNj zLui;795Q7cwaXdornXW$Mp|228AFCPIfK;h*4}PV=YCqr%jFK}IlRxW!81OFgc*E5 zApRFGZrNlmUHfOgUuSk~o3Sa9A3;%=QAD^^C9Qn;y=-nyWHvNIQLo`C8NOb|VtT&0 zrkoqz0tV_0(Mzi5Mko3mkHGJB^`F&P)w3LU>hos78$5MW^`04+@6h0EO#pY-D0=2z5S~3Qq+#;O?d%laSZk z+teK|eYwUBj?J5i?We{p^uPE7tA2_XGqI@@=NIrNu97>RH{8FCp?EzjT;Z3~w)4OFx-A)6m1&Rji#o)XaOH zfc8~1K8e0m(UjMX)im|TrN^By;uOB?gY}FuLDM zI1jRjJ+Q!MKHZV3nJ}rOQ#WJ?!$&cgqJWjqI)Yv1UAWOZcQOFl%*$T|yyUzBtrWd_#5_Q*rKHxv4=N-E-{~J7jbfY{5jv zT0mBp^8EB6I_8bb!wOpete%=|4py-IaAP{!z;9v*x3-py8z^`l%IpO`oNh~UwH;Mq zAK}D!BIyp_JGeJrVK`P80N5e2{XvWUAV?Ab(X9?%>jVHqzJmpQ4G>_z$CeuWCl~`mJ28*c(+4@!W}(D?QcN7P~yYNHfEA|tzX6RO79&ET)(?n zA;H$&;!#m3NbPX+5!EiZ>wr;S9^^_K?Ml?)rCLV5C0MbZ;rnSWirw%oRt-8yq2+Kj z?fd3=Ewd|k>OH1iNYPm;cuDQgzQ=zCh<_`f$43D4SFjP1vWW#M(aZ`h{>f;aYwiHV ziJErs70-=0^bk?4HM&clUXXU8iX&;TVbOzj$AFliM+6y`#@2 z%$3qVXCKBqcRzb@-ru9Aj^D^zFV^+uu6Ihz<;2l5+05i&VdrZ$KMTq+m&=%0GbNDF+5U_7Ziv+=MHom+Fx1m&^9U17&x+L%3%G0lh~r zBxXQfx3~7AYoK?%4jGx>FanOlE+;A(gf8-ciD5z9ltI1jMU(}|_ zCk6E?B|B-}3(R6EiPU>dkgPicN?EuG`ggurnew65Jjq;IY|1=)}v_Rn+ zZCuB-hl&)npSXnBxG5FmveI|%o_(#d-e?e_{L#NBYPw##uIb!sCpY4kX4!DA+rf-L zVB#o_<2Ysd3j37}+5I6W=t6VDu$PYMuOT=tv%NBmaz3mvOdaA6Bc5(cyd)QHcVHLA zR^RXIt}NskQ0Nm(^|L}70;xoK#b#H5Qb(#@nkTBd>iKQhA!mA-rmXL*;)Ke5Q!Rq& zfVIzCNhC6`l$h*LxjQpROojw@wFGz!t?u(ehIpW`dmKEgTm=s@?k@#%TrSL zYcONAiShA^V9h=$@jDrmi*Ldvm*-Lpye3A)cbaFZHr)#H^bBVOPJJ`-l>(D< z>ckh9i3piO%S)!5lFW)2P-8waNh;YR#KW!FV*gR^?qDP*7)zi&NFQmZ{ zOeKAu5^$uFuhC$zNGn+kKSyGnn0=H%cn*dJmra|sjEt}?z%C|v^P|h8H)z$x@Ft@r zwXlfD)l-b$QMp-Qjb-}{bG5!%rnyJ(4l95gZ&65C9V=`P!bg&!HV zd)40=S_NdA44eMsi29LXyH;#?+E%cH{u_h9;kHx!r3-&4#)hbD#cjc6Gl>m}`4PIk eIuctr!HOw>ZVC(*4HVlr1qwcdAT3qU#_mtJ{_K$e diff --git a/app/src/androidTest/java/android/test/ProviderTestCase2MockContext.java b/app/src/androidTest/java/android/test/ProviderTestCase2MockContext.java deleted file mode 100644 index 405902649..000000000 --- a/app/src/androidTest/java/android/test/ProviderTestCase2MockContext.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.test; - -import android.annotation.TargetApi; -import android.content.ContentProvider; -import android.content.ContentResolver; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.res.Resources; -import android.database.DatabaseUtils; -import android.os.Build; -import android.test.mock.MockContentResolver; -import android.test.mock.MockContext; - -import org.junit.After; -import org.junit.Before; - -import java.io.File; - -/** - * This test case class provides a framework for testing a single - * {@link ContentProvider} and for testing your app code with an - * isolated content provider. Instead of using the system map of - * providers that is based on the manifests of other applications, the test - * case creates its own internal map. It then uses this map to resolve providers - * given an authority. This allows you to inject test providers and to null out - * providers that you do not want to use. - *

- * This test case also sets up the following mock objects: - *

- *
    - *
  • - * An {@link android.test.IsolatedContext} that stubs out Context methods that might - * affect the rest of the running system, while allowing tests to do real file and - * database work. - *
  • - *
  • - * A {@link android.test.mock.MockContentResolver} that provides the functionality of a - * regular content resolver, but uses {@link IsolatedContext}. It stubs out - * {@link ContentResolver#notifyChange(Uri, ContentObserver, boolean)} to - * prevent the test from affecting the running system. - *
  • - *
  • - * An instance of the provider under test, running in an {@link IsolatedContext}. - *
  • - *
- *

- * This framework is set up automatically by the base class' {@link #setUp()} method. If you - * override this method, you must call the super method as the first statement in - * your override. - *

- *

- * In order for their tests to be run, concrete subclasses must provide their own - * constructor with no arguments. This constructor must call - * {@link #ProviderTestCase2MockContext(Class, String)} as its first operation. - *

- * For more information on content provider testing, please see - * Content Provider Testing. - */ -public abstract class ProviderTestCase2MockContext extends AndroidTestCase { - - Class mProviderClass; - String mProviderAuthority; - - private IsolatedContext mProviderContext; - private MockContentResolver mResolver; - - private class MockContext2 extends MockContext { - - @Override - public Resources getResources() { - return getContext().getResources(); - } - - @Override - public File getDir(String name, int mode) { - // name the directory so the directory will be separated from - // one created through the regular Context - return getContext().getDir("mockcontext2_" + name, mode); - } - - @Override - public Context getApplicationContext() { - return this; - } - - @Override - public String getPackageName() { - return "org.fdroid.fdroid"; - } - } - - /** - * Constructor. - * - * @param providerClass The class name of the provider under test - * @param providerAuthority The provider's authority string - */ - public ProviderTestCase2MockContext(Class 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; - } - - protected abstract 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 - @Before - protected void setUp() throws Exception { - super.setUp(); - - mResolver = new MockContentResolver(); - final String filenamePrefix = "test."; - final 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, new ContextWrapper(targetContextWrapper) { - // The FDroidProvider class needs access to an application context in order to initialize - // the singleton DBHelper instance. - @Override - public Context getApplicationContext() { - return 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 - @After - protected void tearDown() throws Exception { - shutdownProvider(); - super.tearDown(); - } - - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - private void shutdownProvider() { - if (Build.VERSION.SDK_INT >= 11) { - mProvider.shutdown(); - } - } - - /** - * 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/app/src/androidTest/java/org/fdroid/fdroid/FileCompatTest.java b/app/src/androidTest/java/org/fdroid/fdroid/FileCompatTest.java index ff7e86378..18456f749 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/FileCompatTest.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/FileCompatTest.java @@ -1,7 +1,10 @@ package org.fdroid.fdroid; import android.app.Instrumentation; +import android.content.Context; import android.os.Build; +import android.os.Environment; +import android.support.annotation.Nullable; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.util.Log; @@ -14,10 +17,15 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.UUID; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; /** @@ -36,8 +44,8 @@ public class FileCompatTest { @Before public void setUp() { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - File dir = TestUtilsOld.getWriteableDir(instrumentation); - sourceFile = SanitizedFile.knownSanitized(TestUtilsOld.copyAssetToDir(instrumentation.getContext(), "simpleIndex.jar", dir)); + File dir = getWriteableDir(instrumentation); + sourceFile = SanitizedFile.knownSanitized(copyAssetToDir(instrumentation.getContext(), "simpleIndex.jar", dir)); destFile = new SanitizedFile(dir, "dest-" + UUID.randomUUID() + ".testproduct"); assertFalse(destFile.exists()); assertTrue(sourceFile.getAbsolutePath() + " should exist.", sourceFile.exists()); @@ -62,24 +70,70 @@ public class FileCompatTest { @Test public void testSymlinkLibcore() { - - if (Build.VERSION.SDK_INT >= 19) { - FileCompatForTest.symlinkLibcoreTest(sourceFile, destFile); - assertTrue(destFile.getAbsolutePath() + " should exist after symlinking", destFile.exists()); - } else { - Log.w(TAG, "Cannot test symlink-libcore on this device. Requires android-19, but this has android-" + Build.VERSION.SDK_INT); - } + assumeTrue(Build.VERSION.SDK_INT >= 19); + FileCompatForTest.symlinkLibcoreTest(sourceFile, destFile); + assertTrue(destFile.getAbsolutePath() + " should exist after symlinking", destFile.exists()); } @Test public void testSymlinkOs() { - - if (Build.VERSION.SDK_INT >= 21) { - FileCompatForTest.symlinkOsTest(sourceFile, destFile); - assertTrue(destFile.getAbsolutePath() + " should exist after symlinking", destFile.exists()); - } else { - Log.w(TAG, "Cannot test symlink-os on this device. Requires android-21, but only has android-" + Build.VERSION.SDK_INT); - } + assumeTrue(Build.VERSION.SDK_INT >= 21); + FileCompatForTest.symlinkOsTest(sourceFile, destFile); + assertTrue(destFile.getAbsolutePath() + " should exist after symlinking", destFile.exists()); } + @Nullable + private static File copyAssetToDir(Context context, String assetName, File directory) { + File tempFile; + InputStream input = null; + OutputStream output = null; + try { + tempFile = File.createTempFile(assetName + "-", ".testasset", directory); + Log.i(TAG, "Copying asset file " + assetName + " to directory " + directory); + input = context.getAssets().open(assetName); + output = new FileOutputStream(tempFile); + Utils.copy(input, output); + } catch (IOException e) { + e.printStackTrace(); + return null; + } finally { + Utils.closeQuietly(output); + Utils.closeQuietly(input); + } + return tempFile; + } + + /** + * Prefer internal over external storage, because external tends to be FAT filesystems, + * which don't support symlinks (which we test using this method). + */ + private static File getWriteableDir(Instrumentation instrumentation) { + Context context = instrumentation.getContext(); + Context targetContext = instrumentation.getTargetContext(); + + File[] dirsToTry = new File[]{ + context.getCacheDir(), + context.getFilesDir(), + targetContext.getCacheDir(), + targetContext.getFilesDir(), + context.getExternalCacheDir(), + context.getExternalFilesDir(null), + targetContext.getExternalCacheDir(), + targetContext.getExternalFilesDir(null), + Environment.getExternalStorageDirectory(), + }; + + return getWriteableDir(dirsToTry); + } + + private static File getWriteableDir(File[] dirsToTry) { + + for (File dir : dirsToTry) { + if (dir != null && dir.canWrite()) { + return dir; + } + } + + return null; + } } diff --git a/app/src/androidTest/java/org/fdroid/fdroid/TestUtilsOld.java b/app/src/androidTest/java/org/fdroid/fdroid/TestUtilsOld.java deleted file mode 100644 index df8144457..000000000 --- a/app/src/androidTest/java/org/fdroid/fdroid/TestUtilsOld.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.fdroid.fdroid; - -import android.app.Instrumentation; -import android.content.Context; -import android.os.Environment; -import android.support.annotation.Nullable; -import android.util.Log; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class TestUtilsOld { - - private static final String TAG = "TestUtilsOld"; - - @Nullable - public static File copyAssetToDir(Context context, String assetName, File directory) { - File tempFile; - InputStream input = null; - OutputStream output = null; - try { - tempFile = File.createTempFile(assetName + "-", ".testasset", directory); - Log.i(TAG, "Copying asset file " + assetName + " to directory " + directory); - input = context.getAssets().open(assetName); - output = new FileOutputStream(tempFile); - Utils.copy(input, output); - } catch (IOException e) { - e.printStackTrace(); - return null; - } finally { - Utils.closeQuietly(output); - Utils.closeQuietly(input); - } - return tempFile; - } - - /** - * Prefer internal over external storage, because external tends to be FAT filesystems, - * which don't support symlinks (which we test using this method). - */ - public static File getWriteableDir(Instrumentation instrumentation) { - Context context = instrumentation.getContext(); - Context targetContext = instrumentation.getTargetContext(); - - File[] dirsToTry = new File[]{ - context.getCacheDir(), - context.getFilesDir(), - targetContext.getCacheDir(), - targetContext.getFilesDir(), - context.getExternalCacheDir(), - context.getExternalFilesDir(null), - targetContext.getExternalCacheDir(), - targetContext.getExternalFilesDir(null), - Environment.getExternalStorageDirectory(), - }; - - return getWriteableDir(dirsToTry); - } - - private static File getWriteableDir(File[] dirsToTry) { - - for (File dir : dirsToTry) { - if (dir != null && dir.canWrite()) { - return dir; - } - } - - return null; - } -} diff --git a/app/src/test/java/org/fdroid/fdroid/AcceptableMultiRepoUpdaterTest.java b/app/src/test/java/org/fdroid/fdroid/AcceptableMultiRepoUpdaterTest.java new file mode 100644 index 000000000..d159c6018 --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/AcceptableMultiRepoUpdaterTest.java @@ -0,0 +1,82 @@ + +package org.fdroid.fdroid; + +import android.util.Log; + +import org.fdroid.fdroid.RepoUpdater.UpdateException; +import org.fdroid.fdroid.data.Repo; +import org.fdroid.fdroid.data.RepoProvider; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.annotation.Config; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +@Config(constants = BuildConfig.class) +@RunWith(RobolectricGradleTestRunner.class) +public class AcceptableMultiRepoUpdaterTest extends MultiRepoUpdaterTest { + private static final String TAG = "AcceptableMultiRepoTest"; + + private void assertSomewhatAcceptable() { + Log.i(TAG, "Asserting at least one versions of each .apk is in index."); + List repos = RepoProvider.Helper.all(context); + assertEquals("Repos", 3, repos.size()); + + assertApp2048(); + assertAppAdaway(); + assertAppAdbWireless(); + assertAppIcsImport(); + } + + @Test + public void testAcceptableConflictingThenMainThenArchive() throws UpdateException { + assertEmpty(); + if (updateConflicting() && updateMain() && updateArchive()) { + assertSomewhatAcceptable(); + } + } + + @Test + public void testAcceptableConflictingThenArchiveThenMain() throws UpdateException { + assertEmpty(); + if (updateConflicting() && updateArchive() && updateMain()) { + assertSomewhatAcceptable(); + } + } + + @Test + public void testAcceptableArchiveThenMainThenConflicting() throws UpdateException { + assertEmpty(); + if (updateArchive() && updateMain() && updateConflicting()) { + assertSomewhatAcceptable(); + } + } + + @Test + public void testAcceptableArchiveThenConflictingThenMain() throws UpdateException { + assertEmpty(); + if (updateArchive() && updateConflicting() && updateMain()) { + assertSomewhatAcceptable(); + } + } + + @Test + public void testAcceptableMainThenArchiveThenConflicting() throws UpdateException { + assertEmpty(); + if (updateMain() && updateArchive() && updateConflicting()) { + assertSomewhatAcceptable(); + } + } + + @Test + public void testAcceptableMainThenConflictingThenArchive() throws UpdateException { + assertEmpty(); + if (updateMain() && updateConflicting() && updateArchive()) { + assertSomewhatAcceptable(); + } + } + +} diff --git a/app/src/test/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java b/app/src/test/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java index ac77a566d..79e31254b 100644 --- a/app/src/test/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java +++ b/app/src/test/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java @@ -5,7 +5,6 @@ import android.content.ContentValues; import android.content.Context; import android.support.annotation.NonNull; import android.text.TextUtils; -import android.util.Log; import org.fdroid.fdroid.RepoUpdater.UpdateException; import org.fdroid.fdroid.data.Apk; @@ -16,10 +15,6 @@ import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoProvider; import org.junit.After; import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricGradleTestRunner; -import org.robolectric.annotation.Config; import java.io.File; import java.util.List; @@ -30,18 +25,16 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -@Config(constants = BuildConfig.class) -@RunWith(RobolectricGradleTestRunner.class) -public class MultiRepoUpdaterTest extends FDroidProviderTest { - private static final String TAG = "MultiRepoUpdaterTest"; +public abstract class MultiRepoUpdaterTest extends FDroidProviderTest { + private static final String TAG = "AcceptableMultiRepoUpdaterTest"; // NOPMD - private static final String REPO_MAIN = "Test F-Droid repo"; - private static final String REPO_ARCHIVE = "Test F-Droid repo (Archive)"; - private static final String REPO_CONFLICTING = "Test F-Droid repo with different apps"; + protected static final String REPO_MAIN = "Test F-Droid repo"; + protected static final String REPO_ARCHIVE = "Test F-Droid repo (Archive)"; + protected static final String REPO_CONFLICTING = "Test F-Droid repo with different apps"; - private RepoUpdater conflictingRepoUpdater; - private RepoUpdater mainRepoUpdater; - private RepoUpdater archiveRepoUpdater; + protected RepoUpdater conflictingRepoUpdater; + protected RepoUpdater mainRepoUpdater; + protected RepoUpdater archiveRepoUpdater; private static final String PUB_KEY = "3082050b308202f3a003020102020420d8f212300d06092a864886f70d01010b050030363110300e0603" + @@ -77,7 +70,7 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest { "e8af60597c4ae2977977cf61dc715a572e241ae717cafdb4f71781943945ac52e0f50b"; @Before - public void setup() throws Exception { + public final void setupMultiRepo() throws Exception { // On a fresh database install, there will be F-Droid + GP repos, including their Archive // repos that we are not interested in. RepoProvider.Helper.remove(context, 1); @@ -93,123 +86,33 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest { } @After - public void tearDown() { + public final void tearDownMultiRepo() { Preferences.clearSingletonForTesting(); } - /** - * Check that all of the expected apps and apk versions are available in the database. This - * check will take into account the repository the apks came from, to ensure that each - * repository indeed contains the apks that it said it would provide. - */ - private void assertExpected() { - Log.i(TAG, "Asserting all versions of each .apk are in index."); - List repos = RepoProvider.Helper.all(context); - assertEquals("Repos", 3, repos.size()); - - assertMainRepo(repos); - assertMainArchiveRepo(repos); - assertConflictingRepo(repos); - } - - private void assertSomewhatAcceptable() { - Log.i(TAG, "Asserting at least one versions of each .apk is in index."); - List repos = RepoProvider.Helper.all(context); - assertEquals("Repos", 3, repos.size()); - - assertApp2048(); - assertAppAdaway(); - assertAppAdbWireless(); - assertAppIcsImport(); - } - - private void assertApp(String packageName, int[] versionCodes) { + protected void assertApp(String packageName, int[] versionCodes) { List apks = ApkProvider.Helper.findByPackageName(context, packageName, ApkProvider.DataColumns.ALL); assertApksExist(apks, packageName, versionCodes); } - private void assertApp2048() { + protected void assertApp2048() { assertApp("com.uberspot.a2048", new int[]{19, 18}); } - private void assertAppAdaway() { + protected void assertAppAdaway() { assertApp("org.adaway", new int[]{54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 42, 40, 38, 37, 36, 35}); } - private void assertAppAdbWireless() { + protected void assertAppAdbWireless() { assertApp("siir.es.adbWireless", new int[]{12}); } - private void assertAppIcsImport() { + protected void assertAppIcsImport() { assertApp("org.dgtale.icsimport", new int[]{3, 2}); } - /** - * + 2048 (com.uberspot.a2048) - * - Version 1.96 (19) - * - Version 1.95 (18) - * + AdAway (org.adaway) - * - Version 3.0.2 (54) - * - Version 3.0.1 (53) - * - Version 3.0 (52) - * + adbWireless (siir.es.adbWireless) - * - Version 1.5.4 (12) - */ - private void assertMainRepo(List allRepos) { - Repo repo = findRepo(REPO_MAIN, allRepos); - - List apks = ApkProvider.Helper.findByRepo(context, repo, ApkProvider.DataColumns.ALL); - assertEquals("Apks for main repo", apks.size(), 6); - assertApksExist(apks, "com.uberspot.a2048", new int[]{18, 19}); - assertApksExist(apks, "org.adaway", new int[]{52, 53, 54}); - assertApksExist(apks, "siir.es.adbWireless", new int[]{12}); - } - - /** - * + AdAway (org.adaway) - * - Version 2.9.2 (51) - * - Version 2.9.1 (50) - * - Version 2.9 (49) - * - Version 2.8.1 (48) - * - Version 2.8 (47) - * - Version 2.7 (46) - * - Version 2.6 (45) - * - Version 2.3 (42) - * - Version 2.1 (40) - * - Version 1.37 (38) - * - Version 1.36 (37) - * - Version 1.35 (36) - * - Version 1.34 (35) - */ - private void assertMainArchiveRepo(List allRepos) { - Repo repo = findRepo(REPO_ARCHIVE, allRepos); - - List apks = ApkProvider.Helper.findByRepo(context, repo, ApkProvider.DataColumns.ALL); - assertEquals("Apks for main archive repo", 13, apks.size()); - assertApksExist(apks, "org.adaway", new int[]{35, 36, 37, 38, 40, 42, 45, 46, 47, 48, 49, 50, 51}); - } - - /** - * + AdAway (org.adaway) - * - Version 3.0.1 (53) * - * - Version 3.0 (52) * - * - Version 2.9.2 (51) * - * - Version 2.2.1 (50) * - * + Add to calendar (org.dgtale.icsimport) - * - Version 1.2 (3) - * - Version 1.1 (2) - */ - private void assertConflictingRepo(List allRepos) { - Repo repo = findRepo(REPO_CONFLICTING, allRepos); - - List apks = ApkProvider.Helper.findByRepo(context, repo, ApkProvider.DataColumns.ALL); - assertEquals("Apks for main repo", 6, apks.size()); - assertApksExist(apks, "org.adaway", new int[]{50, 51, 52, 53}); - assertApksExist(apks, "org.dgtale.icsimport", new int[]{2, 3}); - } - @NonNull - private Repo findRepo(@NonNull String name, List allRepos) { + protected Repo findRepo(@NonNull String name, List allRepos) { Repo repo = null; for (Repo r : allRepos) { if (TextUtils.equals(name, r.getName())) { @@ -225,7 +128,7 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest { /** * Checks that each version of appId as specified in versionCodes is present in apksToCheck. */ - private void assertApksExist(List apksToCheck, String appId, int[] versionCodes) { + protected void assertApksExist(List apksToCheck, String appId, int[] versionCodes) { for (int versionCode : versionCodes) { boolean found = false; for (Apk apk : apksToCheck) { @@ -239,7 +142,7 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest { } } - private void assertEmpty() { + protected void assertEmpty() { assertEquals("No apps present", 0, AppProvider.Helper.all(context.getContentResolver()).size()); String[] packages = { @@ -253,102 +156,6 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest { } } - /* At time fo writing, the following tests did not pass. This is because the multi-repo support - in F-Droid was not sufficient. When working on proper multi repo support than this should be - ucommented and all these tests should pass: - - public void testCorrectConflictingThenMainThenArchive() throws UpdateException { - assertEmpty(); - if (updateConflicting() && updateMain() && updateArchive()) { - assertExpected(); - } - } - - public void testCorrectConflictingThenArchiveThenMain() throws UpdateException { - assertEmpty(); - if (updateConflicting() && updateArchive() && updateMain()) { - assertExpected(); - } - } - - public void testCorrectArchiveThenMainThenConflicting() throws UpdateException { - assertEmpty(); - if (updateArchive() && updateMain() && updateConflicting()) { - assertExpected(); - } - } - - public void testCorrectArchiveThenConflictingThenMain() throws UpdateException { - assertEmpty(); - if (updateArchive() && updateConflicting() && updateMain()) { - assertExpected(); - } - } - - public void testCorrectMainThenArchiveThenConflicting() throws UpdateException { - assertEmpty(); - if (updateMain() && updateArchive() && updateConflicting()) { - assertExpected(); - } - } - - public void testCorrectMainThenConflictingThenArchive() throws UpdateException { - assertEmpty(); - if (updateMain() && updateConflicting() && updateArchive()) { - assertExpected(); - } - } - - */ - - @Test - public void testAcceptableConflictingThenMainThenArchive() throws UpdateException { - assertEmpty(); - if (updateConflicting() && updateMain() && updateArchive()) { - assertSomewhatAcceptable(); - } - } - - @Test - public void testAcceptableConflictingThenArchiveThenMain() throws UpdateException { - assertEmpty(); - if (updateConflicting() && updateArchive() && updateMain()) { - assertSomewhatAcceptable(); - } - } - - @Test - public void testAcceptableArchiveThenMainThenConflicting() throws UpdateException { - assertEmpty(); - if (updateArchive() && updateMain() && updateConflicting()) { - assertSomewhatAcceptable(); - } - } - - @Test - public void testAcceptableArchiveThenConflictingThenMain() throws UpdateException { - assertEmpty(); - if (updateArchive() && updateConflicting() && updateMain()) { - assertSomewhatAcceptable(); - } - } - - @Test - public void testAcceptableMainThenArchiveThenConflicting() throws UpdateException { - assertEmpty(); - if (updateMain() && updateArchive() && updateConflicting()) { - assertSomewhatAcceptable(); - } - } - - @Test - public void testAcceptableMainThenConflictingThenArchive() throws UpdateException { - assertEmpty(); - if (updateMain() && updateConflicting() && updateArchive()) { - assertSomewhatAcceptable(); - } - } - private RepoUpdater createUpdater(String name, Context context) { Repo repo = new Repo(); repo.signingCertificate = PUB_KEY; @@ -367,15 +174,15 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest { return new RepoUpdater(context, RepoProvider.Helper.findByAddress(context, repo.address)); } - private boolean updateConflicting() throws UpdateException { + protected boolean updateConflicting() throws UpdateException { return updateRepo(conflictingRepoUpdater, "multiRepo.conflicting.jar"); } - private boolean updateMain() throws UpdateException { + protected boolean updateMain() throws UpdateException { return updateRepo(mainRepoUpdater, "multiRepo.normal.jar"); } - private boolean updateArchive() throws UpdateException { + protected boolean updateArchive() throws UpdateException { return updateRepo(archiveRepoUpdater, "multiRepo.archive.jar"); } @@ -385,7 +192,7 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest { updater.processDownloadedFile(indexJar); } finally { if (indexJar != null && indexJar.exists()) { - indexJar.delete(); + assertTrue(indexJar.delete()); } } return true; diff --git a/app/src/test/java/org/fdroid/fdroid/ProperMultiRepoUpdaterTest.java b/app/src/test/java/org/fdroid/fdroid/ProperMultiRepoUpdaterTest.java new file mode 100644 index 000000000..da7105e42 --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/ProperMultiRepoUpdaterTest.java @@ -0,0 +1,155 @@ + +package org.fdroid.fdroid; + +import android.util.Log; + +import org.fdroid.fdroid.RepoUpdater.UpdateException; +import org.fdroid.fdroid.data.Apk; +import org.fdroid.fdroid.data.ApkProvider; +import org.fdroid.fdroid.data.Repo; +import org.fdroid.fdroid.data.RepoProvider; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/* +At time fo writing, the following tests did not pass. This is because the multi-repo support +in F-Droid was not sufficient. When working on proper multi repo support than this should be +uncommented and all these tests will be required to pass: + +@Config(constants = BuildConfig.class) +@RunWith(RobolectricGradleTestRunner.class) +*/ +public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest { + private static final String TAG = "ProperMultiRepoSupport"; + + @Test + public void testCorrectConflictingThenMainThenArchive() throws UpdateException { + assertEmpty(); + if (updateConflicting() && updateMain() && updateArchive()) { + assertExpected(); + } + } + + @Test + public void testCorrectConflictingThenArchiveThenMain() throws UpdateException { + assertEmpty(); + if (updateConflicting() && updateArchive() && updateMain()) { + assertExpected(); + } + } + + @Test + public void testCorrectArchiveThenMainThenConflicting() throws UpdateException { + assertEmpty(); + if (updateArchive() && updateMain() && updateConflicting()) { + assertExpected(); + } + } + + @Test + public void testCorrectArchiveThenConflictingThenMain() throws UpdateException { + assertEmpty(); + if (updateArchive() && updateConflicting() && updateMain()) { + assertExpected(); + } + } + + @Test + public void testCorrectMainThenArchiveThenConflicting() throws UpdateException { + assertEmpty(); + if (updateMain() && updateArchive() && updateConflicting()) { + assertExpected(); + } + } + + @Test + public void testCorrectMainThenConflictingThenArchive() throws UpdateException { + assertEmpty(); + if (updateMain() && updateConflicting() && updateArchive()) { + assertExpected(); + } + } + + /** + * Check that all of the expected apps and apk versions are available in the database. This + * check will take into account the repository the apks came from, to ensure that each + * repository indeed contains the apks that it said it would provide. + */ + private void assertExpected() { + Log.i(TAG, "Asserting all versions of each .apk are in index."); + List repos = RepoProvider.Helper.all(context); + assertEquals("Repos", 3, repos.size()); + + assertMainRepo(repos); + assertMainArchiveRepo(repos); + assertConflictingRepo(repos); + } + + /** + * + 2048 (com.uberspot.a2048) + * - Version 1.96 (19) + * - Version 1.95 (18) + * + AdAway (org.adaway) + * - Version 3.0.2 (54) + * - Version 3.0.1 (53) + * - Version 3.0 (52) + * + adbWireless (siir.es.adbWireless) + * - Version 1.5.4 (12) + */ + private void assertMainRepo(List allRepos) { + Repo repo = findRepo(REPO_MAIN, allRepos); + + List apks = ApkProvider.Helper.findByRepo(context, repo, ApkProvider.DataColumns.ALL); + assertEquals("Apks for main repo", apks.size(), 6); + assertApksExist(apks, "com.uberspot.a2048", new int[]{18, 19}); + assertApksExist(apks, "org.adaway", new int[]{52, 53, 54}); + assertApksExist(apks, "siir.es.adbWireless", new int[]{12}); + } + + /** + * + AdAway (org.adaway) + * - Version 2.9.2 (51) + * - Version 2.9.1 (50) + * - Version 2.9 (49) + * - Version 2.8.1 (48) + * - Version 2.8 (47) + * - Version 2.7 (46) + * - Version 2.6 (45) + * - Version 2.3 (42) + * - Version 2.1 (40) + * - Version 1.37 (38) + * - Version 1.36 (37) + * - Version 1.35 (36) + * - Version 1.34 (35) + */ + private void assertMainArchiveRepo(List allRepos) { + Repo repo = findRepo(REPO_ARCHIVE, allRepos); + + List apks = ApkProvider.Helper.findByRepo(context, repo, ApkProvider.DataColumns.ALL); + assertEquals("Apks for main archive repo", 13, apks.size()); + assertApksExist(apks, "org.adaway", new int[]{35, 36, 37, 38, 40, 42, 45, 46, 47, 48, 49, 50, 51}); + } + + /** + * + AdAway (org.adaway) + * - Version 3.0.1 (53) * + * - Version 3.0 (52) * + * - Version 2.9.2 (51) * + * - Version 2.2.1 (50) * + * + Add to calendar (org.dgtale.icsimport) + * - Version 1.2 (3) + * - Version 1.1 (2) + */ + private void assertConflictingRepo(List allRepos) { + Repo repo = findRepo(REPO_CONFLICTING, allRepos); + + List apks = ApkProvider.Helper.findByRepo(context, repo, ApkProvider.DataColumns.ALL); + assertEquals("Apks for main repo", 6, apks.size()); + assertApksExist(apks, "org.adaway", new int[]{50, 51, 52, 53}); + assertApksExist(apks, "org.dgtale.icsimport", new int[]{2, 3}); + } + +} diff --git a/app/src/test/java/org/fdroid/fdroid/TestUtils.java b/app/src/test/java/org/fdroid/fdroid/TestUtils.java index 598c60a3b..2ced7a025 100644 --- a/app/src/test/java/org/fdroid/fdroid/TestUtils.java +++ b/app/src/test/java/org/fdroid/fdroid/TestUtils.java @@ -6,11 +6,13 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class TestUtils { - private static final String TAG = "TestUtils"; + @SuppressWarnings("unused") + private static final String TAG = "TestUtils"; // NOPMD public static File copyResourceToTempFile(String resourceName) { File tempFile = null; @@ -24,7 +26,7 @@ public class TestUtils { } catch (IOException e) { e.printStackTrace(); if (tempFile != null && tempFile.exists()) { - tempFile.delete(); + assertTrue(tempFile.delete()); } fail(); return null; diff --git a/app/src/androidTest/java/org/fdroid/fdroid/UtilsTest.java b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java similarity index 97% rename from app/src/androidTest/java/org/fdroid/fdroid/UtilsTest.java rename to app/src/test/java/org/fdroid/fdroid/UtilsTest.java index 3aadf8849..7ec649ca3 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/UtilsTest.java +++ b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java @@ -1,14 +1,14 @@ package org.fdroid.fdroid; -import android.app.Instrumentation; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; import org.apache.commons.io.FileUtils; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import java.io.File; import java.io.IOException; @@ -17,7 +17,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -@RunWith(AndroidJUnit4.class) +@Config(constants = BuildConfig.class) +@RunWith(RobolectricGradleTestRunner.class) public class UtilsTest { String fdroidFingerprint = "43238D512C1E5EB2D6569F4A3AFBF5523418B82E0A3ED1552770ABB9A9C9CCAB"; @@ -50,7 +51,7 @@ public class UtilsTest { @Test public void testFormatFingerprint() { - Context context = InstrumentationRegistry.getTargetContext(); + Context context = RuntimeEnvironment.application; String badResult = Utils.formatFingerprint(context, ""); // real fingerprints String formatted; @@ -145,22 +146,29 @@ public class UtilsTest { @Test public void testClearOldFiles() throws IOException, InterruptedException { - Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - File dir = new File(TestUtilsOld.getWriteableDir(instrumentation), "clearOldFiles"); + File tempDir = new File(System.getProperty("java.io.tmpdir")); + assertTrue(tempDir.isDirectory()); + assertTrue(tempDir.canWrite()); + + File dir = new File(tempDir, "F-Droid-test.clearOldFiles"); FileUtils.deleteQuietly(dir); - dir.mkdirs(); + assertTrue(dir.mkdirs()); assertTrue(dir.isDirectory()); File first = new File(dir, "first"); + first.deleteOnExit(); + File second = new File(dir, "second"); + second.deleteOnExit(); + assertFalse(first.exists()); assertFalse(second.exists()); - first.createNewFile(); + assertTrue(first.createNewFile()); assertTrue(first.exists()); Thread.sleep(7000); - second.createNewFile(); + assertTrue(second.createNewFile()); assertTrue(second.exists()); Utils.clearOldFiles(dir, 3); diff --git a/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java index 02a0e379c..6d3c6811e 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java @@ -23,7 +23,6 @@ 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; @@ -38,8 +37,8 @@ public class ApkProviderTest extends FDroidProviderTest { @Test public void testAppApks() { for (int i = 1; i <= 10; i++) { - insertApk(contentResolver, "org.fdroid.fdroid", i); - insertApk(contentResolver, "com.example", i); + ProviderTestUtils.insertApk(contentResolver, "org.fdroid.fdroid", i); + ProviderTestUtils.insertApk(contentResolver, "com.example", i); } assertTotalApkCount(20); @@ -187,23 +186,12 @@ public class ApkProviderTest extends FDroidProviderTest { Apk apk = new MockApk("org.fdroid.fdroid", 13); // Insert a new record... - Uri newUri = insertApk(contentResolver, apk.packageName, apk.versionCode); + Uri newUri = ProviderTestUtils.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. @@ -214,12 +202,19 @@ public class ApkProviderTest extends FDroidProviderTest { assertEquals(13, toCheck.versionCode); } + @Test(expected = IllegalArgumentException.class) + public void testCursorMustMoveToFirst() { + ProviderTestUtils.insertApk(contentResolver, "org.example.test", 12); + Cursor cursor = queryAllApks(); + new Apk(cursor); + } + @Test public void testCount() { String[] projectionCount = new String[] {ApkProvider.DataColumns._COUNT}; for (int i = 0; i < 13; i++) { - insertApk(contentResolver, "com.example", i); + ProviderTestUtils.insertApk(contentResolver, "com.example", i); } Uri all = ApkProvider.getContentUri(); @@ -236,38 +231,48 @@ public class ApkProviderTest extends FDroidProviderTest { allWithCount.close(); } + @Test(expected = IllegalArgumentException.class) + public void testInsertWithInvalidExtraFieldDescription() { + assertInvalidExtraField(RepoProvider.DataColumns.DESCRIPTION); + } + + @Test(expected = IllegalArgumentException.class) + public void testInsertWithInvalidExtraFieldAddress() { + assertInvalidExtraField(RepoProvider.DataColumns.ADDRESS); + } + + @Test(expected = IllegalArgumentException.class) + public void testInsertWithInvalidExtraFieldFingerprint() { + assertInvalidExtraField(RepoProvider.DataColumns.FINGERPRINT); + } + + @Test(expected = IllegalArgumentException.class) + public void testInsertWithInvalidExtraFieldName() { + assertInvalidExtraField(RepoProvider.DataColumns.NAME); + } + + @Test(expected = IllegalArgumentException.class) + public void testInsertWithInvalidExtraFieldSigningCert() { + assertInvalidExtraField(RepoProvider.DataColumns.SIGNING_CERT); + } + + public void assertInvalidExtraField(String field) { + ContentValues invalidRepo = new ContentValues(); + invalidRepo.put(field, "Test data"); + ProviderTestUtils.insertApk(contentResolver, "org.fdroid.fdroid", 10, invalidRepo); + } + @Test - public void testInsertWithExtraFields() { + public void testInsertWithValidExtraFields() { 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); + Uri uri = ProviderTestUtils.insertApk(contentResolver, "com.example.com", 1, values); assertResultCount(1, queryAllApks()); @@ -293,18 +298,18 @@ public class ApkProviderTest extends FDroidProviderTest { public void testKnownApks() { for (int i = 0; i < 7; i++) { - insertApk(contentResolver, "org.fdroid.fdroid", i); + ProviderTestUtils.insertApk(contentResolver, "org.fdroid.fdroid", i); } for (int i = 0; i < 9; i++) { - insertApk(contentResolver, "org.example", i); + ProviderTestUtils.insertApk(contentResolver, "org.example", i); } for (int i = 0; i < 3; i++) { - insertApk(contentResolver, "com.example", i); + ProviderTestUtils.insertApk(contentResolver, "com.example", i); } - insertApk(contentResolver, "com.apk.thingo", 1); + ProviderTestUtils.insertApk(contentResolver, "com.apk.thingo", 1); Apk[] known = { new MockApk("org.fdroid.fdroid", 1), @@ -351,18 +356,18 @@ public class ApkProviderTest extends FDroidProviderTest { public void testFindByApp() { for (int i = 0; i < 7; i++) { - insertApk(contentResolver, "org.fdroid.fdroid", i); + ProviderTestUtils.insertApk(contentResolver, "org.fdroid.fdroid", i); } for (int i = 0; i < 9; i++) { - insertApk(contentResolver, "org.example", i); + ProviderTestUtils.insertApk(contentResolver, "org.example", i); } for (int i = 0; i < 3; i++) { - insertApk(contentResolver, "com.example", i); + ProviderTestUtils.insertApk(contentResolver, "com.example", i); } - insertApk(contentResolver, "com.apk.thingo", 1); + ProviderTestUtils.insertApk(contentResolver, "com.apk.thingo", 1); assertTotalApkCount(7 + 9 + 3 + 1); @@ -386,7 +391,7 @@ public class ApkProviderTest extends FDroidProviderTest { @Test public void testUpdate() { - Uri apkUri = insertApk(contentResolver, "com.example", 10); + Uri apkUri = ProviderTestUtils.insertApk(contentResolver, "com.example", 10); String[] allFields = ApkProvider.DataColumns.ALL; Cursor cursor = contentResolver.query(apkUri, allFields, null, null, null); @@ -442,18 +447,18 @@ public class ApkProviderTest extends FDroidProviderTest { // 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); + ProviderTestUtils.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); + ProviderTestUtils.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); + ProviderTestUtils.insertApk(contentResolver, "com.other.thing." + i, i); } Apk apk = ApkProvider.Helper.find(context, "com.example", 11); @@ -529,7 +534,7 @@ public class ApkProviderTest extends FDroidProviderTest { 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); + Uri uri = ProviderTestUtils.insertApk(contentResolver, id, versionCode, additionalValues); return ApkProvider.Helper.get(context, uri); } } diff --git a/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java index 3aa382521..779ca10f4 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java @@ -25,7 +25,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; @Config(constants = BuildConfig.class, application = Application.class) @RunWith(RobolectricGradleTestRunner.class) @@ -224,17 +223,6 @@ public class AppProviderTest extends FDroidProviderTest { 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 // PACKAGE_NAME. @@ -250,6 +238,17 @@ public class AppProviderTest extends FDroidProviderTest { assertEquals("F-Droid", otherApp.name); } + /** + * We intentionally throw an IllegalArgumentException if you haven't + * yet called cursor.move*(). + */ + @Test(expected = IllegalArgumentException.class) + public void testCursorMustMoveToFirst() { + insertApp("org.fdroid.fdroid", "F-Droid"); + Cursor cursor = queryAllApps(); + new App(cursor); + } + private Cursor queryAllApps() { String[] projection = new String[] { AppProvider.DataColumns._ID, diff --git a/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java index 1d537de18..5afc4cfc2 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java @@ -19,7 +19,6 @@ 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; @@ -85,24 +84,12 @@ public class InstalledAppProviderTest extends FDroidProviderTest{ @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") @@ -110,7 +97,19 @@ public class InstalledAppProviderTest extends FDroidProviderTest{ assertResultCount(contentResolver, 2, InstalledAppProvider.getContentUri()); assertIsInstalledVersionInDb(contentResolver, "com.example.app2", 11, "1.1"); + } + /** + * We expect this to happen, because we should be using insert() instead as it will + * do an insert/replace query. + */ + @Test(expected = UnsupportedOperationException.class) + public void testUpdateFails() { + contentResolver.update( + InstalledAppProvider.getAppUri("com.example.app2"), + createContentValues(11, "1.1"), + null, null + ); } @Test 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 9fa7d9d9a..2e75977ea 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java @@ -17,7 +17,6 @@ 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) @@ -124,6 +123,11 @@ public class ProviderUriTests { 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); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidApkUrisWithTooManyApks() { + String[] projection = ApkProvider.DataColumns.ALL; List manyApks = new ArrayList<>(ApkProvider.MAX_APKS_TO_QUERY - 5); for (int i = 0; i < ApkProvider.MAX_APKS_TO_QUERY - 1; i++) { @@ -133,18 +137,12 @@ public class ProviderUriTests { 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(); - } + + // 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); } } diff --git a/app/src/androidTest/assets/README.md b/app/src/test/resources/README.md similarity index 100% rename from app/src/androidTest/assets/README.md rename to app/src/test/resources/README.md diff --git a/app/src/androidTest/assets/masterKeyIndex.jar b/app/src/test/resources/masterKeyIndex.jar similarity index 100% rename from app/src/androidTest/assets/masterKeyIndex.jar rename to app/src/test/resources/masterKeyIndex.jar diff --git a/app/src/test/resources/simpleIndex.jar b/app/src/test/resources/simpleIndex.jar new file mode 100644 index 0000000000000000000000000000000000000000..1c173ceb37eb2898f6dbb0df4a9dc849d943c919 GIT binary patch literal 2412 zcma)82{hDe8z0LsvXpG)VsdH7GPcYZQK@E%F-(YS8QmBRS!xiYELVM|L?jwnK9$Iq zE&GQoX_PTz#uhSCx3M(VM9me$9i4M=`@Zv?@A;qqIq!Sk^ZT9WJn!@Veh(HcA}S96 zfj|K9qMQ?eZ6PmMV~|c(`qp+R1B{iOH45qI1jV2hF$}sbS|8dYjn>y;GFVj(hW#e* z$B9rIjGi^7C;W-1MCSBfYPPZt;SZ(#kjY0nW|K+^V@mt)0y55jCN2<{_fHhM9l%c% zJaYbcW^CdU5qQ~5d8z?lDAHBq09ukfcqy4-u*gjMbTKK623jfwSu^YwW%_Pa1G!=V zELt3-pfAUY5o8xH__6aFeaUu`_i%|A92%$2*m$A6fux5%3p=Ar6`g>mtvPY z(_$^bBGQD7kAk2#WHF~@LG>+Z4l8nJq7 zhH0N>S2{sZxBhb1sL$m1e8Li_rnx!xdpXqLBe%K)rC9l&owhrF~G!z9henOzYw zk<*ZqyD1cct`;)zkk)`>Z{B?nCj|Z}R9mvnura#~eLBWB@j>Qqpi|deGwb#lX;nNj zLui;795Q7cwaXdornXW$Mp|228AFCPIfK;h*4}PV=YCqr%jFK}IlRxW!81OFgc*E5 zApRFGZrNlmUHfOgUuSk~o3Sa9A3;%=QAD^^C9Qn;y=-nyWHvNIQLo`C8NOb|VtT&0 zrkoqz0tV_0(Mzi5Mko3mkHGJB^`F&P)w3LU>hos78$5MW^`04+@6h0EO#pY-D0=2z5S~3Qq+#;O?d%laSZk z+teK|eYwUBj?J5i?We{p^uPE7tA2_XGqI@@=NIrNu97>RH{8FCp?EzjT;Z3~w)4OFx-A)6m1&Rji#o)XaOH zfc8~1K8e0m(UjMX)im|TrN^By;uOB?gY}FuLDM zI1jRjJ+Q!MKHZV3nJ}rOQ#WJ?!$&cgqJWjqI)Yv1UAWOZcQOFl%*$T|yyUzBtrWd_#5_Q*rKHxv4=N-E-{~J7jbfY{5jv zT0mBp^8EB6I_8bb!wOpete%=|4py-IaAP{!z;9v*x3-py8z^`l%IpO`oNh~UwH;Mq zAK}D!BIyp_JGeJrVK`P80N5e2{XvWUAV?Ab(X9?%>jVHqzJmpQ4G>_z$CeuWCl~`mJ28*c(+4@!W}(D?QcN7P~yYNHfEA|tzX6RO79&ET)(?n zA;H$&;!#m3NbPX+5!EiZ>wr;S9^^_K?Ml?)rCLV5C0MbZ;rnSWirw%oRt-8yq2+Kj z?fd3=Ewd|k>OH1iNYPm;cuDQgzQ=zCh<_`f$43D4SFjP1vWW#M(aZ`h{>f;aYwiHV ziJErs70-=0^bk?4HM&clUXXU8iX&;TVbOzj$AFliM+6y`#@2 z%$3qVXCKBqcRzb@-ru9Aj^D^zFV^+uu6Ihz<;2l5+05i&VdrZ$KMTq+m&=%0GbNDF+5U_7Ziv+=MHom+Fx1m&^9U17&x+L%3%G0lh~r zBxXQfx3~7AYoK?%4jGx>FanOlE+;A(gf8-ciD5z9ltI1jMU(}|_ zCk6E?B|B-}3(R6EiPU>dkgPicN?EuG`ggurnew65Jjq;IY|1=)}v_Rn+ zZCuB-hl&)npSXnBxG5FmveI|%o_(#d-e?e_{L#NBYPw##uIb!sCpY4kX4!DA+rf-L zVB#o_<2Ysd3j37}+5I6W=t6VDu$PYMuOT=tv%NBmaz3mvOdaA6Bc5(cyd)QHcVHLA zR^RXIt}NskQ0Nm(^|L}70;xoK#b#H5Qb(#@nkTBd>iKQhA!mA-rmXL*;)Ke5Q!Rq& zfVIzCNhC6`l$h*LxjQpROojw@wFGz!t?u(ehIpW`dmKEgTm=s@?k@#%TrSL zYcONAiShA^V9h=$@jDrmi*Ldvm*-Lpye3A)cbaFZHr)#H^bBVOPJJ`-l>(D< z>ckh9i3piO%S)!5lFW)2P-8waNh;YR#KW!FV*gR^?qDP*7)zi&NFQmZ{ zOeKAu5^$uFuhC$zNGn+kKSyGnn0=H%cn*dJmra|sjEt}?z%C|v^P|h8H)z$x@Ft@r zwXlfD)l-b$QMp-Qjb-}{bG5!%rnyJ(4l95gZ&65C9V=`P!bg&!HV zd)40=S_NdA44eMsi29LXyH;#?+E%cH{u_h9;kHx!r3-&4#)hbD#cjc6Gl>m}`4PIk eIuctr!HOw>ZVC(*4HVlr1qwcdAT3qU#_mtJ{_K$e literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/simpleIndexWithCorruptedCertificate.jar b/app/src/test/resources/simpleIndexWithCorruptedCertificate.jar similarity index 100% rename from app/src/androidTest/assets/simpleIndexWithCorruptedCertificate.jar rename to app/src/test/resources/simpleIndexWithCorruptedCertificate.jar diff --git a/app/src/androidTest/assets/simpleIndexWithCorruptedEverything.jar b/app/src/test/resources/simpleIndexWithCorruptedEverything.jar similarity index 100% rename from app/src/androidTest/assets/simpleIndexWithCorruptedEverything.jar rename to app/src/test/resources/simpleIndexWithCorruptedEverything.jar diff --git a/app/src/androidTest/assets/simpleIndexWithCorruptedManifest.jar b/app/src/test/resources/simpleIndexWithCorruptedManifest.jar similarity index 100% rename from app/src/androidTest/assets/simpleIndexWithCorruptedManifest.jar rename to app/src/test/resources/simpleIndexWithCorruptedManifest.jar diff --git a/app/src/androidTest/assets/simpleIndexWithCorruptedSignature.jar b/app/src/test/resources/simpleIndexWithCorruptedSignature.jar similarity index 100% rename from app/src/androidTest/assets/simpleIndexWithCorruptedSignature.jar rename to app/src/test/resources/simpleIndexWithCorruptedSignature.jar diff --git a/app/src/androidTest/assets/simpleIndexWithoutSignature.jar b/app/src/test/resources/simpleIndexWithoutSignature.jar similarity index 100% rename from app/src/androidTest/assets/simpleIndexWithoutSignature.jar rename to app/src/test/resources/simpleIndexWithoutSignature.jar diff --git a/config/pmd/rules-main.xml b/config/pmd/rules-main.xml new file mode 100644 index 000000000..5b33b45d8 --- /dev/null +++ b/config/pmd/rules-main.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/config/pmd/rules-test.xml b/config/pmd/rules-test.xml new file mode 100644 index 000000000..0889f5bcf --- /dev/null +++ b/config/pmd/rules-test.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/config/pmd/rules.xml b/config/pmd/rules.xml index 3e4600f4e..e9a161577 100644 --- a/config/pmd/rules.xml +++ b/config/pmd/rules.xml @@ -12,7 +12,6 @@ - From 53e74dcdbd9c8fee5340e04955469c0059584d17 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Thu, 9 Jun 2016 10:25:50 +1000 Subject: [PATCH 09/10] Appease checkstyle for test code. Like PMD, we also had to add a concession to allow static imports. This time, it was achieved by moving the assertions to a more generally named `Assert` class, and then allowing static imports from that. --- .../ProviderTestUtils.java => Assert.java} | 7 ++- .../fdroid/fdroid/MultiRepoUpdaterTest.java | 4 +- .../java/org/fdroid/fdroid/TestFDroidApp.java | 4 +- .../fdroid/fdroid/data/ApkProviderTest.java | 49 ++++++++++--------- .../fdroid/fdroid/data/AppProviderTest.java | 10 ++-- .../fdroid/data/FDroidProviderTest.java | 1 - .../fdroid/data/InstalledAppProviderTest.java | 6 +-- .../fdroid/fdroid/data/ProviderUriTests.java | 12 ++--- config/checkstyle/checkstyle.xml | 4 +- 9 files changed, 49 insertions(+), 48 deletions(-) rename app/src/test/java/org/fdroid/fdroid/{data/ProviderTestUtils.java => Assert.java} (97%) diff --git a/app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java b/app/src/test/java/org/fdroid/fdroid/Assert.java similarity index 97% rename from app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java rename to app/src/test/java/org/fdroid/fdroid/Assert.java index ce277c31a..1296e0f9c 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ProviderTestUtils.java +++ b/app/src/test/java/org/fdroid/fdroid/Assert.java @@ -1,4 +1,4 @@ -package org.fdroid.fdroid.data; +package org.fdroid.fdroid; import android.content.ContentValues; import android.database.Cursor; @@ -6,6 +6,9 @@ import android.net.Uri; import junit.framework.AssertionFailedError; +import org.fdroid.fdroid.data.ApkProvider; +import org.fdroid.fdroid.data.AppProvider; +import org.fdroid.fdroid.data.InstalledAppProvider; import org.robolectric.shadows.ShadowContentResolver; import java.util.ArrayList; @@ -17,7 +20,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; -public class ProviderTestUtils { +public class Assert { public static void assertContainsOnly(List actualList, T[] expectedArray) { List expectedList = new ArrayList<>(expectedArray.length); diff --git a/app/src/test/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java b/app/src/test/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java index 79e31254b..2da14d8eb 100644 --- a/app/src/test/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java +++ b/app/src/test/java/org/fdroid/fdroid/MultiRepoUpdaterTest.java @@ -20,12 +20,12 @@ import java.io.File; import java.util.List; import java.util.UUID; -import static org.fdroid.fdroid.TestUtils.copyResourceToTempFile; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; public abstract class MultiRepoUpdaterTest extends FDroidProviderTest { + @SuppressWarnings("unused") private static final String TAG = "AcceptableMultiRepoUpdaterTest"; // NOPMD protected static final String REPO_MAIN = "Test F-Droid repo"; @@ -187,7 +187,7 @@ public abstract class MultiRepoUpdaterTest extends FDroidProviderTest { } private boolean updateRepo(RepoUpdater updater, String indexJarPath) throws UpdateException { - File indexJar = copyResourceToTempFile(indexJarPath); + File indexJar = TestUtils.copyResourceToTempFile(indexJarPath); try { updater.processDownloadedFile(indexJar); } finally { diff --git a/app/src/test/java/org/fdroid/fdroid/TestFDroidApp.java b/app/src/test/java/org/fdroid/fdroid/TestFDroidApp.java index b0fcd8319..15bef648f 100644 --- a/app/src/test/java/org/fdroid/fdroid/TestFDroidApp.java +++ b/app/src/test/java/org/fdroid/fdroid/TestFDroidApp.java @@ -8,4 +8,6 @@ import android.app.Application; * {@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 {} +public class TestFDroidApp extends Application { + +} diff --git a/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java index 6d3c6811e..03e7ebfa0 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java @@ -5,6 +5,7 @@ import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; +import org.fdroid.fdroid.Assert; import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.mock.MockApk; @@ -20,9 +21,9 @@ 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.Assert.assertCantDelete; +import static org.fdroid.fdroid.Assert.assertContainsOnly; +import static org.fdroid.fdroid.Assert.assertResultCount; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -37,8 +38,8 @@ public class ApkProviderTest extends FDroidProviderTest { @Test public void testAppApks() { for (int i = 1; i <= 10; i++) { - ProviderTestUtils.insertApk(contentResolver, "org.fdroid.fdroid", i); - ProviderTestUtils.insertApk(contentResolver, "com.example", i); + Assert.insertApk(contentResolver, "org.fdroid.fdroid", i); + Assert.insertApk(contentResolver, "com.example", i); } assertTotalApkCount(20); @@ -186,7 +187,7 @@ public class ApkProviderTest extends FDroidProviderTest { Apk apk = new MockApk("org.fdroid.fdroid", 13); // Insert a new record... - Uri newUri = ProviderTestUtils.insertApk(contentResolver, apk.packageName, apk.versionCode); + Uri newUri = Assert.insertApk(contentResolver, apk.packageName, apk.versionCode); assertEquals(ApkProvider.getContentUri(apk).toString(), newUri.toString()); cursor = queryAllApks(); assertNotNull(cursor); @@ -204,7 +205,7 @@ public class ApkProviderTest extends FDroidProviderTest { @Test(expected = IllegalArgumentException.class) public void testCursorMustMoveToFirst() { - ProviderTestUtils.insertApk(contentResolver, "org.example.test", 12); + Assert.insertApk(contentResolver, "org.example.test", 12); Cursor cursor = queryAllApks(); new Apk(cursor); } @@ -214,7 +215,7 @@ public class ApkProviderTest extends FDroidProviderTest { String[] projectionCount = new String[] {ApkProvider.DataColumns._COUNT}; for (int i = 0; i < 13; i++) { - ProviderTestUtils.insertApk(contentResolver, "com.example", i); + Assert.insertApk(contentResolver, "com.example", i); } Uri all = ApkProvider.getContentUri(); @@ -259,7 +260,7 @@ public class ApkProviderTest extends FDroidProviderTest { public void assertInvalidExtraField(String field) { ContentValues invalidRepo = new ContentValues(); invalidRepo.put(field, "Test data"); - ProviderTestUtils.insertApk(contentResolver, "org.fdroid.fdroid", 10, invalidRepo); + Assert.insertApk(contentResolver, "org.fdroid.fdroid", 10, invalidRepo); } @Test @@ -272,7 +273,7 @@ public class ApkProviderTest extends FDroidProviderTest { 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 = ProviderTestUtils.insertApk(contentResolver, "com.example.com", 1, values); + Uri uri = Assert.insertApk(contentResolver, "com.example.com", 1, values); assertResultCount(1, queryAllApks()); @@ -298,18 +299,18 @@ public class ApkProviderTest extends FDroidProviderTest { public void testKnownApks() { for (int i = 0; i < 7; i++) { - ProviderTestUtils.insertApk(contentResolver, "org.fdroid.fdroid", i); + Assert.insertApk(contentResolver, "org.fdroid.fdroid", i); } for (int i = 0; i < 9; i++) { - ProviderTestUtils.insertApk(contentResolver, "org.example", i); + Assert.insertApk(contentResolver, "org.example", i); } for (int i = 0; i < 3; i++) { - ProviderTestUtils.insertApk(contentResolver, "com.example", i); + Assert.insertApk(contentResolver, "com.example", i); } - ProviderTestUtils.insertApk(contentResolver, "com.apk.thingo", 1); + Assert.insertApk(contentResolver, "com.apk.thingo", 1); Apk[] known = { new MockApk("org.fdroid.fdroid", 1), @@ -356,18 +357,18 @@ public class ApkProviderTest extends FDroidProviderTest { public void testFindByApp() { for (int i = 0; i < 7; i++) { - ProviderTestUtils.insertApk(contentResolver, "org.fdroid.fdroid", i); + Assert.insertApk(contentResolver, "org.fdroid.fdroid", i); } for (int i = 0; i < 9; i++) { - ProviderTestUtils.insertApk(contentResolver, "org.example", i); + Assert.insertApk(contentResolver, "org.example", i); } for (int i = 0; i < 3; i++) { - ProviderTestUtils.insertApk(contentResolver, "com.example", i); + Assert.insertApk(contentResolver, "com.example", i); } - ProviderTestUtils.insertApk(contentResolver, "com.apk.thingo", 1); + Assert.insertApk(contentResolver, "com.apk.thingo", 1); assertTotalApkCount(7 + 9 + 3 + 1); @@ -391,7 +392,7 @@ public class ApkProviderTest extends FDroidProviderTest { @Test public void testUpdate() { - Uri apkUri = ProviderTestUtils.insertApk(contentResolver, "com.example", 10); + Uri apkUri = Assert.insertApk(contentResolver, "com.example", 10); String[] allFields = ApkProvider.DataColumns.ALL; Cursor cursor = contentResolver.query(apkUri, allFields, null, null, null); @@ -447,18 +448,18 @@ public class ApkProviderTest extends FDroidProviderTest { // 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++) { - ProviderTestUtils.insertApk(contentResolver, "org.fdroid.apk." + i, i); + Assert.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"); - ProviderTestUtils.insertApk(contentResolver, "com.example", 11, values); + Assert.insertApk(contentResolver, "com.example", 11, values); // ...and a few more for good measure... for (int i = 15; i < 20; i++) { - ProviderTestUtils.insertApk(contentResolver, "com.other.thing." + i, i); + Assert.insertApk(contentResolver, "com.other.thing." + i, i); } Apk apk = ApkProvider.Helper.find(context, "com.example", 11); @@ -507,7 +508,7 @@ public class ApkProviderTest extends FDroidProviderTest { } } if (!found) { - fail("Apk [" + apk + "] not found in " + ProviderTestUtils.listToString(apks)); + fail("Apk [" + apk + "] not found in " + Assert.listToString(apks)); } } @@ -534,7 +535,7 @@ public class ApkProviderTest extends FDroidProviderTest { protected Apk insertApkForRepo(String id, int versionCode, long repoId) { ContentValues additionalValues = new ContentValues(); additionalValues.put(ApkProvider.DataColumns.REPO_ID, repoId); - Uri uri = ProviderTestUtils.insertApk(contentResolver, id, versionCode, additionalValues); + Uri uri = Assert.insertApk(contentResolver, id, versionCode, additionalValues); return ApkProvider.Helper.get(context, uri); } } diff --git a/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java index 779ca10f4..9178eb2c3 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java @@ -18,8 +18,8 @@ import org.robolectric.shadows.ShadowContentResolver; import java.util.ArrayList; import java.util.List; -import static org.fdroid.fdroid.data.ProviderTestUtils.assertContainsOnly; -import static org.fdroid.fdroid.data.ProviderTestUtils.assertResultCount; +import static org.fdroid.fdroid.Assert.assertContainsOnly; +import static org.fdroid.fdroid.Assert.assertResultCount; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -253,7 +253,7 @@ public class AppProviderTest extends FDroidProviderTest { String[] projection = new String[] { AppProvider.DataColumns._ID, AppProvider.DataColumns.NAME, - AppProvider.DataColumns.PACKAGE_NAME + AppProvider.DataColumns.PACKAGE_NAME, }; return contentResolver.query(AppProvider.getContentUri(), projection, null, null, null); } @@ -366,8 +366,4 @@ public class AppProviderTest extends FDroidProviderTest { contentResolver.insert(uri, 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/FDroidProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/FDroidProviderTest.java index 5bef2d769..adddf695c 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/FDroidProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/FDroidProviderTest.java @@ -35,5 +35,4 @@ public abstract class FDroidProviderTest { FDroidProvider.clearDbHelperSingleton(); } - } diff --git a/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java index 5afc4cfc2..e207f1a16 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java @@ -14,8 +14,8 @@ import org.robolectric.RuntimeEnvironment; 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.fdroid.fdroid.Assert.assertIsInstalledVersionInDb; +import static org.fdroid.fdroid.Assert.assertResultCount; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -24,7 +24,7 @@ import java.util.Map; @Config(constants = BuildConfig.class, application = Application.class) @RunWith(RobolectricGradleTestRunner.class) -public class InstalledAppProviderTest extends FDroidProviderTest{ +public class InstalledAppProviderTest extends FDroidProviderTest { @Before public void setup() { 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 2e75977ea..118adcf65 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java @@ -15,8 +15,8 @@ import org.robolectric.shadows.ShadowContentResolver; import java.util.ArrayList; import java.util.List; -import static org.fdroid.fdroid.data.ProviderTestUtils.assertInvalidUri; -import static org.fdroid.fdroid.data.ProviderTestUtils.assertValidUri; +import static org.fdroid.fdroid.Assert.assertInvalidUri; +import static org.fdroid.fdroid.Assert.assertValidUri; @Config(constants = BuildConfig.class) @RunWith(RobolectricGradleTestRunner.class) @@ -44,7 +44,7 @@ public class ProviderUriTests { @Test public void validInstalledAppProviderUris() { ShadowContentResolver.registerProvider(InstalledAppProvider.getAuthority(), new InstalledAppProvider()); - String[] projection = new String[] { InstalledAppProvider.DataColumns._ID }; + 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); @@ -63,7 +63,7 @@ public class ProviderUriTests { @Test public void validRepoProviderUris() { ShadowContentResolver.registerProvider(RepoProvider.getAuthority(), new RepoProvider()); - String[] projection = new String[] { RepoProvider.DataColumns._ID }; + String[] projection = new String[] {RepoProvider.DataColumns._ID}; assertValidUri(resolver, RepoProvider.getContentUri(), projection); assertValidUri(resolver, RepoProvider.getContentUri(10000L), projection); assertValidUri(resolver, RepoProvider.allExceptSwapUri(), projection); @@ -79,7 +79,7 @@ public class ProviderUriTests { @Test public void validAppProviderUris() { ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); - String[] projection = new String[] { AppProvider.DataColumns._ID }; + String[] projection = new String[] {AppProvider.DataColumns._ID}; assertValidUri(resolver, AppProvider.getContentUri(), "content://org.fdroid.fdroid.data.AppProvider", projection); assertValidUri(resolver, AppProvider.getSearchUri("'searching!'"), "content://org.fdroid.fdroid.data.AppProvider/search/'searching!'", projection); assertValidUri(resolver, AppProvider.getSearchUri("/"), "content://org.fdroid.fdroid.data.AppProvider/search/%2F", projection); @@ -110,7 +110,7 @@ public class ProviderUriTests { @Test public void validApkProviderUris() { ShadowContentResolver.registerProvider(ApkProvider.getAuthority(), new ApkProvider()); - String[] projection = new String[] { ApkProvider.DataColumns._ID }; + String[] projection = new String[] {ApkProvider.DataColumns._ID}; List apks = new ArrayList<>(10); for (int i = 0; i < 10; i++) { diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 8bb944a11..735e5d608 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -32,8 +32,8 @@ - + From 182a63af41f9408447b4df14176fc847608cf8ed Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Thu, 9 Jun 2016 10:40:44 +1000 Subject: [PATCH 10/10] Ensure tests for proper multirepo support are not run. They are here so that when we support multiple repos in a more robust manner, we can use these tests to show that it was successful. --- .../java/org/fdroid/fdroid/ProperMultiRepoUpdaterTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/test/java/org/fdroid/fdroid/ProperMultiRepoUpdaterTest.java b/app/src/test/java/org/fdroid/fdroid/ProperMultiRepoUpdaterTest.java index da7105e42..b0e992854 100644 --- a/app/src/test/java/org/fdroid/fdroid/ProperMultiRepoUpdaterTest.java +++ b/app/src/test/java/org/fdroid/fdroid/ProperMultiRepoUpdaterTest.java @@ -3,12 +3,10 @@ package org.fdroid.fdroid; import android.util.Log; -import org.fdroid.fdroid.RepoUpdater.UpdateException; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.ApkProvider; import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoProvider; -import org.junit.Test; import java.util.List; @@ -22,10 +20,11 @@ uncommented and all these tests will be required to pass: @Config(constants = BuildConfig.class) @RunWith(RobolectricGradleTestRunner.class) */ +@SuppressWarnings("unused") public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest { private static final String TAG = "ProperMultiRepoSupport"; - @Test + /*@Test public void testCorrectConflictingThenMainThenArchive() throws UpdateException { assertEmpty(); if (updateConflicting() && updateMain() && updateArchive()) { @@ -71,7 +70,7 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest { if (updateMain() && updateConflicting() && updateArchive()) { assertExpected(); } - } + }*/ /** * Check that all of the expected apps and apk versions are available in the database. This