1/* 2 * Copyright (C) 2010 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 */ 16package com.android.vcard.tests.testutils; 17 18import android.content.ContentProviderOperation; 19import android.content.ContentProviderResult; 20import android.content.ContentValues; 21import android.net.Uri; 22import android.provider.ContactsContract.Data; 23import android.provider.ContactsContract.RawContacts; 24import android.test.mock.MockContentProvider; 25import android.text.TextUtils; 26 27import junit.framework.TestCase; 28 29import java.util.ArrayList; 30import java.util.Arrays; 31import java.util.Collection; 32import java.util.HashMap; 33import java.util.HashSet; 34import java.util.Map; 35import java.util.Map.Entry; 36import java.util.Set; 37import java.util.SortedMap; 38import java.util.TreeMap; 39 40public class ImportTestProvider extends MockContentProvider { 41 private final Map<String, Collection<ContentValues>> 42 mMimeTypeToExpectedContentValues = new HashMap<String, Collection<ContentValues>>(); 43 44 public void addExpectedContentValues(ContentValues expectedContentValues) { 45 final String mimeType = expectedContentValues.getAsString(Data.MIMETYPE); 46 47 final Collection<ContentValues> contentValuesCollection; 48 if (mMimeTypeToExpectedContentValues.containsKey(mimeType)) { 49 contentValuesCollection = mMimeTypeToExpectedContentValues.get(mimeType); 50 } else { 51 contentValuesCollection = new ArrayList<ContentValues>(); 52 mMimeTypeToExpectedContentValues.put(mimeType, contentValuesCollection); 53 } 54 55 contentValuesCollection.add(expectedContentValues); 56 } 57 58 @Override 59 public ContentProviderResult[] applyBatch( 60 ArrayList<ContentProviderOperation> operations) { 61 if (operations == null) { 62 TestCase.fail("There is no operation."); 63 } 64 65 final int size = operations.size(); 66 ContentProviderResult[] fakeResultArray = new ContentProviderResult[size]; 67 for (int i = 0; i < size; i++) { 68 Uri uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, String.valueOf(i)); 69 fakeResultArray[i] = new ContentProviderResult(uri); 70 } 71 72 for (int i = 0; i < size; i++) { 73 ContentProviderOperation operation = operations.get(i); 74 ContentValues contentValues = operation.resolveValueBackReferences( 75 fakeResultArray, i); 76 } 77 for (int i = 0; i < size; i++) { 78 ContentProviderOperation operation = operations.get(i); 79 ContentValues actualContentValues = operation.resolveValueBackReferences( 80 fakeResultArray, i); 81 final Uri uri = operation.getUri(); 82 if (uri.equals(RawContacts.CONTENT_URI)) { 83 TestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME)); 84 TestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE)); 85 } else if (uri.equals(Data.CONTENT_URI)) { 86 final String mimeType = actualContentValues.getAsString(Data.MIMETYPE); 87 if (!mMimeTypeToExpectedContentValues.containsKey(mimeType)) { 88 TestCase.fail("Unregistered MimeType " + mimeType); 89 } 90 // Remove data meaningless in this unit tests. 91 // Specifically, Data.DATA1 - DATA7 are set to null or empty String 92 // regardless of the input, but it may change depending on how 93 // resolver-related code handles it. 94 // Here, we ignore these implementation-dependent specs and 95 // just check whether vCard importer correctly inserts rellevent data. 96 Set<String> keyToBeRemoved = new HashSet<String>(); 97 for (Entry<String, Object> entry : actualContentValues.valueSet()) { 98 Object value = entry.getValue(); 99 if (value == null || TextUtils.isEmpty(value.toString())) { 100 keyToBeRemoved.add(entry.getKey()); 101 } 102 } 103 for (String key: keyToBeRemoved) { 104 actualContentValues.remove(key); 105 } 106 /* for testing 107 Log.d("@@@", 108 String.format("MimeType: %s, data: %s", 109 mimeType, actualContentValues.toString())); */ 110 // Remove RAW_CONTACT_ID entry just for safety, since we do not care 111 // how resolver-related code handles the entry in this unit test, 112 if (actualContentValues.containsKey(Data.RAW_CONTACT_ID)) { 113 actualContentValues.remove(Data.RAW_CONTACT_ID); 114 } 115 final Collection<ContentValues> contentValuesCollection = 116 mMimeTypeToExpectedContentValues.get(mimeType); 117 if (contentValuesCollection.isEmpty()) { 118 TestCase.fail("ContentValues for MimeType " + mimeType 119 + " is not expected at all (" + actualContentValues + ")"); 120 } 121 boolean checked = false; 122 for (ContentValues expectedContentValues : contentValuesCollection) { 123 /*for testing 124 Log.d("@@@", "expected: " 125 + convertToEasilyReadableString(expectedContentValues)); 126 Log.d("@@@", "actual : " 127 + convertToEasilyReadableString(actualContentValues));*/ 128 if (equalsForContentValues(expectedContentValues, 129 actualContentValues)) { 130 TestCase.assertTrue(contentValuesCollection.remove(expectedContentValues)); 131 checked = true; 132 break; 133 } 134 } 135 if (!checked) { 136 final StringBuilder builder = new StringBuilder(); 137 builder.append("\n"); 138 builder.append("Unexpected: "); 139 builder.append(convertToEasilyReadableString(actualContentValues)); 140 builder.append("\n"); 141 builder.append("Expected : "); 142 for (ContentValues expectedContentValues : contentValuesCollection) { 143 builder.append(convertToEasilyReadableString(expectedContentValues)); 144 } 145 TestCase.fail(builder.toString()); 146 } 147 } else { 148 TestCase.fail("Unexpected Uri has come: " + uri); 149 } 150 } // for (int i = 0; i < size; i++) { 151 return fakeResultArray; 152 } 153 154 /** 155 * Checks all expected ContentValues are consumed during import. 156 */ 157 public void verify() { 158 StringBuilder builder = new StringBuilder(); 159 for (Collection<ContentValues> contentValuesCollection : 160 mMimeTypeToExpectedContentValues.values()) { 161 for (ContentValues expectedContentValues: contentValuesCollection) { 162 builder.append(convertToEasilyReadableString(expectedContentValues)); 163 builder.append("\n"); 164 } 165 } 166 if (builder.length() > 0) { 167 final String failMsg = 168 "There is(are) remaining expected ContentValues instance(s): \n" 169 + builder.toString(); 170 TestCase.fail(failMsg); 171 } 172 } 173 174 /** 175 * Utility method to print ContentValues whose content is printed with sorted keys. 176 */ 177 private String convertToEasilyReadableString(ContentValues contentValues) { 178 if (contentValues == null) { 179 return "null"; 180 } 181 String mimeTypeValue = ""; 182 SortedMap<String, String> sortedMap = new TreeMap<String, String>(); 183 for (Entry<String, Object> entry : contentValues.valueSet()) { 184 final String key = entry.getKey(); 185 final Object value = entry.getValue(); 186 final String valueString = (value != null ? value.toString() : null); 187 if (Data.MIMETYPE.equals(key)) { 188 mimeTypeValue = valueString; 189 } else { 190 TestCase.assertNotNull(key); 191 sortedMap.put(key, valueString); 192 } 193 } 194 StringBuilder builder = new StringBuilder(); 195 builder.append(Data.MIMETYPE); 196 builder.append('='); 197 builder.append(mimeTypeValue); 198 for (Entry<String, String> entry : sortedMap.entrySet()) { 199 final String key = entry.getKey(); 200 final String value = entry.getValue(); 201 builder.append(' '); 202 builder.append(key); 203 builder.append("=\""); 204 builder.append(value); 205 builder.append('"'); 206 } 207 return builder.toString(); 208 } 209 210 private static boolean equalsForContentValues( 211 final ContentValues expected, final ContentValues actual) { 212 if (expected == actual) { 213 return true; 214 } else if (expected == null || actual == null || expected.size() != actual.size()) { 215 return false; 216 } 217 218 for (Entry<String, Object> entry : expected.valueSet()) { 219 final String key = entry.getKey(); 220 final Object value = entry.getValue(); 221 if (!actual.containsKey(key)) { 222 return false; 223 } 224 // Type mismatch usuall happens as importer doesn't care the type of each value. 225 // For example, variable type might be Integer when importing the type of TEL, 226 // while variable type would be String when importing the type of RELATION. 227 final Object actualValue = actual.get(key); 228 if (value instanceof byte[]) { 229 if (!Arrays.equals((byte[])value, (byte[])actualValue)) { 230 byte[] e = (byte[])value; 231 byte[] a = (byte[])actualValue; 232 return false; 233 } 234 } else if (!value.equals(actualValue) && 235 !value.toString().equals(actualValue.toString())) { 236 return false; 237 } 238 } 239 return true; 240 } 241}