CallDetailActivityTest.java revision c06f21bd72611b335f4d34586f656225fb02f0fb
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.contacts; 18 19import static com.android.contacts.CallDetailActivity.Tasks.UPDATE_PHONE_CALL_DETAILS; 20import static com.android.contacts.voicemail.VoicemailPlaybackPresenter.Tasks.CHECK_FOR_CONTENT; 21import static com.android.contacts.voicemail.VoicemailPlaybackPresenter.Tasks.PREPARE_MEDIA_PLAYER; 22 23import com.android.contacts.util.AsyncTaskExecutors; 24import com.android.contacts.util.FakeAsyncTaskExecutor; 25import com.android.contacts.util.IntegrationTestUtils; 26import com.android.contacts.util.LocaleTestUtils; 27import com.android.internal.view.menu.ContextMenuBuilder; 28import com.google.common.base.Preconditions; 29import com.google.common.io.Closeables; 30 31import android.content.ContentResolver; 32import android.content.ContentUris; 33import android.content.ContentValues; 34import android.content.Intent; 35import android.content.res.AssetManager; 36import android.net.Uri; 37import android.provider.CallLog; 38import android.provider.VoicemailContract; 39import android.test.ActivityInstrumentationTestCase2; 40import android.test.suitebuilder.annotation.LargeTest; 41import android.test.suitebuilder.annotation.Suppress; 42import android.view.Menu; 43import android.widget.TextView; 44 45import java.io.IOException; 46import java.io.InputStream; 47import java.io.OutputStream; 48import java.util.List; 49import java.util.Locale; 50 51/** 52 * Unit tests for the {@link CallDetailActivity}. 53 */ 54@LargeTest 55public class CallDetailActivityTest extends ActivityInstrumentationTestCase2<CallDetailActivity> { 56 private static final String TEST_ASSET_NAME = "quick_test_recording.mp3"; 57 private static final String MIME_TYPE = "audio/mp3"; 58 private static final String CONTACT_NUMBER = "+1412555555"; 59 private static final String VOICEMAIL_FILE_LOCATION = "/sdcard/sadlfj893w4j23o9sfu.mp3"; 60 61 private Uri mCallLogUri; 62 private Uri mVoicemailUri; 63 private IntegrationTestUtils mTestUtils; 64 private LocaleTestUtils mLocaleTestUtils; 65 private FakeAsyncTaskExecutor mFakeAsyncTaskExecutor; 66 private CallDetailActivity mActivityUnderTest; 67 68 public CallDetailActivityTest() { 69 super(CallDetailActivity.class); 70 } 71 72 @Override 73 protected void setUp() throws Exception { 74 super.setUp(); 75 mFakeAsyncTaskExecutor = new FakeAsyncTaskExecutor(getInstrumentation()); 76 AsyncTaskExecutors.setFactoryForTest(mFakeAsyncTaskExecutor.getFactory()); 77 // I don't like the default of focus-mode for tests, the green focus border makes the 78 // screenshots look weak. 79 setActivityInitialTouchMode(true); 80 mTestUtils = new IntegrationTestUtils(getInstrumentation()); 81 // Some of the tests rely on the text that appears on screen - safest to force a 82 // specific locale. 83 mLocaleTestUtils = new LocaleTestUtils(getInstrumentation().getTargetContext()); 84 mLocaleTestUtils.setLocale(Locale.US); 85 } 86 87 @Override 88 protected void tearDown() throws Exception { 89 mLocaleTestUtils.restoreLocale(); 90 mLocaleTestUtils = null; 91 cleanUpUri(); 92 mTestUtils = null; 93 AsyncTaskExecutors.setFactoryForTest(null); 94 super.tearDown(); 95 } 96 97 public void testInitialActivityStartsWithFetchingVoicemail() throws Throwable { 98 setActivityIntentForTestVoicemailEntry(); 99 startActivityUnderTest(); 100 // When the activity first starts, we will show "Fetching voicemail" on the screen. 101 // The duration should not be visible. 102 assertHasOneTextViewContaining("Fetching voicemail"); 103 assertZeroTextViewsContaining("00:00"); 104 } 105 106 public void testWhenCheckForContentCompletes_UiShowsBuffering() throws Throwable { 107 setActivityIntentForTestVoicemailEntry(); 108 startActivityUnderTest(); 109 // There is a background check that is testing to see if we have the content available. 110 // Once that task completes, we shouldn't be showing the fetching message, we should 111 // be showing "Buffering". 112 mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); 113 assertHasOneTextViewContaining("Buffering"); 114 assertZeroTextViewsContaining("Fetching voicemail"); 115 } 116 117 public void testInvalidVoicemailShowsErrorMessage() throws Throwable { 118 setActivityIntentForTestVoicemailEntry(); 119 startActivityUnderTest(); 120 mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); 121 // There should be exactly one background task ready to prepare the media player. 122 // Preparing the media player will have thrown an IOException since the file doesn't exist. 123 // This should have put a failed to play message on screen, buffering is gone. 124 mFakeAsyncTaskExecutor.runTask(PREPARE_MEDIA_PLAYER); 125 assertHasOneTextViewContaining("Couldn't play voicemail"); 126 assertZeroTextViewsContaining("Buffering"); 127 } 128 129 public void testOnResumeDoesNotCreateManyFragments() throws Throwable { 130 // There was a bug where every time the activity was resumed, a new fragment was created. 131 // Before the fix, this was failing reproducibly with at least 3 "Buffering" views. 132 setActivityIntentForTestVoicemailEntry(); 133 startActivityUnderTest(); 134 mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); 135 getInstrumentation().runOnMainSync(new Runnable() { 136 @Override 137 public void run() { 138 getInstrumentation().callActivityOnPause(mActivityUnderTest); 139 getInstrumentation().callActivityOnResume(mActivityUnderTest); 140 getInstrumentation().callActivityOnPause(mActivityUnderTest); 141 getInstrumentation().callActivityOnResume(mActivityUnderTest); 142 } 143 }); 144 assertHasOneTextViewContaining("Buffering"); 145 } 146 147 /** 148 * Test for bug where increase rate button with invalid voicemail causes a crash. 149 * <p> 150 * The repro steps for this crash were to open a voicemail that does not have an attachment, 151 * then click the play button (which just reported an error), then after that try to adjust the 152 * rate. See http://b/5047879. 153 */ 154 public void testClickIncreaseRateButtonWithInvalidVoicemailDoesNotCrash() throws Throwable { 155 setActivityIntentForTestVoicemailEntry(); 156 startActivityUnderTest(); 157 mTestUtils.clickButton(mActivityUnderTest, R.id.playback_start_stop); 158 mTestUtils.clickButton(mActivityUnderTest, R.id.rate_increase_button); 159 } 160 161 /** Test for bug where missing Extras on intent used to start Activity causes NPE. */ 162 public void testCallLogUriWithMissingExtrasShouldNotCauseNPE() throws Throwable { 163 setActivityIntentForTestCallEntry(); 164 startActivityUnderTest(); 165 } 166 167 /** 168 * Test for bug where voicemails should not have remove-from-call-log entry. 169 * <p> 170 * See http://b/5054103. 171 */ 172 public void testVoicemailDoesNotHaveRemoveFromCallLog() throws Throwable { 173 setActivityIntentForTestVoicemailEntry(); 174 startActivityUnderTest(); 175 Menu menu = new ContextMenuBuilder(mActivityUnderTest); 176 mActivityUnderTest.onCreateOptionsMenu(menu); 177 mActivityUnderTest.onPrepareOptionsMenu(menu); 178 assertFalse(menu.findItem(R.id.menu_remove_from_call_log).isVisible()); 179 } 180 181 /** Test to check that I haven't broken the remove-from-call-log entry from regular calls. */ 182 public void testRegularCallDoesHaveRemoveFromCallLog() throws Throwable { 183 setActivityIntentForTestCallEntry(); 184 startActivityUnderTest(); 185 Menu menu = new ContextMenuBuilder(mActivityUnderTest); 186 mActivityUnderTest.onCreateOptionsMenu(menu); 187 mActivityUnderTest.onPrepareOptionsMenu(menu); 188 assertTrue(menu.findItem(R.id.menu_remove_from_call_log).isVisible()); 189 } 190 191 /** 192 * Test to show that we are correctly displaying playback rate on the ui. 193 * <p> 194 * See bug http://b/5044075. 195 */ 196 @Suppress 197 public void testVoicemailPlaybackRateDisplayedOnUi() throws Throwable { 198 setActivityIntentForTestVoicemailEntry(); 199 startActivityUnderTest(); 200 // Find the TextView containing the duration. It should be initially displaying "00:00". 201 List<TextView> views = mTestUtils.getTextViewsWithString(mActivityUnderTest, "00:00"); 202 assertEquals(1, views.size()); 203 TextView timeDisplay = views.get(0); 204 // Hit the plus button. At this point we should be displaying "fast speed". 205 mTestUtils.clickButton(mActivityUnderTest, R.id.rate_increase_button); 206 assertEquals("fast speed", mTestUtils.getText(timeDisplay)); 207 // Hit the minus button. We should be back to "normal" speed. 208 mTestUtils.clickButton(mActivityUnderTest, R.id.rate_decrease_button); 209 assertEquals("normal speed", mTestUtils.getText(timeDisplay)); 210 // Wait for one and a half seconds. The timer will be back. 211 Thread.sleep(1500); 212 assertEquals("00:00", mTestUtils.getText(timeDisplay)); 213 } 214 215 @Suppress 216 public void testClickingCallStopsPlayback() throws Throwable { 217 setActivityIntentForRealFileVoicemailEntry(); 218 startActivityUnderTest(); 219 mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); 220 mFakeAsyncTaskExecutor.runTask(PREPARE_MEDIA_PLAYER); 221 mTestUtils.clickButton(mActivityUnderTest, R.id.playback_speakerphone); 222 mTestUtils.clickButton(mActivityUnderTest, R.id.playback_start_stop); 223 mTestUtils.clickButton(mActivityUnderTest, R.id.call_and_sms_main_action); 224 Thread.sleep(2000); 225 // TODO: Suppressed the test for now, because I'm looking for an easy way to say "the audio 226 // is not playing at this point", and I can't find it without doing dirty things. 227 } 228 229 private void setActivityIntentForTestCallEntry() { 230 Preconditions.checkState(mCallLogUri == null, "mUri should be null"); 231 ContentResolver contentResolver = getContentResolver(); 232 ContentValues values = new ContentValues(); 233 values.put(CallLog.Calls.NUMBER, CONTACT_NUMBER); 234 values.put(CallLog.Calls.TYPE, CallLog.Calls.INCOMING_TYPE); 235 mCallLogUri = contentResolver.insert(CallLog.Calls.CONTENT_URI, values); 236 setActivityIntent(new Intent(Intent.ACTION_VIEW, mCallLogUri)); 237 } 238 239 private void setActivityIntentForTestVoicemailEntry() { 240 Preconditions.checkState(mVoicemailUri == null, "mUri should be null"); 241 ContentResolver contentResolver = getContentResolver(); 242 ContentValues values = new ContentValues(); 243 values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER); 244 values.put(VoicemailContract.Voicemails.HAS_CONTENT, 1); 245 values.put(VoicemailContract.Voicemails._DATA, VOICEMAIL_FILE_LOCATION); 246 mVoicemailUri = contentResolver.insert(VoicemailContract.Voicemails.CONTENT_URI, values); 247 Uri callLogUri = ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, 248 ContentUris.parseId(mVoicemailUri)); 249 Intent intent = new Intent(Intent.ACTION_VIEW, callLogUri); 250 intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI, mVoicemailUri); 251 setActivityIntent(intent); 252 } 253 254 private void setActivityIntentForRealFileVoicemailEntry() throws IOException { 255 Preconditions.checkState(mVoicemailUri == null, "mUri should be null"); 256 ContentValues values = new ContentValues(); 257 values.put(VoicemailContract.Voicemails.DATE, String.valueOf(System.currentTimeMillis())); 258 values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER); 259 values.put(VoicemailContract.Voicemails.MIME_TYPE, MIME_TYPE); 260 values.put(VoicemailContract.Voicemails.HAS_CONTENT, 1); 261 String packageName = getInstrumentation().getTargetContext().getPackageName(); 262 mVoicemailUri = getContentResolver().insert( 263 VoicemailContract.Voicemails.buildSourceUri(packageName), values); 264 AssetManager assets = getAssets(); 265 OutputStream outputStream = null; 266 InputStream inputStream = null; 267 try { 268 inputStream = assets.open(TEST_ASSET_NAME); 269 outputStream = getContentResolver().openOutputStream(mVoicemailUri); 270 copyBetweenStreams(inputStream, outputStream); 271 } finally { 272 Closeables.closeQuietly(outputStream); 273 Closeables.closeQuietly(inputStream); 274 } 275 Uri callLogUri = ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, 276 ContentUris.parseId(mVoicemailUri)); 277 Intent intent = new Intent(Intent.ACTION_VIEW, callLogUri); 278 intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI, mVoicemailUri); 279 setActivityIntent(intent); 280 } 281 282 public void copyBetweenStreams(InputStream in, OutputStream out) throws IOException { 283 byte[] buffer = new byte[1024]; 284 int bytesRead; 285 int total = 0; 286 while ((bytesRead = in.read(buffer)) != -1) { 287 total += bytesRead; 288 out.write(buffer, 0, bytesRead); 289 } 290 } 291 292 private void cleanUpUri() { 293 if (mVoicemailUri != null) { 294 getContentResolver().delete(VoicemailContract.Voicemails.CONTENT_URI, 295 "_ID = ?", new String[] { String.valueOf(ContentUris.parseId(mVoicemailUri)) }); 296 mVoicemailUri = null; 297 } 298 if (mCallLogUri != null) { 299 getContentResolver().delete(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, 300 "_ID = ?", new String[] { String.valueOf(ContentUris.parseId(mCallLogUri)) }); 301 mCallLogUri = null; 302 } 303 } 304 305 private ContentResolver getContentResolver() { 306 return getInstrumentation().getTargetContext().getContentResolver(); 307 } 308 309 private TextView assertHasOneTextViewContaining(String text) throws Throwable { 310 Preconditions.checkNotNull(mActivityUnderTest, "forget to call startActivityUnderTest()?"); 311 List<TextView> views = mTestUtils.getTextViewsWithString(mActivityUnderTest, text); 312 assertEquals("There should have been one TextView with text '" + text + "' but found " 313 + views, 1, views.size()); 314 return views.get(0); 315 } 316 317 private void assertZeroTextViewsContaining(String text) throws Throwable { 318 Preconditions.checkNotNull(mActivityUnderTest, "forget to call startActivityUnderTest()?"); 319 List<TextView> views = mTestUtils.getTextViewsWithString(mActivityUnderTest, text); 320 assertEquals("There should have been no TextViews with text '" + text + "' but found " 321 + views, 0, views.size()); 322 } 323 324 private void startActivityUnderTest() throws Throwable { 325 Preconditions.checkState(mActivityUnderTest == null, "must only start the activity once"); 326 mActivityUnderTest = getActivity(); 327 assertNotNull("activity should not be null", mActivityUnderTest); 328 // We have to run all tasks, not just one. 329 // This is because it seems that we can have onResume, onPause, onResume during the course 330 // of a single unit test. 331 mFakeAsyncTaskExecutor.runAllTasks(UPDATE_PHONE_CALL_DETAILS); 332 } 333 334 private AssetManager getAssets() { 335 return getInstrumentation().getContext().getAssets(); 336 } 337} 338