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.
This commit is contained in:
		
							parent
							
								
									09fd3d188c
								
							
						
					
					
						commit
						4e66bb810f
					
				@ -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'
 | 
			
		||||
 | 
			
		||||
@ -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<ApkProvider> providerTest, String id, int versionCode) {
 | 
			
		||||
    public static Uri insertApk(FDroidProviderTestOld<ApkProvider> providerTest, String id, int versionCode) {
 | 
			
		||||
        return insertApk(providerTest, id, versionCode, new ContentValues());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Uri insertApk(FDroidProviderTest<ApkProvider> providerTest, String id, int versionCode, ContentValues additionalValues) {
 | 
			
		||||
    public static Uri insertApk(FDroidProviderTestOld<ApkProvider> providerTest, String id, int versionCode, ContentValues additionalValues) {
 | 
			
		||||
 | 
			
		||||
        ContentValues values = new ContentValues();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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<AppProvider> {
 | 
			
		||||
 | 
			
		||||
    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<App> 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<String> 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<App> 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<App> actualApps, String[] expectedIds) {
 | 
			
		||||
        List<String> 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<String> 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<String> 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<String> 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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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<ApkProvider> {
 | 
			
		||||
abstract class BaseApkProviderTest extends FDroidProviderTestOld<ApkProvider> {
 | 
			
		||||
 | 
			
		||||
    BaseApkProviderTest() {
 | 
			
		||||
        super(ApkProvider.class, ApkProvider.getAuthority());
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ import mock.MockContextSwappableComponents;
 | 
			
		||||
import mock.MockFDroidResources;
 | 
			
		||||
 | 
			
		||||
@SuppressWarnings("PMD")  // TODO port this to JUnit 4 semantics
 | 
			
		||||
public abstract class FDroidProviderTest<T extends FDroidProvider> extends ProviderTestCase2MockContext<T> {
 | 
			
		||||
public abstract class FDroidProviderTestOld<T extends FDroidProvider> extends ProviderTestCase2MockContext<T> {
 | 
			
		||||
 | 
			
		||||
    private FDroidProvider[] allProviders = {
 | 
			
		||||
        new AppProvider(),
 | 
			
		||||
@ -26,7 +26,7 @@ public abstract class FDroidProviderTest<T extends FDroidProvider> extends Provi
 | 
			
		||||
 | 
			
		||||
    private MockContextSwappableComponents swappableContext;
 | 
			
		||||
 | 
			
		||||
    public FDroidProviderTest(Class<T> providerClass, String providerAuthority) {
 | 
			
		||||
    public FDroidProviderTestOld(Class<T> providerClass, String providerAuthority) {
 | 
			
		||||
        super(providerClass, providerAuthority);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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<InstalledAppProvider>, although there is also a
 | 
			
		||||
 * For practical reasons, it extends FDroidProviderTestOld<InstalledAppProvider>, 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<InstalledAppProvider> {
 | 
			
		||||
public class InstalledAppCacheTest extends FDroidProviderTestOld<InstalledAppProvider> {
 | 
			
		||||
 | 
			
		||||
    private MockInstallablePackageManager packageManager;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										374
									
								
								app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java
									
									
									
									
									
										Normal file
									
								
							@ -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<String> 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<App> 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<App> actualApps, String[] expectedIds) {
 | 
			
		||||
        List<String> 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<String> 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<String> 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<String> 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
 | 
			
		||||
@ -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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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<String, Long> foundBefore = InstalledAppProvider.Helper.all(RuntimeEnvironment.application);
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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 <T extends Comparable> void assertContainsOnly(List<T> actualList, T[] expectedArray) {
 | 
			
		||||
        List<T> expectedList = new ArrayList<>(expectedArray.length);
 | 
			
		||||
        Collections.addAll(expectedList, expectedArray);
 | 
			
		||||
        assertContainsOnly(actualList, expectedList);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <T extends Comparable> void assertContainsOnly(T[] actualArray, List<T> expectedList) {
 | 
			
		||||
        List<T> actualList = new ArrayList<>(actualArray.length);
 | 
			
		||||
        Collections.addAll(actualList, actualArray);
 | 
			
		||||
        assertContainsOnly(actualList, expectedList);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <T extends Comparable> void assertContainsOnly(T[] actualArray, T[] expectedArray) {
 | 
			
		||||
        List<T> expectedList = new ArrayList<>(expectedArray.length);
 | 
			
		||||
        Collections.addAll(expectedList, expectedArray);
 | 
			
		||||
        assertContainsOnly(actualArray, expectedList);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <T> String listToString(List<T> list) {
 | 
			
		||||
        String string = "[";
 | 
			
		||||
        for (int i = 0; i < list.size(); i++) {
 | 
			
		||||
            if (i > 0) {
 | 
			
		||||
                string += ", ";
 | 
			
		||||
            }
 | 
			
		||||
            string += "'" + list.get(i) + "'";
 | 
			
		||||
        }
 | 
			
		||||
        string += "]";
 | 
			
		||||
        return string;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <T extends Comparable> void assertContainsOnly(List<T> actualList, List<T> 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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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<App> 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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user