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.
This commit is contained in:
Hans-Christoph Steiner 2022-12-01 17:38:39 +01:00
parent d19c5028ac
commit 3fa5e98fc4
5 changed files with 103 additions and 75 deletions

View File

@ -24,8 +24,6 @@ import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -103,7 +101,7 @@ public class SwapRepoEmulatorTest {
assertFalse(TextUtils.isEmpty(signingCert)); assertFalse(TextUtils.isEmpty(signingCert));
assertFalse(TextUtils.isEmpty(fingerprint)); assertFalse(TextUtils.isEmpty(fingerprint));
assertTrue(isPortInUse(FDroidApp.ipAddressString, FDroidApp.port)); assertTrue(Utils.isPortInUse(FDroidApp.ipAddressString, FDroidApp.port));
Thread.sleep(100); Thread.sleep(100);
File swapJarFile = File.createTempFile("swap", "", context.getCacheDir()); File swapJarFile = File.createTempFile("swap", "", context.getCacheDir());
@ -151,19 +149,6 @@ public class SwapRepoEmulatorTest {
assertFalse(localHttpd.isAlive()); 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) { private boolean isSystemPackage(ResolveInfo resolveInfo) {
return (resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; return (resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
} }

View File

@ -34,19 +34,12 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.widget.Toast; 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.CompatibilityChecker;
import org.fdroid.CompatibilityCheckerImpl; import org.fdroid.CompatibilityCheckerImpl;
import org.fdroid.database.DbUpdateChecker;
import org.fdroid.database.FDroidDatabase; import org.fdroid.database.FDroidDatabase;
import org.fdroid.database.Repository; import org.fdroid.database.Repository;
import org.fdroid.database.UpdatableApp; import org.fdroid.database.UpdatableApp;
import org.fdroid.database.DbUpdateChecker;
import org.fdroid.download.Mirror; import org.fdroid.download.Mirror;
import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
@ -65,6 +58,12 @@ import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.disposables.Disposable;
@ -118,14 +117,18 @@ public class UpdateService extends JobIntentService {
updateNewRepoNow(context, address, null); 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 intent = new Intent(context, UpdateService.class);
intent.putExtra(EXTRA_MANUAL_UPDATE, true); intent.putExtra(EXTRA_MANUAL_UPDATE, true);
intent.putExtra(EXTRA_REPO_FINGERPRINT, fingerprint); intent.putExtra(EXTRA_REPO_FINGERPRINT, fingerprint);
if (!TextUtils.isEmpty(address)) { if (!TextUtils.isEmpty(address)) {
intent.setData(Uri.parse(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));
} }
/** /**

View File

@ -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;
}
} }

View File

@ -54,12 +54,7 @@ public class PreferencesTest {
public void setup() { public void setup() {
ShadowLog.stream = System.out; ShadowLog.stream = System.out;
String sharedPreferencesName = CONTEXT.getPackageName() + "_preferences_defaults"; defaults = getSharedPreferences(CONTEXT);
PreferenceManager pm = new PreferenceManager(CONTEXT);
pm.setSharedPreferencesName(sharedPreferencesName);
pm.setSharedPreferencesMode(Context.MODE_PRIVATE);
pm.inflateFromResource(CONTEXT, R.xml.preferences, null);
defaults = pm.getSharedPreferences();
assertTrue(defaults.getAll().size() > 0); assertTrue(defaults.getAll().size() > 0);
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(CONTEXT); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(CONTEXT);
@ -72,6 +67,15 @@ public class PreferencesTest {
defaultValueSp.edit().remove(PreferenceManager.KEY_HAS_SET_DEFAULT_VALUES).commit(); 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 * Check that the defaults are being set when using
* {@link PreferenceManager#getDefaultSharedPreferences(Context)}, and that * {@link PreferenceManager#getDefaultSharedPreferences(Context)}, and that

View File

@ -2,33 +2,41 @@ package org.fdroid.fdroid.updater;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContextWrapper; import android.content.ContextWrapper;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import org.apache.commons.net.util.SubnetUtils; import org.apache.commons.net.util.SubnetUtils;
import org.fdroid.database.Repository;
import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Hasher; import org.fdroid.fdroid.Hasher;
import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.PreferencesTest;
import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.nearby.LocalHTTPD; import org.fdroid.fdroid.nearby.LocalHTTPD;
import org.fdroid.fdroid.nearby.LocalRepoKeyStore; import org.fdroid.fdroid.nearby.LocalRepoKeyStore;
import org.fdroid.fdroid.nearby.LocalRepoManager; import org.fdroid.fdroid.nearby.LocalRepoManager;
import org.fdroid.fdroid.nearby.LocalRepoService; import org.fdroid.fdroid.nearby.LocalRepoService;
import org.fdroid.fdroid.nearby.WifiStateChangeService; import org.fdroid.fdroid.nearby.WifiStateChangeService;
import org.fdroid.fdroid.net.ConnectivityMonitorService;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowLog; import org.robolectric.shadows.ShadowLog;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@ -36,17 +44,14 @@ import static org.junit.Assert.assertTrue;
import static org.robolectric.Shadows.shadowOf; import static org.robolectric.Shadows.shadowOf;
/** /**
* This test almost works, it needs to have the {@link android.content.ContentProvider} * This test uses the swap repo setup as a fake repo to test {@link UpdateService}.
* and {@link ContentResolver} stuff worked out. It currently fails as
* {@code updater.update()}.
*/ */
@Ignore
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class SwapRepoTest { public class UpdateServiceTest {
public static final String TAG = "UpdateService";
private LocalHTTPD localHttpd; private LocalHTTPD localHttpd;
protected ContentResolver contentResolver; protected ContentResolver contentResolver;
protected ContextWrapper context; protected ContextWrapper context;
@ -55,7 +60,7 @@ public class SwapRepoTest {
ShadowLog.stream = System.out; ShadowLog.stream = System.out;
contentResolver = ApplicationProvider.getApplicationContext().getContentResolver(); contentResolver = ApplicationProvider.getApplicationContext().getContentResolver();
context = new ContextWrapper(ApplicationProvider.getApplicationContext()) { context = new ContextWrapper(ApplicationProvider.getApplicationContext()) {
@Override @Override
public ContentResolver getContentResolver() { public ContentResolver getContentResolver() {
@ -74,39 +79,47 @@ public class SwapRepoTest {
throws IOException, LocalRepoKeyStore.InitException, InterruptedException { throws IOException, LocalRepoKeyStore.InitException, InterruptedException {
PackageManager packageManager = context.getPackageManager(); PackageManager packageManager = context.getPackageManager();
ApplicationInfo appInfo = new ApplicationInfo(); ApplicationInfo appInfo = new ApplicationInfo();
appInfo.flags = 0; appInfo.flags = 0;
appInfo.packageName = context.getPackageName(); appInfo.packageName = context.getPackageName();
appInfo.minSdkVersion = 10; appInfo.minSdkVersion = 10;
appInfo.targetSdkVersion = 23; appInfo.targetSdkVersion = 23;
appInfo.sourceDir = getClass().getClassLoader().getResource("F-Droid.apk").getPath(); File build = new File(getClass().getClassLoader().getResource("").getPath(), "../../../..");
appInfo.publicSourceDir = getClass().getClassLoader().getResource("F-Droid.apk").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); System.out.println("appInfo.sourceDir " + appInfo.sourceDir);
appInfo.name = "F-Droid"; appInfo.name = "F-Droid";
PackageInfo packageInfo = new PackageInfo(); PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = appInfo.packageName; packageInfo.packageName = appInfo.packageName;
packageInfo.applicationInfo = appInfo; packageInfo.applicationInfo = appInfo;
packageInfo.signatures = new Signature[1];
packageInfo.signatures[0] = new Signature("fake".getBytes());
packageInfo.versionCode = 1002001; packageInfo.versionCode = 1002001;
packageInfo.versionName = "1.2-fake"; packageInfo.versionName = "1.2-fake";
shadowOf(packageManager).addPackage(packageInfo); shadowOf(packageManager).addPackage(packageInfo);
try { try {
String host = null; // null basically means localhost
FDroidApp.initWifiSettings(); FDroidApp.initWifiSettings();
FDroidApp.networkState = ConnectivityMonitorService.FLAG_NET_NO_LIMIT;
FDroidApp.ipAddressString = "127.0.0.1"; FDroidApp.ipAddressString = "127.0.0.1";
FDroidApp.subnetInfo = new SubnetUtils("127.0.0.0/8").getInfo(); FDroidApp.subnetInfo = new SubnetUtils("127.0.0.0/8").getInfo();
String address = "http://" + FDroidApp.ipAddressString + ":" + FDroidApp.port + "/fdroid/repo"; 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()}); LocalRepoService.runProcess(context, new String[]{context.getPackageName()});
Log.i(TAG, "REPO: " + FDroidApp.repo);
File indexJarFile = LocalRepoManager.get(context).getIndexJar(); File indexJarFile = LocalRepoManager.get(context).getIndexJar();
System.out.println("indexJarFile:" + indexJarFile); System.out.println("indexJarFile:" + indexJarFile);
assertTrue(indexJarFile.isFile()); assertTrue(indexJarFile.isFile());
localHttpd = new LocalHTTPD( localHttpd = new LocalHTTPD(
context, context,
FDroidApp.ipAddressString, host,
FDroidApp.port, FDroidApp.port,
LocalRepoManager.get(context).getWebRoot(), LocalRepoManager.get(context).getWebRoot(),
false); false);
@ -116,45 +129,56 @@ public class SwapRepoTest {
LocalRepoKeyStore localRepoKeyStore = LocalRepoKeyStore.get(context); LocalRepoKeyStore localRepoKeyStore = LocalRepoKeyStore.get(context);
Certificate localCert = localRepoKeyStore.getCertificate(); Certificate localCert = localRepoKeyStore.getCertificate();
String fingerprint = Utils.calcFingerprint(localCert).toLowerCase(Locale.ROOT);
String signingCert = Hasher.hex(localCert); String signingCert = Hasher.hex(localCert);
assertFalse(TextUtils.isEmpty(signingCert)); assertFalse(TextUtils.isEmpty(signingCert));
assertFalse(TextUtils.isEmpty(Utils.calcFingerprint(localCert))); assertFalse(TextUtils.isEmpty(fingerprint));
//Repo repo = createRepo("", FDroidApp.repo.getAddress(), context, signingCert); assertTrue(Utils.isPortInUse(host, FDroidApp.port));
//IndexUpdater updater = new IndexUpdater(context, repo); Thread.sleep(100);
//updater.update();
//assertTrue(updater.hasChanged());
//updater.processDownloadedFile(indexJarFile);
boolean foundRepo = false; Log.i(TAG, "FDroidApp.networkState " + FDroidApp.networkState);
//for (Repo repoFromDb : RepoProvider.Helper.all(context)) { SharedPreferences prefs = PreferencesTest.getSharedPreferences(context);
// if (TextUtils.equals(repo.address, repoFromDb.address)) { prefs.edit()
// foundRepo = true; .putInt(Preferences.PREF_OVER_DATA, Preferences.OVER_NETWORK_ALWAYS)
// repo = repoFromDb; .putInt(Preferences.PREF_OVER_WIFI, Preferences.OVER_NETWORK_ALWAYS)
// } .commit();
//} final Intent intent = UpdateService.getIntent(context, address, fingerprint);
assertTrue(foundRepo); 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()); // TODO test what is in the repo.
//List<Apk> apks = ApkProvider.Helper.findByRepo(context, repo, Schema.ApkTable.Cols.ALL); // TODO add app/src/test/assets/urzip.apk to the repo, then test another update
//assertEquals(1, apks.size()); // TODO test various PREF_OVER_DATA and PREF_OVER_WIFI combos
//for (Apk apk : apks) { Thread.sleep(1000);
// System.out.println(apk);
//}
//MultiIndexUpdaterTest.assertApksExist(apks, context.getPackageName(), new int[]{BuildConfig.VERSION_CODE});
Thread.sleep(10000);
} finally { } finally {
if (localHttpd != null) { if (localHttpd != null) {
localHttpd.stop(); localHttpd.stop();
} }
} }
assertFalse(localHttpd.isAlive());
} }
/** class TestLocalRepoService extends LocalRepoService {
* Creates a real instance of {@code Repo} by loading it from the database, @Override
* that ensures it includes the primary key from the database. protected void onHandleIntent(Intent intent) {
*/ super.onHandleIntent(intent);
static Repository createRepo(String uri, String signingCert) { }
return FDroidApp.createSwapRepo(uri, signingCert);
} }
}
static class TestUpdateService extends UpdateService {
@Override
public void onHandleWork(@NonNull Intent intent) {
super.onHandleWork(intent);
}
}
}