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); + } + } +}