1package com.xtremelabs.robolectric.shadows; 2 3import android.app.Activity; 4import android.app.Dialog; 5import android.appwidget.AppWidgetProvider; 6import android.content.*; 7import android.content.pm.ActivityInfo; 8import android.database.CharArrayBuffer; 9import android.database.ContentObserver; 10import android.database.Cursor; 11import android.database.DataSetObserver; 12import android.database.sqlite.SQLiteCursor; 13import android.net.Uri; 14import android.os.Bundle; 15import android.view.KeyEvent; 16import android.view.View; 17import android.widget.FrameLayout; 18import com.xtremelabs.robolectric.ApplicationResolver; 19import com.xtremelabs.robolectric.R; 20import com.xtremelabs.robolectric.Robolectric; 21import com.xtremelabs.robolectric.WithTestDefaultsRunner; 22import com.xtremelabs.robolectric.util.TestRunnable; 23import com.xtremelabs.robolectric.util.Transcript; 24import org.hamcrest.CoreMatchers; 25import org.junit.Test; 26import org.junit.runner.RunWith; 27 28import java.util.concurrent.atomic.AtomicBoolean; 29 30import static com.xtremelabs.robolectric.Robolectric.shadowOf; 31import static com.xtremelabs.robolectric.util.TestUtil.assertInstanceOf; 32import static com.xtremelabs.robolectric.util.TestUtil.newConfig; 33import static org.hamcrest.CoreMatchers.*; 34import static org.hamcrest.core.StringStartsWith.startsWith; 35import static org.junit.Assert.*; 36 37@RunWith(WithTestDefaultsRunner.class) 38public class ActivityTest { 39 @Test(expected = IllegalStateException.class) 40 public void shouldComplainIfActivityIsDestroyedWithRegisteredBroadcastReceivers() throws Exception { 41 DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); 42 activity.registerReceiver(new AppWidgetProvider(), new IntentFilter()); 43 activity.onDestroy(); 44 } 45 46 @Test 47 public void shouldNotComplainIfActivityIsDestroyedWhileAnotherActivityHasRegisteredBroadcastReceivers() throws Exception { 48 DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); 49 50 DialogLifeCycleActivity activity2 = new DialogLifeCycleActivity(); 51 activity2.registerReceiver(new AppWidgetProvider(), new IntentFilter()); 52 53 activity.onDestroy(); // should not throw exception 54 } 55 56 @Test 57 public void shouldNotRegisterNullBroadcastReceiver() { 58 DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); 59 activity.registerReceiver(null, new IntentFilter()); 60 61 activity.onDestroy(); 62 } 63 64 @Test 65 public void startActivityForResultAndReceiveResult_shouldSendResponsesBackToActivity() throws Exception { 66 final Transcript transcript = new Transcript(); 67 Activity activity = new Activity() { 68 @Override 69 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 70 transcript.add("onActivityResult called with requestCode " + requestCode + ", resultCode " + resultCode + ", intent data " + data.getData()); 71 } 72 }; 73 activity.startActivityForResult(new Intent().setType("audio/*"), 123); 74 activity.startActivityForResult(new Intent().setType("image/*"), 456); 75 76 shadowOf(activity).receiveResult(new Intent().setType("image/*"), Activity.RESULT_OK, 77 new Intent().setData(Uri.parse("content:foo"))); 78 transcript.assertEventsSoFar("onActivityResult called with requestCode 456, resultCode -1, intent data content:foo"); 79 } 80 81 @Test 82 public void startActivityForResultAndReceiveResult_whenNoIntentMatches_shouldThrowException() throws Exception { 83 Activity activity = new Activity() { 84 @Override 85 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 86 throw new IllegalStateException("should not be called"); 87 } 88 }; 89 activity.startActivityForResult(new Intent().setType("audio/*"), 123); 90 activity.startActivityForResult(new Intent().setType("image/*"), 456); 91 92 Intent requestIntent = new Intent().setType("video/*"); 93 try { 94 shadowOf(activity).receiveResult(requestIntent, Activity.RESULT_OK, 95 new Intent().setData(Uri.parse("content:foo"))); 96 fail(); 97 } catch (Exception e) { 98 assertThat(e.getMessage(), startsWith("No intent matches " + requestIntent)); 99 } 100 } 101 102 @Test 103 public void shouldSupportStartActivityForResult() throws Exception { 104 DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); 105 ShadowActivity shadowActivity = Robolectric.shadowOf(activity); 106 Intent intent = new Intent().setClass(activity, DialogLifeCycleActivity.class); 107 assertThat(shadowActivity.getNextStartedActivity(), nullValue()); 108 109 activity.startActivityForResult(intent, 142); 110 111 Intent startedIntent = shadowActivity.getNextStartedActivity(); 112 assertThat(startedIntent, notNullValue()); 113 assertThat(startedIntent, sameInstance(intent)); 114 } 115 116 @Test 117 public void shouldSupportGetStartedActitivitesForResult() throws Exception { 118 DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); 119 ShadowActivity shadowActivity = Robolectric.shadowOf(activity); 120 Intent intent = new Intent().setClass(activity, DialogLifeCycleActivity.class); 121 122 activity.startActivityForResult(intent, 142); 123 124 ShadowActivity.IntentForResult intentForResult = shadowActivity.getNextStartedActivityForResult(); 125 assertThat(intentForResult, notNullValue()); 126 assertThat(shadowActivity.getNextStartedActivityForResult(), nullValue()); 127 assertThat(intentForResult.intent, notNullValue()); 128 assertThat(intentForResult.intent, sameInstance(intent)); 129 assertThat(intentForResult.requestCode, equalTo(142)); 130 } 131 132 @Test 133 public void shouldSupportPeekStartedActitivitesForResult() throws Exception { 134 DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); 135 ShadowActivity shadowActivity = Robolectric.shadowOf(activity); 136 Intent intent = new Intent().setClass(activity, DialogLifeCycleActivity.class); 137 138 activity.startActivityForResult(intent, 142); 139 140 ShadowActivity.IntentForResult intentForResult = shadowActivity.peekNextStartedActivityForResult(); 141 assertThat(intentForResult, notNullValue()); 142 assertThat(shadowActivity.peekNextStartedActivityForResult(), sameInstance(intentForResult)); 143 assertThat(intentForResult.intent, notNullValue()); 144 assertThat(intentForResult.intent, sameInstance(intent)); 145 assertThat(intentForResult.requestCode, equalTo(142)); 146 } 147 148 @Test 149 public void onContentChangedShouldBeCalledAfterContentViewIsSet() throws RuntimeException { 150 final Transcript transcript = new Transcript(); 151 Activity customActivity = new Activity() { 152 @Override 153 public void onContentChanged() { 154 transcript.add("onContentChanged was called; title is \"" + shadowOf(findViewById(R.id.title)).innerText() + "\""); 155 } 156 }; 157 customActivity.setContentView(R.layout.main); 158 transcript.assertEventsSoFar("onContentChanged was called; title is \"Main Layout\""); 159 } 160 161 @Test 162 public void shouldRetrievePackageNameFromTheManifest() throws Exception { 163 Robolectric.application = new ApplicationResolver(newConfig("TestAndroidManifestWithPackageName.xml")).resolveApplication(); 164 assertThat("com.wacka.wa", equalTo(new Activity().getPackageName())); 165 } 166 167 @Test 168 public void shouldRunUiTasksImmediatelyByDefault() throws Exception { 169 TestRunnable runnable = new TestRunnable(); 170 DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); 171 activity.runOnUiThread(runnable); 172 assertTrue(runnable.wasRun); 173 } 174 175 @Test 176 public void shouldQueueUiTasksWhenUiThreadIsPaused() throws Exception { 177 Robolectric.pauseMainLooper(); 178 179 DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); 180 TestRunnable runnable = new TestRunnable(); 181 activity.runOnUiThread(runnable); 182 assertFalse(runnable.wasRun); 183 184 Robolectric.unPauseMainLooper(); 185 assertTrue(runnable.wasRun); 186 } 187 188 @Test 189 public void showDialog_shouldCreatePrepareAndShowDialog() { 190 final DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); 191 final AtomicBoolean dialogWasShown = new AtomicBoolean(false); 192 193 new Dialog(activity) { 194 { 195 activity.dialog = this; 196 } 197 198 @Override 199 public void show() { 200 dialogWasShown.set(true); 201 } 202 }; 203 204 activity.showDialog(1); 205 206 assertTrue(activity.createdDialog); 207 assertTrue(activity.preparedDialog); 208 assertTrue(dialogWasShown.get()); 209 } 210 211 @Test 212 public void showDialog_shouldCreatePrepareAndShowDialogWithBundle() { 213 final DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); 214 final AtomicBoolean dialogWasShown = new AtomicBoolean(false); 215 216 new Dialog(activity) { 217 { 218 activity.dialog = this; 219 } 220 221 @Override 222 public void show() { 223 dialogWasShown.set(true); 224 } 225 }; 226 227 activity.showDialog(1, new Bundle()); 228 229 assertTrue(activity.createdDialog); 230 assertTrue(activity.preparedDialogWithBundle); 231 assertTrue(dialogWasShown.get()); 232 } 233 234 @Test 235 public void showDialog_shouldReuseDialogs() { 236 final DialogCreatingActivity activity = new DialogCreatingActivity(); 237 238 activity.showDialog(1); 239 240 Dialog firstDialog = ShadowDialog.getLatestDialog(); 241 242 activity.showDialog(1); 243 244 final Dialog secondDialog = ShadowDialog.getLatestDialog(); 245 246 assertSame("dialogs should be the same instance", firstDialog, secondDialog); 247 } 248 249 @Test 250 public void showDialog_shouldShowDialog() throws Exception { 251 final DialogCreatingActivity activity = new DialogCreatingActivity(); 252 activity.showDialog(1); 253 Dialog dialog = ShadowDialog.getLatestDialog(); 254 assertTrue(dialog.isShowing()); 255 } 256 257 @Test 258 public void dismissDialog_shouldDismissPreviouslyShownDialog() throws Exception { 259 final DialogCreatingActivity activity = new DialogCreatingActivity(); 260 activity.showDialog(1); 261 activity.dismissDialog(1); 262 Dialog dialog = ShadowDialog.getLatestDialog(); 263 assertFalse(dialog.isShowing()); 264 } 265 266 @Test(expected = IllegalArgumentException.class) 267 public void dismissDialog_shouldThrowExceptionIfDialogWasNotPreviouslyShown() throws Exception { 268 final DialogCreatingActivity activity = new DialogCreatingActivity(); 269 activity.dismissDialog(1); 270 } 271 272 @Test 273 public void removeDialog_shouldCreateDialogAgain() { 274 final DialogCreatingActivity activity = new DialogCreatingActivity(); 275 276 activity.showDialog(1); 277 Dialog firstDialog = ShadowDialog.getLatestDialog(); 278 279 activity.removeDialog(1); 280 assertNull(Robolectric.shadowOf(activity).getDialogById(1)); 281 282 activity.showDialog(1); 283 Dialog secondDialog = ShadowDialog.getLatestDialog(); 284 285 assertNotSame("dialogs should not be the same instance", firstDialog, secondDialog); 286 } 287 288 @Test 289 public void shouldCallOnCreateDialogFromShowDialog() { 290 ActivityWithOnCreateDialog activity = new ActivityWithOnCreateDialog(); 291 activity.showDialog(123); 292 assertTrue(activity.onCreateDialogWasCalled); 293 assertThat(ShadowDialog.getLatestDialog(), CoreMatchers.<Object>notNullValue()); 294 } 295 296 @Test 297 public void shouldCallFinishInOnBackPressed() { 298 Activity activity = new Activity(); 299 activity.onBackPressed(); 300 301 ShadowActivity shadowActivity = shadowOf(activity); 302 assertTrue(shadowActivity.isFinishing()); 303 } 304 305 @Test 306 public void shouldSupportCurrentFocus() { 307 DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); 308 ShadowActivity shadow = shadowOf(activity); 309 310 assertNull(shadow.getCurrentFocus()); 311 View view = new View(activity); 312 shadow.setCurrentFocus(view); 313 assertEquals(view, shadow.getCurrentFocus()); 314 } 315 316 @Test 317 public void shouldSetOrientation() { 318 DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); 319 activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 320 assertThat(activity.getRequestedOrientation(), equalTo(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)); 321 } 322 323 @Test 324 public void retrieveIdOfResource() { 325 Activity activity = new Activity(); 326 327 int id1 = R.string.hello; 328 String string = activity.getString(id1); 329 assertEquals("Hello", string); 330 331 int id = activity.getResources().getIdentifier("hello", "string", "com.xtremelabs.robolectric"); 332 assertTrue(id > 0); 333 334 String hello = activity.getResources().getString(id); 335 assertEquals("Hello", hello); 336 } 337 338 @Test 339 public void getCurrentFocus_shouldBeNullWhenNoFocusRequested() { 340 Activity activity = new Activity(); 341 activity.setContentView(R.layout.main); 342 343 View focusedView = shadowOf(activity).getCurrentFocus(); 344 assertNull(focusedView); 345 } 346 347 @Test 348 public void getCurrentFocus_shouldReturnSubViewAfterFocusedRequest() { 349 Activity activity = new Activity(); 350 activity.setContentView(R.layout.main); 351 352 View view = activity.findViewById(R.id.button); 353 view.requestFocus(); 354 355 View focusedView = shadowOf(activity).getCurrentFocus(); 356 assertEquals(R.id.button, focusedView.getId()); 357 } 358 359 @Test 360 public void retrieveIdOfNonExistingResource() { 361 Activity activity = new Activity(); 362 363 int id = activity.getResources().getIdentifier("just_alot_of_crap", "string", "com.xtremelabs.robolectric"); 364 assertTrue(id == 0); 365 } 366 367 @Test 368 public void shouldSetContentViewWithFrameLayoutAsParent() throws Exception { 369 Activity activity = new Activity(); 370 activity.setContentView(R.layout.toplevel_merge); 371 372 View contentView = shadowOf(activity).getContentView(); 373 assertInstanceOf(FrameLayout.class, contentView); 374 assertThat(((FrameLayout) contentView).getChildCount(), equalTo(2)); 375 } 376 377 @Test 378 public void onKeyUp_recordsThatItWasCalled() throws Exception { 379 Activity activity = new Activity(); 380 boolean consumed = activity.onKeyUp(KeyEvent.KEYCODE_0, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0)); 381 382 assertFalse(consumed); 383 assertTrue(shadowOf(activity).onKeyUpWasCalled()); 384 385 shadowOf(activity).resetKeyUpWasCalled(); 386 assertFalse(shadowOf(activity).onKeyUpWasCalled()); 387 } 388 389 @Test 390 public void onKeyUp_callsOnBackPressedWhichFinishesTheActivity() throws Exception { 391 Activity activity = new Activity(); 392 boolean consumed = activity.onKeyUp(KeyEvent.KEYCODE_BACK, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)); 393 394 assertTrue(consumed); 395 assertTrue(shadowOf(activity).onKeyUpWasCalled()); 396 assertTrue(activity.isFinishing()); 397 } 398 399 @Test 400 public void shouldGiveSharedPreferences() throws Exception { 401 Activity activity = new Activity(); 402 SharedPreferences preferences = activity.getPreferences(Context.MODE_PRIVATE); 403 assertNotNull(preferences); 404 preferences.edit().putString("foo", "bar").commit(); 405 assertThat(activity.getPreferences(Context.MODE_PRIVATE).getString("foo", null), equalTo("bar")); 406 } 407 408 @Test 409 public void shouldFindContentViewContainer() throws Exception { 410 Activity activity = new Activity(); 411 View contentView = new View(activity); 412 activity.setContentView(contentView); 413 414 FrameLayout contentViewContainer = (FrameLayout) activity.findViewById(android.R.id.content); 415 assertThat(contentViewContainer.getChildAt(0), is(contentView)); 416 } 417 418 @Test 419 public void createGoesThroughFullLifeCycle() throws Exception { 420 TestActivity activity = new TestActivity(); 421 422 shadowOf(activity).create(); 423 424 activity.transcript.assertEventsSoFar( 425 "onCreate", 426 "onStart", 427 "onPostCreate", 428 "onResume" 429 ); 430 } 431 432 433 @Test 434 public void recreateGoesThroughFullLifeCycle() throws Exception { 435 TestActivity activity = new TestActivity(); 436 437 ShadowActivity shadow = shadowOf(activity); 438 shadow.recreate(); 439 440 activity.transcript.assertEventsSoFar( 441 "onSaveInstanceState", 442 "onPause", 443 "onStop", 444 "onRetainNonConfigurationInstance", 445 "onDestroy", 446 "onCreate", 447 "onStart", 448 "onRestoreInstanceState", 449 "onResume" 450 ); 451 452 Integer storedValue = (Integer) activity.getLastNonConfigurationInstance(); 453 assertEquals(5, storedValue.intValue()); 454 } 455 456 @Test 457 public void startAndStopManagingCursorTracksCursors() throws Exception { 458 TestActivity activity = new TestActivity(); 459 460 ShadowActivity shadow = shadowOf(activity); 461 462 assertThat( shadow.getManagedCursors(), notNullValue() ); 463 assertThat( shadow.getManagedCursors().size(), equalTo(0) ); 464 465 Cursor c = Robolectric.newInstanceOf(SQLiteCursor.class); 466 activity.startManagingCursor(c); 467 468 assertThat( shadow.getManagedCursors(), notNullValue() ); 469 assertThat( shadow.getManagedCursors().size(), equalTo(1) ); 470 assertThat( shadow.getManagedCursors().get(0), sameInstance(c) ); 471 472 activity.stopManagingCursor(c); 473 474 assertThat( shadow.getManagedCursors(), notNullValue() ); 475 assertThat( shadow.getManagedCursors().size(), equalTo(0) ); 476 } 477 478 private static class TestActivity extends Activity { 479 Transcript transcript = new Transcript(); 480 481 private boolean isRecreating = false; 482 483 @Override 484 public void onSaveInstanceState(Bundle outState) { 485 isRecreating = true; 486 transcript.add("onSaveInstanceState"); 487 outState.putString("TestActivityKey", "TestActivityValue"); 488 super.onSaveInstanceState(outState); 489 } 490 491 @Override 492 public void onRestoreInstanceState(Bundle savedInstanceState) { 493 transcript.add("onRestoreInstanceState"); 494 assertTrue(savedInstanceState.containsKey("TestActivityKey")); 495 assertEquals("TestActivityValue", savedInstanceState.getString("TestActivityKey")); 496 super.onRestoreInstanceState(savedInstanceState); 497 } 498 499 @Override 500 public Object onRetainNonConfigurationInstance() { 501 transcript.add("onRetainNonConfigurationInstance"); 502 return new Integer(5); 503 } 504 505 @Override 506 public void onPause() { 507 transcript.add("onPause"); 508 super.onPause(); 509 } 510 511 @Override 512 public void onDestroy() { 513 transcript.add("onDestroy"); 514 super.onDestroy(); 515 } 516 517 @Override 518 public void onCreate(Bundle savedInstanceState) { 519 transcript.add("onCreate"); 520 521 if( isRecreating ) { 522 assertTrue(savedInstanceState.containsKey("TestActivityKey")); 523 assertEquals("TestActivityValue", savedInstanceState.getString("TestActivityKey")); 524 } 525 526 super.onCreate(savedInstanceState); 527 } 528 529 @Override 530 public void onStart() { 531 transcript.add("onStart"); 532 super.onStart(); 533 } 534 535 @Override 536 public void onPostCreate(Bundle savedInstanceState) { 537 transcript.add("onPostCreate"); 538 super.onPostCreate(savedInstanceState); 539 } 540 541 @Override 542 public void onStop() { 543 transcript.add("onStop"); 544 super.onStop(); 545 } 546 547 @Override 548 public void onResume() { 549 transcript.add("onResume"); 550 super.onResume(); 551 } 552 } 553 554 private static class DialogCreatingActivity extends Activity { 555 @Override 556 protected Dialog onCreateDialog(int id) { 557 return new Dialog(this); 558 } 559 } 560 561 private static class DialogLifeCycleActivity extends Activity { 562 public boolean createdDialog = false; 563 public boolean preparedDialog = false; 564 public boolean preparedDialogWithBundle = false; 565 public Dialog dialog = null; 566 567 @Override 568 protected void onDestroy() { 569 super.onDestroy(); 570 } 571 572 @Override 573 protected Dialog onCreateDialog(int id) { 574 createdDialog = true; 575 return dialog; 576 } 577 578 @Override 579 protected void onPrepareDialog(int id, Dialog dialog) { 580 preparedDialog = true; 581 } 582 583 @Override 584 protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) { 585 preparedDialogWithBundle = true; 586 } 587 } 588 589 private static class ActivityWithOnCreateDialog extends Activity { 590 boolean onCreateDialogWasCalled = false; 591 592 @Override 593 protected Dialog onCreateDialog(int id) { 594 onCreateDialogWasCalled = true; 595 return new Dialog(null); 596 } 597 } 598} 599