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 17package com.android.contacts.common.model.account; 18 19import android.content.Context; 20import android.provider.ContactsContract.CommonDataKinds.Email; 21import android.provider.ContactsContract.CommonDataKinds.Event; 22import android.provider.ContactsContract.CommonDataKinds.Im; 23import android.provider.ContactsContract.CommonDataKinds.Note; 24import android.provider.ContactsContract.CommonDataKinds.Organization; 25import android.provider.ContactsContract.CommonDataKinds.Photo; 26import android.provider.ContactsContract.CommonDataKinds.Relation; 27import android.provider.ContactsContract.CommonDataKinds.SipAddress; 28import android.provider.ContactsContract.CommonDataKinds.StructuredName; 29import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 30import android.provider.ContactsContract.CommonDataKinds.Website; 31import android.test.AndroidTestCase; 32import android.test.suitebuilder.annotation.SmallTest; 33import android.util.Log; 34 35import com.android.contacts.common.model.dataitem.DataKind; 36import com.android.contacts.common.unittest.R; 37import com.google.common.base.Objects; 38 39import 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 48public 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 will be initialized, but empty. 73 */ 74 public void testNoPackage() { 75 final ExternalAccountType type = new ExternalAccountType(getContext(), 76 "!!!no such package name!!!", false); 77 assertTrue(type.isInitialized()); 78 } 79 80 /** 81 * Initialize with the test package itself and see if EditSchema is correctly parsed. 82 */ 83 public void testEditSchema() { 84 final ExternalAccountType type = new ExternalAccountType(getContext(), 85 getTestContext().getPackageName(), false); 86 87 assertTrue(type.isInitialized()); 88 89 // Let's just check if the DataKinds are registered. 90 assertNotNull(type.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE)); 91 assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME)); 92 assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME)); 93 assertNotNull(type.getKindForMimetype(Email.CONTENT_ITEM_TYPE)); 94 assertNotNull(type.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE)); 95 assertNotNull(type.getKindForMimetype(Im.CONTENT_ITEM_TYPE)); 96 assertNotNull(type.getKindForMimetype(Organization.CONTENT_ITEM_TYPE)); 97 assertNotNull(type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE)); 98 assertNotNull(type.getKindForMimetype(Note.CONTENT_ITEM_TYPE)); 99 assertNotNull(type.getKindForMimetype(Website.CONTENT_ITEM_TYPE)); 100 assertNotNull(type.getKindForMimetype(SipAddress.CONTENT_ITEM_TYPE)); 101 assertNotNull(type.getKindForMimetype(Event.CONTENT_ITEM_TYPE)); 102 assertNotNull(type.getKindForMimetype(Relation.CONTENT_ITEM_TYPE)); 103 } 104 105 /** 106 * Initialize with "contacts_fallback.xml" and compare the DataKinds to those of 107 * {@link com.android.contacts.common.model.account.FallbackAccountType}. 108 */ 109 public void testEditSchema_fallback() { 110 final ExternalAccountType type = new ExternalAccountType(getContext(), 111 getTestContext().getPackageName(), false, 112 getTestContext().getResources().getXml(R.xml.contacts_fallback) 113 ); 114 115 assertTrue(type.isInitialized()); 116 117 // Create a fallback type with the same resource package name, and compare all the data 118 // kinds to its. 119 final AccountType reference = FallbackAccountType.createWithPackageNameForTest( 120 getContext(), type.resourcePackageName); 121 122 assertsDataKindEquals(reference.getSortedDataKinds(), type.getSortedDataKinds()); 123 } 124 125 public void testEditSchema_mustHaveChecks() { 126 checkEditSchema_mustHaveChecks(R.xml.missing_contacts_base, true); 127 checkEditSchema_mustHaveChecks(R.xml.missing_contacts_photo, false); 128 checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name, false); 129 checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr1, false); 130 checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr2, false); 131 checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr3, false); 132 checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr4, false); 133 checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr5, false); 134 checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr6, false); 135 checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr7, false); 136 } 137 138 private void checkEditSchema_mustHaveChecks(int xmlResId, boolean expectInitialized) { 139 final ExternalAccountType type = new ExternalAccountType(getContext(), 140 getTestContext().getPackageName(), false, 141 getTestContext().getResources().getXml(xmlResId) 142 ); 143 144 assertEquals(expectInitialized, type.isInitialized()); 145 } 146 147 /** 148 * Initialize with "contacts_readonly.xml" and see if all data kinds are correctly registered. 149 */ 150 public void testReadOnlyDefinition() { 151 final ExternalAccountType type = new ExternalAccountType(getContext(), 152 getTestContext().getPackageName(), false, 153 getTestContext().getResources().getXml(R.xml.contacts_readonly) 154 ); 155 assertTrue(type.isInitialized()); 156 157 // Shouldn't have a "null" mimetype. 158 assertTrue(type.getKindForMimetype(null) == null); 159 160 // 3 kinds are defined in XML and 4 are added by default. 161 assertEquals(4 + 3, type.getSortedDataKinds().size()); 162 163 // Check for the default kinds. 164 assertNotNull(type.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE)); 165 assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME)); 166 assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME)); 167 assertNotNull(type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE)); 168 169 // Check for type specific kinds. 170 DataKind kind = type.getKindForMimetype("vnd.android.cursor.item/a.b.c"); 171 assertNotNull(kind); 172 // No check for icon -- we actually just ignore it. 173 assertEquals("data1", ((BaseAccountType.SimpleInflater) kind.actionHeader) 174 .getColumnNameForTest()); 175 assertEquals("data2", ((BaseAccountType.SimpleInflater) kind.actionBody) 176 .getColumnNameForTest()); 177 178 kind = type.getKindForMimetype("vnd.android.cursor.item/d.e.f"); 179 assertNotNull(kind); 180 assertEquals("data3", ((BaseAccountType.SimpleInflater) kind.actionHeader) 181 .getColumnNameForTest()); 182 assertEquals("data4", ((BaseAccountType.SimpleInflater) kind.actionBody) 183 .getColumnNameForTest()); 184 185 kind = type.getKindForMimetype("vnd.android.cursor.item/xyz"); 186 assertNotNull(kind); 187 assertEquals("data5", ((BaseAccountType.SimpleInflater) kind.actionHeader) 188 .getColumnNameForTest()); 189 assertEquals("data6", ((BaseAccountType.SimpleInflater) kind.actionBody) 190 .getColumnNameForTest()); 191 } 192 193 private static void assertsDataKindEquals(List<DataKind> expectedKinds, 194 List<DataKind> actualKinds) { 195 final int count = Math.max(actualKinds.size(), expectedKinds.size()); 196 for (int i = 0; i < count; i++) { 197 String actual = actualKinds.size() > i ? actualKinds.get(i).toString() : "(n/a)"; 198 String expected = expectedKinds.size() > i ? expectedKinds.get(i).toString() : "(n/a)"; 199 200 // Because assertEquals()'s output is not very friendly when comparing two similar 201 // strings, we manually do the check. 202 if (!Objects.equal(actual, expected)) { 203 final int commonPrefixEnd = findCommonPrefixEnd(actual, expected); 204 fail("Kind #" + i 205 + "\n[Actual]\n" + insertMarkerAt(actual, commonPrefixEnd) 206 + "\n[Expected]\n" + insertMarkerAt(expected, commonPrefixEnd)); 207 } 208 } 209 } 210 211 private static int findCommonPrefixEnd(String s1, String s2) { 212 int i = 0; 213 for (;;) { 214 final boolean s1End = (s1.length() <= i); 215 final boolean s2End = (s2.length() <= i); 216 if (s1End || s2End) { 217 return i; 218 } 219 if (s1.charAt(i) != s2.charAt(i)) { 220 return i; 221 } 222 i++; 223 } 224 } 225 226 private static String insertMarkerAt(String s, int position) { 227 final String MARKER = "***"; 228 if (position > s.length()) { 229 return s + MARKER; 230 } else { 231 return new StringBuilder(s).insert(position, MARKER).toString(); 232 } 233 } 234} 235