Add a smoke test for ImportProcessor.

Bug: 2733143
Change-Id: Iec63217c1b7be04ab0438bb76be96e70dc26e5d6
diff --git a/src/com/android/contacts/vcard/ImportProcessor.java b/src/com/android/contacts/vcard/ImportProcessor.java
index f4b5f5a..6f765f9 100644
--- a/src/com/android/contacts/vcard/ImportProcessor.java
+++ b/src/com/android/contacts/vcard/ImportProcessor.java
@@ -56,7 +56,7 @@
 public class ImportProcessor {
     private static final String LOG_TAG = "ImportRequestProcessor";
 
-    private final Service mService;
+    private final Context mContext;
 
     private ContentResolver mResolver;
     private NotificationManager mNotificationManager;
@@ -103,25 +103,36 @@
     }
     /* package */ CommitterGenerator mCommitterGenerator = new DefaultCommitterGenerator();
 
-    public ImportProcessor(final Service service) {
-        mService = service;
+    public ImportProcessor(final Context context) {
+        mContext = context;
     }
 
-    public synchronized void pushRequest(ImportRequest parameter) {
+    /**
+     * Checks this object and initialize it if not.
+     *
+     * This method is needed since {@link VCardService} is not ready when this object is
+     * created and we need to delay this initialization, while we want to initialize
+     * this object soon in tests.
+     */
+    /* package */ void ensureInit() {
         if (mResolver == null) {
             // Service object may not ready at the construction time
             // (e.g. ContentResolver may be null).
-            mResolver = mService.getContentResolver();
+            mResolver = mContext.getContentResolver();
             mNotificationManager =
-                    (NotificationManager)mService.getSystemService(Context.NOTIFICATION_SERVICE);
+                    (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         }
+    }
+
+    public synchronized void pushRequest(ImportRequest parameter) {
+        ensureInit();
 
         final boolean needThreadStart;
         if (!mReadyForRequest) {
             mFailedUris.clear();
             mCreatedUris.clear();
 
-            mNotifier.init(mService, mNotificationManager);
+            mNotifier.init(mContext, mNotificationManager);
             needThreadStart = true;
         } else {
             needThreadStart = false;
@@ -175,7 +186,7 @@
     }
 
     /**
-     * Would be run inside syncronized block.
+     * Would be run inside synchronized block.
      */
     /* package */ boolean handleOneRequest(final ImportRequest parameter) {
         if (mCanceled) {
@@ -242,12 +253,12 @@
     }
     */
 
-    private void doFinishNotification(Uri createdUri) {
+    private void doFinishNotification(final Uri createdUri) {
         final Notification notification = new Notification();
         notification.icon = android.R.drawable.stat_sys_download_done;
         notification.flags |= Notification.FLAG_AUTO_CANCEL;
 
-        final String title = mService.getString(R.string.importing_vcard_finished_title);
+        final String title = mContext.getString(R.string.importing_vcard_finished_title);
 
         final Intent intent;
         if (createdUri != null) {
@@ -260,13 +271,14 @@
             intent = null;
         }
 
-        notification.setLatestEventInfo(mService, title, "",
-                PendingIntent.getActivity(mService, 0, intent, 0));
+        notification.setLatestEventInfo(mContext, title, "",
+                PendingIntent.getActivity(mContext, 0, intent, 0));
         mNotificationManager.notify(VCardService.IMPORT_NOTIFICATION_ID, notification);
     }
 
-    private boolean readOneVCard(Uri uri, int vcardType, String charset,
-            VCardInterpreter interpreter,
+    // Make package private for testing use.
+    /* package */ boolean readOneVCard(Uri uri, int vcardType, String charset,
+            final VCardInterpreter interpreter,
             final int[] possibleVCardVersions) {
         boolean successful = false;
         final int length = possibleVCardVersions.length;
@@ -300,7 +312,7 @@
             } catch (IOException e) {
                 Log.e(LOG_TAG, "IOException was emitted: " + e.getMessage());
             } catch (VCardNestedException e) {
-                // This exception should not be thrown here. We should intsead handle it
+                // This exception should not be thrown here. We should instead handle it
                 // in the preprocessing session in ImportVCardActivity, as we don't try
                 // to detect the type of given vCard here.
                 //
diff --git a/tests/assets/v21_simple.vcf b/tests/assets/v21_simple.vcf
new file mode 100644
index 0000000..86f4d33
--- /dev/null
+++ b/tests/assets/v21_simple.vcf
@@ -0,0 +1,3 @@
+BEGIN:VCARD
+N:test
+END:VCARD
\ No newline at end of file
diff --git a/tests/src/com/android/contacts/vcard/ImportProcessorTest.java b/tests/src/com/android/contacts/vcard/ImportProcessorTest.java
new file mode 100644
index 0000000..f393448
--- /dev/null
+++ b/tests/src/com/android/contacts/vcard/ImportProcessorTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2010 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.vcard;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.vcard.VCardInterpreter;
+import com.android.vcard.VCardSourceDetector;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.List;
+
+public class ImportProcessorTest extends AndroidTestCase {
+    private static final String LOG_TAG = "ImportProcessorTest";
+    private ImportProcessor mImportProcessor;
+
+    private String mCopiedFileName;
+
+    // XXX: better way to copy stream?
+    private Uri copyToLocal(final String fileName) throws IOException {
+        final Context context = getContext();
+        // We need to use Context of this unit test runner (not of test to be tested),
+        // as only the former knows assets to be copied.
+        final Context testContext = getTestContext();
+        final ContentResolver resolver = testContext.getContentResolver();
+        mCopiedFileName = fileName;
+        ReadableByteChannel inputChannel = null;
+        WritableByteChannel outputChannel = null;
+        Uri destUri;
+        try {
+            inputChannel = Channels.newChannel(testContext.getAssets().open(fileName));
+            destUri = Uri.parse(context.getFileStreamPath(fileName).toURI().toString());
+            outputChannel =
+                    getContext().openFileOutput(fileName,
+                            Context.MODE_WORLD_WRITEABLE).getChannel();
+            final ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
+            while (inputChannel.read(buffer) != -1) {
+                buffer.flip();
+                outputChannel.write(buffer);
+                buffer.compact();
+            }
+            buffer.flip();
+            while (buffer.hasRemaining()) {
+                outputChannel.write(buffer);
+            }
+        } finally {
+            if (inputChannel != null) {
+                try {
+                    inputChannel.close();
+                } catch (IOException e) {
+                    Log.w(LOG_TAG, "Failed to close inputChannel.");
+                }
+            }
+            if (outputChannel != null) {
+                try {
+                    outputChannel.close();
+                } catch(IOException e) {
+                    Log.w(LOG_TAG, "Failed to close outputChannel");
+                }
+            }
+        }
+        return destUri;
+    }
+
+    @Override
+    public void setUp() {
+        mImportProcessor = new ImportProcessor(getContext());
+        mImportProcessor.ensureInit();
+        mCopiedFileName = null;
+    }
+
+    @Override
+    public void tearDown() {
+        if (!TextUtils.isEmpty(mCopiedFileName)) {
+            getContext().deleteFile(mCopiedFileName);
+            mCopiedFileName = null;
+        }
+    }
+
+    /**
+     * Confirm {@link ImportProcessor#readOneVCard(android.net.Uri, int, String,
+     * com.android.vcard.VCardInterpreter, int[])} successfully handles correct input.
+     */
+    public void testProcessSimple() throws IOException {
+        final Uri uri = copyToLocal("v21_simple.vcf");
+        final int vcardType = VCardSourceDetector.PARSE_TYPE_UNKNOWN;
+        final String charset = null;
+        final VCardInterpreter interpreter = new EmptyVCardInterpreter();
+        final int[] versions = new int[] {
+                ImportVCardActivity.VCARD_VERSION_V21
+        };
+
+        assertTrue(mImportProcessor.readOneVCard(
+                uri, vcardType, charset, interpreter, versions));
+    }
+}
+
+/* package */ class EmptyVCardInterpreter implements VCardInterpreter {
+    @Override
+    public void end() {
+    }
+
+    @Override
+    public void endEntry() {
+    }
+
+    @Override
+    public void endProperty() {
+    }
+
+    @Override
+    public void propertyGroup(String group) {
+    }
+
+    @Override
+    public void propertyName(String name) {
+    }
+
+    @Override
+    public void propertyParamType(String type) {
+    }
+
+    @Override
+    public void propertyParamValue(String value) {
+    }
+
+    @Override
+    public void propertyValues(List<String> values) {
+    }
+
+    @Override
+    public void start() {
+    }
+
+    @Override
+    public void startEntry() {
+    }
+
+    @Override
+    public void startProperty() {
+    }
+}
\ No newline at end of file