From 589a62cc03a76d3114b7aec8736ba3fa09ea1103 Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Tue, 12 Jul 2016 17:24:39 +1000 Subject: [PATCH 01/14] Try fixing travis build issue --- .travis.yml | 4 +++- library/android-mvc-test/build.gradle | 2 ++ samples/benchmark/build.gradle | 2 ++ samples/simple-mvc/app/build.gradle | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fa20317..20f56dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,4 +46,6 @@ env: - ADB_INSTALL_TIMEOUT=10 # Add this -sudo: required \ No newline at end of file +sudo: required + +- GRADLE_OPTS="-Xmx512m -XX:MaxPermSize=512m" \ No newline at end of file diff --git a/library/android-mvc-test/build.gradle b/library/android-mvc-test/build.gradle index d68bec3..02c37a7 100644 --- a/library/android-mvc-test/build.gradle +++ b/library/android-mvc-test/build.gradle @@ -46,6 +46,8 @@ android { versionCode 1 versionName "1.0.0" + multiDexEnabled false + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } diff --git a/samples/benchmark/build.gradle b/samples/benchmark/build.gradle index af55f7c..14f5a35 100644 --- a/samples/benchmark/build.gradle +++ b/samples/benchmark/build.gradle @@ -44,6 +44,8 @@ android { targetSdkVersion rootProject.ext.androidTargetSdkVersion versionCode 1 versionName "1.0.0" + + multiDexEnabled false } signingConfigs { diff --git a/samples/simple-mvc/app/build.gradle b/samples/simple-mvc/app/build.gradle index d6952d4..d19c801 100644 --- a/samples/simple-mvc/app/build.gradle +++ b/samples/simple-mvc/app/build.gradle @@ -42,6 +42,8 @@ android { targetSdkVersion rootProject.ext.androidTargetSdkVersion versionCode 1 versionName "1.0.0" + + multiDexEnabled false } sourceSets { From 44498c400f5461ba5cdab5244ee91180d37baa6b Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Tue, 12 Jul 2016 17:29:17 +1000 Subject: [PATCH 02/14] update env for travis --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 20f56dc..a8107c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,6 +35,7 @@ jdk: env: - TERM=dumb +- GRADLE_OPTS="-Xmx512m -XX:MaxPermSize=512m" after_success: - ./gradlew coveralls @@ -46,6 +47,4 @@ env: - ADB_INSTALL_TIMEOUT=10 # Add this -sudo: required - -- GRADLE_OPTS="-Xmx512m -XX:MaxPermSize=512m" \ No newline at end of file +sudo: required \ No newline at end of file From 3493739244dba5149325d0d97eaf50fbdae20044 Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Tue, 12 Jul 2016 17:39:57 +1000 Subject: [PATCH 03/14] Tweak germ size on gradle --- gradle.properties | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index e8aac03..c8ab4ce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -35,5 +35,4 @@ org.gradle.daemon=true org.gradle.parallel=true - -org.gradle.jvmargs=-Xmx2048M \ No newline at end of file +org.gradle.jvmargs=-XX:MaxPermSize=1024m -XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -Xmx2048m \ No newline at end of file From a523b1c094d1e406907f3a1ade1dbc52ee7127c8 Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Wed, 13 Jul 2016 11:53:24 +1000 Subject: [PATCH 04/14] Uplift support library to 24.0.0 and remove all hack for issue reported https://code.google.com/p/android/issues/detail?id=197271 --- ChangeLog.md | 1 + build.gradle | 4 +- .../lib/android/mvc/BaseTestCase.java | 4 +- .../mvc/view/eventv2v/TestV2VEvents.java | 5 + .../TestInjectedStateManagedObjects.java | 4 + .../injection/TestInjectionAndLifeCycle.java | 5 + ...tInjectionAndLifeCycleForRootFragment.java | 5 + .../view/lifecycle/BaseTestCaseLifeCycle.java | 5 + .../nav/TestCaseNavigationAndInjection.java | 5 + .../mvc/view/nav/TestCaseNavigationBasic.java | 6 + .../nav/TestCaseNavigationFromController.java | 5 + .../viewpager/TestFragmentsInViewPager.java | 5 + .../lib/android/mvc/MvcActivity.java | 123 +----------------- 13 files changed, 56 insertions(+), 121 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 7b66d44..b1542d9 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -10,6 +10,7 @@ injection. A concrete class can be injected straight away. * Register providers to Graph's root component rather than to graph itself. * Managers listen to eventBusC * New lifecycle MvcFragment.onPopAway +* Uplift support library to 24.0.0 and remove all hack for issue reported https://code.google.com/p/android/issues/detail?id=197271 Version:2.3 * New navigation method that allow configuring the location not pushed to back stack diff --git a/build.gradle b/build.gradle index e23b3b2..98c9faf 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' + classpath 'com.android.tools.build:gradle:2.2.0-alpha5' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' @@ -83,7 +83,7 @@ ext { androidMinSdkVersion = 14 androidCompileSdkVersion = 23 androidBuildToolVersion = "23.0.3" - supportLibVersion = "23.4.0" + supportLibVersion = "24.0.0" androidTargetSdkVersion = androidCompileSdkVersion lib = [ intelljAnnoatations: 'com.intellij:annotations:12.0', diff --git a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/BaseTestCase.java b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/BaseTestCase.java index d92859d..ba8b802 100644 --- a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/BaseTestCase.java +++ b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/BaseTestCase.java @@ -59,7 +59,7 @@ @RunWith(AndroidJUnit4.class) @LargeTest -public class BaseTestCase extends ActivityInstrumentationTestCase2 { +public abstract class BaseTestCase extends ActivityInstrumentationTestCase2 { protected LifeCycleValidator lifeCycleValidator; protected LifeCycleMonitor lifeCycleMonitorMock; @@ -76,6 +76,8 @@ public class BaseTestCase extends ActivityInstrumentatio @EventBusV private EventBus eventBusV; + protected abstract Class getActivityClass(); + private static class Waiter { boolean skip = false; private String name; diff --git a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/eventv2v/TestV2VEvents.java b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/eventv2v/TestV2VEvents.java index 2bce39b..1059431 100644 --- a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/eventv2v/TestV2VEvents.java +++ b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/eventv2v/TestV2VEvents.java @@ -39,6 +39,11 @@ public TestV2VEvents() { super(EventBusV2VActivity.class); } + @Override + protected Class getActivityClass() { + return EventBusV2VActivity.class; + } + @Override public void setUp() throws Exception { super.setUp(); diff --git a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/injection/TestInjectedStateManagedObjects.java b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/injection/TestInjectedStateManagedObjects.java index 19fb0d9..d2b4067 100644 --- a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/injection/TestInjectedStateManagedObjects.java +++ b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/injection/TestInjectedStateManagedObjects.java @@ -58,4 +58,8 @@ public void test_should_manage_state_of_nested_stateManagedObjects() throws Thro onView(withId(R.id.textA)).check(matches(withText("3:C"))); } + @Override + protected Class getActivityClass() { + return InjectionTestActivityStateManagedObjects.class; + } } diff --git a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/injection/TestInjectionAndLifeCycle.java b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/injection/TestInjectionAndLifeCycle.java index 0c1d4ba..2556bb1 100644 --- a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/injection/TestInjectionAndLifeCycle.java +++ b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/injection/TestInjectionAndLifeCycle.java @@ -38,6 +38,11 @@ public TestInjectionAndLifeCycle() { super(InjectionTestActivity.class); } + @Override + protected Class getActivityClass() { + return InjectionTestActivity.class; + } + @Override public void tearDown() throws Exception { super.tearDown(); diff --git a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/injection/TestInjectionAndLifeCycleForRootFragment.java b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/injection/TestInjectionAndLifeCycleForRootFragment.java index dfc9933..b748e19 100644 --- a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/injection/TestInjectionAndLifeCycleForRootFragment.java +++ b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/injection/TestInjectionAndLifeCycleForRootFragment.java @@ -35,6 +35,11 @@ public TestInjectionAndLifeCycleForRootFragment() { super(InjectionTestActivityTestRootFragment.class); } + @Override + protected Class getActivityClass() { + return InjectionTestActivityTestRootFragment.class; + } + @Override public void setUp() throws Exception { super.setUp(); diff --git a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/lifecycle/BaseTestCaseLifeCycle.java b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/lifecycle/BaseTestCaseLifeCycle.java index 7f0a6af..e1d3131 100644 --- a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/lifecycle/BaseTestCaseLifeCycle.java +++ b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/lifecycle/BaseTestCaseLifeCycle.java @@ -23,4 +23,9 @@ public abstract class BaseTestCaseLifeCycle extends BaseTestCase getActivityClass() { + return MvcTestActivity.class; + } } diff --git a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationAndInjection.java b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationAndInjection.java index adc1417..6445b0b 100644 --- a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationAndInjection.java +++ b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationAndInjection.java @@ -42,6 +42,11 @@ public TestCaseNavigationAndInjection() { abstract class CountMonitor implements Graph.Monitor { } + @Override + protected Class getActivityClass() { + return MvcTestActivityNavigation.class; + } + @Override protected void prepareDependencies(MvcComponent testComponent) throws Exception { super.prepareDependencies(testComponent); diff --git a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationBasic.java b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationBasic.java index 5bd0ddb..5820a04 100644 --- a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationBasic.java +++ b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationBasic.java @@ -61,6 +61,7 @@ public class TestCaseNavigationBasic extends BaseTestCase getActivityClass() { + return MvcTestActivityNavigation.class; + } + @Override public void setUp() throws Exception { super.setUp(); diff --git a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationFromController.java b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationFromController.java index c8dbc67..d55788b 100644 --- a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationFromController.java +++ b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationFromController.java @@ -113,6 +113,11 @@ public Object answer(InvocationOnMock invocation) throws Throwable { Mvc.graph().getRootComponent().register(comp); } + @Override + protected Class getActivityClass() { + return MvcTestActivityNavigation.class; + } + @Override public void setUp() throws Exception { super.setUp(); diff --git a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/viewpager/TestFragmentsInViewPager.java b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/viewpager/TestFragmentsInViewPager.java index 237a391..75f0d79 100644 --- a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/viewpager/TestFragmentsInViewPager.java +++ b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/viewpager/TestFragmentsInViewPager.java @@ -384,4 +384,9 @@ public void test_call_tab_controller_update_on_swipe() throws Throwable { onView(withId(R.id.viewpager)).perform(swipeLeft()); onView(withText("Tab C")).check(matches(isDisplayed())); } + + @Override + protected Class getActivityClass() { + return ViewPagerTestActivity.class; + } } diff --git a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/MvcActivity.java b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/MvcActivity.java index 60427b2..caf67d2 100644 --- a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/MvcActivity.java +++ b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/MvcActivity.java @@ -16,10 +16,8 @@ package com.shipdream.lib.android.mvc; -import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentHostCallback; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; @@ -30,7 +28,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -74,7 +71,7 @@ protected void onCreate(Bundle savedInstanceState) { } FragmentTransaction trans = getSupportFragmentManager().beginTransaction(); trans.replace(R.id.activity_mvc_root, delegateFragment, getDelegateFragmentTag()); - trans.commit(); + trans.commitNow(); } } @@ -207,114 +204,6 @@ public static abstract class DelegateFragmentbug - * with this solution - * FIXME: ChildFragmentManager hack - remove this method when the bug is fixed in future android support library - */ - private FragmentManager retainedChildFragmentManager; - private FragmentHostCallback currentHost; - private Class fragmentImplClass; - private Field mHostField; - - { - try { - fragmentImplClass = Class.forName("android.support.v4.app.FragmentManagerImpl"); - mHostField = fragmentImplClass.getDeclaredField("mHost"); - mHostField.setAccessible(true); - } catch (ClassNotFoundException e) { - throw new RuntimeException("FragmentManagerImpl is renamed due to the " + - "change of Android SDK, this workaround doesn't work any more. " + - "See the issue at " + - "https://code.google.com/p/android/issues/detail?id=74222", e); - } catch (NoSuchFieldException e) { - throw new RuntimeException("FragmentManagerImpl.mHost is found due to the " + - "change of Android SDK, this workaround doesn't work any more. " + - "See the issue at " + - "https://code.google.com/p/android/issues/detail?id=74222", e); - } - } - - /** - * Get child fragment manager with android support lib rev20/rev21 which has a the - * bug to retain child - * fragment manager in nested fragments. See this solution - * FIXME: ChildFragmentManager hack - remove this method when the bug is fixed in future android support library - */ - protected FragmentManager childFragmentManager() { - if (retainedChildFragmentManager == null) { - retainedChildFragmentManager = getChildFragmentManager(); - } - return retainedChildFragmentManager; - } - - /** - * Hack to fix this bug - * with this solution - * FIXME: ChildFragmentManager hack - remove this method when the bug is fixed in future android support library - */ - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (retainedChildFragmentManager != null) { - //restore the last retained child fragment manager to the new - //created fragment - try { - //Copy the mHost(Activity) to retainedChildFragmentManager - currentHost = (FragmentHostCallback) mHostField.get(getFragmentManager()); - - Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager"); - childFMField.setAccessible(true); - childFMField.set(this, retainedChildFragmentManager); - - refreshHosts(getFragmentManager()); - } catch (Exception e) { - logger.warn(e.getMessage(), e); - } - //Refresh children fragment's hosts - } else { - //If the child fragment manager has not been retained yet, let it hold the internal - //child fragment manager as early as possible. This can prevent child fragment - //manager from missing to be set and then retained, which could happen when - //OS kills activity and restarts it. In this case, the delegate fragment restored - //but childFragmentManager() may not be called so mRetainedChildFragmentManager is - //yet set. If the fragment is rotated, the state of child fragment manager will be - //lost since mRetainedChildFragmentManager hasn't set to be retained by the OS. - retainedChildFragmentManager = getChildFragmentManager(); - } - } - - private void refreshHosts(FragmentManager fragmentManager) throws IllegalAccessException { - if (fragmentManager != null) { - replaceFragmentManagerHost(fragmentManager); - } - - List frags = fragmentManager.getFragments(); - if (frags != null) { - for (Fragment f : frags) { - if (f != null) { - try { - //Copy the mHost(Activity) to retainedChildFragmentManager - Field mHostField = Fragment.class.getDeclaredField("mHost"); - mHostField.setAccessible(true); - mHostField.set(f, currentHost); - } catch (Exception e) { - logger.warn(e.getMessage(), e); - } - if (f.getChildFragmentManager() != null) { - refreshHosts(f.getChildFragmentManager()); - } - } - } - } - } - - private void replaceFragmentManagerHost(FragmentManager fragmentManager) throws IllegalAccessException { - if (currentHost != null) { - mHostField.set(fragmentManager, currentHost); - } - } - /** * Gets the id of activity layout resource. By default it's a single * {@link android.widget.FrameLayout} into which new fragment will be injected into during @@ -360,10 +249,10 @@ protected int getContentLayoutResId() { @Override public boolean onBackButtonPressed() { MvcFragment topFragment = null; - //FIXME: ChildFragmentManager hack - use getChildFragmentManager when bug is fixed + NavLocation curLoc = delegateFragmentController.getCurrentLocation(); if (curLoc != null && curLoc.getLocationId() != null) { - topFragment = (MvcFragment) childFragmentManager().findFragmentByTag( + topFragment = (MvcFragment) getChildFragmentManager().findFragmentByTag( getFragmentTag(curLoc.getLocationId())); } @@ -545,8 +434,7 @@ interface FragmentManipulator { @SuppressWarnings("unchecked") private void performForwardNav(final NavigationManager.Event.OnLocationForward event) { - //FIXME: ChildFragmentManager hack - use getChildFragmentManager when bug is fixed - FragmentManager fm = childFragmentManager(); + FragmentManager fm = getChildFragmentManager(); MvcActivity activity = ((MvcActivity) getActivity()); @@ -657,8 +545,7 @@ public void run() { } private void performBackNav(final NavigationManager.Event.OnLocationBack event) { - //FIXME: ChildFragmentManager hack - use getChildFragmentManager when bug is fixed - FragmentManager fm = childFragmentManager(); + FragmentManager fm = getChildFragmentManager(); NavLocation lastLoc = event.getLastValue(); if (lastLoc != null) { From dddae2bebf1a4f8d0e1e5da561f527e9a4bd2a6c Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Wed, 13 Jul 2016 12:06:19 +1000 Subject: [PATCH 05/14] Try fixing build issue on travis --- library/android-mvc/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/android-mvc/build.gradle b/library/android-mvc/build.gradle index 1672761..8a72e36 100644 --- a/library/android-mvc/build.gradle +++ b/library/android-mvc/build.gradle @@ -28,8 +28,8 @@ dependencies { android { compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 + sourceCompatibility 1.7 + targetCompatibility 1.7 } compileSdkVersion rootProject.ext.androidCompileSdkVersion From e352c068c8c0b80f8f7ad9b0d79cdcc668881514 Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Wed, 13 Jul 2016 12:11:46 +1000 Subject: [PATCH 06/14] Downgrade gradle build tool to make the project build on travis --- build.gradle | 2 +- library/android-mvc/build.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 98c9faf..8c5664f 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.0-alpha5' + classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' diff --git a/library/android-mvc/build.gradle b/library/android-mvc/build.gradle index 8a72e36..1672761 100644 --- a/library/android-mvc/build.gradle +++ b/library/android-mvc/build.gradle @@ -28,8 +28,8 @@ dependencies { android { compileOptions { - sourceCompatibility 1.7 - targetCompatibility 1.7 + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 } compileSdkVersion rootProject.ext.androidCompileSdkVersion From c1263b9bcfb3cf684bd3e4233b38424a5f04868b Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Wed, 13 Jul 2016 13:05:34 +1000 Subject: [PATCH 07/14] More unit tests for controller.runTask --- .../shipdream/lib/android/mvc/Controller.java | 25 ++-- .../com/shipdream/lib/android/mvc/Task.java | 37 +++--- .../lib/android/mvc/TestTestUtil.java | 23 ++++ .../android/mvc/controller/MyController.java | 11 +- .../mvc/controller/TestRunAsyncTask.java | 117 +++++++++++++++++- 5 files changed, 173 insertions(+), 40 deletions(-) create mode 100644 library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestTestUtil.java diff --git a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Controller.java b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Controller.java index bd55b03..a864a21 100644 --- a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Controller.java +++ b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Controller.java @@ -165,19 +165,24 @@ protected Task.Monitor runTask(ExecutorService executorService, final Task task, final Task.Callback callback) { final Task.Monitor monitor = new Task.Monitor(task, uiThreadRunner, callback); - if (monitor.getState() == Task.Monitor.State.CANCELED) { - return null; - } - - monitor.setState(Task.Monitor.State.STARTED); - - if (callback != null) { - callback.onStarted(); - } - Future future = executorService.submit(new Callable() { @Override public Void call() throws Exception { + if (monitor.getState() == Task.Monitor.State.CANCELED) { + return null; + } + + monitor.setState(Task.Monitor.State.STARTED); + + if (callback != null) { + uiThreadRunner.post(new Runnable() { + @Override + public void run() { + callback.onStarted(); + } + }); + } + try { final RESULT result = task.execute(monitor); diff --git a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Task.java b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Task.java index 0c88456..d3eace1 100644 --- a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Task.java +++ b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Task.java @@ -93,14 +93,6 @@ public synchronized State getState() { return state; } - /** - * The task this monitor is monitoring - * @return The task - */ - public Task getTask() { - return task; - } - /** * Attempts to cancel execution of this task. This attempt will * fail if the task has already completed, has already been cancelled, @@ -133,26 +125,25 @@ public synchronized boolean cancel(final boolean mayInterruptIfRunning) { } return true; case STARTED: - if (future != null) { - boolean cancelled = future.cancel(mayInterruptIfRunning); - if (mayInterruptIfRunning && cancelled) { + boolean cancelled = future.cancel(mayInterruptIfRunning); + if (cancelled) { + if (mayInterruptIfRunning) { state = State.INTERRUPTED; } else { state = State.CANCELED; } - if (callback != null) { - uiThreadRunner.post(new Runnable() { - @Override - public void run() { - callback.onCancelled(mayInterruptIfRunning); - callback.onFinally(); - } - }); - } - return cancelled; - } else { - return false; } + + if (callback != null) { + uiThreadRunner.post(new Runnable() { + @Override + public void run() { + callback.onCancelled(mayInterruptIfRunning); + callback.onFinally(); + } + }); + } + return cancelled; case DONE: case ERRED: case CANCELED: diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestTestUtil.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestTestUtil.java new file mode 100644 index 0000000..4fc6942 --- /dev/null +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestTestUtil.java @@ -0,0 +1,23 @@ +package com.shipdream.lib.android.mvc; + +import org.junit.Assert; +import org.junit.Test; + +import static org.mockito.Mockito.mock; + +public class TestTestUtil { + @Test + public void should_assign_controller_view_correctly() { + Controller controller = new Controller() { + @Override + public Class modelType() { + return null; + } + }; + UiView view = mock(UiView.class); + + TestUtil.assignControllerView(controller, view); + + Assert.assertTrue(controller.view == view); + } +} diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/MyController.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/MyController.java index b6c3153..6fa8c54 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/MyController.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/MyController.java @@ -129,15 +129,12 @@ public void onException(Exception e) { }); } - public Task.Monitor loadHeavyResourceAndCancel(final Object sender) { Task asyncTask = new Task() { @Override public Void execute(Monitor monitor) throws Exception { Thread.sleep(LONG_TASK_DURATION); - if (monitor.getState() == Monitor.State.CANCELED) { - view.onResourceCancelled(); - } else { + if (monitor.getState() != Monitor.State.CANCELED) { view.onResourceLoaded(); } return null; @@ -153,8 +150,14 @@ public void onException(Exception e) { @Override public void onCancelled(boolean interrupted) { + super.onCancelled(interrupted); view.onResourceCancelled(); } }); } + + public Task.Monitor loadHeavyResource(final Object sender, Task task, Task.Callback callback) { + return runTask(task, + callback); + } } diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java index 322b844..a325d3f 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java @@ -23,12 +23,12 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.slf4j.Logger; import java.util.concurrent.Executors; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -106,8 +106,9 @@ public void shouldHandleAsyncTaskExceptionAndDetectFailEvent() throws Exception } @Test - public void shouldBeAbleToCancelAsyncActionAndDetectCancelEvent() throws Exception { + public void shouldBeAbleToCancelAsyncActionAndDetectInterruptedEvent() throws Exception { Task.Monitor monitor = controller.loadHeavyResourceAndCancel(this); + Thread.sleep(10); monitor.cancel(true); Thread.sleep(WAIT_DURATION); @@ -120,4 +121,114 @@ public void shouldBeAbleToCancelAsyncActionAndDetectCancelEvent() throws Excepti Assert.assertEquals(monitor.getState(), Task.Monitor.State.INTERRUPTED); } + + @Test + public void shouldBeAbleToCancelAsyncActionAndDetectCancelEvent() throws Exception { + Task.Monitor monitor = controller.loadHeavyResourceAndCancel(this); + Thread.sleep(10); + monitor.cancel(false); + + Thread.sleep(WAIT_DURATION); + + verify(view, times(0)).onResourceLoaded(); + + verify(view, times(0)).onResourceFailed(any(MvcGraph.Exception.class)); + + verify(view, times(1)).onResourceCancelled(); + + Assert.assertEquals(monitor.getState(), Task.Monitor.State.CANCELED); + } + + @Test + public void should_catch_exception_during_running_async_task() throws InterruptedException { + final Task.Callback callback = mock(Task.Callback.class); + Task.Monitor monitor1 = controller.loadHeavyResource(this, new Task() { + @Override + public Object execute(Monitor monitor) throws Exception { + Thread.sleep(50); + throw new RuntimeException(); + } + }, new Task.Callback() { + @Override + public void onStarted() { + super.onStarted(); + callback.onStarted(); + } + + @Override + public void onSuccess(Object o) { + super.onSuccess(o); + callback.onSuccess(o); + } + + @Override + public void onCancelled(boolean interrupted) { + super.onCancelled(interrupted); + callback.onCancelled(interrupted); + } + + @Override + public void onException(Exception e) { + super.onException(e); + callback.onException(e); + } + + @Override + public void onFinally() { + super.onFinally(); + callback.onFinally(); + } + }); + + Thread.sleep(100); + + verify(callback, times(1)).onStarted(); + verify(callback, times(1)).onException(any(Exception.class)); + verify(callback, times(0)).onSuccess(anyObject()); + verify(callback, times(0)).onCancelled(anyBoolean()); + verify(callback, times(1)).onFinally(); + } + + @Test + public void should_be_able_to_cancel_a_task_before_it_starts() throws Exception { + Task.Callback callback = mock(Task.Callback.class); + Task.Monitor monitor1 = controller.loadHeavyResource(this, new Task() { + @Override + public Object execute(Monitor monitor) throws Exception { + Thread.sleep(WAIT_DURATION); + return null; + } + }, callback); + + Task.Callback callback2 = mock(Task.Callback.class); + Task.Monitor monitor2 = controller.loadHeavyResource(this, new Task() { + @Override + public Object execute(Monitor monitor) throws Exception { + return null; + } + }, callback2); + monitor2.cancel(true); + + Thread.sleep(WAIT_DURATION + 100); + + verify(callback, times(1)).onStarted(); + verify(callback, times(0)).onException(any(Exception.class)); + verify(callback, times(1)).onSuccess(anyObject()); + verify(callback, times(0)).onCancelled(anyBoolean()); + verify(callback, times(1)).onFinally(); + + verify(callback2, times(0)).onStarted(); + verify(callback2, times(0)).onException(any(Exception.class)); + verify(callback2, times(0)).onSuccess(anyObject()); + verify(callback2, times(1)).onCancelled(anyBoolean()); + verify(callback2, times(1)).onFinally(); + + Assert.assertEquals(monitor1.getState(), Task.Monitor.State.DONE); + Assert.assertEquals(monitor2.getState(), Task.Monitor.State.CANCELED); + + Assert.assertFalse(monitor1.cancel(true)); + Assert.assertFalse(monitor1.cancel(false)); + Assert.assertFalse(monitor2.cancel(true)); + Assert.assertFalse(monitor2.cancel(false)); + } } From d6b000fb74b31ed092f05b556bec29979a76b4ea Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Wed, 13 Jul 2016 13:13:25 +1000 Subject: [PATCH 08/14] Add some tests for static class methods and variables --- .../lib/android/mvc/TestOrientation.java | 12 +++++++ .../shipdream/lib/android/mvc/TestReason.java | 36 +++++++++++++++++++ .../lib/android/mvc/TestTestUtil.java | 5 +++ 3 files changed, 53 insertions(+) create mode 100644 library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestOrientation.java create mode 100644 library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestReason.java diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestOrientation.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestOrientation.java new file mode 100644 index 0000000..424714b --- /dev/null +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestOrientation.java @@ -0,0 +1,12 @@ +package com.shipdream.lib.android.mvc; + +import org.junit.Test; + +public class TestOrientation { + @Test + public void able_to_write_read_orientation() { + Orientation orientation1 = Orientation.UNSPECIFIED; + Orientation orientation2 = Orientation.LANDSCAPE; + Orientation orientation3 = Orientation.PORTRAIT; + } +} diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestReason.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestReason.java new file mode 100644 index 0000000..1f3bc35 --- /dev/null +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestReason.java @@ -0,0 +1,36 @@ +package com.shipdream.lib.android.mvc; + +import org.junit.Assert; +import org.junit.Test; + +public class TestReason { + @Test + public void reason_should_be_set_and_read_correctly() { + Reason reason = new Reason(); + reason.isNewInstance = true; + reason.isFirstTime = true; + reason.isRestored = true; + reason.isRotated = true; + reason.isPoppedOut = true; + + Assert.assertTrue(reason.isNewInstance()); + Assert.assertTrue(reason.isFirstTime()); + Assert.assertTrue(reason.isRestored()); + Assert.assertTrue(reason.isRotated()); + Assert.assertTrue(reason.isPoppedOut()); + + reason.toString(); + + reason.isNewInstance = false; + reason.isFirstTime = false; + reason.isRestored = false; + reason.isRotated = false; + reason.isPoppedOut = false; + + Assert.assertFalse(reason.isNewInstance()); + Assert.assertFalse(reason.isFirstTime()); + Assert.assertFalse(reason.isRestored()); + Assert.assertFalse(reason.isRotated()); + Assert.assertFalse(reason.isPoppedOut()); + } +} diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestTestUtil.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestTestUtil.java index 4fc6942..5cd0f47 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestTestUtil.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestTestUtil.java @@ -6,6 +6,11 @@ import static org.mockito.Mockito.mock; public class TestTestUtil { + @Test + public void can_construct_testUtil() { + new TestUtil(); + } + @Test public void should_assign_controller_view_correctly() { Controller controller = new Controller() { From 7228b3752e021157ac0957d809790ab2a6162df7 Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Wed, 13 Jul 2016 22:07:10 +1000 Subject: [PATCH 09/14] More coverage for core library --- .../shipdream/lib/android/mvc/Controller.java | 4 +- .../lib/android/mvc/MvcComponent.java | 20 ++- .../shipdream/lib/android/mvc/MvcGraph.java | 52 +++--- .../lib/android/mvc/MvcGraphException.java | 11 ++ .../shipdream/lib/android/mvc/Navigator.java | 50 +++--- .../shipdream/lib/android/mvc/BaseTest.java | 9 + .../lib/android/mvc/TestController.java | 128 ++++++++++++++ .../android/mvc/TestFragmentController.java | 35 ++++ .../lib/android/mvc/TestMvcBean.java | 50 +++++- .../lib/android/mvc/TestMvcComponent.java | 32 ++++ .../lib/android/mvc/TestMvcGraph.java | 158 ++++++++++++++---- .../android/mvc/TestNavigationManager.java | 86 +++++++++- .../mvc/controller/TestRunAsyncTask.java | 10 +- .../inject/TestControllerSimpleInject.java | 14 +- .../lib/android/mvc/inject/test/Phone.java | 4 + .../lib/android/mvc/inject/test/Robot.java | 4 + .../lib/android/mvc/inject/test/Smart.java | 12 ++ .../mvc/inject/test/internal/PhoneImpl.java | 7 + .../mvc/inject/test/internal/RobotImpl.java | 4 + .../controller/LifeCycleTestController.java | 8 +- .../internal/BaseNavigationManagerTest.java | 6 + 21 files changed, 588 insertions(+), 116 deletions(-) create mode 100644 library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/MvcGraphException.java create mode 100644 library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestController.java create mode 100644 library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestFragmentController.java create mode 100644 library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcComponent.java create mode 100644 library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/Phone.java create mode 100644 library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/Robot.java create mode 100644 library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/Smart.java create mode 100644 library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/internal/PhoneImpl.java create mode 100644 library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/internal/RobotImpl.java diff --git a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Controller.java b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Controller.java index a864a21..5023bd0 100644 --- a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Controller.java +++ b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Controller.java @@ -200,13 +200,13 @@ public void run() { } } } catch (final Exception e) { - if (e instanceof MvcGraph.Exception) { + if (e instanceof MvcGraphException) { //Injection exception will always be thrown out since it's a development //time error uiThreadRunner.post(new Runnable() { @Override public void run() { - throw new IllegalStateException(e); + throw new RuntimeException(e); } }); } diff --git a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/MvcComponent.java b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/MvcComponent.java index 40da905..fbead97 100644 --- a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/MvcComponent.java +++ b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/MvcComponent.java @@ -13,7 +13,17 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; -//TODO: documents +/** + * A component manages injectable objects. It is able to locate implementation class automatically by + *
    + * The injecting class is a concrete class and has empty constructor + * The injecting class is an interface or abstract class and there is concrete class is named + * with suffix "impl" and sitting in the subpackage "internal" which is at the + * same level of the interface or abstract. For instance, the interface is + * a.b.c.Car and there is an concrete class at a.b.c.internal.CarImpl + * The injecting class is registered by {@link #register(Object)} or {@link #register(Provider)} + *
+ */ public class MvcComponent extends Component { private Logger logger = LoggerFactory.getLogger(getClass()); public MvcComponent(String name) { @@ -51,11 +61,11 @@ public Provider findProvider(final Class type, Annotation qualifier) t || provider.getQualifier() != null) { String msg; if (qualifier == null) { - msg = String.format("Can't find implementation class for %s. Make sure class %s exists, or its implementation is registered to graph's root component", - type.getName(), getClassName(type)); + msg = String.format("Can't find implementation class for %s. Make sure class %s without qualifier %s exists, or its implementation is registered to graph's root component.", + type.getName(), getClassName(type), provider.getQualifier().toString()); } else { - msg = String.format("Can't find implementation class for %s. Make sure class %s exists, or its implementation is registered to graph's root component", - type.getName(), getClassName(type) + "@" + qualifier.toString()); + msg = String.format("Can't find implementation class for %s. Make sure class %s with qualifier %s exists, or its implementation is registered to graph's root component.", + type.getName(), getClassName(type), qualifier.toString()); } throw new ProviderMissingException(msg); } diff --git a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/MvcGraph.java b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/MvcGraph.java index d8df9e0..802b20d 100644 --- a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/MvcGraph.java +++ b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/MvcGraph.java @@ -21,16 +21,6 @@ public class MvcGraph { final Logger logger = LoggerFactory.getLogger(getClass()); - public static class Exception extends RuntimeException { - public Exception(String s) { - super(s); - } - - public Exception(String message, Throwable cause) { - super(message, cause); - } - } - UiThreadRunner uiThreadRunner; Graph graph; @@ -89,6 +79,9 @@ public void onDisposed(Provider provider, T instance) { * @param monitor The monitor */ public void registerMonitor(Graph.Monitor monitor) { + if (!uiThreadRunner.isOnUiThread()) { + throw new MvcGraphException("Cannot register mvc graph monitor from Non-UiThread"); + } graph.registerMonitor(monitor); } @@ -98,6 +91,9 @@ public void registerMonitor(Graph.Monitor monitor) { * @param monitor The monitor */ public void unregisterMonitor(Graph.Monitor monitor) { + if (!uiThreadRunner.isOnUiThread()) { + throw new MvcGraphException("Cannot unregister mvc graph monitor from Non-UiThread"); + } graph.unregisterMonitor(monitor); } @@ -105,6 +101,9 @@ public void unregisterMonitor(Graph.Monitor monitor) { * Clear {@link Graph.Monitor} which will be called the graph is about to inject or release an object */ public void clearMonitors() { + if (!uiThreadRunner.isOnUiThread()) { + throw new MvcGraphException("Cannot clear mvc graph monitors from Non-UiThread"); + } graph.clearMonitors(); } @@ -119,7 +118,7 @@ public void clearMonitors() { public T reference(Class type, Annotation qualifier) throws ProviderMissingException, ProvideException, CircularDependenciesException { if (!uiThreadRunner.isOnUiThread()) { - throw new IllegalStateException("Cannot reference an instance from Non-UiThread"); + throw new MvcGraphException("Cannot reference an instance from Non-UiThread"); } return graph.reference(type, qualifier, Inject.class); } @@ -134,7 +133,7 @@ public T reference(Class type, Annotation qualifier) public void dereference(T instance, Class type, Annotation qualifier) throws ProviderMissingException { if (!uiThreadRunner.isOnUiThread()) { - throw new IllegalStateException("Cannot dereference an instance from Non-UiThread"); + throw new MvcGraphException("Cannot dereference an instance from Non-UiThread"); } graph.dereference(instance, type, qualifier, Inject.class); } @@ -146,12 +145,12 @@ public void dereference(T instance, Class type, Annotation qualifier) */ public void use(final Class type, final Consumer consumer) { if (!uiThreadRunner.isOnUiThread()) { - throw new Exception("Cannot use an instance from Non-UiThread"); + throw new MvcGraphException("Cannot use an instance from Non-UiThread"); } try { graph.use(type, Inject.class, consumer); } catch (PokeException e) { - throw new Exception(e.getMessage(), e); + throw new MvcGraphException(e.getMessage(), e); } } @@ -264,16 +263,16 @@ public void consume(Os instance) { * @param type The type of the injectable instance * @param qualifier Qualifier for the injectable instance * @param consumer Consume to use the instance - * @throws Exception throw when there are exceptions during the consumption of the instance + * @throws MvcGraphException throw when there are exceptions during the consumption of the instance */ public void use(final Class type, final Annotation qualifier, final Consumer consumer) { if (!uiThreadRunner.isOnUiThread()) { - throw new Exception("Cannot use an instance from Non-UiThread"); + throw new MvcGraphException("Cannot use an instance from Non-UiThread"); } try { graph.use(type, qualifier, Inject.class, consumer); } catch (PokeException e) { - throw new Exception(e.getMessage(), e); + throw new MvcGraphException(e.getMessage(), e); } } @@ -285,12 +284,12 @@ public void use(final Class type, final Annotation qualifier, final Consu */ public void inject(Object target) { if (!uiThreadRunner.isOnUiThread()) { - throw new Exception("Cannot inject an instance from Non-UiThread"); + throw new MvcGraphException("Cannot inject an instance from Non-UiThread"); } try { graph.inject(target, Inject.class); } catch (PokeException e) { - throw new Exception(e.getMessage(), e); + throw new MvcGraphException(e.getMessage(), e); } } @@ -303,12 +302,12 @@ public void inject(Object target) { */ public void release(Object target) { if (!uiThreadRunner.isOnUiThread()) { - throw new Exception("Cannot release an instance from Non-UiThread"); + throw new MvcGraphException("Cannot release an instance from Non-UiThread"); } try { graph.release(target, Inject.class); } catch (ProviderMissingException e) { - throw new Exception(e.getMessage(), e); + throw new MvcGraphException(e.getMessage(), e); } } @@ -319,12 +318,15 @@ public void release(Object target) { */ public void setRootComponent(MvcComponent component) throws Graph.IllegalRootComponentException { if (!uiThreadRunner.isOnUiThread()) { - throw new Exception("Cannot set root component from Non-UiThread"); + throw new MvcGraphException("Cannot set root component from Non-UiThread"); } graph.setRootComponent(component); } public MvcComponent getRootComponent() { + if (!uiThreadRunner.isOnUiThread()) { + throw new MvcGraphException("Cannot getRootComponent() from Non-UiThread"); + } return (MvcComponent) graph.getRootComponent(); } @@ -335,7 +337,7 @@ public MvcComponent getRootComponent() { */ public void registerDereferencedListener(Provider.DereferenceListener onProviderFreedListener) { if (!uiThreadRunner.isOnUiThread()) { - throw new Exception("Cannot register dereference listener from Non-UiThread"); + throw new MvcGraphException("Cannot register dereference listener from Non-UiThread"); } graph.registerDereferencedListener(onProviderFreedListener); } @@ -348,7 +350,7 @@ public void registerDereferencedListener(Provider.DereferenceListener onProvider */ public void unregisterDereferencedListener(Provider.DereferenceListener onProviderFreedListener) { if (!uiThreadRunner.isOnUiThread()) { - throw new Exception("Cannot unregister dereference listener from Non-UiThread"); + throw new MvcGraphException("Cannot unregister dereference listener from Non-UiThread"); } graph.unregisterDereferencedListener(onProviderFreedListener); } @@ -359,7 +361,7 @@ public void unregisterDereferencedListener(Provider.DereferenceListener onProvid */ public void clearDereferencedListeners() { if (!uiThreadRunner.isOnUiThread()) { - throw new Exception("Cannot clear dereference listeners from Non-UiThread"); + throw new MvcGraphException("Cannot clear dereference listeners from Non-UiThread"); } graph.clearDereferencedListeners(); } diff --git a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/MvcGraphException.java b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/MvcGraphException.java new file mode 100644 index 0000000..7ed0c32 --- /dev/null +++ b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/MvcGraphException.java @@ -0,0 +1,11 @@ +package com.shipdream.lib.android.mvc; + +public class MvcGraphException extends RuntimeException { + public MvcGraphException(String s) { + super(s); + } + + public MvcGraphException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Navigator.java b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Navigator.java index a741331..3b02e1a 100644 --- a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Navigator.java +++ b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Navigator.java @@ -74,13 +74,13 @@ public Object getSender() { /** * Prepare the instance subject to being injected with no qualifier for the fragment being * navigated to. This instance will be not be released until the navigation is settled. To - * config the instance try {@link #with(Class, com.shipdream.lib.android.mvc.Preparer)} or {@link #with(Class, Annotation, com.shipdream.lib.android.mvc.Preparer)} + * config the instance try {@link #with(Class, Preparer)} or {@link #with(Class, Annotation, Preparer)} * * @param type The class type of the instance needs to be prepared * @return This navigator - * @throws MvcGraph.Exception Raised when the required injectable object cannot be injected + * @throws MvcGraphException Raised when the required injectable object cannot be injected */ - public Navigator with(Class type) throws MvcGraph.Exception { + public Navigator with(Class type) throws MvcGraphException { with(type, null, null); return this; } @@ -121,9 +121,9 @@ public Navigator with(Class type) throws MvcGraph.Exception { * @param type The class type of the instance needs to be prepared * @param preparer The preparer in which the injected instance will be prepared * @return This navigator - * @throws MvcGraph.Exception Raised when the required injectable object cannot be injected + * @throws MvcGraphException Raised when the required injectable object cannot be injected */ - public Navigator with(Class type, com.shipdream.lib.android.mvc.Preparer preparer) throws MvcGraph.Exception { + public Navigator with(Class type, Preparer preparer) throws MvcGraphException { with(type, null, preparer); return this; } @@ -164,27 +164,29 @@ public Navigator with(Class type, com.shipdream.lib.android.mvc.Preparer< * @param qualifier The qualifier * @param preparer The preparer in which the injected instance will be prepared * @return This navigator - * @throws MvcGraph.Exception Raised when the required injectable object cannot be injected + * @throws MvcGraphException Raised when the required injectable object cannot be injected */ - public Navigator with(Class type, Annotation qualifier, com.shipdream.lib.android.mvc.Preparer preparer) throws MvcGraph.Exception { + public Navigator with(Class type, Annotation qualifier, Preparer preparer) throws MvcGraphException { + T instance; try { - T instance = Mvc.graph().reference(type, qualifier); + instance = Mvc.graph().reference(type, qualifier); + } catch (PokeException e) { + throw new MvcGraphException(e.getMessage(), e); + } - if (preparer != null) { - preparer.prepare(instance); - } + if (preparer != null) { + preparer.prepare(instance); + } - if (pendingReleaseInstances == null) { - pendingReleaseInstances = new ArrayList<>(); - } - PendingReleaseInstance pendingReleaseInstance = new PendingReleaseInstance(); - pendingReleaseInstance.instance = instance; - pendingReleaseInstance.type = type; - pendingReleaseInstance.qualifier = qualifier; - pendingReleaseInstances.add(pendingReleaseInstance); - } catch (PokeException e) { - throw new MvcGraph.Exception(e.getMessage(), e); + if (pendingReleaseInstances == null) { + pendingReleaseInstances = new ArrayList<>(); } + PendingReleaseInstance pendingReleaseInstance = new PendingReleaseInstance(); + pendingReleaseInstance.instance = instance; + pendingReleaseInstance.type = type; + pendingReleaseInstance.qualifier = qualifier; + pendingReleaseInstances.add(pendingReleaseInstance); + return this; } @@ -193,7 +195,7 @@ public Navigator with(Class type, Annotation qualifier, com.shipdream.lib * given locationId is different from the current location and raises {@link NavigationManager.Event.OnLocationForward} *

*

- * To set argument for the next location navigating to, use {@link #with(Class, Annotation, com.shipdream.lib.android.mvc.Preparer)} + * To set argument for the next location navigating to, use {@link #with(Class, Annotation, Preparer)} * to prepare the controller injecting into the next fragment. *

* @@ -209,12 +211,12 @@ public void to(@NotNull Class controllerClass) { * given locationId is different from the current location and raises {@link NavigationManager.Event.OnLocationForward} *

*

- * To set argument for the next location navigating to, use {@link #with(Class, Annotation, com.shipdream.lib.android.mvc.Preparer)} + * To set argument for the next location navigating to, use {@link #with(Class, Annotation, Preparer)} * to prepare the controller injecting into the next fragment. *

* * @param controllerClass The controller class type. - * @param forwarder The configuration by {@link Forwarder} of the forward navigation. + * @param forwarder The configuration by {@link Forwarder} of the forward navigation. */ public void to(@NotNull Class controllerClass, @NotNull Forwarder forwarder) { diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/BaseTest.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/BaseTest.java index b9ed9a6..dbee423 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/BaseTest.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/BaseTest.java @@ -31,6 +31,7 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import static org.mockito.Matchers.any; @@ -72,6 +73,14 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } }).when(executorService).submit(any(Runnable.class)); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + Callable runnable = (Callable) invocation.getArguments()[0]; + return runnable.call(); + } + }).when(executorService).submit(any(Callable.class)); + graph.setRootComponent((MvcComponent) new MvcComponent("TestRootComponent").register(new Object(){ @Provides @EventBusC diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestController.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestController.java new file mode 100644 index 0000000..5d0e639 --- /dev/null +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestController.java @@ -0,0 +1,128 @@ +package com.shipdream.lib.android.mvc; + +import com.shipdream.lib.android.mvc.event.bus.EventBus; +import com.shipdream.lib.android.mvc.event.bus.annotation.EventBusV; +import com.shipdream.lib.poke.Provides; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class TestController extends BaseTest{ + private UiThreadRunner uiThreadRunner; + private EventBus eventBusV; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + uiThreadRunner = mock(UiThreadRunner.class); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + Runnable runnable = (Runnable) invocation.getArguments()[0]; + runnable.run(); + return null; + } + }).when(uiThreadRunner).post(any(Runnable.class)); + + eventBusV = mock(EventBus.class); + + MvcComponent component = new MvcComponent(""); + component.register(new Object(){ + @Provides + UiThreadRunner uiThreadRunner() { + return uiThreadRunner; + } + + @Provides + @EventBusV + EventBus eventBus() { + return eventBusV; + } + }); + graph.getRootComponent().attach(component, true); + } + + @Test + public void should_post_event_to_event2v_channel() throws Exception { + Controller controller = new Controller() { + @Override + public Class modelType() { + return null; + } + }; + graph.inject(controller); + + String event = ";"; + controller.postEvent(event); + + verify(uiThreadRunner).post(any(Runnable.class)); + verify(eventBusV).post(eq(event)); + } + + @Test + public void should_post_wrapped_MvcGraphException_when_run_async_task() { + Controller controller = new Controller() { + @Override + public Class modelType() { + return null; + } + }; + + graph.inject(controller); + + boolean exceptionCaught = false; + final MvcGraphException exp = new MvcGraphException(""); + try { + controller.runTask(new Task() { + @Override + public Object execute(Monitor monitor) throws Exception { + throw exp; + } + }); + } catch (Exception e) { + exceptionCaught = true; + Assert.assertTrue(e.getCause() == exp); + } + + Assert.assertTrue(exceptionCaught); + } + + @Test + public void should_post_wrapped_other_exception_when_run_async_task_without_callback() { + Controller controller = new Controller() { + @Override + public Class modelType() { + return null; + } + }; + + graph.inject(controller); + + boolean exceptionCaught = false; + final Exception exp = new Exception(""); + try { + controller.runTask(new Task() { + @Override + public Object execute(Monitor monitor) throws Exception { + throw exp; + } + }); + } catch (Exception e) { + exceptionCaught = true; + Assert.assertTrue(e.getCause() == exp); + } + + Assert.assertTrue(exceptionCaught); + } +} + diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestFragmentController.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestFragmentController.java new file mode 100644 index 0000000..4602385 --- /dev/null +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestFragmentController.java @@ -0,0 +1,35 @@ +package com.shipdream.lib.android.mvc; + +import org.junit.Assert; +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class TestFragmentController extends BaseTest { + @Test + public void should_run_all_life_cycle_calls_without_exception() { + FragmentController controller = new FragmentController() { + @Override + public Class modelType() { + return null; + } + }; + + Assert.assertFalse(controller.onBackButtonPressed()); + controller.currentOrientation(); + controller.onOrientationChanged(Orientation.LANDSCAPE, Orientation.PORTRAIT); + controller.onResume(); + controller.onPause(); + controller.onPopAway(); + controller.onPoppedOutToFront(); + controller.onPushToBackStack(); + controller.onReturnForeground(); + + UiView view = mock(UiView.class); + controller.view = view; + controller.onViewReady(new Reason()); + + verify(view).update(); + } +} diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcBean.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcBean.java index d93f9d2..1d176e1 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcBean.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcBean.java @@ -19,10 +19,14 @@ import org.junit.Assert; import org.junit.Test; +import java.util.concurrent.ExecutorService; + +import javax.inject.Inject; + public class TestMvcBean { @Test(expected = IllegalArgumentException.class) public void should_throw_exception_when_bind_null_to_a_mvcBean() { - com.shipdream.lib.android.mvc.Bean bean = new com.shipdream.lib.android.mvc.Bean() { + Bean bean = new Bean() { @Override public Class modelType() { return String.class; @@ -32,9 +36,37 @@ public Class modelType() { bean.bindModel(null); } + @Test + public void should_be_able_to_inject_background_executor_service() throws InterruptedException { + class Worker { + Thread thread = Thread.currentThread(); + @Inject + ExecutorService executorService; + } + final Worker worker = new Worker(); + Mvc.graph().inject(worker); + + + worker.executorService.submit(new Runnable() { + @Override + public void run() { + synchronized (worker) { + worker.thread = Thread.currentThread(); + worker.notify(); + } + } + }); + + synchronized (worker) { + worker.wait(); + } + + Assert.assertTrue(worker.thread != Thread.currentThread()); + } + @Test public void should_rebind_state_after_restoring_mvcBean() { - com.shipdream.lib.android.mvc.Bean bean = new com.shipdream.lib.android.mvc.Bean() { + Bean bean = new Bean() { @Override public Class modelType() { @@ -51,7 +83,7 @@ public Class modelType() { @Test public void should_call_on_restore_call_back_after_a_stateful_mvcBean_is_restored() { - class MyBean extends com.shipdream.lib.android.mvc.Bean { + class MyBean extends Bean { private boolean called = false; @Override @@ -77,7 +109,7 @@ public void onRestored() { @Test public void should_not_call_on_restore_call_back_after_a_non_stateful_mvcBean_is_restored() { - class MyBean extends com.shipdream.lib.android.mvc.Bean { + class MyBean extends Bean { private boolean called = false; @Override @@ -102,7 +134,7 @@ public void onRestored() { } public void should_create_state_instance_on_construct_when_the_state_type_is_specified_for_a_mvcBean() { - class MyBean extends com.shipdream.lib.android.mvc.Bean { + class MyBean extends Bean { @Override public Class modelType() { return String.class; @@ -118,7 +150,7 @@ public Class modelType() { } public void should_NOT_create_state_instance_on_construct_when_the_state_type_is_null_for_a_mvcBean() { - class MyBean extends com.shipdream.lib.android.mvc.Bean { + class MyBean extends Bean { @Override public Class modelType() { return null; @@ -139,7 +171,7 @@ class BadClass { {int x = 1 / 0;} } - class MyBean extends com.shipdream.lib.android.mvc.Bean { + class MyBean extends Bean { @Override public Class modelType() { return BadClass.class; @@ -153,7 +185,7 @@ public Class modelType() { @Test(expected = IllegalArgumentException.class) public void should_throw_exception_when_binding_null_to_stateful_mvcBean() { - class MyBean extends com.shipdream.lib.android.mvc.Bean { + class MyBean extends Bean { @Override public Class modelType() { return String.class; @@ -167,7 +199,7 @@ public Class modelType() { @Test public void should_be_able_to_successfully_bind_state_to_stateful_mvcBean() { - class MyBean extends com.shipdream.lib.android.mvc.Bean { + class MyBean extends Bean { @Override public Class modelType() { return String.class; diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcComponent.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcComponent.java new file mode 100644 index 0000000..5128a64 --- /dev/null +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcComponent.java @@ -0,0 +1,32 @@ +package com.shipdream.lib.android.mvc; + +import com.shipdream.lib.android.mvc.inject.test.Phone; +import com.shipdream.lib.android.mvc.inject.test.Robot; +import com.shipdream.lib.android.mvc.inject.test.Smart; + +import org.junit.Test; + +import javax.inject.Inject; + +public class TestMvcComponent { + @Test(expected = MvcGraphException.class) + public void should_throw_provider_missing_exception_when_locate_an_qualified_class() { + class Shop { + @Inject + private Phone nexus6; + } + + Mvc.graph().inject(new Shop()); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_provider_missing_exception_when_locate_an_unqualified_class() { + class Shop { + @Inject + @Smart + private Robot nexus6; + } + + Mvc.graph().inject(new Shop()); + } +} diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcGraph.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcGraph.java index 998fc28..ce42cf6 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcGraph.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcGraph.java @@ -20,8 +20,10 @@ import com.shipdream.lib.poke.Graph; import com.shipdream.lib.poke.Provider; import com.shipdream.lib.poke.Provides; +import com.shipdream.lib.poke.exception.CircularDependenciesException; import com.shipdream.lib.poke.exception.ProvideException; import com.shipdream.lib.poke.exception.ProviderConflictException; +import com.shipdream.lib.poke.exception.ProviderMissingException; import org.junit.Assert; import org.junit.Test; @@ -43,6 +45,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class TestMvcGraph extends BaseTest{ interface Os { @@ -335,39 +338,10 @@ public Object answer(InvocationOnMock invocation) throws Throwable { mvcGraph.getRootComponent().register(obj); } - //TODO: should restore -// @Test -// public void should_be_able_save_and_restore_state_correctly() -// throws ProvideException, ProviderConflictException { -// Bean beanMock = mock(Bean.class); -// -// List beans = new ArrayList(); -// beans.add(beanMock); -// graph.rootComponent.beans = beans; -// -// final BeanKeeper beanKeeperMock = mock(BeanKeeper.class); -// -// // Act -// graph.rootComponent.saveState(beanKeeperMock); -// -// // Verify -// verify(beanKeeperMock).saveState(beanMock); -// -// // Arrange -// reset(beanKeeperMock); -// -// Object stateMock = mock(Object.class); -// when(beanKeeperMock.restoreState(any(Class.class))).thenReturn(stateMock); -// -// graph.rootComponent.restoreState(beanKeeperMock); -// -// // Verify -// verify(beanMock).restoreModel(eq(stateMock)); -// } interface UnimplementedInterface{} - @Test(expected = MvcGraph.Exception.class) + @Test(expected = MvcGraphException.class) public void should_raise_mvc_graph_exception_when_inject_on_poke_exception() { class View { @Inject @@ -376,7 +350,7 @@ class View { graph.inject(new View()); } - @Test(expected = MvcGraph.Exception.class) + @Test(expected = MvcGraphException.class) public void should_raise_mvc_graph_exception_when_release_on_poke_exception() { class View { @Inject @@ -388,7 +362,7 @@ class View { graph.release(view); } - @Test(expected = MvcGraph.Exception.class) + @Test(expected = MvcGraphException.class) public void should_raise_mvc_graph_exception_when_use_on_poke_exception() { class View { @Inject @@ -400,4 +374,124 @@ public void consume(UnimplementedInterface instance) { } }); } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_use_consumer_on_non_main_thread() { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.use(String.class, mock(Consumer.class)); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_use_consumer_without_annotation_on_non_main_thread() { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.use(String.class, null, mock(Consumer.class)); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_reference_on_non_main_thread() throws ProvideException, CircularDependenciesException, ProviderMissingException { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.reference(String.class, null); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_dreference_on_non_main_thread() throws ProvideException, CircularDependenciesException, ProviderMissingException { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.dereference(this, TestMvcGraph.class, null); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_inject_on_non_main_thread() { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.inject(this); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_release_on_non_main_thread() { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.release(this); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_set_rootComponent_on_non_main_thread() throws Graph.IllegalRootComponentException { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.setRootComponent(new MvcComponent("")); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_get_rootComponent_on_non_main_thread() throws Graph.IllegalRootComponentException { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.getRootComponent(); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_register_deferenceListener_on_non_main_thread() throws Graph.IllegalRootComponentException { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.registerDereferencedListener(mock(Provider.DereferenceListener.class)); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_unregister_deferenceListener_on_non_main_thread() throws Graph.IllegalRootComponentException { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.unregisterDereferencedListener(mock(Provider.DereferenceListener.class)); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_clear_deferenceListeners_on_non_main_thread() throws Graph.IllegalRootComponentException { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.clearDereferencedListeners(); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_register_monitor_on_non_main_thread() throws Graph.IllegalRootComponentException { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.registerMonitor(mock(Graph.Monitor.class)); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_unregister_monitor_on_non_main_thread() throws Graph.IllegalRootComponentException { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.unregisterMonitor(mock(Graph.Monitor.class)); + } + + @Test(expected = MvcGraphException.class) + public void should_throw_exception_when_mvc_graph_clear_monitors_on_non_main_thread() throws Graph.IllegalRootComponentException { + graph.uiThreadRunner = mock(UiThreadRunner.class); + when(graph.uiThreadRunner.isOnUiThread()).thenReturn(false); + graph.clearMonitors(); + } + + @Test + public void default_uiThreadRunner_should_post_on_same_thread() { + final Thread thread = Thread.currentThread(); + graph.uiThreadRunner.post(new Runnable() { + @Override + public void run() { + Assert.assertTrue(Thread.currentThread() == thread); + } + }); + } + + @Test + public void default_uiThreadRunner_should_post_with_delay_on_same_thread() { + final Thread thread = Thread.currentThread(); + graph.uiThreadRunner.postDelayed(new Runnable() { + @Override + public void run() { + Assert.assertTrue(Thread.currentThread() == thread); + } + }, 100); + } } diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestNavigationManager.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestNavigationManager.java index 527bacd..938dc59 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestNavigationManager.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestNavigationManager.java @@ -20,6 +20,9 @@ import com.shipdream.lib.android.mvc.manager.internal.BaseNavigationManagerTest; import com.shipdream.lib.poke.Consumer; import com.shipdream.lib.poke.Provides; +import com.shipdream.lib.poke.exception.ProvideException; +import com.shipdream.lib.poke.exception.ProviderConflictException; +import com.shipdream.lib.poke.exception.ProviderMissingException; import org.junit.Assert; import org.junit.Test; @@ -82,6 +85,48 @@ protected void navigateBackByFragment() { navigationManager.navigate(this).back(); } + @Test + public void should_not_fail_when_logger_is_set_trace() { + navigationManager.logger = mock(Logger.class); + when(navigationManager.logger.isTraceEnabled()).thenReturn(true); + + prepareLocationHistory(); + navigationManager.navigate(this).back(); + } + + interface X{ + } + + @Test(expected = MvcGraphException.class) + public void should_throw_MvcGraphException_when_mvcGraph_with_method_encounters_PokeException() { + navigationManager.navigate(this).with(X.class); + } + + class X_1 implements X { + + } + + @Test + public void should_throw_MvcGraphException_when_mvcGraph_destroy_method_encounters_PokeException() throws ProvideException, ProviderConflictException, ProviderMissingException { + navigationManager.logger = mock(Logger.class); + Navigator navigator = navigationManager.navigate(this); + + Mvc.graph().getRootComponent().register(new Object(){ + @Provides + public X x() { + return new X_1(); + } + }); + + navigator.with(X.class); + + Mvc.graph().getRootComponent().unregister(X.class, null); + + navigator.destroy(); + + verify(navigationManager.logger).warn(anyString(), anyString()); + } + @Test public void shouldClearNavigationHistoryUpToSpecified() throws Exception { ForwardListener forwardListener = prepareLocationHistory(); @@ -240,10 +285,14 @@ public void shouldBeAbleToNavigateBackToFirstLocation() throws Exception { prepareLocationHistory(); reset(backListener); - navigationManager.navigate(this).back(null); + Navigator navigator = navigationManager.navigate(this); + navigator.back(null); ArgumentCaptor event = ArgumentCaptor.forClass(NavigationManager.Event.OnLocationBack.class); verify(backListener).onEvent(event.capture()); + Assert.assertTrue(this == event.getValue().getSender()); + Assert.assertTrue(navigator == event.getValue().getNavigator()); + assertEquals(event.getValue().getLastValue().getLocationId(), locId4.getName()); assertEquals(event.getValue().getCurrentValue().getLocationId(), locId1.getName()); NavLocation currentLoc = navigationManager.getModel().getCurrentLocation(); @@ -253,6 +302,31 @@ public void shouldBeAbleToNavigateBackToFirstLocation() throws Exception { Assert.assertTrue(event.getValue().isFastRewind()); } + @Test + public void should_skip_interim_location_on_back_navigation() throws Exception { + //mock the subscriber + BackListener backListener = mock(BackListener.class); + eventBusC.register(backListener); + + prepareLocationHistory(); + + reset(backListener); + navigationManager.navigate(this).to(locId1, new Forwarder().setInterim(true)); + navigationManager.navigate(this).to(locId2, new Forwarder().setInterim(true)); + navigationManager.navigate(this).back(); + + ArgumentCaptor event + = ArgumentCaptor.forClass(NavigationManager.Event.OnLocationBack.class); + verify(backListener).onEvent(event.capture()); + + assertEquals(event.getValue().getLastValue().getLocationId(), locId2.getName()); + assertEquals(event.getValue().getCurrentValue().getLocationId(), locId4.getName()); + NavLocation currentLoc = navigationManager.getModel().getCurrentLocation(); + assertEquals(currentLoc.getLocationId(), locId4.getName()); + + Assert.assertFalse(event.getValue().isFastRewind()); + } + @Test public void should_post_app_exit_event_on_the_last_back_of_linear_back_navigation() { //mock the subscriber @@ -305,6 +379,8 @@ public void onEvent(NavigationManager.Event.OnAppExit event) { navigateBackByFragment(); verify(exitListener, times(1)).onEvent(event.capture()); + + Assert.assertTrue(this == event.getValue().getSender()); } @Test @@ -316,7 +392,8 @@ public void should_not_raise_navigate_back_event_when_navigate_to_first_location navigationManager.navigate(this).to(locId1); // Arrange - navigationManager.navigate(this).back(null); + Navigator navigator = navigationManager.navigate(this); + navigator.back(null); // Verify ArgumentCaptor event @@ -470,7 +547,7 @@ static class SlowXHolder { } - @Test(expected = MvcGraph.Exception.class) + @Test(expected = MvcGraphException.class) public void should_catch_invocation_exception_when_NPE_detected_on_injection() throws Exception { Object com = new Object() { @Provides @@ -722,9 +799,12 @@ public void interim_forwarder_should_instruct_navigation_not_push_location_to_ba ArgumentCaptor event = ArgumentCaptor.forClass(NavigationManager.Event.OnLocationForward.class); + verify(forwardListener).onEvent(event.capture()); assertEquals(event.getValue().getCurrentValue().getLocationId(), TimerController.class.getName()); assertTrue(event.getValue().getCurrentValue().isInterim()); + Assert.assertTrue(this == event.getValue().getSender()); + Assert.assertTrue(navigator == event.getValue().getNavigator()); } /** diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java index a325d3f..ae46bf9 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java @@ -17,7 +17,7 @@ package com.shipdream.lib.android.mvc.controller; import com.shipdream.lib.android.mvc.BaseTest; -import com.shipdream.lib.android.mvc.MvcGraph; +import com.shipdream.lib.android.mvc.MvcGraphException; import com.shipdream.lib.android.mvc.Task; import org.junit.Assert; @@ -83,7 +83,7 @@ public void shouldBeAbleToRunAsyncTaskSuccessfully() throws Exception { verify(view, times(1)).onResourceLoaded(); - verify(view, times(0)).onResourceFailed(any(MvcGraph.Exception.class)); + verify(view, times(0)).onResourceFailed(any(MvcGraphException.class)); verify(view, times(0)).onResourceCancelled(); @@ -98,7 +98,7 @@ public void shouldHandleAsyncTaskExceptionAndDetectFailEvent() throws Exception verify(view, times(0)).onResourceLoaded(); - verify(view, times(1)).onResourceFailed(any(MvcGraph.Exception.class)); + verify(view, times(1)).onResourceFailed(any(MvcGraphException.class)); verify(view, times(0)).onResourceCancelled(); @@ -115,7 +115,7 @@ public void shouldBeAbleToCancelAsyncActionAndDetectInterruptedEvent() throws Ex verify(view, times(0)).onResourceLoaded(); - verify(view, times(0)).onResourceFailed(any(MvcGraph.Exception.class)); + verify(view, times(0)).onResourceFailed(any(MvcGraphException.class)); verify(view, times(1)).onResourceCancelled(); @@ -132,7 +132,7 @@ public void shouldBeAbleToCancelAsyncActionAndDetectCancelEvent() throws Excepti verify(view, times(0)).onResourceLoaded(); - verify(view, times(0)).onResourceFailed(any(MvcGraph.Exception.class)); + verify(view, times(0)).onResourceFailed(any(MvcGraphException.class)); verify(view, times(1)).onResourceCancelled(); diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/TestControllerSimpleInject.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/TestControllerSimpleInject.java index c749d01..d035167 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/TestControllerSimpleInject.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/TestControllerSimpleInject.java @@ -112,22 +112,22 @@ public void testOnDisposeShouldBeCalledWhenControllerReleased() throws Exception graph.getRootComponent().register(new LifeCycleTestControllerModule(lifeCycleProxy)); TestLifCycleView testView = new TestLifCycleView(); - verify(lifeCycleProxy, times(0)).onConstructCalled(); + verify(lifeCycleProxy, times(0)).onCreated(); graph.inject(testView); - verify(lifeCycleProxy, times(1)).onConstructCalled(); - verify(lifeCycleProxy, times(0)).disposeCalled(); + verify(lifeCycleProxy, times(1)).onCreated(); + verify(lifeCycleProxy, times(0)).onDestroy(); TestLifCycleView testView1 = new TestLifCycleView(); graph.inject(testView1); //Should be 1 still since a cached instance will be reused. - verify(lifeCycleProxy, times(1)).onConstructCalled(); - verify(lifeCycleProxy, times(0)).disposeCalled(); + verify(lifeCycleProxy, times(1)).onCreated(); + verify(lifeCycleProxy, times(0)).onDestroy(); graph.release(testView1); - verify(lifeCycleProxy, times(0)).disposeCalled(); + verify(lifeCycleProxy, times(0)).onDestroy(); graph.release(testView); - verify(lifeCycleProxy, times(1)).disposeCalled(); + verify(lifeCycleProxy, times(1)).onDestroy(); } public static class Car { diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/Phone.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/Phone.java new file mode 100644 index 0000000..cceccf1 --- /dev/null +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/Phone.java @@ -0,0 +1,4 @@ +package com.shipdream.lib.android.mvc.inject.test; + +public interface Phone { +} diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/Robot.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/Robot.java new file mode 100644 index 0000000..1a5822a --- /dev/null +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/Robot.java @@ -0,0 +1,4 @@ +package com.shipdream.lib.android.mvc.inject.test; + +public interface Robot { +} diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/Smart.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/Smart.java new file mode 100644 index 0000000..428e684 --- /dev/null +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/Smart.java @@ -0,0 +1,12 @@ +package com.shipdream.lib.android.mvc.inject.test; + +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Qualifier +@Retention(RUNTIME) +public @interface Smart { +} diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/internal/PhoneImpl.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/internal/PhoneImpl.java new file mode 100644 index 0000000..9b9d982 --- /dev/null +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/internal/PhoneImpl.java @@ -0,0 +1,7 @@ +package com.shipdream.lib.android.mvc.inject.test.internal; + +import com.shipdream.lib.android.mvc.inject.test.Smart; + +@Smart +public class PhoneImpl { +} diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/internal/RobotImpl.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/internal/RobotImpl.java new file mode 100644 index 0000000..53b9dec --- /dev/null +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/test/internal/RobotImpl.java @@ -0,0 +1,4 @@ +package com.shipdream.lib.android.mvc.inject.test.internal; + +public class RobotImpl { +} diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/testNameMapping/controller/LifeCycleTestController.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/testNameMapping/controller/LifeCycleTestController.java index 7f60545..b91d1e2 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/testNameMapping/controller/LifeCycleTestController.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/inject/testNameMapping/controller/LifeCycleTestController.java @@ -20,8 +20,8 @@ public class LifeCycleTestController extends Controller { public interface Proxy { - void onConstructCalled(); - void disposeCalled(); + void onCreated(); + void onDestroy(); } public Proxy proxy; @@ -32,11 +32,11 @@ public Class modelType() { public void onCreated() { super.onCreated(); - proxy.onConstructCalled(); + proxy.onCreated(); } public void onDestroy() { super.onDestroy(); - proxy.disposeCalled(); + proxy.onDestroy(); } } diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/manager/internal/BaseNavigationManagerTest.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/manager/internal/BaseNavigationManagerTest.java index faadc7c..93475e2 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/manager/internal/BaseNavigationManagerTest.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/manager/internal/BaseNavigationManagerTest.java @@ -22,6 +22,7 @@ import com.shipdream.lib.poke.Component; import com.shipdream.lib.poke.Provides; +import org.junit.After; import org.junit.Before; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -60,4 +61,9 @@ protected ExecutorService createExecutorService() { graph.inject(navigationManager); navigationManager.onCreated(); } + + @After + public void tearDown() throws Exception { + navigationManager.onDestroy(); + } } From fec46e0ddc17ac4c382ae63d74be814a0f22f513 Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Wed, 13 Jul 2016 22:50:43 +1000 Subject: [PATCH 10/14] More coverage for poke library --- .../java/com/shipdream/lib/poke/Graph.java | 2 +- .../lib/poke/TestCircularDependencies.java | 72 +++++++++- .../com/shipdream/lib/poke/TestGraph.java | 130 ++++++++++++++++++ .../com/shipdream/lib/poke/TestProvider.java | 64 --------- 4 files changed, 202 insertions(+), 66 deletions(-) create mode 100644 library/poke/src/test/java/com/shipdream/lib/poke/TestGraph.java delete mode 100644 library/poke/src/test/java/com/shipdream/lib/poke/TestProvider.java diff --git a/library/poke/src/main/java/com/shipdream/lib/poke/Graph.java b/library/poke/src/main/java/com/shipdream/lib/poke/Graph.java index 72413d0..e6d365e 100644 --- a/library/poke/src/main/java/com/shipdream/lib/poke/Graph.java +++ b/library/poke/src/main/java/com/shipdream/lib/poke/Graph.java @@ -367,7 +367,7 @@ public T reference(Class type, Annotation qualifier, Class provider, Power instance) { } }); + Assert.assertEquals(1, powerProvider.getReferencedListeners().size()); + ProviderByClassType driverProvider = new ProviderByClassType(Driver.class, DriverImpl.class); driverProvider.registerOnReferencedListener(new Provider.ReferencedListener() { @Override @@ -152,6 +155,73 @@ public void onReferenced(Provider provider, Robot instance) { component.register(driverProvider); graph.inject(factory, MyInject.class); + + powerProvider.clearOnReferencedListener(); + Assert.assertEquals(null, powerProvider.getReferencedListeners()); + } + + @Test + public void shouldNotifyCreatedCallbackWhenObjectFullyInjectedWithCircularDependencies() throws PokeException { + class Plant { + @MyInject + private Power power; + } + + Provider powerProvider = new Provider(Power.class, new ScopeCache()) { + @Override + protected Power createInstance() throws ProvideException { + return new Power() { + }; + } + }; + component.register(powerProvider); + + Provider.CreationListener creationListener = mock(Provider.CreationListener.class); + powerProvider.registerCreationListener(creationListener); + Assert.assertEquals(1, powerProvider.getCreationListeners().size()); + + Plant plant = new Plant(); + graph.getRootComponent().scopeCache = null; + graph.inject(plant, MyInject.class); + + verify(creationListener, times(1)).onCreated(eq(powerProvider), eq(plant.power)); + + powerProvider.unregisterCreationListener(creationListener); + Assert.assertEquals(null, powerProvider.getCreationListeners()); + graph.inject(plant, MyInject.class); + verify(creationListener, times(1)).onCreated(eq(powerProvider), any(Power.class)); + + powerProvider.registerCreationListener(creationListener); + graph.inject(plant, MyInject.class); + verify(creationListener, times(2)).onCreated(eq(powerProvider), any(Power.class)); + + powerProvider.clearCreationListeners(); + graph.inject(plant, MyInject.class); + verify(creationListener, times(2)).onCreated(eq(powerProvider), any(Power.class)); + } + + @Test + public void component_cache_should_override_provider_cache() throws PokeException { + class Plant { + @MyInject + private Power power; + } + + ScopeCache scopeCache = new ScopeCache(); + Provider powerProvider = new Provider(Power.class, scopeCache) { + @Override + protected Power createInstance() throws ProvideException { + return new Power() { + }; + } + }; + + Assert.assertTrue(scopeCache == powerProvider.getScopeCache()); + + component.register(powerProvider); + + Assert.assertTrue(scopeCache != powerProvider.getScopeCache()); + Assert.assertTrue(powerProvider.getScopeCache() == component.scopeCache); } @Test diff --git a/library/poke/src/test/java/com/shipdream/lib/poke/TestGraph.java b/library/poke/src/test/java/com/shipdream/lib/poke/TestGraph.java new file mode 100644 index 0000000..d44bfe1 --- /dev/null +++ b/library/poke/src/test/java/com/shipdream/lib/poke/TestGraph.java @@ -0,0 +1,130 @@ +package com.shipdream.lib.poke; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Random; + +import javax.inject.Inject; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class TestGraph extends BaseTestCases { + @Test + public void should_get_correct_rootComponent() throws Exception{ + Graph graph = new Graph(); + Component c = new Component(); + Assert.assertNull(graph.getRootComponent()); + + graph.setRootComponent(c); + Assert.assertTrue(graph.getRootComponent() == c); + } + + @Test + public void should_call_on_instance_created_callback() throws Exception{ + Graph graph = new Graph(); + + Provider.DisposeListener disposeListener = mock(Provider.DisposeListener.class); + graph.registerDisposeListener(disposeListener); + + graph.setRootComponent(new Component()); + graph.getRootComponent().register(new Object(){ + @Provides + String string() { + return ""; + } + }); + + String instance = graph.reference(String.class, null, Inject.class); + String instance2 = graph.reference(String.class, null, Inject.class); + + graph.dereference(instance, String.class, null, Inject.class); + verify(disposeListener, times(0)).onDisposed(any(Provider.class), eq(instance)); + + graph.dereference(instance2, String.class, null, Inject.class); + + verify(disposeListener, times(1)).onDisposed(any(Provider.class), eq(instance2)); + + Assert.assertTrue(instance == instance2); + } + + @Test + public void should_call_on_dispose_when_dereference_without_component_cache() throws Exception{ + Graph graph = new Graph(); + + Provider.DisposeListener disposeListener = mock(Provider.DisposeListener.class); + graph.registerDisposeListener(disposeListener); + + //Component without cache, every injection will create a new string instance + Component c = new Component(false); + graph.setRootComponent(c); + graph.getRootComponent().register(new Object(){ + @Provides + String string() { + return String.valueOf(new Random().nextDouble()); + } + }); + + String instance = graph.reference(String.class, null, Inject.class); + String instance2 = graph.reference(String.class, null, Inject.class); + + graph.dereference(instance, String.class, null, Inject.class); + verify(disposeListener, times(1)).onDisposed(any(Provider.class), eq(instance)); + + graph.dereference(instance2, String.class, null, Inject.class); + + verify(disposeListener, times(1)).onDisposed(any(Provider.class), eq(instance2)); + + Assert.assertTrue(instance != instance2); + } + + @Test + public void should_be_able_to_unregister_dispose_listener() throws Exception{ + Graph graph = new Graph(); + + Provider.DisposeListener disposeListener = mock(Provider.DisposeListener.class); + graph.registerDisposeListener(disposeListener); + + graph.setRootComponent(new Component()); + graph.getRootComponent().register(new Object(){ + @Provides + String string() { + return ""; + } + }); + + String instance = graph.reference(String.class, null, Inject.class); + + graph.unregisterDisposeListener(disposeListener); + + graph.dereference(instance, String.class, null, Inject.class); + verify(disposeListener, times(0)).onDisposed(any(Provider.class), eq(instance)); + } + + @Test + public void should_be_able_to_clear_dispose_listeners() throws Exception{ + Graph graph = new Graph(); + + Provider.DisposeListener disposeListener = mock(Provider.DisposeListener.class); + graph.registerDisposeListener(disposeListener); + + graph.setRootComponent(new Component()); + graph.getRootComponent().register(new Object(){ + @Provides + String string() { + return ""; + } + }); + + String instance = graph.reference(String.class, null, Inject.class); + + graph.clearDisposeListeners(); + + graph.dereference(instance, String.class, null, Inject.class); + verify(disposeListener, times(0)).onDisposed(any(Provider.class), eq(instance)); + } +} diff --git a/library/poke/src/test/java/com/shipdream/lib/poke/TestProvider.java b/library/poke/src/test/java/com/shipdream/lib/poke/TestProvider.java deleted file mode 100644 index a91f3ab..0000000 --- a/library/poke/src/test/java/com/shipdream/lib/poke/TestProvider.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.shipdream.lib.poke; - -import com.shipdream.lib.poke.exception.PokeException; -import com.shipdream.lib.poke.exception.ProvideException; -import com.shipdream.lib.poke.exception.ProviderMissingException; - -import org.junit.Assert; -import org.junit.Test; - -/** - * Created by kejun on 5/26/2016. - */ -public class TestProvider { - class Book { - @MyInject - String title; - } - - @Test - public void test_constructor() { - ScopeCache scopeCache = new ScopeCache(); - Provider p = new Provider(String.class, scopeCache) { - @Override - protected Object createInstance() throws ProvideException { - return null; - } - }; - - Assert.assertTrue(p.getScopeCache() == scopeCache); - Assert.assertTrue(p.getQualifier() == null); - } - - @Test - public void test_provider_missing_exception_constructor() { - ProviderMissingException e2 = new ProviderMissingException("msg"); - ProviderMissingException e3 = new ProviderMissingException("msg", new Throwable()); - } - - @Test - public void should_throw_provide_exception_with_provider_providing_null_instance() throws PokeException { - Graph graph = new Graph(); - - Component c = new Component(false); - c.register(new Provider(String.class) { - @Override - protected Object createInstance() throws ProvideException { - return null; - } - }); - graph.setRootComponent(c); - - boolean exp = false; - - Book b = new Book(); - - try { - graph.inject(b, MyInject.class); - } catch (ProvideException e) { - exp = true; - } - - Assert.assertTrue(exp); - } -} From 7fb97406f81df664e038d75a5688e7e4dd3fdccd Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Thu, 14 Jul 2016 09:22:13 +1000 Subject: [PATCH 11/14] add wait time out in the test --- .../test/java/com/shipdream/lib/android/mvc/TestMvcBean.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcBean.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcBean.java index 1d176e1..e04ee45 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcBean.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcBean.java @@ -58,7 +58,7 @@ public void run() { }); synchronized (worker) { - worker.wait(); + worker.wait(1000); } Assert.assertTrue(worker.thread != Thread.currentThread()); From ba07bab31f016ad3ff9ced251ed747c5ebf28a31 Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Thu, 14 Jul 2016 10:49:03 +1000 Subject: [PATCH 12/14] AsyncTask callback.onException throws out exception by default --- .../com/shipdream/lib/android/mvc/Controller.java | 9 +++++++-- .../java/com/shipdream/lib/android/mvc/Task.java | 8 ++++++-- .../com/shipdream/lib/android/mvc/TestMvcBean.java | 12 +++--------- .../shipdream/lib/android/mvc/TestMvcComponent.java | 2 +- .../lib/android/mvc/controller/TestRunAsyncTask.java | 5 ++--- .../simple/controller/CounterBasicController.java | 1 - 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Controller.java b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Controller.java index 5023bd0..8462bfa 100644 --- a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Controller.java +++ b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Controller.java @@ -224,8 +224,13 @@ public void run() { uiThreadRunner.post(new Runnable() { @Override public void run() { - callback.onException(e); - callback.onFinally(); + try { + callback.onException(e); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + callback.onFinally(); + } } }); } else { diff --git a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Task.java b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Task.java index d3eace1..9c61d1c 100644 --- a/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Task.java +++ b/library/android-mvc-core/src/main/java/com/shipdream/lib/android/mvc/Task.java @@ -179,9 +179,13 @@ public void onCancelled(boolean interrupted){} * Called when the execution of the task encounters exceptions. Note that an * {@link InterruptedException} caused by cancelling won't trigger this callback but * {@link #onCancelled(boolean)} with argument equals true - * @param e The exception + * @param e The exception to handle + * @throws Exception The exception by default will be thrown out otherwise override this + * method to handle it and DO NOT call super.onException(e) */ - public void onException(Exception e){} + public void onException(Exception e) throws Exception { + throw e; + } /** * Called when the task has started and runs into {@link #onSuccess(Object)} ()}, diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcBean.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcBean.java index e04ee45..bdb53be 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcBean.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcBean.java @@ -19,11 +19,9 @@ import org.junit.Assert; import org.junit.Test; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; -import javax.inject.Inject; - -public class TestMvcBean { +public class TestMvcBean extends BaseTest { @Test(expected = IllegalArgumentException.class) public void should_throw_exception_when_bind_null_to_a_mvcBean() { Bean bean = new Bean() { @@ -40,14 +38,10 @@ public Class modelType() { public void should_be_able_to_inject_background_executor_service() throws InterruptedException { class Worker { Thread thread = Thread.currentThread(); - @Inject - ExecutorService executorService; } final Worker worker = new Worker(); - Mvc.graph().inject(worker); - - worker.executorService.submit(new Runnable() { + Executors.newSingleThreadExecutor().submit(new Runnable() { @Override public void run() { synchronized (worker) { diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcComponent.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcComponent.java index 5128a64..985d215 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcComponent.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/TestMvcComponent.java @@ -8,7 +8,7 @@ import javax.inject.Inject; -public class TestMvcComponent { +public class TestMvcComponent extends BaseTest { @Test(expected = MvcGraphException.class) public void should_throw_provider_missing_exception_when_locate_an_qualified_class() { class Shop { diff --git a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java index ae46bf9..20fec2a 100644 --- a/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java +++ b/library/android-mvc-core/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java @@ -140,7 +140,7 @@ public void shouldBeAbleToCancelAsyncActionAndDetectCancelEvent() throws Excepti } @Test - public void should_catch_exception_during_running_async_task() throws InterruptedException { + public void should_catch_exception_during_running_async_task() throws Exception { final Task.Callback callback = mock(Task.Callback.class); Task.Monitor monitor1 = controller.loadHeavyResource(this, new Task() { @Override @@ -168,8 +168,7 @@ public void onCancelled(boolean interrupted) { } @Override - public void onException(Exception e) { - super.onException(e); + public void onException(Exception e) throws Exception { callback.onException(e); } diff --git a/samples/simple-mvc/core/src/main/java/com/shipdream/lib/android/mvc/samples/simple/controller/CounterBasicController.java b/samples/simple-mvc/core/src/main/java/com/shipdream/lib/android/mvc/samples/simple/controller/CounterBasicController.java index 770e38c..dded368 100644 --- a/samples/simple-mvc/core/src/main/java/com/shipdream/lib/android/mvc/samples/simple/controller/CounterBasicController.java +++ b/samples/simple-mvc/core/src/main/java/com/shipdream/lib/android/mvc/samples/simple/controller/CounterBasicController.java @@ -75,7 +75,6 @@ public void onSuccess(Response response) { @Override public void onException(Exception e) { - super.onException(e); view.showErrorMessageToFetchIp(); logger.warn(e.getMessage(), e); } From f1738b5bb894813a7752780ace821ff95ea01342 Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Thu, 14 Jul 2016 10:49:26 +1000 Subject: [PATCH 13/14] Uplift gson --- library/android-mvc-core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/android-mvc-core/build.gradle b/library/android-mvc-core/build.gradle index e9783ea..7b27d95 100644 --- a/library/android-mvc-core/build.gradle +++ b/library/android-mvc-core/build.gradle @@ -59,7 +59,7 @@ dependencies { compile project(':library:poke') compile rootProject.lib.slf4jApi - compile 'com.google.code.gson:gson:2.3.1' + compile 'com.google.code.gson:gson:2.7' testCompile rootProject.ext.lib.junit testCompile rootProject.ext.lib.mokito From 69ff28141508ebe5954ba0f79a4f3ac4763b3b14 Mon Sep 17 00:00:00 2001 From: Kejun Xia Date: Thu, 14 Jul 2016 10:49:42 +1000 Subject: [PATCH 14/14] Uplift logback --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8c5664f..3569d40 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ apply plugin: 'jacoco' // Top-level build file where you can add configuration options common to all sub-projects/modules. def log4jVersion = '1.7.6' -def logbackAndroidVersion = '1.1.1-4' +def logbackAndroidVersion = '1.1.1-5' buildscript { repositories {