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 17 package com.android.dialer; 18 19 import static com.android.dialer.CallDetailActivity.Tasks.UPDATE_PHONE_CALL_DETAILS; 20 import static com.android.dialer.voicemail.VoicemailPlaybackPresenter.Tasks.CHECK_FOR_CONTENT; 21 import static com.android.dialer.voicemail.VoicemailPlaybackPresenter.Tasks.PREPARE_MEDIA_PLAYER; 22 23 import android.content.ContentResolver; 24 import android.content.ContentUris; 25 import android.content.ContentValues; 26 import android.content.Intent; 27 import android.content.res.AssetManager; 28 import android.net.Uri; 29 import android.provider.CallLog; 30 import android.provider.VoicemailContract; 31 import android.test.ActivityInstrumentationTestCase2; 32 import android.test.suitebuilder.annotation.LargeTest; 33 import android.test.suitebuilder.annotation.Suppress; 34 import android.view.Menu; 35 import android.widget.TextView; 36 37 import com.android.dialer.util.AsyncTaskExecutors; 38 import com.android.dialer.util.FakeAsyncTaskExecutor; 39 import com.android.contacts.common.test.IntegrationTestUtils; 40 import com.android.dialer.util.LocaleTestUtils; 41 import com.android.internal.view.menu.ContextMenuBuilder; 42 import com.google.common.io.Closeables; 43 44 import java.io.IOException; 45 import java.io.InputStream; 46 import java.io.OutputStream; 47 import java.util.List; 48 import java.util.Locale; 49 50 /** 51 * Unit tests for the {@link CallDetailActivity}. 52 */ 53 @LargeTest 54 public class CallDetailActivityTest extends ActivityInstrumentationTestCase2<CallDetailActivity> { 55 private static final String TEST_ASSET_NAME = "quick_test_recording.mp3"; 56 private static final String MIME_TYPE = "audio/mp3"; 57 private static final String CONTACT_NUMBER = "+1412555555"; 58 private static final String VOICEMAIL_FILE_LOCATION = "/sdcard/sadlfj893w4j23o9sfu.mp3"; 59 60 private Uri mCallLogUri; 61 private Uri mVoicemailUri; 62 private IntegrationTestUtils mTestUtils; 63 private LocaleTestUtils mLocaleTestUtils; 64 private FakeAsyncTaskExecutor mFakeAsyncTaskExecutor; 65 private CallDetailActivity mActivityUnderTest; 66 67 public CallDetailActivityTest() { 68 super(CallDetailActivity.class); 69 } 70 71 @Override 72 protected void setUp() throws Exception { 73 super.setUp(); 74 mFakeAsyncTaskExecutor = new FakeAsyncTaskExecutor(getInstrumentation()); 75 AsyncTaskExecutors.setFactoryForTest(mFakeAsyncTaskExecutor.getFactory()); 76 // I don't like the default of focus-mode for tests, the green focus border makes the 77 // screenshots look weak. 78 setActivityInitialTouchMode(true); 79 mTestUtils = new IntegrationTestUtils(getInstrumentation()); 80 // Some of the tests rely on the text that appears on screen - safest to force a 81 // specific locale. 82 mLocaleTestUtils = new LocaleTestUtils(getInstrumentation().getTargetContext()); 83 mLocaleTestUtils.setLocale(Locale.US); 84 } 85 86 @Override 87 protected void tearDown() throws Exception { 88 mLocaleTestUtils.restoreLocale(); 89 mLocaleTestUtils = null; 90 cleanUpUri(); 91 mTestUtils = null; 92 AsyncTaskExecutors.setFactoryForTest(null); 93 super.tearDown(); 94 } 95 96 public void testInitialActivityStartsWithFetchingVoicemail() throws Throwable { 97 setActivityIntentForTestVoicemailEntry(); 98 startActivityUnderTest(); 99 // When the activity first starts, we will show "Fetching voicemail" on the screen. 100 // The duration should not be visible. 101 assertHasOneTextViewContaining("Fetching voicemail"); 102 assertZeroTextViewsContaining("00:00"); 103 } 104 105 public void testWhenCheckForContentCompletes_UiShowsBuffering() throws Throwable { 106 setActivityIntentForTestVoicemailEntry(); 107 startActivityUnderTest(); 108 // There is a background check that is testing to see if we have the content available. 109 // Once that task completes, we shouldn't be showing the fetching message, we should 110 // be showing "Buffering". 111 mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); 112 assertHasOneTextViewContaining("Buffering"); 113 assertZeroTextViewsContaining("Fetching voicemail"); 114 } 115 116 public void testInvalidVoicemailShowsErrorMessage() throws Throwable { 117 setActivityIntentForTestVoicemailEntry(); 118 startActivityUnderTest(); 119 mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); 120 // There should be exactly one background task ready to prepare the media player. 121 // Preparing the media player will have thrown an IOException since the file doesn't exist. 122 // This should have put a failed to play message on screen, buffering is gone. 123 mFakeAsyncTaskExecutor.runTask(PREPARE_MEDIA_PLAYER); 124 assertHasOneTextViewContaining("Couldn't play voicemail"); 125 assertZeroTextViewsContaining("Buffering"); 126 } 127 128 public void testOnResumeDoesNotCreateManyFragments() throws Throwable { 129 // There was a bug where every time the activity was resumed, a new fragment was created. 130 // Before the fix, this was failing reproducibly with at least 3 "Buffering" views. 131 setActivityIntentForTestVoicemailEntry(); 132 startActivityUnderTest(); 133 mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); 134 getInstrumentation().runOnMainSync(new Runnable() { 135 @Override 136 public void run() { 137 getInstrumentation().callActivityOnPause(mActivityUnderTest); 138 getInstrumentation().callActivityOnResume(mActivityUnderTest); 139 getInstrumentation().callActivityOnPause(mActivityUnderTest); 140 getInstrumentation().callActivityOnResume(mActivityUnderTest); 141 } 142 }); 143 assertHasOneTextViewContaining("Buffering"); 144 } 145 146 /** 147 * Test for bug where increase rate button with invalid voicemail causes a crash. 148 * <p> 149 * The repro steps for this crash were to open a voicemail that does not have an attachment, 150 * then click the play button (which just reported an error), then after that try to adjust the 151 * rate. See http://b/5047879. 152 */ 153 public void testClickIncreaseRateButtonWithInvalidVoicemailDoesNotCrash() throws Throwable { 154 setActivityIntentForTestVoicemailEntry(); 155 startActivityUnderTest(); 156 mTestUtils.clickButton(mActivityUnderTest, R.id.playback_start_stop); 157 mTestUtils.clickButton(mActivityUnderTest, R.id.rate_increase_button); 158 } 159 160 /** Test for bug where missing Extras on intent used to start Activity causes NPE. */ 161 public void testCallLogUriWithMissingExtrasShouldNotCauseNPE() throws Throwable { 162 setActivityIntentForTestCallEntry(); 163 startActivityUnderTest(); 164 } 165 166 /** 167 * Test for bug where voicemails should not have remove-from-call-log entry. 168 * <p> 169 * See http://b/5054103. 170 */ 171 public void testVoicemailDoesNotHaveRemoveFromCallLog() throws Throwable { 172 setActivityIntentForTestVoicemailEntry(); 173 startActivityUnderTest(); 174 Menu menu = new ContextMenuBuilder(mActivityUnderTest); 175 mActivityUnderTest.onCreateOptionsMenu(menu); 176 mActivityUnderTest.onPrepareOptionsMenu(menu); 177 assertFalse(menu.findItem(R.id.menu_remove_from_call_log).isVisible()); 178 } 179 180 /** Test to check that I haven't broken the remove-from-call-log entry from regular calls. */ 181 public void testRegularCallDoesHaveRemoveFromCallLog() throws Throwable { 182 setActivityIntentForTestCallEntry(); 183 startActivityUnderTest(); 184 Menu menu = new ContextMenuBuilder(mActivityUnderTest); 185 mActivityUnderTest.onCreateOptionsMenu(menu); 186 mActivityUnderTest.onPrepareOptionsMenu(menu); 187 assertTrue(menu.findItem(R.id.menu_remove_from_call_log).isVisible()); 188 } 189 190 /** 191 * Test to show that we are correctly displaying playback rate on the ui. 192 * <p> 193 * See bug http://b/5044075. 194 */ 195 @Suppress 196 public void testVoicemailPlaybackRateDisplayedOnUi() throws Throwable { 197 setActivityIntentForTestVoicemailEntry(); 198 startActivityUnderTest(); 199 // Find the TextView containing the duration. It should be initially displaying "00:00". 200 List<TextView> views = mTestUtils.getTextViewsWithString(mActivityUnderTest, "00:00"); 201 assertEquals(1, views.size()); 202 TextView timeDisplay = views.get(0); 203 // Hit the plus button. At this point we should be displaying "fast speed". 204 mTestUtils.clickButton(mActivityUnderTest, R.id.rate_increase_button); 205 assertEquals("fast speed", mTestUtils.getText(timeDisplay)); 206 // Hit the minus button. We should be back to "normal" speed. 207 mTestUtils.clickButton(mActivityUnderTest, R.id.rate_decrease_button); 208 assertEquals("normal speed", mTestUtils.getText(timeDisplay)); 209 // Wait for one and a half seconds. The timer will be back. 210 Thread.sleep(1500); 211 assertEquals("00:00", mTestUtils.getText(timeDisplay)); 212 } 213 214 @Suppress 215 public void testClickingCallStopsPlayback() throws Throwable { 216 setActivityIntentForRealFileVoicemailEntry(); 217 startActivityUnderTest(); 218 mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); 219 mFakeAsyncTaskExecutor.runTask(PREPARE_MEDIA_PLAYER); 220 mTestUtils.clickButton(mActivityUnderTest, R.id.playback_speakerphone); 221 mTestUtils.clickButton(mActivityUnderTest, R.id.playback_start_stop); 222 mTestUtils.clickButton(mActivityUnderTest, R.id.call_and_sms_main_action); 223 Thread.sleep(2000); 224 // TODO: Suppressed the test for now, because I'm looking for an easy way to say "the audio 225 // is not playing at this point", and I can't find it without doing dirty things. 226 } 227 228 private void setActivityIntentForTestCallEntry() { 229 assertNull(mCallLogUri); 230 ContentResolver contentResolver = getContentResolver(); 231 ContentValues values = new ContentValues(); 232 values.put(CallLog.Calls.NUMBER, CONTACT_NUMBER); 233 values.put(CallLog.Calls.NUMBER_PRESENTATION, CallLog.Calls.PRESENTATION_ALLOWED); 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 assertNull(mVoicemailUri); 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 assertNull(mVoicemailUri); 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 assertNotNull(mActivityUnderTest); 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 assertNotNull(mActivityUnderTest); 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 assertNull(mActivityUnderTest); 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