Home | History | Annotate | Download | only in test_utils
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 package android.pim.vcard.test_utils;
     17 
     18 import android.content.ContentResolver;
     19 import android.content.Context;
     20 import android.content.EntityIterator;
     21 import android.net.Uri;
     22 import android.pim.vcard.VCardComposer;
     23 import android.pim.vcard.VCardConfig;
     24 import android.pim.vcard.VCardEntryConstructor;
     25 import android.pim.vcard.VCardInterpreter;
     26 import android.pim.vcard.VCardInterpreterCollection;
     27 import android.pim.vcard.VCardParser;
     28 import android.pim.vcard.VCardUtils;
     29 import android.pim.vcard.exception.VCardException;
     30 import android.test.AndroidTestCase;
     31 import android.test.mock.MockContext;
     32 import android.text.TextUtils;
     33 import android.util.Log;
     34 
     35 import junit.framework.TestCase;
     36 
     37 import java.io.ByteArrayInputStream;
     38 import java.io.IOException;
     39 import java.io.InputStream;
     40 import java.lang.reflect.Method;
     41 import java.util.Arrays;
     42 
     43 /**
     44  * <p>
     45  * The class lets users checks that given expected vCard data are same as given actual vCard data.
     46  * Able to verify both vCard importer/exporter.
     47  * </p>
     48  * <p>
     49  * First a user has to initialize the object by calling either
     50  * {@link #initForImportTest(int, int)} or {@link #initForExportTest(int)}.
     51  * "Round trip test" (import -> export -> import, or export -> import -> export) is not supported.
     52  * </p>
     53  */
     54 public class VCardVerifier {
     55     private static final String LOG_TAG = "VCardVerifier";
     56 
     57     private static class CustomMockContext extends MockContext {
     58         final ContentResolver mResolver;
     59         public CustomMockContext(ContentResolver resolver) {
     60             mResolver = resolver;
     61         }
     62 
     63         @Override
     64         public ContentResolver getContentResolver() {
     65             return mResolver;
     66         }
     67     }
     68 
     69     private class VCardVerifierInternal implements VCardComposer.OneEntryHandler {
     70         public boolean onInit(Context context) {
     71             return true;
     72         }
     73         public boolean onEntryCreated(String vcard) {
     74             verifyOneVCardForExport(vcard);
     75             return true;
     76         }
     77         public void onTerminate() {
     78         }
     79     }
     80 
     81     private final AndroidTestCase mAndroidTestCase;
     82     private final VCardVerifierInternal mVCardVerifierInternal;
     83     private int mVCardType;
     84     private boolean mIsDoCoMo;
     85 
     86     // Only one of them must be non-empty.
     87     private ExportTestResolver mExportTestResolver;
     88     private InputStream mInputStream;
     89 
     90     // To allow duplication, use list instead of set.
     91     // When null, we don't need to do the verification.
     92     private PropertyNodesVerifier mPropertyNodesVerifier;
     93     private LineVerifier mLineVerifier;
     94     private ContentValuesVerifier mContentValuesVerifier;
     95     private boolean mInitialized;
     96     private boolean mVerified = false;
     97     private String mCharset;
     98 
     99     // Called by VCardTestsBase
    100     public VCardVerifier(AndroidTestCase androidTestCase) {
    101         mAndroidTestCase = androidTestCase;
    102         mVCardVerifierInternal = new VCardVerifierInternal();
    103         mExportTestResolver = null;
    104         mInputStream = null;
    105         mInitialized = false;
    106         mVerified = false;
    107     }
    108 
    109     // Should be called at the beginning of each import test.
    110     public void initForImportTest(int vcardType, int resId) {
    111         if (mInitialized) {
    112             AndroidTestCase.fail("Already initialized");
    113         }
    114         mVCardType = vcardType;
    115         mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
    116         setInputResourceId(resId);
    117         mInitialized = true;
    118     }
    119 
    120     // Should be called at the beginning of each export test.
    121     public void initForExportTest(int vcardType) {
    122         initForExportTest(vcardType, "UTF-8");
    123     }
    124 
    125     public void initForExportTest(int vcardType, String charset) {
    126         if (mInitialized) {
    127             AndroidTestCase.fail("Already initialized");
    128         }
    129         mExportTestResolver = new ExportTestResolver(mAndroidTestCase);
    130         mVCardType = vcardType;
    131         mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
    132         mInitialized = true;
    133         if (TextUtils.isEmpty(charset)) {
    134             mCharset = "UTF-8";
    135         } else {
    136             mCharset = charset;
    137         }
    138     }
    139 
    140     private void setInputResourceId(int resId) {
    141         InputStream inputStream = mAndroidTestCase.getContext().getResources().openRawResource(resId);
    142         if (inputStream == null) {
    143             AndroidTestCase.fail("Wrong resId: " + resId);
    144         }
    145         setInputStream(inputStream);
    146     }
    147 
    148     private void setInputStream(InputStream inputStream) {
    149         if (mExportTestResolver != null) {
    150             AndroidTestCase.fail("addInputEntry() is called.");
    151         } else if (mInputStream != null) {
    152             AndroidTestCase.fail("InputStream is already set");
    153         }
    154         mInputStream = inputStream;
    155     }
    156 
    157     public ContactEntry addInputEntry() {
    158         if (!mInitialized) {
    159             AndroidTestCase.fail("Not initialized");
    160         }
    161         if (mInputStream != null) {
    162             AndroidTestCase.fail("setInputStream is called");
    163         }
    164         return mExportTestResolver.addInputContactEntry();
    165     }
    166 
    167     public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithoutVersion() {
    168         if (!mInitialized) {
    169             AndroidTestCase.fail("Not initialized");
    170         }
    171         if (mPropertyNodesVerifier == null) {
    172             mPropertyNodesVerifier = new PropertyNodesVerifier(mAndroidTestCase);
    173         }
    174         return mPropertyNodesVerifier.addPropertyNodesVerifierElem();
    175     }
    176 
    177     public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
    178         final PropertyNodesVerifierElem elem = addPropertyNodesVerifierElemWithoutVersion();
    179         final String versionString;
    180         if (VCardConfig.isVersion21(mVCardType)) {
    181             versionString = "2.1";
    182         } else if (VCardConfig.isVersion30(mVCardType)) {
    183             versionString = "3.0";
    184         } else if (VCardConfig.isVersion40(mVCardType)) {
    185             versionString = "4.0";
    186         } else {
    187             throw new RuntimeException("Unexpected vcard type during a unit test");
    188         }
    189         elem.addExpectedNodeWithOrder("VERSION", versionString);
    190 
    191         return elem;
    192     }
    193 
    194     public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithEmptyName() {
    195         if (!mInitialized) {
    196             AndroidTestCase.fail("Not initialized");
    197         }
    198         final PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem();
    199         if (VCardConfig.isVersion40(mVCardType)) {
    200             elem.addExpectedNodeWithOrder("FN", "");
    201         } else if (VCardConfig.isVersion30(mVCardType)) {
    202             elem.addExpectedNodeWithOrder("N", "");
    203             elem.addExpectedNodeWithOrder("FN", "");
    204         } else if (mIsDoCoMo) {
    205             elem.addExpectedNodeWithOrder("N", "");
    206         }
    207         return elem;
    208     }
    209 
    210     public LineVerifierElem addLineVerifierElem() {
    211         if (!mInitialized) {
    212             AndroidTestCase.fail("Not initialized");
    213         }
    214         if (mLineVerifier == null) {
    215             mLineVerifier = new LineVerifier(mAndroidTestCase, mVCardType);
    216         }
    217         return mLineVerifier.addLineVerifierElem();
    218     }
    219 
    220     public ContentValuesVerifierElem addContentValuesVerifierElem() {
    221         if (!mInitialized) {
    222             AndroidTestCase.fail("Not initialized");
    223         }
    224         if (mContentValuesVerifier == null) {
    225             mContentValuesVerifier = new ContentValuesVerifier();
    226         }
    227 
    228         return mContentValuesVerifier.addElem(mAndroidTestCase);
    229     }
    230 
    231     /**
    232      * Sets up sub-verifiers correctly and try parse given vCard as InputStream.
    233      * Errors around InputStream must be handled outside this method.
    234      *
    235      * Used both from {@link #verifyForImportTest()} and from {@link #verifyForExportTest()}.
    236      */
    237     private void verifyWithInputStream(InputStream is) throws IOException {
    238         final VCardInterpreter interpreter;
    239         if (mContentValuesVerifier != null) {
    240             final VCardEntryConstructor constructor = new VCardEntryConstructor(mVCardType);
    241             constructor.addEntryHandler(mContentValuesVerifier);
    242             if (mPropertyNodesVerifier != null) {
    243                 interpreter = new VCardInterpreterCollection(Arrays.asList(
    244                         mPropertyNodesVerifier, constructor));
    245             } else {
    246                 interpreter = constructor;
    247             }
    248         } else {
    249             if (mPropertyNodesVerifier != null) {
    250                 interpreter = mPropertyNodesVerifier;
    251             } else {
    252                 interpreter = null;
    253             }
    254         }
    255 
    256         try {
    257             // Note: we must not specify charset toward vCard parsers. This code checks whether
    258             // those parsers are able to encode given binary without any extra information for
    259             // charset.
    260             final VCardParser parser = VCardUtils.getAppropriateParser(mVCardType);
    261             parser.parse(is, interpreter);
    262         } catch (VCardException e) {
    263             AndroidTestCase.fail("Unexpected VCardException: " + e.getMessage());
    264         }
    265     }
    266 
    267     private void verifyOneVCardForExport(final String vcard) {
    268         Log.d(LOG_TAG, vcard);
    269         InputStream is = null;
    270         try {
    271             is = new ByteArrayInputStream(vcard.getBytes(mCharset));
    272             verifyWithInputStream(is);
    273         } catch (IOException e) {
    274             AndroidTestCase.fail("Unexpected IOException: " + e.getMessage());
    275         } finally {
    276             if (is != null) {
    277                 try {
    278                     is.close();
    279                 } catch (IOException e) {
    280                     AndroidTestCase.fail("Unexpected IOException: " + e.getMessage());
    281                 }
    282             }
    283         }
    284     }
    285 
    286     public void verify() {
    287         if (!mInitialized) {
    288             TestCase.fail("Not initialized.");
    289         }
    290         if (mVerified) {
    291             TestCase.fail("verify() was called twice.");
    292         }
    293 
    294         if (mInputStream != null) {
    295             if (mExportTestResolver != null){
    296                 TestCase.fail("There are two input sources.");
    297             }
    298             verifyForImportTest();
    299         } else if (mExportTestResolver != null){
    300             verifyForExportTest();
    301         } else {
    302             TestCase.fail("No input is determined");
    303         }
    304         mVerified = true;
    305     }
    306 
    307     private void verifyForImportTest() {
    308         if (mLineVerifier != null) {
    309             AndroidTestCase.fail("Not supported now.");
    310         }
    311 
    312         try {
    313             verifyWithInputStream(mInputStream);
    314         } catch (IOException e) {
    315             AndroidTestCase.fail("IOException was thrown: " + e.getMessage());
    316         } finally {
    317             if (mInputStream != null) {
    318                 try {
    319                     mInputStream.close();
    320                 } catch (IOException e) {
    321                 }
    322             }
    323         }
    324     }
    325 
    326     public static EntityIterator mockGetEntityIteratorMethod(
    327             final ContentResolver resolver,
    328             final Uri uri, final String selection,
    329             final String[] selectionArgs, final String sortOrder) {
    330         if (ExportTestResolver.class.equals(resolver.getClass())) {
    331             return ((ExportTestResolver)resolver).getProvider().queryEntities(
    332                     uri, selection, selectionArgs, sortOrder);
    333         }
    334 
    335         Log.e(LOG_TAG, "Unexpected provider given.");
    336         return null;
    337     }
    338 
    339     private Method getMockGetEntityIteratorMethod()
    340             throws SecurityException, NoSuchMethodException {
    341         return this.getClass().getMethod("mockGetEntityIteratorMethod",
    342                 ContentResolver.class, Uri.class, String.class, String[].class, String.class);
    343     }
    344 
    345     private void verifyForExportTest() {
    346        final VCardComposer composer =
    347             new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType, mCharset);
    348         composer.addHandler(mLineVerifier);
    349         composer.addHandler(mVCardVerifierInternal);
    350         if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
    351             AndroidTestCase.fail("init() failed. Reason: " + composer.getErrorReason());
    352         }
    353         AndroidTestCase.assertFalse(composer.isAfterLast());
    354         try {
    355             while (!composer.isAfterLast()) {
    356                 try {
    357                     final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod();
    358                     AndroidTestCase.assertNotNull(mockGetEntityIteratorMethod);
    359                     AndroidTestCase.assertTrue(
    360                             composer.createOneEntry(mockGetEntityIteratorMethod));
    361                 } catch (Exception e) {
    362                     e.printStackTrace();
    363                     AndroidTestCase.fail(e.toString());
    364                 }
    365             }
    366         } finally {
    367             composer.terminate();
    368         }
    369     }
    370 }
    371