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