1/*
2 * Copyright (C) 2011 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 */
16package com.android.vcard.tests;
17
18import com.android.vcard.VCardConfig;
19import com.android.vcard.VCardConstants;
20import com.android.vcard.VCardEntry;
21import com.android.vcard.VCardEntry.AndroidCustomData;
22import com.android.vcard.VCardEntry.AnniversaryData;
23import com.android.vcard.VCardEntry.BirthdayData;
24import com.android.vcard.VCardEntry.EmailData;
25import com.android.vcard.VCardEntry.EntryElement;
26import com.android.vcard.VCardEntry.EntryLabel;
27import com.android.vcard.VCardEntry.ImData;
28import com.android.vcard.VCardEntry.NameData;
29import com.android.vcard.VCardEntry.NicknameData;
30import com.android.vcard.VCardEntry.NoteData;
31import com.android.vcard.VCardEntry.OrganizationData;
32import com.android.vcard.VCardEntry.PhoneData;
33import com.android.vcard.VCardEntry.PhotoData;
34import com.android.vcard.VCardEntry.PostalData;
35import com.android.vcard.VCardEntry.SipData;
36import com.android.vcard.VCardEntryConstructor;
37import com.android.vcard.VCardEntryHandler;
38import com.android.vcard.VCardInterpreter;
39import com.android.vcard.VCardProperty;
40
41import android.content.ContentProviderOperation;
42import android.content.ContentResolver;
43import android.provider.ContactsContract.CommonDataKinds.Email;
44import android.provider.ContactsContract.CommonDataKinds.Im;
45import android.provider.ContactsContract.CommonDataKinds.Organization;
46import android.provider.ContactsContract.CommonDataKinds.Phone;
47import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
48import android.provider.ContactsContract.CommonDataKinds.SipAddress;
49import android.test.AndroidTestCase;
50
51import java.util.ArrayList;
52import java.util.Arrays;
53import java.util.HashMap;
54import java.util.List;
55import java.util.Map;
56
57public class VCardEntryTests extends AndroidTestCase {
58    private class MockVCardEntryHandler implements VCardEntryHandler {
59        private List<VCardEntry> mEntries = new ArrayList<VCardEntry>();
60        private boolean mOnStartCalled;
61        private boolean mOnEndCalled;
62
63        @Override
64        public void onStart() {
65            assertFalse(mOnStartCalled);
66            mOnStartCalled = true;
67        }
68
69        @Override
70        public void onEntryCreated(VCardEntry entry) {
71            assertTrue(mOnStartCalled);
72            assertFalse(mOnEndCalled);
73            mEntries.add(entry);
74        }
75
76        @Override
77        public void onEnd() {
78            assertTrue(mOnStartCalled);
79            assertFalse(mOnEndCalled);
80            mOnEndCalled = true;
81        }
82
83        public List<VCardEntry> getEntries() {
84            return mEntries;
85        }
86    }
87
88    /**
89     * Tests VCardEntry and related clasess can handle nested classes given
90     * {@link VCardInterpreter} is called appropriately.
91     *
92     * This test manually calls VCardInterpreter's callback mechanism and checks
93     * {@link VCardEntryConstructor} constructs {@link VCardEntry} per given calls.
94     *
95     * Intended vCard is as follows:
96     * <code>
97     * BEGIN:VCARD
98     * N:test1
99     * BEGIN:VCARD
100     * N:test2
101     * END:VCARD
102     * TEL:1
103     * END:VCARD
104     * </code>
105     */
106    public void testNestHandling() {
107        VCardEntryConstructor entryConstructor = new VCardEntryConstructor();
108        MockVCardEntryHandler entryHandler = new MockVCardEntryHandler();
109        entryConstructor.addEntryHandler(entryHandler);
110
111        entryConstructor.onVCardStarted();
112        entryConstructor.onEntryStarted();
113        VCardProperty property = new VCardProperty();
114        property.setName(VCardConstants.PROPERTY_N);
115        property.setValues("test1");
116        entryConstructor.onPropertyCreated(property);
117
118        entryConstructor.onEntryStarted();  // begin nest
119        property = new VCardProperty();
120        property.setName(VCardConstants.PROPERTY_N);
121        property.setValues("test2");
122        entryConstructor.onPropertyCreated(property);
123        entryConstructor.onEntryEnded();  // end nest
124
125        property = new VCardProperty();
126        property.setName(VCardConstants.PROPERTY_TEL);
127        property.setValues("1");
128        entryConstructor.onPropertyCreated(property);
129        entryConstructor.onEntryEnded();
130        entryConstructor.onVCardEnded();
131
132        List<VCardEntry> entries = entryHandler.getEntries();
133        assertEquals(2, entries.size());
134        VCardEntry parent = entries.get(1);
135        VCardEntry child = entries.get(0);
136        assertEquals("test1", parent.getDisplayName());
137        assertEquals("test2", child.getDisplayName());
138        List<VCardEntry.PhoneData> phoneList = parent.getPhoneList();
139        assertNotNull(phoneList);
140        assertEquals(1, phoneList.size());
141        assertEquals("1", phoneList.get(0).getNumber());
142    }
143
144    private class MockEntryElementIterator implements VCardEntry.EntryElementIterator {
145        private boolean mStartCalled;
146        private boolean mEndCalled;
147        private EntryLabel mLabel;
148        private final Map<EntryLabel, EntryElement> mExpectedElements =
149                new HashMap<EntryLabel, EntryElement>();
150
151        public void addExpectedElement(EntryElement elem) {
152            mExpectedElements.put(elem.getEntryLabel(), elem);
153        }
154
155        @Override
156        public void onIterationStarted() {
157            assertFalse(mStartCalled);
158            assertFalse(mEndCalled);
159            assertNull(mLabel);
160            mStartCalled = true;
161        }
162
163        @Override
164        public void onIterationEnded() {
165            assertTrue(mStartCalled);
166            assertFalse(mEndCalled);
167            assertNull(mLabel);
168            assertTrue("Expected Elements remaining: " +
169                    Arrays.toString(mExpectedElements.values().toArray()),
170                    mExpectedElements.isEmpty());
171        }
172
173        @Override
174        public void onElementGroupStarted(EntryLabel label) {
175            assertTrue(mStartCalled);
176            assertFalse(mEndCalled);
177            assertNull(mLabel);
178            mLabel = label;
179        }
180
181        @Override
182        public void onElementGroupEnded() {
183            assertTrue(mStartCalled);
184            assertFalse(mEndCalled);
185            assertNotNull(mLabel);
186            mLabel = null;
187        }
188
189        @Override
190        public boolean onElement(EntryElement elem) {
191            EntryElement expectedElem = mExpectedElements.remove(elem.getEntryLabel());
192            assertNotNull("Unexpected elem: " + elem.toString(), expectedElem);
193            assertEquals(expectedElem, elem);
194            return true;
195        }
196    }
197
198    /**
199     * Tests every element in VCardEntry is iterated by
200     * {@link VCardEntry#iterateAllData(com.android.vcard.VCardEntry.EntryElementIterator)}.
201     */
202    public void testEntryElementIterator() {
203        VCardEntry entry = new VCardEntry();
204        MockEntryElementIterator iterator = new MockEntryElementIterator();
205
206        VCardProperty property = new VCardProperty();
207        property.setName("N");
208        property.setValues("family", "given", "middle", "prefix", "suffix");
209        entry.addProperty(property);
210        NameData nameData = new NameData();
211        nameData.setFamily("family");
212        nameData.setGiven("given");
213        nameData.setMiddle("middle");
214        nameData.setPrefix("prefix");
215        nameData.setSuffix("suffix");
216        iterator.addExpectedElement(nameData);
217
218        property = new VCardProperty();
219        property.setName("TEL");
220        property.setParameter("TYPE", "HOME");
221        property.setValues("1");
222        entry.addProperty(property);
223        PhoneData phoneData = new PhoneData("1", Phone.TYPE_HOME, null, false);
224        iterator.addExpectedElement(phoneData);
225
226        property = new VCardProperty();
227        property.setName("EMAIL");
228        property.setParameter("TYPE", "WORK");
229        property.setValues("email");
230        entry.addProperty(property);
231        EmailData emailData = new EmailData("email", Email.TYPE_WORK, null, false);
232        iterator.addExpectedElement(emailData);
233
234        property = new VCardProperty();
235        property.setName("ADR");
236        property.setParameter("TYPE", "HOME");
237        property.setValues(null, null, "street");
238        entry.addProperty(property);
239        PostalData postalData = new PostalData(null, null, "street", null, null, null,
240                null, StructuredPostal.TYPE_HOME, null, false,
241                VCardConfig.VCARD_TYPE_DEFAULT);
242        iterator.addExpectedElement(postalData);
243
244        property = new VCardProperty();
245        property.setName("ORG");
246        property.setValues("organization", "depertment");
247        entry.addProperty(property);
248        OrganizationData organizationData = new OrganizationData(
249                "organization", "depertment", null, null, Organization.TYPE_WORK, false);
250        iterator.addExpectedElement(organizationData);
251
252        property = new VCardProperty();
253        property.setName("X-GOOGLE-TALK");
254        property.setParameter("TYPE", "WORK");
255        property.setValues("googletalk");
256        entry.addProperty(property);
257        ImData imData = new ImData(
258                Im.PROTOCOL_GOOGLE_TALK, null, "googletalk", Im.TYPE_WORK, false);
259        iterator.addExpectedElement(imData);
260
261        property = new VCardProperty();
262        property.setName("PHOTO");
263        property.setParameter("TYPE", "PNG");
264        byte[] photoBytes = new byte[] {1};
265        property.setByteValue(photoBytes);
266        entry.addProperty(property);
267        PhotoData photoData = new PhotoData("PNG", photoBytes, false);
268        iterator.addExpectedElement(photoData);
269
270        property = new VCardProperty();
271        property.setName("X-SIP");
272        property.setValues("sipdata");
273        entry.addProperty(property);
274        SipData sipData = new SipData("sip:sipdata", SipAddress.TYPE_OTHER, null, false);
275        iterator.addExpectedElement(sipData);
276
277        property = new VCardProperty();
278        property.setName("NICKNAME");
279        property.setValues("nickname");
280        entry.addProperty(property);
281        NicknameData nicknameData = new NicknameData("nickname");
282        iterator.addExpectedElement(nicknameData);
283
284        property = new VCardProperty();
285        property.setName("NOTE");
286        property.setValues("note");
287        entry.addProperty(property);
288        NoteData noteData = new NoteData("note");
289        iterator.addExpectedElement(noteData);
290
291        property = new VCardProperty();
292        property.setName("BDAY");
293        property.setValues("birthday");
294        entry.addProperty(property);
295        BirthdayData birthdayData = new BirthdayData("birthday");
296        iterator.addExpectedElement(birthdayData);
297
298        property = new VCardProperty();
299        property.setName("ANNIVERSARY");
300        property.setValues("anniversary");
301        entry.addProperty(property);
302        AnniversaryData anniversaryData = new AnniversaryData("anniversary");
303        iterator.addExpectedElement(anniversaryData);
304
305        property = new VCardProperty();
306        property.setName("X-ANDROID-CUSTOM");
307        property.setValues("mime;value");
308        entry.addProperty(property);
309        AndroidCustomData androidCustom = new AndroidCustomData("mime", Arrays.asList("value"));
310        iterator.addExpectedElement(androidCustom);
311
312        entry.iterateAllData(iterator);
313    }
314
315    public void testToString() {
316        VCardEntry entry = new VCardEntry();
317        VCardProperty property = new VCardProperty();
318        property.setName("N");
319        property.setValues("Family", "Given", "Middle", "Prefix", "Suffix");
320        entry.addProperty(property);
321        entry.consolidateFields();
322
323        String result = entry.toString();
324        assertNotNull(result);
325
326        assertTrue(result.contains(String.valueOf(entry.hashCode())));
327        assertTrue(result.contains(VCardEntry.EntryLabel.NAME.toString()));
328        assertTrue(result.contains("Family"));
329        assertTrue(result.contains("Given"));
330        assertTrue(result.contains("Middle"));
331        assertTrue(result.contains("Prefix"));
332        assertTrue(result.contains("Suffix"));
333    }
334
335    /**
336     * Tests that VCardEntry emits correct insert operation for name field.
337     */
338    public void testConstructInsertOperationsInsertName() {
339        VCardEntry entry = new VCardEntry();
340        VCardProperty property = new VCardProperty();
341        property.setName("N");
342        property.setValues("Family", "Given", "Middle", "Prefix", "Suffix");
343        entry.addProperty(property);
344        entry.consolidateFields();
345
346        NameData nameData = entry.getNameData();
347        assertEquals("Family", nameData.getFamily());
348        assertEquals("Given", nameData.getGiven());
349        assertEquals("Middle", nameData.getMiddle());
350        assertEquals("Prefix", nameData.getPrefix());
351        assertEquals("Suffix", nameData.getSuffix());
352
353        ContentResolver resolver = getContext().getContentResolver();
354        ArrayList<ContentProviderOperation> operationList =
355                new ArrayList<ContentProviderOperation>();
356        entry.constructInsertOperations(resolver, operationList);
357
358        // Need too many details for testing these. Just check basics.
359        // TODO: introduce nice-to-test mechanism here.
360        assertEquals(2, operationList.size());
361        assertEquals(ContentProviderOperation.TYPE_INSERT, operationList.get(0).getType());
362        assertEquals(ContentProviderOperation.TYPE_INSERT, operationList.get(1).getType());
363    }
364
365    /**
366     * Tests that VCardEntry refrains from emitting unnecessary insert operation.
367     */
368    public void testConstructInsertOperationsEmptyData() {
369        VCardEntry entry = new VCardEntry();
370        ContentResolver resolver = getContext().getContentResolver();
371        ArrayList<ContentProviderOperation> operationList =
372                new ArrayList<ContentProviderOperation>();
373        entry.constructInsertOperations(resolver, operationList);
374        assertEquals(0, operationList.size());
375    }
376
377    // TODO: add bunch of test for constructInsertOperations..
378}