Home | History | Annotate | Download | only in shadows
      1 package com.xtremelabs.robolectric.shadows;
      2 
      3 import android.app.Activity;
      4 import android.app.Dialog;
      5 import android.appwidget.AppWidgetProvider;
      6 import android.content.*;
      7 import android.content.pm.ActivityInfo;
      8 import android.database.CharArrayBuffer;
      9 import android.database.ContentObserver;
     10 import android.database.Cursor;
     11 import android.database.DataSetObserver;
     12 import android.database.sqlite.SQLiteCursor;
     13 import android.net.Uri;
     14 import android.os.Bundle;
     15 import android.view.KeyEvent;
     16 import android.view.View;
     17 import android.widget.FrameLayout;
     18 import com.xtremelabs.robolectric.ApplicationResolver;
     19 import com.xtremelabs.robolectric.R;
     20 import com.xtremelabs.robolectric.Robolectric;
     21 import com.xtremelabs.robolectric.WithTestDefaultsRunner;
     22 import com.xtremelabs.robolectric.util.TestRunnable;
     23 import com.xtremelabs.robolectric.util.Transcript;
     24 import org.hamcrest.CoreMatchers;
     25 import org.junit.Test;
     26 import org.junit.runner.RunWith;
     27 
     28 import java.util.concurrent.atomic.AtomicBoolean;
     29 
     30 import static com.xtremelabs.robolectric.Robolectric.shadowOf;
     31 import static com.xtremelabs.robolectric.util.TestUtil.assertInstanceOf;
     32 import static com.xtremelabs.robolectric.util.TestUtil.newConfig;
     33 import static org.hamcrest.CoreMatchers.*;
     34 import static org.hamcrest.core.StringStartsWith.startsWith;
     35 import static org.junit.Assert.*;
     36 
     37 @RunWith(WithTestDefaultsRunner.class)
     38 public 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