package org.fdroid.fdroid.updater;
import android.content.ContentValues;
import android.support.annotation.StringDef;
import android.util.Log;
import org.fdroid.fdroid.BuildConfig;
import org.fdroid.fdroid.RepoUpdater;
import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.ApkProvider;
import org.fdroid.fdroid.data.App;
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.AppMetadataTable;
import org.fdroid.fdroid.data.Schema.RepoTable.Cols;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner;
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;
// TODO: Use sdk=24 when Robolectric supports this
@Config(constants = BuildConfig.class, sdk = 23)
@RunWith(RobolectricGradleTestRunner.class)
public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
private static final String TAG = "ProperMultiRepoSupport";
@Retention(RetentionPolicy.SOURCE)
@StringDef({"Conflicting", "Normal"})
public @interface RepoIdentifier { }
/*
*This test fails due to issue #568 (https://gitlab.com/fdroid/fdroidclient/issues/568).
@Test
public void appsRemovedFromRepo() throws RepoUpdater.UpdateException {
assertEquals(0, AppProvider.Helper.all(context.getContentResolver()).size());
updateMain();
Repo repo = RepoProvider.Helper.findByAddress(context, REPO_MAIN_URI);
assertEquals(3, AppProvider.Helper.all(context.getContentResolver()).size());
assertEquals(6, ApkProvider.Helper.findByRepo(context, repo, Schema.ApkTable.Cols.ALL).size());
assertEquals(3, ApkProvider.Helper.findByPackageName(context, "org.adaway").size());
assertEquals(2, ApkProvider.Helper.findByPackageName(context, "com.uberspot.a2048").size());
assertEquals(1, ApkProvider.Helper.findByPackageName(context, "siir.es.adbWireless").size());
RepoUpdater updater = new RepoUpdater(context, RepoProvider.Helper.findByAddress(context, repo.address));
updateRepo(updater, "multiRepo.conflicting.jar");
assertEquals(2, AppProvider.Helper.all(context.getContentResolver()).size());
assertEquals(6, ApkProvider.Helper.findByRepo(context, repo, Schema.ApkTable.Cols.ALL).size());
assertEquals(4, ApkProvider.Helper.findByPackageName(context, "org.adaway").size());
assertEquals(2, ApkProvider.Helper.findByPackageName(context, "org.dgtale.icsimport").size());
}*/
@Test
public void mainRepo() throws RepoUpdater.UpdateException {
assertEmpty();
updateMain();
assertMainRepo();
}
@Test
public void archiveRepo() throws RepoUpdater.UpdateException {
assertEmpty();
updateArchive();
assertMainArchiveRepoMetadata();
}
@Test
public void conflictingRepo() throws RepoUpdater.UpdateException {
assertEmpty();
updateConflicting();
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 metadataWithRepoPriority() throws RepoUpdater.UpdateException {
updateConflicting();
updateMain();
updateArchive();
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);
assertMainRepo();
assertMainArchiveRepoMetadata();
assertConflictingRepo();
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 "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 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");
assertAdbMetadata(adb, "Normal");
assertAdbMetadata(allApps.get("siir.es.adbWireless"), "Normal");
}
@Test
public void testCorrectConflictingThenMainThenArchive() throws RepoUpdater.UpdateException {
assertEmpty();
updateConflicting();
updateMain();
updateArchive();
assertExpected();
}
@Test
public void testCorrectConflictingThenArchiveThenMain() throws RepoUpdater.UpdateException {
assertEmpty();
updateConflicting();
updateArchive();
updateMain();
assertExpected();
}
@Test
public void testCorrectArchiveThenMainThenConflicting() throws RepoUpdater.UpdateException {
assertEmpty();
updateArchive();
updateMain();
updateConflicting();
assertExpected();
}
@Test
public void testCorrectArchiveThenConflictingThenMain() throws RepoUpdater.UpdateException {
assertEmpty();
updateArchive();
updateConflicting();
updateMain();
assertExpected();
}
@Test
public void testCorrectMainThenArchiveThenConflicting() throws RepoUpdater.UpdateException {
assertEmpty();
updateMain();
updateArchive();
updateConflicting();
assertExpected();
}
@Test
public void testCorrectMainThenConflictingThenArchive() throws RepoUpdater.UpdateException {
assertEmpty();
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<Repo> repos = RepoProvider.Helper.all(context);
assertEquals("Repos", 3, repos.size());
assertMainRepo(repos);
assertMainArchiveRepoMetadata(repos);
assertConflictingRepo(repos);
}
private void assertMainRepo() {
assertMainRepo(RepoProvider.Helper.all(context));
}
/**
* + 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<Repo> allRepos) {
Repo repo = findRepo(REPO_MAIN, allRepos);
List<Apk> apks = ApkProvider.Helper.findByRepo(context, repo, Schema.ApkTable.Cols.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});
assert2048Metadata(repo, "Normal");
assertAdAwayMetadata(repo, "Normal");
assertAdbMetadata(repo, "Normal");
}
private void assert2048Metadata(Repo repo, @RepoIdentifier String id) {
App a2048 = AppProvider.Helper.findSpecificApp(context.getContentResolver(), "com.uberspot.a2048", repo.getId(), AppMetadataTable.Cols.ALL);
assert2048Metadata(a2048, id);
}
/**
* @param id An identifier that we've put in the metadata for each repo to ensure that
* we can identify the metadata is coming from the correct repo.
*/
private void assert2048Metadata(App a2048, @RepoIdentifier String id) {
assertNotNull(a2048);
assertEquals("2048", a2048.name);
assertEquals(String.format("<p>2048 from %s repo.</p>", id), a2048.description);
assertEquals(String.format("Puzzle game (%s)", id), a2048.summary);
assertEquals(String.format("https://github.com/uberspot/2048-android?%s", id), a2048.webURL);
assertEquals(String.format("https://github.com/uberspot/2048-android?code&%s", id), a2048.sourceURL);
assertEquals(String.format("https://github.com/uberspot/2048-android/issues?%s", id), a2048.trackerURL);
}
private void assertAdAwayMetadata(Repo repo, @RepoIdentifier String id) {
App adaway = AppProvider.Helper.findSpecificApp(context.getContentResolver(), "org.adaway", repo.getId(), AppMetadataTable.Cols.ALL);
assertAdAwayMetadata(adaway, id);
}
/** @see ProperMultiRepoUpdaterTest#assert2048Metadata(Repo, String) */
private void assertAdAwayMetadata(App adaway, @RepoIdentifier String id) {
assertNotNull(adaway);
assertEquals(String.format("AdAway", id), adaway.name);
assertEquals(String.format("<p>AdAway from %s repo.</p>", id), adaway.description);
assertEquals(String.format("Block advertisements (%s)", id), adaway.summary);
assertEquals(String.format("http://sufficientlysecure.org/index.php/adaway?%s", id), adaway.webURL);
assertEquals(String.format("https://github.com/dschuermann/ad-away?%s", id), adaway.sourceURL);
assertEquals(String.format("https://github.com/dschuermann/ad-away/issues?%s", id), adaway.trackerURL);
assertEquals(String.format("https://github.com/dschuermann/ad-away/raw/HEAD/CHANGELOG?%s", id), adaway.changelogURL);
assertEquals(String.format("http://sufficientlysecure.org/index.php/adaway?%s", id), adaway.donateURL);
assertEquals(String.format("369138", id), adaway.flattrID);
}
private void assertAdbMetadata(Repo repo, @RepoIdentifier String id) {
App adb = AppProvider.Helper.findSpecificApp(context.getContentResolver(), "siir.es.adbWireless", repo.getId(), AppMetadataTable.Cols.ALL);
assertAdbMetadata(adb, id);
}
/** @see ProperMultiRepoUpdaterTest#assert2048Metadata(Repo, String) */
private void assertAdbMetadata(App adb, @RepoIdentifier String id) {
assertNotNull(adb);
assertEquals("adbWireless", adb.name);
assertEquals(String.format("<p>adbWireless from %s repo.</p>", id), adb.description);
assertEquals(String.format("Wireless adb (%s)", id), adb.summary);
assertEquals(String.format("https://adbwireless.example.com?%s", id), adb.webURL);
assertEquals(String.format("https://adbwireless.example.com/source?%s", id), adb.sourceURL);
assertEquals(String.format("https://adbwireless.example.com/issues?%s", id), adb.trackerURL);
}
private void assertCalendarMetadata(Repo repo, @RepoIdentifier String id) {
App calendar = AppProvider.Helper.findSpecificApp(context.getContentResolver(), "org.dgtale.icsimport", repo.getId(), AppMetadataTable.Cols.ALL);
assertCalendarMetadata(calendar, id);
}
/** @see ProperMultiRepoUpdaterTest#assert2048Metadata(Repo, String) */
private void assertCalendarMetadata(App calendar, @RepoIdentifier String id) {
assertNotNull(calendar);
assertEquals("Add to calendar", calendar.name);
assertEquals(String.format("<p>Add to calendar from %s repo.</p>", id), calendar.description);
assertEquals(String.format("Import .ics files into calendar (%s)", id), calendar.summary);
assertEquals(String.format("https://github.com/danielegobbetti/ICSImport/blob/HEAD/README.md?%s", id), calendar.webURL);
assertEquals(String.format("https://github.com/danielegobbetti/ICSImport?%s", id), calendar.sourceURL);
assertEquals(String.format("https://github.com/danielegobbetti/ICSImport/issues?%s", id), calendar.trackerURL);
assertEquals("2225390", calendar.flattrID);
}
private void assertMainArchiveRepoMetadata() {
assertMainArchiveRepoMetadata(RepoProvider.Helper.all(context));
}
/**
* + 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 assertMainArchiveRepoMetadata(List<Repo> allRepos) {
Repo repo = findRepo(REPO_ARCHIVE, allRepos);
List<Apk> apks = ApkProvider.Helper.findByRepo(context, repo, Schema.ApkTable.Cols.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});
assertAdAwayMetadata(repo, "Normal");
}
private void assertConflictingRepo() {
assertConflictingRepo(RepoProvider.Helper.all(context));
}
/**
* + 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<Repo> allRepos) {
Repo repo = findRepo(REPO_CONFLICTING, allRepos);
List<Apk> apks = ApkProvider.Helper.findByRepo(context, repo, Schema.ApkTable.Cols.ALL);
assertEquals("Apks for conflicting repo", 6, apks.size());
assertApksExist(apks, "org.adaway", new int[]{50, 51, 52, 53});
assertApksExist(apks, "org.dgtale.icsimport", new int[]{2, 3});
assertAdAwayMetadata(repo, "Conflicting");
assertCalendarMetadata(repo, "Conflicting");
}
}