blob: 4057ee8211d9015766ee3fd1b1c1102802b9c36d [file] [log] [blame]
Marcus Hagerottbea2b852016-08-11 14:55:52 -07001/*
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
17package com.android.contacts;
18
Marcus Hagerott0d1de542016-08-18 11:30:15 -070019import android.content.ContentProviderOperation;
Marcus Hagerottbea2b852016-08-11 14:55:52 -070020import android.content.ContentResolver;
21import android.content.ContentUris;
22import android.content.ContentValues;
23import android.content.Context;
Marcus Hagerott0d1de542016-08-18 11:30:15 -070024import android.content.OperationApplicationException;
Marcus Hagerottbea2b852016-08-11 14:55:52 -070025import android.database.Cursor;
26import android.net.Uri;
27import android.os.Bundle;
Marcus Hagerott0d1de542016-08-18 11:30:15 -070028import android.os.RemoteException;
Marcus Hagerottbea2b852016-08-11 14:55:52 -070029import android.provider.ContactsContract;
30import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
31import android.provider.ContactsContract.Data;
32import android.test.InstrumentationTestCase;
Walter Jang7e79b522016-08-23 09:33:41 -070033import android.test.suitebuilder.annotation.MediumTest;
Marcus Hagerottbea2b852016-08-11 14:55:52 -070034
Gary Mai69c182a2016-12-05 13:07:03 -080035import com.android.contacts.model.account.AccountWithDataSet;
Marcus Hagerottbea2b852016-08-11 14:55:52 -070036
Marcus Hagerott0d1de542016-08-18 11:30:15 -070037import java.util.ArrayList;
38import java.util.List;
39
Marcus Hagerottbea2b852016-08-11 14:55:52 -070040/**
41 * Tests of GroupsDaoImpl that perform DB operations directly against CP2
42 */
Walter Jang7e79b522016-08-23 09:33:41 -070043@MediumTest
Marcus Hagerottbea2b852016-08-11 14:55:52 -070044public class GroupsDaoIntegrationTests extends InstrumentationTestCase {
45
Marcus Hagerott0d1de542016-08-18 11:30:15 -070046 private ContentResolver mResolver;
47 private List<Uri> mTestRecords;
Marcus Hagerottbea2b852016-08-11 14:55:52 -070048
49 @Override
50 protected void setUp() throws Exception {
51 super.setUp();
52
Marcus Hagerott0d1de542016-08-18 11:30:15 -070053 mTestRecords = new ArrayList<>();
54 mResolver = getContext().getContentResolver();
Marcus Hagerottbea2b852016-08-11 14:55:52 -070055 }
56
57 @Override
58 protected void tearDown() throws Exception {
59 super.tearDown();
60
61 // Cleanup anything leftover by the tests.
Marcus Hagerott0d1de542016-08-18 11:30:15 -070062 cleanupTestRecords();
63 mTestRecords.clear();
Marcus Hagerottbea2b852016-08-11 14:55:52 -070064 }
65
66 public void test_createGroup_createsGroupWithCorrectTitle() throws Exception {
Marcus Hagerott0d1de542016-08-18 11:30:15 -070067 final ContactSaveService.GroupsDao sut = createDao();
68 final Uri uri = sut.create("Test Create Group", getLocalAccount());
Marcus Hagerottbea2b852016-08-11 14:55:52 -070069
70 assertNotNull(uri);
71 assertGroupHasTitle(uri, "Test Create Group");
72 }
73
74 public void test_deleteEmptyGroup_marksRowDeleted() throws Exception {
Marcus Hagerott0d1de542016-08-18 11:30:15 -070075 final ContactSaveService.GroupsDao sut = createDao();
76 final Uri uri = sut.create("Test Delete Group", getLocalAccount());
Marcus Hagerottbea2b852016-08-11 14:55:52 -070077
78 assertEquals(1, sut.delete(uri));
79
Marcus Hagerott0d1de542016-08-18 11:30:15 -070080 final Cursor cursor = mResolver.query(uri, null, null, null, null, null);
Marcus Hagerottbea2b852016-08-11 14:55:52 -070081 try {
82 cursor.moveToFirst();
Marcus Hagerott0d1de542016-08-18 11:30:15 -070083 assertEquals(1, cursor.getInt(cursor.getColumnIndexOrThrow(
84 ContactsContract.Groups.DELETED)));
Marcus Hagerottbea2b852016-08-11 14:55:52 -070085 } finally {
86 cursor.close();
87 }
88 }
89
90 public void test_undoDeleteEmptyGroup_createsGroupWithMatchingTitle() throws Exception {
Marcus Hagerott0d1de542016-08-18 11:30:15 -070091 final ContactSaveService.GroupsDao sut = createDao();
92 final Uri uri = sut.create("Test Undo Delete Empty Group", getLocalAccount());
Marcus Hagerottbea2b852016-08-11 14:55:52 -070093
Marcus Hagerott0d1de542016-08-18 11:30:15 -070094 final Bundle undoData = sut.captureDeletionUndoData(uri);
Marcus Hagerottbea2b852016-08-11 14:55:52 -070095
96 assertEquals(1, sut.delete(uri));
97
Marcus Hagerott0d1de542016-08-18 11:30:15 -070098 final Uri groupUri = sut.undoDeletion(undoData);
Marcus Hagerottbea2b852016-08-11 14:55:52 -070099
100 assertGroupHasTitle(groupUri, "Test Undo Delete Empty Group");
101 }
102
103 public void test_deleteNonEmptyGroup_removesGroupAndMembers() throws Exception {
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700104 final ContactSaveService.GroupsDao sut = createDao();
105 final Uri groupUri = sut.create("Test delete non-empty group", getLocalAccount());
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700106
107 final long groupId = ContentUris.parseId(groupUri);
108 addMemberToGroup(ContentUris.parseId(createRawContact()), groupId);
109 addMemberToGroup(ContentUris.parseId(createRawContact()), groupId);
110
111 assertEquals(1, sut.delete(groupUri));
112
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700113 final Cursor cursor = mResolver.query(Data.CONTENT_URI, null,
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700114 Data.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?",
115 new String[] { GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(groupId) },
116 null, null);
117
118 try {
119 cursor.moveToFirst();
120 // This is more of a characterization test since our code isn't manually deleting
121 // the membership rows just the group but this still helps document the expected
122 // behavior.
123 assertEquals(0, cursor.getCount());
124 } finally {
125 cursor.close();
126 }
127 }
128
129 public void test_undoDeleteNonEmptyGroup_restoresGroupAndMembers() throws Exception {
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700130 final ContactSaveService.GroupsDao sut = createDao();
131 final Uri groupUri = sut.create("Test undo delete non-empty group", getLocalAccount());
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700132
133 final long groupId = ContentUris.parseId(groupUri);
134 addMemberToGroup(ContentUris.parseId(createRawContact()), groupId);
135 addMemberToGroup(ContentUris.parseId(createRawContact()), groupId);
136
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700137 final Bundle undoData = sut.captureDeletionUndoData(groupUri);
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700138
139 sut.delete(groupUri);
140
141 final Uri recreatedGroup = sut.undoDeletion(undoData);
142
143 final long newGroupId = ContentUris.parseId(recreatedGroup);
144
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700145 final Cursor cursor = mResolver.query(Data.CONTENT_URI, null,
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700146 Data.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?",
147 new String[] { GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(newGroupId) },
148 null, null);
149
150 try {
151 assertEquals(2, cursor.getCount());
152 } finally {
153 cursor.close();
154 }
155 }
156
157 public void test_captureUndoDataForDeletedGroup_returnsEmptyBundle() {
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700158 final ContactSaveService.GroupsDao sut = createDao();
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700159
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700160 final Uri uri = sut.create("a deleted group", getLocalAccount());
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700161 sut.delete(uri);
162
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700163 final Bundle undoData = sut.captureDeletionUndoData(uri);
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700164
165 assertTrue(undoData.isEmpty());
166 }
167
168 public void test_captureUndoDataForNonExistentGroup_returnsEmptyBundle() {
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700169 final ContactSaveService.GroupsDao sut = createDao();
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700170
171 // This test could potentially be flaky if this ID exists for some reason. 10 is subtracted
172 // to reduce the likelihood of this happening; some other test may use Integer.MAX_VALUE
173 // or nearby values to cover some special case or boundary condition.
174 final long nonExistentId = Integer.MAX_VALUE - 10;
175
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700176 final Bundle undoData = sut.captureDeletionUndoData(ContentUris
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700177 .withAppendedId(ContactsContract.Groups.CONTENT_URI, nonExistentId));
178
179 assertTrue(undoData.isEmpty());
180 }
181
182 public void test_undoWithEmptyBundle_doesNothing() {
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700183 final ContactSaveService.GroupsDao sut = createDao();
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700184
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700185 final Uri uri = sut.undoDeletion(new Bundle());
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700186
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700187 assertNull(uri);
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700188 }
189
190 public void test_undoDeleteEmptyGroupWithMissingMembersKey_shouldRecreateGroup() {
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700191 final ContactSaveService.GroupsDao sut = createDao();
192 final Uri groupUri = sut.create("Test undo delete null memberIds", getLocalAccount());
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700193
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700194 final Bundle undoData = sut.captureDeletionUndoData(groupUri);
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700195 undoData.remove(ContactSaveService.GroupsDaoImpl.KEY_GROUP_MEMBERS);
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700196 sut.delete(groupUri);
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700197
198 sut.undoDeletion(undoData);
199
200 assertGroupWithTitleExists("Test undo delete null memberIds");
201 }
202
203 private void assertGroupHasTitle(Uri groupUri, String title) {
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700204 final Cursor cursor = mResolver.query(groupUri,
205 new String[] { ContactsContract.Groups.TITLE },
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700206 ContactsContract.Groups.DELETED + "=?",
207 new String[] { "0" }, null, null);
208 try {
209 assertTrue("Group does not have title \"" + title + "\"",
210 cursor.getCount() == 1 && cursor.moveToFirst() &&
211 title.equals(cursor.getString(0)));
212 } finally {
213 cursor.close();
214 }
215 }
216
217 private void assertGroupWithTitleExists(String title) {
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700218 final Cursor cursor = mResolver.query(ContactsContract.Groups.CONTENT_URI, null,
Marcus Hagerottd3cc63d2016-08-17 16:53:39 -0700219 ContactsContract.Groups.TITLE + "=? AND " +
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700220 ContactsContract.Groups.DELETED + "=?",
221 new String[] { title, "0" }, null, null);
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700222 try {
Marcus Hagerottd3cc63d2016-08-17 16:53:39 -0700223 assertTrue("No group exists with title \"" + title + "\"", cursor.getCount() > 0);
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700224 } finally {
225 cursor.close();
226 }
227 }
228
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700229 public ContactSaveService.GroupsDao createDao() {
230 return new GroupsDaoWrapper(new ContactSaveService.GroupsDaoImpl(getContext()));
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700231 }
232
233 private Uri createRawContact() {
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700234 final ContentValues values = new ContentValues();
235 values.putNull(ContactsContract.RawContacts.ACCOUNT_NAME);
236 values.putNull(ContactsContract.RawContacts.ACCOUNT_TYPE);
237 final Uri result = mResolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
238 mTestRecords.add(result);
239 return result;
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700240 }
241
242 private Uri addMemberToGroup(long rawContactId, long groupId) {
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700243 final ContentValues values = new ContentValues();
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700244 values.put(Data.RAW_CONTACT_ID, rawContactId);
245 values.put(Data.MIMETYPE,
246 GroupMembership.CONTENT_ITEM_TYPE);
247 values.put(GroupMembership.GROUP_ROW_ID, groupId);
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700248
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700249 // Dont' need to add to testRecords because it will be cleaned up when parent raw_contact
250 // is deleted.
251 return mResolver.insert(Data.CONTENT_URI, values);
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700252 }
253
254 private Context getContext() {
255 return getInstrumentation().getTargetContext();
256 }
Marcus Hagerott0d1de542016-08-18 11:30:15 -0700257
258 private AccountWithDataSet getLocalAccount() {
259 return new AccountWithDataSet(null, null, null);
260 }
261
262 private void cleanupTestRecords() throws RemoteException, OperationApplicationException {
263 final ArrayList<ContentProviderOperation> ops = new ArrayList<>();
264 for (Uri uri : mTestRecords) {
265 if (uri == null) continue;
266 ops.add(ContentProviderOperation
267 .newDelete(uri.buildUpon()
268 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
269 .build())
270 .build());
271 }
272 mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
273 }
274
275 private class GroupsDaoWrapper implements ContactSaveService.GroupsDao {
276 private final ContactSaveService.GroupsDao mDelegate;
277
278 public GroupsDaoWrapper(ContactSaveService.GroupsDao delegate) {
279 mDelegate = delegate;
280 }
281
282 @Override
283 public Uri create(String title, AccountWithDataSet account) {
284 final Uri result = mDelegate.create(title, account);
285 mTestRecords.add(result);
286 return result;
287 }
288
289 @Override
290 public int delete(Uri groupUri) {
291 return mDelegate.delete(groupUri);
292 }
293
294 @Override
295 public Bundle captureDeletionUndoData(Uri groupUri) {
296 return mDelegate.captureDeletionUndoData(groupUri);
297 }
298
299 @Override
300 public Uri undoDeletion(Bundle undoData) {
301 final Uri result = mDelegate.undoDeletion(undoData);
302 mTestRecords.add(result);
303 return result;
304 }
305 }
Marcus Hagerottbea2b852016-08-11 14:55:52 -0700306}