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

View File

@ -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));
}
/**

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() {
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

View File

@ -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<Apk> 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);
}
}
}
static class TestUpdateService extends UpdateService {
@Override
public void onHandleWork(@NonNull Intent intent) {
super.onHandleWork(intent);
}
}
}