Finalise tests for repo priorities + app metadata

This commit is contained in:
Peter Serwylo 2016-08-08 22:13:09 +10:00
parent 1d1c1ebb74
commit 3ec64d6d82
5 changed files with 106 additions and 20 deletions

View File

@ -122,7 +122,8 @@ public class AppProvider extends FDroidProvider {
}
public static App findHighestPriorityMetadata(ContentResolver resolver, String packageName) {
throw new UnsupportedOperationException("TODO: Pull back the metadata with the highest priority for packageName");
final Uri uri = getHighestPriorityMetadataUri(packageName);
return cursorToApp(resolver.query(uri, Cols.ALL, null, null, null));
}
public static App findByPackageName(ContentResolver resolver, String packageName, long repoId) {
@ -266,8 +267,8 @@ public class AppProvider extends FDroidProvider {
return pkg +
" JOIN " + app + " ON (" + app + "." + Cols.PACKAGE_ID + " = " + pkg + "." + PackageTable.Cols.ROW_ID + ") " +
" LEFT JOIN " + apk + " ON (" + apk + "." + ApkTable.Cols.APP_ID + " = " + app + "." + Cols.ROW_ID + ") " +
" LEFT JOIN " + repo + " ON (" + apk + "." + ApkTable.Cols.REPO_ID + " = " + repo + "." + RepoTable.Cols._ID + ") ";
" JOIN " + repo + " ON (" + app + "." + Cols.REPO_ID + " = " + repo + "." + RepoTable.Cols._ID + ") " +
" LEFT JOIN " + apk + " ON (" + apk + "." + ApkTable.Cols.APP_ID + " = " + app + "." + Cols.ROW_ID + ") ";
}
@Override
@ -420,6 +421,7 @@ public class AppProvider extends FDroidProvider {
private static final String PATH_CATEGORY = "category";
private static final String PATH_CALC_APP_DETAILS_FROM_INDEX = "calcDetailsFromIndex";
private static final String PATH_REPO = "repo";
private static final String PATH_HIGHEST_PRIORITY = "highestPriority";
private static final int CAN_UPDATE = CODE_SINGLE + 1;
private static final int INSTALLED = CAN_UPDATE + 1;
@ -433,6 +435,7 @@ public class AppProvider extends FDroidProvider {
private static final int SEARCH_REPO = REPO + 1;
private static final int SEARCH_INSTALLED = SEARCH_REPO + 1;
private static final int SEARCH_CAN_UPDATE = SEARCH_INSTALLED + 1;
private static final int HIGHEST_PRIORITY = SEARCH_CAN_UPDATE + 1;
static {
MATCHER.addURI(getAuthority(), null, CODE_LIST);
@ -448,6 +451,7 @@ public class AppProvider extends FDroidProvider {
MATCHER.addURI(getAuthority(), PATH_CAN_UPDATE, CAN_UPDATE);
MATCHER.addURI(getAuthority(), PATH_INSTALLED, INSTALLED);
MATCHER.addURI(getAuthority(), PATH_NO_APKS, NO_APKS);
MATCHER.addURI(getAuthority(), PATH_HIGHEST_PRIORITY + "/*", HIGHEST_PRIORITY);
MATCHER.addURI(getAuthority(), PATH_APP + "/#/*", CODE_SINGLE);
}
@ -510,6 +514,13 @@ public class AppProvider extends FDroidProvider {
.build();
}
private static Uri getHighestPriorityMetadataUri(String packageName) {
return getContentUri().buildUpon()
.appendPath(PATH_HIGHEST_PRIORITY)
.appendPath(packageName)
.build();
}
public static Uri getContentUri(String packageName) {
return Uri.withAppendedPath(getContentUri(), packageName);
}
@ -649,10 +660,9 @@ public class AppProvider extends FDroidProvider {
}
protected AppQuerySelection querySingle(String packageName, long repoId) {
final String selection = PackageTable.NAME + "." + PackageTable.Cols.PACKAGE_NAME + " = ? " +
" AND " + getTableName() + "." + Cols.REPO_ID + " = ? ";
final String[] args = {packageName, Long.toString(repoId)};
return new AppQuerySelection(selection, args);
final String selection = getTableName() + "." + Cols.REPO_ID + " = ? ";
final String[] args = {Long.toString(repoId)};
return new AppQuerySelection(selection, args).add(queryPackageName(packageName));
}
/**
@ -683,6 +693,23 @@ public class AppProvider extends FDroidProvider {
return new AppQuerySelection(selection, args);
}
private AppQuerySelection queryHighestPriority() {
final String highestPriority =
"SELECT MIN(r." + RepoTable.Cols.PRIORITY + ") " +
"FROM " + RepoTable.NAME + " AS r " +
"JOIN " + AppMetadataTable.NAME + " AS m ON (m." + Cols.REPO_ID + " = r." + RepoTable.Cols._ID + ") " +
"WHERE m." + Cols.PACKAGE_ID + " = " + getTableName() + "." + Cols.PACKAGE_ID;
final String selection = RepoTable.NAME + "." + RepoTable.Cols.PRIORITY + " = (" + highestPriority + ")";
return new AppQuerySelection(selection);
}
private AppQuerySelection queryPackageName(String packageName) {
final String selection = PackageTable.NAME + "." + PackageTable.Cols.PACKAGE_NAME + " = ? ";
final String[] args = {packageName};
return new AppQuerySelection(selection, args);
}
private AppQuerySelection queryRecentlyUpdated() {
final String app = getTableName();
final String lastUpdated = app + "." + Cols.LAST_UPDATED;
@ -729,6 +756,14 @@ public class AppProvider extends FDroidProvider {
// Queries which are for the main list of apps should not include swap apps.
boolean includeSwap = true;
// It is usually the case that we ask for app(s) for which we don't care what repo is
// responsible for providing them. In that case, we need to populate the metadata with
// that form the repo with the highest priority.
// Whenever we know which repo it is coming from, then it is important that we don't
// delegate to the repo with the highest priority, but rather the specific repo we are
// querying from.
boolean repoIsKnown = false;
switch (MATCHER.match(uri)) {
case CODE_LIST:
includeSwap = false;
@ -739,6 +774,7 @@ public class AppProvider extends FDroidProvider {
long repoId = Long.parseLong(pathParts.get(1));
String packageName = pathParts.get(2);
selection = selection.add(querySingle(packageName, repoId));
repoIsKnown = true;
break;
case CAN_UPDATE:
@ -748,6 +784,7 @@ public class AppProvider extends FDroidProvider {
case REPO:
selection = selection.add(queryRepo(Long.parseLong(uri.getLastPathSegment())));
repoIsKnown = true;
break;
case INSTALLED:
@ -774,6 +811,7 @@ public class AppProvider extends FDroidProvider {
selection = selection
.add(querySearch(uri.getPathSegments().get(2)))
.add(queryRepo(Long.parseLong(uri.getPathSegments().get(1))));
repoIsKnown = true;
break;
case NO_APKS:
@ -797,11 +835,20 @@ public class AppProvider extends FDroidProvider {
includeSwap = false;
break;
case HIGHEST_PRIORITY:
selection = selection.add(queryPackageName(uri.getLastPathSegment()));
includeSwap = false;
break;
default:
Log.e(TAG, "Invalid URI for app content provider: " + uri);
throw new UnsupportedOperationException("Invalid URI for app content provider: " + uri);
}
if (!repoIsKnown) {
selection = selection.add(queryHighestPriority());
}
return runQuery(uri, selection, projection, includeSwap, sortOrder);
}

View File

@ -29,7 +29,8 @@ public class PackageProvider extends FDroidProvider {
}
public static long getPackageId(Context context, String packageName) {
Cursor cursor = context.getContentResolver().query(getPackageUri(packageName), Cols.ALL, null, null, null);
String[] projection = new String[] {Cols.ROW_ID};
Cursor cursor = context.getContentResolver().query(getPackageUri(packageName), projection, null, null, null);
if (cursor == null) {
return 0;
}
@ -39,7 +40,7 @@ public class PackageProvider extends FDroidProvider {
return 0;
} else {
cursor.moveToFirst();
return cursor.getLong(cursor.getColumnIndexOrThrow(Cols.PACKAGE_NAME));
return cursor.getLong(cursor.getColumnIndexOrThrow(Cols.ROW_ID));
}
} finally {
cursor.close();

View File

@ -115,7 +115,7 @@ public interface Schema {
* @see Cols#ALL_COLS
*/
String[] ALL = {
_ID, ROW_ID, IS_COMPATIBLE, NAME, SUMMARY, ICON, DESCRIPTION,
_ID, ROW_ID, REPO_ID, IS_COMPATIBLE, NAME, SUMMARY, ICON, DESCRIPTION,
LICENSE, AUTHOR, EMAIL, WEB_URL, TRACKER_URL, SOURCE_URL,
CHANGELOG_URL, DONATE_URL, BITCOIN_ADDR, LITECOIN_ADDR, FLATTR_ID,
UPSTREAM_VERSION_NAME, UPSTREAM_VERSION_CODE, ADDED, LAST_UPDATED,

View File

@ -7,6 +7,7 @@ import org.fdroid.fdroid.data.ApkProvider;
import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.data.Schema;
import org.fdroid.fdroid.updater.MultiRepoUpdaterTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

View File

@ -1,6 +1,7 @@
package org.fdroid.fdroid.updater;
import android.content.ContentValues;
import android.support.annotation.StringDef;
import android.util.Log;
@ -13,6 +14,7 @@ import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.data.Schema;
import org.fdroid.fdroid.data.Schema.RepoTable.Cols;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner;
@ -20,7 +22,9 @@ import org.robolectric.annotation.Config;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -56,13 +60,24 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
assertConflictingRepo();
}
private Map<String, App> allApps() {
List<App> apps = AppProvider.Helper.all(context.getContentResolver());
Map<String, App> appsIndexedByPackageName = new HashMap<>(apps.size());
for (App app : apps) {
appsIndexedByPackageName.put(app.packageName, app);
}
return appsIndexedByPackageName;
}
@Test
public void conflictingMetadataTakesPriority() throws RepoUpdater.UpdateException {
public void metadataWithRepoPriority() throws RepoUpdater.UpdateException {
updateConflicting();
updateMain();
updateArchive();
assertEquals(1, RepoProvider.Helper.findByAddress(context, REPO_CONFLICTING_URI).priority);
Repo conflictingRepo = RepoProvider.Helper.findByAddress(context, REPO_CONFLICTING_URI);
assertEquals(1, conflictingRepo.priority);
assertEquals(2, RepoProvider.Helper.findByAddress(context, REPO_MAIN_URI).priority);
assertEquals(3, RepoProvider.Helper.findByAddress(context, REPO_ARCHIVE_URI).priority);
@ -70,22 +85,44 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
assertMainArchiveRepoMetadata();
assertConflictingRepo();
App a2048 = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(), "com.uberspot.a2048");
assert2048Metadata(a2048, "Conflicting");
assertRepoTakesPriority("Conflicting");
// Make the conflicting repo less important than the main repo.
ContentValues values = new ContentValues(1);
values.put(Cols.PRIORITY, 5);
RepoProvider.Helper.update(context, conflictingRepo, values);
Repo updatedConflictingRepo = RepoProvider.Helper.findByAddress(context, REPO_CONFLICTING_URI);
assertEquals(5, updatedConflictingRepo.priority);
assertRepoTakesPriority("Normal");
}
private void assertRepoTakesPriority(@RepoIdentifier String higherPriority) {
Map<String, App> allApps = allApps();
// Provided by both the "Main" and "Conflicting" repo, so need to fetch metdata from the
// repo with the higher "Conflicting" repo has a higher priority.
App adAway = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(), "org.adaway");
assertAdAwayMetadata(adAway, higherPriority);
assertAdAwayMetadata(allApps.get("org.adaway"), higherPriority);
// This is only provided by the "Conflicting" repo.
App calendar = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(), "org.dgtale.icsimport");
assertCalendarMetadata(calendar, "Conflicting");
// This is only provided by the "Main" or "Archive" repo. Both the main and archive repo both
// pull their metadata from the same build recipe in fdroidserver. The only difference is that
// the archive repository contains .apks from further back, but their metadata is the same.
App adAway = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(), "org.adaway");
assertAdAwayMetadata(adAway, "Normal");
App a2048 = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(), "com.uberspot.a2048");
assert2048Metadata(a2048, "Normal");
assert2048Metadata(allApps.get("com.uberspot.a2048"), "Normal");
// This is only provided by the "Conflicting" repo.
App calendar = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(), "org.dgtale.icsimport");
assertCalendarMetadata(calendar, "Conflicting");
assertCalendarMetadata(allApps.get("org.dgtale.icsimport"), "Conflicting");
// This is only provided by the "Main" repo.
App adb = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(), "siir.es.adbWireless");
assertAdAwayMetadata(adb, "Normal");
assertAdbMetadata(adb, "Normal");
assertAdbMetadata(allApps.get("siir.es.adbWireless"), "Normal");
}
@Test