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 */
16
17package com.android.contacts.calllog;
18
19import static com.google.android.collect.Lists.newArrayList;
20
21import android.database.MatrixCursor;
22import android.provider.CallLog.Calls;
23import android.test.AndroidTestCase;
24
25import java.util.List;
26
27/**
28 * Unit tests for {@link CallLogGroupBuilder}
29 */
30public class CallLogGroupBuilderTest extends AndroidTestCase {
31    /** A phone number for testing. */
32    private static final String TEST_NUMBER1 = "14125551234";
33    /** A phone number for testing. */
34    private static final String TEST_NUMBER2 = "14125555555";
35
36    /** The object under test. */
37    private CallLogGroupBuilder mBuilder;
38    /** Records the created groups. */
39    private FakeGroupCreator mFakeGroupCreator;
40    /** Cursor to store the values. */
41    private MatrixCursor mCursor;
42
43    @Override
44    protected void setUp() throws Exception {
45        super.setUp();
46        mFakeGroupCreator = new FakeGroupCreator();
47        mBuilder = new CallLogGroupBuilder(mFakeGroupCreator);
48        createCursor();
49    }
50
51    @Override
52    protected void tearDown() throws Exception {
53        mCursor = null;
54        mBuilder = null;
55        mFakeGroupCreator = null;
56        super.tearDown();
57    }
58
59    public void testAddGroups_NoCalls() {
60        mBuilder.addGroups(mCursor);
61        assertEquals(0, mFakeGroupCreator.groups.size());
62    }
63
64    public void testAddGroups_OneCall() {
65        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
66        mBuilder.addGroups(mCursor);
67        assertEquals(0, mFakeGroupCreator.groups.size());
68    }
69
70    public void testAddGroups_TwoCallsNotMatching() {
71        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
72        addOldCallLogEntry(TEST_NUMBER2, Calls.INCOMING_TYPE);
73        mBuilder.addGroups(mCursor);
74        assertEquals(0, mFakeGroupCreator.groups.size());
75    }
76
77    public void testAddGroups_ThreeCallsMatching() {
78        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
79        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
80        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
81        mBuilder.addGroups(mCursor);
82        assertEquals(1, mFakeGroupCreator.groups.size());
83        assertGroupIs(0, 3, false, mFakeGroupCreator.groups.get(0));
84    }
85
86    public void testAddGroups_MatchingIncomingAndOutgoing() {
87        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
88        addOldCallLogEntry(TEST_NUMBER1, Calls.OUTGOING_TYPE);
89        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
90        mBuilder.addGroups(mCursor);
91        assertEquals(1, mFakeGroupCreator.groups.size());
92        assertGroupIs(0, 3, false, mFakeGroupCreator.groups.get(0));
93    }
94
95    public void testAddGroups_HeaderSplitsGroups() {
96        addNewCallLogHeader();
97        addNewCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
98        addNewCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
99        addOldCallLogHeader();
100        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
101        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
102        mBuilder.addGroups(mCursor);
103        assertEquals(2, mFakeGroupCreator.groups.size());
104        assertGroupIs(1, 2, false, mFakeGroupCreator.groups.get(0));
105        assertGroupIs(4, 2, false, mFakeGroupCreator.groups.get(1));
106    }
107
108    public void testAddGroups_Voicemail() {
109        // Groups with one or more missed calls.
110        assertCallsAreGrouped(Calls.VOICEMAIL_TYPE, Calls.MISSED_TYPE);
111        assertCallsAreGrouped(Calls.VOICEMAIL_TYPE, Calls.MISSED_TYPE, Calls.MISSED_TYPE);
112        // Does not group with other types of calls, include voicemail themselves.
113        assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.VOICEMAIL_TYPE);
114        assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.INCOMING_TYPE);
115        assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.OUTGOING_TYPE);
116    }
117
118    public void testAddGroups_Missed() {
119        // Groups with one or more missed calls.
120        assertCallsAreGrouped(Calls.MISSED_TYPE, Calls.MISSED_TYPE);
121        assertCallsAreGrouped(Calls.MISSED_TYPE, Calls.MISSED_TYPE, Calls.MISSED_TYPE);
122        // Does not group with other types of calls.
123        assertCallsAreNotGrouped(Calls.MISSED_TYPE, Calls.VOICEMAIL_TYPE);
124        assertCallsAreNotGrouped(Calls.MISSED_TYPE, Calls.INCOMING_TYPE);
125        assertCallsAreNotGrouped(Calls.MISSED_TYPE, Calls.OUTGOING_TYPE);
126    }
127
128    public void testAddGroups_Incoming() {
129        // Groups with one or more incoming or outgoing.
130        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.INCOMING_TYPE);
131        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE);
132        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE);
133        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE, Calls.INCOMING_TYPE);
134        // Does not group with voicemail and missed calls.
135        assertCallsAreNotGrouped(Calls.INCOMING_TYPE, Calls.VOICEMAIL_TYPE);
136        assertCallsAreNotGrouped(Calls.INCOMING_TYPE, Calls.MISSED_TYPE);
137    }
138
139    public void testAddGroups_Outgoing() {
140        // Groups with one or more incoming or outgoing.
141        assertCallsAreGrouped(Calls.OUTGOING_TYPE, Calls.INCOMING_TYPE);
142        assertCallsAreGrouped(Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE);
143        assertCallsAreGrouped(Calls.OUTGOING_TYPE, Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE);
144        assertCallsAreGrouped(Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE, Calls.INCOMING_TYPE);
145        // Does not group with voicemail and missed calls.
146        assertCallsAreNotGrouped(Calls.INCOMING_TYPE, Calls.VOICEMAIL_TYPE);
147        assertCallsAreNotGrouped(Calls.INCOMING_TYPE, Calls.MISSED_TYPE);
148    }
149
150    public void testAddGroups_Mixed() {
151        addMultipleOldCallLogEntries(TEST_NUMBER1,
152                Calls.VOICEMAIL_TYPE,  // Stand-alone
153                Calls.INCOMING_TYPE,  // Group 1: 1-2
154                Calls.OUTGOING_TYPE,
155                Calls.MISSED_TYPE,  // Group 2: 3-4
156                Calls.MISSED_TYPE,
157                Calls.VOICEMAIL_TYPE,  // Stand-alone
158                Calls.INCOMING_TYPE,  // Stand-alone
159                Calls.VOICEMAIL_TYPE,  // Group 3: 7-9
160                Calls.MISSED_TYPE,
161                Calls.MISSED_TYPE,
162                Calls.OUTGOING_TYPE);  // Stand-alone
163        mBuilder.addGroups(mCursor);
164        assertEquals(3, mFakeGroupCreator.groups.size());
165        assertGroupIs(1, 2, false, mFakeGroupCreator.groups.get(0));
166        assertGroupIs(3, 2, false, mFakeGroupCreator.groups.get(1));
167        assertGroupIs(7, 3, false, mFakeGroupCreator.groups.get(2));
168    }
169
170    public void testEqualPhoneNumbers() {
171        // Identical.
172        assertTrue(mBuilder.equalNumbers("6505555555", "6505555555"));
173        assertTrue(mBuilder.equalNumbers("650 555 5555", "650 555 5555"));
174        // Formatting.
175        assertTrue(mBuilder.equalNumbers("6505555555", "650 555 5555"));
176        assertTrue(mBuilder.equalNumbers("6505555555", "(650) 555-5555"));
177        assertTrue(mBuilder.equalNumbers("650 555 5555", "(650) 555-5555"));
178        // Short codes.
179        assertTrue(mBuilder.equalNumbers("55555", "55555"));
180        assertTrue(mBuilder.equalNumbers("55555", "555 55"));
181        // Different numbers.
182        assertFalse(mBuilder.equalNumbers("6505555555", "650555555"));
183        assertFalse(mBuilder.equalNumbers("6505555555", "6505555551"));
184        assertFalse(mBuilder.equalNumbers("650 555 5555", "650 555 555"));
185        assertFalse(mBuilder.equalNumbers("650 555 5555", "650 555 5551"));
186        assertFalse(mBuilder.equalNumbers("55555", "5555"));
187        assertFalse(mBuilder.equalNumbers("55555", "55551"));
188        // SIP addresses.
189        assertTrue(mBuilder.equalNumbers("6505555555@host.com", "6505555555@host.com"));
190        assertTrue(mBuilder.equalNumbers("6505555555@host.com", "6505555555@HOST.COM"));
191        assertTrue(mBuilder.equalNumbers("user@host.com", "user@host.com"));
192        assertTrue(mBuilder.equalNumbers("user@host.com", "user@HOST.COM"));
193        assertFalse(mBuilder.equalNumbers("USER@host.com", "user@host.com"));
194        assertFalse(mBuilder.equalNumbers("user@host.com", "user@host1.com"));
195        // SIP address vs phone number.
196        assertFalse(mBuilder.equalNumbers("6505555555@host.com", "6505555555"));
197        assertFalse(mBuilder.equalNumbers("6505555555", "6505555555@host.com"));
198        assertFalse(mBuilder.equalNumbers("user@host.com", "6505555555"));
199        assertFalse(mBuilder.equalNumbers("6505555555", "user@host.com"));
200        // Nulls.
201        assertTrue(mBuilder.equalNumbers(null, null));
202        assertFalse(mBuilder.equalNumbers(null, "6505555555"));
203        assertFalse(mBuilder.equalNumbers("6505555555", null));
204        assertFalse(mBuilder.equalNumbers(null, "6505555555@host.com"));
205        assertFalse(mBuilder.equalNumbers("6505555555@host.com", null));
206    }
207
208    public void testCompareSipAddresses() {
209        // Identical.
210        assertTrue(mBuilder.compareSipAddresses("6505555555@host.com", "6505555555@host.com"));
211        assertTrue(mBuilder.compareSipAddresses("user@host.com", "user@host.com"));
212        // Host is case insensitive.
213        assertTrue(mBuilder.compareSipAddresses("6505555555@host.com", "6505555555@HOST.COM"));
214        assertTrue(mBuilder.compareSipAddresses("user@host.com", "user@HOST.COM"));
215        // Userinfo is case sensitive.
216        assertFalse(mBuilder.compareSipAddresses("USER@host.com", "user@host.com"));
217        // Different hosts.
218        assertFalse(mBuilder.compareSipAddresses("user@host.com", "user@host1.com"));
219        // Different users.
220        assertFalse(mBuilder.compareSipAddresses("user1@host.com", "user@host.com"));
221        // Nulls.
222        assertTrue(mBuilder.compareSipAddresses(null, null));
223        assertFalse(mBuilder.compareSipAddresses(null, "6505555555@host.com"));
224        assertFalse(mBuilder.compareSipAddresses("6505555555@host.com", null));
225    }
226
227    /** Creates (or recreates) the cursor used to store the call log content for the tests. */
228    private void createCursor() {
229        mCursor = new MatrixCursor(CallLogQuery.EXTENDED_PROJECTION);
230    }
231
232    /** Clears the content of the {@link FakeGroupCreator} used in the tests. */
233    private void clearFakeGroupCreator() {
234        mFakeGroupCreator.groups.clear();
235    }
236
237    /** Asserts that calls of the given types are grouped together into a single group. */
238    private void assertCallsAreGrouped(int... types) {
239        createCursor();
240        clearFakeGroupCreator();
241        addMultipleOldCallLogEntries(TEST_NUMBER1, types);
242        mBuilder.addGroups(mCursor);
243        assertEquals(1, mFakeGroupCreator.groups.size());
244        assertGroupIs(0, types.length, false, mFakeGroupCreator.groups.get(0));
245
246    }
247
248    /** Asserts that calls of the given types are not grouped together at all. */
249    private void assertCallsAreNotGrouped(int... types) {
250        createCursor();
251        clearFakeGroupCreator();
252        addMultipleOldCallLogEntries(TEST_NUMBER1, types);
253        mBuilder.addGroups(mCursor);
254        assertEquals(0, mFakeGroupCreator.groups.size());
255    }
256
257    /** Adds a set of calls with the given types, all from the same number, in the old section. */
258    private void addMultipleOldCallLogEntries(String number, int... types) {
259        for (int type : types) {
260            addOldCallLogEntry(number, type);
261        }
262    }
263
264    /** Adds a call with the given number and type to the old section of the call log. */
265    private void addOldCallLogEntry(String number, int type) {
266        addCallLogEntry(number, type, CallLogQuery.SECTION_OLD_ITEM);
267    }
268
269    /** Adds a call with the given number and type to the new section of the call log. */
270    private void addNewCallLogEntry(String number, int type) {
271        addCallLogEntry(number, type, CallLogQuery.SECTION_NEW_ITEM);
272    }
273
274    /** Adds a call log entry with the given number and type to the cursor. */
275    private void addCallLogEntry(String number, int type, int section) {
276        if (section != CallLogQuery.SECTION_NEW_ITEM
277                && section != CallLogQuery.SECTION_OLD_ITEM) {
278            throw new IllegalArgumentException("not an item section: " + section);
279        }
280        mCursor.moveToNext();
281        Object[] values = CallLogQueryTestUtils.createTestExtendedValues();
282        values[CallLogQuery.ID] = mCursor.getPosition();
283        values[CallLogQuery.NUMBER] = number;
284        values[CallLogQuery.CALL_TYPE] = type;
285        values[CallLogQuery.SECTION] = section;
286        mCursor.addRow(values);
287    }
288
289    /** Adds the old section header to the call log. */
290    private void addOldCallLogHeader() {
291        addCallLogHeader(CallLogQuery.SECTION_OLD_HEADER);
292    }
293
294    /** Adds the new section header to the call log. */
295    private void addNewCallLogHeader() {
296        addCallLogHeader(CallLogQuery.SECTION_NEW_HEADER);
297    }
298
299    /** Adds a call log entry with a header to the cursor. */
300    private void addCallLogHeader(int section) {
301        if (section != CallLogQuery.SECTION_NEW_HEADER
302                && section != CallLogQuery.SECTION_OLD_HEADER) {
303            throw new IllegalArgumentException("not a header section: " + section);
304        }
305        mCursor.moveToNext();
306        Object[] values = CallLogQueryTestUtils.createTestExtendedValues();
307        values[CallLogQuery.ID] = mCursor.getPosition();
308        values[CallLogQuery.SECTION] = section;
309        mCursor.addRow(values);
310    }
311
312    /** Asserts that the group matches the given values. */
313    private void assertGroupIs(int cursorPosition, int size, boolean expanded, GroupSpec group) {
314        assertEquals(cursorPosition, group.cursorPosition);
315        assertEquals(size, group.size);
316        assertEquals(expanded, group.expanded);
317    }
318
319    /** Defines an added group. Used by the {@link FakeGroupCreator}. */
320    private static class GroupSpec {
321        /** The starting position of the group. */
322        public final int cursorPosition;
323        /** The number of elements in the group. */
324        public final int size;
325        /** Whether the group should be initially expanded. */
326        public final boolean expanded;
327
328        public GroupSpec(int cursorPosition, int size, boolean expanded) {
329            this.cursorPosition = cursorPosition;
330            this.size = size;
331            this.expanded = expanded;
332        }
333    }
334
335    /** Fake implementation of a GroupCreator which stores the created groups in a member field. */
336    private static class FakeGroupCreator implements CallLogGroupBuilder.GroupCreator {
337        /** The list of created groups. */
338        public final List<GroupSpec> groups = newArrayList();
339
340        @Override
341        public void addGroup(int cursorPosition, int size, boolean expanded) {
342            groups.add(new GroupSpec(cursorPosition, size, expanded));
343        }
344    }
345}
346