Permalink
Browse files

Have stores emit only non-null values (#148)

  • Loading branch information...
apoi committed Dec 19, 2016
1 parent ac8a272 commit 590cdf6fdd8004853577c210d6dbafdea2432d84
Showing with 1,404 additions and 863 deletions.
  1. +7 −4 .travis.yml
  2. +10 −9 app/build.gradle
  3. +4 −14 app/src/androidTest/java/io/reark/rxgithubapp/advanced/activities/ChooseRepositoryActivityTest.java
  4. +5 −18 app/src/androidTest/java/io/reark/rxgithubapp/advanced/activities/MainActivityTest.java
  5. +204 −0 app/src/androidTest/java/io/reark/rxgithubapp/advanced/data/stores/GitHubRepositoryStoreTest.java
  6. +142 −0 ...droidTest/java/io/reark/rxgithubapp/advanced/data/stores/cores/GitHubRepositoryStoreCoreTest.java
  7. +0 −73 app/src/main/java/io/reark/rxgithubapp/advanced/activities/utils/SystemAnimations.java
  8. +1 −0 app/src/main/java/io/reark/rxgithubapp/advanced/data/DataLayer.java
  9. +18 −1 app/src/main/java/io/reark/rxgithubapp/advanced/data/schematicProvider/GitHubProvider.java
  10. +16 −50 app/src/main/java/io/reark/rxgithubapp/advanced/data/stores/GitHubRepositorySearchStore.java
  11. +5 −11 app/src/main/java/io/reark/rxgithubapp/advanced/data/stores/GitHubRepositoryStore.java
  12. +0 −51 app/src/main/java/io/reark/rxgithubapp/advanced/data/stores/GsonStoreBase.java
  13. +8 −68 app/src/main/java/io/reark/rxgithubapp/advanced/data/stores/NetworkRequestStatusStore.java
  14. +7 −6 app/src/main/java/io/reark/rxgithubapp/advanced/data/stores/StoreModule.java
  15. +13 −62 app/src/main/java/io/reark/rxgithubapp/advanced/data/stores/UserSettingsStore.java
  16. +101 −0 ...rc/main/java/io/reark/rxgithubapp/advanced/data/stores/cores/GitHubRepositorySearchStoreCore.java
  17. +11 −6 app/src/main/java/io/reark/rxgithubapp/advanced/data/stores/cores/GitHubRepositoryStoreCore.java
  18. +0 −51 app/src/main/java/io/reark/rxgithubapp/advanced/data/stores/cores/GsonStoreCoreBase.java
  19. +102 −0 app/src/main/java/io/reark/rxgithubapp/advanced/data/stores/cores/NetworkRequestStatusStoreCore.java
  20. +105 −0 app/src/main/java/io/reark/rxgithubapp/advanced/data/stores/cores/UserSettingsStoreCore.java
  21. +8 −6 app/src/main/java/io/reark/rxgithubapp/advanced/network/FetcherModule.java
  22. +9 −9 appbasic/build.gradle
  23. +6 −2 appbasic/src/main/java/io/reark/rxgithubapp/basic/data/stores/GitHubRepositorySearchStore.java
  24. +7 −2 appbasic/src/main/java/io/reark/rxgithubapp/basic/data/stores/GitHubRepositoryStore.java
  25. +5 −20 appbasic/src/main/java/io/reark/rxgithubapp/basic/data/stores/NetworkRequestStatusStore.java
  26. +7 −3 appbasic/src/main/java/io/reark/rxgithubapp/basic/data/stores/UserSettingsStore.java
  27. +9 −7 appbasic/src/main/java/io/reark/rxgithubapp/basic/network/FetcherModule.java
  28. +8 −8 appshared/build.gradle
  29. +20 −13 appshared/src/main/java/io/reark/rxgithubapp/shared/data/ClientDataLayerBase.java
  30. +7 −7 appshared/src/main/java/io/reark/rxgithubapp/shared/data/DataLayerBase.java
  31. +1 −1 appshared/src/main/java/io/reark/rxgithubapp/shared/network/fetchers/GitHubRepositoryFetcher.java
  32. +1 −1 ...red/src/main/java/io/reark/rxgithubapp/shared/network/fetchers/GitHubRepositorySearchFetcher.java
  33. +9 −1 appshared/src/main/java/io/reark/rxgithubapp/shared/pojo/GitHubOwner.java
  34. +30 −4 appshared/src/main/java/io/reark/rxgithubapp/shared/pojo/GitHubRepository.java
  35. +11 −2 appshared/src/main/java/io/reark/rxgithubapp/shared/pojo/GitHubRepositorySearch.java
  36. +1 −1 appshared/src/main/java/io/reark/rxgithubapp/shared/pojo/GitHubRepositorySearchResults.java
  37. +16 −0 appshared/src/main/java/io/reark/rxgithubapp/shared/pojo/UserSettings.java
  38. +30 −0 appshared/src/test/java/io/reark/rxgithubapp/shared/pojo/GitHubRepositorySearchTest.java
  39. +79 −0 appshared/src/test/java/io/reark/rxgithubapp/shared/pojo/GitHubRepositoryTest.java
  40. +1 −0 build.gradle
  41. +1 −18 gradle.properties
  42. +14 −16 reark/build.gradle
  43. +64 −29 reark/src/androidTest/java/io/reark/reark/data/stores/ContentProviderStoreTest.java
  44. +1 −1 reark/src/androidTest/java/io/reark/reark/data/stores/SimpleMockContentProvider.java
  45. +16 −115 reark/src/main/java/io/reark/reark/data/stores/ContentProviderStore.java
  46. +29 −7 reark/src/main/java/io/reark/reark/data/stores/DefaultStore.java
  47. +11 −6 reark/src/main/java/io/reark/reark/data/stores/MemoryStore.java
  48. +2 −2 reark/src/main/java/io/reark/reark/data/stores/StoreItem.java
  49. +23 −22 reark/src/main/java/io/reark/reark/data/stores/cores/ContentProviderStoreCore.java
  50. +38 −43 reark/src/main/java/io/reark/reark/data/stores/cores/ContentProviderStoreCoreBase.java
  51. +10 −3 reark/src/main/java/io/reark/reark/data/stores/cores/MemoryStoreCore.java
  52. +3 −3 reark/src/main/java/io/reark/reark/data/stores/{cores → interfaces}/StoreCoreInterface.java
  53. +13 −10 reark/src/main/java/io/reark/reark/data/stores/{ → interfaces}/StoreGetInterface.java
  54. +3 −2 reark/src/main/java/io/reark/reark/data/stores/{ → interfaces}/StoreInterface.java
  55. +1 −1 reark/src/main/java/io/reark/reark/data/stores/{ → interfaces}/StorePutInterface.java
  56. +1 −2 reark/src/main/java/io/reark/reark/data/utils/DataLayerUtils.java
  57. +4 −3 reark/src/main/java/io/reark/reark/network/fetchers/FetcherBase.java
  58. +38 −9 reark/src/main/java/io/reark/reark/pojo/NetworkRequestStatus.java
  59. +76 −47 reark/src/test/java/io/reark/reark/data/stores/MemoryStoreTest.java
  60. +32 −13 reark/src/test/java/io/reark/reark/data/stores/cores/MemoryStoreCoreTest.java
View
@@ -7,14 +7,14 @@ android:
components:
- platform-tools
- tools
- android-23
- build-tools-23.0.3
- android-24
- build-tools-25.0.0
- extra-google-m2repository
- extra-android-m2repository
- sys-img-armeabi-v7a-android-23
- sys-img-armeabi-v7a-android-24
before_script:
- echo no | android create avd --force -n test -t android-23 --abi armeabi-v7a
- echo no | android create avd --force -n test -t android-24 --abi armeabi-v7a
- emulator -avd test -no-audio -no-window &
- android-wait-for-emulator
- adb shell settings put global window_animation_scale 0 &
@@ -25,6 +25,9 @@ before_script:
script:
- ./gradlew test spoon
after_failure:
- find . -path "*spoon*.json" | xargs grep "fail"
notifications:
email: false
View
@@ -1,3 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'com.neenbedankt.android-apt'
apply plugin: 'spoon'
buildscript {
repositories {
mavenCentral()
@@ -10,17 +15,13 @@ repositories {
mavenCentral()
}
apply plugin: 'com.android.application'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'com.neenbedankt.android-apt'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
compileSdkVersion 24
buildToolsVersion "25.0.0"
defaultConfig {
minSdkVersion 15
targetSdkVersion 23
targetSdkVersion 24
versionCode 1
versionName "1.0"
@@ -76,7 +77,7 @@ dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.android.support:recyclerview-v7:23.3.0'
compile 'com.android.support:recyclerview-v7:24.2.1'
// Schematic
apt 'net.simonvt.schematic:schematic-compiler:0.6.7'
@@ -92,7 +93,7 @@ dependencies {
testCompile 'org.powermock:powermock-api-mockito:1.6.4'
testCompile 'org.powermock:powermock-module-junit4:1.6.4'
androidTestCompile 'com.android.support:support-annotations:23.3.0'
androidTestCompile 'com.android.support:support-annotations:24.2.1'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
@@ -25,19 +25,16 @@
*/
package io.reark.rxgithubapp.advanced.activities;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import io.reark.rxgithubapp.R;
import io.reark.rxgithubapp.advanced.activities.utils.SystemAnimations;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;
@@ -50,14 +47,11 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
public class ChooseRepositoryActivityTest {
@Rule
public ActivityTestRule<ChooseRepositoryActivity> activityRule = new ActivityTestRule<>(ChooseRepositoryActivity.class);
@BeforeClass
public static void setUp() {
SystemAnimations.disableAll(InstrumentationRegistry.getContext());
}
@Ignore("Ignore UI tests due to Travis")
@Test
public void testInitialActivityState() {
onView(withId(R.id.repositories_list_view)).check(matches(isDisplayed()));
@@ -66,13 +60,9 @@ public void testInitialActivityState() {
onView(withId(R.id.repositories_status_text)).check(matches(withText((""))));
}
@Ignore("Ignore UI tests due to Travis")
@Test
public void testCanPerformInsertingSearchText() {
onView(withId(R.id.repositories_search)).perform(closeSoftKeyboard(), typeText("reark"));
}
@AfterClass
public static void tearDown() {
SystemAnimations.enableAll(InstrumentationRegistry.getContext());
}
}
@@ -25,19 +25,16 @@
*/
package io.reark.rxgithubapp.advanced.activities;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import io.reark.rxgithubapp.R;
import io.reark.rxgithubapp.advanced.activities.utils.SystemAnimations;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
@@ -53,27 +50,17 @@
@Rule
public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class);
@BeforeClass
public static void setUp() {
SystemAnimations.disableAll(InstrumentationRegistry.getContext());
}
@Ignore("Ignore UI tests due to Travis")
@Test
public void testInitialActivityState() {
public void testInitialActivityState() throws InterruptedException {
onView(withText(R.string.repository_fragment_intro)).check(matches(isDisplayed()));
onView(withId(R.id.widget_avatar_image_view)).check(matches(isDisplayed()));
onView(withText(R.string.repository_fragment_change)).check(matches(isDisplayed()));
}
@Ignore("Ignore UI tests due to Travis")
@Test
public void testPressingChangeButtonLaunchesRepositoriesActivity() {
public void testPressingChangeButtonLaunchesRepositoriesActivity() throws InterruptedException {
onView(withText(R.string.repository_fragment_change)).perform(click());
onView(withId(R.id.repositories_view)).check(matches(isDisplayed()));
}
@AfterClass
public static void tearDown() {
SystemAnimations.enableAll(InstrumentationRegistry.getContext());
}
}
@@ -0,0 +1,204 @@
package io.reark.rxgithubapp.advanced.data.stores;
import android.content.pm.ProviderInfo;
import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.ProviderTestCase2;
import android.test.mock.MockContentResolver;
import com.google.gson.Gson;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.TimeUnit;
import io.reark.rxgithubapp.advanced.data.schematicProvider.generated.GitHubProvider;
import io.reark.rxgithubapp.shared.pojo.GitHubOwner;
import io.reark.rxgithubapp.shared.pojo.GitHubRepository;
import rx.Observable;
import rx.observers.TestSubscriber;
import static io.reark.rxgithubapp.advanced.data.schematicProvider.GitHubProvider.GitHubRepositories.GITHUB_REPOSITORIES;
import static io.reark.rxgithubapp.shared.pojo.GitHubRepository.none;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
@RunWith(AndroidJUnit4.class)
public class GitHubRepositoryStoreTest extends ProviderTestCase2<GitHubProvider> {
private GitHubRepositoryStore gitHubRepositoryStore;
private TestSubscriber<GitHubRepository> testSubscriber;
private GitHubProvider contentProvider;
private Gson gson = new Gson();
public GitHubRepositoryStoreTest() {
super(GitHubProvider.class, GitHubProvider.AUTHORITY);
}
@Before
@Override
public void setUp() throws Exception {
setContext(InstrumentationRegistry.getTargetContext());
final ProviderInfo providerInfo = new ProviderInfo();
providerInfo.authority = GitHubProvider.AUTHORITY;
contentProvider = new GitHubProvider();
contentProvider.attachInfo(InstrumentationRegistry.getTargetContext(), providerInfo);
contentProvider.delete(GITHUB_REPOSITORIES, null, null);
Thread.sleep(2500);
final MockContentResolver contentResolver = new MockContentResolver();
contentResolver.addProvider(GitHubProvider.AUTHORITY, contentProvider);
gitHubRepositoryStore = new GitHubRepositoryStore(contentResolver, gson);
testSubscriber = new TestSubscriber<>();
super.setUp();
}
@After
@Override
public void tearDown() throws Exception {
contentProvider.delete(GITHUB_REPOSITORIES, null, null);
super.tearDown();
}
@Test
public void getOne_WithData_ReturnsData_AndCompletes() throws InterruptedException {
final GitHubRepository value = create(100, "repository1");
gitHubRepositoryStore.put(value); // TODO synchronous init with contentProvider
Thread.sleep(1500);
// getOnce is expected to return a observable that emits the value and then completes.
gitHubRepositoryStore.getOnce(100).subscribe(testSubscriber);
testSubscriber.awaitTerminalEvent(1500, TimeUnit.MILLISECONDS);
testSubscriber.assertCompleted();
testSubscriber.assertNoErrors();
testSubscriber.assertReceivedOnNext(singletonList(value));
}
@Test
public void getOne_WithNoData_ReturnsNoneValue_AndCompletes() {
// getOnce is expected to emit empty value in case no actual value exists.
gitHubRepositoryStore.getOnce(100).subscribe(testSubscriber);
testSubscriber.awaitTerminalEvent(1500, TimeUnit.MILLISECONDS);
testSubscriber.assertCompleted();
testSubscriber.assertNoErrors();
testSubscriber.assertValue(none());
}
@Test
public void getOnceAndStream_ReturnsOnlyValuesForSubscribedId_AndDoesNotComplete() throws InterruptedException {
final GitHubRepository value1 = create(100, "repository1");
final GitHubRepository value2 = create(200, "repository2");
gitHubRepositoryStore.getOnceAndStream(100).subscribe(testSubscriber);
Thread.sleep(1500);
gitHubRepositoryStore.put(value1);
gitHubRepositoryStore.put(value2);
testSubscriber.awaitTerminalEvent(1500, TimeUnit.MILLISECONDS);
testSubscriber.assertNotCompleted();
testSubscriber.assertNoErrors();
testSubscriber.assertReceivedOnNext(asList(none(), value1));
}
@Test
public void getOnceAndStream_ReturnsAllValuesForSubscribedId_AndDoesNotComplete() throws InterruptedException {
final GitHubRepository value1 = create(100, "repository-1");
final GitHubRepository value2 = create(100, "repository-2");
final GitHubRepository value3 = create(100, "repository-3");
gitHubRepositoryStore.getOnceAndStream(100).subscribe(testSubscriber);
Thread.sleep(1500);
gitHubRepositoryStore.put(value1);
Thread.sleep(1500);
gitHubRepositoryStore.put(value2);
Thread.sleep(1500);
gitHubRepositoryStore.put(value3);
testSubscriber.awaitTerminalEvent(1500, TimeUnit.MILLISECONDS);
testSubscriber.assertNotCompleted();
testSubscriber.assertNoErrors();
testSubscriber.assertReceivedOnNext(asList(none(), value1, value2, value3));
}
@Test
public void getOnceAndStream_ReturnsOnlyNewValues_AndDoesNotComplete() throws InterruptedException {
final GitHubRepository value = create(100, "repository1");
// In the default store implementation identical values are filtered out.
gitHubRepositoryStore.getOnceAndStream(100).subscribe(testSubscriber);
Thread.sleep(1500);
gitHubRepositoryStore.put(value);
gitHubRepositoryStore.put(value);
testSubscriber.awaitTerminalEvent(1500, TimeUnit.MILLISECONDS);
testSubscriber.assertNotCompleted();
testSubscriber.assertNoErrors();
testSubscriber.assertReceivedOnNext(asList(none(), value));
}
@Test
public void getOnceAndStream_WithInitialValue_ReturnsInitialValues_AndDoesNotComplete() throws InterruptedException {
final GitHubRepository value = create(100, "repository1");
gitHubRepositoryStore.put(value);
Thread.sleep(1500);
gitHubRepositoryStore.getOnceAndStream(100).subscribe(testSubscriber);
testSubscriber.awaitTerminalEvent(1500, TimeUnit.MILLISECONDS);
testSubscriber.assertNotCompleted();
testSubscriber.assertNoErrors();
testSubscriber.assertReceivedOnNext(singletonList(value));
}
@Test
public void getOnceAndStream_WithInitialValue_WithDelayedSubscription_ReturnsFirstValue_AndDoesNotComplete() throws InterruptedException {
final GitHubRepository value1 = create(100, "repository1");
final GitHubRepository value2 = create(100, "repository1");
// This behavior is a little surprising, but it is because we cannot guarantee that the
// observable that is produced as the stream will keep its first (cached) value up to date.
// The only ways to around this would be custom subscribe function or converting the
// source observable into a behavior, but these would significantly increase the
// complexity and are hard to implement in other kinds of store (such as content providers).
// Put initial value.
gitHubRepositoryStore.put(value1);
Thread.sleep(1500);
// Create the stream observable but do not subscribe immediately.
Observable<GitHubRepository> stream = gitHubRepositoryStore.getOnceAndStream(100);
// Put new value into the store.
gitHubRepositoryStore.put(value2);
// Subscribe to stream that was created potentially a long time ago.
stream.subscribe(testSubscriber);
// Observe that the stream actually gives as the first item the cached value at the time of
// creating the stream observable.
testSubscriber.awaitTerminalEvent(1500, TimeUnit.MILLISECONDS);
testSubscriber.assertNotCompleted();
testSubscriber.assertNoErrors();
testSubscriber.assertReceivedOnNext(singletonList(value1));
}
@NonNull
private static GitHubRepository create(int id, String name) {
return new GitHubRepository(id, name, 10, 10, new GitHubOwner("owner"));
}
}
Oops, something went wrong.

0 comments on commit 590cdf6

Please sign in to comment.