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