/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.contacts.model;
import com.android.contacts.tests.R;
import android.content.Context;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Event;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Note;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.CommonDataKinds.Relation;
import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.CommonDataKinds.Website;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import java.util.List;
import libcore.util.Objects;
/**
* Test case for {@link ExternalAccountType}.
*
* adb shell am instrument -w -e class com.android.contacts.model.ExternalAccountTypeTest \
com.android.contacts.tests/android.test.InstrumentationTestRunner
*/
@SmallTest
public class ExternalAccountTypeTest extends AndroidTestCase {
public void testResolveExternalResId() {
final Context c = getContext();
// In this test we use the test package itself as an external package.
final String packageName = getTestContext().getPackageName();
// Resource name empty.
assertEquals(-1, ExternalAccountType.resolveExternalResId(c, null, packageName, ""));
assertEquals(-1, ExternalAccountType.resolveExternalResId(c, "", packageName, ""));
// Name doesn't begin with '@'
assertEquals(-1, ExternalAccountType.resolveExternalResId(c, "x", packageName, ""));
// Invalid resource name
assertEquals(-1, ExternalAccountType.resolveExternalResId(c, "@", packageName, ""));
assertEquals(-1, ExternalAccountType.resolveExternalResId(c, "@a", packageName, ""));
assertEquals(-1, ExternalAccountType.resolveExternalResId(c, "@a/b", packageName, ""));
// Valid resource name
assertEquals(R.string.test_string, ExternalAccountType.resolveExternalResId(c,
"@string/test_string", packageName, ""));
}
/**
* Initialize with an invalid package name and see if type type will *not* be initialized.
*/
public void testNoPackage() {
final ExternalAccountType type = new ExternalAccountType(getContext(),
"!!!no such package name!!!", false);
assertFalse(type.isInitialized());
}
/**
* Initialize with the name of an existing package, which has no contacts.xml metadata.
*/
public void testNoMetadata() {
// Use the main application package, which does exist, but has no contacts.xml in it.
String packageName = getContext().getPackageName();
final ExternalAccountType type = new ExternalAccountType(getContext(),
packageName, false);
assertTrue(type.isInitialized());
}
/**
* Initialize with the test package itself and see if EditSchema is correctly parsed.
*/
public void testEditSchema() {
final ExternalAccountType type = new ExternalAccountType(getContext(),
getTestContext().getPackageName(), false);
assertTrue(type.isInitialized());
// Let's just check if the DataKinds are registered.
assertNotNull(type.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME));
assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME));
assertNotNull(type.getKindForMimetype(Email.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(Im.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(Organization.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(Note.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(Website.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(SipAddress.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(Event.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(Relation.CONTENT_ITEM_TYPE));
}
/**
* Initialize with "contacts_fallback.xml" and compare the DataKinds to those of
* {@link FallbackAccountType}.
*/
public void testEditSchema_fallback() {
final ExternalAccountType type = new ExternalAccountType(getContext(),
getTestContext().getPackageName(), false,
getTestContext().getResources().getXml(R.xml.contacts_fallback)
);
assertTrue(type.isInitialized());
// Create a fallback type with the same resource package name, and compare all the data
// kinds to its.
final AccountType reference = FallbackAccountType.createForTest(
getContext(), type.resPackageName);
assertsDataKindEquals(reference.getSortedDataKinds(), type.getSortedDataKinds());
}
public void testEditSchema_mustHaveChecks() {
checkEditSchema_mustHaveChecks(R.xml.missing_contacts_base, true);
checkEditSchema_mustHaveChecks(R.xml.missing_contacts_photo, false);
checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name, false);
checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr1, false);
checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr2, false);
checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr3, false);
checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr4, false);
checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr5, false);
checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr6, false);
checkEditSchema_mustHaveChecks(R.xml.missing_contacts_name_attr7, false);
}
private void checkEditSchema_mustHaveChecks(int xmlResId, boolean expectInitialized) {
final ExternalAccountType type = new ExternalAccountType(getContext(),
getTestContext().getPackageName(), false,
getTestContext().getResources().getXml(xmlResId)
);
assertEquals(expectInitialized, type.isInitialized());
}
/**
* Initialize with "contacts_readonly.xml" and see if all data kinds are correctly registered.
*/
public void testReadOnlyDefinition() {
final ExternalAccountType type = new ExternalAccountType(getContext(),
getTestContext().getPackageName(), false,
getTestContext().getResources().getXml(R.xml.contacts_readonly)
);
assertTrue(type.isInitialized());
// Shouldn't have a "null" mimetype.
assertTrue(type.getKindForMimetype(null) == null);
// 3 kinds are defined in XML and 4 are added by default.
assertEquals(4 + 3, type.getSortedDataKinds().size());
// Check for the default kinds.
assertNotNull(type.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME));
assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME));
assertNotNull(type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE));
// Check for type specific kinds.
DataKind kind = type.getKindForMimetype("vnd.android.cursor.item/a.b.c");
assertNotNull(kind);
// No check for icon -- we actually just ignore it.
assertEquals("data1", ((BaseAccountType.SimpleInflater) kind.actionHeader)
.getColumnNameForTest());
assertEquals("data2", ((BaseAccountType.SimpleInflater) kind.actionBody)
.getColumnNameForTest());
assertEquals(true, kind.actionBodySocial);
kind = type.getKindForMimetype("vnd.android.cursor.item/d.e.f");
assertNotNull(kind);
assertEquals("data3", ((BaseAccountType.SimpleInflater) kind.actionHeader)
.getColumnNameForTest());
assertEquals("data4", ((BaseAccountType.SimpleInflater) kind.actionBody)
.getColumnNameForTest());
assertEquals(false, kind.actionBodySocial);
kind = type.getKindForMimetype("vnd.android.cursor.item/xyz");
assertNotNull(kind);
assertEquals("data5", ((BaseAccountType.SimpleInflater) kind.actionHeader)
.getColumnNameForTest());
assertEquals("data6", ((BaseAccountType.SimpleInflater) kind.actionBody)
.getColumnNameForTest());
assertEquals(true, kind.actionBodySocial);
}
private static void assertsDataKindEquals(List<DataKind> expectedKinds,
List<DataKind> actualKinds) {
final int count = Math.max(actualKinds.size(), expectedKinds.size());
for (int i = 0; i < count; i++) {
String actual = actualKinds.size() > i ? actualKinds.get(i).toString() : "(n/a)";
String expected = expectedKinds.size() > i ? expectedKinds.get(i).toString() : "(n/a)";
// Because assertEquals()'s output is not very friendly when comparing two similar
// strings, we manually do the check.
if (!Objects.equal(actual, expected)) {
final int commonPrefixEnd = findCommonPrefixEnd(actual, expected);
fail("Kind #" + i
+ "\n[Actual]\n" + insertMarkerAt(actual, commonPrefixEnd)
+ "\n[Expected]\n" + insertMarkerAt(expected, commonPrefixEnd));
}
}
}
private static int findCommonPrefixEnd(String s1, String s2) {
int i = 0;
for (;;) {
final boolean s1End = (s1.length() <= i);
final boolean s2End = (s2.length() <= i);
if (s1End || s2End) {
return i;
}
if (s1.charAt(i) != s2.charAt(i)) {
return i;
}
i++;
}
}
private static String insertMarkerAt(String s, int position) {
final String MARKER = "***";
if (position > s.length()) {
return s + MARKER;
} else {
return new StringBuilder(s).insert(position, MARKER).toString();
}
}
}