1 // Copyright 2014 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.
5 #import "chrome/browser/ui/cocoa/profiles/profile_menu_controller.h"
7 #include "base/mac/scoped_nsobject.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/threading/thread_restrictions.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/profiles/profile_manager.h"
12 #include "chrome/browser/ui/browser_list.h"
13 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
14 #include "chrome/browser/ui/cocoa/run_loop_testing.h"
15 #include "chrome/common/pref_names.h"
16 #include "chrome/grit/generated_resources.h"
17 #include "chrome/test/base/test_browser_window.h"
18 #include "chrome/test/base/testing_profile.h"
19 #include "components/syncable_prefs/pref_service_syncable.h"
20 #include "testing/gtest_mac.h"
21 #include "ui/base/l10n/l10n_util_mac.h"
23 class ProfileMenuControllerTest : public CocoaProfileTest {
25 ProfileMenuControllerTest() {
26 item_.reset([[NSMenuItem alloc] initWithTitle:@"Users"
30 [[ProfileMenuController alloc] initWithMainMenuItem:item_]);
33 void SetUp() override {
34 CocoaProfileTest::SetUp();
35 ASSERT_TRUE(profile());
37 // Spin the runloop so |-initializeMenu| gets called.
38 chrome::testing::NSRunLoopRunAllPending();
41 void TestBottomItems() {
42 NSMenu* menu = [controller() menu];
43 NSInteger count = [menu numberOfItems];
47 NSMenuItem* item = [menu itemAtIndex:count - 4];
48 EXPECT_TRUE([item isSeparatorItem]);
50 item = [menu itemAtIndex:count - 3];
51 EXPECT_EQ(@selector(editProfile:), [item action]);
53 item = [menu itemAtIndex:count - 2];
54 EXPECT_TRUE([item isSeparatorItem]);
56 item = [menu itemAtIndex:count - 1];
57 EXPECT_EQ(@selector(newProfile:), [item action]);
60 void VerifyProfileNamedIsActive(NSString* title, int line) {
61 for (NSMenuItem* item in [[controller() menu] itemArray]) {
62 if ([[item title] isEqualToString:title]) {
63 EXPECT_EQ(NSOnState, [item state]) << [[item title] UTF8String]
64 << " (from line " << line << ")";
66 EXPECT_EQ(NSOffState, [item state]) << [[item title] UTF8String]
67 << " (from line " << line << ")";
72 ProfileMenuController* controller() { return controller_.get(); }
74 NSMenuItem* menu_item() { return item_.get(); }
77 base::scoped_nsobject<NSMenuItem> item_;
78 base::scoped_nsobject<ProfileMenuController> controller_;
81 TEST_F(ProfileMenuControllerTest, InitializeMenu) {
82 NSMenu* menu = [controller() menu];
83 // Profile, <sep>, Edit, <sep>, New.
84 ASSERT_EQ(5, [menu numberOfItems]);
88 EXPECT_FALSE([menu_item() isHidden]);
91 TEST_F(ProfileMenuControllerTest, CreateItemWithTitle) {
93 [controller() createItemWithTitle:@"Title"
94 action:@selector(someSelector:)];
95 EXPECT_NSEQ(@"Title", [item title]);
96 EXPECT_EQ(controller(), [item target]);
97 EXPECT_EQ(@selector(someSelector:), [item action]);
98 EXPECT_NSEQ(@"", [item keyEquivalent]);
101 TEST_F(ProfileMenuControllerTest, RebuildMenu) {
102 NSMenu* menu = [controller() menu];
103 EXPECT_EQ(5, [menu numberOfItems]);
105 EXPECT_FALSE([menu_item() isHidden]);
107 // Create some more profiles on the manager.
108 TestingProfileManager* manager = testing_profile_manager();
109 manager->CreateTestingProfile("Profile 2");
110 manager->CreateTestingProfile("Profile 3");
112 // Verify that the menu got rebuilt.
113 ASSERT_EQ(7, [menu numberOfItems]);
115 NSMenuItem* item = [menu itemAtIndex:0];
116 EXPECT_EQ(@selector(switchToProfileFromMenu:), [item action]);
118 item = [menu itemAtIndex:1];
119 EXPECT_EQ(@selector(switchToProfileFromMenu:), [item action]);
121 item = [menu itemAtIndex:2];
122 EXPECT_EQ(@selector(switchToProfileFromMenu:), [item action]);
126 EXPECT_FALSE([menu_item() isHidden]);
129 TEST_F(ProfileMenuControllerTest, InsertItems) {
130 base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@""]);
131 ASSERT_EQ(0, [menu numberOfItems]);
133 // Even with one profile items can still be inserted.
134 BOOL result = [controller() insertItemsIntoMenu:menu
138 EXPECT_EQ(1, [menu numberOfItems]);
139 [menu removeAllItems];
141 // Same for use in building the dock menu.
142 result = [controller() insertItemsIntoMenu:menu
145 EXPECT_FALSE(result);
146 EXPECT_EQ(0, [menu numberOfItems]);
147 [menu removeAllItems];
149 // Create one more profile on the manager.
150 TestingProfileManager* manager = testing_profile_manager();
151 manager->CreateTestingProfile("Profile 2");
153 // With more than one profile, insertItems should return YES.
154 result = [controller() insertItemsIntoMenu:menu
158 ASSERT_EQ(2, [menu numberOfItems]);
160 NSMenuItem* item = [menu itemAtIndex:0];
161 EXPECT_EQ(@selector(switchToProfileFromMenu:), [item action]);
163 item = [menu itemAtIndex:1];
164 EXPECT_EQ(@selector(switchToProfileFromMenu:), [item action]);
165 [menu removeAllItems];
167 // And for the dock, the selector should be different and there should be a
169 result = [controller() insertItemsIntoMenu:menu
173 ASSERT_EQ(3, [menu numberOfItems]);
175 // First item is a label item.
176 item = [menu itemAtIndex:0];
177 EXPECT_FALSE([item isEnabled]);
179 item = [menu itemAtIndex:1];
180 EXPECT_EQ(@selector(switchToProfileFromDock:), [item action]);
182 item = [menu itemAtIndex:2];
183 EXPECT_EQ(@selector(switchToProfileFromDock:), [item action]);
186 TEST_F(ProfileMenuControllerTest, InitialActiveBrowser) {
187 [controller() activeBrowserChangedTo:NULL];
188 VerifyProfileNamedIsActive(l10n_util::GetNSString(IDS_DEFAULT_PROFILE_NAME),
192 // Note: BrowserList::SetLastActive() is typically called as part of
193 // BrowserWindow::Show() and when a Browser becomes active. We don't need a full
194 // BrowserWindow, so it is called manually.
195 TEST_F(ProfileMenuControllerTest, SetActiveAndRemove) {
196 NSMenu* menu = [controller() menu];
197 TestingProfileManager* manager = testing_profile_manager();
198 TestingProfile* profile2 = manager->CreateTestingProfile("Profile 2");
199 TestingProfile* profile3 = manager->CreateTestingProfile("Profile 3");
200 ASSERT_EQ(7, [menu numberOfItems]);
202 // Create a browser and "show" it.
203 Browser::CreateParams profile2_params(profile2, chrome::GetActiveDesktop());
204 scoped_ptr<Browser> p2_browser(
205 chrome::CreateBrowserWithTestWindowForParams(&profile2_params));
206 BrowserList::SetLastActive(p2_browser.get());
207 VerifyProfileNamedIsActive(@"Profile 2", __LINE__);
209 // Close the browser and make sure it's still active.
211 VerifyProfileNamedIsActive(@"Profile 2", __LINE__);
213 // Open a new browser and make sure it takes effect.
214 Browser::CreateParams profile3_params(profile3, chrome::GetActiveDesktop());
215 scoped_ptr<Browser> p3_browser(
216 chrome::CreateBrowserWithTestWindowForParams(&profile3_params));
217 BrowserList::SetLastActive(p3_browser.get());
218 VerifyProfileNamedIsActive(@"Profile 3", __LINE__);
221 VerifyProfileNamedIsActive(@"Profile 3", __LINE__);
224 TEST_F(ProfileMenuControllerTest, DeleteActiveProfile) {
225 TestingProfileManager* manager = testing_profile_manager();
227 manager->CreateTestingProfile("Profile 2");
228 TestingProfile* profile3 = manager->CreateTestingProfile("Profile 3");
229 ASSERT_EQ(3U, manager->profile_manager()->GetNumberOfProfiles());
231 const base::FilePath profile3_path = profile3->GetPath();
232 manager->DeleteTestingProfile("Profile 3");
234 // Simulate an unloaded profile by setting the "last used" local state pref
235 // the profile that was just deleted.
236 PrefService* local_state = g_browser_process->local_state();
237 local_state->SetString(prefs::kProfileLastUsed,
238 profile3_path.BaseName().MaybeAsASCII());
240 // Simulate the active browser changing to NULL and ensure a profile doesn't
241 // get created by disallowing IO operations temporarily.
242 const bool io_was_allowed = base::ThreadRestrictions::SetIOAllowed(false);
243 [controller() activeBrowserChangedTo:NULL];
244 base::ThreadRestrictions::SetIOAllowed(io_was_allowed);
247 TEST_F(ProfileMenuControllerTest, SupervisedProfile) {
248 TestingProfileManager* manager = testing_profile_manager();
249 TestingProfile* supervised_profile = manager->CreateTestingProfile(
250 "test1", scoped_ptr<syncable_prefs::PrefServiceSyncable>(),
251 base::ASCIIToUTF16("Supervised User"), 0, "TEST_ID",
252 TestingProfile::TestingFactories());
253 // The supervised profile is initially marked as omitted from the avatar menu
254 // (in non-test code, until we have confirmation that it has actually been
255 // created on the server). For the test, just tell the cache to un-hide it.
256 ProfileInfoCache* cache = manager->profile_info_cache();
258 cache->GetIndexOfProfileWithPath(supervised_profile->GetPath());
259 cache->SetIsOmittedProfileAtIndex(index, false);
261 BrowserList::SetLastActive(browser());
263 NSMenu* menu = [controller() menu];
264 // Person 1, Supervised User, <sep>, Edit, <sep>, New.
265 ASSERT_EQ(6, [menu numberOfItems]);
267 NSMenuItem* item = [menu itemAtIndex:0];
268 ASSERT_EQ(@selector(switchToProfileFromMenu:), [item action]);
269 EXPECT_TRUE([controller() validateMenuItem:item]);
271 item = [menu itemAtIndex:1];
272 ASSERT_EQ(@selector(switchToProfileFromMenu:), [item action]);
273 EXPECT_TRUE([controller() validateMenuItem:item]);
275 item = [menu itemAtIndex:5];
276 ASSERT_EQ(@selector(newProfile:), [item action]);
277 EXPECT_TRUE([controller() validateMenuItem:item]);
279 // Open a new browser for the supervised user and switch to it.
280 Browser::CreateParams supervised_profile_params(
281 supervised_profile, chrome::HOST_DESKTOP_TYPE_NATIVE);
282 scoped_ptr<Browser> supervised_browser(
283 chrome::CreateBrowserWithTestWindowForParams(&supervised_profile_params));
284 BrowserList::SetLastActive(supervised_browser.get());
286 item = [menu itemAtIndex:0];
287 ASSERT_EQ(@selector(switchToProfileFromMenu:), [item action]);
288 EXPECT_FALSE([controller() validateMenuItem:item]);
290 item = [menu itemAtIndex:1];
291 ASSERT_EQ(@selector(switchToProfileFromMenu:), [item action]);
292 EXPECT_TRUE([controller() validateMenuItem:item]);
294 item = [menu itemAtIndex:5];
295 ASSERT_EQ(@selector(newProfile:), [item action]);
296 EXPECT_FALSE([controller() validateMenuItem:item]);