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