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/root_window_controller.h"
8#include "ash/shelf/shelf_layout_manager.h"
9#include "ash/shell.h"
10#include "ash/shell_delegate.h"
11#include "ash/system/tray/system_tray.h"
12#include "ash/system/tray/tray_constants.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 "components/user_manager/user_info.h"
19#include "ui/events/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
24namespace ash {
25
26class TrayUserTest : public ash::test::AshTestBase {
27 public:
28  TrayUserTest();
29
30  // testing::Test:
31  virtual void SetUp() OVERRIDE;
32
33  // This has to be called prior to first use with the proper configuration.
34  void InitializeParameters(int users_logged_in, bool multiprofile);
35
36  // Show the system tray menu using the provided event generator.
37  void ShowTrayMenu(ui::test::EventGenerator* generator);
38
39  // Move the mouse over the user item.
40  void MoveOverUserItem(ui::test::EventGenerator* generator, int index);
41
42  // Click on the user item. Note that the tray menu needs to be shown.
43  void ClickUserItem(ui::test::EventGenerator* generator, int index);
44
45  // Accessors to various system components.
46  ShelfLayoutManager* shelf() { return shelf_; }
47  SystemTray* tray() { return tray_; }
48  ash::test::TestSessionStateDelegate* delegate() { return delegate_; }
49  ash::TrayUser* tray_user(int index) { return tray_user_[index]; }
50  ash::TrayUserSeparator* tray_user_separator() { return tray_user_separator_; }
51
52 private:
53  ShelfLayoutManager* shelf_;
54  SystemTray* tray_;
55  ash::test::TestSessionStateDelegate* delegate_;
56
57  // Note that the ownership of these items is on the shelf.
58  std::vector<ash::TrayUser*> tray_user_;
59
60  // The separator between the tray users and the rest of the menu.
61  // Note: The item will get owned by the shelf.
62  TrayUserSeparator* tray_user_separator_;
63
64  DISALLOW_COPY_AND_ASSIGN(TrayUserTest);
65};
66
67TrayUserTest::TrayUserTest()
68    : shelf_(NULL),
69      tray_(NULL),
70      delegate_(NULL),
71      tray_user_separator_(NULL) {
72}
73
74void TrayUserTest::SetUp() {
75  ash::test::AshTestBase::SetUp();
76  shelf_ = Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
77  tray_ = Shell::GetPrimaryRootWindowController()->GetSystemTray();
78  delegate_ = static_cast<ash::test::TestSessionStateDelegate*>(
79      ash::Shell::GetInstance()->session_state_delegate());
80}
81
82void TrayUserTest::InitializeParameters(int users_logged_in,
83                                        bool multiprofile) {
84  // Show the shelf.
85  shelf()->LayoutShelf();
86  shelf()->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
87
88  // Set our default assumptions. Note that it is sufficient to set these
89  // after everything was created.
90  delegate_->set_logged_in_users(users_logged_in);
91  ash::test::TestShellDelegate* shell_delegate =
92      static_cast<ash::test::TestShellDelegate*>(
93          ash::Shell::GetInstance()->delegate());
94  shell_delegate->set_multi_profiles_enabled(multiprofile);
95
96  // Instead of using the existing tray panels we create new ones which makes
97  // the access easier.
98  for (int i = 0; i < delegate_->GetMaximumNumberOfLoggedInUsers(); i++) {
99    tray_user_.push_back(new ash::TrayUser(tray_, i));
100    tray_->AddTrayItem(tray_user_[i]);
101  }
102  // We then add also the separator.
103  tray_user_separator_ = new ash::TrayUserSeparator(tray_);
104  tray_->AddTrayItem(tray_user_separator_);
105}
106
107void TrayUserTest::ShowTrayMenu(ui::test::EventGenerator* generator) {
108  gfx::Point center = tray()->GetBoundsInScreen().CenterPoint();
109
110  generator->MoveMouseTo(center.x(), center.y());
111  EXPECT_FALSE(tray()->IsAnyBubbleVisible());
112  generator->ClickLeftButton();
113}
114
115void TrayUserTest::MoveOverUserItem(ui::test::EventGenerator* generator,
116                                    int index) {
117  gfx::Point center =
118      tray_user(index)->GetUserPanelBoundsInScreenForTest().CenterPoint();
119
120  generator->MoveMouseTo(center.x(), center.y());
121}
122
123void TrayUserTest::ClickUserItem(ui::test::EventGenerator* generator,
124                                 int index) {
125  MoveOverUserItem(generator, index);
126  generator->ClickLeftButton();
127}
128
129// Make sure that we show items for all users in the tray accordingly.
130TEST_F(TrayUserTest, CheckTrayItemSize) {
131  InitializeParameters(1, false);
132  tray_user(0)->UpdateAfterLoginStatusChangeForTest(user::LOGGED_IN_GUEST);
133  gfx::Size size = tray_user(0)->GetLayoutSizeForTest();
134  EXPECT_EQ(kTrayItemSize, size.height());
135  tray_user(0)->UpdateAfterLoginStatusChangeForTest(user::LOGGED_IN_USER);
136  size = tray_user(0)->GetLayoutSizeForTest();
137  EXPECT_EQ(kTrayItemSize, size.height());
138}
139
140// Make sure that in single user mode the user panel cannot be activated and no
141// separators are being created.
142TEST_F(TrayUserTest, SingleUserModeDoesNotAllowAddingUser) {
143  InitializeParameters(1, false);
144
145  // Move the mouse over the status area and click to open the status menu.
146  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
147
148  EXPECT_FALSE(tray()->IsAnyBubbleVisible());
149
150  for (int i = 0; i < delegate()->GetMaximumNumberOfLoggedInUsers(); i++)
151    EXPECT_EQ(ash::TrayUser::HIDDEN, tray_user(i)->GetStateForTest());
152  EXPECT_FALSE(tray_user_separator()->separator_shown());
153
154  ShowTrayMenu(&generator);
155
156  EXPECT_TRUE(tray()->HasSystemBubble());
157  EXPECT_TRUE(tray()->IsAnyBubbleVisible());
158
159  for (int i = 0; i < delegate()->GetMaximumNumberOfLoggedInUsers(); i++)
160    EXPECT_EQ(i == 0 ? ash::TrayUser::SHOWN : ash::TrayUser::HIDDEN,
161              tray_user(i)->GetStateForTest());
162  EXPECT_FALSE(tray_user_separator()->separator_shown());
163  tray()->CloseSystemBubble();
164}
165
166#if defined(OS_CHROMEOS)
167// Make sure that in multi user mode the user panel can be activated and there
168// will be one panel for each user plus one additional separator at the end.
169// Note: the mouse watcher (for automatic closing upon leave) cannot be tested
170// here since it does not work with the event system in unit tests.
171TEST_F(TrayUserTest, MutiUserModeDoesNotAllowToAddUser) {
172  InitializeParameters(1, true);
173
174  // Move the mouse over the status area and click to open the status menu.
175  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
176  generator.set_async(false);
177
178  int max_users = delegate()->GetMaximumNumberOfLoggedInUsers();
179  // Checking now for each amount of users that the correct is done.
180  for (int j = 1; j < max_users; j++) {
181    // Set the number of logged in users.
182    delegate()->set_logged_in_users(j);
183
184    // Verify that nothing is shown.
185    EXPECT_FALSE(tray()->IsAnyBubbleVisible());
186    for (int i = 0; i < max_users; i++)
187      EXPECT_FALSE(tray_user(i)->GetStateForTest());
188    EXPECT_FALSE(tray_user_separator()->separator_shown());
189    // After clicking on the tray the menu should get shown and for each logged
190    // in user we should get a visible item. In addition, the separator should
191    // show up when we reach more then one user.
192    ShowTrayMenu(&generator);
193
194    EXPECT_TRUE(tray()->HasSystemBubble());
195    EXPECT_TRUE(tray()->IsAnyBubbleVisible());
196    for (int i = 0; i < max_users; i++) {
197      EXPECT_EQ(i < j ? ash::TrayUser::SHOWN : ash::TrayUser::HIDDEN,
198                tray_user(i)->GetStateForTest());
199    }
200
201    // Check the visibility of the separator.
202    EXPECT_EQ(j > 1 ? true : false, tray_user_separator()->separator_shown());
203
204    // Move the mouse over the user item and it should hover.
205    MoveOverUserItem(&generator, 0);
206    EXPECT_EQ(ash::TrayUser::HOVERED, tray_user(0)->GetStateForTest());
207    for (int i = 1; i < max_users; i++) {
208      EXPECT_EQ(i < j ? ash::TrayUser::SHOWN : ash::TrayUser::HIDDEN,
209                tray_user(i)->GetStateForTest());
210    }
211
212    // Check that clicking the button allows to add item if we have still room
213    // for one more user.
214    ClickUserItem(&generator, 0);
215    EXPECT_EQ(j == max_users ? ash::TrayUser::ACTIVE_BUT_DISABLED
216                             : ash::TrayUser::ACTIVE,
217              tray_user(0)->GetStateForTest());
218
219    // Click the button again to see that the menu goes away.
220    ClickUserItem(&generator, 0);
221    EXPECT_EQ(ash::TrayUser::HOVERED, tray_user(0)->GetStateForTest());
222
223    // Close and check that everything is deleted.
224    tray()->CloseSystemBubble();
225    EXPECT_FALSE(tray()->IsAnyBubbleVisible());
226    for (int i = 0; i < delegate()->GetMaximumNumberOfLoggedInUsers(); i++)
227      EXPECT_EQ(ash::TrayUser::HIDDEN, tray_user(i)->GetStateForTest());
228  }
229}
230
231// Make sure that user changing gets properly executed.
232TEST_F(TrayUserTest, MutiUserModeButtonClicks) {
233  // Have two users.
234  InitializeParameters(2, true);
235  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
236  ShowTrayMenu(&generator);
237
238  // Switch to a new user - which has a capitalized name.
239  ClickUserItem(&generator, 1);
240  const user_manager::UserInfo* active_user = delegate()->GetActiveUserInfo();
241  const user_manager::UserInfo* second_user = delegate()->GetUserInfo(1);
242  EXPECT_EQ(active_user->GetUserID(), second_user->GetUserID());
243  // Since the name is capitalized, the email should be different then the
244  // user_id.
245  EXPECT_NE(active_user->GetUserID(), second_user->GetEmail());
246  tray()->CloseSystemBubble();
247}
248
249#endif
250
251}  // namespace ash
252