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.common.collect.Lists.newArrayList;
20
21import android.database.MatrixCursor;
22import android.provider.CallLog.Calls;
23import android.test.AndroidTestCase;
24import android.test.suitebuilder.annotation.SmallTest;
25
26import java.util.List;
27
28/**
29 * Unit tests for {@link CallLogGroupBuilder}
30 */
31@SmallTest
32public class CallLogGroupBuilderTest extends AndroidTestCase {
33    /** A phone number for testing. */
34    private static final String TEST_NUMBER1 = "14125551234";
35    /** A phone number for testing. */
36    private static final String TEST_NUMBER2 = "14125555555";
37
38    /** The object under test. */
39    private CallLogGroupBuilder mBuilder;
40    /** Records the created groups. */
41    private FakeGroupCreator mFakeGroupCreator;
42    /** Cursor to store the values. */
43    private MatrixCursor mCursor;
44
45    @Override
46    protected void setUp() throws Exception {
47        super.setUp();
48        mFakeGroupCreator = new FakeGroupCreator();
49        mBuilder = new CallLogGroupBuilder(mFakeGroupCreator);
50        createCursor();
51    }
52
53    @Override
54    protected void tearDown() throws Exception {
55        mCursor = null;
56        mBuilder = null;
57        mFakeGroupCreator = null;
58        super.tearDown();
59    }
60
61    public void testAddGroups_NoCalls() {
62        mBuilder.addGroups(mCursor);
63        assertEquals(0, mFakeGroupCreator.groups.size());
64    }
65
66    public void testAddGroups_OneCall() {
67        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
68        mBuilder.addGroups(mCursor);
69        assertEquals(0, mFakeGroupCreator.groups.size());
70    }
71
72    public void testAddGroups_TwoCallsNotMatching() {
73        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
74        addOldCallLogEntry(TEST_NUMBER2, Calls.INCOMING_TYPE);
75        mBuilder.addGroups(mCursor);
76        assertEquals(0, mFakeGroupCreator.groups.size());
77    }
78
79    public void testAddGroups_ThreeCallsMatching() {
80        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
81        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
82        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
83        mBuilder.addGroups(mCursor);
84        assertEquals(1, mFakeGroupCreator.groups.size());
85        assertGroupIs(0, 3, false, mFakeGroupCreator.groups.get(0));
86    }
87
88    public void testAddGroups_MatchingIncomingAndOutgoing() {
89        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
90        addOldCallLogEntry(TEST_NUMBER1, Calls.OUTGOING_TYPE);
91        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
92        mBuilder.addGroups(mCursor);
93        assertEquals(1, mFakeGroupCreator.groups.size());
94        assertGroupIs(0, 3, false, mFakeGroupCreator.groups.get(0));
95    }
96
97    public void testAddGroups_HeaderSplitsGroups() {
98        addNewCallLogHeader();
99        addNewCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
100        addNewCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
101        addOldCallLogHeader();
102        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
103        addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
104        mBuilder.addGroups(mCursor);
105        assertEquals(2, mFakeGroupCreator.groups.size());
106        assertGroupIs(1, 2, false, mFakeGroupCreator.groups.get(0));
107        assertGroupIs(4, 2, false, mFakeGroupCreator.groups.get(1));
108    }
109
110    public void testAddGroups_Voicemail() {
111        // Does not group with other types of calls, include voicemail themselves.
112        assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.MISSED_TYPE);
113        //assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.MISSED_TYPE, Calls.MISSED_TYPE);
114        assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.VOICEMAIL_TYPE);
115        assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.INCOMING_TYPE);
116        assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.OUTGOING_TYPE);
117    }
118
119    public void testAddGroups_Missed() {
120        // Groups with one or more missed calls.
121        assertCallsAreGrouped(Calls.MISSED_TYPE, Calls.MISSED_TYPE);
122        assertCallsAreGrouped(Calls.MISSED_TYPE, Calls.MISSED_TYPE, Calls.MISSED_TYPE);
123        // Does not group with other types of calls.
124        assertCallsAreNotGrouped(Calls.MISSED_TYPE, Calls.VOICEMAIL_TYPE);
125        assertCallsAreGrouped(Calls.MISSED_TYPE, Calls.INCOMING_TYPE);
126        assertCallsAreGrouped(Calls.MISSED_TYPE, Calls.OUTGOING_TYPE);
127    }
128
129    public void testAddGroups_Incoming() {
130        // Groups with one or more incoming or outgoing.
131        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.INCOMING_TYPE);
132        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE);
133        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE);
134        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE, Calls.INCOMING_TYPE);
135        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.MISSED_TYPE);
136        // Does not group with voicemail and missed calls.
137        assertCallsAreNotGrouped(Calls.INCOMING_TYPE, Calls.VOICEMAIL_TYPE);
138    }
139
140    public void testAddGroups_Outgoing() {
141        // Groups with one or more incoming or outgoing.
142        assertCallsAreGrouped(Calls.OUTGOING_TYPE, Calls.INCOMING_TYPE);
143        assertCallsAreGrouped(Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE);
144        assertCallsAreGrouped(Calls.OUTGOING_TYPE, Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE);
145        assertCallsAreGrouped(Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE, Calls.INCOMING_TYPE);
146        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.MISSED_TYPE);
147        // Does not group with voicemail and missed calls.
148        assertCallsAreNotGrouped(Calls.INCOMING_TYPE, Calls.VOICEMAIL_TYPE);
149    }
150
151    public void testAddGroups_Mixed() {
152        addMultipleOldCallLogEntries(TEST_NUMBER1,
153                Calls.VOICEMAIL_TYPE,  // Stand-alone
154                Calls.INCOMING_TYPE,  // Group 1: 1-4
155                Calls.OUTGOING_TYPE,
156                Calls.MISSED_TYPE,
157                Calls.MISSED_TYPE,
158                Calls.VOICEMAIL_TYPE,  // Stand-alone
159                Calls.INCOMING_TYPE,  // Stand-alone
160                Calls.VOICEMAIL_TYPE,  // Stand-alone
161                Calls.MISSED_TYPE, // Group 2: 8-10
162                Calls.MISSED_TYPE,
163                Calls.OUTGOING_TYPE);
164        mBuilder.addGroups(mCursor);
165        assertEquals(2, mFakeGroupCreator.groups.size());
166        assertGroupIs(1, 4, false, mFakeGroupCreator.groups.get(0));
167        assertGroupIs(8, 3, false, mFakeGroupCreator.groups.get(1));
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