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 
17 package com.android.dialer.calllog;
18 
19 import android.content.Context;
20 import android.database.MatrixCursor;
21 import android.test.AndroidTestCase;
22 import android.test.suitebuilder.annotation.SmallTest;
23 import android.view.View;
24 import android.widget.LinearLayout;
25 
26 import com.google.common.collect.Lists;
27 
28 import java.util.List;
29 
30 /**
31  * Unit tests for {@link CallLogAdapter}.
32  */
33 @SmallTest
34 public class CallLogAdapterTest extends AndroidTestCase {
35     private static final String TEST_NUMBER = "12345678";
36     private static final String TEST_NAME = "name";
37     private static final String TEST_NUMBER_LABEL = "label";
38     private static final int TEST_NUMBER_TYPE = 1;
39     private static final String TEST_COUNTRY_ISO = "US";
40 
41     /** The object under test. */
42     private TestCallLogAdapter mAdapter;
43 
44     private MatrixCursor mCursor;
45     private View mView;
46 
47     @Override
setUp()48     protected void setUp() throws Exception {
49         super.setUp();
50         // Use a call fetcher that does not do anything.
51         CallLogAdapter.CallFetcher fakeCallFetcher = new CallLogAdapter.CallFetcher() {
52             @Override
53             public void fetchCalls() {}
54         };
55 
56         ContactInfoHelper fakeContactInfoHelper =
57                 new ContactInfoHelper(getContext(), TEST_COUNTRY_ISO) {
58                     @Override
59                     public ContactInfo lookupNumber(String number, String countryIso) {
60                         ContactInfo info = new ContactInfo();
61                         info.number = number;
62                         info.formattedNumber = number;
63                         return info;
64                     }
65                 };
66 
67         mAdapter = new TestCallLogAdapter(getContext(), fakeCallFetcher, fakeContactInfoHelper);
68         // The cursor used in the tests to store the entries to display.
69         mCursor = new MatrixCursor(CallLogQuery._PROJECTION);
70         mCursor.moveToFirst();
71         // The views into which to store the data.
72         mView = new LinearLayout(getContext());
73         mView.setTag(CallLogListItemViews.createForTest(getContext()));
74     }
75 
76     @Override
tearDown()77     protected void tearDown() throws Exception {
78         mAdapter = null;
79         mCursor = null;
80         mView = null;
81         super.tearDown();
82     }
83 
testBindView_NoCallLogCacheNorMemoryCache_EnqueueRequest()84     public void testBindView_NoCallLogCacheNorMemoryCache_EnqueueRequest() {
85         mCursor.addRow(createCallLogEntry());
86 
87         // Bind the views of a single row.
88         mAdapter.bindStandAloneView(mView, getContext(), mCursor);
89 
90         // There is one request for contact details.
91         assertEquals(1, mAdapter.requests.size());
92 
93         TestCallLogAdapter.Request request = mAdapter.requests.get(0);
94         // It is for the number we need to show.
95         assertEquals(TEST_NUMBER, request.number);
96         // It has the right country.
97         assertEquals(TEST_COUNTRY_ISO, request.countryIso);
98         // Since there is nothing in the cache, it is an immediate request.
99         assertTrue("should be immediate", request.immediate);
100     }
101 
testBindView_CallLogCacheButNoMemoryCache_EnqueueRequest()102     public void testBindView_CallLogCacheButNoMemoryCache_EnqueueRequest() {
103         mCursor.addRow(createCallLogEntryWithCachedValues());
104 
105         // Bind the views of a single row.
106         mAdapter.bindStandAloneView(mView, getContext(), mCursor);
107 
108         // There is one request for contact details.
109         assertEquals(1, mAdapter.requests.size());
110 
111         TestCallLogAdapter.Request request = mAdapter.requests.get(0);
112         // The values passed to the request, match the ones in the call log cache.
113         assertEquals(TEST_NAME, request.callLogInfo.name);
114         assertEquals(1, request.callLogInfo.type);
115         assertEquals(TEST_NUMBER_LABEL, request.callLogInfo.label);
116     }
117 
118 
testBindView_NoCallLogButMemoryCache_EnqueueRequest()119     public void testBindView_NoCallLogButMemoryCache_EnqueueRequest() {
120         mCursor.addRow(createCallLogEntry());
121         mAdapter.injectContactInfoForTest(TEST_NUMBER, TEST_COUNTRY_ISO, createContactInfo());
122 
123         // Bind the views of a single row.
124         mAdapter.bindStandAloneView(mView, getContext(), mCursor);
125 
126         // There is one request for contact details.
127         assertEquals(1, mAdapter.requests.size());
128 
129         TestCallLogAdapter.Request request = mAdapter.requests.get(0);
130         // Since there is something in the cache, it is not an immediate request.
131         assertFalse("should not be immediate", request.immediate);
132     }
133 
testBindView_BothCallLogAndMemoryCache_NoEnqueueRequest()134     public void testBindView_BothCallLogAndMemoryCache_NoEnqueueRequest() {
135         mCursor.addRow(createCallLogEntryWithCachedValues());
136         mAdapter.injectContactInfoForTest(TEST_NUMBER, TEST_COUNTRY_ISO, createContactInfo());
137 
138         // Bind the views of a single row.
139         mAdapter.bindStandAloneView(mView, getContext(), mCursor);
140 
141         // Cache and call log are up-to-date: no need to request update.
142         assertEquals(0, mAdapter.requests.size());
143     }
144 
testBindView_MismatchBetwenCallLogAndMemoryCache_EnqueueRequest()145     public void testBindView_MismatchBetwenCallLogAndMemoryCache_EnqueueRequest() {
146         mCursor.addRow(createCallLogEntryWithCachedValues());
147 
148         // Contact info contains a different name.
149         ContactInfo info = createContactInfo();
150         info.name = "new name";
151         mAdapter.injectContactInfoForTest(TEST_NUMBER, TEST_COUNTRY_ISO, info);
152 
153         // Bind the views of a single row.
154         mAdapter.bindStandAloneView(mView, getContext(), mCursor);
155 
156         // There is one request for contact details.
157         assertEquals(1, mAdapter.requests.size());
158 
159         TestCallLogAdapter.Request request = mAdapter.requests.get(0);
160         // Since there is something in the cache, it is not an immediate request.
161         assertFalse("should not be immediate", request.immediate);
162     }
163 
164     /** Returns a contact info with default values. */
createContactInfo()165     private ContactInfo createContactInfo() {
166         ContactInfo info = new ContactInfo();
167         info.number = TEST_NUMBER;
168         info.name = TEST_NAME;
169         info.type = TEST_NUMBER_TYPE;
170         info.label = TEST_NUMBER_LABEL;
171         return info;
172     }
173 
174     /** Returns a call log entry without cached values. */
createCallLogEntry()175     private Object[] createCallLogEntry() {
176         Object[] values = CallLogQueryTestUtils.createTestValues();
177         values[CallLogQuery.NUMBER] = TEST_NUMBER;
178         values[CallLogQuery.COUNTRY_ISO] = TEST_COUNTRY_ISO;
179         return values;
180     }
181 
182     /** Returns a call log entry with a cached values. */
createCallLogEntryWithCachedValues()183     private Object[] createCallLogEntryWithCachedValues() {
184         Object[] values = createCallLogEntry();
185         values[CallLogQuery.CACHED_NAME] = TEST_NAME;
186         values[CallLogQuery.CACHED_NUMBER_TYPE] = TEST_NUMBER_TYPE;
187         values[CallLogQuery.CACHED_NUMBER_LABEL] = TEST_NUMBER_LABEL;
188         return values;
189     }
190 
191     /**
192      * Subclass of {@link CallLogAdapter} used in tests to intercept certain calls.
193      */
194     // TODO: This would be better done by splitting the contact lookup into a collaborator class
195     // instead.
196     private static final class TestCallLogAdapter extends CallLogAdapter {
197         public static class Request {
198             public final String number;
199             public final String countryIso;
200             public final ContactInfo callLogInfo;
201             public final boolean immediate;
202 
Request(String number, String countryIso, ContactInfo callLogInfo, boolean immediate)203             public Request(String number, String countryIso, ContactInfo callLogInfo,
204                     boolean immediate) {
205                 this.number = number;
206                 this.countryIso = countryIso;
207                 this.callLogInfo = callLogInfo;
208                 this.immediate = immediate;
209             }
210         }
211 
212         public final List<Request> requests = Lists.newArrayList();
213 
TestCallLogAdapter(Context context, CallFetcher callFetcher, ContactInfoHelper contactInfoHelper)214         public TestCallLogAdapter(Context context, CallFetcher callFetcher,
215                 ContactInfoHelper contactInfoHelper) {
216             super(context, callFetcher, contactInfoHelper, null, null, false);
217         }
218 
219         @Override
enqueueRequest(String number, String countryIso, ContactInfo callLogInfo, boolean immediate)220         protected void enqueueRequest(String number, String countryIso, ContactInfo callLogInfo,
221                 boolean immediate) {
222             requests.add(new Request(number, countryIso, callLogInfo, immediate));
223         }
224     }
225 }
226