Bradlee Speice 2014-11-22 22:15:54 -05:00
commit 3ed3d6c08b
7 changed files with 158 additions and 141 deletions

View File

@ -5,4 +5,4 @@ script:
- ./gradlew test
after_success:
- ./gradlew jacocoTestReport coveralls --stacktrace
- ./gradlew jacocoTestReport coveralls

View File

@ -22,6 +22,12 @@ repositories {
}
}
test {
testLogging {
exceptionFormat = 'full'
}
}
def androidModule = project(':app')
def firstVariant = androidModule.android.applicationVariants.toList().first()
@ -30,7 +36,7 @@ dependencies {
testCompile 'junit:junit:4.+'
testCompile 'org.robolectric:robolectric:+'
testCompile 'org.mockito:mockito-all:+'
testCompile 'org.mockito:mockito-core:+'
testCompile 'com.jayway.awaitility:awaitility:+'
testCompile 'org.jetbrains.spek:spek:+'

View File

@ -0,0 +1,82 @@
package org.bspeice.minimalbible.activity.downloader
import org.jetbrains.spek.api.Spek
import kotlin.test.assertTrue
import android.content.DialogInterface
/**
* Created by bspeice on 11/22/14.
*/
class BookListFragmentSpek : Spek() {{
given("A BookListFragment with showDialog() mocked out") {
val fragment = object : BookListFragment() {
var condition = false
override fun showDialog() {
condition = true
}
}
on("attempting to display modules with the dialog not shown already") {
fragment.displayModules(false)
it("should show the download dialog") {
assertTrue(fragment.condition)
}
}
}
given("a BookListFragment with displayLanguageSpinner() mocked out") {
val fragment = object : BookListFragment() {
var condition = false
override fun displayLanguageSpinner() {
condition = true
}
}
on("attempting to display modules with the dialog already shown") {
fragment.displayModules(true)
it("should show the available languages spinner") {
assertTrue(fragment.condition)
}
}
}
given("a DownloadDialogListener with with buttonPositive() mocked out") {
val listener = object : BookListFragment.DownloadDialogListener(null, null) {
var condition = false
override fun buttonPositive() {
condition = true
}
}
on("handling a positive button press") {
listener.handleButton(DialogInterface.BUTTON_POSITIVE)
it("should call the proper handler") {
assertTrue(listener.condition)
}
}
}
given("A DownloadDialogListener with buttonNegative() mocked out") {
val listener = object : BookListFragment.DownloadDialogListener(null, null) {
var condition = false
override fun buttonNegative() {
condition = true
}
}
on("handling a negative button press") {
listener.handleButton(DialogInterface.BUTTON_NEGATIVE)
it("should call the proper handler") {
assertTrue(listener.condition)
}
}
}
}
}

View File

@ -1,70 +0,0 @@
package org.bspeice.minimalbible.test.activity.downloader;
import org.bspeice.minimalbible.MBTestCase;
import org.bspeice.minimalbible.activity.downloader.BookListFragment;
import org.crosswire.common.util.Language;
import org.crosswire.jsword.book.Book;
import org.crosswire.jsword.book.BookCategory;
import org.mockito.Mockito;
import rx.Observable;
import rx.functions.Action1;
import static org.mockito.Mockito.when;
public class BookListFragmentTest extends MBTestCase {
public void testBooksByLanguage() throws Exception {
BookCategory bibleCategory = BookCategory.BIBLE;
BookCategory dictionaryCategory = BookCategory.DICTIONARY;
Language russianLanguage = new Language("ru");
Language englishLanguage = new Language("en");
final Book russianBible = Mockito.mock(Book.class);
when(russianBible.getBookCategory()).thenReturn(bibleCategory);
when(russianBible.getLanguage()).thenReturn(russianLanguage);
final Book englishBible = Mockito.mock(Book.class);
when(englishBible.getBookCategory()).thenReturn(bibleCategory);
when(englishBible.getLanguage()).thenReturn(englishLanguage);
final Book englishDictionary = Mockito.mock(Book.class);
when(englishDictionary.getBookCategory()).thenReturn(dictionaryCategory);
when(englishDictionary.getLanguage()).thenReturn(englishLanguage);
Observable<Book> mockBooks = Observable.just(russianBible, englishBible,
englishDictionary);
// Since we're not testing lifecycle here, don't worry about newInstance()
TestableBookListFragment fragment = new TestableBookListFragment();
fragment.booksByLanguage(mockBooks, englishLanguage, bibleCategory)
.subscribe(new Action1<Book>() {
@Override
public void call(Book book) {
assertEquals(englishBible, book);
}
});
fragment.booksByLanguage(mockBooks, russianLanguage, bibleCategory)
.subscribe(new Action1<Book>() {
@Override
public void call(Book book) {
assertEquals(russianBible, book);
}
});
fragment.booksByLanguage(mockBooks, englishLanguage, dictionaryCategory)
.subscribe(new Action1<Book>() {
@Override
public void call(Book book) {
assertEquals(englishDictionary, book);
}
});
}
public static class TestableBookListFragment extends BookListFragment {
@Override
public Observable<Book> booksByLanguage(Observable<Book> books, Language language, BookCategory category) {
return super.booksByLanguage(books, language, category);
}
}
}

View File

@ -2,7 +2,6 @@ package org.bspeice.minimalbible.activity.downloader;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
@ -11,7 +10,6 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.Toast;
import org.bspeice.minimalbible.Injector;
@ -45,9 +43,9 @@ public class BookListFragment extends BaseFragment {
protected static final String ARG_BOOK_CATEGORY = "book_category";
@Inject
protected DownloadPrefs downloadPrefs;
protected ProgressDialog refreshDialog;
@Inject RefreshManager refreshManager;
DownloadPrefs downloadPrefs;
@Inject
RefreshManager refreshManager;
@Inject
List<Language> availableLanguages;
@ -56,7 +54,7 @@ public class BookListFragment extends BaseFragment {
@InjectView(R.id.spn_available_languages)
Spinner languagesSpinner;
private LayoutInflater inflater;
LayoutInflater inflater;
/**
* Returns a new instance of this fragment for the given section number.
@ -95,61 +93,44 @@ public class BookListFragment extends BaseFragment {
.getString(ARG_BOOK_CATEGORY));
}
void displayModules() {
displayModules(downloadPrefs.hasShownDownloadDialog());
}
/**
* Trigger the functionality to display a list of modules. Prompts user if downloading
* from the internet is allowable.
*/
protected void displayModules() {
boolean dialogDisplayed = downloadPrefs.hasShownDownloadDialog();
if (!dialogDisplayed) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
DownloadDialogListener dialogListener = new DownloadDialogListener();
builder.setMessage(
"About to contact servers to download content. Continue?")
.setPositiveButton("Yes", dialogListener)
.setNegativeButton("No", dialogListener)
.setCancelable(false).show();
} else {
refreshModules();
}
void displayModules(boolean dialogDisplayed) {
if (!dialogDisplayed) {
showDialog();
} else {
displayLanguageSpinner();
}
}
/**
* Do the work of refreshing modules (download manager handles using cached copy vs. actual
* refresh), and then displaying them when ready.
*/
private void refreshModules() {
// Check if the downloadManager has already refreshed everything
if (!refreshManager.getRefreshComplete().get()) {
// downloadManager is in progress of refreshing
refreshDialog = new ProgressDialog(getActivity());
refreshDialog.setMessage("Refreshing available modules...");
refreshDialog.setCancelable(false);
refreshDialog.show();
}
languagesSpinner.setAdapter(getLocaleSpinner());
if (BookListFragment.this.getActivity() != null) {
// On a screen rotate, getActivity() will be null. But, the activity
// will already have been set up correctly, so we don't need to worry
// about it.
// If not null, we need to set it up now.
setInsetsSpinner(BookListFragment.this.getActivity(), languagesSpinner);
}
if (refreshDialog != null) {
refreshDialog.cancel();
}
void showDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
DownloadDialogListener dialogListener = new DownloadDialogListener(this, downloadPrefs);
builder.setMessage(
"About to contact servers to download content. Continue?")
.setPositiveButton("Yes", dialogListener)
.setNegativeButton("No", dialogListener)
.setCancelable(false).show();
}
@SuppressWarnings("ConstantConditions")
// getAvailableLanguagesList() will not return null
SpinnerAdapter getLocaleSpinner() {
void displayLanguageSpinner() {
ArrayAdapter<Object> adapter = new ArrayAdapter<Object>(this.getActivity(),
android.R.layout.simple_spinner_item,
availableLanguages.toArray());
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
return adapter;
languagesSpinner.setAdapter(adapter);
if (BookListFragment.this.getActivity() != null) {
// On a screen rotate, getActivity() will be null, but the activity
// will already have been set up. If not null, we need to set it up now.
setInsetsSpinner(BookListFragment.this.getActivity(), languagesSpinner);
}
}
@SuppressWarnings("unused")
@ -195,35 +176,53 @@ public class BookListFragment extends BaseFragment {
});
}
private class DownloadDialogListener implements
static class DownloadDialogListener implements
DialogInterface.OnClickListener {
BookListFragment fragment;
DownloadPrefs downloadPrefs;
DownloadDialogListener(BookListFragment fragment, DownloadPrefs downloadPrefs) {
this.fragment = fragment;
this.downloadPrefs = downloadPrefs;
}
@Override
public void onClick(@NotNull DialogInterface dialog, int which) {
downloadPrefs.hasShownDownloadDialog(true);
handleButton(which);
}
void handleButton(int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
// Clicked ready to continue - allow downloading in the future
downloadPrefs.hasEnabledDownload(true);
// And warn them that it has been enabled in the future.
Toast.makeText(getActivity(),
"Downloading now enabled. Disable in settings.",
Toast.LENGTH_SHORT).show();
refreshModules();
buttonPositive();
break;
// case DialogInterface.BUTTON_NEGATIVE:
default:
// Clicked to not download - Permanently disable downloading
downloadPrefs.hasEnabledDownload(false);
Toast.makeText(getActivity(),
"Disabling downloading. Re-enable it in settings.",
Toast.LENGTH_SHORT).show();
refreshModules();
buttonNegative();
break;
}
}
}
void buttonPositive() {
// Clicked ready to continue - allow downloading in the future
downloadPrefs.hasEnabledDownload(true);
// And warn them that it has been enabled in the future.
showToast("Downloading now enabled. Disable in settings");
fragment.displayModules();
}
void buttonNegative() {
// Clicked to not download - Permanently disable downloading
downloadPrefs.hasEnabledDownload(false);
showToast("Disabling downloading. Re-enable it in settings.");
fragment.getActivity().finish();
}
void showToast(String text) {
Toast.makeText(fragment.getActivity(), text, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -48,9 +48,9 @@ class RefreshManager(val installers: Collection<Installer>,
val fifteenDaysAgo = Calendar.getInstance().getTime().getTime() - 1296000
fun doReload(enabledDownload: Boolean, lastUpdated: Long,
fun doReload(downloadEnabled: Boolean, lastUpdated: Long,
networkState: Int? = ConnectivityManager.TYPE_DUMMY): Boolean =
if (!enabledDownload || networkState != ConnectivityManager.TYPE_WIFI)
if (!downloadEnabled || networkState != ConnectivityManager.TYPE_WIFI)
false
else if (lastUpdated < fifteenDaysAgo)
true

View File

@ -4,7 +4,7 @@ buildscript {
ext.kotlin_version = '0.9.206'
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.13.+'
@ -16,6 +16,6 @@ buildscript {
allprojects {
repositories {
jcenter()
mavenCentral()
}
}