1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/profiles/profile_list_desktop.h"
6
7#include <string>
8
9#include "base/command_line.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/metrics/field_trial.h"
12#include "base/strings/string16.h"
13#include "base/strings/utf_string_conversions.h"
14#include "chrome/browser/prefs/pref_service_syncable.h"
15#include "chrome/browser/profiles/avatar_menu_observer.h"
16#include "chrome/browser/profiles/profile_info_cache.h"
17#include "chrome/browser/profiles/profiles_state.h"
18#include "chrome/grit/generated_resources.h"
19#include "chrome/test/base/testing_browser_process.h"
20#include "chrome/test/base/testing_profile_manager.h"
21#include "components/signin/core/common/profile_management_switches.h"
22#include "content/public/test/test_browser_thread_bundle.h"
23#include "testing/gtest/include/gtest/gtest.h"
24#include "ui/base/l10n/l10n_util.h"
25
26using base::ASCIIToUTF16;
27
28namespace {
29
30class MockObserver : public AvatarMenuObserver {
31 public:
32  MockObserver() : count_(0) {}
33  virtual ~MockObserver() {}
34
35  virtual void OnAvatarMenuChanged(
36      AvatarMenu* avatar_menu) OVERRIDE {
37    ++count_;
38  }
39
40  int change_count() const { return count_; }
41
42 private:
43  int count_;
44
45  DISALLOW_COPY_AND_ASSIGN(MockObserver);
46};
47
48class ProfileListDesktopTest : public testing::Test {
49 public:
50  ProfileListDesktopTest()
51      : manager_(TestingBrowserProcess::GetGlobal()) {
52  }
53
54  virtual void SetUp() {
55    ASSERT_TRUE(manager_.SetUp());
56#if defined(OS_CHROMEOS)
57    // AvatarMenu and multiple profiles works after user logged in.
58    manager_.SetLoggedIn(true);
59#endif
60  }
61
62  AvatarMenu* GetAvatarMenu() {
63    // Reset the MockObserver.
64    mock_observer_.reset(new MockObserver());
65    EXPECT_EQ(0, change_count());
66
67    // Reset the model.
68    avatar_menu_.reset(new AvatarMenu(
69        manager()->profile_info_cache(),
70        mock_observer_.get(),
71        NULL));
72    avatar_menu_->RebuildMenu();
73    EXPECT_EQ(0, change_count());
74    return avatar_menu_.get();
75  }
76
77  TestingProfileManager* manager() { return &manager_; }
78
79  void AddOmittedProfile(std::string name) {
80    ProfileInfoCache* cache = manager()->profile_info_cache();
81    cache->AddProfileToCache(
82        cache->GetUserDataDir().AppendASCII(name), ASCIIToUTF16(name),
83        base::string16(), 0, "TEST_ID");
84  }
85
86  int change_count() const { return mock_observer_->change_count(); }
87
88 private:
89  TestingProfileManager manager_;
90  scoped_ptr<MockObserver> mock_observer_;
91  scoped_ptr<AvatarMenu> avatar_menu_;
92  content::TestBrowserThreadBundle thread_bundle_;
93
94  DISALLOW_COPY_AND_ASSIGN(ProfileListDesktopTest);
95};
96
97TEST_F(ProfileListDesktopTest, InitialCreation) {
98  manager()->CreateTestingProfile("Test 1");
99  manager()->CreateTestingProfile("Test 2");
100
101  AvatarMenu* model = GetAvatarMenu();
102  EXPECT_EQ(0, change_count());
103
104  ASSERT_EQ(2U, model->GetNumberOfItems());
105
106  const AvatarMenu::Item& item1 = model->GetItemAt(0);
107  EXPECT_EQ(0U, item1.menu_index);
108  EXPECT_EQ(ASCIIToUTF16("Test 1"), item1.name);
109
110  const AvatarMenu::Item& item2 = model->GetItemAt(1);
111  EXPECT_EQ(1U, item2.menu_index);
112  EXPECT_EQ(ASCIIToUTF16("Test 2"), item2.name);
113}
114
115TEST_F(ProfileListDesktopTest, NoOmittedProfiles) {
116  ProfileListDesktop profile_list(manager()->profile_info_cache());
117
118  // Profiles are stored and listed alphabetically.
119  manager()->CreateTestingProfile("1 included");
120  manager()->CreateTestingProfile("2 included");
121  manager()->CreateTestingProfile("3 included");
122  manager()->CreateTestingProfile("4 included");
123
124  profile_list.RebuildMenu();
125  ASSERT_EQ(4u, profile_list.GetNumberOfItems());
126
127  const AvatarMenu::Item& item1 = profile_list.GetItemAt(0);
128  EXPECT_EQ(0u, item1.menu_index);
129  EXPECT_EQ(0u, item1.profile_index);
130  EXPECT_EQ(ASCIIToUTF16("1 included"), item1.name);
131
132  const AvatarMenu::Item& item2 = profile_list.GetItemAt(1);
133  EXPECT_EQ(1u, item2.menu_index);
134  EXPECT_EQ(1u, item2.profile_index);
135  EXPECT_EQ(ASCIIToUTF16("2 included"), item2.name);
136
137  const AvatarMenu::Item& item3 = profile_list.GetItemAt(2);
138  EXPECT_EQ(2u, item3.menu_index);
139  EXPECT_EQ(2u, item3.profile_index);
140  EXPECT_EQ(ASCIIToUTF16("3 included"), item3.name);
141
142  const AvatarMenu::Item& item4 = profile_list.GetItemAt(3);
143  EXPECT_EQ(3u, item4.menu_index);
144  EXPECT_EQ(3u, item4.profile_index);
145  EXPECT_EQ(ASCIIToUTF16("4 included"), item4.name);
146
147  EXPECT_EQ(0u, profile_list.MenuIndexFromProfileIndex(0));
148  EXPECT_EQ(1u, profile_list.MenuIndexFromProfileIndex(1));
149  EXPECT_EQ(2u, profile_list.MenuIndexFromProfileIndex(2));
150  EXPECT_EQ(3u, profile_list.MenuIndexFromProfileIndex(3));
151}
152
153TEST_F(ProfileListDesktopTest, WithOmittedProfiles) {
154  ProfileListDesktop profile_list(manager()->profile_info_cache());
155
156  // Profiles are stored and listed alphabetically.
157  AddOmittedProfile("0 omitted");
158  manager()->CreateTestingProfile("1 included");
159  AddOmittedProfile("2 omitted");
160  manager()->CreateTestingProfile("3 included");
161  manager()->CreateTestingProfile("4 included");
162  AddOmittedProfile("5 omitted");
163  manager()->CreateTestingProfile("6 included");
164  AddOmittedProfile("7 omitted");
165
166  profile_list.RebuildMenu();
167  ASSERT_EQ(4u, profile_list.GetNumberOfItems());
168
169  const AvatarMenu::Item& item1 = profile_list.GetItemAt(0);
170  EXPECT_EQ(0u, item1.menu_index);
171  EXPECT_EQ(1u, item1.profile_index);
172  EXPECT_EQ(ASCIIToUTF16("1 included"), item1.name);
173
174  const AvatarMenu::Item& item2 = profile_list.GetItemAt(1);
175  EXPECT_EQ(1u, item2.menu_index);
176  EXPECT_EQ(3u, item2.profile_index);
177  EXPECT_EQ(ASCIIToUTF16("3 included"), item2.name);
178
179  const AvatarMenu::Item& item3 = profile_list.GetItemAt(2);
180  EXPECT_EQ(2u, item3.menu_index);
181  EXPECT_EQ(4u, item3.profile_index);
182  EXPECT_EQ(ASCIIToUTF16("4 included"), item3.name);
183
184  const AvatarMenu::Item& item4 = profile_list.GetItemAt(3);
185  EXPECT_EQ(3u, item4.menu_index);
186  EXPECT_EQ(6u, item4.profile_index);
187  EXPECT_EQ(ASCIIToUTF16("6 included"), item4.name);
188
189  EXPECT_EQ(0u, profile_list.MenuIndexFromProfileIndex(1));
190  EXPECT_EQ(1u, profile_list.MenuIndexFromProfileIndex(3));
191  EXPECT_EQ(2u, profile_list.MenuIndexFromProfileIndex(4));
192  EXPECT_EQ(3u, profile_list.MenuIndexFromProfileIndex(6));
193}
194
195TEST_F(ProfileListDesktopTest, ActiveItem) {
196  manager()->CreateTestingProfile("Test 1");
197  manager()->CreateTestingProfile("Test 2");
198
199  AvatarMenu* model = GetAvatarMenu();
200  ASSERT_EQ(2U, model->GetNumberOfItems());
201  // TODO(jeremy): Expand test to verify active profile index other than 0
202  // crbug.com/100871
203  ASSERT_EQ(0U, model->GetActiveProfileIndex());
204}
205
206TEST_F(ProfileListDesktopTest, ModifyingNameResortsCorrectly) {
207  std::string name1("Alpha");
208  std::string name2("Beta");
209  std::string newname1("Gamma");
210
211  manager()->CreateTestingProfile(name1);
212  manager()->CreateTestingProfile(name2);
213
214  AvatarMenu* model = GetAvatarMenu();
215  EXPECT_EQ(0, change_count());
216
217  ASSERT_EQ(2U, model->GetNumberOfItems());
218
219  const AvatarMenu::Item& item1 = model->GetItemAt(0);
220  EXPECT_EQ(0U, item1.menu_index);
221  EXPECT_EQ(ASCIIToUTF16(name1), item1.name);
222
223  const AvatarMenu::Item& item2 = model->GetItemAt(1);
224  EXPECT_EQ(1U, item2.menu_index);
225  EXPECT_EQ(ASCIIToUTF16(name2), item2.name);
226
227  // Change name of the first profile, to trigger resorting of the profiles:
228  // now the first model should be named "beta", and the second be "gamma".
229  manager()->profile_info_cache()->SetNameOfProfileAtIndex(0,
230        ASCIIToUTF16(newname1));
231  const AvatarMenu::Item& item1next = model->GetItemAt(0);
232  EXPECT_GT(change_count(), 1);
233  EXPECT_EQ(0U, item1next.menu_index);
234  EXPECT_EQ(ASCIIToUTF16(name2), item1next.name);
235
236  const AvatarMenu::Item& item2next = model->GetItemAt(1);
237  EXPECT_EQ(1U, item2next.menu_index);
238  EXPECT_EQ(ASCIIToUTF16(newname1), item2next.name);
239}
240
241TEST_F(ProfileListDesktopTest, ChangeOnNotify) {
242  manager()->CreateTestingProfile("Test 1");
243  manager()->CreateTestingProfile("Test 2");
244
245  AvatarMenu* model = GetAvatarMenu();
246  EXPECT_EQ(0, change_count());
247  EXPECT_EQ(2U, model->GetNumberOfItems());
248
249  manager()->CreateTestingProfile("Test 3");
250
251  // Four changes happened via the call to CreateTestingProfile: adding the
252  // profile to the cache, setting the user name, rebuilding the list of
253  // profiles after the name change, and changing the avatar.
254  // On Windows, an extra change happens to set the shortcut name for the
255  // profile.
256  // TODO(michaelpg): Determine why five changes happen on ChromeOS and
257  // investigate other platforms.
258  EXPECT_GE(change_count(), 4);
259  ASSERT_EQ(3U, model->GetNumberOfItems());
260
261  const AvatarMenu::Item& item1 = model->GetItemAt(0);
262  EXPECT_EQ(0U, item1.menu_index);
263  EXPECT_EQ(ASCIIToUTF16("Test 1"), item1.name);
264
265  const AvatarMenu::Item& item2 = model->GetItemAt(1);
266  EXPECT_EQ(1U, item2.menu_index);
267  EXPECT_EQ(ASCIIToUTF16("Test 2"), item2.name);
268
269  const AvatarMenu::Item& item3 = model->GetItemAt(2);
270  EXPECT_EQ(2U, item3.menu_index);
271  EXPECT_EQ(ASCIIToUTF16("Test 3"), item3.name);
272}
273
274TEST_F(ProfileListDesktopTest, ShowAvatarMenuInTrial) {
275  // If multiprofile mode is not enabled, the trial will not be enabled, so it
276  // isn't tested.
277  if (!profiles::IsMultipleProfilesEnabled())
278    return;
279
280  base::FieldTrialList field_trial_list_(NULL);
281  base::FieldTrialList::CreateFieldTrial("ShowProfileSwitcher", "AlwaysShow");
282
283#if defined(OS_CHROMEOS)
284  EXPECT_FALSE(AvatarMenu::ShouldShowAvatarMenu());
285#else
286  EXPECT_TRUE(AvatarMenu::ShouldShowAvatarMenu());
287#endif
288}
289
290TEST_F(ProfileListDesktopTest, DontShowOldAvatarMenuForSingleProfile) {
291  switches::DisableNewAvatarMenuForTesting(CommandLine::ForCurrentProcess());
292
293  manager()->CreateTestingProfile("Test 1");
294
295  EXPECT_FALSE(AvatarMenu::ShouldShowAvatarMenu());
296
297  // If multiprofile mode is enabled, there are no other cases when we wouldn't
298  // show the menu.
299  if (profiles::IsMultipleProfilesEnabled())
300    return;
301
302  manager()->CreateTestingProfile("Test 2");
303
304  EXPECT_FALSE(AvatarMenu::ShouldShowAvatarMenu());
305}
306
307TEST_F(ProfileListDesktopTest, AlwaysShowNewAvatarMenu) {
308  // If multiprofile mode is not enabled then the menu is never shown.
309  if (!profiles::IsMultipleProfilesEnabled())
310    return;
311
312  switches::EnableNewAvatarMenuForTesting(CommandLine::ForCurrentProcess());
313
314  manager()->CreateTestingProfile("Test 1");
315
316  EXPECT_TRUE(AvatarMenu::ShouldShowAvatarMenu());
317}
318
319TEST_F(ProfileListDesktopTest, ShowAvatarMenu) {
320  // If multiprofile mode is not enabled then the menu is never shown.
321  if (!profiles::IsMultipleProfilesEnabled())
322    return;
323
324  manager()->CreateTestingProfile("Test 1");
325  manager()->CreateTestingProfile("Test 2");
326
327#if defined(OS_CHROMEOS)
328  EXPECT_FALSE(AvatarMenu::ShouldShowAvatarMenu());
329#else
330  EXPECT_TRUE(AvatarMenu::ShouldShowAvatarMenu());
331#endif
332}
333
334TEST_F(ProfileListDesktopTest, SyncState) {
335  // If multiprofile mode is not enabled then the menu is never shown.
336  if (!profiles::IsMultipleProfilesEnabled())
337    return;
338
339  manager()->CreateTestingProfile("Test 1");
340
341  // Add a managed user profile.
342  ProfileInfoCache* cache = manager()->profile_info_cache();
343  base::FilePath path = cache->GetUserDataDir().AppendASCII("p2");
344  cache->AddProfileToCache(path, ASCIIToUTF16("Test 2"), base::string16(), 0,
345                           "TEST_ID");
346  cache->SetIsOmittedProfileAtIndex(cache->GetIndexOfProfileWithPath(path),
347                                    false);
348
349  AvatarMenu* model = GetAvatarMenu();
350  model->RebuildMenu();
351  EXPECT_EQ(2U, model->GetNumberOfItems());
352
353  // Now check that the sync_state of a supervised user shows the supervised
354  // user avatar label instead.
355  base::string16 supervised_user_label =
356      l10n_util::GetStringUTF16(IDS_SUPERVISED_USER_AVATAR_LABEL);
357  const AvatarMenu::Item& item1 = model->GetItemAt(0);
358  EXPECT_NE(item1.sync_state, supervised_user_label);
359
360  const AvatarMenu::Item& item2 = model->GetItemAt(1);
361  EXPECT_EQ(item2.sync_state, supervised_user_label);
362}
363
364}  // namespace
365