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 17package androidx.lifecycle; 18 19import static androidx.lifecycle.Lifecycle.Event.ON_CREATE; 20import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY; 21import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; 22import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; 23import static androidx.lifecycle.Lifecycle.Event.ON_START; 24import static androidx.lifecycle.Lifecycle.Event.ON_STOP; 25import static androidx.lifecycle.Lifecycle.State.CREATED; 26import static androidx.lifecycle.Lifecycle.State.DESTROYED; 27import static androidx.lifecycle.Lifecycle.State.RESUMED; 28import static androidx.lifecycle.Lifecycle.State.STARTED; 29import static androidx.lifecycle.testapp.TestEvent.LIFECYCLE_EVENT; 30import static androidx.lifecycle.testapp.TestEvent.OWNER_CALLBACK; 31 32import static org.hamcrest.CoreMatchers.is; 33import static org.hamcrest.MatcherAssert.assertThat; 34 35import android.app.Activity; 36import android.app.Instrumentation; 37import android.app.Instrumentation.ActivityMonitor; 38import android.support.test.InstrumentationRegistry; 39import android.support.test.rule.ActivityTestRule; 40 41import androidx.core.util.Pair; 42import androidx.lifecycle.testapp.TestEvent; 43 44import java.util.ArrayList; 45import java.util.Arrays; 46import java.util.List; 47import java.util.concurrent.CountDownLatch; 48import java.util.concurrent.TimeUnit; 49 50class TestUtils { 51 52 private static final long TIMEOUT_MS = 2000; 53 54 @SuppressWarnings("unchecked") 55 static <T extends Activity> T recreateActivity(final T activity, ActivityTestRule rule) 56 throws Throwable { 57 ActivityMonitor monitor = new ActivityMonitor( 58 activity.getClass().getCanonicalName(), null, false); 59 Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 60 instrumentation.addMonitor(monitor); 61 rule.runOnUiThread(activity::recreate); 62 T result; 63 64 // this guarantee that we will reinstall monitor between notifications about onDestroy 65 // and onCreate 66 //noinspection SynchronizationOnLocalVariableOrMethodParameter 67 synchronized (monitor) { 68 do { 69 // the documetation says "Block until an Activity is created 70 // that matches this monitor." This statement is true, but there are some other 71 // true statements like: "Block until an Activity is destoyed" or 72 // "Block until an Activity is resumed"... 73 74 // this call will release synchronization monitor's monitor 75 result = (T) monitor.waitForActivityWithTimeout(TIMEOUT_MS); 76 if (result == null) { 77 throw new RuntimeException("Timeout. Failed to recreate an activity"); 78 } 79 } while (result == activity); 80 } 81 return result; 82 } 83 84 static void waitTillCreated(final LifecycleOwner owner, ActivityTestRule<?> activityRule) 85 throws Throwable { 86 waitTillState(owner, activityRule, CREATED); 87 } 88 89 static void waitTillStarted(final LifecycleOwner owner, ActivityTestRule<?> activityRule) 90 throws Throwable { 91 waitTillState(owner, activityRule, STARTED); 92 } 93 94 static void waitTillResumed(final LifecycleOwner owner, ActivityTestRule<?> activityRule) 95 throws Throwable { 96 waitTillState(owner, activityRule, RESUMED); 97 } 98 99 static void waitTillDestroyed(final LifecycleOwner owner, ActivityTestRule<?> activityRule) 100 throws Throwable { 101 waitTillState(owner, activityRule, DESTROYED); 102 } 103 104 static void waitTillState(final LifecycleOwner owner, ActivityTestRule<?> activityRule, 105 Lifecycle.State state) 106 throws Throwable { 107 final CountDownLatch latch = new CountDownLatch(1); 108 activityRule.runOnUiThread(() -> { 109 Lifecycle.State currentState = owner.getLifecycle().getCurrentState(); 110 if (currentState == state) { 111 latch.countDown(); 112 } else { 113 owner.getLifecycle().addObserver(new LifecycleObserver() { 114 @OnLifecycleEvent(Lifecycle.Event.ON_ANY) 115 public void onStateChanged(LifecycleOwner provider) { 116 if (provider.getLifecycle().getCurrentState() == state) { 117 latch.countDown(); 118 provider.getLifecycle().removeObserver(this); 119 } 120 } 121 }); 122 } 123 }); 124 boolean latchResult = latch.await(1, TimeUnit.MINUTES); 125 assertThat("expected " + state + " never happened. Current state:" 126 + owner.getLifecycle().getCurrentState(), latchResult, is(true)); 127 128 // wait for another loop to ensure all observers are called 129 activityRule.runOnUiThread(() -> { 130 // do nothing 131 }); 132 } 133 134 @SafeVarargs 135 static <T> List<T> flatMap(List<T>... items) { 136 ArrayList<T> result = new ArrayList<>(); 137 for (List<T> item : items) { 138 result.addAll(item); 139 } 140 return result; 141 } 142 143 /** 144 * Event tuples of {@link TestEvent} and {@link Lifecycle.Event} 145 * in the order they should arrive. 146 */ 147 @SuppressWarnings("unchecked") 148 static class OrderedTuples { 149 static final List<Pair<TestEvent, Lifecycle.Event>> CREATE = 150 Arrays.asList(new Pair(OWNER_CALLBACK, ON_CREATE), 151 new Pair(LIFECYCLE_EVENT, ON_CREATE)); 152 static final List<Pair<TestEvent, Lifecycle.Event>> START = 153 Arrays.asList(new Pair(OWNER_CALLBACK, ON_START), 154 new Pair(LIFECYCLE_EVENT, ON_START)); 155 static final List<Pair<TestEvent, Lifecycle.Event>> RESUME = 156 Arrays.asList(new Pair(OWNER_CALLBACK, ON_RESUME), 157 new Pair(LIFECYCLE_EVENT, ON_RESUME)); 158 static final List<Pair<TestEvent, Lifecycle.Event>> PAUSE = 159 Arrays.asList(new Pair(LIFECYCLE_EVENT, ON_PAUSE), 160 new Pair(OWNER_CALLBACK, ON_PAUSE)); 161 static final List<Pair<TestEvent, Lifecycle.Event>> STOP = 162 Arrays.asList(new Pair(LIFECYCLE_EVENT, ON_STOP), 163 new Pair(OWNER_CALLBACK, ON_STOP)); 164 static final List<Pair<TestEvent, Lifecycle.Event>> DESTROY = 165 Arrays.asList(new Pair(LIFECYCLE_EVENT, ON_DESTROY), 166 new Pair(OWNER_CALLBACK, ON_DESTROY)); 167 } 168} 169