package com.xtremelabs.robolectric.shadows; import android.app.Activity; import android.app.Dialog; import android.appwidget.AppWidgetProvider; import android.content.*; import android.content.pm.ActivityInfo; import android.database.CharArrayBuffer; import android.database.ContentObserver; import android.database.Cursor; import android.database.DataSetObserver; import android.database.sqlite.SQLiteCursor; import android.net.Uri; import android.os.Bundle; import android.view.KeyEvent; import android.view.View; import android.widget.FrameLayout; import com.xtremelabs.robolectric.ApplicationResolver; import com.xtremelabs.robolectric.R; import com.xtremelabs.robolectric.Robolectric; import com.xtremelabs.robolectric.WithTestDefaultsRunner; import com.xtremelabs.robolectric.util.TestRunnable; import com.xtremelabs.robolectric.util.Transcript; import org.hamcrest.CoreMatchers; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.atomic.AtomicBoolean; import static com.xtremelabs.robolectric.Robolectric.shadowOf; import static com.xtremelabs.robolectric.util.TestUtil.assertInstanceOf; import static com.xtremelabs.robolectric.util.TestUtil.newConfig; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.core.StringStartsWith.startsWith; import static org.junit.Assert.*; @RunWith(WithTestDefaultsRunner.class) public class ActivityTest { @Test(expected = IllegalStateException.class) public void shouldComplainIfActivityIsDestroyedWithRegisteredBroadcastReceivers() throws Exception { DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); activity.registerReceiver(new AppWidgetProvider(), new IntentFilter()); activity.onDestroy(); } @Test public void shouldNotComplainIfActivityIsDestroyedWhileAnotherActivityHasRegisteredBroadcastReceivers() throws Exception { DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); DialogLifeCycleActivity activity2 = new DialogLifeCycleActivity(); activity2.registerReceiver(new AppWidgetProvider(), new IntentFilter()); activity.onDestroy(); // should not throw exception } @Test public void shouldNotRegisterNullBroadcastReceiver() { DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); activity.registerReceiver(null, new IntentFilter()); activity.onDestroy(); } @Test public void startActivityForResultAndReceiveResult_shouldSendResponsesBackToActivity() throws Exception { final Transcript transcript = new Transcript(); Activity activity = new Activity() { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { transcript.add("onActivityResult called with requestCode " + requestCode + ", resultCode " + resultCode + ", intent data " + data.getData()); } }; activity.startActivityForResult(new Intent().setType("audio/*"), 123); activity.startActivityForResult(new Intent().setType("image/*"), 456); shadowOf(activity).receiveResult(new Intent().setType("image/*"), Activity.RESULT_OK, new Intent().setData(Uri.parse("content:foo"))); transcript.assertEventsSoFar("onActivityResult called with requestCode 456, resultCode -1, intent data content:foo"); } @Test public void startActivityForResultAndReceiveResult_whenNoIntentMatches_shouldThrowException() throws Exception { Activity activity = new Activity() { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { throw new IllegalStateException("should not be called"); } }; activity.startActivityForResult(new Intent().setType("audio/*"), 123); activity.startActivityForResult(new Intent().setType("image/*"), 456); Intent requestIntent = new Intent().setType("video/*"); try { shadowOf(activity).receiveResult(requestIntent, Activity.RESULT_OK, new Intent().setData(Uri.parse("content:foo"))); fail(); } catch (Exception e) { assertThat(e.getMessage(), startsWith("No intent matches " + requestIntent)); } } @Test public void shouldSupportStartActivityForResult() throws Exception { DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); ShadowActivity shadowActivity = Robolectric.shadowOf(activity); Intent intent = new Intent().setClass(activity, DialogLifeCycleActivity.class); assertThat(shadowActivity.getNextStartedActivity(), nullValue()); activity.startActivityForResult(intent, 142); Intent startedIntent = shadowActivity.getNextStartedActivity(); assertThat(startedIntent, notNullValue()); assertThat(startedIntent, sameInstance(intent)); } @Test public void shouldSupportGetStartedActitivitesForResult() throws Exception { DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); ShadowActivity shadowActivity = Robolectric.shadowOf(activity); Intent intent = new Intent().setClass(activity, DialogLifeCycleActivity.class); activity.startActivityForResult(intent, 142); ShadowActivity.IntentForResult intentForResult = shadowActivity.getNextStartedActivityForResult(); assertThat(intentForResult, notNullValue()); assertThat(shadowActivity.getNextStartedActivityForResult(), nullValue()); assertThat(intentForResult.intent, notNullValue()); assertThat(intentForResult.intent, sameInstance(intent)); assertThat(intentForResult.requestCode, equalTo(142)); } @Test public void shouldSupportPeekStartedActitivitesForResult() throws Exception { DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); ShadowActivity shadowActivity = Robolectric.shadowOf(activity); Intent intent = new Intent().setClass(activity, DialogLifeCycleActivity.class); activity.startActivityForResult(intent, 142); ShadowActivity.IntentForResult intentForResult = shadowActivity.peekNextStartedActivityForResult(); assertThat(intentForResult, notNullValue()); assertThat(shadowActivity.peekNextStartedActivityForResult(), sameInstance(intentForResult)); assertThat(intentForResult.intent, notNullValue()); assertThat(intentForResult.intent, sameInstance(intent)); assertThat(intentForResult.requestCode, equalTo(142)); } @Test public void onContentChangedShouldBeCalledAfterContentViewIsSet() throws RuntimeException { final Transcript transcript = new Transcript(); Activity customActivity = new Activity() { @Override public void onContentChanged() { transcript.add("onContentChanged was called; title is \"" + shadowOf(findViewById(R.id.title)).innerText() + "\""); } }; customActivity.setContentView(R.layout.main); transcript.assertEventsSoFar("onContentChanged was called; title is \"Main Layout\""); } @Test public void shouldRetrievePackageNameFromTheManifest() throws Exception { Robolectric.application = new ApplicationResolver(newConfig("TestAndroidManifestWithPackageName.xml")).resolveApplication(); assertThat("com.wacka.wa", equalTo(new Activity().getPackageName())); } @Test public void shouldRunUiTasksImmediatelyByDefault() throws Exception { TestRunnable runnable = new TestRunnable(); DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); activity.runOnUiThread(runnable); assertTrue(runnable.wasRun); } @Test public void shouldQueueUiTasksWhenUiThreadIsPaused() throws Exception { Robolectric.pauseMainLooper(); DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); TestRunnable runnable = new TestRunnable(); activity.runOnUiThread(runnable); assertFalse(runnable.wasRun); Robolectric.unPauseMainLooper(); assertTrue(runnable.wasRun); } @Test public void showDialog_shouldCreatePrepareAndShowDialog() { final DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); final AtomicBoolean dialogWasShown = new AtomicBoolean(false); new Dialog(activity) { { activity.dialog = this; } @Override public void show() { dialogWasShown.set(true); } }; activity.showDialog(1); assertTrue(activity.createdDialog); assertTrue(activity.preparedDialog); assertTrue(dialogWasShown.get()); } @Test public void showDialog_shouldCreatePrepareAndShowDialogWithBundle() { final DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); final AtomicBoolean dialogWasShown = new AtomicBoolean(false); new Dialog(activity) { { activity.dialog = this; } @Override public void show() { dialogWasShown.set(true); } }; activity.showDialog(1, new Bundle()); assertTrue(activity.createdDialog); assertTrue(activity.preparedDialogWithBundle); assertTrue(dialogWasShown.get()); } @Test public void showDialog_shouldReuseDialogs() { final DialogCreatingActivity activity = new DialogCreatingActivity(); activity.showDialog(1); Dialog firstDialog = ShadowDialog.getLatestDialog(); activity.showDialog(1); final Dialog secondDialog = ShadowDialog.getLatestDialog(); assertSame("dialogs should be the same instance", firstDialog, secondDialog); } @Test public void showDialog_shouldShowDialog() throws Exception { final DialogCreatingActivity activity = new DialogCreatingActivity(); activity.showDialog(1); Dialog dialog = ShadowDialog.getLatestDialog(); assertTrue(dialog.isShowing()); } @Test public void dismissDialog_shouldDismissPreviouslyShownDialog() throws Exception { final DialogCreatingActivity activity = new DialogCreatingActivity(); activity.showDialog(1); activity.dismissDialog(1); Dialog dialog = ShadowDialog.getLatestDialog(); assertFalse(dialog.isShowing()); } @Test(expected = IllegalArgumentException.class) public void dismissDialog_shouldThrowExceptionIfDialogWasNotPreviouslyShown() throws Exception { final DialogCreatingActivity activity = new DialogCreatingActivity(); activity.dismissDialog(1); } @Test public void removeDialog_shouldCreateDialogAgain() { final DialogCreatingActivity activity = new DialogCreatingActivity(); activity.showDialog(1); Dialog firstDialog = ShadowDialog.getLatestDialog(); activity.removeDialog(1); assertNull(Robolectric.shadowOf(activity).getDialogById(1)); activity.showDialog(1); Dialog secondDialog = ShadowDialog.getLatestDialog(); assertNotSame("dialogs should not be the same instance", firstDialog, secondDialog); } @Test public void shouldCallOnCreateDialogFromShowDialog() { ActivityWithOnCreateDialog activity = new ActivityWithOnCreateDialog(); activity.showDialog(123); assertTrue(activity.onCreateDialogWasCalled); assertThat(ShadowDialog.getLatestDialog(), CoreMatchers.notNullValue()); } @Test public void shouldCallFinishInOnBackPressed() { Activity activity = new Activity(); activity.onBackPressed(); ShadowActivity shadowActivity = shadowOf(activity); assertTrue(shadowActivity.isFinishing()); } @Test public void shouldSupportCurrentFocus() { DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); ShadowActivity shadow = shadowOf(activity); assertNull(shadow.getCurrentFocus()); View view = new View(activity); shadow.setCurrentFocus(view); assertEquals(view, shadow.getCurrentFocus()); } @Test public void shouldSetOrientation() { DialogLifeCycleActivity activity = new DialogLifeCycleActivity(); activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); assertThat(activity.getRequestedOrientation(), equalTo(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)); } @Test public void retrieveIdOfResource() { Activity activity = new Activity(); int id1 = R.string.hello; String string = activity.getString(id1); assertEquals("Hello", string); int id = activity.getResources().getIdentifier("hello", "string", "com.xtremelabs.robolectric"); assertTrue(id > 0); String hello = activity.getResources().getString(id); assertEquals("Hello", hello); } @Test public void getCurrentFocus_shouldBeNullWhenNoFocusRequested() { Activity activity = new Activity(); activity.setContentView(R.layout.main); View focusedView = shadowOf(activity).getCurrentFocus(); assertNull(focusedView); } @Test public void getCurrentFocus_shouldReturnSubViewAfterFocusedRequest() { Activity activity = new Activity(); activity.setContentView(R.layout.main); View view = activity.findViewById(R.id.button); view.requestFocus(); View focusedView = shadowOf(activity).getCurrentFocus(); assertEquals(R.id.button, focusedView.getId()); } @Test public void retrieveIdOfNonExistingResource() { Activity activity = new Activity(); int id = activity.getResources().getIdentifier("just_alot_of_crap", "string", "com.xtremelabs.robolectric"); assertTrue(id == 0); } @Test public void shouldSetContentViewWithFrameLayoutAsParent() throws Exception { Activity activity = new Activity(); activity.setContentView(R.layout.toplevel_merge); View contentView = shadowOf(activity).getContentView(); assertInstanceOf(FrameLayout.class, contentView); assertThat(((FrameLayout) contentView).getChildCount(), equalTo(2)); } @Test public void onKeyUp_recordsThatItWasCalled() throws Exception { Activity activity = new Activity(); boolean consumed = activity.onKeyUp(KeyEvent.KEYCODE_0, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0)); assertFalse(consumed); assertTrue(shadowOf(activity).onKeyUpWasCalled()); shadowOf(activity).resetKeyUpWasCalled(); assertFalse(shadowOf(activity).onKeyUpWasCalled()); } @Test public void onKeyUp_callsOnBackPressedWhichFinishesTheActivity() throws Exception { Activity activity = new Activity(); boolean consumed = activity.onKeyUp(KeyEvent.KEYCODE_BACK, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)); assertTrue(consumed); assertTrue(shadowOf(activity).onKeyUpWasCalled()); assertTrue(activity.isFinishing()); } @Test public void shouldGiveSharedPreferences() throws Exception { Activity activity = new Activity(); SharedPreferences preferences = activity.getPreferences(Context.MODE_PRIVATE); assertNotNull(preferences); preferences.edit().putString("foo", "bar").commit(); assertThat(activity.getPreferences(Context.MODE_PRIVATE).getString("foo", null), equalTo("bar")); } @Test public void shouldFindContentViewContainer() throws Exception { Activity activity = new Activity(); View contentView = new View(activity); activity.setContentView(contentView); FrameLayout contentViewContainer = (FrameLayout) activity.findViewById(android.R.id.content); assertThat(contentViewContainer.getChildAt(0), is(contentView)); } @Test public void createGoesThroughFullLifeCycle() throws Exception { TestActivity activity = new TestActivity(); shadowOf(activity).create(); activity.transcript.assertEventsSoFar( "onCreate", "onStart", "onPostCreate", "onResume" ); } @Test public void recreateGoesThroughFullLifeCycle() throws Exception { TestActivity activity = new TestActivity(); ShadowActivity shadow = shadowOf(activity); shadow.recreate(); activity.transcript.assertEventsSoFar( "onSaveInstanceState", "onPause", "onStop", "onRetainNonConfigurationInstance", "onDestroy", "onCreate", "onStart", "onRestoreInstanceState", "onResume" ); Integer storedValue = (Integer) activity.getLastNonConfigurationInstance(); assertEquals(5, storedValue.intValue()); } @Test public void startAndStopManagingCursorTracksCursors() throws Exception { TestActivity activity = new TestActivity(); ShadowActivity shadow = shadowOf(activity); assertThat( shadow.getManagedCursors(), notNullValue() ); assertThat( shadow.getManagedCursors().size(), equalTo(0) ); Cursor c = Robolectric.newInstanceOf(SQLiteCursor.class); activity.startManagingCursor(c); assertThat( shadow.getManagedCursors(), notNullValue() ); assertThat( shadow.getManagedCursors().size(), equalTo(1) ); assertThat( shadow.getManagedCursors().get(0), sameInstance(c) ); activity.stopManagingCursor(c); assertThat( shadow.getManagedCursors(), notNullValue() ); assertThat( shadow.getManagedCursors().size(), equalTo(0) ); } private static class TestActivity extends Activity { Transcript transcript = new Transcript(); private boolean isRecreating = false; @Override public void onSaveInstanceState(Bundle outState) { isRecreating = true; transcript.add("onSaveInstanceState"); outState.putString("TestActivityKey", "TestActivityValue"); super.onSaveInstanceState(outState); } @Override public void onRestoreInstanceState(Bundle savedInstanceState) { transcript.add("onRestoreInstanceState"); assertTrue(savedInstanceState.containsKey("TestActivityKey")); assertEquals("TestActivityValue", savedInstanceState.getString("TestActivityKey")); super.onRestoreInstanceState(savedInstanceState); } @Override public Object onRetainNonConfigurationInstance() { transcript.add("onRetainNonConfigurationInstance"); return new Integer(5); } @Override public void onPause() { transcript.add("onPause"); super.onPause(); } @Override public void onDestroy() { transcript.add("onDestroy"); super.onDestroy(); } @Override public void onCreate(Bundle savedInstanceState) { transcript.add("onCreate"); if( isRecreating ) { assertTrue(savedInstanceState.containsKey("TestActivityKey")); assertEquals("TestActivityValue", savedInstanceState.getString("TestActivityKey")); } super.onCreate(savedInstanceState); } @Override public void onStart() { transcript.add("onStart"); super.onStart(); } @Override public void onPostCreate(Bundle savedInstanceState) { transcript.add("onPostCreate"); super.onPostCreate(savedInstanceState); } @Override public void onStop() { transcript.add("onStop"); super.onStop(); } @Override public void onResume() { transcript.add("onResume"); super.onResume(); } } private static class DialogCreatingActivity extends Activity { @Override protected Dialog onCreateDialog(int id) { return new Dialog(this); } } private static class DialogLifeCycleActivity extends Activity { public boolean createdDialog = false; public boolean preparedDialog = false; public boolean preparedDialogWithBundle = false; public Dialog dialog = null; @Override protected void onDestroy() { super.onDestroy(); } @Override protected Dialog onCreateDialog(int id) { createdDialog = true; return dialog; } @Override protected void onPrepareDialog(int id, Dialog dialog) { preparedDialog = true; } @Override protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) { preparedDialogWithBundle = true; } } private static class ActivityWithOnCreateDialog extends Activity { boolean onCreateDialogWasCalled = false; @Override protected Dialog onCreateDialog(int id) { onCreateDialogWasCalled = true; return new Dialog(null); } } }