Home | History | Annotate | Download | only in braille_display_private
      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