1 package org.robolectric.integrationtests.axt; 2 3 import static android.os.Build.VERSION_CODES.P; 4 import static com.google.common.truth.Truth.assertThat; 5 import static org.junit.Assert.assertThrows; 6 7 import android.app.Activity; 8 import android.app.AppComponentFactory; 9 import android.app.UiAutomation; 10 import android.content.Context; 11 import android.content.Intent; 12 import android.content.res.Configuration; 13 import android.os.Bundle; 14 import android.os.Looper; 15 import androidx.appcompat.R; 16 import androidx.appcompat.app.AppCompatActivity; 17 import androidx.fragment.app.Fragment; 18 import androidx.lifecycle.Lifecycle.State; 19 import androidx.test.core.app.ActivityScenario; 20 import androidx.test.core.app.ApplicationProvider; 21 import androidx.test.ext.junit.runners.AndroidJUnit4; 22 import androidx.test.platform.app.InstrumentationRegistry; 23 import java.util.ArrayList; 24 import java.util.List; 25 import java.util.concurrent.atomic.AtomicReference; 26 import javax.annotation.Nonnull; 27 import javax.annotation.Nullable; 28 import org.junit.Before; 29 import org.junit.Test; 30 import org.junit.runner.RunWith; 31 import org.robolectric.annotation.Config; 32 33 /** 34 * Integration tests for {@link ActivityScenario} that verify it behaves consistently on device and 35 * Robolectric. 36 */ 37 @RunWith(AndroidJUnit4.class) 38 public class ActivityScenarioTest { 39 40 private static final List<String> callbacks = new ArrayList<>(); 41 42 /** 43 * @noinspection NewClassNamingConvention 44 */ 45 public static class TranscriptActivity extends Activity { 46 47 @Override onCreate(Bundle savedInstanceState)48 public void onCreate(Bundle savedInstanceState) { 49 super.onCreate(savedInstanceState); 50 callbacks.add("onCreate"); 51 } 52 53 @Override onStart()54 public void onStart() { 55 super.onStart(); 56 callbacks.add("onStart"); 57 } 58 59 @Override onPostCreate(Bundle savedInstanceState)60 public void onPostCreate(Bundle savedInstanceState) { 61 super.onPostCreate(savedInstanceState); 62 callbacks.add("onPostCreate"); 63 } 64 65 @Override onResume()66 public void onResume() { 67 super.onResume(); 68 callbacks.add("onResume"); 69 } 70 71 @Override onPause()72 public void onPause() { 73 super.onPause(); 74 callbacks.add("onPause"); 75 } 76 77 @Override onStop()78 public void onStop() { 79 super.onStop(); 80 callbacks.add("onStop " + isChangingConfigurations()); 81 } 82 83 @Override onRestart()84 public void onRestart() { 85 super.onRestart(); 86 callbacks.add("onRestart"); 87 } 88 89 @Override onDestroy()90 public void onDestroy() { 91 super.onDestroy(); 92 callbacks.add("onDestroy"); 93 } 94 95 @Override onWindowFocusChanged(boolean hasFocus)96 public void onWindowFocusChanged(boolean hasFocus) { 97 super.onWindowFocusChanged(hasFocus); 98 callbacks.add("onWindowFocusChanged " + hasFocus); 99 } 100 } 101 102 /** 103 * @noinspection NewClassNamingConvention 104 */ 105 public static class LifecycleOwnerActivity extends AppCompatActivity { 106 @Override onCreate(Bundle bundle)107 protected void onCreate(Bundle bundle) { 108 setTheme(R.style.Theme_AppCompat); 109 super.onCreate(bundle); 110 } 111 } 112 113 @Before setUp()114 public void setUp() { 115 callbacks.clear(); 116 } 117 118 /** 119 * @noinspection NewClassNamingConvention 120 */ 121 public static class ActivityWithCustomConstructor extends Activity { 122 private final int intValue; 123 ActivityWithCustomConstructor(int intValue)124 public ActivityWithCustomConstructor(int intValue) { 125 this.intValue = intValue; 126 } 127 getIntValue()128 public int getIntValue() { 129 return intValue; 130 } 131 } 132 133 /** 134 * @noinspection NewClassNamingConvention 135 */ 136 public static class CustomAppComponentFactory extends AppComponentFactory { 137 138 @Nonnull 139 @Override instantiateActivity( @onnull ClassLoader cl, @Nonnull String className, @Nullable Intent intent)140 public Activity instantiateActivity( 141 @Nonnull ClassLoader cl, @Nonnull String className, @Nullable Intent intent) 142 throws ClassNotFoundException, IllegalAccessException, InstantiationException { 143 if (className.contains(ActivityWithCustomConstructor.class.getName())) { 144 return new ActivityWithCustomConstructor(100); 145 } 146 return super.instantiateActivity(cl, className, intent); 147 } 148 } 149 150 @Test launch_callbackSequence()151 public void launch_callbackSequence() { 152 try (ActivityScenario<TranscriptActivity> activityScenario = 153 ActivityScenario.launch(TranscriptActivity.class)) { 154 assertThat(activityScenario).isNotNull(); 155 assertThat(callbacks) 156 .containsExactly( 157 "onCreate", "onStart", "onPostCreate", "onResume", "onWindowFocusChanged true"); 158 } 159 } 160 161 @Test launch_pauseAndResume_callbackSequence()162 public void launch_pauseAndResume_callbackSequence() { 163 try (ActivityScenario<TranscriptActivity> activityScenario = 164 ActivityScenario.launch(TranscriptActivity.class)) { 165 assertThat(activityScenario).isNotNull(); 166 activityScenario.moveToState(State.STARTED); 167 activityScenario.moveToState(State.RESUMED); 168 assertThat(callbacks) 169 .containsExactly( 170 "onCreate", 171 "onStart", 172 "onPostCreate", 173 "onResume", 174 "onWindowFocusChanged true", 175 "onPause", 176 "onResume"); 177 } 178 } 179 180 @Test launch_stopAndResume_callbackSequence()181 public void launch_stopAndResume_callbackSequence() { 182 try (ActivityScenario<TranscriptActivity> activityScenario = 183 ActivityScenario.launch(TranscriptActivity.class)) { 184 assertThat(activityScenario).isNotNull(); 185 activityScenario.moveToState(State.CREATED); 186 activityScenario.moveToState(State.RESUMED); 187 assertThat(callbacks) 188 .containsExactly( 189 "onCreate", 190 "onStart", 191 "onPostCreate", 192 "onResume", 193 "onWindowFocusChanged true", 194 "onPause", 195 "onStop false", 196 "onRestart", 197 "onStart", 198 "onResume"); 199 } 200 } 201 202 @Test launchAlias_createTargetAndCallbackSequence()203 public void launchAlias_createTargetAndCallbackSequence() { 204 Context context = ApplicationProvider.getApplicationContext(); 205 try (ActivityScenario<Activity> activityScenario = 206 ActivityScenario.launch( 207 new Intent() 208 .setClassName( 209 context, "org.robolectric.integrationtests.axt.ActivityScenarioTestAlias"))) { 210 211 assertThat(activityScenario).isNotNull(); 212 activityScenario.onActivity( 213 activity -> assertThat(activity).isInstanceOf(TranscriptActivity.class)); 214 assertThat(callbacks) 215 .containsExactly( 216 "onCreate", "onStart", "onPostCreate", "onResume", "onWindowFocusChanged true"); 217 } 218 } 219 220 @Test launch_lifecycleOwnerActivity()221 public void launch_lifecycleOwnerActivity() { 222 try (ActivityScenario<LifecycleOwnerActivity> activityScenario = 223 ActivityScenario.launch(LifecycleOwnerActivity.class)) { 224 assertThat(activityScenario).isNotNull(); 225 activityScenario.onActivity( 226 activity -> 227 assertThat(activity.getLifecycle().getCurrentState()).isEqualTo(State.RESUMED)); 228 activityScenario.moveToState(State.STARTED); 229 activityScenario.onActivity( 230 activity -> 231 assertThat(activity.getLifecycle().getCurrentState()).isEqualTo(State.STARTED)); 232 activityScenario.moveToState(State.CREATED); 233 activityScenario.onActivity( 234 activity -> 235 assertThat(activity.getLifecycle().getCurrentState()).isEqualTo(State.CREATED)); 236 } 237 } 238 239 @Test recreate_retainFragmentHostingActivity()240 public void recreate_retainFragmentHostingActivity() { 241 Fragment fragment = new Fragment(); 242 fragment.setRetainInstance(true); 243 try (ActivityScenario<LifecycleOwnerActivity> activityScenario = 244 ActivityScenario.launch(LifecycleOwnerActivity.class)) { 245 assertThat(activityScenario).isNotNull(); 246 activityScenario.onActivity( 247 activity -> { 248 activity 249 .getSupportFragmentManager() 250 .beginTransaction() 251 .add(android.R.id.content, fragment) 252 .commitNow(); 253 assertThat(activity.getSupportFragmentManager().findFragmentById(android.R.id.content)) 254 .isSameInstanceAs(fragment); 255 }); 256 activityScenario.recreate(); 257 activityScenario.onActivity( 258 activity -> 259 assertThat( 260 activity.getSupportFragmentManager().findFragmentById(android.R.id.content)) 261 .isSameInstanceAs(fragment)); 262 } 263 } 264 265 @Test recreate_nonRetainFragmentHostingActivity()266 public void recreate_nonRetainFragmentHostingActivity() { 267 Fragment fragment = new Fragment(); 268 fragment.setRetainInstance(false); 269 try (ActivityScenario<LifecycleOwnerActivity> activityScenario = 270 ActivityScenario.launch(LifecycleOwnerActivity.class)) { 271 assertThat(activityScenario).isNotNull(); 272 activityScenario.onActivity( 273 activity -> { 274 activity 275 .getSupportFragmentManager() 276 .beginTransaction() 277 .add(android.R.id.content, fragment) 278 .commitNow(); 279 assertThat(activity.getSupportFragmentManager().findFragmentById(android.R.id.content)) 280 .isSameInstanceAs(fragment); 281 }); 282 activityScenario.recreate(); 283 activityScenario.onActivity( 284 activity -> 285 assertThat( 286 activity.getSupportFragmentManager().findFragmentById(android.R.id.content)) 287 .isNotSameInstanceAs(fragment)); 288 } 289 } 290 291 @Test recreate_isChangingConfigurations()292 public void recreate_isChangingConfigurations() { 293 try (ActivityScenario<TranscriptActivity> activityScenario = 294 ActivityScenario.launch(TranscriptActivity.class)) { 295 activityScenario.recreate(); 296 297 assertThat(callbacks).contains("onStop true"); 298 } 299 } 300 301 @Test setRotation_recreatesActivity()302 public void setRotation_recreatesActivity() { 303 UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); 304 try (ActivityScenario<TranscriptActivity> activityScenario = 305 ActivityScenario.launch(TranscriptActivity.class)) { 306 AtomicReference<Activity> originalActivity = new AtomicReference<>(); 307 activityScenario.onActivity(originalActivity::set); 308 309 uiAutomation.setRotation(UiAutomation.ROTATION_FREEZE_90); 310 311 activityScenario.onActivity( 312 activity -> { 313 assertThat(activity.getResources().getConfiguration().orientation) 314 .isEqualTo(Configuration.ORIENTATION_LANDSCAPE); 315 assertThat(activity).isNotSameInstanceAs(originalActivity); 316 }); 317 } 318 } 319 320 @Test onActivityExceptionPropagated()321 public void onActivityExceptionPropagated() { 322 try (ActivityScenario<TranscriptActivity> activityScenario = 323 ActivityScenario.launch(TranscriptActivity.class)) { 324 assertThrows( 325 IllegalStateException.class, 326 () -> 327 activityScenario.onActivity( 328 activity -> { 329 throw new IllegalStateException("test"); 330 })); 331 } 332 } 333 334 @Test onActivity_runsOnMainLooperThread()335 public void onActivity_runsOnMainLooperThread() { 336 try (ActivityScenario<TranscriptActivity> activityScenario = 337 ActivityScenario.launch(TranscriptActivity.class)) { 338 activityScenario.onActivity( 339 activity -> 340 assertThat(Looper.getMainLooper().getThread()).isEqualTo(Thread.currentThread())); 341 } 342 } 343 344 @Test getCallingActivity_empty()345 public void getCallingActivity_empty() { 346 try (ActivityScenario<TranscriptActivity> activityScenario = 347 ActivityScenario.launch(TranscriptActivity.class)) { 348 activityScenario.onActivity(activity -> assertThat(activity.getCallingActivity()).isNull()); 349 } 350 } 351 352 @Test getCallingActivity_isSet()353 public void getCallingActivity_isSet() { 354 try (ActivityScenario<TranscriptActivity> activityScenario = 355 ActivityScenario.launchActivityForResult(TranscriptActivity.class)) { 356 activityScenario.onActivity( 357 activity -> 358 assertThat(activity.getCallingActivity().getPackageName()) 359 .isEqualTo("org.robolectric.integrationtests.axt")); 360 } 361 } 362 363 @Test 364 @Config(minSdk = P) launchActivityWithCustomConstructor()365 public void launchActivityWithCustomConstructor() { 366 try (ActivityScenario<ActivityWithCustomConstructor> activityScenario = 367 ActivityScenario.launch(ActivityWithCustomConstructor.class)) { 368 assertThat(activityScenario.getState()).isEqualTo(State.RESUMED); 369 activityScenario.onActivity(activity -> assertThat(activity.getIntValue()).isEqualTo(100)); 370 } 371 } 372 } 373