1// Copyright (c) 2011 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 "base/command_line.h"
6#include "base/memory/scoped_ptr.h"
7#include "base/message_loop.h"
8#include "chrome/browser/automation/ui_controls.h"
9#include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h"
10#include "chrome/browser/chromeos/cros/mock_input_method_library.h"
11#include "chrome/browser/chromeos/cros/mock_screen_lock_library.h"
12#include "chrome/browser/chromeos/login/mock_authenticator.h"
13#include "chrome/browser/chromeos/login/screen_locker.h"
14#include "chrome/browser/chromeos/login/screen_locker_tester.h"
15#include "chrome/browser/chromeos/login/user_manager.h"
16#include "chrome/browser/profiles/profile_manager.h"
17#include "chrome/browser/ui/browser.h"
18#include "chrome/browser/ui/browser_window.h"
19#include "chrome/browser/ui/views/browser_dialogs.h"
20#include "chrome/common/chrome_switches.h"
21#include "chrome/test/ui_test_utils.h"
22#include "content/common/notification_service.h"
23#include "content/common/notification_type.h"
24#include "testing/gmock/include/gmock/gmock.h"
25#include "testing/gtest/include/gtest/gtest.h"
26#include "views/controls/textfield/textfield.h"
27#include "views/window/window_gtk.h"
28
29namespace {
30
31// An object that wait for lock state and fullscreen state.
32class Waiter : public NotificationObserver {
33 public:
34  explicit Waiter(Browser* browser)
35      : browser_(browser),
36        running_(false) {
37    registrar_.Add(this,
38                   NotificationType::SCREEN_LOCK_STATE_CHANGED,
39                   NotificationService::AllSources());
40    handler_id_ = g_signal_connect(
41        G_OBJECT(browser_->window()->GetNativeHandle()),
42        "window-state-event",
43        G_CALLBACK(OnWindowStateEventThunk),
44        this);
45  }
46
47  ~Waiter() {
48    g_signal_handler_disconnect(
49        G_OBJECT(browser_->window()->GetNativeHandle()),
50        handler_id_);
51  }
52
53  virtual void Observe(NotificationType type,
54                       const NotificationSource& source,
55                       const NotificationDetails& details) {
56    DCHECK(type == NotificationType::SCREEN_LOCK_STATE_CHANGED);
57    if (running_)
58      MessageLoop::current()->Quit();
59  }
60
61  // Wait until the two conditions are met.
62  void Wait(bool locker_state, bool fullscreen) {
63    running_ = true;
64    scoped_ptr<chromeos::test::ScreenLockerTester>
65        tester(chromeos::ScreenLocker::GetTester());
66    while (tester->IsLocked() != locker_state ||
67           browser_->window()->IsFullscreen() != fullscreen) {
68      ui_test_utils::RunMessageLoop();
69    }
70    // Make sure all pending tasks are executed.
71    ui_test_utils::RunAllPendingInMessageLoop();
72    running_ = false;
73  }
74
75  CHROMEGTK_CALLBACK_1(Waiter, gboolean, OnWindowStateEvent,
76                       GdkEventWindowState*);
77
78 private:
79  Browser* browser_;
80  gulong handler_id_;
81  NotificationRegistrar registrar_;
82
83  // Are we currently running the message loop?
84  bool running_;
85
86  DISALLOW_COPY_AND_ASSIGN(Waiter);
87};
88
89gboolean Waiter::OnWindowStateEvent(GtkWidget* widget,
90                                    GdkEventWindowState* event) {
91  MessageLoop::current()->Quit();
92  return false;
93}
94
95}  // namespace
96
97namespace chromeos {
98
99class ScreenLockerTest : public CrosInProcessBrowserTest {
100 public:
101  ScreenLockerTest() : mock_screen_lock_library_(NULL),
102                       mock_input_method_library_(NULL) {
103  }
104
105 protected:
106  MockScreenLockLibrary *mock_screen_lock_library_;
107  MockInputMethodLibrary *mock_input_method_library_;
108
109  // Test the no password mode with different unlock scheme given by
110  // |unlock| function.
111  void TestNoPassword(void (unlock)(views::Widget*)) {
112    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
113        .Times(1)
114        .RetiresOnSaturation();
115    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
116        .Times(1)
117        .RetiresOnSaturation();
118    UserManager::Get()->OffTheRecordUserLoggedIn();
119    ScreenLocker::Show();
120    scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
121    tester->EmulateWindowManagerReady();
122    if (!chromeos::ScreenLocker::GetTester()->IsLocked())
123      ui_test_utils::WaitForNotification(
124          NotificationType::SCREEN_LOCK_STATE_CHANGED);
125    EXPECT_TRUE(tester->IsLocked());
126    tester->InjectMockAuthenticator("", "");
127
128    unlock(tester->GetWidget());
129
130    ui_test_utils::RunAllPendingInMessageLoop();
131    EXPECT_TRUE(tester->IsLocked());
132
133    // Emulate LockScreen request from PowerManager (via SessionManager).
134    ScreenLocker::Hide();
135    ui_test_utils::RunAllPendingInMessageLoop();
136    EXPECT_FALSE(tester->IsLocked());
137  }
138
139  void LockScreenWithUser(test::ScreenLockerTester* tester,
140                          const std::string& user) {
141    UserManager::Get()->UserLoggedIn(user);
142    ScreenLocker::Show();
143    tester->EmulateWindowManagerReady();
144    if (!tester->IsLocked()) {
145      ui_test_utils::WaitForNotification(
146          NotificationType::SCREEN_LOCK_STATE_CHANGED);
147    }
148    EXPECT_TRUE(tester->IsLocked());
149  }
150
151 private:
152  virtual void SetUpInProcessBrowserTestFixture() {
153    cros_mock_->InitStatusAreaMocks();
154    cros_mock_->InitMockScreenLockLibrary();
155    mock_screen_lock_library_ = cros_mock_->mock_screen_lock_library();
156    mock_input_method_library_ = cros_mock_->mock_input_method_library();
157    EXPECT_CALL(*mock_screen_lock_library_, AddObserver(testing::_))
158        .Times(1)
159        .RetiresOnSaturation();
160    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockCompleted())
161        .Times(1)
162        .RetiresOnSaturation();
163    // Expectations for the status are on the screen lock window.
164    cros_mock_->SetStatusAreaMocksExpectations();
165    // Expectations for the status area on the browser window.
166    cros_mock_->SetStatusAreaMocksExpectations();
167  }
168
169  virtual void SetUpCommandLine(CommandLine* command_line) {
170    command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
171    command_line->AppendSwitch(switches::kNoFirstRun);
172  }
173
174  DISALLOW_COPY_AND_ASSIGN(ScreenLockerTest);
175};
176
177// Temporarily disabling all screen locker tests while investigating the
178// issue crbug.com/78764.
179IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestBasic) {
180  EXPECT_CALL(*mock_input_method_library_, GetNumActiveInputMethods())
181      .Times(1)
182      .WillRepeatedly((testing::Return(0)))
183      .RetiresOnSaturation();
184  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
185      .Times(1)
186      .RetiresOnSaturation();
187  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
188      .Times(1)
189      .RetiresOnSaturation();
190  UserManager::Get()->UserLoggedIn("user");
191  ScreenLocker::Show();
192  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
193  tester->EmulateWindowManagerReady();
194  if (!chromeos::ScreenLocker::GetTester()->IsLocked())
195    ui_test_utils::WaitForNotification(
196        NotificationType::SCREEN_LOCK_STATE_CHANGED);
197
198  // Test to make sure that the widget is actually appearing and is of
199  // reasonable size, preventing a regression of
200  // http://code.google.com/p/chromium-os/issues/detail?id=5987
201  gfx::Rect lock_bounds = tester->GetChildWidget()->GetWindowScreenBounds();
202  EXPECT_GT(lock_bounds.width(), 10);
203  EXPECT_GT(lock_bounds.height(), 10);
204
205  tester->InjectMockAuthenticator("user", "pass");
206  EXPECT_TRUE(tester->IsLocked());
207  tester->EnterPassword("fail");
208  ui_test_utils::RunAllPendingInMessageLoop();
209  EXPECT_TRUE(tester->IsLocked());
210  tester->EnterPassword("pass");
211  ui_test_utils::RunAllPendingInMessageLoop();
212  // Successful authentication simply send a unlock request to PowerManager.
213  EXPECT_TRUE(tester->IsLocked());
214
215  // Emulate LockScreen request from PowerManager (via SessionManager).
216  // TODO(oshima): Find out better way to handle this in mock.
217  ScreenLocker::Hide();
218  ui_test_utils::RunAllPendingInMessageLoop();
219  EXPECT_FALSE(tester->IsLocked());
220}
221
222IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestFullscreenExit) {
223  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
224      .Times(1)
225      .RetiresOnSaturation();
226  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
227      .Times(1)
228      .RetiresOnSaturation();
229  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
230  {
231    Waiter waiter(browser());
232    browser()->ToggleFullscreenMode();
233    waiter.Wait(false /* not locked */, true /* full screen */);
234    EXPECT_TRUE(browser()->window()->IsFullscreen());
235    EXPECT_FALSE(tester->IsLocked());
236  }
237  {
238    Waiter waiter(browser());
239    UserManager::Get()->UserLoggedIn("user");
240    ScreenLocker::Show();
241    tester->EmulateWindowManagerReady();
242    waiter.Wait(true /* locked */, false /* full screen */);
243    EXPECT_FALSE(browser()->window()->IsFullscreen());
244    EXPECT_TRUE(tester->IsLocked());
245  }
246  tester->InjectMockAuthenticator("user", "pass");
247  tester->EnterPassword("pass");
248  ui_test_utils::RunAllPendingInMessageLoop();
249  ScreenLocker::Hide();
250  ui_test_utils::RunAllPendingInMessageLoop();
251  EXPECT_FALSE(tester->IsLocked());
252}
253
254void MouseMove(views::Widget* widget) {
255  ui_controls::SendMouseMove(10, 10);
256}
257
258IN_PROC_BROWSER_TEST_F(ScreenLockerTest,
259                       DISABLED_TestNoPasswordWithMouseMove) {
260  TestNoPassword(MouseMove);
261}
262
263void MouseClick(views::Widget* widget) {
264  ui_controls::SendMouseClick(ui_controls::RIGHT);
265}
266
267IN_PROC_BROWSER_TEST_F(ScreenLockerTest,
268                       DISABLED_TestNoPasswordWithMouseClick) {
269  TestNoPassword(MouseClick);
270}
271
272void KeyPress(views::Widget* widget) {
273  ui_controls::SendKeyPress(GTK_WINDOW(widget->GetNativeView()),
274                            ui::VKEY_SPACE, false, false, false, false);
275}
276
277IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestNoPasswordWithKeyPress) {
278  TestNoPassword(KeyPress);
279}
280
281IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestShowTwice) {
282  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
283      .Times(2)
284      .RetiresOnSaturation();
285  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
286  LockScreenWithUser(tester.get(), "user");
287
288  // Ensure there's a profile or this test crashes.
289  ProfileManager::GetDefaultProfile();
290
291  // Calling Show again simply send LockCompleted signal.
292  ScreenLocker::Show();
293  EXPECT_TRUE(tester->IsLocked());
294
295  // Close the locker to match expectations.
296  ScreenLocker::Hide();
297  ui_test_utils::RunAllPendingInMessageLoop();
298  EXPECT_FALSE(tester->IsLocked());
299}
300
301IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestEscape) {
302  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
303      .Times(1)
304      .RetiresOnSaturation();
305  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
306  LockScreenWithUser(tester.get(), "user");
307
308  // Ensure there's a profile or this test crashes.
309  ProfileManager::GetDefaultProfile();
310
311  tester->SetPassword("password");
312  EXPECT_EQ("password", tester->GetPassword());
313  // Escape clears the password.
314  ui_controls::SendKeyPress(GTK_WINDOW(tester->GetWidget()->GetNativeView()),
315                            ui::VKEY_ESCAPE, false, false, false, false);
316  ui_test_utils::RunAllPendingInMessageLoop();
317  EXPECT_EQ("", tester->GetPassword());
318
319  // Close the locker to match expectations.
320  ScreenLocker::Hide();
321  ui_test_utils::RunAllPendingInMessageLoop();
322  EXPECT_FALSE(tester->IsLocked());
323}
324
325}  // namespace chromeos
326