Home | History | Annotate | Download | only in testutils
      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  */
     16 package com.android.vcard.tests.testutils;
     17 
     18 import android.content.ContentProviderOperation;
     19 import android.content.ContentProviderResult;
     20 import android.content.ContentValues;
     21 import android.net.Uri;
     22 import android.provider.ContactsContract.Data;
     23 import android.provider.ContactsContract.RawContacts;
     24 import android.test.mock.MockContentProvider;
     25 import android.text.TextUtils;
     26 
     27 import junit.framework.TestCase;
     28 
     29 import java.util.ArrayList;
     30 import java.util.Arrays;
     31 import java.util.Collection;
     32 import java.util.HashMap;
     33 import java.util.HashSet;
     34 import java.util.Map;
     35 import java.util.Map.Entry;
     36 import java.util.Set;
     37 import java.util.SortedMap;
     38 import java.util.TreeMap;
     39 
     40 public 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 }