1package org.robolectric.android.controller; 2 3import static org.assertj.core.api.Assertions.assertThat; 4import static org.junit.Assert.assertEquals; 5import static org.robolectric.Shadows.shadowOf; 6 7import android.app.Activity; 8import android.app.Fragment; 9import android.content.ComponentName; 10import android.content.Intent; 11import android.content.res.Configuration; 12import android.os.Build; 13import android.os.Bundle; 14import android.os.Handler; 15import android.os.Looper; 16import android.view.ContextThemeWrapper; 17import android.view.Window; 18import android.widget.LinearLayout; 19import java.util.ArrayList; 20import java.util.List; 21import org.junit.Before; 22import org.junit.Test; 23import org.junit.runner.RunWith; 24import org.robolectric.R; 25import org.robolectric.Robolectric; 26import org.robolectric.RobolectricTestRunner; 27import org.robolectric.RuntimeEnvironment; 28import org.robolectric.annotation.Config; 29import org.robolectric.shadows.ShadowLooper; 30import org.robolectric.util.Scheduler; 31import org.robolectric.util.TestRunnable; 32 33@RunWith(RobolectricTestRunner.class) 34public class ActivityControllerTest { 35 private static final List<String> transcript = new ArrayList<>(); 36 private final ComponentName componentName = new ComponentName("org.robolectric", MyActivity.class.getName()); 37 private final ActivityController<MyActivity> controller = Robolectric.buildActivity(MyActivity.class); 38 39 @Before 40 public void setUp() throws Exception { 41 transcript.clear(); 42 } 43 44 @Test 45 public void canCreateActivityNotListedInManifest() { 46 Activity activity = Robolectric.setupActivity(Activity.class); 47 assertThat(activity).isNotNull(); 48 assertThat(activity.getThemeResId()).isEqualTo(R.style.Theme_Robolectric); 49 } 50 51 public static class TestDelayedPostActivity extends Activity { 52 TestRunnable r1 = new TestRunnable(); 53 TestRunnable r2 = new TestRunnable(); 54 @Override 55 protected void onCreate(Bundle savedInstanceState) { 56 super.onCreate(savedInstanceState); 57 Handler h = new Handler(); 58 h.post(r1); 59 h.postDelayed(r2, 60000); 60 } 61 } 62 63 @Test 64 public void pendingTasks_areRunEagerly_whenActivityIsStarted_andSchedulerUnPaused() { 65 final Scheduler s = Robolectric.getForegroundThreadScheduler(); 66 final long startTime = s.getCurrentTime(); 67 TestDelayedPostActivity activity = Robolectric.setupActivity(TestDelayedPostActivity.class); 68 assertThat(activity.r1.wasRun).as("immediate task").isTrue(); 69 assertThat(s.getCurrentTime()).as("currentTime").isEqualTo(startTime); 70 } 71 72 @Test 73 public void delayedTasks_areNotRunEagerly_whenActivityIsStarted_andSchedulerUnPaused() { 74 // Regression test for issue #1509 75 final Scheduler s = Robolectric.getForegroundThreadScheduler(); 76 final long startTime = s.getCurrentTime(); 77 TestDelayedPostActivity activity = Robolectric.setupActivity(TestDelayedPostActivity.class); 78 assertThat(activity.r2.wasRun).as("before flush").isFalse(); 79 assertThat(s.getCurrentTime()).as("currentTime before flush").isEqualTo(startTime); 80 s.advanceToLastPostedRunnable(); 81 assertThat(activity.r2.wasRun).as("after flush").isTrue(); 82 assertThat(s.getCurrentTime()).as("currentTime after flush").isEqualTo(startTime + 60000); 83 } 84 85 @Test 86 public void shouldSetIntent() throws Exception { 87 MyActivity myActivity = controller.create().get(); 88 assertThat(myActivity.getIntent()).isNotNull(); 89 assertThat(myActivity.getIntent().getComponent()).isEqualTo(componentName); 90 } 91 92 @Test 93 public void shouldSetIntentComponentWithCustomIntentWithoutComponentSet() throws Exception { 94 MyActivity myActivity = Robolectric.buildActivity(MyActivity.class, new Intent(Intent.ACTION_VIEW)).create().get(); 95 assertThat(myActivity.getIntent().getAction()).isEqualTo(Intent.ACTION_VIEW); 96 assertThat(myActivity.getIntent().getComponent()).isEqualTo(componentName); 97 } 98 99 @Test 100 public void shouldSetIntentForGivenActivityInstance() throws Exception { 101 ActivityController<MyActivity> activityController = ActivityController.of(new MyActivity()).create(); 102 assertThat(activityController.get().getIntent()).isNotNull(); 103 } 104 105 @Test 106 public void whenLooperIsNotPaused_shouldCreateWithMainLooperPaused() throws Exception { 107 ShadowLooper.unPauseMainLooper(); 108 controller.create(); 109 assertThat(shadowOf(Looper.getMainLooper()).isPaused()).isFalse(); 110 assertThat(transcript).contains("finishedOnCreate", "onCreate"); 111 } 112 113 @Test 114 public void whenLooperIsAlreadyPaused_shouldCreateWithMainLooperPaused() throws Exception { 115 ShadowLooper.pauseMainLooper(); 116 controller.create(); 117 assertThat(shadowOf(Looper.getMainLooper()).isPaused()).isTrue(); 118 assertThat(transcript).contains("finishedOnCreate"); 119 120 ShadowLooper.unPauseMainLooper(); 121 assertThat(transcript).contains("onCreate"); 122 } 123 124 @Test 125 public void visible_addsTheDecorViewToTheWindowManager() { 126 controller.create().visible(); 127 assertEquals(controller.get().getWindow().getDecorView().getParent().getClass().getName(), "android.view.ViewRootImpl"); 128 } 129 130 @Test 131 public void start_callsPerformStartWhilePaused() { 132 controller.create().start(); 133 assertThat(transcript).contains("finishedOnStart", "onStart"); 134 } 135 136 @Test 137 public void stop_callsPerformStopWhilePaused() { 138 controller.create().start().stop(); 139 assertThat(transcript).contains("finishedOnStop", "onStop"); 140 } 141 142 @Test 143 public void restart_callsPerformRestartWhilePaused() { 144 controller.create().start().stop().restart(); 145 assertThat(transcript).contains("finishedOnRestart", "onRestart"); 146 } 147 148 @Test 149 public void pause_callsPerformPauseWhilePaused() { 150 controller.create().pause(); 151 assertThat(transcript).contains("finishedOnPause", "onPause"); 152 } 153 154 @Test 155 public void resume_callsPerformResumeWhilePaused() { 156 controller.create().start().resume(); 157 assertThat(transcript).contains("finishedOnResume", "onResume"); 158 } 159 160 @Test 161 public void destroy_callsPerformDestroyWhilePaused() { 162 controller.create().destroy(); 163 assertThat(transcript).contains("finishedOnDestroy", "onDestroy"); 164 } 165 166 @Test 167 public void postCreate_callsOnPostCreateWhilePaused() { 168 controller.create().postCreate(new Bundle()); 169 assertThat(transcript).contains("finishedOnPostCreate", "onPostCreate"); 170 } 171 172 @Test 173 public void postResume_callsOnPostResumeWhilePaused() { 174 controller.create().postResume(); 175 assertThat(transcript).contains("finishedOnPostResume", "onPostResume"); 176 } 177 178 @Test 179 public void restoreInstanceState_callsPerformRestoreInstanceStateWhilePaused() { 180 controller.create().restoreInstanceState(new Bundle()); 181 assertThat(transcript).contains("finishedOnRestoreInstanceState", "onRestoreInstanceState"); 182 } 183 184 @Test 185 public void newIntent_callsOnNewIntentWhilePaused() { 186 controller.create().newIntent(new Intent(Intent.ACTION_VIEW)); 187 assertThat(transcript).contains("finishedOnNewIntent", "onNewIntent"); 188 } 189 190 @Test 191 public void userLeaving_callsPerformUserLeavingWhilePaused() { 192 controller.create().userLeaving(); 193 assertThat(transcript).contains("finishedOnUserLeaveHint", "onUserLeaveHint"); 194 } 195 196 @Test 197 public void setup_callsLifecycleMethodsAndMakesVisible() { 198 controller.setup(); 199 assertThat(transcript).contains("onCreate", "onStart", "onPostCreate", "onResume", "onPostResume"); 200 assertEquals(controller.get().getWindow().getDecorView().getParent().getClass().getName(), "android.view.ViewRootImpl"); 201 } 202 203 @Test 204 public void setupWithBundle_callsLifecycleMethodsAndMakesVisible() { 205 controller.setup(new Bundle()); 206 assertThat(transcript).contains("onCreate", "onStart", "onRestoreInstanceState", "onPostCreate", "onResume", "onPostResume"); 207 assertEquals(controller.get().getWindow().getDecorView().getParent().getClass().getName(), "android.view.ViewRootImpl"); 208 } 209 210 @Test 211 @Config(sdk = Build.VERSION_CODES.KITKAT) 212 public void attach_shouldWorkWithAPI19() { 213 MyActivity activity = Robolectric.buildActivity(MyActivity.class).create().get(); 214 assertThat(activity).isNotNull(); 215 } 216 217 @Test 218 public void configurationChange_callsLifecycleMethodsAndAppliesConfig() { 219 Configuration config = new Configuration(RuntimeEnvironment.application.getResources().getConfiguration()); 220 final float newFontScale = config.fontScale *= 2; 221 222 controller.setup(); 223 transcript.clear(); 224 controller.configurationChange(config); 225 assertThat(transcript).contains("onPause", "onStop", "onDestroy", "onCreate", "onStart", "onRestoreInstanceState", "onPostCreate", "onResume", "onPostResume"); 226 assertThat(controller.get().getResources().getConfiguration().fontScale).isEqualTo(newFontScale); 227 } 228 229 @Test 230 public void configurationChange_callsOnConfigurationChangedAndAppliesConfigWhenAllManaged() { 231 Configuration config = new Configuration(RuntimeEnvironment.application.getResources().getConfiguration()); 232 final float newFontScale = config.fontScale *= 2; 233 234 ActivityController<ConfigAwareActivity> configController = 235 Robolectric.buildActivity(ConfigAwareActivity.class).setup(); 236 transcript.clear(); 237 configController.configurationChange(config); 238 assertThat(transcript).contains("onConfigurationChanged"); 239 assertThat(configController.get().getResources().getConfiguration().fontScale).isEqualTo(newFontScale); 240 } 241 242 @Test 243 public void configurationChange_callsLifecycleMethodsAndAppliesConfigWhenAnyNonManaged() { 244 Configuration config = new Configuration(RuntimeEnvironment.application.getResources().getConfiguration()); 245 final float newFontScale = config.fontScale *= 2; 246 final int newOrientation = config.orientation = (config.orientation + 1) % 3; 247 248 ActivityController<ConfigAwareActivity> configController = 249 Robolectric.buildActivity(ConfigAwareActivity.class).setup(); 250 transcript.clear(); 251 configController.configurationChange(config); 252 assertThat(transcript).contains("onPause", "onStop", "onDestroy", "onCreate", "onStart", "onResume"); 253 assertThat(configController.get().getResources().getConfiguration().fontScale).isEqualTo(newFontScale); 254 assertThat(configController.get().getResources().getConfiguration().orientation).isEqualTo(newOrientation); 255 } 256 257 @Test 258 @Config(qualifiers = "land") 259 public void noArgsConfigurationChange_appliesChangedSystemConfiguration() throws Exception { 260 ActivityController<ConfigAwareActivity> configController = 261 Robolectric.buildActivity(ConfigAwareActivity.class).setup(); 262 RuntimeEnvironment.setQualifiers("port"); 263 configController.configurationChange(); 264 assertThat(configController.get().newConfig.orientation) 265 .isEqualTo(Configuration.ORIENTATION_PORTRAIT); 266 } 267 268 @Test 269 @Config(qualifiers = "land") 270 public void configurationChange_restoresTheme() { 271 Configuration config = new Configuration(RuntimeEnvironment.application.getResources().getConfiguration()); 272 config.orientation = Configuration.ORIENTATION_PORTRAIT; 273 274 controller.get().setTheme(android.R.style.Theme_Black); 275 controller.setup(); 276 transcript.clear(); 277 controller.configurationChange(config); 278 int restoredTheme = shadowOf((ContextThemeWrapper) controller.get()).callGetThemeResId(); 279 assertThat(restoredTheme).isEqualTo(android.R.style.Theme_Black); 280 } 281 282 @Test 283 @Config(qualifiers = "land") 284 public void configurationChange_reattachesRetainedFragments() { 285 Configuration config = new Configuration(RuntimeEnvironment.application.getResources().getConfiguration()); 286 config.orientation = Configuration.ORIENTATION_PORTRAIT; 287 288 ActivityController<NonConfigStateActivity> configController = 289 Robolectric.buildActivity(NonConfigStateActivity.class).setup(); 290 NonConfigStateActivity activity = configController.get(); 291 Fragment retainedFragment = activity.retainedFragment; 292 Fragment otherFragment = activity.nonRetainedFragment; 293 configController.configurationChange(config); 294 activity = configController.get(); 295 296 assertThat(activity.retainedFragment).isNotNull(); 297 assertThat(activity.retainedFragment).isSameAs(retainedFragment); 298 assertThat(activity.nonRetainedFragment).isNotNull(); 299 assertThat(activity.nonRetainedFragment).isNotSameAs(otherFragment); 300 } 301 302 public static class MyActivity extends Activity { 303 @Override 304 protected void onRestoreInstanceState(Bundle savedInstanceState) { 305 super.onRestoreInstanceState(savedInstanceState); 306 transcribeWhilePaused("onRestoreInstanceState"); 307 transcript.add("finishedOnRestoreInstanceState"); 308 } 309 310 @Override 311 protected void onCreate(Bundle savedInstanceState) { 312 super.onCreate(savedInstanceState); 313 getWindow().requestFeature(Window.FEATURE_ACTION_BAR); 314 setContentView(new LinearLayout(RuntimeEnvironment.application)); 315 transcribeWhilePaused("onCreate"); 316 transcript.add("finishedOnCreate"); 317 } 318 319 @Override 320 protected void onPostCreate(Bundle savedInstanceState) { 321 super.onPostCreate(savedInstanceState); 322 transcribeWhilePaused("onPostCreate"); 323 transcript.add("finishedOnPostCreate"); 324 } 325 326 @Override 327 protected void onPostResume() { 328 super.onPostResume(); 329 transcribeWhilePaused("onPostResume"); 330 transcript.add("finishedOnPostResume"); 331 } 332 333 @Override 334 protected void onDestroy() { 335 super.onDestroy(); 336 transcribeWhilePaused("onDestroy"); 337 transcript.add("finishedOnDestroy"); 338 } 339 340 @Override 341 protected void onStart() { 342 super.onStart(); 343 transcribeWhilePaused("onStart"); 344 transcript.add("finishedOnStart"); 345 } 346 347 @Override 348 protected void onStop() { 349 super.onStop(); 350 transcribeWhilePaused("onStop"); 351 transcript.add("finishedOnStop"); 352 } 353 354 @Override 355 protected void onResume() { 356 super.onResume(); 357 transcribeWhilePaused("onResume"); 358 transcript.add("finishedOnResume"); 359 } 360 361 @Override 362 protected void onRestart() { 363 super.onRestart(); 364 transcribeWhilePaused("onRestart"); 365 transcript.add("finishedOnRestart"); 366 } 367 368 @Override 369 protected void onPause() { 370 super.onPause(); 371 transcribeWhilePaused("onPause"); 372 transcript.add("finishedOnPause"); 373 } 374 375 @Override 376 protected void onNewIntent(Intent intent) { 377 super.onNewIntent(intent); 378 transcribeWhilePaused("onNewIntent"); 379 transcript.add("finishedOnNewIntent"); 380 } 381 382 @Override 383 protected void onUserLeaveHint() { 384 super.onUserLeaveHint(); 385 transcribeWhilePaused("onUserLeaveHint"); 386 transcript.add("finishedOnUserLeaveHint"); 387 } 388 389 @Override 390 public void onConfigurationChanged(Configuration newConfig) { 391 super.onConfigurationChanged(newConfig); 392 transcribeWhilePaused("onConfigurationChanged"); 393 transcript.add("finishedOnConfigurationChanged"); 394 } 395 396 private void transcribeWhilePaused(final String event) { 397 runOnUiThread(new Runnable() { 398 @Override public void run() { 399 transcript.add(event); 400 } 401 }); 402 } 403 } 404 405 public static class ConfigAwareActivity extends MyActivity { 406 407 Configuration newConfig; 408 409 @Override 410 protected void onCreate(Bundle savedInstanceState) { 411 super.onCreate(savedInstanceState); 412 if (savedInstanceState != null) { 413 assertThat(savedInstanceState.getSerializable("test")).isNotNull(); 414 } 415 } 416 417 @Override 418 public void onSaveInstanceState(Bundle outState) { 419 super.onSaveInstanceState(outState); 420 outState.putSerializable("test", new Exception()); 421 } 422 423 @Override 424 public void onConfigurationChanged(Configuration newConfig) { 425 this.newConfig = new Configuration(newConfig); 426 super.onConfigurationChanged(newConfig); 427 } 428 } 429 430 public static final class NonConfigStateActivity extends Activity { 431 Fragment retainedFragment; 432 Fragment nonRetainedFragment; 433 434 @Override 435 protected void onCreate(Bundle savedInstanceState) { 436 super.onCreate(savedInstanceState); 437 if (savedInstanceState == null) { 438 retainedFragment = new Fragment(); 439 retainedFragment.setRetainInstance(true); 440 nonRetainedFragment = new Fragment(); 441 getFragmentManager().beginTransaction() 442 .add(android.R.id.content, retainedFragment, "retained") 443 .add(android.R.id.content, nonRetainedFragment, "non-retained") 444 .commit(); 445 } else { 446 retainedFragment = getFragmentManager().findFragmentByTag("retained"); 447 nonRetainedFragment = getFragmentManager().findFragmentByTag("non-retained"); 448 } 449 } 450 } 451} 452