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.TYPE, CallLog.Calls.INCOMING_TYPE); 234 mCallLogUri = contentResolver.insert(CallLog.Calls.CONTENT_URI, values); 235 setActivityIntent(new Intent(Intent.ACTION_VIEW, mCallLogUri)); 236 } 237 238 private void setActivityIntentForTestVoicemailEntry() { 239 assertNull(mVoicemailUri); 240 ContentResolver contentResolver = getContentResolver(); 241 ContentValues values = new ContentValues(); 242 values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER); 243 values.put(VoicemailContract.Voicemails.HAS_CONTENT, 1); 244 values.put(VoicemailContract.Voicemails._DATA, VOICEMAIL_FILE_LOCATION); 245 mVoicemailUri = contentResolver.insert(VoicemailContract.Voicemails.CONTENT_URI, values); 246 Uri callLogUri = ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, 247 ContentUris.parseId(mVoicemailUri)); 248 Intent intent = new Intent(Intent.ACTION_VIEW, callLogUri); 249 intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI, mVoicemailUri); 250 setActivityIntent(intent); 251 } 252 253 private void setActivityIntentForRealFileVoicemailEntry() throws IOException { 254 assertNull(mVoicemailUri); 255 ContentValues values = new ContentValues(); 256 values.put(VoicemailContract.Voicemails.DATE, String.valueOf(System.currentTimeMillis())); 257 values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER); 258 values.put(VoicemailContract.Voicemails.MIME_TYPE, MIME_TYPE); 259 values.put(VoicemailContract.Voicemails.HAS_CONTENT, 1); 260 String packageName = getInstrumentation().getTargetContext().getPackageName(); 261 mVoicemailUri = getContentResolver().insert( 262 VoicemailContract.Voicemails.buildSourceUri(packageName), values); 263 AssetManager assets = getAssets(); 264 OutputStream outputStream = null; 265 InputStream inputStream = null; 266 try { 267 inputStream = assets.open(TEST_ASSET_NAME); 268 outputStream = getContentResolver().openOutputStream(mVoicemailUri); 269 copyBetweenStreams(inputStream, outputStream); 270 } finally { 271 Closeables.closeQuietly(outputStream); 272 Closeables.closeQuietly(inputStream); 273 } 274 Uri callLogUri = ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, 275 ContentUris.parseId(mVoicemailUri)); 276 Intent intent = new Intent(Intent.ACTION_VIEW, callLogUri); 277 intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI, mVoicemailUri); 278 setActivityIntent(intent); 279 } 280 281 public void copyBetweenStreams(InputStream in, OutputStream out) throws IOException { 282 byte[] buffer = new byte[1024]; 283 int bytesRead; 284 int total = 0; 285 while ((bytesRead = in.read(buffer)) != -1) { 286 total += bytesRead; 287 out.write(buffer, 0, bytesRead); 288 } 289 } 290 291 private void cleanUpUri() { 292 if (mVoicemailUri != null) { 293 getContentResolver().delete(VoicemailContract.Voicemails.CONTENT_URI, 294 "_ID = ?", new String[] { String.valueOf(ContentUris.parseId(mVoicemailUri)) }); 295 mVoicemailUri = null; 296 } 297 if (mCallLogUri != null) { 298 getContentResolver().delete(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, 299 "_ID = ?", new String[] { String.valueOf(ContentUris.parseId(mCallLogUri)) }); 300 mCallLogUri = null; 301 } 302 } 303 304 private ContentResolver getContentResolver() { 305 return getInstrumentation().getTargetContext().getContentResolver(); 306 } 307 308 private TextView assertHasOneTextViewContaining(String text) throws Throwable { 309 assertNotNull(mActivityUnderTest); 310 List<TextView> views = mTestUtils.getTextViewsWithString(mActivityUnderTest, text); 311 assertEquals("There should have been one TextView with text '" + text + "' but found " 312 + views, 1, views.size()); 313 return views.get(0); 314 } 315 316 private void assertZeroTextViewsContaining(String text) throws Throwable { 317 assertNotNull(mActivityUnderTest); 318 List<TextView> views = mTestUtils.getTextViewsWithString(mActivityUnderTest, text); 319 assertEquals("There should have been no TextViews with text '" + text + "' but found " 320 + views, 0, views.size()); 321 } 322 323 private void startActivityUnderTest() throws Throwable { 324 assertNull(mActivityUnderTest); 325 mActivityUnderTest = getActivity(); 326 assertNotNull("activity should not be null", mActivityUnderTest); 327 // We have to run all tasks, not just one. 328 // This is because it seems that we can have onResume, onPause, onResume during the course 329 // of a single unit test. 330 mFakeAsyncTaskExecutor.runAllTasks(UPDATE_PHONE_CALL_DETAILS); 331 } 332 333 private AssetManager getAssets() { 334 return getInstrumentation().getContext().getAssets(); 335 } 336 } 337