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.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; 31 import android.test.InstrumentationTestCase; 32 import android.test.suitebuilder.annotation.SmallTest; 33 import android.test.suitebuilder.annotation.Suppress; 34 35 import com.android.contacts.tests.R; 36 import com.android.contacts.model.dataitem.DataKind; 37 38 import com.google.common.base.Objects; 39 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.List; 43 44 /** 45 * Test case for {@link com.android.contacts.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 51 public class ExternalAccountTypeTest extends InstrumentationTestCase { 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 )); 58 59 @Suppress testResolveExternalResId()60 public void testResolveExternalResId() { 61 final Context c = getInstrumentation().getTargetContext(); 62 // In this test we use the test package itself as an external package. 63 final String packageName = getInstrumentation().getContext().getPackageName(); 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 testIsFromTestApp()82 public void testIsFromTestApp() { 83 assertTrue(ExternalAccountType.isFromTestApp("com.google.android.contacts.tests")); 84 assertFalse(ExternalAccountType.isFromTestApp("com.google.android.contacts")); 85 } 86 87 /** 88 * Initialize with an invalid package name and see if type will be initialized, but empty. 89 */ testNoPackage()90 public void testNoPackage() { 91 final ExternalAccountType type = new ExternalAccountType(getInstrumentation().getTargetContext(), 92 "!!!no such package name!!!", false); 93 assertTrue(type.isInitialized()); 94 } 95 96 /** 97 * Initialize with the test package itself and see if EditSchema is correctly parsed. 98 */ 99 @Suppress testEditSchema()100 public void testEditSchema() { 101 final ExternalAccountType type = new ExternalAccountType(getInstrumentation().getTargetContext(), 102 getInstrumentation().getContext().getPackageName(), false); 103 104 assertTrue(type.isInitialized()); 105 106 // Let's just check if the DataKinds are registered. 107 assertNotNull(type.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE)); 108 assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_NAME)); 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.model.account.FallbackAccountType}. 125 */ testEditSchema_fallback()126 public void testEditSchema_fallback() { 127 final ExternalAccountType type = new ExternalAccountType(getInstrumentation().getTargetContext(), 128 getInstrumentation().getContext().getPackageName(), false, 129 getInstrumentation().getContext().getResources().getXml(R.xml.contacts_fallback) 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( 137 getInstrumentation().getTargetContext(), type.resourcePackageName); 138 139 assertsDataKindEquals(reference.getSortedDataKinds(), type.getSortedDataKinds()); 140 } 141 testEditSchema_mustHaveChecks()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); 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 checkEditSchema_mustHaveChecks(int xmlResId, boolean expectInitialized)154 private void checkEditSchema_mustHaveChecks(int xmlResId, boolean expectInitialized) { 155 final ExternalAccountType type = new ExternalAccountType(getInstrumentation().getTargetContext(), 156 getInstrumentation().getContext().getPackageName(), false, 157 getInstrumentation().getContext().getResources().getXml(xmlResId) 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 */ testReadOnlyDefinition()166 public void testReadOnlyDefinition() { 167 final ExternalAccountType type = new ExternalAccountType(getInstrumentation().getTargetContext(), 168 getInstrumentation().getContext().getPackageName(), false, 169 getInstrumentation().getContext().getResources().getXml(R.xml.contacts_readonly) 170 ); 171 assertTrue(type.isInitialized()); 172 173 // Shouldn't have a "null" mimetype. 174 assertTrue(type.getKindForMimetype(null) == null); 175 176 // 3 kinds are defined in XML and the rest are added by default. 177 assertEquals(3 + mDefaultMimetypes.size(), type.getSortedDataKinds().size()); 178 179 // Check for the default kinds. 180 for (String mimetype : mDefaultMimetypes) { 181 assertNotNull(type.getKindForMimetype(mimetype)); 182 } 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()); 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()); 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()); 206 } 207 assertsDataKindEquals(List<DataKind> expectedKinds, List<DataKind> actualKinds)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 findCommonPrefixEnd(String s1, String s2)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 insertMarkerAt(String s, int position)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 } 250