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#ifndef USE_BRLAPI
6#error This test requires brlapi.
7#endif
8
9#include <deque>
10
11#include "base/bind.h"
12#include "chrome/browser/chrome_notification_types.h"
13#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
14#include "chrome/browser/chromeos/login/lock/screen_locker.h"
15#include "chrome/browser/chromeos/login/lock/screen_locker_tester.h"
16#include "chrome/browser/chromeos/profiles/profile_helper.h"
17#include "chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.h"
18#include "chrome/browser/extensions/api/braille_display_private/braille_display_private_api.h"
19#include "chrome/browser/extensions/api/braille_display_private/brlapi_connection.h"
20#include "chrome/browser/extensions/api/braille_display_private/stub_braille_controller.h"
21#include "chrome/browser/extensions/extension_apitest.h"
22#include "chrome/browser/profiles/profile_manager.h"
23#include "chrome/test/base/testing_profile.h"
24#include "chromeos/chromeos_switches.h"
25#include "components/user_manager/user_manager.h"
26#include "content/public/browser/browser_thread.h"
27#include "content/public/browser/notification_service.h"
28#include "content/public/test/test_utils.h"
29#include "testing/gtest/include/gtest/gtest.h"
30
31using chromeos::ProfileHelper;
32using chromeos::ScreenLocker;
33using user_manager::UserManager;
34using chromeos::test::ScreenLockerTester;
35using content::BrowserThread;
36
37namespace extensions {
38namespace api {
39namespace braille_display_private {
40
41namespace {
42
43const char kTestUserName[] = "owner@invalid.domain";
44
45// Used to make ReadKeys return an error.
46brlapi_keyCode_t kErrorKeyCode = BRLAPI_KEY_MAX;
47
48}  // namespace
49
50// Data maintained by the mock BrlapiConnection.  This data lives throughout
51// a test, while the api implementation takes ownership of the connection
52// itself.
53struct MockBrlapiConnectionData {
54  bool connected;
55  size_t display_size;
56  brlapi_error_t error;
57  std::vector<std::string> written_content;
58  // List of brlapi key codes.  A negative number makes the connection mock
59  // return an error from ReadKey.
60  std::deque<brlapi_keyCode_t> pending_keys;
61  // Causes a new display to appear to appear on disconnect, that is the
62  // display size doubles and the controller gets notified of a brltty
63  // restart.
64  bool reappear_on_disconnect;
65};
66
67class MockBrlapiConnection : public BrlapiConnection {
68 public:
69  explicit MockBrlapiConnection(MockBrlapiConnectionData* data)
70      : data_(data) {}
71  virtual ConnectResult Connect(const OnDataReadyCallback& on_data_ready)
72      OVERRIDE {
73    data_->connected = true;
74    on_data_ready_ = on_data_ready;
75    if (!data_->pending_keys.empty()) {
76      BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
77                              base::Bind(&MockBrlapiConnection::NotifyDataReady,
78                                        base::Unretained(this)));
79    }
80    return CONNECT_SUCCESS;
81  }
82
83  virtual void Disconnect() OVERRIDE {
84    data_->connected = false;
85    if (data_->reappear_on_disconnect) {
86      data_->display_size *= 2;
87      BrowserThread::PostTask(
88          BrowserThread::IO, FROM_HERE,
89          base::Bind(&BrailleControllerImpl::PokeSocketDirForTesting,
90                     base::Unretained(BrailleControllerImpl::GetInstance())));
91    }
92  }
93
94  virtual bool Connected() OVERRIDE {
95    return data_->connected;
96  }
97
98  virtual brlapi_error_t* BrlapiError() OVERRIDE {
99    return &data_->error;
100  }
101
102  virtual std::string BrlapiStrError() OVERRIDE {
103    return data_->error.brlerrno != BRLAPI_ERROR_SUCCESS ? "Error" : "Success";
104  }
105
106  virtual bool GetDisplaySize(size_t* size) OVERRIDE {
107    *size = data_->display_size;
108    return true;
109  }
110
111  virtual bool WriteDots(const unsigned char* cells) OVERRIDE {
112    std::string written(reinterpret_cast<const char*>(cells),
113                        data_->display_size);
114    data_->written_content.push_back(written);
115    return true;
116  }
117
118  virtual int ReadKey(brlapi_keyCode_t* key_code) OVERRIDE {
119    if (!data_->pending_keys.empty()) {
120      brlapi_keyCode_t queued_key_code = data_->pending_keys.front();
121      data_->pending_keys.pop_front();
122      if (queued_key_code == kErrorKeyCode) {
123        data_->error.brlerrno = BRLAPI_ERROR_EOF;
124        return -1;  // Signal error.
125      }
126      *key_code = queued_key_code;
127      return 1;
128    } else {
129      return 0;
130    }
131  }
132
133 private:
134
135  void NotifyDataReady() {
136    on_data_ready_.Run();
137    if (!data_->pending_keys.empty()) {
138      BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
139                              base::Bind(&MockBrlapiConnection::NotifyDataReady,
140                                        base::Unretained(this)));
141    }
142  }
143
144  MockBrlapiConnectionData* data_;
145  OnDataReadyCallback on_data_ready_;
146};
147
148class BrailleDisplayPrivateApiTest : public ExtensionApiTest {
149 public:
150  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
151    ExtensionApiTest::SetUpInProcessBrowserTestFixture();
152    connection_data_.connected = false;
153    connection_data_.display_size = 0;
154    connection_data_.error.brlerrno = BRLAPI_ERROR_SUCCESS;
155    connection_data_.reappear_on_disconnect = false;
156    BrailleControllerImpl::GetInstance()->SetCreateBrlapiConnectionForTesting(
157        base::Bind(
158            &BrailleDisplayPrivateApiTest::CreateBrlapiConnection,
159            base::Unretained(this)));
160    DisableAccessibilityManagerBraille();
161  }
162
163 protected:
164  MockBrlapiConnectionData connection_data_;
165
166  // By default, don't let the accessibility manager interfere and
167  // steal events.  Some tests override this to keep the normal behaviour
168  // of the accessibility manager.
169  virtual void DisableAccessibilityManagerBraille() {
170    chromeos::AccessibilityManager::SetBrailleControllerForTest(
171        &stub_braille_controller_);
172  }
173
174 private:
175  scoped_ptr<BrlapiConnection> CreateBrlapiConnection() {
176    return scoped_ptr<BrlapiConnection>(
177        new MockBrlapiConnection(&connection_data_));
178  }
179
180  StubBrailleController stub_braille_controller_;
181};
182
183IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, WriteDots) {
184  connection_data_.display_size = 11;
185  ASSERT_TRUE(RunComponentExtensionTest("braille_display_private/write_dots"))
186      << message_;
187  ASSERT_EQ(3U, connection_data_.written_content.size());
188  const std::string expected_content(connection_data_.display_size, '\0');
189  for (size_t i = 0; i < connection_data_.written_content.size(); ++i) {
190    ASSERT_EQ(std::string(
191        connection_data_.display_size,
192        static_cast<char>(i)),
193                 connection_data_.written_content[i])
194        << "String " << i << " doesn't match";
195  }
196}
197
198IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, KeyEvents) {
199  connection_data_.display_size = 11;
200
201  // Braille navigation commands.
202  connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD |
203                                          BRLAPI_KEY_CMD_LNUP);
204  connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD |
205                                          BRLAPI_KEY_CMD_LNDN);
206  connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD |
207                                          BRLAPI_KEY_CMD_FWINLT);
208  connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD |
209                                          BRLAPI_KEY_CMD_FWINRT);
210  connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD |
211                                          BRLAPI_KEY_CMD_TOP);
212  connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD |
213                                          BRLAPI_KEY_CMD_BOT);
214  connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD |
215                                          BRLAPI_KEY_CMD_ROUTE | 5);
216
217  // Braille display standard keyboard emulation.
218
219  // An ascii character.
220  connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_SYM | 'A');
221  // A non-ascii 'latin1' character.  Small letter a with ring above.
222  connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_SYM | 0xE5);
223  // A non-latin1 Unicode character.  LATIN SMALL LETTER A WITH MACRON.
224  connection_data_.pending_keys.push_back(
225      BRLAPI_KEY_TYPE_SYM | BRLAPI_KEY_SYM_UNICODE | 0x100);
226  // A Unicode character outside the BMP.  CAT FACE WITH TEARS OF JOY.
227  // With anticipation for the first emoji-enabled braille display.
228  connection_data_.pending_keys.push_back(
229      BRLAPI_KEY_TYPE_SYM | BRLAPI_KEY_SYM_UNICODE | 0x1F639);
230  // Invalid Unicode character.
231  connection_data_.pending_keys.push_back(
232      BRLAPI_KEY_TYPE_SYM | BRLAPI_KEY_SYM_UNICODE | 0x110000);
233
234  // Non-alphanumeric function keys.
235
236  // Backspace.
237  connection_data_.pending_keys.push_back(
238      BRLAPI_KEY_TYPE_SYM | BRLAPI_KEY_SYM_BACKSPACE);
239  // Shift+Tab.
240  connection_data_.pending_keys.push_back(
241      BRLAPI_KEY_TYPE_SYM | BRLAPI_KEY_FLG_SHIFT | BRLAPI_KEY_SYM_TAB);
242  // Alt+F3. (0-based).
243  connection_data_.pending_keys.push_back(
244      BRLAPI_KEY_TYPE_SYM | BRLAPI_KEY_FLG_META |
245      (BRLAPI_KEY_SYM_FUNCTION + 2));
246
247  // ctrl+dot1+dot2.
248  connection_data_.pending_keys.push_back(
249      BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_FLG_CONTROL | BRLAPI_KEY_CMD_PASSDOTS |
250      BRLAPI_DOT1 | BRLAPI_DOT2);
251
252  // Braille dot keys, all combinations including space (0).
253  for (int i = 0; i < 256; ++i) {
254    connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD |
255                                            BRLAPI_KEY_CMD_PASSDOTS | i);
256  }
257
258  ASSERT_TRUE(RunComponentExtensionTest("braille_display_private/key_events"));
259}
260
261IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, DisplayStateChanges) {
262  connection_data_.display_size = 11;
263  connection_data_.pending_keys.push_back(kErrorKeyCode);
264  connection_data_.reappear_on_disconnect = true;
265  ASSERT_TRUE(RunComponentExtensionTest(
266      "braille_display_private/display_state_changes"));
267}
268
269class BrailleDisplayPrivateAPIUserTest : public BrailleDisplayPrivateApiTest {
270 public:
271  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
272    command_line->AppendSwitch(chromeos::switches::kLoginManager);
273    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
274                                    TestingProfile::kTestUserProfileDir);
275  }
276
277  class MockEventDelegate : public BrailleDisplayPrivateAPI::EventDelegate {
278   public:
279    MockEventDelegate() : event_count_(0) {}
280
281    int GetEventCount() { return event_count_; }
282
283    virtual void BroadcastEvent(scoped_ptr<Event> event) OVERRIDE {
284      ++event_count_;
285    }
286    virtual bool HasListener() OVERRIDE { return true; }
287
288   private:
289    int event_count_;
290  };
291
292  MockEventDelegate* SetMockEventDelegate(BrailleDisplayPrivateAPI* api) {
293    MockEventDelegate* delegate = new MockEventDelegate();
294    api->SetEventDelegateForTest(
295        scoped_ptr<BrailleDisplayPrivateAPI::EventDelegate>(delegate).Pass());
296    return delegate;
297  }
298
299  void LockScreen(ScreenLockerTester* tester) {
300    ScreenLocker::Show();
301    tester->EmulateWindowManagerReady();
302    content::WindowedNotificationObserver lock_state_observer(
303        chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
304        content::NotificationService::AllSources());
305    if (!tester->IsLocked())
306      lock_state_observer.Wait();
307    ASSERT_TRUE(tester->IsLocked());
308  }
309
310  void DismissLockScreen(ScreenLockerTester* tester) {
311    ScreenLocker::Hide();
312    content::WindowedNotificationObserver lock_state_observer(
313        chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
314        content::NotificationService::AllSources());
315    if (tester->IsLocked())
316      lock_state_observer.Wait();
317    ASSERT_FALSE(tester->IsLocked());
318  }
319
320 protected:
321  virtual void DisableAccessibilityManagerBraille() OVERRIDE {
322    // Let the accessibility manager behave as usual for these tests.
323  }
324};
325
326IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateAPIUserTest,
327                       KeyEventOnLockScreen) {
328  scoped_ptr<ScreenLockerTester> tester(ScreenLocker::GetTester());
329  // Log in.
330  user_manager::UserManager::Get()->UserLoggedIn(
331      kTestUserName, kTestUserName, true);
332  user_manager::UserManager::Get()->SessionStarted();
333  Profile* profile = ProfileManager::GetActiveUserProfile();
334  ASSERT_FALSE(
335      ProfileHelper::GetSigninProfile()->IsSameProfile(profile))
336      << ProfileHelper::GetSigninProfile()->GetDebugName() << " vs. "
337      << profile->GetDebugName();
338
339  // Create API and event delegate for sign in profile.
340  BrailleDisplayPrivateAPI signin_api(ProfileHelper::GetSigninProfile());
341  MockEventDelegate* signin_delegate = SetMockEventDelegate(&signin_api);
342  EXPECT_EQ(0, signin_delegate->GetEventCount());
343  // Create api and delegate for the logged in user.
344  BrailleDisplayPrivateAPI user_api(profile);
345  MockEventDelegate* user_delegate = SetMockEventDelegate(&user_api);
346
347  // Send key event to both profiles.
348  KeyEvent key_event;
349  key_event.command = KEY_COMMAND_LINE_UP;
350  signin_api.OnBrailleKeyEvent(key_event);
351  user_api.OnBrailleKeyEvent(key_event);
352  EXPECT_EQ(0, signin_delegate->GetEventCount());
353  EXPECT_EQ(1, user_delegate->GetEventCount());
354
355  // Lock screen, and make sure that the key event goes to the
356  // signin profile.
357  LockScreen(tester.get());
358  signin_api.OnBrailleKeyEvent(key_event);
359  user_api.OnBrailleKeyEvent(key_event);
360  EXPECT_EQ(1, signin_delegate->GetEventCount());
361  EXPECT_EQ(1, user_delegate->GetEventCount());
362
363  // Unlock screen, making sur ekey events go to the user profile again.
364  DismissLockScreen(tester.get());
365  signin_api.OnBrailleKeyEvent(key_event);
366  user_api.OnBrailleKeyEvent(key_event);
367  EXPECT_EQ(1, signin_delegate->GetEventCount());
368  EXPECT_EQ(2, user_delegate->GetEventCount());
369}
370
371}  // namespace braille_display_private
372}  // namespace api
373}  // namespace extensions
374