| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.contacts; |
| |
| import static com.android.contacts.CallDetailActivity.Tasks.UPDATE_PHONE_CALL_DETAILS; |
| import static com.android.contacts.voicemail.VoicemailPlaybackPresenter.Tasks.CHECK_FOR_CONTENT; |
| import static com.android.contacts.voicemail.VoicemailPlaybackPresenter.Tasks.PREPARE_MEDIA_PLAYER; |
| |
| import android.content.ContentResolver; |
| import android.content.ContentUris; |
| import android.content.ContentValues; |
| import android.content.Intent; |
| import android.content.res.AssetManager; |
| import android.net.Uri; |
| import android.provider.CallLog; |
| import android.provider.VoicemailContract; |
| import android.test.ActivityInstrumentationTestCase2; |
| import android.test.suitebuilder.annotation.LargeTest; |
| import android.test.suitebuilder.annotation.Suppress; |
| import android.view.Menu; |
| import android.widget.TextView; |
| |
| import com.android.contacts.util.AsyncTaskExecutors; |
| import com.android.contacts.util.FakeAsyncTaskExecutor; |
| import com.android.contacts.util.IntegrationTestUtils; |
| import com.android.contacts.util.LocaleTestUtils; |
| import com.android.internal.view.menu.ContextMenuBuilder; |
| import com.google.common.io.Closeables; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.List; |
| import java.util.Locale; |
| |
| /** |
| * Unit tests for the {@link CallDetailActivity}. |
| */ |
| @LargeTest |
| public class CallDetailActivityTest extends ActivityInstrumentationTestCase2<CallDetailActivity> { |
| private static final String TEST_ASSET_NAME = "quick_test_recording.mp3"; |
| private static final String MIME_TYPE = "audio/mp3"; |
| private static final String CONTACT_NUMBER = "+1412555555"; |
| private static final String VOICEMAIL_FILE_LOCATION = "/sdcard/sadlfj893w4j23o9sfu.mp3"; |
| |
| private Uri mCallLogUri; |
| private Uri mVoicemailUri; |
| private IntegrationTestUtils mTestUtils; |
| private LocaleTestUtils mLocaleTestUtils; |
| private FakeAsyncTaskExecutor mFakeAsyncTaskExecutor; |
| private CallDetailActivity mActivityUnderTest; |
| |
| public CallDetailActivityTest() { |
| super(CallDetailActivity.class); |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| mFakeAsyncTaskExecutor = new FakeAsyncTaskExecutor(getInstrumentation()); |
| AsyncTaskExecutors.setFactoryForTest(mFakeAsyncTaskExecutor.getFactory()); |
| // I don't like the default of focus-mode for tests, the green focus border makes the |
| // screenshots look weak. |
| setActivityInitialTouchMode(true); |
| mTestUtils = new IntegrationTestUtils(getInstrumentation()); |
| // Some of the tests rely on the text that appears on screen - safest to force a |
| // specific locale. |
| mLocaleTestUtils = new LocaleTestUtils(getInstrumentation().getTargetContext()); |
| mLocaleTestUtils.setLocale(Locale.US); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| mLocaleTestUtils.restoreLocale(); |
| mLocaleTestUtils = null; |
| cleanUpUri(); |
| mTestUtils = null; |
| AsyncTaskExecutors.setFactoryForTest(null); |
| super.tearDown(); |
| } |
| |
| public void testInitialActivityStartsWithFetchingVoicemail() throws Throwable { |
| setActivityIntentForTestVoicemailEntry(); |
| startActivityUnderTest(); |
| // When the activity first starts, we will show "Fetching voicemail" on the screen. |
| // The duration should not be visible. |
| assertHasOneTextViewContaining("Fetching voicemail"); |
| assertZeroTextViewsContaining("00:00"); |
| } |
| |
| public void testWhenCheckForContentCompletes_UiShowsBuffering() throws Throwable { |
| setActivityIntentForTestVoicemailEntry(); |
| startActivityUnderTest(); |
| // There is a background check that is testing to see if we have the content available. |
| // Once that task completes, we shouldn't be showing the fetching message, we should |
| // be showing "Buffering". |
| mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); |
| assertHasOneTextViewContaining("Buffering"); |
| assertZeroTextViewsContaining("Fetching voicemail"); |
| } |
| |
| public void testInvalidVoicemailShowsErrorMessage() throws Throwable { |
| setActivityIntentForTestVoicemailEntry(); |
| startActivityUnderTest(); |
| mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); |
| // There should be exactly one background task ready to prepare the media player. |
| // Preparing the media player will have thrown an IOException since the file doesn't exist. |
| // This should have put a failed to play message on screen, buffering is gone. |
| mFakeAsyncTaskExecutor.runTask(PREPARE_MEDIA_PLAYER); |
| assertHasOneTextViewContaining("Couldn't play voicemail"); |
| assertZeroTextViewsContaining("Buffering"); |
| } |
| |
| public void testOnResumeDoesNotCreateManyFragments() throws Throwable { |
| // There was a bug where every time the activity was resumed, a new fragment was created. |
| // Before the fix, this was failing reproducibly with at least 3 "Buffering" views. |
| setActivityIntentForTestVoicemailEntry(); |
| startActivityUnderTest(); |
| mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); |
| getInstrumentation().runOnMainSync(new Runnable() { |
| @Override |
| public void run() { |
| getInstrumentation().callActivityOnPause(mActivityUnderTest); |
| getInstrumentation().callActivityOnResume(mActivityUnderTest); |
| getInstrumentation().callActivityOnPause(mActivityUnderTest); |
| getInstrumentation().callActivityOnResume(mActivityUnderTest); |
| } |
| }); |
| assertHasOneTextViewContaining("Buffering"); |
| } |
| |
| /** |
| * Test for bug where increase rate button with invalid voicemail causes a crash. |
| * <p> |
| * The repro steps for this crash were to open a voicemail that does not have an attachment, |
| * then click the play button (which just reported an error), then after that try to adjust the |
| * rate. See http://b/5047879. |
| */ |
| public void testClickIncreaseRateButtonWithInvalidVoicemailDoesNotCrash() throws Throwable { |
| setActivityIntentForTestVoicemailEntry(); |
| startActivityUnderTest(); |
| mTestUtils.clickButton(mActivityUnderTest, R.id.playback_start_stop); |
| mTestUtils.clickButton(mActivityUnderTest, R.id.rate_increase_button); |
| } |
| |
| /** Test for bug where missing Extras on intent used to start Activity causes NPE. */ |
| public void testCallLogUriWithMissingExtrasShouldNotCauseNPE() throws Throwable { |
| setActivityIntentForTestCallEntry(); |
| startActivityUnderTest(); |
| } |
| |
| /** |
| * Test for bug where voicemails should not have remove-from-call-log entry. |
| * <p> |
| * See http://b/5054103. |
| */ |
| public void testVoicemailDoesNotHaveRemoveFromCallLog() throws Throwable { |
| setActivityIntentForTestVoicemailEntry(); |
| startActivityUnderTest(); |
| Menu menu = new ContextMenuBuilder(mActivityUnderTest); |
| mActivityUnderTest.onCreateOptionsMenu(menu); |
| mActivityUnderTest.onPrepareOptionsMenu(menu); |
| assertFalse(menu.findItem(R.id.menu_remove_from_call_log).isVisible()); |
| } |
| |
| /** Test to check that I haven't broken the remove-from-call-log entry from regular calls. */ |
| public void testRegularCallDoesHaveRemoveFromCallLog() throws Throwable { |
| setActivityIntentForTestCallEntry(); |
| startActivityUnderTest(); |
| Menu menu = new ContextMenuBuilder(mActivityUnderTest); |
| mActivityUnderTest.onCreateOptionsMenu(menu); |
| mActivityUnderTest.onPrepareOptionsMenu(menu); |
| assertTrue(menu.findItem(R.id.menu_remove_from_call_log).isVisible()); |
| } |
| |
| /** |
| * Test to show that we are correctly displaying playback rate on the ui. |
| * <p> |
| * See bug http://b/5044075. |
| */ |
| @Suppress |
| public void testVoicemailPlaybackRateDisplayedOnUi() throws Throwable { |
| setActivityIntentForTestVoicemailEntry(); |
| startActivityUnderTest(); |
| // Find the TextView containing the duration. It should be initially displaying "00:00". |
| List<TextView> views = mTestUtils.getTextViewsWithString(mActivityUnderTest, "00:00"); |
| assertEquals(1, views.size()); |
| TextView timeDisplay = views.get(0); |
| // Hit the plus button. At this point we should be displaying "fast speed". |
| mTestUtils.clickButton(mActivityUnderTest, R.id.rate_increase_button); |
| assertEquals("fast speed", mTestUtils.getText(timeDisplay)); |
| // Hit the minus button. We should be back to "normal" speed. |
| mTestUtils.clickButton(mActivityUnderTest, R.id.rate_decrease_button); |
| assertEquals("normal speed", mTestUtils.getText(timeDisplay)); |
| // Wait for one and a half seconds. The timer will be back. |
| Thread.sleep(1500); |
| assertEquals("00:00", mTestUtils.getText(timeDisplay)); |
| } |
| |
| @Suppress |
| public void testClickingCallStopsPlayback() throws Throwable { |
| setActivityIntentForRealFileVoicemailEntry(); |
| startActivityUnderTest(); |
| mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT); |
| mFakeAsyncTaskExecutor.runTask(PREPARE_MEDIA_PLAYER); |
| mTestUtils.clickButton(mActivityUnderTest, R.id.playback_speakerphone); |
| mTestUtils.clickButton(mActivityUnderTest, R.id.playback_start_stop); |
| mTestUtils.clickButton(mActivityUnderTest, R.id.call_and_sms_main_action); |
| Thread.sleep(2000); |
| // TODO: Suppressed the test for now, because I'm looking for an easy way to say "the audio |
| // is not playing at this point", and I can't find it without doing dirty things. |
| } |
| |
| private void setActivityIntentForTestCallEntry() { |
| assertNull(mCallLogUri); |
| ContentResolver contentResolver = getContentResolver(); |
| ContentValues values = new ContentValues(); |
| values.put(CallLog.Calls.NUMBER, CONTACT_NUMBER); |
| values.put(CallLog.Calls.TYPE, CallLog.Calls.INCOMING_TYPE); |
| mCallLogUri = contentResolver.insert(CallLog.Calls.CONTENT_URI, values); |
| setActivityIntent(new Intent(Intent.ACTION_VIEW, mCallLogUri)); |
| } |
| |
| private void setActivityIntentForTestVoicemailEntry() { |
| assertNull(mVoicemailUri); |
| ContentResolver contentResolver = getContentResolver(); |
| ContentValues values = new ContentValues(); |
| values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER); |
| values.put(VoicemailContract.Voicemails.HAS_CONTENT, 1); |
| values.put(VoicemailContract.Voicemails._DATA, VOICEMAIL_FILE_LOCATION); |
| mVoicemailUri = contentResolver.insert(VoicemailContract.Voicemails.CONTENT_URI, values); |
| Uri callLogUri = ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, |
| ContentUris.parseId(mVoicemailUri)); |
| Intent intent = new Intent(Intent.ACTION_VIEW, callLogUri); |
| intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI, mVoicemailUri); |
| setActivityIntent(intent); |
| } |
| |
| private void setActivityIntentForRealFileVoicemailEntry() throws IOException { |
| assertNull(mVoicemailUri); |
| ContentValues values = new ContentValues(); |
| values.put(VoicemailContract.Voicemails.DATE, String.valueOf(System.currentTimeMillis())); |
| values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER); |
| values.put(VoicemailContract.Voicemails.MIME_TYPE, MIME_TYPE); |
| values.put(VoicemailContract.Voicemails.HAS_CONTENT, 1); |
| String packageName = getInstrumentation().getTargetContext().getPackageName(); |
| mVoicemailUri = getContentResolver().insert( |
| VoicemailContract.Voicemails.buildSourceUri(packageName), values); |
| AssetManager assets = getAssets(); |
| OutputStream outputStream = null; |
| InputStream inputStream = null; |
| try { |
| inputStream = assets.open(TEST_ASSET_NAME); |
| outputStream = getContentResolver().openOutputStream(mVoicemailUri); |
| copyBetweenStreams(inputStream, outputStream); |
| } finally { |
| Closeables.closeQuietly(outputStream); |
| Closeables.closeQuietly(inputStream); |
| } |
| Uri callLogUri = ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, |
| ContentUris.parseId(mVoicemailUri)); |
| Intent intent = new Intent(Intent.ACTION_VIEW, callLogUri); |
| intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI, mVoicemailUri); |
| setActivityIntent(intent); |
| } |
| |
| public void copyBetweenStreams(InputStream in, OutputStream out) throws IOException { |
| byte[] buffer = new byte[1024]; |
| int bytesRead; |
| int total = 0; |
| while ((bytesRead = in.read(buffer)) != -1) { |
| total += bytesRead; |
| out.write(buffer, 0, bytesRead); |
| } |
| } |
| |
| private void cleanUpUri() { |
| if (mVoicemailUri != null) { |
| getContentResolver().delete(VoicemailContract.Voicemails.CONTENT_URI, |
| "_ID = ?", new String[] { String.valueOf(ContentUris.parseId(mVoicemailUri)) }); |
| mVoicemailUri = null; |
| } |
| if (mCallLogUri != null) { |
| getContentResolver().delete(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, |
| "_ID = ?", new String[] { String.valueOf(ContentUris.parseId(mCallLogUri)) }); |
| mCallLogUri = null; |
| } |
| } |
| |
| private ContentResolver getContentResolver() { |
| return getInstrumentation().getTargetContext().getContentResolver(); |
| } |
| |
| private TextView assertHasOneTextViewContaining(String text) throws Throwable { |
| assertNotNull(mActivityUnderTest); |
| List<TextView> views = mTestUtils.getTextViewsWithString(mActivityUnderTest, text); |
| assertEquals("There should have been one TextView with text '" + text + "' but found " |
| + views, 1, views.size()); |
| return views.get(0); |
| } |
| |
| private void assertZeroTextViewsContaining(String text) throws Throwable { |
| assertNotNull(mActivityUnderTest); |
| List<TextView> views = mTestUtils.getTextViewsWithString(mActivityUnderTest, text); |
| assertEquals("There should have been no TextViews with text '" + text + "' but found " |
| + views, 0, views.size()); |
| } |
| |
| private void startActivityUnderTest() throws Throwable { |
| assertNull(mActivityUnderTest); |
| mActivityUnderTest = getActivity(); |
| assertNotNull("activity should not be null", mActivityUnderTest); |
| // We have to run all tasks, not just one. |
| // This is because it seems that we can have onResume, onPause, onResume during the course |
| // of a single unit test. |
| mFakeAsyncTaskExecutor.runAllTasks(UPDATE_PHONE_CALL_DETAILS); |
| } |
| |
| private AssetManager getAssets() { |
| return getInstrumentation().getContext().getAssets(); |
| } |
| } |