Finish porting tests to Robolectric, and appease PMD.

To appease PMD, we now have a three rulesets in `config/pmd/*.xml`:

 * `rules.xml`: The bulk of the rules, used by both main and test code.
 * `rules-main.xml`: Rules specific to the andoid client code.
 * `rules-test.xml`: Rules specific to test code.

The rationale is because checkstyle by default checks for "too many static
imports", which is a fair call. However in JUnit4 code, it is common to
import many `assert*` static methods.
This commit is contained in:
Peter Serwylo 2016-06-09 09:55:12 +10:00
parent 253900e927
commit 4e73d1e5e6
25 changed files with 474 additions and 667 deletions

View File

@ -230,13 +230,22 @@ pmd {
consoleOutput = true consoleOutput = true
} }
task pmd(type: Pmd, dependsOn: assembleDebug) { task pmdMain(type: Pmd, dependsOn: assembleDebug) {
ruleSetFiles = files("${project.rootDir}/config/pmd/rules.xml") ruleSetFiles = files("${project.rootDir}/config/pmd/rules.xml", "${project.rootDir}/config/pmd/rules-main.xml")
ruleSets = [] // otherwise defaults clash with the list in rules.xml ruleSets = [] // otherwise defaults clash with the list in rules.xml
source 'src/main/java', 'src/test/java', 'src/androidTest/java' source 'src/main/java'
include '**/*.java' include '**/*.java'
} }
task pmdTest(type: Pmd, dependsOn: assembleDebug) {
ruleSetFiles = files("${project.rootDir}/config/pmd/rules.xml", "${project.rootDir}/config/pmd/rules-test.xml")
ruleSets = [] // otherwise defaults clash with the list in rules.xml
source 'src/test/java', 'src/androidTest/java'
include '**/*.java'
}
task pmd(dependsOn: [pmdMain, pmdTest]) {}
// This person took the example code below from another blogpost online, however // This person took the example code below from another blogpost online, however
// I lost the reference to it: // I lost the reference to it:
// http://stackoverflow.com/questions/23297562/gradle-javadoc-and-android-documentation // http://stackoverflow.com/questions/23297562/gradle-javadoc-and-android-documentation

View File

@ -1,257 +0,0 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.test;
import android.annotation.TargetApi;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Resources;
import android.database.DatabaseUtils;
import android.os.Build;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import org.junit.After;
import org.junit.Before;
import java.io.File;
/**
* This test case class provides a framework for testing a single
* {@link ContentProvider} and for testing your app code with an
* isolated content provider. Instead of using the system map of
* providers that is based on the manifests of other applications, the test
* case creates its own internal map. It then uses this map to resolve providers
* given an authority. This allows you to inject test providers and to null out
* providers that you do not want to use.
* <p>
* This test case also sets up the following mock objects:
* </p>
* <ul>
* <li>
* An {@link android.test.IsolatedContext} that stubs out Context methods that might
* affect the rest of the running system, while allowing tests to do real file and
* database work.
* </li>
* <li>
* A {@link android.test.mock.MockContentResolver} that provides the functionality of a
* regular content resolver, but uses {@link IsolatedContext}. It stubs out
* {@link ContentResolver#notifyChange(Uri, ContentObserver, boolean)} to
* prevent the test from affecting the running system.
* </li>
* <li>
* An instance of the provider under test, running in an {@link IsolatedContext}.
* </li>
* </ul>
* <p>
* This framework is set up automatically by the base class' {@link #setUp()} method. If you
* override this method, you must call the super method as the first statement in
* your override.
* </p>
* <p>
* In order for their tests to be run, concrete subclasses must provide their own
* constructor with no arguments. This constructor must call
* {@link #ProviderTestCase2MockContext(Class, String)} as its first operation.
* </p>
* For more information on content provider testing, please see
* <a href="{@docRoot}tools/testing/contentprovider_testing.html">Content Provider Testing</a>.
*/
public abstract class ProviderTestCase2MockContext<T extends ContentProvider> extends AndroidTestCase {
Class<T> mProviderClass;
String mProviderAuthority;
private IsolatedContext mProviderContext;
private MockContentResolver mResolver;
private class MockContext2 extends MockContext {
@Override
public Resources getResources() {
return getContext().getResources();
}
@Override
public File getDir(String name, int mode) {
// name the directory so the directory will be separated from
// one created through the regular Context
return getContext().getDir("mockcontext2_" + name, mode);
}
@Override
public Context getApplicationContext() {
return this;
}
@Override
public String getPackageName() {
return "org.fdroid.fdroid";
}
}
/**
* Constructor.
*
* @param providerClass The class name of the provider under test
* @param providerAuthority The provider's authority string
*/
public ProviderTestCase2MockContext(Class<T> providerClass, String providerAuthority) {
mProviderClass = providerClass;
mProviderAuthority = providerAuthority;
}
private T mProvider;
/**
* Returns the content provider created by this class in the {@link #setUp()} method.
* @return T An instance of the provider class given as a parameter to the test case class.
*/
public T getProvider() {
return mProvider;
}
protected abstract Context createMockContext(Context delegate);
/**
* Sets up the environment for the test fixture.
* <p>
* Creates a new
* {@link android.test.mock.MockContentResolver}, a new IsolatedContext
* that isolates the provider's file operations, and a new instance of
* the provider under test within the isolated environment.
* </p>
*
* @throws Exception
*/
@Override
@Before
protected void setUp() throws Exception {
super.setUp();
mResolver = new MockContentResolver();
final String filenamePrefix = "test.";
final RenamingDelegatingContext targetContextWrapper = new
RenamingDelegatingContext(
createMockContext(new MockContext2()), // The context that most methods are delegated to
getContext(), // The context that file methods are delegated to
filenamePrefix);
mProviderContext = new IsolatedContext(mResolver, new ContextWrapper(targetContextWrapper) {
// The FDroidProvider class needs access to an application context in order to initialize
// the singleton DBHelper instance.
@Override
public Context getApplicationContext() {
return targetContextWrapper;
}
});
mProvider = mProviderClass.newInstance();
mProvider.attachInfo(mProviderContext, null);
assertNotNull(mProvider);
mResolver.addProvider(mProviderAuthority, getProvider());
}
/**
* Tears down the environment for the test fixture.
* <p>
* Calls {@link android.content.ContentProvider#shutdown()} on the
* {@link android.content.ContentProvider} represented by mProvider.
*/
@Override
@After
protected void tearDown() throws Exception {
shutdownProvider();
super.tearDown();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void shutdownProvider() {
if (Build.VERSION.SDK_INT >= 11) {
mProvider.shutdown();
}
}
/**
* Gets the {@link MockContentResolver} created by this class during initialization. You
* must use the methods of this resolver to access the provider under test.
*
* @return A {@link MockContentResolver} instance.
*/
public MockContentResolver getMockContentResolver() {
return mResolver;
}
/**
* Gets the {@link IsolatedContext} created by this class during initialization.
* @return The {@link IsolatedContext} instance
*/
public IsolatedContext getMockContext() {
return mProviderContext;
}
/**
* <p>
* Creates a new content provider of the same type as that passed to the test case class,
* with an authority name set to the authority parameter, and using an SQLite database as
* the underlying data source. The SQL statement parameter is used to create the database.
* This method also creates a new {@link MockContentResolver} and adds the provider to it.
* </p>
* <p>
* Both the new provider and the new resolver are put into an {@link IsolatedContext}
* that uses the targetContext parameter for file operations and a {@link MockContext}
* for everything else. The IsolatedContext prepends the filenamePrefix parameter to
* file, database, and directory names.
* </p>
* <p>
* This is a convenience method for creating a "mock" provider that can contain test data.
* </p>
*
* @param targetContext The context to use as the basis of the IsolatedContext
* @param filenamePrefix A string that is prepended to file, database, and directory names
* @param providerClass The type of the provider being tested
* @param authority The authority string to associated with the test provider
* @param databaseName The name assigned to the database
* @param databaseVersion The version assigned to the database
* @param sql A string containing the SQL statements that are needed to create the desired
* database and its tables. The format is the same as that generated by the
* <a href="http://www.sqlite.org/sqlite.html">sqlite3</a> tool's <code>.dump</code> command.
* @return ContentResolver A new {@link MockContentResolver} linked to the provider
*
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static <T extends ContentProvider> ContentResolver newResolverWithContentProviderFromSql(
Context targetContext, String filenamePrefix, Class<T> providerClass, String authority,
String databaseName, int databaseVersion, String sql)
throws IllegalAccessException, InstantiationException {
MockContentResolver resolver = new MockContentResolver();
RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
new MockContext(), // The context that most methods are delegated to
targetContext, // The context that file methods are delegated to
filenamePrefix);
Context context = new IsolatedContext(resolver, targetContextWrapper);
DatabaseUtils.createDbFromSqlStatements(context, databaseName, databaseVersion, sql);
T provider = providerClass.newInstance();
provider.attachInfo(context, null);
resolver.addProvider(authority, provider);
return resolver;
}
}

View File

@ -1,7 +1,10 @@
package org.fdroid.fdroid; package org.fdroid.fdroid;
import android.app.Instrumentation; import android.app.Instrumentation;
import android.content.Context;
import android.os.Build; import android.os.Build;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import android.util.Log; import android.util.Log;
@ -14,10 +17,15 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID; import java.util.UUID;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
/** /**
@ -36,8 +44,8 @@ public class FileCompatTest {
@Before @Before
public void setUp() { public void setUp() {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
File dir = TestUtilsOld.getWriteableDir(instrumentation); File dir = getWriteableDir(instrumentation);
sourceFile = SanitizedFile.knownSanitized(TestUtilsOld.copyAssetToDir(instrumentation.getContext(), "simpleIndex.jar", dir)); sourceFile = SanitizedFile.knownSanitized(copyAssetToDir(instrumentation.getContext(), "simpleIndex.jar", dir));
destFile = new SanitizedFile(dir, "dest-" + UUID.randomUUID() + ".testproduct"); destFile = new SanitizedFile(dir, "dest-" + UUID.randomUUID() + ".testproduct");
assertFalse(destFile.exists()); assertFalse(destFile.exists());
assertTrue(sourceFile.getAbsolutePath() + " should exist.", sourceFile.exists()); assertTrue(sourceFile.getAbsolutePath() + " should exist.", sourceFile.exists());
@ -62,24 +70,70 @@ public class FileCompatTest {
@Test @Test
public void testSymlinkLibcore() { public void testSymlinkLibcore() {
assumeTrue(Build.VERSION.SDK_INT >= 19);
if (Build.VERSION.SDK_INT >= 19) { FileCompatForTest.symlinkLibcoreTest(sourceFile, destFile);
FileCompatForTest.symlinkLibcoreTest(sourceFile, destFile); assertTrue(destFile.getAbsolutePath() + " should exist after symlinking", destFile.exists());
assertTrue(destFile.getAbsolutePath() + " should exist after symlinking", destFile.exists());
} else {
Log.w(TAG, "Cannot test symlink-libcore on this device. Requires android-19, but this has android-" + Build.VERSION.SDK_INT);
}
} }
@Test @Test
public void testSymlinkOs() { public void testSymlinkOs() {
assumeTrue(Build.VERSION.SDK_INT >= 21);
if (Build.VERSION.SDK_INT >= 21) { FileCompatForTest.symlinkOsTest(sourceFile, destFile);
FileCompatForTest.symlinkOsTest(sourceFile, destFile); assertTrue(destFile.getAbsolutePath() + " should exist after symlinking", destFile.exists());
assertTrue(destFile.getAbsolutePath() + " should exist after symlinking", destFile.exists());
} else {
Log.w(TAG, "Cannot test symlink-os on this device. Requires android-21, but only has android-" + Build.VERSION.SDK_INT);
}
} }
@Nullable
private static File copyAssetToDir(Context context, String assetName, File directory) {
File tempFile;
InputStream input = null;
OutputStream output = null;
try {
tempFile = File.createTempFile(assetName + "-", ".testasset", directory);
Log.i(TAG, "Copying asset file " + assetName + " to directory " + directory);
input = context.getAssets().open(assetName);
output = new FileOutputStream(tempFile);
Utils.copy(input, output);
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
Utils.closeQuietly(output);
Utils.closeQuietly(input);
}
return tempFile;
}
/**
* Prefer internal over external storage, because external tends to be FAT filesystems,
* which don't support symlinks (which we test using this method).
*/
private static File getWriteableDir(Instrumentation instrumentation) {
Context context = instrumentation.getContext();
Context targetContext = instrumentation.getTargetContext();
File[] dirsToTry = new File[]{
context.getCacheDir(),
context.getFilesDir(),
targetContext.getCacheDir(),
targetContext.getFilesDir(),
context.getExternalCacheDir(),
context.getExternalFilesDir(null),
targetContext.getExternalCacheDir(),
targetContext.getExternalFilesDir(null),
Environment.getExternalStorageDirectory(),
};
return getWriteableDir(dirsToTry);
}
private static File getWriteableDir(File[] dirsToTry) {
for (File dir : dirsToTry) {
if (dir != null && dir.canWrite()) {
return dir;
}
}
return null;
}
} }

View File

@ -1,73 +0,0 @@
package org.fdroid.fdroid;
import android.app.Instrumentation;
import android.content.Context;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class TestUtilsOld {
private static final String TAG = "TestUtilsOld";
@Nullable
public static File copyAssetToDir(Context context, String assetName, File directory) {
File tempFile;
InputStream input = null;
OutputStream output = null;
try {
tempFile = File.createTempFile(assetName + "-", ".testasset", directory);
Log.i(TAG, "Copying asset file " + assetName + " to directory " + directory);
input = context.getAssets().open(assetName);
output = new FileOutputStream(tempFile);
Utils.copy(input, output);
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
Utils.closeQuietly(output);
Utils.closeQuietly(input);
}
return tempFile;
}
/**
* Prefer internal over external storage, because external tends to be FAT filesystems,
* which don't support symlinks (which we test using this method).
*/
public static File getWriteableDir(Instrumentation instrumentation) {
Context context = instrumentation.getContext();
Context targetContext = instrumentation.getTargetContext();
File[] dirsToTry = new File[]{
context.getCacheDir(),
context.getFilesDir(),
targetContext.getCacheDir(),
targetContext.getFilesDir(),
context.getExternalCacheDir(),
context.getExternalFilesDir(null),
targetContext.getExternalCacheDir(),
targetContext.getExternalFilesDir(null),
Environment.getExternalStorageDirectory(),
};
return getWriteableDir(dirsToTry);
}
private static File getWriteableDir(File[] dirsToTry) {
for (File dir : dirsToTry) {
if (dir != null && dir.canWrite()) {
return dir;
}
}
return null;
}
}

View File

@ -0,0 +1,82 @@
package org.fdroid.fdroid;
import android.util.Log;
import org.fdroid.fdroid.RepoUpdater.UpdateException;
import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import java.util.List;
import static org.junit.Assert.assertEquals;
@Config(constants = BuildConfig.class)
@RunWith(RobolectricGradleTestRunner.class)
public class AcceptableMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
private static final String TAG = "AcceptableMultiRepoTest";
private void assertSomewhatAcceptable() {
Log.i(TAG, "Asserting at least one versions of each .apk is in index.");
List<Repo> repos = RepoProvider.Helper.all(context);
assertEquals("Repos", 3, repos.size());
assertApp2048();
assertAppAdaway();
assertAppAdbWireless();
assertAppIcsImport();
}
@Test
public void testAcceptableConflictingThenMainThenArchive() throws UpdateException {
assertEmpty();
if (updateConflicting() && updateMain() && updateArchive()) {
assertSomewhatAcceptable();
}
}
@Test
public void testAcceptableConflictingThenArchiveThenMain() throws UpdateException {
assertEmpty();
if (updateConflicting() && updateArchive() && updateMain()) {
assertSomewhatAcceptable();
}
}
@Test
public void testAcceptableArchiveThenMainThenConflicting() throws UpdateException {
assertEmpty();
if (updateArchive() && updateMain() && updateConflicting()) {
assertSomewhatAcceptable();
}
}
@Test
public void testAcceptableArchiveThenConflictingThenMain() throws UpdateException {
assertEmpty();
if (updateArchive() && updateConflicting() && updateMain()) {
assertSomewhatAcceptable();
}
}
@Test
public void testAcceptableMainThenArchiveThenConflicting() throws UpdateException {
assertEmpty();
if (updateMain() && updateArchive() && updateConflicting()) {
assertSomewhatAcceptable();
}
}
@Test
public void testAcceptableMainThenConflictingThenArchive() throws UpdateException {
assertEmpty();
if (updateMain() && updateConflicting() && updateArchive()) {
assertSomewhatAcceptable();
}
}
}

View File

@ -5,7 +5,6 @@ import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import org.fdroid.fdroid.RepoUpdater.UpdateException; import org.fdroid.fdroid.RepoUpdater.UpdateException;
import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.Apk;
@ -16,10 +15,6 @@ import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.data.RepoProvider;
import org.junit.After; import org.junit.After;
import org.junit.Before; 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.io.File;
import java.util.List; import java.util.List;
@ -30,18 +25,16 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@Config(constants = BuildConfig.class) public abstract class MultiRepoUpdaterTest extends FDroidProviderTest {
@RunWith(RobolectricGradleTestRunner.class) private static final String TAG = "AcceptableMultiRepoUpdaterTest"; // NOPMD
public class MultiRepoUpdaterTest extends FDroidProviderTest {
private static final String TAG = "MultiRepoUpdaterTest";
private static final String REPO_MAIN = "Test F-Droid repo"; protected static final String REPO_MAIN = "Test F-Droid repo";
private static final String REPO_ARCHIVE = "Test F-Droid repo (Archive)"; protected static final String REPO_ARCHIVE = "Test F-Droid repo (Archive)";
private static final String REPO_CONFLICTING = "Test F-Droid repo with different apps"; protected static final String REPO_CONFLICTING = "Test F-Droid repo with different apps";
private RepoUpdater conflictingRepoUpdater; protected RepoUpdater conflictingRepoUpdater;
private RepoUpdater mainRepoUpdater; protected RepoUpdater mainRepoUpdater;
private RepoUpdater archiveRepoUpdater; protected RepoUpdater archiveRepoUpdater;
private static final String PUB_KEY = private static final String PUB_KEY =
"3082050b308202f3a003020102020420d8f212300d06092a864886f70d01010b050030363110300e0603" + "3082050b308202f3a003020102020420d8f212300d06092a864886f70d01010b050030363110300e0603" +
@ -77,7 +70,7 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest {
"e8af60597c4ae2977977cf61dc715a572e241ae717cafdb4f71781943945ac52e0f50b"; "e8af60597c4ae2977977cf61dc715a572e241ae717cafdb4f71781943945ac52e0f50b";
@Before @Before
public void setup() throws Exception { public final void setupMultiRepo() throws Exception {
// On a fresh database install, there will be F-Droid + GP repos, including their Archive // On a fresh database install, there will be F-Droid + GP repos, including their Archive
// repos that we are not interested in. // repos that we are not interested in.
RepoProvider.Helper.remove(context, 1); RepoProvider.Helper.remove(context, 1);
@ -93,123 +86,33 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest {
} }
@After @After
public void tearDown() { public final void tearDownMultiRepo() {
Preferences.clearSingletonForTesting(); Preferences.clearSingletonForTesting();
} }
/** protected void assertApp(String packageName, int[] versionCodes) {
* Check that all of the expected apps and apk versions are available in the database. This
* check will take into account the repository the apks came from, to ensure that each
* repository indeed contains the apks that it said it would provide.
*/
private void assertExpected() {
Log.i(TAG, "Asserting all versions of each .apk are in index.");
List<Repo> repos = RepoProvider.Helper.all(context);
assertEquals("Repos", 3, repos.size());
assertMainRepo(repos);
assertMainArchiveRepo(repos);
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);
assertEquals("Repos", 3, repos.size());
assertApp2048();
assertAppAdaway();
assertAppAdbWireless();
assertAppIcsImport();
}
private void assertApp(String packageName, int[] versionCodes) {
List<Apk> apks = ApkProvider.Helper.findByPackageName(context, packageName, ApkProvider.DataColumns.ALL); List<Apk> apks = ApkProvider.Helper.findByPackageName(context, packageName, ApkProvider.DataColumns.ALL);
assertApksExist(apks, packageName, versionCodes); assertApksExist(apks, packageName, versionCodes);
} }
private void assertApp2048() { protected void assertApp2048() {
assertApp("com.uberspot.a2048", new int[]{19, 18}); assertApp("com.uberspot.a2048", new int[]{19, 18});
} }
private void assertAppAdaway() { protected void assertAppAdaway() {
assertApp("org.adaway", new int[]{54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 42, 40, 38, 37, 36, 35}); assertApp("org.adaway", new int[]{54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 42, 40, 38, 37, 36, 35});
} }
private void assertAppAdbWireless() { protected void assertAppAdbWireless() {
assertApp("siir.es.adbWireless", new int[]{12}); assertApp("siir.es.adbWireless", new int[]{12});
} }
private void assertAppIcsImport() { protected void assertAppIcsImport() {
assertApp("org.dgtale.icsimport", new int[]{3, 2}); assertApp("org.dgtale.icsimport", new int[]{3, 2});
} }
/**
* + 2048 (com.uberspot.a2048)
* - Version 1.96 (19)
* - Version 1.95 (18)
* + AdAway (org.adaway)
* - Version 3.0.2 (54)
* - Version 3.0.1 (53)
* - Version 3.0 (52)
* + adbWireless (siir.es.adbWireless)
* - Version 1.5.4 (12)
*/
private void assertMainRepo(List<Repo> allRepos) {
Repo repo = findRepo(REPO_MAIN, allRepos);
List<Apk> apks = ApkProvider.Helper.findByRepo(context, repo, ApkProvider.DataColumns.ALL);
assertEquals("Apks for main repo", apks.size(), 6);
assertApksExist(apks, "com.uberspot.a2048", new int[]{18, 19});
assertApksExist(apks, "org.adaway", new int[]{52, 53, 54});
assertApksExist(apks, "siir.es.adbWireless", new int[]{12});
}
/**
* + AdAway (org.adaway)
* - Version 2.9.2 (51)
* - Version 2.9.1 (50)
* - Version 2.9 (49)
* - Version 2.8.1 (48)
* - Version 2.8 (47)
* - Version 2.7 (46)
* - Version 2.6 (45)
* - Version 2.3 (42)
* - Version 2.1 (40)
* - Version 1.37 (38)
* - Version 1.36 (37)
* - Version 1.35 (36)
* - Version 1.34 (35)
*/
private void assertMainArchiveRepo(List<Repo> allRepos) {
Repo repo = findRepo(REPO_ARCHIVE, allRepos);
List<Apk> apks = ApkProvider.Helper.findByRepo(context, repo, ApkProvider.DataColumns.ALL);
assertEquals("Apks for main archive repo", 13, apks.size());
assertApksExist(apks, "org.adaway", new int[]{35, 36, 37, 38, 40, 42, 45, 46, 47, 48, 49, 50, 51});
}
/**
* + AdAway (org.adaway)
* - Version 3.0.1 (53) *
* - Version 3.0 (52) *
* - Version 2.9.2 (51) *
* - Version 2.2.1 (50) *
* + Add to calendar (org.dgtale.icsimport)
* - Version 1.2 (3)
* - Version 1.1 (2)
*/
private void assertConflictingRepo(List<Repo> allRepos) {
Repo repo = findRepo(REPO_CONFLICTING, allRepos);
List<Apk> apks = ApkProvider.Helper.findByRepo(context, repo, ApkProvider.DataColumns.ALL);
assertEquals("Apks for main repo", 6, apks.size());
assertApksExist(apks, "org.adaway", new int[]{50, 51, 52, 53});
assertApksExist(apks, "org.dgtale.icsimport", new int[]{2, 3});
}
@NonNull @NonNull
private Repo findRepo(@NonNull String name, List<Repo> allRepos) { protected Repo findRepo(@NonNull String name, List<Repo> allRepos) {
Repo repo = null; Repo repo = null;
for (Repo r : allRepos) { for (Repo r : allRepos) {
if (TextUtils.equals(name, r.getName())) { if (TextUtils.equals(name, r.getName())) {
@ -225,7 +128,7 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest {
/** /**
* Checks that each version of appId as specified in versionCodes is present in apksToCheck. * Checks that each version of appId as specified in versionCodes is present in apksToCheck.
*/ */
private void assertApksExist(List<Apk> apksToCheck, String appId, int[] versionCodes) { protected void assertApksExist(List<Apk> apksToCheck, String appId, int[] versionCodes) {
for (int versionCode : versionCodes) { for (int versionCode : versionCodes) {
boolean found = false; boolean found = false;
for (Apk apk : apksToCheck) { for (Apk apk : apksToCheck) {
@ -239,7 +142,7 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest {
} }
} }
private void assertEmpty() { protected void assertEmpty() {
assertEquals("No apps present", 0, AppProvider.Helper.all(context.getContentResolver()).size()); assertEquals("No apps present", 0, AppProvider.Helper.all(context.getContentResolver()).size());
String[] packages = { String[] packages = {
@ -253,102 +156,6 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest {
} }
} }
/* At time fo writing, the following tests did not pass. This is because the multi-repo support
in F-Droid was not sufficient. When working on proper multi repo support than this should be
ucommented and all these tests should pass:
public void testCorrectConflictingThenMainThenArchive() throws UpdateException {
assertEmpty();
if (updateConflicting() && updateMain() && updateArchive()) {
assertExpected();
}
}
public void testCorrectConflictingThenArchiveThenMain() throws UpdateException {
assertEmpty();
if (updateConflicting() && updateArchive() && updateMain()) {
assertExpected();
}
}
public void testCorrectArchiveThenMainThenConflicting() throws UpdateException {
assertEmpty();
if (updateArchive() && updateMain() && updateConflicting()) {
assertExpected();
}
}
public void testCorrectArchiveThenConflictingThenMain() throws UpdateException {
assertEmpty();
if (updateArchive() && updateConflicting() && updateMain()) {
assertExpected();
}
}
public void testCorrectMainThenArchiveThenConflicting() throws UpdateException {
assertEmpty();
if (updateMain() && updateArchive() && updateConflicting()) {
assertExpected();
}
}
public void testCorrectMainThenConflictingThenArchive() throws UpdateException {
assertEmpty();
if (updateMain() && updateConflicting() && updateArchive()) {
assertExpected();
}
}
*/
@Test
public void testAcceptableConflictingThenMainThenArchive() throws UpdateException {
assertEmpty();
if (updateConflicting() && updateMain() && updateArchive()) {
assertSomewhatAcceptable();
}
}
@Test
public void testAcceptableConflictingThenArchiveThenMain() throws UpdateException {
assertEmpty();
if (updateConflicting() && updateArchive() && updateMain()) {
assertSomewhatAcceptable();
}
}
@Test
public void testAcceptableArchiveThenMainThenConflicting() throws UpdateException {
assertEmpty();
if (updateArchive() && updateMain() && updateConflicting()) {
assertSomewhatAcceptable();
}
}
@Test
public void testAcceptableArchiveThenConflictingThenMain() throws UpdateException {
assertEmpty();
if (updateArchive() && updateConflicting() && updateMain()) {
assertSomewhatAcceptable();
}
}
@Test
public void testAcceptableMainThenArchiveThenConflicting() throws UpdateException {
assertEmpty();
if (updateMain() && updateArchive() && updateConflicting()) {
assertSomewhatAcceptable();
}
}
@Test
public void testAcceptableMainThenConflictingThenArchive() throws UpdateException {
assertEmpty();
if (updateMain() && updateConflicting() && updateArchive()) {
assertSomewhatAcceptable();
}
}
private RepoUpdater createUpdater(String name, Context context) { private RepoUpdater createUpdater(String name, Context context) {
Repo repo = new Repo(); Repo repo = new Repo();
repo.signingCertificate = PUB_KEY; repo.signingCertificate = PUB_KEY;
@ -367,15 +174,15 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest {
return new RepoUpdater(context, RepoProvider.Helper.findByAddress(context, repo.address)); return new RepoUpdater(context, RepoProvider.Helper.findByAddress(context, repo.address));
} }
private boolean updateConflicting() throws UpdateException { protected boolean updateConflicting() throws UpdateException {
return updateRepo(conflictingRepoUpdater, "multiRepo.conflicting.jar"); return updateRepo(conflictingRepoUpdater, "multiRepo.conflicting.jar");
} }
private boolean updateMain() throws UpdateException { protected boolean updateMain() throws UpdateException {
return updateRepo(mainRepoUpdater, "multiRepo.normal.jar"); return updateRepo(mainRepoUpdater, "multiRepo.normal.jar");
} }
private boolean updateArchive() throws UpdateException { protected boolean updateArchive() throws UpdateException {
return updateRepo(archiveRepoUpdater, "multiRepo.archive.jar"); return updateRepo(archiveRepoUpdater, "multiRepo.archive.jar");
} }
@ -385,7 +192,7 @@ public class MultiRepoUpdaterTest extends FDroidProviderTest {
updater.processDownloadedFile(indexJar); updater.processDownloadedFile(indexJar);
} finally { } finally {
if (indexJar != null && indexJar.exists()) { if (indexJar != null && indexJar.exists()) {
indexJar.delete(); assertTrue(indexJar.delete());
} }
} }
return true; return true;

View File

@ -0,0 +1,155 @@
package org.fdroid.fdroid;
import android.util.Log;
import org.fdroid.fdroid.RepoUpdater.UpdateException;
import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.ApkProvider;
import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoProvider;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertEquals;
/*
At time fo writing, the following tests did not pass. This is because the multi-repo support
in F-Droid was not sufficient. When working on proper multi repo support than this should be
uncommented and all these tests will be required to pass:
@Config(constants = BuildConfig.class)
@RunWith(RobolectricGradleTestRunner.class)
*/
public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
private static final String TAG = "ProperMultiRepoSupport";
@Test
public void testCorrectConflictingThenMainThenArchive() throws UpdateException {
assertEmpty();
if (updateConflicting() && updateMain() && updateArchive()) {
assertExpected();
}
}
@Test
public void testCorrectConflictingThenArchiveThenMain() throws UpdateException {
assertEmpty();
if (updateConflicting() && updateArchive() && updateMain()) {
assertExpected();
}
}
@Test
public void testCorrectArchiveThenMainThenConflicting() throws UpdateException {
assertEmpty();
if (updateArchive() && updateMain() && updateConflicting()) {
assertExpected();
}
}
@Test
public void testCorrectArchiveThenConflictingThenMain() throws UpdateException {
assertEmpty();
if (updateArchive() && updateConflicting() && updateMain()) {
assertExpected();
}
}
@Test
public void testCorrectMainThenArchiveThenConflicting() throws UpdateException {
assertEmpty();
if (updateMain() && updateArchive() && updateConflicting()) {
assertExpected();
}
}
@Test
public void testCorrectMainThenConflictingThenArchive() throws UpdateException {
assertEmpty();
if (updateMain() && updateConflicting() && updateArchive()) {
assertExpected();
}
}
/**
* Check that all of the expected apps and apk versions are available in the database. This
* check will take into account the repository the apks came from, to ensure that each
* repository indeed contains the apks that it said it would provide.
*/
private void assertExpected() {
Log.i(TAG, "Asserting all versions of each .apk are in index.");
List<Repo> repos = RepoProvider.Helper.all(context);
assertEquals("Repos", 3, repos.size());
assertMainRepo(repos);
assertMainArchiveRepo(repos);
assertConflictingRepo(repos);
}
/**
* + 2048 (com.uberspot.a2048)
* - Version 1.96 (19)
* - Version 1.95 (18)
* + AdAway (org.adaway)
* - Version 3.0.2 (54)
* - Version 3.0.1 (53)
* - Version 3.0 (52)
* + adbWireless (siir.es.adbWireless)
* - Version 1.5.4 (12)
*/
private void assertMainRepo(List<Repo> allRepos) {
Repo repo = findRepo(REPO_MAIN, allRepos);
List<Apk> apks = ApkProvider.Helper.findByRepo(context, repo, ApkProvider.DataColumns.ALL);
assertEquals("Apks for main repo", apks.size(), 6);
assertApksExist(apks, "com.uberspot.a2048", new int[]{18, 19});
assertApksExist(apks, "org.adaway", new int[]{52, 53, 54});
assertApksExist(apks, "siir.es.adbWireless", new int[]{12});
}
/**
* + AdAway (org.adaway)
* - Version 2.9.2 (51)
* - Version 2.9.1 (50)
* - Version 2.9 (49)
* - Version 2.8.1 (48)
* - Version 2.8 (47)
* - Version 2.7 (46)
* - Version 2.6 (45)
* - Version 2.3 (42)
* - Version 2.1 (40)
* - Version 1.37 (38)
* - Version 1.36 (37)
* - Version 1.35 (36)
* - Version 1.34 (35)
*/
private void assertMainArchiveRepo(List<Repo> allRepos) {
Repo repo = findRepo(REPO_ARCHIVE, allRepos);
List<Apk> apks = ApkProvider.Helper.findByRepo(context, repo, ApkProvider.DataColumns.ALL);
assertEquals("Apks for main archive repo", 13, apks.size());
assertApksExist(apks, "org.adaway", new int[]{35, 36, 37, 38, 40, 42, 45, 46, 47, 48, 49, 50, 51});
}
/**
* + AdAway (org.adaway)
* - Version 3.0.1 (53) *
* - Version 3.0 (52) *
* - Version 2.9.2 (51) *
* - Version 2.2.1 (50) *
* + Add to calendar (org.dgtale.icsimport)
* - Version 1.2 (3)
* - Version 1.1 (2)
*/
private void assertConflictingRepo(List<Repo> allRepos) {
Repo repo = findRepo(REPO_CONFLICTING, allRepos);
List<Apk> apks = ApkProvider.Helper.findByRepo(context, repo, ApkProvider.DataColumns.ALL);
assertEquals("Apks for main repo", 6, apks.size());
assertApksExist(apks, "org.adaway", new int[]{50, 51, 52, 53});
assertApksExist(apks, "org.dgtale.icsimport", new int[]{2, 3});
}
}

View File

@ -6,11 +6,13 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
public class TestUtils { public class TestUtils {
private static final String TAG = "TestUtils"; @SuppressWarnings("unused")
private static final String TAG = "TestUtils"; // NOPMD
public static File copyResourceToTempFile(String resourceName) { public static File copyResourceToTempFile(String resourceName) {
File tempFile = null; File tempFile = null;
@ -24,7 +26,7 @@ public class TestUtils {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
if (tempFile != null && tempFile.exists()) { if (tempFile != null && tempFile.exists()) {
tempFile.delete(); assertTrue(tempFile.delete());
} }
fail(); fail();
return null; return null;

View File

@ -1,14 +1,14 @@
package org.fdroid.fdroid; package org.fdroid.fdroid;
import android.app.Instrumentation;
import android.content.Context; import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -17,7 +17,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@RunWith(AndroidJUnit4.class) @Config(constants = BuildConfig.class)
@RunWith(RobolectricGradleTestRunner.class)
public class UtilsTest { public class UtilsTest {
String fdroidFingerprint = "43238D512C1E5EB2D6569F4A3AFBF5523418B82E0A3ED1552770ABB9A9C9CCAB"; String fdroidFingerprint = "43238D512C1E5EB2D6569F4A3AFBF5523418B82E0A3ED1552770ABB9A9C9CCAB";
@ -50,7 +51,7 @@ public class UtilsTest {
@Test @Test
public void testFormatFingerprint() { public void testFormatFingerprint() {
Context context = InstrumentationRegistry.getTargetContext(); Context context = RuntimeEnvironment.application;
String badResult = Utils.formatFingerprint(context, ""); String badResult = Utils.formatFingerprint(context, "");
// real fingerprints // real fingerprints
String formatted; String formatted;
@ -145,22 +146,29 @@ public class UtilsTest {
@Test @Test
public void testClearOldFiles() throws IOException, InterruptedException { public void testClearOldFiles() throws IOException, InterruptedException {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); File tempDir = new File(System.getProperty("java.io.tmpdir"));
File dir = new File(TestUtilsOld.getWriteableDir(instrumentation), "clearOldFiles"); assertTrue(tempDir.isDirectory());
assertTrue(tempDir.canWrite());
File dir = new File(tempDir, "F-Droid-test.clearOldFiles");
FileUtils.deleteQuietly(dir); FileUtils.deleteQuietly(dir);
dir.mkdirs(); assertTrue(dir.mkdirs());
assertTrue(dir.isDirectory()); assertTrue(dir.isDirectory());
File first = new File(dir, "first"); File first = new File(dir, "first");
first.deleteOnExit();
File second = new File(dir, "second"); File second = new File(dir, "second");
second.deleteOnExit();
assertFalse(first.exists()); assertFalse(first.exists());
assertFalse(second.exists()); assertFalse(second.exists());
first.createNewFile(); assertTrue(first.createNewFile());
assertTrue(first.exists()); assertTrue(first.exists());
Thread.sleep(7000); Thread.sleep(7000);
second.createNewFile(); assertTrue(second.createNewFile());
assertTrue(second.exists()); assertTrue(second.exists());
Utils.clearOldFiles(dir, 3); Utils.clearOldFiles(dir, 3);

View File

@ -23,7 +23,6 @@ import java.util.List;
import static org.fdroid.fdroid.data.ProviderTestUtils.assertCantDelete; import static org.fdroid.fdroid.data.ProviderTestUtils.assertCantDelete;
import static org.fdroid.fdroid.data.ProviderTestUtils.assertContainsOnly; import static org.fdroid.fdroid.data.ProviderTestUtils.assertContainsOnly;
import static org.fdroid.fdroid.data.ProviderTestUtils.assertResultCount; import static org.fdroid.fdroid.data.ProviderTestUtils.assertResultCount;
import static org.fdroid.fdroid.data.ProviderTestUtils.insertApk;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@ -38,8 +37,8 @@ public class ApkProviderTest extends FDroidProviderTest {
@Test @Test
public void testAppApks() { public void testAppApks() {
for (int i = 1; i <= 10; i++) { for (int i = 1; i <= 10; i++) {
insertApk(contentResolver, "org.fdroid.fdroid", i); ProviderTestUtils.insertApk(contentResolver, "org.fdroid.fdroid", i);
insertApk(contentResolver, "com.example", i); ProviderTestUtils.insertApk(contentResolver, "com.example", i);
} }
assertTotalApkCount(20); assertTotalApkCount(20);
@ -187,23 +186,12 @@ public class ApkProviderTest extends FDroidProviderTest {
Apk apk = new MockApk("org.fdroid.fdroid", 13); Apk apk = new MockApk("org.fdroid.fdroid", 13);
// Insert a new record... // Insert a new record...
Uri newUri = insertApk(contentResolver, apk.packageName, apk.versionCode); Uri newUri = ProviderTestUtils.insertApk(contentResolver, apk.packageName, apk.versionCode);
assertEquals(ApkProvider.getContentUri(apk).toString(), newUri.toString()); assertEquals(ApkProvider.getContentUri(apk).toString(), newUri.toString());
cursor = queryAllApks(); cursor = queryAllApks();
assertNotNull(cursor); assertNotNull(cursor);
assertEquals(1, cursor.getCount()); assertEquals(1, cursor.getCount());
// We intentionally throw an IllegalArgumentException if you haven't
// yet called cursor.move*()...
try {
new Apk(cursor);
fail();
} catch (IllegalArgumentException e) {
// Success!
} catch (Exception e) {
fail();
}
// And now we should be able to recover these values from the apk // And now we should be able to recover these values from the apk
// value object (because the queryAllApks() helper asks for VERSION_CODE and // value object (because the queryAllApks() helper asks for VERSION_CODE and
// PACKAGE_NAME. // PACKAGE_NAME.
@ -214,12 +202,19 @@ public class ApkProviderTest extends FDroidProviderTest {
assertEquals(13, toCheck.versionCode); assertEquals(13, toCheck.versionCode);
} }
@Test(expected = IllegalArgumentException.class)
public void testCursorMustMoveToFirst() {
ProviderTestUtils.insertApk(contentResolver, "org.example.test", 12);
Cursor cursor = queryAllApks();
new Apk(cursor);
}
@Test @Test
public void testCount() { public void testCount() {
String[] projectionCount = new String[] {ApkProvider.DataColumns._COUNT}; String[] projectionCount = new String[] {ApkProvider.DataColumns._COUNT};
for (int i = 0; i < 13; i++) { for (int i = 0; i < 13; i++) {
insertApk(contentResolver, "com.example", i); ProviderTestUtils.insertApk(contentResolver, "com.example", i);
} }
Uri all = ApkProvider.getContentUri(); Uri all = ApkProvider.getContentUri();
@ -236,38 +231,48 @@ public class ApkProviderTest extends FDroidProviderTest {
allWithCount.close(); allWithCount.close();
} }
@Test(expected = IllegalArgumentException.class)
public void testInsertWithInvalidExtraFieldDescription() {
assertInvalidExtraField(RepoProvider.DataColumns.DESCRIPTION);
}
@Test(expected = IllegalArgumentException.class)
public void testInsertWithInvalidExtraFieldAddress() {
assertInvalidExtraField(RepoProvider.DataColumns.ADDRESS);
}
@Test(expected = IllegalArgumentException.class)
public void testInsertWithInvalidExtraFieldFingerprint() {
assertInvalidExtraField(RepoProvider.DataColumns.FINGERPRINT);
}
@Test(expected = IllegalArgumentException.class)
public void testInsertWithInvalidExtraFieldName() {
assertInvalidExtraField(RepoProvider.DataColumns.NAME);
}
@Test(expected = IllegalArgumentException.class)
public void testInsertWithInvalidExtraFieldSigningCert() {
assertInvalidExtraField(RepoProvider.DataColumns.SIGNING_CERT);
}
public void assertInvalidExtraField(String field) {
ContentValues invalidRepo = new ContentValues();
invalidRepo.put(field, "Test data");
ProviderTestUtils.insertApk(contentResolver, "org.fdroid.fdroid", 10, invalidRepo);
}
@Test @Test
public void testInsertWithExtraFields() { public void testInsertWithValidExtraFields() {
assertResultCount(0, queryAllApks()); assertResultCount(0, queryAllApks());
String[] repoFields = new String[] {
RepoProvider.DataColumns.DESCRIPTION,
RepoProvider.DataColumns.ADDRESS,
RepoProvider.DataColumns.FINGERPRINT,
RepoProvider.DataColumns.NAME,
RepoProvider.DataColumns.SIGNING_CERT,
};
for (String field : repoFields) {
ContentValues invalidRepo = new ContentValues();
invalidRepo.put(field, "Test data");
try {
insertApk(contentResolver, "org.fdroid.fdroid", 10, invalidRepo);
fail();
} catch (IllegalArgumentException e) {
} catch (Exception e) {
fail();
}
assertResultCount(0, queryAllApks());
}
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(ApkProvider.DataColumns.REPO_ID, 10); values.put(ApkProvider.DataColumns.REPO_ID, 10);
values.put(ApkProvider.DataColumns.REPO_ADDRESS, "http://example.com"); values.put(ApkProvider.DataColumns.REPO_ADDRESS, "http://example.com");
values.put(ApkProvider.DataColumns.REPO_VERSION, 3); values.put(ApkProvider.DataColumns.REPO_VERSION, 3);
values.put(ApkProvider.DataColumns.FEATURES, "Some features"); values.put(ApkProvider.DataColumns.FEATURES, "Some features");
Uri uri = insertApk(contentResolver, "com.example.com", 1, values); Uri uri = ProviderTestUtils.insertApk(contentResolver, "com.example.com", 1, values);
assertResultCount(1, queryAllApks()); assertResultCount(1, queryAllApks());
@ -293,18 +298,18 @@ public class ApkProviderTest extends FDroidProviderTest {
public void testKnownApks() { public void testKnownApks() {
for (int i = 0; i < 7; i++) { for (int i = 0; i < 7; i++) {
insertApk(contentResolver, "org.fdroid.fdroid", i); ProviderTestUtils.insertApk(contentResolver, "org.fdroid.fdroid", i);
} }
for (int i = 0; i < 9; i++) { for (int i = 0; i < 9; i++) {
insertApk(contentResolver, "org.example", i); ProviderTestUtils.insertApk(contentResolver, "org.example", i);
} }
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
insertApk(contentResolver, "com.example", i); ProviderTestUtils.insertApk(contentResolver, "com.example", i);
} }
insertApk(contentResolver, "com.apk.thingo", 1); ProviderTestUtils.insertApk(contentResolver, "com.apk.thingo", 1);
Apk[] known = { Apk[] known = {
new MockApk("org.fdroid.fdroid", 1), new MockApk("org.fdroid.fdroid", 1),
@ -351,18 +356,18 @@ public class ApkProviderTest extends FDroidProviderTest {
public void testFindByApp() { public void testFindByApp() {
for (int i = 0; i < 7; i++) { for (int i = 0; i < 7; i++) {
insertApk(contentResolver, "org.fdroid.fdroid", i); ProviderTestUtils.insertApk(contentResolver, "org.fdroid.fdroid", i);
} }
for (int i = 0; i < 9; i++) { for (int i = 0; i < 9; i++) {
insertApk(contentResolver, "org.example", i); ProviderTestUtils.insertApk(contentResolver, "org.example", i);
} }
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
insertApk(contentResolver, "com.example", i); ProviderTestUtils.insertApk(contentResolver, "com.example", i);
} }
insertApk(contentResolver, "com.apk.thingo", 1); ProviderTestUtils.insertApk(contentResolver, "com.apk.thingo", 1);
assertTotalApkCount(7 + 9 + 3 + 1); assertTotalApkCount(7 + 9 + 3 + 1);
@ -386,7 +391,7 @@ public class ApkProviderTest extends FDroidProviderTest {
@Test @Test
public void testUpdate() { public void testUpdate() {
Uri apkUri = insertApk(contentResolver, "com.example", 10); Uri apkUri = ProviderTestUtils.insertApk(contentResolver, "com.example", 10);
String[] allFields = ApkProvider.DataColumns.ALL; String[] allFields = ApkProvider.DataColumns.ALL;
Cursor cursor = contentResolver.query(apkUri, allFields, null, null, null); Cursor cursor = contentResolver.query(apkUri, allFields, null, null, null);
@ -442,18 +447,18 @@ public class ApkProviderTest extends FDroidProviderTest {
// the Helper.find() method doesn't stumble upon the app we are interested // the Helper.find() method doesn't stumble upon the app we are interested
// in by shear dumb luck... // in by shear dumb luck...
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
insertApk(contentResolver, "org.fdroid.apk." + i, i); ProviderTestUtils.insertApk(contentResolver, "org.fdroid.apk." + i, i);
} }
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(ApkProvider.DataColumns.VERSION_NAME, "v1.1"); values.put(ApkProvider.DataColumns.VERSION_NAME, "v1.1");
values.put(ApkProvider.DataColumns.HASH, "xxxxyyyy"); values.put(ApkProvider.DataColumns.HASH, "xxxxyyyy");
values.put(ApkProvider.DataColumns.HASH_TYPE, "a hash type"); values.put(ApkProvider.DataColumns.HASH_TYPE, "a hash type");
insertApk(contentResolver, "com.example", 11, values); ProviderTestUtils.insertApk(contentResolver, "com.example", 11, values);
// ...and a few more for good measure... // ...and a few more for good measure...
for (int i = 15; i < 20; i++) { for (int i = 15; i < 20; i++) {
insertApk(contentResolver, "com.other.thing." + i, i); ProviderTestUtils.insertApk(contentResolver, "com.other.thing." + i, i);
} }
Apk apk = ApkProvider.Helper.find(context, "com.example", 11); Apk apk = ApkProvider.Helper.find(context, "com.example", 11);
@ -529,7 +534,7 @@ public class ApkProviderTest extends FDroidProviderTest {
protected Apk insertApkForRepo(String id, int versionCode, long repoId) { protected Apk insertApkForRepo(String id, int versionCode, long repoId) {
ContentValues additionalValues = new ContentValues(); ContentValues additionalValues = new ContentValues();
additionalValues.put(ApkProvider.DataColumns.REPO_ID, repoId); additionalValues.put(ApkProvider.DataColumns.REPO_ID, repoId);
Uri uri = insertApk(contentResolver, id, versionCode, additionalValues); Uri uri = ProviderTestUtils.insertApk(contentResolver, id, versionCode, additionalValues);
return ApkProvider.Helper.get(context, uri); return ApkProvider.Helper.get(context, uri);
} }
} }

View File

@ -25,7 +25,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@Config(constants = BuildConfig.class, application = Application.class) @Config(constants = BuildConfig.class, application = Application.class)
@RunWith(RobolectricGradleTestRunner.class) @RunWith(RobolectricGradleTestRunner.class)
@ -224,17 +223,6 @@ public class AppProviderTest extends FDroidProviderTest {
assertNotNull(cursor); assertNotNull(cursor);
assertEquals(1, cursor.getCount()); assertEquals(1, cursor.getCount());
// We intentionally throw an IllegalArgumentException if you haven't
// yet called cursor.move*()...
try {
new App(cursor);
fail();
} catch (IllegalArgumentException e) {
// Success!
} catch (Exception e) {
fail();
}
// And now we should be able to recover these values from the app // And now we should be able to recover these values from the app
// value object (because the queryAllApps() helper asks for NAME and // value object (because the queryAllApps() helper asks for NAME and
// PACKAGE_NAME. // PACKAGE_NAME.
@ -250,6 +238,17 @@ public class AppProviderTest extends FDroidProviderTest {
assertEquals("F-Droid", otherApp.name); assertEquals("F-Droid", otherApp.name);
} }
/**
* We intentionally throw an IllegalArgumentException if you haven't
* yet called cursor.move*().
*/
@Test(expected = IllegalArgumentException.class)
public void testCursorMustMoveToFirst() {
insertApp("org.fdroid.fdroid", "F-Droid");
Cursor cursor = queryAllApps();
new App(cursor);
}
private Cursor queryAllApps() { private Cursor queryAllApps() {
String[] projection = new String[] { String[] projection = new String[] {
AppProvider.DataColumns._ID, AppProvider.DataColumns._ID,

View File

@ -19,7 +19,6 @@ import static org.fdroid.fdroid.data.ProviderTestUtils.assertResultCount;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Map; import java.util.Map;
@ -85,24 +84,12 @@ public class InstalledAppProviderTest extends FDroidProviderTest{
@Test @Test
public void testUpdate() { public void testUpdate() {
insertInstalledApp("com.example.app1", 10, "1.0"); insertInstalledApp("com.example.app1", 10, "1.0");
insertInstalledApp("com.example.app2", 10, "1.0"); insertInstalledApp("com.example.app2", 10, "1.0");
assertResultCount(contentResolver, 2, InstalledAppProvider.getContentUri()); assertResultCount(contentResolver, 2, InstalledAppProvider.getContentUri());
assertIsInstalledVersionInDb(contentResolver, "com.example.app2", 10, "1.0"); assertIsInstalledVersionInDb(contentResolver, "com.example.app2", 10, "1.0");
try {
contentResolver.update(
InstalledAppProvider.getAppUri("com.example.app2"),
createContentValues(11, "1.1"),
null, null
);
fail();
} catch (UnsupportedOperationException e) {
// We expect this to happen, because we should be using insert() instead.
}
contentResolver.insert( contentResolver.insert(
InstalledAppProvider.getContentUri(), InstalledAppProvider.getContentUri(),
createContentValues("com.example.app2", 11, "1.1") createContentValues("com.example.app2", 11, "1.1")
@ -110,7 +97,19 @@ public class InstalledAppProviderTest extends FDroidProviderTest{
assertResultCount(contentResolver, 2, InstalledAppProvider.getContentUri()); assertResultCount(contentResolver, 2, InstalledAppProvider.getContentUri());
assertIsInstalledVersionInDb(contentResolver, "com.example.app2", 11, "1.1"); assertIsInstalledVersionInDb(contentResolver, "com.example.app2", 11, "1.1");
}
/**
* We expect this to happen, because we should be using insert() instead as it will
* do an insert/replace query.
*/
@Test(expected = UnsupportedOperationException.class)
public void testUpdateFails() {
contentResolver.update(
InstalledAppProvider.getAppUri("com.example.app2"),
createContentValues(11, "1.1"),
null, null
);
} }
@Test @Test

View File

@ -17,7 +17,6 @@ import java.util.List;
import static org.fdroid.fdroid.data.ProviderTestUtils.assertInvalidUri; import static org.fdroid.fdroid.data.ProviderTestUtils.assertInvalidUri;
import static org.fdroid.fdroid.data.ProviderTestUtils.assertValidUri; import static org.fdroid.fdroid.data.ProviderTestUtils.assertValidUri;
import static org.junit.Assert.fail;
@Config(constants = BuildConfig.class) @Config(constants = BuildConfig.class)
@RunWith(RobolectricGradleTestRunner.class) @RunWith(RobolectricGradleTestRunner.class)
@ -124,6 +123,11 @@ public class ProviderUriTests {
assertValidUri(resolver, ApkProvider.getContentUri(apks), projection); assertValidUri(resolver, ApkProvider.getContentUri(apks), projection);
assertValidUri(resolver, ApkProvider.getContentUri("org.fdroid.fdroid", 100), "content://org.fdroid.fdroid.data.ApkProvider/apk/100/org.fdroid.fdroid", projection); assertValidUri(resolver, ApkProvider.getContentUri("org.fdroid.fdroid", 100), "content://org.fdroid.fdroid.data.ApkProvider/apk/100/org.fdroid.fdroid", projection);
assertValidUri(resolver, ApkProvider.getRepoUri(1000), "content://org.fdroid.fdroid.data.ApkProvider/repo/1000", projection); assertValidUri(resolver, ApkProvider.getRepoUri(1000), "content://org.fdroid.fdroid.data.ApkProvider/repo/1000", projection);
}
@Test(expected = IllegalArgumentException.class)
public void invalidApkUrisWithTooManyApks() {
String[] projection = ApkProvider.DataColumns.ALL;
List<Apk> manyApks = new ArrayList<>(ApkProvider.MAX_APKS_TO_QUERY - 5); List<Apk> manyApks = new ArrayList<>(ApkProvider.MAX_APKS_TO_QUERY - 5);
for (int i = 0; i < ApkProvider.MAX_APKS_TO_QUERY - 1; i++) { for (int i = 0; i < ApkProvider.MAX_APKS_TO_QUERY - 1; i++) {
@ -133,18 +137,12 @@ public class ProviderUriTests {
manyApks.add(new MockApk("org.fdroid.fdroid.1", 1)); manyApks.add(new MockApk("org.fdroid.fdroid.1", 1));
manyApks.add(new MockApk("org.fdroid.fdroid.2", 2)); manyApks.add(new MockApk("org.fdroid.fdroid.2", 2));
try {
// Technically, it is a valid URI, because it doesn't // Technically, it is a valid URI, because it doesn't
// throw an UnsupportedOperationException. However it // throw an UnsupportedOperationException. However it
// is still not okay (we run out of bindable parameters // is still not okay (we run out of bindable parameters
// in the sqlite query. // in the sqlite query.
assertValidUri(resolver, ApkProvider.getContentUri(manyApks), projection); assertValidUri(resolver, ApkProvider.getContentUri(manyApks), projection);
fail();
} catch (IllegalArgumentException e) {
// This is the expected error behaviour.
} catch (Exception e) {
fail();
}
} }
} }

Binary file not shown.

View File

@ -0,0 +1,9 @@
<?xml version="1.0"?>
<ruleset name="Custom ruleset (for main code)"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
<rule ref="rulesets/java/imports.xml" />
</ruleset>

11
config/pmd/rules-test.xml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0"?>
<ruleset name="Custom ruleset (for tests)"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
<rule ref="rulesets/java/imports.xml">
<exclude name="TooManyStaticImports" /><!-- Static imports are commonly used for JUnit tests -->
</rule>
</ruleset>

View File

@ -12,7 +12,6 @@
<rule ref="rulesets/java/android.xml"/> <rule ref="rulesets/java/android.xml"/>
<rule ref="rulesets/java/clone.xml"/> <rule ref="rulesets/java/clone.xml"/>
<rule ref="rulesets/java/finalizers.xml"/> <rule ref="rulesets/java/finalizers.xml"/>
<rule ref="rulesets/java/imports.xml"/>
<rule ref="rulesets/java/migrating.xml"/> <rule ref="rulesets/java/migrating.xml"/>
<rule ref="rulesets/java/unnecessary.xml"> <rule ref="rulesets/java/unnecessary.xml">
<exclude name="UselessParentheses"/> <!--Too nitpicky--> <exclude name="UselessParentheses"/> <!--Too nitpicky-->