1 // Copyright 2013 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 <vector> 6 7 #include "ash/ash_switches.h" 8 #include "ash/root_window_controller.h" 9 #include "ash/shelf/shelf_layout_manager.h" 10 #include "ash/shell.h" 11 #include "ash/shell_delegate.h" 12 #include "ash/system/tray/system_tray.h" 13 #include "ash/system/user/tray_user.h" 14 #include "ash/system/user/tray_user_separator.h" 15 #include "ash/test/ash_test_base.h" 16 #include "ash/test/test_session_state_delegate.h" 17 #include "ash/test/test_shell_delegate.h" 18 #include "base/command_line.h" 19 #include "ui/aura/test/event_generator.h" 20 #include "ui/gfx/animation/animation_container_element.h" 21 #include "ui/views/view.h" 22 #include "ui/views/widget/widget.h" 23 24 #if defined(OS_CHROMEOS) 25 #include "ash/system/tray/system_tray_notifier.h" 26 #endif 27 28 namespace ash { 29 namespace internal { 30 31 class TrayUserTest : public ash::test::AshTestBase { 32 public: 33 TrayUserTest(); 34 35 // testing::Test: 36 virtual void SetUp() OVERRIDE; 37 38 // This has to be called prior to first use with the proper configuration. 39 void InitializeParameters(int users_logged_in, bool multiprofile); 40 41 // Show the system tray menu using the provided event generator. 42 void ShowTrayMenu(aura::test::EventGenerator* generator); 43 44 // Move the mouse over the user item. 45 void MoveOverUserItem(aura::test::EventGenerator* generator, int index); 46 47 // Click on the user item. Note that the tray menu needs to be shown. 48 void ClickUserItem(aura::test::EventGenerator* generator, int index); 49 50 // Accessors to various system components. 51 ShelfLayoutManager* shelf() { return shelf_; } 52 SystemTray* tray() { return tray_; } 53 ash::test::TestSessionStateDelegate* delegate() { return delegate_; } 54 ash::internal::TrayUser* tray_user(int index) { return tray_user_[index]; } 55 ash::internal::TrayUserSeparator* tray_user_separator() { 56 return tray_user_separator_; 57 } 58 59 private: 60 ShelfLayoutManager* shelf_; 61 SystemTray* tray_; 62 ash::test::TestSessionStateDelegate* delegate_; 63 64 // Note that the ownership of these items is on the shelf. 65 std::vector<ash::internal::TrayUser*> tray_user_; 66 67 // The separator between the tray users and the rest of the menu. 68 // Note: The item will get owned by the shelf. 69 TrayUserSeparator* tray_user_separator_; 70 71 DISALLOW_COPY_AND_ASSIGN(TrayUserTest); 72 }; 73 74 #if defined(OS_CHROMEOS) 75 // The tray user test which tests functionality where multiple tray items are 76 // visible in the system tray. 77 class MultiTrayUserTest : public TrayUserTest { 78 public: 79 MultiTrayUserTest() {} 80 81 virtual void SetUp() OVERRIDE { 82 CommandLine* command_line = CommandLine::ForCurrentProcess(); 83 command_line->AppendSwitch(ash::switches::kAshEnableMultiUserTray); 84 TrayUserTest::SetUp(); 85 } 86 87 private: 88 DISALLOW_COPY_AND_ASSIGN(MultiTrayUserTest); 89 }; 90 #endif 91 92 TrayUserTest::TrayUserTest() 93 : shelf_(NULL), 94 tray_(NULL), 95 delegate_(NULL), 96 tray_user_separator_(NULL) { 97 } 98 99 void TrayUserTest::SetUp() { 100 ash::test::AshTestBase::SetUp(); 101 shelf_ = Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager(); 102 tray_ = Shell::GetPrimaryRootWindowController()->GetSystemTray(); 103 delegate_ = static_cast<ash::test::TestSessionStateDelegate*>( 104 ash::Shell::GetInstance()->session_state_delegate()); 105 } 106 107 void TrayUserTest::InitializeParameters(int users_logged_in, 108 bool multiprofile) { 109 // Show the shelf. 110 shelf()->LayoutShelf(); 111 shelf()->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); 112 113 // Set our default assumptions. Note that it is sufficient to set these 114 // after everything was created. 115 delegate_->set_logged_in_users(users_logged_in); 116 ash::test::TestShellDelegate* shell_delegate = 117 static_cast<ash::test::TestShellDelegate*>( 118 ash::Shell::GetInstance()->delegate()); 119 shell_delegate->set_multi_profiles_enabled(multiprofile); 120 121 // Instead of using the existing tray panels we create new ones which makes 122 // the access easier. 123 for (int i = 0; i < delegate_->GetMaximumNumberOfLoggedInUsers(); i++) { 124 tray_user_.push_back(new ash::internal::TrayUser(tray_, i)); 125 tray_->AddTrayItem(tray_user_[i]); 126 } 127 // We then add also the separator. 128 tray_user_separator_ = new ash::internal::TrayUserSeparator(tray_); 129 tray_->AddTrayItem(tray_user_separator_); 130 } 131 132 void TrayUserTest::ShowTrayMenu(aura::test::EventGenerator* generator) { 133 gfx::Point center = tray()->GetBoundsInScreen().CenterPoint(); 134 135 generator->MoveMouseTo(center.x(), center.y()); 136 EXPECT_FALSE(tray()->IsAnyBubbleVisible()); 137 generator->ClickLeftButton(); 138 } 139 140 void TrayUserTest::MoveOverUserItem(aura::test::EventGenerator* generator, 141 int index) { 142 gfx::Point center = 143 tray_user(index)->GetUserPanelBoundsInScreenForTest().CenterPoint(); 144 145 generator->MoveMouseTo(center.x(), center.y()); 146 } 147 148 void TrayUserTest::ClickUserItem(aura::test::EventGenerator* generator, 149 int index) { 150 MoveOverUserItem(generator, index); 151 generator->ClickLeftButton(); 152 } 153 154 // Make sure that in single user mode the user panel cannot be activated and no 155 // separators are being created. 156 TEST_F(TrayUserTest, SingleUserModeDoesNotAllowAddingUser) { 157 InitializeParameters(1, false); 158 159 // Move the mouse over the status area and click to open the status menu. 160 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 161 162 EXPECT_FALSE(tray()->IsAnyBubbleVisible()); 163 164 for (int i = 0; i < delegate()->GetMaximumNumberOfLoggedInUsers(); i++) 165 EXPECT_EQ(ash::internal::TrayUser::HIDDEN, 166 tray_user(i)->GetStateForTest()); 167 EXPECT_FALSE(tray_user_separator()->separator_shown()); 168 169 ShowTrayMenu(&generator); 170 171 EXPECT_TRUE(tray()->HasSystemBubble()); 172 EXPECT_TRUE(tray()->IsAnyBubbleVisible()); 173 174 for (int i = 0; i < delegate()->GetMaximumNumberOfLoggedInUsers(); i++) 175 EXPECT_EQ(i == 0 ? ash::internal::TrayUser::SHOWN : 176 ash::internal::TrayUser::HIDDEN, 177 tray_user(i)->GetStateForTest()); 178 EXPECT_FALSE(tray_user_separator()->separator_shown()); 179 tray()->CloseSystemBubble(); 180 } 181 182 #if defined(OS_CHROMEOS) 183 // Make sure that in multi user mode the user panel can be activated and there 184 // will be one panel for each user plus one additional separator at the end. 185 // Note: the mouse watcher (for automatic closing upon leave) cannot be tested 186 // here since it does not work with the event system in unit tests. 187 TEST_F(TrayUserTest, MutiUserModeDoesNotAllowToAddUser) { 188 InitializeParameters(1, true); 189 190 // Move the mouse over the status area and click to open the status menu. 191 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 192 generator.set_async(false); 193 194 int max_users = delegate()->GetMaximumNumberOfLoggedInUsers(); 195 // Checking now for each amount of users that the correct is done. 196 for (int j = 1; j < max_users; j++) { 197 // Set the number of logged in users. 198 delegate()->set_logged_in_users(j); 199 200 // Verify that nothing is shown. 201 EXPECT_FALSE(tray()->IsAnyBubbleVisible()); 202 for (int i = 0; i < max_users; i++) 203 EXPECT_FALSE(tray_user(i)->GetStateForTest()); 204 EXPECT_FALSE(tray_user_separator()->separator_shown()); 205 // After clicking on the tray the menu should get shown and for each logged 206 // in user we should get a visible item. In addition, the separator should 207 // show up when we reach more then one user. 208 ShowTrayMenu(&generator); 209 210 EXPECT_TRUE(tray()->HasSystemBubble()); 211 EXPECT_TRUE(tray()->IsAnyBubbleVisible()); 212 for (int i = 0; i < max_users; i++) { 213 EXPECT_EQ(i < j ? ash::internal::TrayUser::SHOWN : 214 ash::internal::TrayUser::HIDDEN, 215 tray_user(i)->GetStateForTest()); 216 } 217 218 // Check the visibility of the separator. 219 EXPECT_EQ(j > 1 ? true : false, tray_user_separator()->separator_shown()); 220 221 // Move the mouse over the user item and it should hover. 222 MoveOverUserItem(&generator, 0); 223 EXPECT_EQ(ash::internal::TrayUser::HOVERED, 224 tray_user(0)->GetStateForTest()); 225 for (int i = 1; i < max_users; i++) { 226 EXPECT_EQ(i < j ? ash::internal::TrayUser::SHOWN : 227 ash::internal::TrayUser::HIDDEN, 228 tray_user(i)->GetStateForTest()); 229 } 230 231 // Check that clicking the button allows to add item if we have still room 232 // for one more user. 233 ClickUserItem(&generator, 0); 234 EXPECT_EQ(j == max_users ? 235 ash::internal::TrayUser::ACTIVE_BUT_DISABLED : 236 ash::internal::TrayUser::ACTIVE, 237 tray_user(0)->GetStateForTest()); 238 239 // Click the button again to see that the menu goes away. 240 ClickUserItem(&generator, 0); 241 EXPECT_EQ(ash::internal::TrayUser::HOVERED, 242 tray_user(0)->GetStateForTest()); 243 244 // Close and check that everything is deleted. 245 tray()->CloseSystemBubble(); 246 EXPECT_FALSE(tray()->IsAnyBubbleVisible()); 247 for (int i = 0; i < delegate()->GetMaximumNumberOfLoggedInUsers(); i++) 248 EXPECT_EQ(ash::internal::TrayUser::HIDDEN, 249 tray_user(i)->GetStateForTest()); 250 } 251 } 252 253 // Make sure that user changing gets properly executed. 254 TEST_F(TrayUserTest, MutiUserModeButtonClicks) { 255 // Have two users. 256 InitializeParameters(2, true); 257 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 258 ShowTrayMenu(&generator); 259 260 // Switch to a new user - which has a capitalized name. 261 ClickUserItem(&generator, 1); 262 EXPECT_EQ(delegate()->get_activated_user(), delegate()->GetUserID(1)); 263 // Since the name is capitalized, the email should be different then the 264 // user_id. 265 EXPECT_NE(delegate()->get_activated_user(), delegate()->GetUserEmail(1)); 266 tray()->CloseSystemBubble(); 267 } 268 269 // Make sure that we show items for all users in the tray accordingly. 270 TEST_F(MultiTrayUserTest, CheckTrayUserItems) { 271 InitializeParameters(1, true); 272 273 int max_users = delegate()->GetMaximumNumberOfLoggedInUsers(); 274 // Checking now for each amount of users that the proper items are visible in 275 // the tray. The proper item is hereby: 276 // 2 -> User #1 277 // 1 -> User #2 278 // 0 -> User #3 279 // Note: Tray items are required to populate system tray items as well as the 280 // system tray menu. The system tray menu changes it's appearance with the 281 // addition of more users, but the system tray does not create new items after 282 // it got created. 283 for (int present_users = 1; present_users <= max_users; ++present_users) { 284 // We simulate the user addition by telling the delegate the new number of 285 // users, then change all user tray items and finally tell the tray to 286 // re-layout itself. 287 delegate()->set_logged_in_users(present_users); 288 Shell::GetInstance()->system_tray_notifier()->NotifyUserAddedToSession(); 289 tray()->Layout(); 290 291 // Check that the tray items are being shown in the reverse order. 292 for (int i = 0; i < max_users; i++) { 293 gfx::Rect rect = 294 tray()->GetTrayItemViewForTest(tray_user(i))->GetBoundsInScreen(); 295 if (max_users - 1 - i < present_users) 296 EXPECT_FALSE(rect.IsEmpty()); 297 else 298 EXPECT_TRUE(rect.IsEmpty()); 299 } 300 } 301 302 // Click on the last item to see that the user changes. 303 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 304 generator.set_async(false); 305 306 // Switch to a new user - again, note that we have to click on the reverse 307 // item in the list. Since the first clickable item is 1, we get user #2. 308 gfx::Point point = 309 tray()->GetTrayItemViewForTest(tray_user(1))-> 310 GetBoundsInScreen().CenterPoint(); 311 312 generator.MoveMouseTo(point.x(), point.y()); 313 generator.ClickLeftButton(); 314 EXPECT_EQ(delegate()->get_activated_user(), delegate()->GetUserID(1)); 315 } 316 317 #endif 318 319 } // namespace internal 320 } // namespace ash 321