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.dialer.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        addCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
68        mBuilder.addGroups(mCursor);
69        assertEquals(0, mFakeGroupCreator.groups.size());
70    }
71
72    public void testAddGroups_TwoCallsNotMatching() {
73        addCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
74        addCallLogEntry(TEST_NUMBER2, Calls.INCOMING_TYPE);
75        mBuilder.addGroups(mCursor);
76        assertEquals(0, mFakeGroupCreator.groups.size());
77    }
78
79    public void testAddGroups_ThreeCallsMatching() {
80        addCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
81        addCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
82        addCallLogEntry(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        addCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
90        addCallLogEntry(TEST_NUMBER1, Calls.OUTGOING_TYPE);
91        addCallLogEntry(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_Voicemail() {
98        // Does not group with other types of calls, include voicemail themselves.
99        assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.MISSED_TYPE);
100        //assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.MISSED_TYPE, Calls.MISSED_TYPE);
101        assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.VOICEMAIL_TYPE);
102        assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.INCOMING_TYPE);
103        assertCallsAreNotGrouped(Calls.VOICEMAIL_TYPE, Calls.OUTGOING_TYPE);
104    }
105
106    public void testAddGroups_Missed() {
107        // Groups with one or more missed calls.
108        assertCallsAreGrouped(Calls.MISSED_TYPE, Calls.MISSED_TYPE);
109        assertCallsAreGrouped(Calls.MISSED_TYPE, Calls.MISSED_TYPE, Calls.MISSED_TYPE);
110        // Does not group with other types of calls.
111        assertCallsAreNotGrouped(Calls.MISSED_TYPE, Calls.VOICEMAIL_TYPE);
112        assertCallsAreGrouped(Calls.MISSED_TYPE, Calls.INCOMING_TYPE);
113        assertCallsAreGrouped(Calls.MISSED_TYPE, Calls.OUTGOING_TYPE);
114    }
115
116    public void testAddGroups_Incoming() {
117        // Groups with one or more incoming or outgoing.
118        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.INCOMING_TYPE);
119        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE);
120        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE);
121        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE, Calls.INCOMING_TYPE);
122        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.MISSED_TYPE);
123        // Does not group with voicemail and missed calls.
124        assertCallsAreNotGrouped(Calls.INCOMING_TYPE, Calls.VOICEMAIL_TYPE);
125    }
126
127    public void testAddGroups_Outgoing() {
128        // Groups with one or more incoming or outgoing.
129        assertCallsAreGrouped(Calls.OUTGOING_TYPE, Calls.INCOMING_TYPE);
130        assertCallsAreGrouped(Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE);
131        assertCallsAreGrouped(Calls.OUTGOING_TYPE, Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE);
132        assertCallsAreGrouped(Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE, Calls.INCOMING_TYPE);
133        assertCallsAreGrouped(Calls.INCOMING_TYPE, Calls.MISSED_TYPE);
134        // Does not group with voicemail and missed calls.
135        assertCallsAreNotGrouped(Calls.INCOMING_TYPE, Calls.VOICEMAIL_TYPE);
136    }
137
138    public void testAddGroups_Mixed() {
139        addMultipleCallLogEntries(TEST_NUMBER1,
140                Calls.VOICEMAIL_TYPE,  // Stand-alone
141                Calls.INCOMING_TYPE,  // Group 1: 1-4
142                Calls.OUTGOING_TYPE,
143                Calls.MISSED_TYPE,
144                Calls.MISSED_TYPE,
145                Calls.VOICEMAIL_TYPE,  // Stand-alone
146                Calls.INCOMING_TYPE,  // Stand-alone
147                Calls.VOICEMAIL_TYPE,  // Stand-alone
148                Calls.MISSED_TYPE, // Group 2: 8-10
149                Calls.MISSED_TYPE,
150                Calls.OUTGOING_TYPE);
151        mBuilder.addGroups(mCursor);
152        assertEquals(2, mFakeGroupCreator.groups.size());
153        assertGroupIs(1, 4, false, mFakeGroupCreator.groups.get(0));
154        assertGroupIs(8, 3, false, mFakeGroupCreator.groups.get(1));
155    }
156
157    public void testEqualPhoneNumbers() {
158        // Identical.
159        assertTrue(mBuilder.equalNumbers("6505555555", "6505555555"));
160        assertTrue(mBuilder.equalNumbers("650 555 5555", "650 555 5555"));
161        // Formatting.
162        assertTrue(mBuilder.equalNumbers("6505555555", "650 555 5555"));
163        assertTrue(mBuilder.equalNumbers("6505555555", "(650) 555-5555"));
164        assertTrue(mBuilder.equalNumbers("650 555 5555", "(650) 555-5555"));
165        // Short codes.
166        assertTrue(mBuilder.equalNumbers("55555", "55555"));
167        assertTrue(mBuilder.equalNumbers("55555", "555 55"));
168        // Different numbers.
169        assertFalse(mBuilder.equalNumbers("6505555555", "650555555"));
170        assertFalse(mBuilder.equalNumbers("6505555555", "6505555551"));
171        assertFalse(mBuilder.equalNumbers("650 555 5555", "650 555 555"));
172        assertFalse(mBuilder.equalNumbers("650 555 5555", "650 555 5551"));
173        assertFalse(mBuilder.equalNumbers("55555", "5555"));
174        assertFalse(mBuilder.equalNumbers("55555", "55551"));
175        // SIP addresses.
176        assertTrue(mBuilder.equalNumbers("6505555555@host.com", "6505555555@host.com"));
177        assertTrue(mBuilder.equalNumbers("6505555555@host.com", "6505555555@HOST.COM"));
178        assertTrue(mBuilder.equalNumbers("user@host.com", "user@host.com"));
179        assertTrue(mBuilder.equalNumbers("user@host.com", "user@HOST.COM"));
180        assertFalse(mBuilder.equalNumbers("USER@host.com", "user@host.com"));
181        assertFalse(mBuilder.equalNumbers("user@host.com", "user@host1.com"));
182        // SIP address vs phone number.
183        assertFalse(mBuilder.equalNumbers("6505555555@host.com", "6505555555"));
184        assertFalse(mBuilder.equalNumbers("6505555555", "6505555555@host.com"));
185        assertFalse(mBuilder.equalNumbers("user@host.com", "6505555555"));
186        assertFalse(mBuilder.equalNumbers("6505555555", "user@host.com"));
187        // Nulls.
188        assertTrue(mBuilder.equalNumbers(null, null));
189        assertFalse(mBuilder.equalNumbers(null, "6505555555"));
190        assertFalse(mBuilder.equalNumbers("6505555555", null));
191        assertFalse(mBuilder.equalNumbers(null, "6505555555@host.com"));
192        assertFalse(mBuilder.equalNumbers("6505555555@host.com", null));
193    }
194
195    public void testCompareSipAddresses() {
196        // Identical.
197        assertTrue(mBuilder.compareSipAddresses("6505555555@host.com", "6505555555@host.com"));
198        assertTrue(mBuilder.compareSipAddresses("user@host.com", "user@host.com"));
199        // Host is case insensitive.
200        assertTrue(mBuilder.compareSipAddresses("6505555555@host.com", "6505555555@HOST.COM"));
201        assertTrue(mBuilder.compareSipAddresses("user@host.com", "user@HOST.COM"));
202        // Userinfo is case sensitive.
203        assertFalse(mBuilder.compareSipAddresses("USER@host.com", "user@host.com"));
204        // Different hosts.
205        assertFalse(mBuilder.compareSipAddresses("user@host.com", "user@host1.com"));
206        // Different users.
207        assertFalse(mBuilder.compareSipAddresses("user1@host.com", "user@host.com"));
208        // Nulls.
209        assertTrue(mBuilder.compareSipAddresses(null, null));
210        assertFalse(mBuilder.compareSipAddresses(null, "6505555555@host.com"));
211        assertFalse(mBuilder.compareSipAddresses("6505555555@host.com", null));
212    }
213
214    /** Creates (or recreates) the cursor used to store the call log content for the tests. */
215    private void createCursor() {
216        mCursor = new MatrixCursor(CallLogQuery._PROJECTION);
217    }
218
219    /** Clears the content of the {@link FakeGroupCreator} used in the tests. */
220    private void clearFakeGroupCreator() {
221        mFakeGroupCreator.groups.clear();
222    }
223
224    /** Asserts that calls of the given types are grouped together into a single group. */
225    private void assertCallsAreGrouped(int... types) {
226        createCursor();
227        clearFakeGroupCreator();
228        addMultipleCallLogEntries(TEST_NUMBER1, types);
229        mBuilder.addGroups(mCursor);
230        assertEquals(1, mFakeGroupCreator.groups.size());
231        assertGroupIs(0, types.length, false, mFakeGroupCreator.groups.get(0));
232
233    }
234
235    /** Asserts that calls of the given types are not grouped together at all. */
236    private void assertCallsAreNotGrouped(int... types) {
237        createCursor();
238        clearFakeGroupCreator();
239        addMultipleCallLogEntries(TEST_NUMBER1, types);
240        mBuilder.addGroups(mCursor);
241        assertEquals(0, mFakeGroupCreator.groups.size());
242    }
243
244    /** Adds a set of calls with the given types, all from the same number, in the old section. */
245    private void addMultipleCallLogEntries(String number, int... types) {
246        for (int type : types) {
247            addCallLogEntry(number, type);
248        }
249    }
250    /** Adds a call log entry with the given number and type to the cursor. */
251    private void addCallLogEntry(String number, int type) {
252        mCursor.moveToNext();
253        Object[] values = CallLogQueryTestUtils.createTestValues();
254        values[CallLogQuery.ID] = mCursor.getPosition();
255        values[CallLogQuery.NUMBER] = number;
256        values[CallLogQuery.CALL_TYPE] = type;
257        mCursor.addRow(values);
258    }
259
260    /** Adds a call log entry with a header to the cursor. */
261    private void addCallLogHeader(int section) {
262        mCursor.moveToNext();
263        Object[] values = CallLogQueryTestUtils.createTestValues();
264        values[CallLogQuery.ID] = mCursor.getPosition();
265        mCursor.addRow(values);
266    }
267
268    /** Asserts that the group matches the given values. */
269    private void assertGroupIs(int cursorPosition, int size, boolean expanded, GroupSpec group) {
270        assertEquals(cursorPosition, group.cursorPosition);
271        assertEquals(size, group.size);
272        assertEquals(expanded, group.expanded);
273    }
274
275    /** Defines an added group. Used by the {@link FakeGroupCreator}. */
276    private static class GroupSpec {
277        /** The starting position of the group. */
278        public final int cursorPosition;
279        /** The number of elements in the group. */
280        public final int size;
281        /** Whether the group should be initially expanded. */
282        public final boolean expanded;
283
284        public GroupSpec(int cursorPosition, int size, boolean expanded) {
285            this.cursorPosition = cursorPosition;
286            this.size = size;
287            this.expanded = expanded;
288        }
289    }
290
291    /** Fake implementation of a GroupCreator which stores the created groups in a member field. */
292    private static class FakeGroupCreator implements CallLogGroupBuilder.GroupCreator {
293        /** The list of created groups. */
294        public final List<GroupSpec> groups = newArrayList();
295
296        @Override
297        public void addGroup(int cursorPosition, int size, boolean expanded) {
298            groups.add(new GroupSpec(cursorPosition, size, expanded));
299        }
300
301        @Override
302        public void setDayGroup(long rowId, int dayGroup) {
303            //No-op
304        }
305
306        @Override
307        public void clearDayGroups() {
308            //No-op
309        }
310    }
311}
312