Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 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.contacts.common.model.account; |
| 18 | |
| 19 | import android.content.Context; |
| 20 | import android.provider.ContactsContract.CommonDataKinds.Email; |
| 21 | import android.provider.ContactsContract.CommonDataKinds.Event; |
| 22 | import android.provider.ContactsContract.CommonDataKinds.Im; |
| 23 | import android.provider.ContactsContract.CommonDataKinds.Note; |
| 24 | import android.provider.ContactsContract.CommonDataKinds.Organization; |
| 25 | import android.provider.ContactsContract.CommonDataKinds.Photo; |
| 26 | import android.provider.ContactsContract.CommonDataKinds.Relation; |
| 27 | import android.provider.ContactsContract.CommonDataKinds.SipAddress; |
| 28 | import android.provider.ContactsContract.CommonDataKinds.StructuredName; |
| 29 | import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; |
| 30 | import android.provider.ContactsContract.CommonDataKinds.Website; |
Jay Shrauner | 7bae063 | 2015-08-27 16:23:44 -0700 | [diff] [blame] | 31 | import android.test.InstrumentationTestCase; |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 32 | import android.test.suitebuilder.annotation.SmallTest; |
Brandon Maxwell | 22f3d09 | 2015-11-11 17:46:46 -0800 | [diff] [blame] | 33 | import android.test.suitebuilder.annotation.Suppress; |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 34 | |
| 35 | import com.android.contacts.common.model.dataitem.DataKind; |
Yorke Lee | d0a3388 | 2015-10-28 18:26:19 -0700 | [diff] [blame] | 36 | import com.android.contacts.common.tests.R; |
Gary Mai | 7a6daea | 2016-10-10 15:41:48 -0700 | [diff] [blame] | 37 | |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 38 | import com.google.common.base.Objects; |
| 39 | |
Gary Mai | 7a6daea | 2016-10-10 15:41:48 -0700 | [diff] [blame] | 40 | import java.util.ArrayList; |
| 41 | import java.util.Arrays; |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 42 | import java.util.List; |
| 43 | |
| 44 | /** |
| 45 | * Test case for {@link com.android.contacts.common.model.account.ExternalAccountType}. |
| 46 | * |
| 47 | * adb shell am instrument -w -e class com.android.contacts.model.ExternalAccountTypeTest \ |
| 48 | com.android.contacts.tests/android.test.InstrumentationTestRunner |
| 49 | */ |
| 50 | @SmallTest |
Jay Shrauner | 7bae063 | 2015-08-27 16:23:44 -0700 | [diff] [blame] | 51 | public class ExternalAccountTypeTest extends InstrumentationTestCase { |
Gary Mai | 7a6daea | 2016-10-10 15:41:48 -0700 | [diff] [blame] | 52 | private static ArrayList<String> mDefaultMimetypes = new ArrayList<>(Arrays.asList( |
| 53 | StructuredName.CONTENT_ITEM_TYPE, |
| 54 | DataKind.PSEUDO_MIME_TYPE_NAME, |
| 55 | DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME, |
| 56 | Photo.CONTENT_ITEM_TYPE |
| 57 | )); |
Brandon Maxwell | 22f3d09 | 2015-11-11 17:46:46 -0800 | [diff] [blame] | 58 | |
| 59 | @Suppress |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 60 | public void testResolveExternalResId() { |
Jay Shrauner | 0545258 | 2015-08-29 11:34:25 -0700 | [diff] [blame] | 61 | final Context c = getInstrumentation().getTargetContext(); |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 62 | // In this test we use the test package itself as an external package. |
Jay Shrauner | 7bae063 | 2015-08-27 16:23:44 -0700 | [diff] [blame] | 63 | final String packageName = getInstrumentation().getContext().getPackageName(); |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 64 | |
| 65 | // Resource name empty. |
| 66 | assertEquals(-1, ExternalAccountType.resolveExternalResId(c, null, packageName, "")); |
| 67 | assertEquals(-1, ExternalAccountType.resolveExternalResId(c, "", packageName, "")); |
| 68 | |
| 69 | // Name doesn't begin with '@' |
| 70 | assertEquals(-1, ExternalAccountType.resolveExternalResId(c, "x", packageName, "")); |
| 71 | |
| 72 | // Invalid resource name |
| 73 | assertEquals(-1, ExternalAccountType.resolveExternalResId(c, "@", packageName, "")); |
| 74 | assertEquals(-1, ExternalAccountType.resolveExternalResId(c, "@a", packageName, "")); |
| 75 | assertEquals(-1, ExternalAccountType.resolveExternalResId(c, "@a/b", packageName, "")); |
| 76 | |
| 77 | // Valid resource name |
| 78 | assertEquals(R.string.test_string, ExternalAccountType.resolveExternalResId(c, |
| 79 | "@string/test_string", packageName, "")); |
| 80 | } |
| 81 | |
Wenyi Wang | daa6fb8 | 2016-11-21 15:37:36 -0800 | [diff] [blame] | 82 | public void testIsFromTestApp() { |
| 83 | assertTrue(ExternalAccountType.isFromTestApp("com.google.android.contacts.tests")); |
| 84 | assertFalse(ExternalAccountType.isFromTestApp("com.google.android.contacts")); |
| 85 | } |
| 86 | |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 87 | /** |
Walter Jang | 8df87f1 | 2015-07-10 12:19:24 -0700 | [diff] [blame] | 88 | * Initialize with an invalid package name and see if type will be initialized, but empty. |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 89 | */ |
| 90 | public void testNoPackage() { |
Jay Shrauner | 0545258 | 2015-08-29 11:34:25 -0700 | [diff] [blame] | 91 | final ExternalAccountType type = new ExternalAccountType(getInstrumentation().getTargetContext(), |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 92 | "!!!no such package name!!!", false); |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 93 | assertTrue(type.isInitialized()); |
| 94 | } |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 95 | |
| 96 | /** |
| 97 | * Initialize with the test package itself and see if EditSchema is correctly parsed. |
| 98 | */ |
Brandon Maxwell | 22f3d09 | 2015-11-11 17:46:46 -0800 | [diff] [blame] | 99 | @Suppress |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 100 | public void testEditSchema() { |
Jay Shrauner | 0545258 | 2015-08-29 11:34:25 -0700 | [diff] [blame] | 101 | final ExternalAccountType type = new ExternalAccountType(getInstrumentation().getTargetContext(), |
Jay Shrauner | 7bae063 | 2015-08-27 16:23:44 -0700 | [diff] [blame] | 102 | getInstrumentation().getContext().getPackageName(), false); |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 103 | |
| 104 | assertTrue(type.isInitialized()); |
| 105 | |
| 106 | // Let's just check if the DataKinds are registered. |
| 107 | assertNotNull(type.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE)); |
Gary Mai | 7a6daea | 2016-10-10 15:41:48 -0700 | [diff] [blame] | 108 | assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_NAME)); |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 109 | assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME)); |
| 110 | assertNotNull(type.getKindForMimetype(Email.CONTENT_ITEM_TYPE)); |
| 111 | assertNotNull(type.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE)); |
| 112 | assertNotNull(type.getKindForMimetype(Im.CONTENT_ITEM_TYPE)); |
| 113 | assertNotNull(type.getKindForMimetype(Organization.CONTENT_ITEM_TYPE)); |
| 114 | assertNotNull(type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE)); |
| 115 | assertNotNull(type.getKindForMimetype(Note.CONTENT_ITEM_TYPE)); |
| 116 | assertNotNull(type.getKindForMimetype(Website.CONTENT_ITEM_TYPE)); |
| 117 | assertNotNull(type.getKindForMimetype(SipAddress.CONTENT_ITEM_TYPE)); |
| 118 | assertNotNull(type.getKindForMimetype(Event.CONTENT_ITEM_TYPE)); |
| 119 | assertNotNull(type.getKindForMimetype(Relation.CONTENT_ITEM_TYPE)); |
| 120 | } |
| 121 | |
| 122 | /** |
| 123 | * Initialize with "contacts_fallback.xml" and compare the DataKinds to those of |
| 124 | * {@link com.android.contacts.common.model.account.FallbackAccountType}. |
| 125 | */ |
| 126 | public void testEditSchema_fallback() { |
Jay Shrauner | 0545258 | 2015-08-29 11:34:25 -0700 | [diff] [blame] | 127 | final ExternalAccountType type = new ExternalAccountType(getInstrumentation().getTargetContext(), |
Jay Shrauner | 7bae063 | 2015-08-27 16:23:44 -0700 | [diff] [blame] | 128 | getInstrumentation().getContext().getPackageName(), false, |
| 129 | getInstrumentation().getContext().getResources().getXml(R.xml.contacts_fallback) |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 130 | ); |
| 131 | |
| 132 | assertTrue(type.isInitialized()); |
| 133 | |
| 134 | // Create a fallback type with the same resource package name, and compare all the data |
| 135 | // kinds to its. |
| 136 | final AccountType reference = FallbackAccountType.createWithPackageNameForTest( |
Jay Shrauner | 0545258 | 2015-08-29 11:34:25 -0700 | [diff] [blame] | 137 | getInstrumentation().getTargetContext(), type.resourcePackageName); |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 138 | |
| 139 | assertsDataKindEquals(reference.getSortedDataKinds(), type.getSortedDataKinds()); |
| 140 | } |
| 141 | |
| 142 | public void testEditSchema_mustHaveChecks() { |
| 143 | checkEditSchema_mustHaveChecks(R.xml.missing_contacts_base, true); |
| 144 | checkEditSchema_mustHaveChecks(R.xml.missing_contacts_photo, false); |
| 145 | checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name, false); |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 146 | checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr2, false); |
| 147 | checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr3, false); |
| 148 | checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr4, false); |
| 149 | checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr5, false); |
| 150 | checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr6, false); |
| 151 | checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr7, false); |
| 152 | } |
| 153 | |
| 154 | private void checkEditSchema_mustHaveChecks(int xmlResId, boolean expectInitialized) { |
Jay Shrauner | 0545258 | 2015-08-29 11:34:25 -0700 | [diff] [blame] | 155 | final ExternalAccountType type = new ExternalAccountType(getInstrumentation().getTargetContext(), |
Jay Shrauner | 7bae063 | 2015-08-27 16:23:44 -0700 | [diff] [blame] | 156 | getInstrumentation().getContext().getPackageName(), false, |
| 157 | getInstrumentation().getContext().getResources().getXml(xmlResId) |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 158 | ); |
| 159 | |
| 160 | assertEquals(expectInitialized, type.isInitialized()); |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | * Initialize with "contacts_readonly.xml" and see if all data kinds are correctly registered. |
| 165 | */ |
| 166 | public void testReadOnlyDefinition() { |
Jay Shrauner | 0545258 | 2015-08-29 11:34:25 -0700 | [diff] [blame] | 167 | final ExternalAccountType type = new ExternalAccountType(getInstrumentation().getTargetContext(), |
Jay Shrauner | 7bae063 | 2015-08-27 16:23:44 -0700 | [diff] [blame] | 168 | getInstrumentation().getContext().getPackageName(), false, |
| 169 | getInstrumentation().getContext().getResources().getXml(R.xml.contacts_readonly) |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 170 | ); |
| 171 | assertTrue(type.isInitialized()); |
| 172 | |
| 173 | // Shouldn't have a "null" mimetype. |
| 174 | assertTrue(type.getKindForMimetype(null) == null); |
| 175 | |
Gary Mai | 7a6daea | 2016-10-10 15:41:48 -0700 | [diff] [blame] | 176 | // 3 kinds are defined in XML and the rest are added by default. |
| 177 | assertEquals(3 + mDefaultMimetypes.size(), type.getSortedDataKinds().size()); |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 178 | |
| 179 | // Check for the default kinds. |
Gary Mai | 7a6daea | 2016-10-10 15:41:48 -0700 | [diff] [blame] | 180 | for (String mimetype : mDefaultMimetypes) { |
| 181 | assertNotNull(type.getKindForMimetype(mimetype)); |
| 182 | } |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 183 | |
| 184 | // Check for type specific kinds. |
| 185 | DataKind kind = type.getKindForMimetype("vnd.android.cursor.item/a.b.c"); |
| 186 | assertNotNull(kind); |
| 187 | // No check for icon -- we actually just ignore it. |
| 188 | assertEquals("data1", ((BaseAccountType.SimpleInflater) kind.actionHeader) |
| 189 | .getColumnNameForTest()); |
| 190 | assertEquals("data2", ((BaseAccountType.SimpleInflater) kind.actionBody) |
| 191 | .getColumnNameForTest()); |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 192 | |
| 193 | kind = type.getKindForMimetype("vnd.android.cursor.item/d.e.f"); |
| 194 | assertNotNull(kind); |
| 195 | assertEquals("data3", ((BaseAccountType.SimpleInflater) kind.actionHeader) |
| 196 | .getColumnNameForTest()); |
| 197 | assertEquals("data4", ((BaseAccountType.SimpleInflater) kind.actionBody) |
| 198 | .getColumnNameForTest()); |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 199 | |
| 200 | kind = type.getKindForMimetype("vnd.android.cursor.item/xyz"); |
| 201 | assertNotNull(kind); |
| 202 | assertEquals("data5", ((BaseAccountType.SimpleInflater) kind.actionHeader) |
| 203 | .getColumnNameForTest()); |
| 204 | assertEquals("data6", ((BaseAccountType.SimpleInflater) kind.actionBody) |
| 205 | .getColumnNameForTest()); |
Chiao Cheng | e88fcd3 | 2012-11-13 18:38:05 -0800 | [diff] [blame] | 206 | } |
| 207 | |
| 208 | private static void assertsDataKindEquals(List<DataKind> expectedKinds, |
| 209 | List<DataKind> actualKinds) { |
| 210 | final int count = Math.max(actualKinds.size(), expectedKinds.size()); |
| 211 | for (int i = 0; i < count; i++) { |
| 212 | String actual = actualKinds.size() > i ? actualKinds.get(i).toString() : "(n/a)"; |
| 213 | String expected = expectedKinds.size() > i ? expectedKinds.get(i).toString() : "(n/a)"; |
| 214 | |
| 215 | // Because assertEquals()'s output is not very friendly when comparing two similar |
| 216 | // strings, we manually do the check. |
| 217 | if (!Objects.equal(actual, expected)) { |
| 218 | final int commonPrefixEnd = findCommonPrefixEnd(actual, expected); |
| 219 | fail("Kind #" + i |
| 220 | + "\n[Actual]\n" + insertMarkerAt(actual, commonPrefixEnd) |
| 221 | + "\n[Expected]\n" + insertMarkerAt(expected, commonPrefixEnd)); |
| 222 | } |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | private static int findCommonPrefixEnd(String s1, String s2) { |
| 227 | int i = 0; |
| 228 | for (;;) { |
| 229 | final boolean s1End = (s1.length() <= i); |
| 230 | final boolean s2End = (s2.length() <= i); |
| 231 | if (s1End || s2End) { |
| 232 | return i; |
| 233 | } |
| 234 | if (s1.charAt(i) != s2.charAt(i)) { |
| 235 | return i; |
| 236 | } |
| 237 | i++; |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | private static String insertMarkerAt(String s, int position) { |
| 242 | final String MARKER = "***"; |
| 243 | if (position > s.length()) { |
| 244 | return s + MARKER; |
| 245 | } else { |
| 246 | return new StringBuilder(s).insert(position, MARKER).toString(); |
| 247 | } |
| 248 | } |
| 249 | } |