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/login/users/user_manager.h" 17 #include "chrome/browser/chromeos/profiles/profile_helper.h" 18 #include "chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.h" 19 #include "chrome/browser/extensions/api/braille_display_private/braille_display_private_api.h" 20 #include "chrome/browser/extensions/api/braille_display_private/brlapi_connection.h" 21 #include "chrome/browser/extensions/api/braille_display_private/stub_braille_controller.h" 22 #include "chrome/browser/extensions/extension_apitest.h" 23 #include "chrome/browser/profiles/profile_manager.h" 24 #include "chrome/test/base/testing_profile.h" 25 #include "chromeos/chromeos_switches.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 31 using chromeos::ProfileHelper; 32 using chromeos::ScreenLocker; 33 using chromeos::UserManager; 34 using chromeos::test::ScreenLockerTester; 35 using content::BrowserThread; 36 37 namespace extensions { 38 namespace api { 39 namespace braille_display_private { 40 41 namespace { 42 43 const char kTestUserName[] = "owner (at) invalid.domain"; 44 45 // Used to make ReadKeys return an error. 46 brlapi_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. 53 struct 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 67 class 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 148 class 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 183 IN_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 198 IN_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 261 IN_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 269 class 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 326 IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateAPIUserTest, 327 KeyEventOnLockScreen) { 328 scoped_ptr<ScreenLockerTester> tester(ScreenLocker::GetTester()); 329 // Log in. 330 UserManager::Get()->UserLoggedIn(kTestUserName, kTestUserName, true); 331 UserManager::Get()->SessionStarted(); 332 Profile* profile = ProfileManager::GetActiveUserProfile(); 333 ASSERT_FALSE( 334 ProfileHelper::GetSigninProfile()->IsSameProfile(profile)) 335 << ProfileHelper::GetSigninProfile()->GetDebugName() << " vs. " 336 << profile->GetDebugName(); 337 338 // Create API and event delegate for sign in profile. 339 BrailleDisplayPrivateAPI signin_api(ProfileHelper::GetSigninProfile()); 340 MockEventDelegate* signin_delegate = SetMockEventDelegate(&signin_api); 341 EXPECT_EQ(0, signin_delegate->GetEventCount()); 342 // Create api and delegate for the logged in user. 343 BrailleDisplayPrivateAPI user_api(profile); 344 MockEventDelegate* user_delegate = SetMockEventDelegate(&user_api); 345 346 // Send key event to both profiles. 347 KeyEvent key_event; 348 key_event.command = KEY_COMMAND_LINE_UP; 349 signin_api.OnBrailleKeyEvent(key_event); 350 user_api.OnBrailleKeyEvent(key_event); 351 EXPECT_EQ(0, signin_delegate->GetEventCount()); 352 EXPECT_EQ(1, user_delegate->GetEventCount()); 353 354 // Lock screen, and make sure that the key event goes to the 355 // signin profile. 356 LockScreen(tester.get()); 357 signin_api.OnBrailleKeyEvent(key_event); 358 user_api.OnBrailleKeyEvent(key_event); 359 EXPECT_EQ(1, signin_delegate->GetEventCount()); 360 EXPECT_EQ(1, user_delegate->GetEventCount()); 361 362 // Unlock screen, making sur ekey events go to the user profile again. 363 DismissLockScreen(tester.get()); 364 signin_api.OnBrailleKeyEvent(key_event); 365 user_api.OnBrailleKeyEvent(key_event); 366 EXPECT_EQ(1, signin_delegate->GetEventCount()); 367 EXPECT_EQ(2, user_delegate->GetEventCount()); 368 } 369 370 } // namespace braille_display_private 371 } // namespace api 372 } // namespace extensions 373