From 3fa5e98fc4b72d407acf80773e128e2164a64ece Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 1 Dec 2022 17:38:39 +0100 Subject: [PATCH] convert SwapRepoTest into initial UpdateServiceTest Finally, there can be a test for running UpdateService, which is essential and has some really hairy code in it. At least swap has been removed from there. This is barebones still, but it now can be developed to test quite a bit of what is still needed in UpdateService. --- .../fdroid/updater/SwapRepoEmulatorTest.java | 17 +-- .../java/org/fdroid/fdroid/UpdateService.java | 23 ++-- .../main/java/org/fdroid/fdroid/Utils.java | 12 ++ .../org/fdroid/fdroid/PreferencesTest.java | 16 ++- ...apRepoTest.java => UpdateServiceTest.java} | 110 +++++++++++------- 5 files changed, 103 insertions(+), 75 deletions(-) rename app/src/testFull/java/org/fdroid/fdroid/updater/{SwapRepoTest.java => UpdateServiceTest.java} (56%) diff --git a/app/src/androidTest/java/org/fdroid/fdroid/updater/SwapRepoEmulatorTest.java b/app/src/androidTest/java/org/fdroid/fdroid/updater/SwapRepoEmulatorTest.java index 9377106b3..708291433 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/updater/SwapRepoEmulatorTest.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/updater/SwapRepoEmulatorTest.java @@ -24,8 +24,6 @@ import org.junit.Ignore; import org.junit.Test; import java.io.File; -import java.io.IOException; -import java.net.Socket; import java.security.cert.Certificate; import java.util.HashSet; import java.util.List; @@ -103,7 +101,7 @@ public class SwapRepoEmulatorTest { assertFalse(TextUtils.isEmpty(signingCert)); assertFalse(TextUtils.isEmpty(fingerprint)); - assertTrue(isPortInUse(FDroidApp.ipAddressString, FDroidApp.port)); + assertTrue(Utils.isPortInUse(FDroidApp.ipAddressString, FDroidApp.port)); Thread.sleep(100); File swapJarFile = File.createTempFile("swap", "", context.getCacheDir()); @@ -151,19 +149,6 @@ public class SwapRepoEmulatorTest { assertFalse(localHttpd.isAlive()); } - private boolean isPortInUse(String host, int port) { - boolean result = false; - - try { - (new Socket(host, port)).close(); - result = true; - } catch (IOException e) { - // Could not connect. - e.printStackTrace(); - } - return result; - } - private boolean isSystemPackage(ResolveInfo resolveInfo) { return (resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } diff --git a/app/src/main/java/org/fdroid/fdroid/UpdateService.java b/app/src/main/java/org/fdroid/fdroid/UpdateService.java index 9c2e19b59..81f7abd1c 100644 --- a/app/src/main/java/org/fdroid/fdroid/UpdateService.java +++ b/app/src/main/java/org/fdroid/fdroid/UpdateService.java @@ -34,19 +34,12 @@ import android.text.TextUtils; import android.util.Log; import android.widget.Toast; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.JobIntentService; -import androidx.core.app.NotificationCompat; -import androidx.core.content.ContextCompat; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; - import org.fdroid.CompatibilityChecker; import org.fdroid.CompatibilityCheckerImpl; +import org.fdroid.database.DbUpdateChecker; import org.fdroid.database.FDroidDatabase; import org.fdroid.database.Repository; import org.fdroid.database.UpdatableApp; -import org.fdroid.database.DbUpdateChecker; import org.fdroid.download.Mirror; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.App; @@ -65,6 +58,12 @@ import java.io.File; import java.util.ArrayList; import java.util.List; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.JobIntentService; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.disposables.Disposable; @@ -118,14 +117,18 @@ public class UpdateService extends JobIntentService { updateNewRepoNow(context, address, null); } - public static void updateNewRepoNow(Context context, String address, @Nullable String fingerprint) { + public static Intent getIntent(Context context, String address, @Nullable String fingerprint) { Intent intent = new Intent(context, UpdateService.class); intent.putExtra(EXTRA_MANUAL_UPDATE, true); intent.putExtra(EXTRA_REPO_FINGERPRINT, fingerprint); if (!TextUtils.isEmpty(address)) { intent.setData(Uri.parse(address)); } - enqueueWork(context, intent); + return intent; + } + + public static void updateNewRepoNow(Context context, String address, @Nullable String fingerprint) { + enqueueWork(context, getIntent(context, address, fingerprint)); } /** diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index 524ff9889..449f9c8fc 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -911,4 +911,16 @@ public final class Utils { } } + public static boolean isPortInUse(String host, int port) { + boolean result = false; + + try { + (new Socket(host, port)).close(); + result = true; + } catch (IOException e) { + // Could not connect. + e.printStackTrace(); + } + return result; + } } diff --git a/app/src/test/java/org/fdroid/fdroid/PreferencesTest.java b/app/src/test/java/org/fdroid/fdroid/PreferencesTest.java index a4ce18d7d..d6083da9c 100644 --- a/app/src/test/java/org/fdroid/fdroid/PreferencesTest.java +++ b/app/src/test/java/org/fdroid/fdroid/PreferencesTest.java @@ -54,12 +54,7 @@ public class PreferencesTest { public void setup() { ShadowLog.stream = System.out; - String sharedPreferencesName = CONTEXT.getPackageName() + "_preferences_defaults"; - PreferenceManager pm = new PreferenceManager(CONTEXT); - pm.setSharedPreferencesName(sharedPreferencesName); - pm.setSharedPreferencesMode(Context.MODE_PRIVATE); - pm.inflateFromResource(CONTEXT, R.xml.preferences, null); - defaults = pm.getSharedPreferences(); + defaults = getSharedPreferences(CONTEXT); assertTrue(defaults.getAll().size() > 0); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(CONTEXT); @@ -72,6 +67,15 @@ public class PreferencesTest { defaultValueSp.edit().remove(PreferenceManager.KEY_HAS_SET_DEFAULT_VALUES).commit(); } + public static SharedPreferences getSharedPreferences(Context context) { + String sharedPreferencesName = context.getPackageName() + "_preferences_defaults"; + PreferenceManager pm = new PreferenceManager(context); + pm.setSharedPreferencesName(sharedPreferencesName); + pm.setSharedPreferencesMode(Context.MODE_PRIVATE); + pm.inflateFromResource(context, R.xml.preferences, null); + return pm.getSharedPreferences(); + } + /** * Check that the defaults are being set when using * {@link PreferenceManager#getDefaultSharedPreferences(Context)}, and that diff --git a/app/src/testFull/java/org/fdroid/fdroid/updater/SwapRepoTest.java b/app/src/testFull/java/org/fdroid/fdroid/updater/UpdateServiceTest.java similarity index 56% rename from app/src/testFull/java/org/fdroid/fdroid/updater/SwapRepoTest.java rename to app/src/testFull/java/org/fdroid/fdroid/updater/UpdateServiceTest.java index 8a672487c..664746c38 100644 --- a/app/src/testFull/java/org/fdroid/fdroid/updater/SwapRepoTest.java +++ b/app/src/testFull/java/org/fdroid/fdroid/updater/UpdateServiceTest.java @@ -2,33 +2,41 @@ package org.fdroid.fdroid.updater; import android.content.ContentResolver; import android.content.ContextWrapper; +import android.content.Intent; +import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.Signature; import android.text.TextUtils; +import android.util.Log; import org.apache.commons.net.util.SubnetUtils; -import org.fdroid.database.Repository; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Hasher; import org.fdroid.fdroid.Preferences; +import org.fdroid.fdroid.PreferencesTest; +import org.fdroid.fdroid.UpdateService; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.nearby.LocalHTTPD; import org.fdroid.fdroid.nearby.LocalRepoKeyStore; import org.fdroid.fdroid.nearby.LocalRepoManager; import org.fdroid.fdroid.nearby.LocalRepoService; import org.fdroid.fdroid.nearby.WifiStateChangeService; +import org.fdroid.fdroid.net.ConnectivityMonitorService; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.shadows.ShadowLog; import java.io.File; import java.io.IOException; import java.security.cert.Certificate; +import java.util.Locale; +import androidx.annotation.NonNull; import androidx.test.core.app.ApplicationProvider; import static org.junit.Assert.assertFalse; @@ -36,17 +44,14 @@ import static org.junit.Assert.assertTrue; import static org.robolectric.Shadows.shadowOf; /** - * This test almost works, it needs to have the {@link android.content.ContentProvider} - * and {@link ContentResolver} stuff worked out. It currently fails as - * {@code updater.update()}. + * This test uses the swap repo setup as a fake repo to test {@link UpdateService}. */ -@Ignore @RunWith(RobolectricTestRunner.class) -public class SwapRepoTest { +public class UpdateServiceTest { + public static final String TAG = "UpdateService"; private LocalHTTPD localHttpd; - protected ContentResolver contentResolver; protected ContextWrapper context; @@ -55,7 +60,7 @@ public class SwapRepoTest { ShadowLog.stream = System.out; contentResolver = ApplicationProvider.getApplicationContext().getContentResolver(); - + context = new ContextWrapper(ApplicationProvider.getApplicationContext()) { @Override public ContentResolver getContentResolver() { @@ -74,39 +79,47 @@ public class SwapRepoTest { throws IOException, LocalRepoKeyStore.InitException, InterruptedException { PackageManager packageManager = context.getPackageManager(); - + ApplicationInfo appInfo = new ApplicationInfo(); appInfo.flags = 0; appInfo.packageName = context.getPackageName(); appInfo.minSdkVersion = 10; appInfo.targetSdkVersion = 23; - appInfo.sourceDir = getClass().getClassLoader().getResource("F-Droid.apk").getPath(); - appInfo.publicSourceDir = getClass().getClassLoader().getResource("F-Droid.apk").getPath(); + File build = new File(getClass().getClassLoader().getResource("").getPath(), "../../../.."); + File apk = new File(build.getCanonicalFile(), "outputs/apk/full/debug/app-full-debug.apk"); + Log.i(TAG, "outputs " + apk + " " + apk.isDirectory()); + appInfo.sourceDir = apk.getCanonicalPath(); + appInfo.publicSourceDir = apk.getCanonicalPath(); System.out.println("appInfo.sourceDir " + appInfo.sourceDir); appInfo.name = "F-Droid"; PackageInfo packageInfo = new PackageInfo(); packageInfo.packageName = appInfo.packageName; packageInfo.applicationInfo = appInfo; + packageInfo.signatures = new Signature[1]; + packageInfo.signatures[0] = new Signature("fake".getBytes()); packageInfo.versionCode = 1002001; packageInfo.versionName = "1.2-fake"; shadowOf(packageManager).addPackage(packageInfo); try { + String host = null; // null basically means localhost FDroidApp.initWifiSettings(); + FDroidApp.networkState = ConnectivityMonitorService.FLAG_NET_NO_LIMIT; FDroidApp.ipAddressString = "127.0.0.1"; FDroidApp.subnetInfo = new SubnetUtils("127.0.0.0/8").getInfo(); String address = "http://" + FDroidApp.ipAddressString + ":" + FDroidApp.port + "/fdroid/repo"; - FDroidApp.repo = FDroidApp.createSwapRepo(address, null); + FDroidApp.repo = FDroidApp.createSwapRepo(address, null); // TODO create a regular repo, not swap LocalRepoService.runProcess(context, new String[]{context.getPackageName()}); + Log.i(TAG, "REPO: " + FDroidApp.repo); File indexJarFile = LocalRepoManager.get(context).getIndexJar(); System.out.println("indexJarFile:" + indexJarFile); assertTrue(indexJarFile.isFile()); localHttpd = new LocalHTTPD( context, - FDroidApp.ipAddressString, + host, FDroidApp.port, LocalRepoManager.get(context).getWebRoot(), false); @@ -116,45 +129,56 @@ public class SwapRepoTest { LocalRepoKeyStore localRepoKeyStore = LocalRepoKeyStore.get(context); Certificate localCert = localRepoKeyStore.getCertificate(); + String fingerprint = Utils.calcFingerprint(localCert).toLowerCase(Locale.ROOT); String signingCert = Hasher.hex(localCert); assertFalse(TextUtils.isEmpty(signingCert)); - assertFalse(TextUtils.isEmpty(Utils.calcFingerprint(localCert))); + assertFalse(TextUtils.isEmpty(fingerprint)); - //Repo repo = createRepo("", FDroidApp.repo.getAddress(), context, signingCert); - //IndexUpdater updater = new IndexUpdater(context, repo); - //updater.update(); - //assertTrue(updater.hasChanged()); - //updater.processDownloadedFile(indexJarFile); + assertTrue(Utils.isPortInUse(host, FDroidApp.port)); + Thread.sleep(100); - boolean foundRepo = false; - //for (Repo repoFromDb : RepoProvider.Helper.all(context)) { - // if (TextUtils.equals(repo.address, repoFromDb.address)) { - // foundRepo = true; - // repo = repoFromDb; - // } - //} - assertTrue(foundRepo); + Log.i(TAG, "FDroidApp.networkState " + FDroidApp.networkState); + SharedPreferences prefs = PreferencesTest.getSharedPreferences(context); + prefs.edit() + .putInt(Preferences.PREF_OVER_DATA, Preferences.OVER_NETWORK_ALWAYS) + .putInt(Preferences.PREF_OVER_WIFI, Preferences.OVER_NETWORK_ALWAYS) + .commit(); + final Intent intent = UpdateService.getIntent(context, address, fingerprint); + final TestUpdateService testUpdateService = Robolectric.buildService(TestUpdateService.class, + intent).bind().get(); + Thread t = new Thread() { + @Override + public void run() { + testUpdateService.onCreate(); + testUpdateService.onHandleWork(intent); + } + }; + t.start(); + t.join(10000); - //assertNotEquals(-1, repo.getId()); - //List apks = ApkProvider.Helper.findByRepo(context, repo, Schema.ApkTable.Cols.ALL); - //assertEquals(1, apks.size()); - //for (Apk apk : apks) { - // System.out.println(apk); - //} - //MultiIndexUpdaterTest.assertApksExist(apks, context.getPackageName(), new int[]{BuildConfig.VERSION_CODE}); - Thread.sleep(10000); + // TODO test what is in the repo. + // TODO add app/src/test/assets/urzip.apk to the repo, then test another update + // TODO test various PREF_OVER_DATA and PREF_OVER_WIFI combos + Thread.sleep(1000); } finally { if (localHttpd != null) { localHttpd.stop(); } } + assertFalse(localHttpd.isAlive()); } - /** - * Creates a real instance of {@code Repo} by loading it from the database, - * that ensures it includes the primary key from the database. - */ - static Repository createRepo(String uri, String signingCert) { - return FDroidApp.createSwapRepo(uri, signingCert); + class TestLocalRepoService extends LocalRepoService { + @Override + protected void onHandleIntent(Intent intent) { + super.onHandleIntent(intent); + } } -} \ No newline at end of file + + static class TestUpdateService extends UpdateService { + @Override + public void onHandleWork(@NonNull Intent intent) { + super.onHandleWork(intent); + } + } +}