Multi-repo updater ported to robolectric.

The tests pass, but there is a lingering message that gets logged:

```
Jun 08, 2016 7:31:13 AM com.almworks.sqlite4java.Internal log
WARNING: [sqlite] [DETACH DATABASE temp_update_db]DB[1][C]: exception when clearing
com.almworks.sqlite4java.SQLiteException: [1] DB[1] reset [no such database: temp_update_db]
	at com.almworks.sqlite4java.SQLiteConnection.throwResult(SQLiteConnection.java:1309)
	at com.almworks.sqlite4java.SQLiteConnection.throwResult(SQLiteConnection.java:1282)
	at com.almworks.sqlite4java.SQLiteConnection.cacheStatementHandle(SQLiteConnection.java:1211)
	at com.almworks.sqlite4java.SQLiteConnection.access$900(SQLiteConnection.java:54)
	at com.almworks.sqlite4java.SQLiteConnection$CachedController.dispose(SQLiteConnection.java:1606)
	at com.almworks.sqlite4java.SQLiteStatement.dispose(SQLiteStatement.java:187)
	at org.robolectric.shadows.ShadowSQLiteConnection$Connections$4.call(ShadowSQLiteConnection.java:421)
	at org.robolectric.shadows.ShadowSQLiteConnection$Connections$6.call(ShadowSQLiteConnection.java:449)
	at org.robolectric.shadows.ShadowSQLiteConnection$Connections$6.call(ShadowSQLiteConnection.java:443)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
```

The `temp_update_db` is the one used for repo updates, but I thought that it
correctly gets dropped/detached by the `TempAppProvider` when required. In fact,
given the nature of the error message (no such database: temp_update_db), that
hints at the fact that it is indeed dropped. I'm struggling to figure out what
causes this, but it should not be harmful to the running of the tests. If a test
actually fails, then it is picked up correctly by JUnit.
This commit is contained in:
Peter Serwylo 2016-06-08 07:39:28 +10:00
parent 660ebc5ec8
commit 253900e927
10 changed files with 94 additions and 95 deletions

View File

@ -36,8 +36,8 @@ public class FileCompatTest {
@Before
public void setUp() {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
File dir = TestUtils.getWriteableDir(instrumentation);
sourceFile = SanitizedFile.knownSanitized(TestUtils.copyAssetToDir(instrumentation.getContext(), "simpleIndex.jar", dir));
File dir = TestUtilsOld.getWriteableDir(instrumentation);
sourceFile = SanitizedFile.knownSanitized(TestUtilsOld.copyAssetToDir(instrumentation.getContext(), "simpleIndex.jar", dir));
destFile = new SanitizedFile(dir, "dest-" + UUID.randomUUID() + ".testproduct");
assertFalse(destFile.exists());
assertTrue(sourceFile.getAbsolutePath() + " should exist.", sourceFile.exists());

View File

@ -12,9 +12,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class TestUtils {
public class TestUtilsOld {
private static final String TAG = "TestUtils";
private static final String TAG = "TestUtilsOld";
@Nullable
public static File copyAssetToDir(Context context, String assetName, File directory) {

View File

@ -146,7 +146,7 @@ public class UtilsTest {
@Test
public void testClearOldFiles() throws IOException, InterruptedException {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
File dir = new File(TestUtils.getWriteableDir(instrumentation), "clearOldFiles");
File dir = new File(TestUtilsOld.getWriteableDir(instrumentation), "clearOldFiles");
FileUtils.deleteQuietly(dir);
dir.mkdirs();
assertTrue(dir.isDirectory());

View File

@ -372,6 +372,17 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
private static Preferences instance;
/**
* Should only be used for unit testing, whereby separate tests are required to invoke `setup()`.
* The reason we don't instead ask for the singleton to be lazily loaded in the {@link Preferences#get()}
* method is because that would require each call to that method to require a {@link Context}.
* While it is likely that most places asking for preferences have access to a {@link Context},
* it is a minor convenience to be able to ask for preferences without.
*/
public static void clearSingletonForTesting() {
instance = null;
}
/**
* Needs to be setup before anything else tries to access it.
*/

View File

@ -190,7 +190,9 @@ public class RepoUpdater {
processXmlProgressListener, new URL(repo.address), (int) indexEntry.getSize());
// Process the index...
final SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
final SAXParser parser = factory.newSAXParser();
final XMLReader reader = parser.getXMLReader();
final RepoXMLHandler repoXMLHandler = new RepoXMLHandler(repo, createIndexReceiver());
reader.setContentHandler(repoXMLHandler);

View File

@ -1,16 +1,9 @@
package org.fdroid.fdroid;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.support.annotation.NonNull;
import android.test.InstrumentationTestCase;
import android.test.RenamingDelegatingContext;
import android.test.mock.MockContentResolver;
import android.text.TextUtils;
import android.util.Log;
@ -18,29 +11,37 @@ import org.fdroid.fdroid.RepoUpdater.UpdateException;
import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.ApkProvider;
import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.data.FDroidProvider;
import org.fdroid.fdroid.data.FDroidProviderTest;
import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.data.TempApkProvider;
import org.fdroid.fdroid.data.TempAppProvider;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import java.io.File;
import java.util.List;
import java.util.UUID;
@SuppressWarnings("PMD") // TODO port this to JUnit 4 semantics
public class MultiRepoUpdaterTest extends InstrumentationTestCase {
import static org.fdroid.fdroid.TestUtils.copyResourceToTempFile;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@Config(constants = BuildConfig.class)
@RunWith(RobolectricGradleTestRunner.class)
public class MultiRepoUpdaterTest extends FDroidProviderTest {
private static final String TAG = "MultiRepoUpdaterTest";
private static final String REPO_MAIN = "Test F-Droid repo";
private static final String REPO_ARCHIVE = "Test F-Droid repo (Archive)";
private static final String REPO_CONFLICTING = "Test F-Droid repo with different apps";
private Context context;
private RepoUpdater conflictingRepoUpdater;
private RepoUpdater mainRepoUpdater;
private RepoUpdater archiveRepoUpdater;
private File testFilesDir;
private static final String PUB_KEY =
"3082050b308202f3a003020102020420d8f212300d06092a864886f70d01010b050030363110300e0603" +
@ -75,73 +76,8 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase {
"98f848e0dbfce5a0f2da0198c47e6935a47fda12c518ef45adfb66ddf5aebaab13948a66c004b8592d22" +
"e8af60597c4ae2977977cf61dc715a572e241ae717cafdb4f71781943945ac52e0f50b";
public class TestContext extends RenamingDelegatingContext {
private MockContentResolver resolver;
public TestContext() {
super(getInstrumentation().getTargetContext(), "test.");
resolver = new MockContentResolver();
resolver.addProvider(AppProvider.getAuthority(), prepareProvider(new AppProvider()));
resolver.addProvider(ApkProvider.getAuthority(), prepareProvider(new ApkProvider()));
resolver.addProvider(RepoProvider.getAuthority(), prepareProvider(new RepoProvider()));
resolver.addProvider(TempAppProvider.getAuthority(), prepareProvider(new TempAppProvider()));
resolver.addProvider(TempApkProvider.getAuthority(), prepareProvider(new TempApkProvider()));
}
private ContentProvider prepareProvider(ContentProvider provider) {
provider.attachInfo(this, null);
provider.onCreate();
return provider;
}
@Override
public File getFilesDir() {
return getInstrumentation().getTargetContext().getFilesDir();
}
/**
* String resources used during testing (e.g. when bootstraping the database) are from
* the real org.fdroid.fdroid app, not the test org.fdroid.fdroid.test app.
*/
@Override
public Resources getResources() {
return getInstrumentation().getTargetContext().getResources();
}
@Override
public ContentResolver getContentResolver() {
return resolver;
}
@Override
public AssetManager getAssets() {
return getInstrumentation().getContext().getAssets();
}
@Override
public File getDatabasePath(String name) {
return new File(getInstrumentation().getContext().getFilesDir(), "fdroid_test.db");
}
@Override
public Context getApplicationContext() {
// Used by the DBHelper singleton instance.
return this;
}
}
@Override
public void setUp() throws Exception {
super.setUp();
FDroidProvider.clearDbHelperSingleton();
context = new TestContext();
testFilesDir = TestUtils.getWriteableDir(getInstrumentation());
@Before
public void setup() throws Exception {
// On a fresh database install, there will be F-Droid + GP repos, including their Archive
// repos that we are not interested in.
RepoProvider.Helper.remove(context, 1);
@ -152,6 +88,13 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase {
conflictingRepoUpdater = createUpdater(REPO_CONFLICTING, context);
mainRepoUpdater = createUpdater(REPO_MAIN, context);
archiveRepoUpdater = createUpdater(REPO_ARCHIVE, context);
Preferences.setup(context);
}
@After
public void tearDown() {
Preferences.clearSingletonForTesting();
}
/**
@ -169,9 +112,6 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase {
assertConflictingRepo(repos);
}
/**
*
*/
private void assertSomewhatAcceptable() {
Log.i(TAG, "Asserting at least one versions of each .apk is in index.");
List<Repo> repos = RepoProvider.Helper.all(context);
@ -361,6 +301,7 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase {
*/
@Test
public void testAcceptableConflictingThenMainThenArchive() throws UpdateException {
assertEmpty();
if (updateConflicting() && updateMain() && updateArchive()) {
@ -368,6 +309,7 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase {
}
}
@Test
public void testAcceptableConflictingThenArchiveThenMain() throws UpdateException {
assertEmpty();
if (updateConflicting() && updateArchive() && updateMain()) {
@ -375,6 +317,7 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase {
}
}
@Test
public void testAcceptableArchiveThenMainThenConflicting() throws UpdateException {
assertEmpty();
if (updateArchive() && updateMain() && updateConflicting()) {
@ -382,6 +325,7 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase {
}
}
@Test
public void testAcceptableArchiveThenConflictingThenMain() throws UpdateException {
assertEmpty();
if (updateArchive() && updateConflicting() && updateMain()) {
@ -389,6 +333,7 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase {
}
}
@Test
public void testAcceptableMainThenArchiveThenConflicting() throws UpdateException {
assertEmpty();
if (updateMain() && updateArchive() && updateConflicting()) {
@ -396,6 +341,7 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase {
}
}
@Test
public void testAcceptableMainThenConflictingThenArchive() throws UpdateException {
assertEmpty();
if (updateMain() && updateConflicting() && updateArchive()) {
@ -434,12 +380,14 @@ public class MultiRepoUpdaterTest extends InstrumentationTestCase {
}
private boolean updateRepo(RepoUpdater updater, String indexJarPath) throws UpdateException {
if (!testFilesDir.canWrite()) {
return false;
File indexJar = copyResourceToTempFile(indexJarPath);
try {
updater.processDownloadedFile(indexJar);
} finally {
if (indexJar != null && indexJar.exists()) {
indexJar.delete();
}
}
File indexJar = TestUtils.copyAssetToDir(context, indexJarPath, testFilesDir);
updater.processDownloadedFile(indexJar);
return true;
}

View File

@ -0,0 +1,38 @@
package org.fdroid.fdroid;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.junit.Assert.fail;
public class TestUtils {
private static final String TAG = "TestUtils";
public static File copyResourceToTempFile(String resourceName) {
File tempFile = null;
InputStream input = null;
OutputStream output = null;
try {
tempFile = File.createTempFile(resourceName + "-", ".testasset");
input = TestUtils.class.getClassLoader().getResourceAsStream(resourceName);
output = new FileOutputStream(tempFile);
Utils.copy(input, output);
} catch (IOException e) {
e.printStackTrace();
if (tempFile != null && tempFile.exists()) {
tempFile.delete();
}
fail();
return null;
} finally {
Utils.closeQuietly(output);
Utils.closeQuietly(input);
}
return tempFile;
}
}