Home | History | Annotate | Download | only in lifecycle
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.lifecycle;
     18 
     19 import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
     20 
     21 import static org.hamcrest.CoreMatchers.is;
     22 import static org.hamcrest.CoreMatchers.not;
     23 import static org.hamcrest.CoreMatchers.notNullValue;
     24 import static org.hamcrest.MatcherAssert.assertThat;
     25 
     26 import android.app.Instrumentation;
     27 import android.support.test.InstrumentationRegistry;
     28 import android.support.test.annotation.UiThreadTest;
     29 import android.support.test.filters.MediumTest;
     30 import android.support.test.rule.ActivityTestRule;
     31 import android.support.test.runner.AndroidJUnit4;
     32 
     33 import androidx.fragment.app.Fragment;
     34 import androidx.fragment.app.FragmentActivity;
     35 import androidx.fragment.app.FragmentManager;
     36 import androidx.lifecycle.viewmodeltest.TestViewModel;
     37 import androidx.lifecycle.viewmodeltest.ViewModelActivity;
     38 import androidx.lifecycle.viewmodeltest.ViewModelActivity.ViewModelFragment;
     39 
     40 import org.junit.Rule;
     41 import org.junit.Test;
     42 import org.junit.runner.RunWith;
     43 
     44 import java.util.concurrent.CountDownLatch;
     45 import java.util.concurrent.TimeUnit;
     46 
     47 @MediumTest
     48 @RunWith(AndroidJUnit4.class)
     49 public class ViewModelTest {
     50     private static final int TIMEOUT = 2; // secs
     51 
     52     @Rule
     53     public ActivityTestRule<ViewModelActivity> mActivityRule =
     54             new ActivityTestRule<>(ViewModelActivity.class);
     55 
     56     @Test
     57     public void ensureSameViewHolders() throws Throwable {
     58         final TestViewModel[] activityModel = new TestViewModel[1];
     59         final TestViewModel[] defaultActivityModel = new TestViewModel[1];
     60         final TestViewModel[] fragment1Model = new TestViewModel[1];
     61         final TestViewModel[] fragment2Model = new TestViewModel[1];
     62         final ViewModelActivity[] viewModelActivity = new ViewModelActivity[1];
     63         viewModelActivity[0] = mActivityRule.getActivity();
     64         mActivityRule.runOnUiThread(new Runnable() {
     65             @Override
     66             public void run() {
     67                 ViewModelFragment fragment1 = getFragment(viewModelActivity[0],
     68                         ViewModelActivity.FRAGMENT_TAG_1);
     69                 ViewModelFragment fragment2 = getFragment(viewModelActivity[0],
     70                         ViewModelActivity.FRAGMENT_TAG_2);
     71                 assertThat(fragment1, notNullValue());
     72                 assertThat(fragment2, notNullValue());
     73                 assertThat(fragment1.activityModel, is(fragment2.activityModel));
     74                 assertThat(fragment1.fragmentModel, not(is(fragment2.activityModel)));
     75                 assertThat(mActivityRule.getActivity().activityModel, is(fragment1.activityModel));
     76                 activityModel[0] = mActivityRule.getActivity().activityModel;
     77                 defaultActivityModel[0] = mActivityRule.getActivity().defaultActivityModel;
     78                 assertThat(defaultActivityModel[0], not(is(activityModel[0])));
     79                 fragment1Model[0] = fragment1.fragmentModel;
     80                 fragment2Model[0] = fragment2.fragmentModel;
     81             }
     82         });
     83         viewModelActivity[0] = recreateActivity();
     84         mActivityRule.runOnUiThread(new Runnable() {
     85             @Override
     86             public void run() {
     87                 ViewModelFragment fragment1 = getFragment(viewModelActivity[0],
     88                         ViewModelActivity.FRAGMENT_TAG_1);
     89                 ViewModelFragment fragment2 = getFragment(viewModelActivity[0],
     90                         ViewModelActivity.FRAGMENT_TAG_2);
     91                 assertThat(fragment1, notNullValue());
     92                 assertThat(fragment2, notNullValue());
     93 
     94                 assertThat(fragment1.activityModel, is(activityModel[0]));
     95                 assertThat(fragment2.activityModel, is(activityModel[0]));
     96                 assertThat(fragment1.fragmentModel, is(fragment1Model[0]));
     97                 assertThat(fragment2.fragmentModel, is(fragment2Model[0]));
     98                 assertThat(fragment1.defaultActivityModel, is(defaultActivityModel[0]));
     99                 assertThat(fragment2.defaultActivityModel, is(defaultActivityModel[0]));
    100                 assertThat(mActivityRule.getActivity().activityModel, is(activityModel[0]));
    101                 assertThat(mActivityRule.getActivity().defaultActivityModel,
    102                         is(defaultActivityModel[0]));
    103             }
    104         });
    105     }
    106 
    107     @Test
    108     @UiThreadTest
    109     public void testGetApplication() {
    110         TestViewModel activityModel = mActivityRule.getActivity().activityModel;
    111         assertThat(activityModel.getApplication(),
    112                 is(InstrumentationRegistry.getTargetContext().getApplicationContext()));
    113     }
    114 
    115     @Test
    116     public void testOnClear() throws Throwable {
    117         final ViewModelActivity activity = mActivityRule.getActivity();
    118         final CountDownLatch latch = new CountDownLatch(1);
    119         final LifecycleObserver observer = new LifecycleObserver() {
    120             @SuppressWarnings("unused")
    121             @OnLifecycleEvent(ON_RESUME)
    122             void onResume() {
    123                 try {
    124                     final FragmentManager manager = activity.getSupportFragmentManager();
    125                     Fragment fragment = new Fragment();
    126                     manager.beginTransaction().add(fragment, "temp").commitNow();
    127                     ViewModel1 vm = ViewModelProviders.of(fragment).get(ViewModel1.class);
    128                     assertThat(vm.mCleared, is(false));
    129                     manager.beginTransaction().remove(fragment).commitNow();
    130                     assertThat(vm.mCleared, is(true));
    131                 } finally {
    132                     latch.countDown();
    133                 }
    134             }
    135         };
    136 
    137         mActivityRule.runOnUiThread(new Runnable() {
    138             @Override
    139             public void run() {
    140                 activity.getLifecycle().addObserver(observer);
    141             }
    142         });
    143         assertThat(latch.await(TIMEOUT, TimeUnit.SECONDS), is(true));
    144     }
    145 
    146     private ViewModelFragment getFragment(FragmentActivity activity, String tag) {
    147         return (ViewModelFragment) activity.getSupportFragmentManager()
    148                 .findFragmentByTag(tag);
    149     }
    150 
    151     private ViewModelActivity recreateActivity() throws Throwable {
    152         Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
    153                 ViewModelActivity.class.getCanonicalName(), null, false);
    154         Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
    155         instrumentation.addMonitor(monitor);
    156         final ViewModelActivity previous = mActivityRule.getActivity();
    157         mActivityRule.runOnUiThread(new Runnable() {
    158             @Override
    159             public void run() {
    160                 previous.recreate();
    161             }
    162         });
    163         ViewModelActivity result;
    164 
    165         // this guarantee that we will reinstall monitor between notifications about onDestroy
    166         // and onCreate
    167         //noinspection SynchronizationOnLocalVariableOrMethodParameter
    168         synchronized (monitor) {
    169             do {
    170                 // the documentation says "Block until an Activity is created
    171                 // that matches this monitor." This statement is true, but there are some other
    172                 // true statements like: "Block until an Activity is destroyed" or
    173                 // "Block until an Activity is resumed"...
    174 
    175                 // this call will release synchronization monitor's monitor
    176                 result = (ViewModelActivity) monitor.waitForActivityWithTimeout(4000);
    177                 if (result == null) {
    178                     throw new RuntimeException("Timeout. Failed to recreate an activity");
    179                 }
    180             } while (result == previous);
    181         }
    182         return result;
    183     }
    184 
    185     public static class ViewModel1 extends ViewModel {
    186         boolean mCleared = false;
    187 
    188         @Override
    189         protected void onCleared() {
    190             mCleared = true;
    191         }
    192     }
    193 }
    194