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.editor;
18
19import android.test.AndroidTestCase;
20import android.test.MoreAsserts;
21import android.test.suitebuilder.annotation.SmallTest;
22
23import com.android.contacts.common.model.account.AccountType;
24import com.android.contacts.common.model.account.AccountWithDataSet;
25import com.android.contacts.common.test.mocks.MockAccountTypeManager;
26import com.google.common.collect.Sets;
27
28import java.util.Collection;
29import java.util.Set;
30
31/**
32 * Test case for {@link ContactEditorUtils}.
33 *
34 * adb shell am instrument -w -e class com.android.contacts.editor.ContactEditorUtilsTest \
35       com.android.contacts.tests/android.test.InstrumentationTestRunner
36 */
37@SmallTest
38public class ContactEditorUtilsTest extends AndroidTestCase {
39    private MockAccountTypeManager mAccountTypes;
40    private ContactEditorUtils mTarget;
41
42    private static final MockAccountType TYPE1 = new MockAccountType("type1", null, true);
43    private static final MockAccountType TYPE2 = new MockAccountType("type2", null, true);
44    private static final MockAccountType TYPE2EX = new MockAccountType("type2", "ext", true);
45
46    // Only type 3 is "readonly".
47    private static final MockAccountType TYPE3 = new MockAccountType("type3", null, false);
48
49    private static final AccountWithDataSet ACCOUNT_1_A = new AccountWithDataSet(
50            "a", TYPE1.accountType, TYPE1.dataSet);
51    private static final AccountWithDataSet ACCOUNT_1_B = new AccountWithDataSet(
52            "b", TYPE1.accountType, TYPE1.dataSet);
53
54    private static final AccountWithDataSet ACCOUNT_2_A = new AccountWithDataSet(
55            "a", TYPE2.accountType, TYPE2.dataSet);
56    private static final AccountWithDataSet ACCOUNT_2EX_A = new AccountWithDataSet(
57            "a", TYPE2EX.accountType, TYPE2EX.dataSet);
58
59    private static final AccountWithDataSet ACCOUNT_3_C = new AccountWithDataSet(
60            "c", TYPE3.accountType, TYPE3.dataSet);
61
62    @Override
63    protected void setUp() throws Exception {
64        // Initialize with 0 types, 0 accounts.
65        mAccountTypes = new MockAccountTypeManager(new AccountType[] {},
66                new AccountWithDataSet[] {});
67        mTarget = new ContactEditorUtils(getContext(), mAccountTypes);
68
69        // Clear the preferences.
70        mTarget.cleanupForTest();
71    }
72
73    private void setAccountTypes(AccountType... types) {
74        mAccountTypes.mTypes = types;
75    }
76
77    private void setAccounts(AccountWithDataSet... accounts) {
78        mAccountTypes.mAccounts = accounts;
79    }
80
81    public void testGetWritableAccountTypeStrings() {
82        String[] types;
83
84        // 0 writable types
85        setAccountTypes();
86
87        types = mTarget.getWritableAccountTypeStrings();
88        MoreAsserts.assertEquals(types, new String[0]);
89
90        // 1 writable type
91        setAccountTypes(TYPE1);
92
93        types = mTarget.getWritableAccountTypeStrings();
94        MoreAsserts.assertEquals(Sets.newHashSet(TYPE1.accountType), Sets.newHashSet(types));
95
96        // 2 writable types
97        setAccountTypes(TYPE1, TYPE2EX);
98
99        types = mTarget.getWritableAccountTypeStrings();
100        MoreAsserts.assertEquals(Sets.newHashSet(TYPE1.accountType, TYPE2EX.accountType),
101                Sets.newHashSet(types));
102
103        // 3 writable types + 1 readonly type
104        setAccountTypes(TYPE1, TYPE2, TYPE2EX, TYPE3);
105
106        types = mTarget.getWritableAccountTypeStrings();
107        MoreAsserts.assertEquals(
108                Sets.newHashSet(TYPE1.accountType, TYPE2.accountType, TYPE2EX.accountType),
109                Sets.newHashSet(types));
110    }
111
112    /**
113     * Test for
114     * - {@link ContactEditorUtils#saveDefaultAndAllAccounts}
115     * - {@link ContactEditorUtils#getDefaultAccount}
116     * - {@link ContactEditorUtils#getSavedAccounts()}
117     */
118    public void testSaveDefaultAndAllAccounts() {
119        // Use these account types here.
120        setAccountTypes(TYPE1, TYPE2);
121
122        // If none has been saved, it should return an empty list.
123        assertEquals(0, mTarget.getSavedAccounts().size());
124
125        // Save 0 accounts.
126        mAccountTypes.mAccounts = new AccountWithDataSet[]{};
127        mTarget.saveDefaultAndAllAccounts(null);
128        assertNull(mTarget.getDefaultAccount());
129        MoreAsserts.assertEquals(
130                Sets.newHashSet(mAccountTypes.mAccounts),
131                toSet(mTarget.getSavedAccounts()));
132
133        // 1 account
134        mAccountTypes.mAccounts = new AccountWithDataSet[]{ACCOUNT_1_A};
135        mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_A);
136        assertEquals(ACCOUNT_1_A, mTarget.getDefaultAccount());
137        MoreAsserts.assertEquals(
138                Sets.newHashSet(mAccountTypes.mAccounts),
139                toSet(mTarget.getSavedAccounts()));
140
141        // 2 accounts
142        mAccountTypes.mAccounts = new AccountWithDataSet[]{ACCOUNT_1_A, ACCOUNT_1_B};
143        mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_B);
144        assertEquals(ACCOUNT_1_B, mTarget.getDefaultAccount());
145        MoreAsserts.assertEquals(
146                Sets.newHashSet(mAccountTypes.mAccounts),
147                toSet(mTarget.getSavedAccounts()));
148
149        // 2 accounts, and save null as the default.  Even though there are accounts, the saved
150        // account list should be empty in this case.
151        mTarget.saveDefaultAndAllAccounts(null);
152        assertNull(mTarget.getDefaultAccount());
153        assertEquals(0, mTarget.getSavedAccounts().size());
154    }
155
156    public void testIsAccountValid() {
157        // Use these account types here.
158        setAccountTypes(TYPE1, TYPE2);
159
160        // 0 accounts
161        mAccountTypes.mAccounts = new AccountWithDataSet[]{};
162        assertFalse(mTarget.isValidAccount(ACCOUNT_1_A));
163        assertTrue(mTarget.isValidAccount(null)); // null is always valid
164
165        // 2 accounts
166        mAccountTypes.mAccounts = new AccountWithDataSet[]{ACCOUNT_1_A, ACCOUNT_2_A};
167        assertTrue(mTarget.isValidAccount(ACCOUNT_1_A));
168        assertTrue(mTarget.isValidAccount(ACCOUNT_2_A));
169        assertFalse(mTarget.isValidAccount(ACCOUNT_2EX_A));
170        assertTrue(mTarget.isValidAccount(null)); // null is always valid
171    }
172
173    /**
174     * Tests for {@link ContactEditorUtils#shouldShowAccountChangedNotification()}, starting with
175     * 0 accounts.
176     */
177    public void testShouldShowAccountChangedNotification_0Accounts() {
178        // There's always at least one writable type...
179        setAccountTypes(TYPE1);
180
181        // First launch -- always true.
182        assertTrue(mTarget.shouldShowAccountChangedNotification());
183
184        // We show the notification here, and user clicked "add account"
185        setAccounts(ACCOUNT_1_A);
186
187        // Now we open the contact editor with the new account.
188
189        // When closing the editor, we save the default account.
190        mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_A);
191
192        // Next time the user creates a contact, we don't show the notification.
193        assertFalse(mTarget.shouldShowAccountChangedNotification());
194
195        // User added a new writable account, ACCOUNT_1_B.
196        setAccounts(ACCOUNT_1_A, ACCOUNT_1_B);
197
198        // Now we show the notification again.
199        assertTrue(mTarget.shouldShowAccountChangedNotification());
200
201        // User saved a new contact.  We update the account list and the default account.
202        mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_B);
203
204        // User created another contact.  Now we don't show the notification.
205        assertFalse(mTarget.shouldShowAccountChangedNotification());
206
207        // User installed a new contact sync adapter...
208
209        // Added a new account type: TYPE2, and the TYPE2EX extension.
210        setAccountTypes(TYPE1, TYPE2, TYPE2EX);
211        // Add new accounts: ACCOUNT_2_A, ACCOUNT_2EX_A.
212        setAccounts(ACCOUNT_1_A, ACCOUNT_1_B, ACCOUNT_2_A, ACCOUNT_2EX_A);
213
214        // New account means another notification.
215        assertTrue(mTarget.shouldShowAccountChangedNotification());
216
217        // User saves a new contact, with a different default account.
218        mTarget.saveDefaultAndAllAccounts(ACCOUNT_2_A);
219
220        // Next time user creates a contact, no notification.
221        assertFalse(mTarget.shouldShowAccountChangedNotification());
222
223        // Remove ACCOUNT_2EX_A.
224        setAccountTypes(TYPE1, TYPE2, TYPE2EX);
225        setAccounts(ACCOUNT_1_A, ACCOUNT_1_B, ACCOUNT_2_A);
226
227        // ACCOUNT_2EX_A was not default, so no notification either.
228        assertFalse(mTarget.shouldShowAccountChangedNotification());
229
230        // Remove ACCOUNT_1_B, which is default.
231        setAccountTypes(TYPE1, TYPE2, TYPE2EX);
232        setAccounts(ACCOUNT_1_A, ACCOUNT_1_B);
233
234        // Now we show the notification.
235        assertTrue(mTarget.shouldShowAccountChangedNotification());
236    }
237
238    /**
239     * Tests for {@link ContactEditorUtils#shouldShowAccountChangedNotification()}, starting with
240     * 1 accounts.
241     */
242    public void testShouldShowAccountChangedNotification_1Account() {
243        setAccountTypes(TYPE1, TYPE2);
244        setAccounts(ACCOUNT_1_A);
245
246        // First launch -- always true.
247        assertTrue(mTarget.shouldShowAccountChangedNotification());
248
249        // User saves a new contact.
250        mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_A);
251
252        // Next time, no notification.
253        assertFalse(mTarget.shouldShowAccountChangedNotification());
254
255        // The rest is the same...
256    }
257
258    /**
259     * Tests for {@link ContactEditorUtils#shouldShowAccountChangedNotification()}, starting with
260     * 0 accounts, and the user selected "local only".
261     */
262    public void testShouldShowAccountChangedNotification_0Account_localOnly() {
263        // There's always at least one writable type...
264        setAccountTypes(TYPE1);
265
266        // First launch -- always true.
267        assertTrue(mTarget.shouldShowAccountChangedNotification());
268
269        // We show the notification here, and user clicked "keep local" and saved an contact.
270        mTarget.saveDefaultAndAllAccounts(null);
271
272        // Now there are no accounts, and default account is null.
273
274        // The user created another contact, but this we shouldn't show the notification.
275        assertFalse(mTarget.shouldShowAccountChangedNotification());
276    }
277
278    public void testShouldShowAccountChangedNotification_sanity_check() {
279        // Prepare 1 account and save it as the default.
280        setAccountTypes(TYPE1);
281        setAccounts(ACCOUNT_1_A);
282
283        mTarget.saveDefaultAndAllAccounts(ACCOUNT_1_A);
284
285        // Right after a save, the dialog shouldn't show up.
286        assertFalse(mTarget.shouldShowAccountChangedNotification());
287
288        // Remove the default account to emulate broken preferences.
289        mTarget.removeDefaultAccountForTest();
290        assertTrue(mTarget.shouldShowAccountChangedNotification());
291    }
292
293    private static <T> Set<T> toSet(Collection<T> collection) {
294        Set<T> ret = Sets.newHashSet();
295        ret.addAll(collection);
296        return ret;
297    }
298
299    private static class MockAccountType extends AccountType {
300        private boolean mAreContactsWritable;
301
302        public MockAccountType(String accountType, String dataSet, boolean areContactsWritable) {
303            this.accountType = accountType;
304            this.dataSet = dataSet;
305            mAreContactsWritable = areContactsWritable;
306        }
307
308        @Override
309        public boolean areContactsWritable() {
310            return mAreContactsWritable;
311        }
312
313        @Override
314        public boolean isGroupMembershipEditable() {
315            return true;
316        }
317    }
318}
319