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 android.pim.vcard;
17
18import android.content.ContentProvider;
19import android.content.ContentResolver;
20import android.content.Context;
21import android.content.EntityIterator;
22import android.net.Uri;
23import android.pim.vcard.VCardComposer;
24import android.pim.vcard.VCardConfig;
25import android.pim.vcard.VCardEntryConstructor;
26import android.pim.vcard.VCardInterpreter;
27import android.pim.vcard.VCardInterpreterCollection;
28import android.pim.vcard.VCardParser;
29import android.pim.vcard.VCardParser_V21;
30import android.pim.vcard.VCardParser_V30;
31import android.pim.vcard.exception.VCardException;
32import android.test.AndroidTestCase;
33import android.test.mock.MockContext;
34
35import java.io.ByteArrayInputStream;
36import java.io.IOException;
37import java.io.InputStream;
38import java.lang.reflect.Method;
39import java.util.Arrays;
40
41/* package */ class CustomMockContext extends MockContext {
42    final ContentResolver mResolver;
43    public CustomMockContext(ContentResolver resolver) {
44        mResolver = resolver;
45    }
46
47    @Override
48    public ContentResolver getContentResolver() {
49        return mResolver;
50    }
51}
52
53/* package */ class VCardVerifier {
54    private class VCardVerifierInternal implements VCardComposer.OneEntryHandler {
55        public boolean onInit(Context context) {
56            return true;
57        }
58        public boolean onEntryCreated(String vcard) {
59            verifyOneVCard(vcard);
60            return true;
61        }
62        public void onTerminate() {
63        }
64    }
65
66    private final AndroidTestCase mTestCase;
67    private final VCardVerifierInternal mVCardVerifierInternal;
68    private int mVCardType;
69    private boolean mIsV30;
70    private boolean mIsDoCoMo;
71
72    // Only one of them must be non-empty.
73    private ExportTestResolver mExportTestResolver;
74    private InputStream mInputStream;
75
76    // To allow duplication, use list instead of set.
77    // When null, we don't need to do the verification.
78    private PropertyNodesVerifier mPropertyNodesVerifier;
79    private LineVerifier mLineVerifier;
80    private ContentValuesVerifier mContentValuesVerifier;
81    private boolean mInitialized;
82    private boolean mVerified = false;
83
84    public VCardVerifier(AndroidTestCase androidTestCase) {
85        mTestCase = androidTestCase;
86        mVCardVerifierInternal = new VCardVerifierInternal();
87        mExportTestResolver = null;
88        mInputStream = null;
89        mInitialized = false;
90        mVerified = false;
91    }
92
93    public void initForExportTest(int vcardType) {
94        if (mInitialized) {
95            mTestCase.fail("Already initialized");
96        }
97        mExportTestResolver = new ExportTestResolver(mTestCase);
98        mVCardType = vcardType;
99        mIsV30 = VCardConfig.isV30(vcardType);
100        mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
101        mInitialized = true;
102    }
103
104    public void initForImportTest(int vcardType, int resId) {
105        if (mInitialized) {
106            mTestCase.fail("Already initialized");
107        }
108        mVCardType = vcardType;
109        mIsV30 = VCardConfig.isV30(vcardType);
110        mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
111        setInputResourceId(resId);
112        mInitialized = true;
113    }
114
115    private void setInputResourceId(int resId) {
116        InputStream inputStream = mTestCase.getContext().getResources().openRawResource(resId);
117        if (inputStream == null) {
118            mTestCase.fail("Wrong resId: " + resId);
119        }
120        setInputStream(inputStream);
121    }
122
123    private void setInputStream(InputStream inputStream) {
124        if (mExportTestResolver != null) {
125            mTestCase.fail("addInputEntry() is called.");
126        } else if (mInputStream != null) {
127            mTestCase.fail("InputStream is already set");
128        }
129        mInputStream = inputStream;
130    }
131
132    public ContactEntry addInputEntry() {
133        if (!mInitialized) {
134            mTestCase.fail("Not initialized");
135        }
136        if (mInputStream != null) {
137            mTestCase.fail("setInputStream is called");
138        }
139        return mExportTestResolver.addInputContactEntry();
140    }
141
142    public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
143        if (!mInitialized) {
144            mTestCase.fail("Not initialized");
145        }
146        if (mPropertyNodesVerifier == null) {
147            mPropertyNodesVerifier = new PropertyNodesVerifier(mTestCase);
148        }
149        PropertyNodesVerifierElem elem =
150                mPropertyNodesVerifier.addPropertyNodesVerifierElem();
151        elem.addExpectedNodeWithOrder("VERSION", (mIsV30 ? "3.0" : "2.1"));
152
153        return elem;
154    }
155
156    public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithEmptyName() {
157        if (!mInitialized) {
158            mTestCase.fail("Not initialized");
159        }
160        PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem();
161        if (mIsV30) {
162            elem.addExpectedNodeWithOrder("N", "").addExpectedNodeWithOrder("FN", "");
163        } else if (mIsDoCoMo) {
164            elem.addExpectedNodeWithOrder("N", "");
165        }
166        return elem;
167    }
168
169    public LineVerifierElem addLineVerifierElem() {
170        if (!mInitialized) {
171            mTestCase.fail("Not initialized");
172        }
173        if (mLineVerifier == null) {
174            mLineVerifier = new LineVerifier(mTestCase, mVCardType);
175        }
176        return mLineVerifier.addLineVerifierElem();
177    }
178
179    public ContentValuesVerifierElem addContentValuesVerifierElem() {
180        if (!mInitialized) {
181            mTestCase.fail("Not initialized");
182        }
183        if (mContentValuesVerifier == null) {
184            mContentValuesVerifier = new ContentValuesVerifier();
185        }
186
187        return mContentValuesVerifier.addElem(mTestCase);
188    }
189
190    private void verifyOneVCard(final String vcard) {
191        // Log.d("@@@", vcard);
192        final VCardInterpreter builder;
193        if (mContentValuesVerifier != null) {
194            final VNodeBuilder vnodeBuilder = mPropertyNodesVerifier;
195            final VCardEntryConstructor vcardDataBuilder =
196                    new VCardEntryConstructor(mVCardType);
197            vcardDataBuilder.addEntryHandler(mContentValuesVerifier);
198            if (mPropertyNodesVerifier != null) {
199                builder = new VCardInterpreterCollection(Arrays.asList(
200                        mPropertyNodesVerifier, vcardDataBuilder));
201            } else {
202                builder = vnodeBuilder;
203            }
204        } else {
205            if (mPropertyNodesVerifier != null) {
206                builder = mPropertyNodesVerifier;
207            } else {
208                return;
209            }
210        }
211
212        final VCardParser parser =
213                (mIsV30 ? new VCardParser_V30(true) : new VCardParser_V21());
214        InputStream is = null;
215        try {
216            String charset =
217                (VCardConfig.usesShiftJis(mVCardType) ? "SHIFT_JIS" : "UTF-8");
218            is = new ByteArrayInputStream(vcard.getBytes(charset));
219            mTestCase.assertEquals(true, parser.parse(is, null, builder));
220        } catch (IOException e) {
221            mTestCase.fail("Unexpected IOException: " + e.getMessage());
222        } catch (VCardException e) {
223            mTestCase.fail("Unexpected VCardException: " + e.getMessage());
224        } finally {
225            if (is != null) {
226                try {
227                    is.close();
228                } catch (IOException e) {
229                }
230            }
231        }
232    }
233
234    public void verify() {
235        if (!mInitialized) {
236            mTestCase.fail("Not initialized.");
237        }
238        if (mVerified) {
239            mTestCase.fail("verify() was called twice.");
240        }
241        if (mInputStream != null) {
242            try {
243                verifyForImportTest();
244            } catch (IOException e) {
245                mTestCase.fail("IOException was thrown: " + e.getMessage());
246            } catch (VCardException e) {
247                mTestCase.fail("VCardException was thrown: " + e.getMessage());
248            }
249        } else if (mExportTestResolver != null){
250            verifyForExportTest();
251        } else {
252            mTestCase.fail("No input is determined");
253        }
254        mVerified = true;
255    }
256
257    private void verifyForImportTest() throws IOException, VCardException {
258        if (mLineVerifier != null) {
259            mTestCase.fail("Not supported now.");
260        }
261        if (mContentValuesVerifier != null) {
262            mContentValuesVerifier.verify(mInputStream, mVCardType);
263        }
264    }
265
266    public static EntityIterator mockGetEntityIteratorMethod(
267            final ContentResolver resolver,
268            final Uri uri, final String selection,
269            final String[] selectionArgs, final String sortOrder) {
270        final ContentProvider provider =
271            resolver.acquireContentProviderClient(uri).getLocalContentProvider();
272        return ((ExportTestProvider)provider).queryEntities(
273                uri, selection, selectionArgs, sortOrder);
274    }
275
276    private Method getMockGetEntityIteratorMethod()
277            throws SecurityException, NoSuchMethodException {
278        return this.getClass().getMethod("mockGetEntityIteratorMethod",
279                ContentResolver.class, Uri.class, String.class, String[].class, String.class);
280    }
281
282    private void verifyForExportTest() {
283       final VCardComposer composer =
284            new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType);
285        composer.addHandler(mLineVerifier);
286        composer.addHandler(mVCardVerifierInternal);
287        if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
288            mTestCase.fail("init() failed. Reason: " + composer.getErrorReason());
289        }
290        mTestCase.assertFalse(composer.isAfterLast());
291        try {
292            while (!composer.isAfterLast()) {
293                try {
294                    final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod();
295                    mTestCase.assertTrue(
296                            composer.createOneEntry(getMockGetEntityIteratorMethod()));
297                } catch (Exception e) {
298                    e.printStackTrace();
299                    mTestCase.fail();
300                }
301            }
302        } finally {
303            composer.terminate();
304        }
305    }
306}
307