Clean and make more strict the InstalledManager tests

Unfortunately, they're currently always going to succeed. I'm having issues with the API not giving me a fresh list of what is installed.
This commit is contained in:
Bradlee Speice 2014-07-19 00:29:27 -04:00
parent d6d52cea04
commit 46e1285b61
3 changed files with 117 additions and 20 deletions

View File

@ -1,13 +1,29 @@
package org.bspeice.minimalbible.test.activity.downloader.manager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import junit.framework.TestCase;
import org.bspeice.minimalbible.Injector;
import org.bspeice.minimalbible.activity.downloader.manager.BookDownloadManager;
import org.bspeice.minimalbible.activity.downloader.manager.DLProgressEvent;
import org.bspeice.minimalbible.activity.downloader.manager.InstalledManager;
import org.bspeice.minimalbible.activity.downloader.manager.RefreshManager;
import org.crosswire.jsword.book.Book;
import org.crosswire.jsword.book.Books;
import org.crosswire.jsword.book.BooksEvent;
import org.crosswire.jsword.book.BooksListener;
import org.crosswire.jsword.book.install.InstallManager;
import org.crosswire.jsword.book.install.Installer;
import org.crosswire.jsword.book.sword.SwordBook;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
@ -17,14 +33,20 @@ import dagger.Module;
import dagger.ObjectGraph;
import dagger.Provides;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Func1;
import static com.jayway.awaitility.Awaitility.await;
public class InstalledManagerTest extends TestCase implements Injector {
ObjectGraph mObjectGraph;
@Module(injects = {InstalledManager.class,
InstalledManagerTest.class})
InstalledManagerTest.class,
RefreshManager.class,
BookDownloadManager.class})
static class IMTestModules {
Injector i;
public IMTestModules(Injector i) {
@ -45,10 +67,17 @@ public class InstalledManagerTest extends TestCase implements Injector {
List<Book> provideInstalledBooksList(Books b) {
return b.getBooks();
}
@Provides @Singleton
Collection<Installer> provideInstallers() {
return new InstallManager().getInstallers().values();
}
}
@Inject Books installedBooks;
@Inject InstalledManager iM;
@Inject BookDownloadManager bDM;
@Inject RefreshManager rM;
@Inject Books installedBooks;
@Override
public void inject(Object o) {
@ -60,17 +89,55 @@ public class InstalledManagerTest extends TestCase implements Injector {
mObjectGraph = ObjectGraph.create(new IMTestModules(this));
mObjectGraph.inject(this);
//TODO: Guarantee that a book is installed.
// Guarantee something is installed
getInstalledBooks()
.count()
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer count) {
if (count <= 0) {
Log.i("InstalledManagerTest", "Nothing installed!");
final AtomicBoolean isInstalled = new AtomicBoolean(false);
final Book toInstall = rM.getAvailableModulesFlattened().toBlocking().first();
bDM.installBook(toInstall);
bDM.getDownloadEvents()
.subscribe(new Action1<DLProgressEvent>() {
@Override
public void call(DLProgressEvent dlProgressEvent) {
if (dlProgressEvent.getProgress() == DLProgressEvent.PROGRESS_COMPLETE &&
dlProgressEvent.getB().getName().equals(toInstall.getName())) {
isInstalled.set(true);
}
}
});
await().atMost(30, TimeUnit.SECONDS)
.untilTrue(isInstalled);
}
}
});
}
Observable<Book> getInstalledBooks() {
public Observable<Book> getInstalledBooks() {
/* The golden copy for testing of what's installed.
NOTE: Currently, I have yet to find a guaranteed way to know if a book
is installed or not. So while the test cases are semantically correct,
nothing is actually proven until I can guarantee this list is correct.
*/
// TODO: Guarantee that we return newly-installed books
return Observable.from(installedBooks.getBooks())
.filter(new Func1<Book, Boolean>() {
@Override
public Boolean call(Book book) {
// Double check that the book is actually installed
return book.getDriver().isDeletable(book);
}
})
.filter(new Func1<Book, Boolean>() {
@Override
public Boolean call(Book book) {
// Not sure why, but this book can't be deleted...
return !book.getInitials().equals("ot1nt2");
}
});
}
@ -80,14 +147,22 @@ public class InstalledManagerTest extends TestCase implements Injector {
.subscribe(new Action1<Book>() {
@Override
public void call(Book book) {
if (!iM.isInstalled(book)) {
foundMismatch.set(true);
// Skip if we've already found a mismatch
if (!foundMismatch.get()) {
// We've already filtered to what we know is installed,
// so set to true if iM doesn't think it's installed.
foundMismatch.set(!iM.isInstalled(book));
}
}
});
assertFalse(foundMismatch.get());
}
/**
* Test that we can remove a book. Currently this test is neutered until I can fix
* issues with @link{getInstalledBooks}.
* @throws Exception
*/
public void testRemoveBook() throws Exception {
final AtomicBoolean isRemoved = new AtomicBoolean(false);
getInstalledBooks()
@ -96,9 +171,23 @@ public class InstalledManagerTest extends TestCase implements Injector {
@Override
public void call(Book book) {
iM.removeBook(book);
isRemoved.set(!book.getDriver().isDeletable(book));
// The AbstractBook returns false all the time, make sure we have
// an actual implementation
Log.w("testRemoveBook", book.getInitials());
isRemoved.set(!book.getDriver().isDeletable(book) &&
book instanceof SwordBook);
}
});
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
fail(throwable.getLocalizedMessage());
}
}, new Action0() {
@Override
public void call() {
assertTrue(isRemoved.get());
}
});
}
}

View File

@ -14,6 +14,8 @@ import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import rx.Observable;
/**
* Manager to keep track of which books have been installed
*/
@ -28,6 +30,7 @@ public class InstalledManager implements BooksListener {
@Inject
InstalledManager(Injector injector) {
injector.inject(this);
installedBooksList.addAll(installedBooks.getBooks());
installedBooks.addBooksListener(this);
}
@ -35,6 +38,12 @@ public class InstalledManager implements BooksListener {
return installedBooksList.contains(b);
}
public Observable<Book> getInstalledBooks() {
// This method is needed to provide a fresher copy of what's installed
// than Books.getInstalled() does.
return Observable.from(installedBooksList);
}
@Override
public void bookAdded(BooksEvent booksEvent) {
Log.d(TAG, "Book added: " + booksEvent.getBook().toString());
@ -54,8 +63,6 @@ public class InstalledManager implements BooksListener {
}
public void removeBook(Book b) {
// Not sure why we need to call this multiple times, but...
while (Books.installed().getBooks().contains(b)) {
try {
// This worked in the past, but isn't now...
// installedBooks.remove(b);
@ -66,4 +73,3 @@ public class InstalledManager implements BooksListener {
}
}
}
}

View File

@ -52,7 +52,9 @@ public class RefreshManager {
/**
* Do the work of kicking off the AsyncTask to refresh books, and make sure we know
* when it's done.
* TODO: Should I have a better way of scheduling than Schedulers.io()?
* NOTE: This code assigns its own thread. This is because we are called privately, and
* don't want to expose this method. I don't like hiding the side effects like this, but
* in this case I'm making an exception.
*/
private Observable<Map<Installer, List<Book>>> refreshModules() {
if (availableModules == null) {