• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package com.android.contacts.activities;
17 
18 import static com.android.contacts.tests.ContactsMatchers.DataCursor.hasMimeType;
19 import static com.android.contacts.tests.ContactsMatchers.hasRowMatching;
20 import static com.android.contacts.tests.ContactsMatchers.hasValueForColumn;
21 import static org.hamcrest.MatcherAssert.assertThat;
22 import static org.hamcrest.Matchers.allOf;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.Matchers.anyInt;
25 import static org.mockito.Mockito.doReturn;
26 import static org.mockito.Mockito.spy;
27 
28 import android.annotation.TargetApi;
29 import android.app.Activity;
30 import android.app.Instrumentation;
31 import android.content.BroadcastReceiver;
32 import android.content.ContentProviderClient;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.content.IntentFilter;
36 import android.content.pm.ActivityInfo;
37 import android.database.Cursor;
38 import android.os.Build;
39 import android.provider.ContactsContract;
40 import android.provider.ContactsContract.CommonDataKinds.Phone;
41 import android.provider.ContactsContract.Data;
42 import android.support.test.InstrumentationRegistry;
43 import android.support.test.filters.LargeTest;
44 import android.support.test.filters.SdkSuppress;
45 import android.support.test.runner.AndroidJUnit4;
46 import android.support.test.uiautomator.By;
47 import android.support.test.uiautomator.UiDevice;
48 import android.support.test.uiautomator.Until;
49 import android.support.v4.content.LocalBroadcastManager;
50 import android.telephony.TelephonyManager;
51 import android.test.mock.MockContentResolver;
52 
53 import com.android.contacts.SimImportService;
54 import com.android.contacts.database.SimContactDao;
55 import com.android.contacts.database.SimContactDaoImpl;
56 import com.android.contacts.model.SimCard;
57 import com.android.contacts.model.SimContact;
58 import com.android.contacts.model.account.AccountWithDataSet;
59 import com.android.contacts.test.mocks.ForwardingContentProvider;
60 import com.android.contacts.test.mocks.MockContentProvider;
61 import com.android.contacts.tests.AccountsTestHelper;
62 import com.android.contacts.tests.ContactsMatchers;
63 import com.android.contacts.tests.FakeSimContactDao;
64 import com.android.contacts.tests.StringableCursor;
65 import com.google.common.base.Function;
66 import com.google.common.base.Functions;
67 import com.google.common.util.concurrent.ListenableFuture;
68 import com.google.common.util.concurrent.SettableFuture;
69 
70 import org.junit.After;
71 import org.junit.AfterClass;
72 import org.junit.Before;
73 import org.junit.Test;
74 import org.junit.runner.RunWith;
75 
76 import java.util.Collections;
77 import java.util.concurrent.TimeUnit;
78 
79 /**
80  * UI Tests for {@link SimImportActivity}
81  *
82  * These should probably be converted to espresso tests because espresso does a better job of
83  * waiting for the app to be idle once espresso library is added
84  */
85 //@Suppress
86 @LargeTest
87 @RunWith(AndroidJUnit4.class)
88 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
89 @TargetApi(Build.VERSION_CODES.M)
90 public class SimImportActivityTest {
91 
92     public static final int TIMEOUT = 100000;
93     private Context mContext;
94     private UiDevice mDevice;
95     private Instrumentation mInstrumentation;
96     private FakeSimContactDao mDao;
97     private AccountsTestHelper mAccountHelper;
98     private Activity mActivity;
99 
100     @Before
setUp()101     public void setUp() throws Exception {
102         mContext = InstrumentationRegistry.getTargetContext();
103         mInstrumentation = InstrumentationRegistry.getInstrumentation();
104         mDao = new FakeSimContactDao();
105         SimContactDao.setFactoryForTest(Functions.<SimContactDao>constant(mDao));
106         mDevice = UiDevice.getInstance(mInstrumentation);
107 
108         // Add some test accounts so that account picker is exercised
109         mAccountHelper = new AccountsTestHelper();
110         mAccountHelper.addTestAccount(mAccountHelper.generateAccountName("SimImportActivity1_"));
111         mAccountHelper.addTestAccount(mAccountHelper.generateAccountName("SimImportActivity2_"));
112         mAccountHelper.addTestAccount(mAccountHelper.generateAccountName("SimImportActivity3_"));
113     }
114 
115     @After
tearDown()116     public void tearDown() throws Exception {
117         SimContactDao.setFactoryForTest(SimContactDao.DEFAULT_FACTORY);
118         mAccountHelper.cleanup();
119         if (mActivity != null) {
120             mActivity.finish();
121             mInstrumentation.waitForIdleSync();
122         }
123     }
124 
125     @AfterClass
tearDownClass()126     public static void tearDownClass() {
127         AccountsTestHelper.removeAccountsWithPrefix(
128                 InstrumentationRegistry.getTargetContext(), "SimImportActivity");
129     }
130 
131     @Test
shouldDisplaySimContacts()132     public void shouldDisplaySimContacts() {
133         mDao.addSim(someSimCard(),
134                         new SimContact(1, "Sim One", "5550101"),
135                         new SimContact(2, "Sim Two", null),
136                         new SimContact(3, null, "5550103")
137                 );
138         mActivity = mInstrumentation.startActivitySync(new Intent(mContext, SimImportActivity.class)
139                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
140 
141         mDevice.waitForIdle();
142 
143         assertTrue(mDevice.wait(Until.hasObject(By.text("Sim One")), TIMEOUT));
144 
145         assertTrue(mDevice.hasObject(By.text("Sim One")));
146         assertTrue(mDevice.hasObject(By.text("Sim Two")));
147         assertTrue(mDevice.hasObject(By.text("5550103")));
148     }
149 
150     @Test
shouldHaveEmptyState()151     public void shouldHaveEmptyState() {
152         mDao.addSim(someSimCard());
153 
154         mInstrumentation.startActivitySync(new Intent(mContext, SimImportActivity.class)
155                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
156 
157         mDevice.waitForIdle();
158 
159         assertTrue(mDevice.wait(Until.hasObject(By.textStartsWith("No contacts")), TIMEOUT));
160     }
161 
162     @Test
smokeRotateInEmptyState()163     public void smokeRotateInEmptyState() {
164         mDao.addSim(someSimCard());
165 
166         mActivity = mInstrumentation.startActivitySync(
167                 new Intent(mContext, SimImportActivity.class)
168                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
169 
170         assertTrue(mDevice.wait(Until.hasObject(By.textStartsWith("No contacts")), TIMEOUT));
171 
172         mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
173 
174         mDevice.waitForIdle();
175 
176         assertTrue(mDevice.wait(Until.hasObject(By.textStartsWith("No contacts")), TIMEOUT));
177     }
178 
179     @Test
smokeRotateInNonEmptyState()180     public void smokeRotateInNonEmptyState() throws Exception {
181         mDao.addSim(someSimCard(), new SimContact(1, "Name One", "5550101"),
182                 new SimContact(2, "Name Two", "5550102"));
183 
184         mActivity = mInstrumentation.startActivitySync(
185                 new Intent(mContext, SimImportActivity.class)
186                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
187 
188         assertTrue(mDevice.wait(Until.hasObject(By.textStartsWith("Name One")), TIMEOUT));
189 
190         mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
191 
192         mDevice.waitForIdle();
193 
194         assertTrue(mDevice.wait(Until.hasObject(By.textStartsWith("Name One")), TIMEOUT));
195     }
196 
197     /**
198      * Tests a complete import flow
199      *
200      * <p>Test case outline:</p>
201      * <ul>
202      * <li>Load SIM contacts
203      * <li>Change to a specific target account
204      * <li>Deselect 3 specific SIM contacts
205      * <li>Rotate the screen to landscape
206      * <li>Rotate the screen back to portrait
207      * <li>Press the import button
208      * <li>Wait for import to complete
209      * <li>Query contacts in target account and verify that they match selected contacts
210      * <li>Start import activity again
211      * <li>Switch to target account
212      * <li>Verify that previously imported contacts are disabled and not checked
213      * </ul>
214      *
215      * <p>This mocks out the IccProvider and stubs the canReadSimContacts method to make it work on
216      * an emulator but otherwise uses real dependency.
217      * </p>
218      */
219     @Test
selectionsAreImportedAndDisabledOnSubsequentImports()220     public void selectionsAreImportedAndDisabledOnSubsequentImports() throws Exception {
221         final AccountWithDataSet targetAccount = mAccountHelper.addTestAccount(
222                 mAccountHelper.generateAccountName("SimImportActivity0_targetAccount_"));
223 
224         final MockContentProvider iccProvider = new MockContentProvider();
225         iccProvider.expect(MockContentProvider.Query.forAnyUri())
226                 .withDefaultProjection(new String[] {SimContactDaoImpl._ID, SimContactDaoImpl.NAME,
227                         SimContactDaoImpl.NUMBER, SimContactDaoImpl.EMAILS })
228                 .anyNumberOfTimes()
229                 .returnRow(toCursorRow(new SimContact(1, "Import One", "5550101")))
230                 .returnRow(toCursorRow(new SimContact(2, "Skip Two", "5550102")))
231                 .returnRow(toCursorRow(new SimContact(3, "Import Three", "5550103")))
232                 .returnRow(toCursorRow(new SimContact(4, "Skip Four", "5550104")))
233                 .returnRow(toCursorRow(new SimContact(5, "Skip Five", "5550105")))
234                 .returnRow(toCursorRow(new SimContact(6, "Import Six", "5550106")));
235         final MockContentResolver mockResolver = new MockContentResolver();
236         mockResolver.addProvider("icc", iccProvider);
237         final ContentProviderClient contactsProviderClient = mContext.getContentResolver()
238                 .acquireContentProviderClient(ContactsContract.AUTHORITY);
239         mockResolver.addProvider(ContactsContract.AUTHORITY, new ForwardingContentProvider(
240                 contactsProviderClient));
241 
242         SimContactDao.setFactoryForTest(new Function<Context, SimContactDao>() {
243             @Override
244             public SimContactDao apply(Context input) {
245                 final SimContactDaoImpl spy = spy(new SimContactDaoImpl(
246                         mContext, mockResolver,
247                         (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)));
248                 final SimCard sim = someSimCard();
249                 doReturn(true).when(spy).canReadSimContacts();
250                 doReturn(Collections.singletonList(sim)).when(spy).getSimCards();
251                 doReturn(sim).when(spy).getSimBySubscriptionId(anyInt());
252                 return spy;
253             }
254         });
255 
256         mActivity = mInstrumentation.startActivitySync(
257                 new Intent(mContext, SimImportActivity.class)
258                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
259 
260         assertTrue(mDevice.wait(Until.hasObject(By.desc("Show more")), TIMEOUT));
261 
262         mDevice.findObject(By.desc("Show more")).clickAndWait(Until.newWindow(), TIMEOUT);
263         mDevice.findObject(By.textContains("_targetAccount_")).click();
264 
265         assertTrue(mDevice.wait(Until.hasObject(By.text("Skip Two")), TIMEOUT));
266 
267         mDevice.findObject(By.text("Skip Two")).click();
268         mDevice.findObject(By.text("Skip Four")).click();
269         mDevice.findObject(By.text("Skip Five")).click();
270         mDevice.waitForIdle();
271 
272         assertTrue(mDevice.hasObject(By.text("Skip Two").checked(false)));
273         assertTrue(mDevice.hasObject(By.text("Skip Five").checked(false)));
274 
275         mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
276         mDevice.wait(Until.hasObject(By.text("Import One")), TIMEOUT);
277         mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
278         mDevice.wait(Until.hasObject(By.text("Import One")), TIMEOUT);
279 
280         ListenableFuture<?> nextImportFuture = nextImportCompleteBroadcast();
281 
282         mDevice.findObject(By.text("IMPORT").clickable(true)).click();
283 
284         // Block until import completes
285         nextImportFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
286 
287         final Cursor cursor = new StringableCursor(
288                 mContext.getContentResolver().query(Data.CONTENT_URI, null,
289                         ContactsContract.RawContacts.ACCOUNT_NAME + "=? AND " +
290                                 ContactsContract.RawContacts.ACCOUNT_TYPE+ "=?",
291                         new String[] {
292                                 targetAccount.name,
293                                 targetAccount.type
294                         }, null));
295         // 3 contacts imported with one row for name and one for phone
296         assertThat(cursor, ContactsMatchers.hasCount(3 * 2));
297 
298         assertThat(cursor, hasRowMatching(allOf(
299                 hasMimeType(Phone.CONTENT_ITEM_TYPE),
300                 hasValueForColumn(Phone.DISPLAY_NAME, "Import One"),
301                 hasValueForColumn(Phone.NUMBER, "5550101")
302         )));
303         assertThat(cursor, hasRowMatching(allOf(
304                 hasMimeType(Phone.CONTENT_ITEM_TYPE),
305                 hasValueForColumn(Phone.DISPLAY_NAME, "Import Three"),
306                 hasValueForColumn(Phone.NUMBER, "5550103")
307         )));
308         assertThat(cursor, hasRowMatching(allOf(
309                 hasMimeType(Phone.CONTENT_ITEM_TYPE),
310                 hasValueForColumn(Phone.DISPLAY_NAME, "Import Six"),
311                 hasValueForColumn(Phone.NUMBER, "5550106")
312         )));
313 
314         cursor.close();
315 
316 
317         mActivity = mInstrumentation.startActivitySync(
318                 new Intent(mContext, SimImportActivity.class)
319                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
320 
321         assertTrue(mDevice.wait(Until.hasObject(By.text("Import One")), TIMEOUT));
322 
323         mDevice.findObject(By.descStartsWith("Show more")).clickAndWait(Until.newWindow(), TIMEOUT);
324         mDevice.findObject(By.textContains(targetAccount.name)).click();
325         mDevice.waitForIdle();
326 
327         assertTrue(mDevice.wait(Until.hasObject(By.text("Import One").checked(false).enabled(false)), TIMEOUT));
328         assertTrue(mDevice.hasObject(By.text("Import Three").checked(false).enabled(false)));
329         assertTrue(mDevice.hasObject(By.text("Import Six").checked(false).enabled(false)));
330 
331         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
332             contactsProviderClient.close();
333         }
334     }
335 
nextImportCompleteBroadcast()336     private ListenableFuture<Intent> nextImportCompleteBroadcast() {
337         final SettableFuture<Intent> result = SettableFuture.create();
338         final BroadcastReceiver receiver = new BroadcastReceiver() {
339             @Override
340             public void onReceive(Context context, Intent intent) {
341                 result.set(intent);
342                 LocalBroadcastManager.getInstance(mContext).unregisterReceiver(this);
343             }
344         };
345         LocalBroadcastManager.getInstance(mContext).registerReceiver(receiver, new IntentFilter(
346                 SimImportService.BROADCAST_SIM_IMPORT_COMPLETE));
347         return result;
348     }
349 
toCursorRow(SimContact contact)350     private Object[] toCursorRow(SimContact contact) {
351         return new Object[] { contact.getId(), contact.getName(), contact.getPhone(), null };
352     }
353 
someSimCard()354     private SimCard someSimCard() {
355         return new SimCard("id", 1, "Carrier", "SIM", "18005550101", "us");
356     }
357 }
358