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 static junit.framework.Assert.assertEquals;
20import static junit.framework.Assert.assertFalse;
21import static junit.framework.Assert.assertNull;
22import static org.junit.Assert.assertTrue;
23
24import android.support.test.InstrumentationRegistry;
25import android.support.test.filters.SmallTest;
26import android.support.test.runner.AndroidJUnit4;
27
28import com.android.contacts.model.account.AccountType;
29import com.android.contacts.model.account.AccountWithDataSet;
30
31import org.junit.Before;
32import org.junit.Test;
33import org.junit.runner.RunWith;
34
35import java.util.ArrayList;
36import java.util.Arrays;
37import java.util.Collections;
38import java.util.List;
39
40/**
41 * Test case for {@link ContactEditorUtils}.
42 *
43 * adb shell am instrument -w -e class com.android.contacts.editor.ContactEditorUtilsTest \
44       com.android.contacts.tests/android.test.InstrumentationTestRunner
45
46 * <p>It may make sense to just delete or move these tests since the code under test just forwards
47 * calls to {@link com.android.contacts.preference.ContactsPreferences} and that logic is already
48 * covered by {@link com.android.contacts.preference.ContactsPreferencesTest}
49 * </p>
50 */
51@SmallTest
52@RunWith(AndroidJUnit4.class)
53public class ContactEditorUtilsTest {
54    private ContactEditorUtils mTarget;
55
56    private static final String TYPE1 = "type1";
57    private static final String TYPE2 = "type2";
58    private static final String TYPE2_EXT = "ext";
59
60    private static final AccountWithDataSet ACCOUNT_1_A = new AccountWithDataSet("a", TYPE1, null);
61    private static final AccountWithDataSet ACCOUNT_1_B = new AccountWithDataSet("b", TYPE1, null);
62
63    private static final AccountWithDataSet ACCOUNT_2_A = new AccountWithDataSet("a", TYPE2, null);
64    private static final AccountWithDataSet ACCOUNT_2EX_A = new AccountWithDataSet(
65            "a", TYPE2, TYPE2_EXT);
66
67    @Before
68    public void setUp() throws Exception {
69        mTarget = ContactEditorUtils.create(InstrumentationRegistry.getTargetContext());
70
71        // Clear the preferences.
72        mTarget.cleanupForTest();
73    }
74
75    /**
76     * Test for
77     * - {@link ContactEditorUtils#saveDefaultAccount}
78     * - {@link ContactEditorUtils#getOnlyOrDefaultAccount}
79     */
80    @Test
81    public void testSaveDefaultAccount() {
82        mTarget.saveDefaultAccount(null);
83        assertNull(mTarget.getOnlyOrDefaultAccount(Collections.<AccountWithDataSet>emptyList()));
84
85        mTarget.saveDefaultAccount(ACCOUNT_1_A);
86        assertEquals(ACCOUNT_1_A, mTarget.getOnlyOrDefaultAccount(Collections.
87                <AccountWithDataSet>emptyList()));
88    }
89
90    /**
91     * Tests for
92     * {@link ContactEditorUtils#shouldShowAccountChangedNotification(List<AccountWithDataSet>)},
93     * starting with 0 accounts.
94     */
95    @Test
96    public void testShouldShowAccountChangedNotification_0Accounts() {
97        List<AccountWithDataSet> currentAccounts = new ArrayList<>();
98        assertTrue(mTarget.shouldShowAccountChangedNotification(currentAccounts));
99
100        // We show the notification here, and user clicked "add account"
101        currentAccounts.add(ACCOUNT_1_A);
102
103        // Now we open the contact editor with the new account.
104
105        // When closing the editor, we save the default account.
106        mTarget.saveDefaultAccount(ACCOUNT_1_A);
107
108        // Next time the user creates a contact, we don't show the notification.
109        assertFalse(mTarget.shouldShowAccountChangedNotification(currentAccounts));
110
111        // User added a new writable account, ACCOUNT_1_B.
112        currentAccounts.add(ACCOUNT_1_B);
113
114        // Since default account is still ACCOUNT_1_A, we don't show the notification.
115        assertFalse(mTarget.shouldShowAccountChangedNotification(currentAccounts));
116
117        // User saved a new contact.  We update the account list and the default account.
118        mTarget.saveDefaultAccount(ACCOUNT_1_B);
119
120        // User created another contact.  Now we don't show the notification.
121        assertFalse(mTarget.shouldShowAccountChangedNotification(currentAccounts));
122
123        // User installed a new contact sync adapter...
124
125        // Add new accounts: ACCOUNT_2_A, ACCOUNT_2EX_A.
126        currentAccounts.add(ACCOUNT_2_A);
127        currentAccounts.add(ACCOUNT_2EX_A);
128
129        // New added account but default account is still not changed, so no notification.
130        assertFalse(mTarget.shouldShowAccountChangedNotification(currentAccounts));
131
132        // User saves a new contact, with a different default account.
133        mTarget.saveDefaultAccount(ACCOUNT_2_A);
134
135        // Next time user creates a contact, no notification.
136        assertFalse(mTarget.shouldShowAccountChangedNotification(currentAccounts));
137
138        // Remove ACCOUNT_2EX_A.
139        currentAccounts.remove(ACCOUNT_2EX_A);
140
141        // ACCOUNT_2EX_A was not default, so no notification either.
142        assertFalse(mTarget.shouldShowAccountChangedNotification(currentAccounts));
143
144        // Remove ACCOUNT_2_A, which is default.
145        currentAccounts.remove(ACCOUNT_2_A);
146
147        // Now we show the notification.
148        assertTrue(mTarget.shouldShowAccountChangedNotification(currentAccounts));
149
150        // Do not save the default account, and add a new account now.
151        currentAccounts.add(ACCOUNT_2EX_A);
152
153        // No default account, so show notification.
154        assertTrue(mTarget.shouldShowAccountChangedNotification(currentAccounts));
155    }
156
157    /**
158     * Tests for
159     * {@link ContactEditorUtils#shouldShowAccountChangedNotification(List<AccountWithDataSet>)},
160     * starting with 1 accounts.
161     */
162    @Test
163    public void testShouldShowAccountChangedNotification_1Account() {
164        // Always returns false when 1 writable account.
165        assertFalse(mTarget.shouldShowAccountChangedNotification(
166                Collections.singletonList(ACCOUNT_1_A)));
167
168        // User saves a new contact.
169        mTarget.saveDefaultAccount(ACCOUNT_1_A);
170
171        // Next time, no notification.
172        assertFalse(mTarget.shouldShowAccountChangedNotification(
173                Collections.singletonList(ACCOUNT_1_A)));
174
175        // The rest is the same...
176    }
177
178    /**
179     * Tests for
180     * {@link ContactEditorUtils#shouldShowAccountChangedNotification(List<AccountWithDataSet>)},
181     * starting with 0 accounts, and the user selected "local only".
182     */
183    @Test
184    public void testShouldShowAccountChangedNotification_0Account_localOnly() {
185        // First launch -- always true.
186        assertTrue(mTarget.shouldShowAccountChangedNotification(Collections.
187                <AccountWithDataSet>emptyList()));
188
189        // We show the notification here, and user clicked "keep local" and saved an contact.
190        mTarget.saveDefaultAccount(AccountWithDataSet.getNullAccount());
191
192        // Now there are no accounts, and default account is null.
193
194        // The user created another contact, but this we shouldn't show the notification.
195        assertFalse(mTarget.shouldShowAccountChangedNotification(Collections.
196                <AccountWithDataSet>emptyList()));
197    }
198
199    @Test
200    public void testShouldShowAccountChangedNotification_sanity_check() {
201        // Prepare 1 account and save it as the default.
202        mTarget.saveDefaultAccount(ACCOUNT_1_A);
203
204        // Right after a save, the dialog shouldn't show up.
205        assertFalse(mTarget.shouldShowAccountChangedNotification(
206                Collections.singletonList(ACCOUNT_1_A)));
207
208        // Remove the default account to emulate broken preferences.
209        mTarget.removeDefaultAccountForTest();
210
211        // The dialog shouldn't show up.
212        // The logic is, if there's a writable account, we'll pick it as default
213        assertFalse(mTarget.shouldShowAccountChangedNotification(
214                Collections.singletonList(ACCOUNT_1_A)));
215    }
216
217    @Test
218    public void testShouldShowAccountChangedNotification_nullAccount() {
219        final List<AccountWithDataSet> currentAccounts = new ArrayList<>();
220        final AccountWithDataSet nullAccount = AccountWithDataSet.getNullAccount();
221        currentAccounts.add(nullAccount);
222
223        assertTrue(mTarget.shouldShowAccountChangedNotification(currentAccounts));
224
225        // User chooses to keep the "device" account as the default
226        mTarget.saveDefaultAccount(nullAccount);
227
228        // Right after a save, the dialog shouldn't show up.
229        assertFalse(mTarget.shouldShowAccountChangedNotification(currentAccounts));
230    }
231
232    private static class MockAccountType extends AccountType {
233        private boolean mAreContactsWritable;
234
235        public MockAccountType(String accountType, String dataSet, boolean areContactsWritable) {
236            this.accountType = accountType;
237            this.dataSet = dataSet;
238            mAreContactsWritable = areContactsWritable;
239        }
240
241        @Override
242        public boolean areContactsWritable() {
243            return mAreContactsWritable;
244        }
245
246        @Override
247        public boolean isGroupMembershipEditable() {
248            return true;
249        }
250    }
251}
252