1 // Copyright (c) 2012 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 "chrome/browser/chromeos/input_method/input_method_manager_impl.h" 6 7 #include <algorithm> 8 9 #include "base/basictypes.h" 10 #include "base/compiler_specific.h" 11 #include "base/logging.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/message_loop/message_loop.h" 14 #include "chrome/browser/chromeos/input_method/mock_candidate_window_controller.h" 15 #include "chrome/browser/chromeos/input_method/mock_ibus_controller.h" 16 #include "chromeos/dbus/ibus/mock_ibus_client.h" 17 #include "chromeos/dbus/ibus/mock_ibus_input_context_client.h" 18 #include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" 19 #include "chromeos/ime/extension_ime_util.h" 20 #include "chromeos/ime/fake_input_method_delegate.h" 21 #include "chromeos/ime/mock_component_extension_ime_manager_delegate.h" 22 #include "chromeos/ime/mock_ibus_daemon_controller.h" 23 #include "chromeos/ime/mock_xkeyboard.h" 24 #include "testing/gtest/include/gtest/gtest.h" 25 #include "ui/base/accelerators/accelerator.h" 26 #include "ui/base/keycodes/keyboard_codes.h" 27 28 namespace chromeos { 29 30 namespace input_method { 31 namespace { 32 33 const char nacl_mozc_us_id[] = 34 "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_us"; 35 const char nacl_mozc_jp_id[] = 36 "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp"; 37 38 // Returns true if |descriptors| contain |target|. 39 bool Contain(const InputMethodDescriptors& descriptors, 40 const InputMethodDescriptor& target) { 41 for (size_t i = 0; i < descriptors.size(); ++i) { 42 if (descriptors[i].id() == target.id()) 43 return true; 44 } 45 return false; 46 } 47 48 class InputMethodManagerImplTest : public testing::Test { 49 public: 50 InputMethodManagerImplTest() 51 : delegate_(NULL), 52 controller_(NULL), 53 candidate_window_controller_(NULL), 54 xkeyboard_(NULL) { 55 } 56 virtual ~InputMethodManagerImplTest() {} 57 58 virtual void SetUp() OVERRIDE { 59 mock_ibus_daemon_controller_ = new chromeos::MockIBusDaemonController(); 60 chromeos::IBusDaemonController::InitializeForTesting( 61 mock_ibus_daemon_controller_); 62 mock_dbus_thread_manager_ = 63 new chromeos::MockDBusThreadManagerWithoutGMock(); 64 chromeos::DBusThreadManager::InitializeForTesting( 65 mock_dbus_thread_manager_); 66 delegate_ = new FakeInputMethodDelegate(); 67 manager_.reset(new InputMethodManagerImpl( 68 scoped_ptr<InputMethodDelegate>(delegate_))); 69 controller_ = new MockIBusController; 70 manager_->SetIBusControllerForTesting(controller_); 71 candidate_window_controller_ = new MockCandidateWindowController; 72 manager_->SetCandidateWindowControllerForTesting( 73 candidate_window_controller_); 74 xkeyboard_ = new MockXKeyboard; 75 manager_->SetXKeyboardForTesting(xkeyboard_); 76 77 ime_list_.clear(); 78 79 ComponentExtensionIME ext1; 80 ext1.id = "fpfbhcjppmaeaijcidgiibchfbnhbelj"; 81 ext1.description = "ext1_description"; 82 ext1.path = base::FilePath("ext1_file_path"); 83 84 ComponentExtensionEngine ext1_engine1; 85 ext1_engine1.engine_id = "nacl_mozc_us"; 86 ext1_engine1.display_name = "ext1_engine_1_display_name"; 87 ext1_engine1.language_codes.push_back("ja"); 88 ext1_engine1.layouts.push_back("us"); 89 ext1.engines.push_back(ext1_engine1); 90 91 ComponentExtensionEngine ext1_engine2; 92 ext1_engine2.engine_id = "nacl_mozc_jp"; 93 ext1_engine2.display_name = "ext1_engine_1_display_name"; 94 ext1_engine2.language_codes.push_back("ja"); 95 ext1_engine2.layouts.push_back("jp"); 96 ext1.engines.push_back(ext1_engine2); 97 98 ime_list_.push_back(ext1); 99 100 ComponentExtensionIME ext2; 101 ext2.id = "nmblnjkfdkabgdofidlkienfnnbjhnab"; 102 ext2.description = "ext2_description"; 103 ext2.path = base::FilePath("ext2_file_path"); 104 105 ComponentExtensionEngine ext2_engine1; 106 ext2_engine1.engine_id = "ext2_engine1_engine_id"; 107 ext2_engine1.display_name = "ext2_engine_1_display_name"; 108 ext2_engine1.language_codes.push_back("en"); 109 ext2_engine1.layouts.push_back("us"); 110 ext2.engines.push_back(ext2_engine1); 111 112 ComponentExtensionEngine ext2_engine2; 113 ext2_engine2.engine_id = "ext2_engine2_engine_id"; 114 ext2_engine2.display_name = "ext2_engine_2_display_name"; 115 ext2_engine2.language_codes.push_back("en"); 116 ext2_engine2.layouts.push_back("us(dvorak)"); 117 ext2.engines.push_back(ext2_engine2); 118 119 ime_list_.push_back(ext2); 120 121 mock_ibus_daemon_controller_->EmulateConnect(); 122 } 123 124 virtual void TearDown() OVERRIDE { 125 mock_ibus_daemon_controller_->EmulateDisconnect(); 126 delegate_ = NULL; 127 controller_ = NULL; 128 candidate_window_controller_ = NULL; 129 xkeyboard_ = NULL; 130 manager_.reset(); 131 chromeos::DBusThreadManager::Shutdown(); 132 chromeos::IBusDaemonController::Shutdown(); 133 } 134 135 protected: 136 // Helper function to initialize component extension stuff for testing. 137 void InitComponentExtension() { 138 mock_delegate_ = new MockComponentExtIMEManagerDelegate(); 139 mock_delegate_->set_ime_list(ime_list_); 140 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate(mock_delegate_); 141 manager_->InitializeComponentExtensionForTesting(delegate.Pass()); 142 } 143 144 // Helper function to initialize IBus bus connection for testing. Do not use 145 // ibus related mocks before calling this function. 146 void InitIBusBus() { 147 mock_dbus_thread_manager_->InitIBusBus("dummy address", 148 base::Bind(&base::DoNothing)); 149 mock_ibus_client_ = mock_dbus_thread_manager_->mock_ibus_client(); 150 mock_ibus_input_context_client_ = 151 mock_dbus_thread_manager_->mock_ibus_input_context_client(); 152 mock_ibus_daemon_controller_->EmulateConnect(); 153 } 154 155 scoped_ptr<InputMethodManagerImpl> manager_; 156 FakeInputMethodDelegate* delegate_; 157 MockIBusController* controller_; 158 MockCandidateWindowController* candidate_window_controller_; 159 MockIBusDaemonController* mock_ibus_daemon_controller_; 160 MockIBusInputContextClient* mock_ibus_input_context_client_; 161 MockIBusClient* mock_ibus_client_; 162 MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager_; 163 MockXKeyboard* xkeyboard_; 164 base::MessageLoop message_loop_; 165 MockComponentExtIMEManagerDelegate* mock_delegate_; 166 std::vector<ComponentExtensionIME> ime_list_; 167 168 private: 169 DISALLOW_COPY_AND_ASSIGN(InputMethodManagerImplTest); 170 }; 171 172 class TestableComponentExtensionIMEManager 173 : public ComponentExtensionIMEManager { 174 public: 175 using ComponentExtensionIMEManager::GetComponentExtensionIMEId; 176 }; 177 178 class TestObserver : public InputMethodManager::Observer { 179 public: 180 TestObserver() 181 : input_method_changed_count_(0), 182 input_method_property_changed_count_(0), 183 last_show_message_(false) { 184 } 185 virtual ~TestObserver() {} 186 187 virtual void InputMethodChanged(InputMethodManager* manager, 188 bool show_message) OVERRIDE { 189 ++input_method_changed_count_; 190 last_show_message_ = show_message; 191 } 192 virtual void InputMethodPropertyChanged( 193 InputMethodManager* manager) OVERRIDE { 194 ++input_method_property_changed_count_; 195 } 196 197 int input_method_changed_count_; 198 int input_method_property_changed_count_; 199 bool last_show_message_; 200 201 private: 202 DISALLOW_COPY_AND_ASSIGN(TestObserver); 203 }; 204 205 class TestCandidateWindowObserver 206 : public InputMethodManager::CandidateWindowObserver { 207 public: 208 TestCandidateWindowObserver() 209 : candidate_window_opened_count_(0), 210 candidate_window_closed_count_(0) { 211 } 212 virtual ~TestCandidateWindowObserver() {} 213 214 virtual void CandidateWindowOpened(InputMethodManager* manager) OVERRIDE { 215 ++candidate_window_opened_count_; 216 } 217 virtual void CandidateWindowClosed(InputMethodManager* manager) OVERRIDE { 218 ++candidate_window_closed_count_; 219 } 220 221 int candidate_window_opened_count_; 222 int candidate_window_closed_count_; 223 224 private: 225 DISALLOW_COPY_AND_ASSIGN(TestCandidateWindowObserver); 226 }; 227 228 } // namespace 229 230 TEST_F(InputMethodManagerImplTest, TestGetXKeyboard) { 231 EXPECT_TRUE(manager_->GetXKeyboard()); 232 EXPECT_EQ(xkeyboard_, manager_->GetXKeyboard()); 233 } 234 235 TEST_F(InputMethodManagerImplTest, TestCandidateWindowObserver) { 236 TestCandidateWindowObserver observer; 237 candidate_window_controller_->NotifyCandidateWindowOpened(); // nop 238 candidate_window_controller_->NotifyCandidateWindowClosed(); // nop 239 manager_->AddCandidateWindowObserver(&observer); 240 candidate_window_controller_->NotifyCandidateWindowOpened(); 241 EXPECT_EQ(1, observer.candidate_window_opened_count_); 242 candidate_window_controller_->NotifyCandidateWindowClosed(); 243 EXPECT_EQ(1, observer.candidate_window_closed_count_); 244 candidate_window_controller_->NotifyCandidateWindowOpened(); 245 EXPECT_EQ(2, observer.candidate_window_opened_count_); 246 candidate_window_controller_->NotifyCandidateWindowClosed(); 247 EXPECT_EQ(2, observer.candidate_window_closed_count_); 248 manager_->RemoveCandidateWindowObserver(&observer); 249 } 250 251 TEST_F(InputMethodManagerImplTest, TestObserver) { 252 // For http://crbug.com/19655#c11 - (3). browser_state_monitor_unittest.cc is 253 // also for the scenario. 254 TestObserver observer; 255 InitComponentExtension(); 256 InitIBusBus(); 257 manager_->AddObserver(&observer); 258 EXPECT_EQ(0, observer.input_method_changed_count_); 259 manager_->EnableLayouts("en-US", "xkb:us::eng"); 260 EXPECT_EQ(1, observer.input_method_changed_count_); 261 EXPECT_EQ(1, observer.input_method_property_changed_count_); 262 manager_->ChangeInputMethod("xkb:us:dvorak:eng"); 263 EXPECT_FALSE(observer.last_show_message_); 264 EXPECT_EQ(2, observer.input_method_changed_count_); 265 EXPECT_EQ(2, observer.input_method_property_changed_count_); 266 manager_->ChangeInputMethod("xkb:us:dvorak:eng"); 267 EXPECT_FALSE(observer.last_show_message_); 268 // The observer is always notified even when the same input method ID is 269 // passed to ChangeInputMethod() more than twice. 270 EXPECT_EQ(3, observer.input_method_changed_count_); 271 EXPECT_EQ(3, observer.input_method_property_changed_count_); 272 273 controller_->NotifyPropertyChangedForTesting(); 274 EXPECT_EQ(4, observer.input_method_property_changed_count_); 275 controller_->NotifyPropertyChangedForTesting(); 276 EXPECT_EQ(5, observer.input_method_property_changed_count_); 277 manager_->RemoveObserver(&observer); 278 } 279 280 TEST_F(InputMethodManagerImplTest, TestGetSupportedInputMethods) { 281 InitComponentExtension(); 282 InitIBusBus(); 283 scoped_ptr<InputMethodDescriptors> methods( 284 manager_->GetSupportedInputMethods()); 285 ASSERT_TRUE(methods.get()); 286 // Try to find random 4-5 layuts and IMEs to make sure the returned list is 287 // correct. 288 const InputMethodDescriptor* id_to_find = 289 manager_->GetInputMethodUtil()->GetInputMethodDescriptorFromId( 290 nacl_mozc_us_id); 291 id_to_find = manager_->GetInputMethodUtil()->GetInputMethodDescriptorFromId( 292 "xkb:us::eng"); 293 EXPECT_TRUE(Contain(*methods.get(), *id_to_find)); 294 id_to_find = manager_->GetInputMethodUtil()->GetInputMethodDescriptorFromId( 295 "xkb:us:dvorak:eng"); 296 EXPECT_TRUE(Contain(*methods.get(), *id_to_find)); 297 id_to_find = manager_->GetInputMethodUtil()->GetInputMethodDescriptorFromId( 298 "xkb:fr::fra"); 299 EXPECT_TRUE(Contain(*methods.get(), *id_to_find)); 300 } 301 302 TEST_F(InputMethodManagerImplTest, TestEnableLayouts) { 303 // Currently 5 keyboard layouts are supported for en-US, and 1 for ja. See 304 // ibus_input_method.txt. 305 InitComponentExtension(); 306 InitIBusBus(); 307 manager_->EnableLayouts("en-US", ""); 308 EXPECT_EQ(5U, manager_->GetNumActiveInputMethods()); 309 for (size_t i = 0; i < manager_->GetActiveInputMethodIds().size(); ++i) 310 LOG(ERROR) << manager_->GetActiveInputMethodIds().at(i); 311 // For http://crbug.com/19655#c11 - (2) 312 EXPECT_EQ(0, mock_ibus_daemon_controller_->start_count()); 313 314 // For http://crbug.com/19655#c11 - (5) 315 // The hardware keyboard layout "xkb:us::eng" is always active, hence 2U. 316 manager_->EnableLayouts("ja", ""); // Japanese 317 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 318 EXPECT_EQ(0, mock_ibus_daemon_controller_->start_count()); 319 } 320 321 TEST_F(InputMethodManagerImplTest, TestEnableLayoutsNonUsHardwareKeyboard) { 322 // The physical layout is French. 323 delegate_->set_hardware_keyboard_layout("xkb:fr::fra"); 324 manager_->EnableLayouts("en-US", ""); 325 EXPECT_EQ(6U, manager_->GetNumActiveInputMethods()); // 5 + French 326 // The physical layout is Japanese. 327 delegate_->set_hardware_keyboard_layout("xkb:jp::jpn"); 328 manager_->EnableLayouts("ja", ""); 329 // "xkb:us::eng" is not needed, hence 1. 330 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 331 } 332 333 TEST_F(InputMethodManagerImplTest, TestActiveInputMethods) { 334 manager_->EnableLayouts("ja", ""); // Japanese 335 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 336 scoped_ptr<InputMethodDescriptors> methods( 337 manager_->GetActiveInputMethods()); 338 ASSERT_TRUE(methods.get()); 339 EXPECT_EQ(2U, methods->size()); 340 const InputMethodDescriptor* id_to_find = 341 manager_->GetInputMethodUtil()->GetInputMethodDescriptorFromId( 342 "xkb:us::eng"); 343 EXPECT_TRUE(Contain(*methods.get(), *id_to_find)); 344 id_to_find = manager_->GetInputMethodUtil()->GetInputMethodDescriptorFromId( 345 "xkb:jp::jpn"); 346 EXPECT_TRUE(Contain(*methods.get(), *id_to_find)); 347 } 348 349 TEST_F(InputMethodManagerImplTest, TestSetInputMethodConfig) { 350 InputMethodConfigValue config; 351 config.type = InputMethodConfigValue::kValueTypeString; 352 config.string_value = "string"; 353 EXPECT_EQ(0, controller_->set_input_method_config_internal_count_); 354 EXPECT_TRUE(manager_->SetInputMethodConfig("section", "name", config)); 355 EXPECT_EQ(1, controller_->set_input_method_config_internal_count_); 356 EXPECT_EQ("section", 357 controller_->set_input_method_config_internal_key_.first); 358 EXPECT_EQ("name", 359 controller_->set_input_method_config_internal_key_.second); 360 EXPECT_EQ(config.type, 361 controller_->set_input_method_config_internal_value_.type); 362 EXPECT_EQ(config.string_value, 363 controller_->set_input_method_config_internal_value_.string_value); 364 365 // SetInputMethodConfig should be no-op in STATE_TERMINATING. 366 manager_->SetState(InputMethodManager::STATE_TERMINATING); 367 EXPECT_FALSE(manager_->SetInputMethodConfig("section", "name", config)); 368 EXPECT_EQ(1, controller_->set_input_method_config_internal_count_); 369 } 370 371 TEST_F(InputMethodManagerImplTest, TestEnableTwoLayouts) { 372 // For http://crbug.com/19655#c11 - (8), step 6. 373 TestObserver observer; 374 manager_->AddObserver(&observer); 375 InitComponentExtension(); 376 InitIBusBus(); 377 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 378 std::vector<std::string> ids; 379 ids.push_back("xkb:us:dvorak:eng"); 380 ids.push_back("xkb:us:colemak:eng"); 381 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 382 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 383 // Since all the IDs added avobe are keyboard layouts, Start() should not be 384 // called. 385 EXPECT_EQ(0, mock_ibus_daemon_controller_->start_count()); 386 EXPECT_EQ(1, observer.input_method_changed_count_); 387 EXPECT_EQ(ids[0], manager_->GetCurrentInputMethod().id()); 388 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 389 // Disable Dvorak. 390 ids.erase(ids.begin()); 391 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 392 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 393 EXPECT_EQ(2, observer.input_method_changed_count_); 394 EXPECT_EQ(ids[0], // colemak 395 manager_->GetCurrentInputMethod().id()); 396 EXPECT_EQ("us(colemak)", xkeyboard_->last_layout_); 397 manager_->RemoveObserver(&observer); 398 } 399 400 TEST_F(InputMethodManagerImplTest, TestEnableThreeLayouts) { 401 // For http://crbug.com/19655#c11 - (9). 402 TestObserver observer; 403 manager_->AddObserver(&observer); 404 InitComponentExtension(); 405 InitIBusBus(); 406 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 407 std::vector<std::string> ids; 408 ids.push_back("xkb:us::eng"); 409 ids.push_back("xkb:us:dvorak:eng"); 410 ids.push_back("xkb:us:colemak:eng"); 411 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 412 EXPECT_EQ(3U, manager_->GetNumActiveInputMethods()); 413 EXPECT_EQ(1, observer.input_method_changed_count_); 414 EXPECT_EQ(ids[0], manager_->GetCurrentInputMethod().id()); 415 EXPECT_EQ("us", xkeyboard_->last_layout_); 416 // Switch to Dvorak. 417 manager_->SwitchToNextInputMethod(); 418 EXPECT_EQ(2, observer.input_method_changed_count_); 419 EXPECT_EQ(ids[1], manager_->GetCurrentInputMethod().id()); 420 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 421 // Disable Dvorak. 422 ids.erase(ids.begin() + 1); 423 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 424 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 425 EXPECT_EQ(3, observer.input_method_changed_count_); 426 EXPECT_EQ(ids[0], // US Qwerty 427 manager_->GetCurrentInputMethod().id()); 428 EXPECT_EQ("us", xkeyboard_->last_layout_); 429 manager_->RemoveObserver(&observer); 430 } 431 432 TEST_F(InputMethodManagerImplTest, TestEnableLayoutAndIme) { 433 // For http://crbug.com/19655#c11 - (10). 434 TestObserver observer; 435 manager_->AddObserver(&observer); 436 InitComponentExtension(); 437 InitIBusBus(); 438 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 439 std::vector<std::string> ids; 440 ids.push_back("xkb:us:dvorak:eng"); 441 ids.push_back(nacl_mozc_us_id); 442 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 443 EXPECT_EQ(1, mock_ibus_daemon_controller_->start_count()); 444 EXPECT_EQ(1, observer.input_method_changed_count_); 445 EXPECT_EQ(ids[0], manager_->GetCurrentInputMethod().id()); 446 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 447 // Switch to Mozc 448 manager_->SwitchToNextInputMethod(); 449 EXPECT_EQ(2, observer.input_method_changed_count_); 450 EXPECT_EQ(ids[1], manager_->GetCurrentInputMethod().id()); 451 EXPECT_EQ("us", xkeyboard_->last_layout_); 452 // Disable Mozc. 453 ids.erase(ids.begin() + 1); 454 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 455 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 456 EXPECT_EQ(ids[0], manager_->GetCurrentInputMethod().id()); 457 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 458 // Currently, to work around a crash issue at crosbug.com/27051, 459 // controller_->Stop(); is NOT called when all IMEs are disabled 460 // or on shutdown. 461 EXPECT_EQ(0, mock_ibus_daemon_controller_->stop_count()); 462 463 manager_->SetState(InputMethodManager::STATE_TERMINATING); 464 EXPECT_EQ(0, mock_ibus_daemon_controller_->stop_count()); 465 manager_->RemoveObserver(&observer); 466 } 467 468 TEST_F(InputMethodManagerImplTest, TestEnableLayoutAndIme2) { 469 // For http://crbug.com/19655#c11 - (11). 470 TestObserver observer; 471 manager_->AddObserver(&observer); 472 InitComponentExtension(); 473 InitIBusBus(); 474 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 475 std::vector<std::string> ids; 476 ids.push_back("xkb:us:dvorak:eng"); 477 ids.push_back(nacl_mozc_us_id); 478 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 479 EXPECT_EQ(1, mock_ibus_daemon_controller_->start_count()); 480 EXPECT_EQ(1, observer.input_method_changed_count_); 481 EXPECT_EQ(ids[0], manager_->GetCurrentInputMethod().id()); 482 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 483 484 // Disable Dvorak. 485 ids.erase(ids.begin()); 486 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 487 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 488 EXPECT_EQ(ids[0], // Mozc 489 manager_->GetCurrentInputMethod().id()); 490 EXPECT_EQ("us", xkeyboard_->last_layout_); 491 manager_->RemoveObserver(&observer); 492 } 493 494 TEST_F(InputMethodManagerImplTest, TestEnableImes) { 495 TestObserver observer; 496 manager_->AddObserver(&observer); 497 InitComponentExtension(); 498 InitIBusBus(); 499 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 500 std::vector<std::string> ids; 501 ids.push_back("_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabext2_engine1_engine_id"); 502 ids.push_back("mozc-dv"); 503 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 504 EXPECT_EQ(1, mock_ibus_daemon_controller_->start_count()); 505 EXPECT_EQ(1, observer.input_method_changed_count_); 506 EXPECT_EQ(ids[0], manager_->GetCurrentInputMethod().id()); 507 EXPECT_EQ("us", xkeyboard_->last_layout_); 508 manager_->RemoveObserver(&observer); 509 } 510 511 TEST_F(InputMethodManagerImplTest, TestEnableUnknownIds) { 512 TestObserver observer; 513 manager_->AddObserver(&observer); 514 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 515 std::vector<std::string> ids; 516 ids.push_back("xkb:tl::tlh"); // Klingon, which is not supported. 517 ids.push_back("unknown-super-cool-ime"); 518 EXPECT_FALSE(manager_->EnableInputMethods(ids)); 519 520 // TODO(yusukes): Should we fall back to the hardware keyboard layout in this 521 // case? 522 EXPECT_EQ(0, observer.input_method_changed_count_); 523 524 manager_->RemoveObserver(&observer); 525 } 526 527 TEST_F(InputMethodManagerImplTest, TestEnableLayoutsThenLock) { 528 // For http://crbug.com/19655#c11 - (14). 529 TestObserver observer; 530 manager_->AddObserver(&observer); 531 InitComponentExtension(); 532 InitIBusBus(); 533 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 534 std::vector<std::string> ids; 535 ids.push_back("xkb:us::eng"); 536 ids.push_back("xkb:us:dvorak:eng"); 537 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 538 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 539 EXPECT_EQ(1, observer.input_method_changed_count_); 540 EXPECT_EQ(ids[0], manager_->GetCurrentInputMethod().id()); 541 EXPECT_EQ("us", xkeyboard_->last_layout_); 542 543 // Switch to Dvorak. 544 manager_->SwitchToNextInputMethod(); 545 EXPECT_EQ(2, observer.input_method_changed_count_); 546 EXPECT_EQ(ids[1], manager_->GetCurrentInputMethod().id()); 547 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 548 549 // Lock screen 550 manager_->SetState(InputMethodManager::STATE_LOCK_SCREEN); 551 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 552 EXPECT_EQ(ids[1], // still Dvorak 553 manager_->GetCurrentInputMethod().id()); 554 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 555 // Switch back to Qwerty. 556 manager_->SwitchToNextInputMethod(); 557 EXPECT_EQ(ids[0], manager_->GetCurrentInputMethod().id()); 558 EXPECT_EQ("us", xkeyboard_->last_layout_); 559 560 // Unlock screen. The original state, Dvorak, is restored. 561 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 562 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 563 EXPECT_EQ(ids[1], manager_->GetCurrentInputMethod().id()); 564 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 565 566 manager_->RemoveObserver(&observer); 567 } 568 569 TEST_F(InputMethodManagerImplTest, SwithchInputMethodTest) { 570 // For http://crbug.com/19655#c11 - (15). 571 TestObserver observer; 572 manager_->AddObserver(&observer); 573 InitComponentExtension(); 574 InitIBusBus(); 575 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 576 std::vector<std::string> ids; 577 ids.push_back("xkb:us:dvorak:eng"); 578 ids.push_back("_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabext2_engine2_engine_id"); 579 ids.push_back("_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabext2_engine1_engine_id"); 580 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 581 EXPECT_EQ(3U, manager_->GetNumActiveInputMethods()); 582 EXPECT_EQ(1, observer.input_method_changed_count_); 583 EXPECT_EQ(ids[0], manager_->GetCurrentInputMethod().id()); 584 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 585 586 // Switch to Mozc. 587 manager_->SwitchToNextInputMethod(); 588 EXPECT_EQ(2, observer.input_method_changed_count_); 589 EXPECT_EQ(ids[1], manager_->GetCurrentInputMethod().id()); 590 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 591 592 // Lock screen 593 manager_->SetState(InputMethodManager::STATE_LOCK_SCREEN); 594 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); // Qwerty+Dvorak. 595 EXPECT_EQ("xkb:us:dvorak:eng", 596 manager_->GetCurrentInputMethod().id()); 597 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 598 // controller_->Stop() should never be called when the screen is locked even 599 // after crosbug.com/27051 is fixed. 600 EXPECT_EQ(0, mock_ibus_daemon_controller_->stop_count()); 601 manager_->SwitchToNextInputMethod(); 602 EXPECT_EQ("xkb:us::eng", // The hardware keyboard layout. 603 manager_->GetCurrentInputMethod().id()); 604 EXPECT_EQ("us", xkeyboard_->last_layout_); 605 606 // Unlock screen. The original state, pinyin-dv, is restored. 607 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 608 EXPECT_EQ(3U, manager_->GetNumActiveInputMethods()); // Dvorak and 2 IMEs. 609 EXPECT_EQ(ids[1], manager_->GetCurrentInputMethod().id()); 610 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 611 612 manager_->RemoveObserver(&observer); 613 } 614 615 TEST_F(InputMethodManagerImplTest, TestXkbSetting) { 616 // For http://crbug.com/19655#c11 - (8), step 7-11. 617 InitComponentExtension(); 618 InitIBusBus(); 619 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 620 std::vector<std::string> ids; 621 ids.push_back("xkb:us:dvorak:eng"); 622 ids.push_back("xkb:us:colemak:eng"); 623 ids.push_back(nacl_mozc_jp_id); 624 ids.push_back(nacl_mozc_us_id); 625 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 626 EXPECT_EQ(4U, manager_->GetNumActiveInputMethods()); 627 EXPECT_EQ(1, xkeyboard_->set_current_keyboard_layout_by_name_count_); 628 // See input_methods.txt for an expected XKB layout name. 629 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 630 manager_->SwitchToNextInputMethod(); 631 EXPECT_EQ(2, xkeyboard_->set_current_keyboard_layout_by_name_count_); 632 EXPECT_EQ("us(colemak)", xkeyboard_->last_layout_); 633 manager_->SwitchToNextInputMethod(); 634 EXPECT_EQ(3, xkeyboard_->set_current_keyboard_layout_by_name_count_); 635 EXPECT_EQ("jp", xkeyboard_->last_layout_); 636 manager_->SwitchToNextInputMethod(); 637 EXPECT_EQ(4, xkeyboard_->set_current_keyboard_layout_by_name_count_); 638 EXPECT_EQ("us", xkeyboard_->last_layout_); 639 manager_->SwitchToNextInputMethod(); 640 EXPECT_EQ(5, xkeyboard_->set_current_keyboard_layout_by_name_count_); 641 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 642 // Disable Dvorak. 643 ids.erase(ids.begin()); 644 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 645 EXPECT_EQ(3U, manager_->GetNumActiveInputMethods()); 646 EXPECT_EQ(6, xkeyboard_->set_current_keyboard_layout_by_name_count_); 647 EXPECT_EQ("us(colemak)", xkeyboard_->last_layout_); 648 } 649 650 TEST_F(InputMethodManagerImplTest, TestActivateInputMethodProperty) { 651 manager_->ActivateInputMethodProperty("key"); 652 EXPECT_EQ(1, controller_->activate_input_method_property_count_); 653 EXPECT_EQ("key", controller_->activate_input_method_property_key_); 654 manager_->ActivateInputMethodProperty("key2"); 655 EXPECT_EQ(2, controller_->activate_input_method_property_count_); 656 EXPECT_EQ("key2", controller_->activate_input_method_property_key_); 657 } 658 659 TEST_F(InputMethodManagerImplTest, TestGetCurrentInputMethodProperties) { 660 InitComponentExtension(); 661 InitIBusBus(); 662 EXPECT_TRUE(manager_->GetCurrentInputMethodProperties().empty()); 663 664 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 665 std::vector<std::string> ids; 666 ids.push_back("xkb:us::eng"); 667 ids.push_back(nacl_mozc_us_id); 668 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 669 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 670 EXPECT_TRUE(manager_->GetCurrentInputMethodProperties().empty()); 671 manager_->ChangeInputMethod(nacl_mozc_us_id); 672 673 InputMethodPropertyList current_property_list; 674 current_property_list.push_back(InputMethodProperty("key", 675 "label", 676 false, 677 false)); 678 controller_->SetCurrentPropertiesForTesting(current_property_list); 679 controller_->NotifyPropertyChangedForTesting(); 680 681 ASSERT_EQ(1U, manager_->GetCurrentInputMethodProperties().size()); 682 EXPECT_EQ("key", manager_->GetCurrentInputMethodProperties().at(0).key); 683 684 manager_->ChangeInputMethod("xkb:us::eng"); 685 EXPECT_TRUE(manager_->GetCurrentInputMethodProperties().empty()); 686 687 // Delayed asynchronous property update signal from the Mozc IME. 688 controller_->NotifyPropertyChangedForTesting(); 689 // When XKB layout is in use, GetCurrentInputMethodProperties() should always 690 // return an empty list. 691 EXPECT_TRUE(manager_->GetCurrentInputMethodProperties().empty()); 692 } 693 694 TEST_F(InputMethodManagerImplTest, TestGetCurrentInputMethodPropertiesTwoImes) { 695 InitComponentExtension(); 696 InitIBusBus(); 697 EXPECT_TRUE(manager_->GetCurrentInputMethodProperties().empty()); 698 699 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 700 std::vector<std::string> ids; 701 ids.push_back(nacl_mozc_us_id); // Japanese 702 ids.push_back("_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabext2_engine1_engine_id"); // T-Chinese 703 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 704 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 705 EXPECT_TRUE(manager_->GetCurrentInputMethodProperties().empty()); 706 707 InputMethodPropertyList current_property_list; 708 current_property_list.push_back(InputMethodProperty("key-mozc", 709 "label", 710 false, 711 false)); 712 controller_->SetCurrentPropertiesForTesting(current_property_list); 713 controller_->NotifyPropertyChangedForTesting(); 714 715 ASSERT_EQ(1U, manager_->GetCurrentInputMethodProperties().size()); 716 EXPECT_EQ("key-mozc", manager_->GetCurrentInputMethodProperties().at(0).key); 717 718 manager_->ChangeInputMethod("_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabext2_engine1_engine_id"); 719 // Since the IME is changed, the property for mozc Japanese should be hidden. 720 EXPECT_TRUE(manager_->GetCurrentInputMethodProperties().empty()); 721 722 // Asynchronous property update signal from mozc-chewing. 723 current_property_list.clear(); 724 current_property_list.push_back(InputMethodProperty("key-chewing", 725 "label", 726 false, 727 false)); 728 controller_->SetCurrentPropertiesForTesting(current_property_list); 729 controller_->NotifyPropertyChangedForTesting(); 730 ASSERT_EQ(1U, manager_->GetCurrentInputMethodProperties().size()); 731 EXPECT_EQ("key-chewing", 732 manager_->GetCurrentInputMethodProperties().at(0).key); 733 } 734 735 TEST_F(InputMethodManagerImplTest, TestNextInputMethod) { 736 TestObserver observer; 737 manager_->AddObserver(&observer); 738 InitComponentExtension(); 739 InitIBusBus(); 740 // For http://crbug.com/19655#c11 - (1) 741 manager_->EnableLayouts("en-US", "xkb:us::eng"); 742 EXPECT_EQ(5U, manager_->GetNumActiveInputMethods()); 743 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 744 EXPECT_EQ("us", xkeyboard_->last_layout_); 745 manager_->SwitchToNextInputMethod(); 746 EXPECT_TRUE(observer.last_show_message_); 747 EXPECT_EQ("xkb:us:intl:eng", manager_->GetCurrentInputMethod().id()); 748 EXPECT_EQ("us(intl)", xkeyboard_->last_layout_); 749 manager_->SwitchToNextInputMethod(); 750 EXPECT_TRUE(observer.last_show_message_); 751 EXPECT_EQ("xkb:us:altgr-intl:eng", manager_->GetCurrentInputMethod().id()); 752 EXPECT_EQ("us(altgr-intl)", xkeyboard_->last_layout_); 753 manager_->SwitchToNextInputMethod(); 754 EXPECT_TRUE(observer.last_show_message_); 755 EXPECT_EQ("xkb:us:dvorak:eng", manager_->GetCurrentInputMethod().id()); 756 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 757 manager_->SwitchToNextInputMethod(); 758 EXPECT_TRUE(observer.last_show_message_); 759 EXPECT_EQ("xkb:us:colemak:eng", manager_->GetCurrentInputMethod().id()); 760 EXPECT_EQ("us(colemak)", xkeyboard_->last_layout_); 761 manager_->SwitchToNextInputMethod(); 762 EXPECT_TRUE(observer.last_show_message_); 763 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 764 EXPECT_EQ("us", xkeyboard_->last_layout_); 765 766 manager_->RemoveObserver(&observer); 767 } 768 769 TEST_F(InputMethodManagerImplTest, TestPreviousInputMethod) { 770 TestObserver observer; 771 manager_->AddObserver(&observer); 772 InitComponentExtension(); 773 InitIBusBus(); 774 775 ui::Accelerator keydown_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN); 776 keydown_accelerator.set_type(ui::ET_KEY_PRESSED); 777 ui::Accelerator keyup_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN); 778 keyup_accelerator.set_type(ui::ET_KEY_RELEASED); 779 780 manager_->EnableLayouts("en-US", "xkb:us::eng"); 781 EXPECT_EQ(5U, manager_->GetNumActiveInputMethods()); 782 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 783 EXPECT_EQ("us", xkeyboard_->last_layout_); 784 EXPECT_TRUE(manager_->SwitchToNextInputMethod()); 785 EXPECT_TRUE(observer.last_show_message_); 786 EXPECT_EQ("xkb:us:intl:eng", manager_->GetCurrentInputMethod().id()); 787 EXPECT_EQ("us(intl)", xkeyboard_->last_layout_); 788 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keydown_accelerator)); 789 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keyup_accelerator)); 790 EXPECT_TRUE(observer.last_show_message_); 791 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 792 EXPECT_EQ("us", xkeyboard_->last_layout_); 793 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keydown_accelerator)); 794 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keyup_accelerator)); 795 EXPECT_TRUE(observer.last_show_message_); 796 EXPECT_EQ("xkb:us:intl:eng", manager_->GetCurrentInputMethod().id()); 797 EXPECT_EQ("us(intl)", xkeyboard_->last_layout_); 798 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keydown_accelerator)); 799 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keyup_accelerator)); 800 EXPECT_TRUE(observer.last_show_message_); 801 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 802 EXPECT_EQ("us", xkeyboard_->last_layout_); 803 EXPECT_TRUE(manager_->SwitchToNextInputMethod()); 804 EXPECT_TRUE(observer.last_show_message_); 805 EXPECT_EQ("xkb:us:intl:eng", manager_->GetCurrentInputMethod().id()); 806 EXPECT_EQ("us(intl)", xkeyboard_->last_layout_); 807 EXPECT_TRUE(manager_->SwitchToNextInputMethod()); 808 EXPECT_TRUE(observer.last_show_message_); 809 EXPECT_EQ("xkb:us:altgr-intl:eng", manager_->GetCurrentInputMethod().id()); 810 EXPECT_EQ("us(altgr-intl)", xkeyboard_->last_layout_); 811 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keydown_accelerator)); 812 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keyup_accelerator)); 813 EXPECT_TRUE(observer.last_show_message_); 814 EXPECT_EQ("xkb:us:intl:eng", manager_->GetCurrentInputMethod().id()); 815 EXPECT_EQ("us(intl)", xkeyboard_->last_layout_); 816 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keydown_accelerator)); 817 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keyup_accelerator)); 818 EXPECT_TRUE(observer.last_show_message_); 819 EXPECT_EQ("xkb:us:altgr-intl:eng", manager_->GetCurrentInputMethod().id()); 820 EXPECT_EQ("us(altgr-intl)", xkeyboard_->last_layout_); 821 822 manager_->RemoveObserver(&observer); 823 } 824 825 TEST_F(InputMethodManagerImplTest, 826 TestSwitchToPreviousInputMethodForOneActiveInputMethod) { 827 TestObserver observer; 828 manager_->AddObserver(&observer); 829 InitComponentExtension(); 830 InitIBusBus(); 831 832 ui::Accelerator keydown_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN); 833 keydown_accelerator.set_type(ui::ET_KEY_PRESSED); 834 ui::Accelerator keyup_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN); 835 keyup_accelerator.set_type(ui::ET_KEY_RELEASED); 836 837 std::vector<std::string> ids; 838 ids.push_back("xkb:us:dvorak:eng"); 839 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 840 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 841 842 // Ctrl+Space accelerator should not be consumed if there is only one active 843 // input method. 844 EXPECT_FALSE(manager_->SwitchToPreviousInputMethod(keydown_accelerator)); 845 EXPECT_FALSE(manager_->SwitchToPreviousInputMethod(keyup_accelerator)); 846 847 manager_->RemoveObserver(&observer); 848 } 849 850 TEST_F(InputMethodManagerImplTest, TestSwitchInputMethodWithUsLayouts) { 851 TestObserver observer; 852 manager_->AddObserver(&observer); 853 InitComponentExtension(); 854 InitIBusBus(); 855 manager_->EnableLayouts("en-US", "xkb:us::eng"); 856 EXPECT_EQ(5U, manager_->GetNumActiveInputMethods()); 857 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 858 EXPECT_EQ("us", xkeyboard_->last_layout_); 859 860 // Henkan, Muhenkan, ZenkakuHankaku should be ignored when no Japanese IMEs 861 // and keyboards are enabled. 862 EXPECT_FALSE(manager_->SwitchInputMethod( 863 ui::Accelerator(ui::VKEY_CONVERT, ui::EF_NONE))); 864 EXPECT_FALSE(observer.last_show_message_); 865 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 866 EXPECT_EQ("us", xkeyboard_->last_layout_); 867 EXPECT_FALSE(manager_->SwitchInputMethod( 868 ui::Accelerator(ui::VKEY_NONCONVERT, ui::EF_NONE))); 869 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 870 EXPECT_EQ("us", xkeyboard_->last_layout_); 871 EXPECT_FALSE(manager_->SwitchInputMethod( 872 ui::Accelerator(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE))); 873 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 874 EXPECT_EQ("us", xkeyboard_->last_layout_); 875 EXPECT_FALSE(manager_->SwitchInputMethod( 876 ui::Accelerator(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE))); 877 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 878 EXPECT_EQ("us", xkeyboard_->last_layout_); 879 880 manager_->RemoveObserver(&observer); 881 } 882 883 TEST_F(InputMethodManagerImplTest, TestSwitchInputMethodWithJpLayout) { 884 // Enable "xkb:jp::jpn" and press Muhenkan/ZenkakuHankaku. 885 InitComponentExtension(); 886 InitIBusBus(); 887 888 ui::Accelerator keydown_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN); 889 keydown_accelerator.set_type(ui::ET_KEY_PRESSED); 890 ui::Accelerator keyup_accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN); 891 keyup_accelerator.set_type(ui::ET_KEY_RELEASED); 892 893 manager_->EnableLayouts("ja", "xkb:us::eng"); 894 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 895 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 896 EXPECT_EQ("us", xkeyboard_->last_layout_); 897 EXPECT_TRUE(manager_->SwitchInputMethod( 898 ui::Accelerator(ui::VKEY_NONCONVERT, ui::EF_NONE))); 899 EXPECT_EQ("xkb:jp::jpn", manager_->GetCurrentInputMethod().id()); 900 EXPECT_EQ("jp", xkeyboard_->last_layout_); 901 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keydown_accelerator)); 902 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keyup_accelerator)); 903 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 904 EXPECT_EQ("us", xkeyboard_->last_layout_); 905 EXPECT_TRUE(manager_->SwitchInputMethod( 906 ui::Accelerator(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE))); 907 EXPECT_EQ("xkb:jp::jpn", manager_->GetCurrentInputMethod().id()); 908 EXPECT_EQ("jp", xkeyboard_->last_layout_); 909 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keydown_accelerator)); 910 EXPECT_TRUE(manager_->SwitchToPreviousInputMethod(keyup_accelerator)); 911 EXPECT_EQ("xkb:us::eng", manager_->GetCurrentInputMethod().id()); 912 EXPECT_EQ("us", xkeyboard_->last_layout_); 913 EXPECT_TRUE(manager_->SwitchInputMethod( 914 ui::Accelerator(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE))); 915 EXPECT_EQ("xkb:jp::jpn", manager_->GetCurrentInputMethod().id()); 916 EXPECT_EQ("jp", xkeyboard_->last_layout_); 917 } 918 919 TEST_F(InputMethodManagerImplTest, TestSwitchInputMethodWithJpIme) { 920 InitComponentExtension(); 921 InitIBusBus(); 922 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 923 std::vector<std::string> ids; 924 ids.push_back("xkb:jp::jpn"); 925 ids.push_back(nacl_mozc_jp_id); 926 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 927 EXPECT_EQ("xkb:jp::jpn", manager_->GetCurrentInputMethod().id()); 928 EXPECT_EQ("jp", xkeyboard_->last_layout_); 929 EXPECT_TRUE(manager_->SwitchInputMethod( 930 ui::Accelerator(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE))); 931 EXPECT_EQ(nacl_mozc_jp_id, manager_->GetCurrentInputMethod().id()); 932 EXPECT_EQ("jp", xkeyboard_->last_layout_); 933 EXPECT_TRUE(manager_->SwitchInputMethod( 934 ui::Accelerator(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE))); 935 EXPECT_EQ("xkb:jp::jpn", manager_->GetCurrentInputMethod().id()); 936 EXPECT_EQ("jp", xkeyboard_->last_layout_); 937 EXPECT_TRUE(manager_->SwitchInputMethod( 938 ui::Accelerator(ui::VKEY_CONVERT, ui::EF_NONE))); 939 EXPECT_EQ(nacl_mozc_jp_id, manager_->GetCurrentInputMethod().id()); 940 EXPECT_EQ("jp", xkeyboard_->last_layout_); 941 EXPECT_TRUE(manager_->SwitchInputMethod( 942 ui::Accelerator(ui::VKEY_CONVERT, ui::EF_NONE))); 943 EXPECT_EQ(nacl_mozc_jp_id, manager_->GetCurrentInputMethod().id()); 944 EXPECT_EQ("jp", xkeyboard_->last_layout_); 945 EXPECT_TRUE(manager_->SwitchInputMethod( 946 ui::Accelerator(ui::VKEY_NONCONVERT, ui::EF_NONE))); 947 EXPECT_EQ("xkb:jp::jpn", manager_->GetCurrentInputMethod().id()); 948 EXPECT_EQ("jp", xkeyboard_->last_layout_); 949 EXPECT_TRUE(manager_->SwitchInputMethod( 950 ui::Accelerator(ui::VKEY_NONCONVERT, ui::EF_NONE))); 951 EXPECT_EQ("xkb:jp::jpn", manager_->GetCurrentInputMethod().id()); 952 EXPECT_EQ("jp", xkeyboard_->last_layout_); 953 954 // Add Dvorak. 955 ids.push_back("xkb:us:dvorak:eng"); 956 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 957 EXPECT_EQ("xkb:jp::jpn", manager_->GetCurrentInputMethod().id()); 958 EXPECT_EQ("jp", xkeyboard_->last_layout_); 959 EXPECT_TRUE(manager_->SwitchInputMethod( 960 ui::Accelerator(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE))); 961 EXPECT_EQ(nacl_mozc_jp_id, manager_->GetCurrentInputMethod().id()); 962 EXPECT_EQ("jp", xkeyboard_->last_layout_); 963 EXPECT_TRUE(manager_->SwitchInputMethod( 964 ui::Accelerator(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE))); 965 EXPECT_EQ("xkb:jp::jpn", manager_->GetCurrentInputMethod().id()); 966 EXPECT_EQ("jp", xkeyboard_->last_layout_); 967 } 968 969 TEST_F(InputMethodManagerImplTest, TestAddRemoveExtensionInputMethods) { 970 TestObserver observer; 971 manager_->AddObserver(&observer); 972 InitComponentExtension(); 973 InitIBusBus(); 974 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 975 std::vector<std::string> ids; 976 ids.push_back("xkb:us:dvorak:eng"); 977 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 978 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 979 EXPECT_EQ(0, mock_ibus_daemon_controller_->start_count()); 980 EXPECT_EQ(1, observer.input_method_changed_count_); 981 EXPECT_EQ(ids[0], 982 manager_->GetCurrentInputMethod().id()); 983 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 984 985 // Add two Extension IMEs. 986 std::vector<std::string> layouts; 987 layouts.push_back("us"); 988 std::vector<std::string> languages; 989 languages.push_back("en-US"); 990 manager_->AddInputMethodExtension( 991 extension_ime_util::GetInputMethodID("deadbeef", "engine_id"), 992 "deadbeef input method", 993 layouts, 994 languages, 995 GURL(), 996 NULL); 997 998 // Extension IMEs are not enabled by default. 999 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 1000 1001 std::vector<std::string> extension_ime_ids; 1002 extension_ime_ids.push_back( 1003 extension_ime_util::GetInputMethodID("deadbeef", "engine_id")); 1004 manager_->SetEnabledExtensionImes(&extension_ime_ids); 1005 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 1006 1007 // should be started. 1008 EXPECT_EQ(1, mock_ibus_daemon_controller_->start_count()); 1009 { 1010 scoped_ptr<InputMethodDescriptors> methods( 1011 manager_->GetActiveInputMethods()); 1012 ASSERT_EQ(2U, methods->size()); 1013 EXPECT_EQ(extension_ime_util::GetInputMethodID("deadbeef", "engine_id"), 1014 // Ext IMEs should be at the end of the list. 1015 methods->at(1).id()); 1016 } 1017 manager_->AddInputMethodExtension( 1018 extension_ime_util::GetInputMethodID("cafebabe", "engine_id"), 1019 "cafebabe input method", 1020 layouts, 1021 languages, 1022 GURL(), 1023 NULL); 1024 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 1025 1026 extension_ime_ids.push_back( 1027 extension_ime_util::GetInputMethodID("cafebabe", "engine_id")); 1028 manager_->SetEnabledExtensionImes(&extension_ime_ids); 1029 EXPECT_EQ(3U, manager_->GetNumActiveInputMethods()); 1030 { 1031 scoped_ptr<InputMethodDescriptors> methods( 1032 manager_->GetActiveInputMethods()); 1033 ASSERT_EQ(3U, methods->size()); 1034 EXPECT_EQ(extension_ime_util::GetInputMethodID("deadbeef", "engine_id"), 1035 // Ext IMEs should be at the end of the list. 1036 methods->at(1).id()); 1037 } 1038 1039 // Remove them. 1040 manager_->RemoveInputMethodExtension( 1041 extension_ime_util::GetInputMethodID("deadbeef", "engine_id")); 1042 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 1043 manager_->RemoveInputMethodExtension( 1044 extension_ime_util::GetInputMethodID("cafebabe", "engine_id")); 1045 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 1046 // Currently, to work around a crash issue at crosbug.com/27051, 1047 // controller_->Stop(); is NOT called when all (extension) IMEs are disabled. 1048 EXPECT_EQ(0, mock_ibus_daemon_controller_->stop_count()); 1049 1050 manager_->RemoveObserver(&observer); 1051 } 1052 1053 TEST_F(InputMethodManagerImplTest, TestAddExtensionInputThenLockScreen) { 1054 TestObserver observer; 1055 InitComponentExtension(); 1056 InitIBusBus(); 1057 manager_->AddObserver(&observer); 1058 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 1059 std::vector<std::string> ids; 1060 ids.push_back("xkb:us::eng"); 1061 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 1062 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 1063 EXPECT_EQ(1, observer.input_method_changed_count_); 1064 EXPECT_EQ(ids[0], manager_->GetCurrentInputMethod().id()); 1065 EXPECT_EQ("us", xkeyboard_->last_layout_); 1066 1067 // Add an Extension IME. 1068 std::vector<std::string> layouts; 1069 layouts.push_back("us(dvorak)"); 1070 std::vector<std::string> languages; 1071 languages.push_back("en-US"); 1072 manager_->AddInputMethodExtension( 1073 extension_ime_util::GetInputMethodID("deadbeef", "engine_id"), 1074 "deadbeef input method", 1075 layouts, 1076 languages, 1077 GURL(), 1078 NULL); 1079 // Extension IME is not enabled by default. 1080 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 1081 EXPECT_EQ(1, observer.input_method_changed_count_); 1082 1083 std::vector<std::string> extension_ime_ids; 1084 extension_ime_ids.push_back( 1085 extension_ime_util::GetInputMethodID("deadbeef", "engine_id")); 1086 manager_->SetEnabledExtensionImes(&extension_ime_ids); 1087 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 1088 1089 // Switch to the IME. 1090 manager_->SwitchToNextInputMethod(); 1091 EXPECT_EQ(3, observer.input_method_changed_count_); 1092 EXPECT_EQ(extension_ime_util::GetInputMethodID("deadbeef", "engine_id"), 1093 manager_->GetCurrentInputMethod().id()); 1094 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 1095 1096 // Lock the screen. This is for crosbug.com/27049. 1097 manager_->SetState(InputMethodManager::STATE_LOCK_SCREEN); 1098 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); // Qwerty. No Ext. IME 1099 EXPECT_EQ("xkb:us::eng", 1100 manager_->GetCurrentInputMethod().id()); 1101 EXPECT_EQ("us", xkeyboard_->last_layout_); 1102 EXPECT_EQ(0, mock_ibus_daemon_controller_->stop_count()); 1103 1104 // Unlock the screen. 1105 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 1106 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 1107 EXPECT_EQ(extension_ime_util::GetInputMethodID("deadbeef", "engine_id"), 1108 manager_->GetCurrentInputMethod().id()); 1109 EXPECT_EQ("us(dvorak)", xkeyboard_->last_layout_); 1110 { 1111 // This is for crosbug.com/27052. 1112 scoped_ptr<InputMethodDescriptors> methods( 1113 manager_->GetActiveInputMethods()); 1114 ASSERT_EQ(2U, methods->size()); 1115 EXPECT_EQ(extension_ime_util::GetInputMethodID("deadbeef", "engine_id"), 1116 // Ext. IMEs should be at the end of the list. 1117 methods->at(1).id()); 1118 } 1119 manager_->RemoveObserver(&observer); 1120 } 1121 1122 TEST_F(InputMethodManagerImplTest, TestReset) { 1123 InitComponentExtension(); 1124 InitIBusBus(); 1125 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 1126 std::vector<std::string> ids; 1127 ids.push_back("xkb:us::eng"); 1128 ids.push_back(nacl_mozc_us_id); 1129 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 1130 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 1131 EXPECT_EQ(1, mock_ibus_input_context_client_->reset_call_count()); 1132 manager_->ChangeInputMethod(nacl_mozc_us_id); 1133 EXPECT_EQ(1, mock_ibus_client_->set_global_engine_call_count()); 1134 EXPECT_EQ(nacl_mozc_us_id, mock_ibus_client_->latest_global_engine_name()); 1135 EXPECT_EQ(1, mock_ibus_input_context_client_->reset_call_count()); 1136 manager_->ChangeInputMethod("xkb:us::eng"); 1137 EXPECT_EQ(2, mock_ibus_client_->set_global_engine_call_count()); 1138 EXPECT_EQ(nacl_mozc_us_id, mock_ibus_client_->latest_global_engine_name()); 1139 EXPECT_EQ(1, mock_ibus_input_context_client_->reset_call_count()); 1140 } 1141 1142 TEST_F(InputMethodManagerImplTest, 1143 ChangeInputMethodBeforeComponentExtensionInitialization_OneIME) { 1144 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 1145 std::vector<std::string> ids; 1146 ids.push_back(nacl_mozc_us_id); 1147 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 1148 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 1149 manager_->ChangeInputMethod(nacl_mozc_us_id); 1150 1151 InitIBusBus(); 1152 InitComponentExtension(); 1153 EXPECT_EQ(1, mock_ibus_client_->set_global_engine_call_count()); 1154 EXPECT_EQ(nacl_mozc_us_id, mock_ibus_client_->latest_global_engine_name()); 1155 } 1156 1157 TEST_F(InputMethodManagerImplTest, 1158 ChangeInputMethodBeforeComponentExtensionInitialization_TwoIME) { 1159 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 1160 std::vector<std::string> ids; 1161 ids.push_back(nacl_mozc_us_id); 1162 ids.push_back(nacl_mozc_jp_id); 1163 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 1164 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 1165 manager_->ChangeInputMethod(nacl_mozc_us_id); 1166 manager_->ChangeInputMethod(nacl_mozc_jp_id); 1167 1168 InitComponentExtension(); 1169 InitIBusBus(); 1170 EXPECT_EQ(1, mock_ibus_client_->set_global_engine_call_count()); 1171 EXPECT_EQ(nacl_mozc_jp_id, mock_ibus_client_->latest_global_engine_name()); 1172 } 1173 1174 TEST_F(InputMethodManagerImplTest, 1175 ChangeInputMethodBeforeComponentExtensionInitialization_CompOneIME) { 1176 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 1177 const std::string ext_id = 1178 TestableComponentExtensionIMEManager::GetComponentExtensionIMEId( 1179 ime_list_[0].id, 1180 ime_list_[0].engines[0].engine_id); 1181 std::vector<std::string> ids; 1182 ids.push_back(ext_id); 1183 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 1184 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 1185 manager_->ChangeInputMethod(ext_id); 1186 1187 InitComponentExtension(); 1188 InitIBusBus(); 1189 EXPECT_EQ(1, mock_ibus_client_->set_global_engine_call_count()); 1190 EXPECT_EQ(ext_id, mock_ibus_client_->latest_global_engine_name()); 1191 } 1192 1193 TEST_F(InputMethodManagerImplTest, 1194 ChangeInputMethodBeforeComponentExtensionInitialization_CompTwoIME) { 1195 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 1196 const std::string ext_id1 = 1197 TestableComponentExtensionIMEManager::GetComponentExtensionIMEId( 1198 ime_list_[0].id, 1199 ime_list_[0].engines[0].engine_id); 1200 const std::string ext_id2 = 1201 TestableComponentExtensionIMEManager::GetComponentExtensionIMEId( 1202 ime_list_[1].id, 1203 ime_list_[1].engines[0].engine_id); 1204 std::vector<std::string> ids; 1205 ids.push_back(ext_id1); 1206 ids.push_back(ext_id2); 1207 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 1208 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 1209 manager_->ChangeInputMethod(ext_id1); 1210 manager_->ChangeInputMethod(ext_id2); 1211 1212 InitComponentExtension(); 1213 InitIBusBus(); 1214 EXPECT_EQ(1, mock_ibus_client_->set_global_engine_call_count()); 1215 EXPECT_EQ(ext_id2, mock_ibus_client_->latest_global_engine_name()); 1216 } 1217 1218 TEST_F(InputMethodManagerImplTest, 1219 ChangeInputMethod_ComponenteExtensionOneIME) { 1220 InitComponentExtension(); 1221 InitIBusBus(); 1222 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 1223 const std::string ext_id = 1224 TestableComponentExtensionIMEManager::GetComponentExtensionIMEId( 1225 ime_list_[0].id, 1226 ime_list_[0].engines[0].engine_id); 1227 std::vector<std::string> ids; 1228 ids.push_back(ext_id); 1229 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 1230 EXPECT_EQ(1U, manager_->GetNumActiveInputMethods()); 1231 EXPECT_EQ(1, mock_ibus_client_->set_global_engine_call_count()); 1232 EXPECT_EQ(ext_id, mock_ibus_client_->latest_global_engine_name()); 1233 } 1234 1235 TEST_F(InputMethodManagerImplTest, 1236 ChangeInputMethod_ComponenteExtensionTwoIME) { 1237 InitComponentExtension(); 1238 InitIBusBus(); 1239 manager_->SetState(InputMethodManager::STATE_BROWSER_SCREEN); 1240 const std::string ext_id1 = 1241 TestableComponentExtensionIMEManager::GetComponentExtensionIMEId( 1242 ime_list_[0].id, 1243 ime_list_[0].engines[0].engine_id); 1244 const std::string ext_id2 = 1245 TestableComponentExtensionIMEManager::GetComponentExtensionIMEId( 1246 ime_list_[1].id, 1247 ime_list_[1].engines[0].engine_id); 1248 std::vector<std::string> ids; 1249 ids.push_back(ext_id1); 1250 ids.push_back(ext_id2); 1251 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 1252 EXPECT_EQ(2U, manager_->GetNumActiveInputMethods()); 1253 EXPECT_EQ(1, mock_ibus_client_->set_global_engine_call_count()); 1254 EXPECT_EQ(ext_id1, mock_ibus_client_->latest_global_engine_name()); 1255 manager_->ChangeInputMethod(ext_id2); 1256 EXPECT_EQ(2, mock_ibus_client_->set_global_engine_call_count()); 1257 EXPECT_EQ(ext_id2, mock_ibus_client_->latest_global_engine_name()); 1258 } 1259 1260 TEST_F(InputMethodManagerImplTest, 1261 MigrateOldInputMethodTest) { 1262 std::vector<std::string> input_method_ids; 1263 input_method_ids.push_back("mozc"); 1264 input_method_ids.push_back("mozc-jp"); 1265 input_method_ids.push_back("xkb:us::eng"); 1266 input_method_ids.push_back(nacl_mozc_us_id); 1267 1268 manager_->MigrateOldInputMethods(&input_method_ids); 1269 1270 ASSERT_EQ(4U, input_method_ids.size()); 1271 EXPECT_EQ(input_method_ids.end(), 1272 std::find(input_method_ids.begin(), input_method_ids.end(), 1273 "mozc")); 1274 EXPECT_EQ(input_method_ids.end(), 1275 std::find(input_method_ids.begin(), input_method_ids.end(), 1276 "mozc-jp")); 1277 EXPECT_NE(input_method_ids.end(), 1278 std::find(input_method_ids.begin(), input_method_ids.end(), 1279 "xkb:us::eng")); 1280 EXPECT_NE(input_method_ids.end(), 1281 std::find(input_method_ids.begin(), input_method_ids.end(), 1282 nacl_mozc_us_id)); 1283 1284 } 1285 1286 TEST_F(InputMethodManagerImplTest, 1287 AsyncComponentExtentionInitializeBeforeIBusDaemonConnection) { 1288 const std::string xkb_id = "xkb:cz::cze"; 1289 const std::string ime_id = nacl_mozc_us_id; 1290 const std::string fallback_id = "xkb:us::eng"; 1291 std::vector<std::string> ids; 1292 ids.push_back(xkb_id); 1293 ids.push_back(ime_id); 1294 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 1295 1296 // If component extension IME is not initialized, even XKB layout cannot be 1297 // enabled. 1298 manager_->ChangeInputMethod(xkb_id); 1299 EXPECT_EQ(fallback_id, manager_->GetCurrentInputMethod().id()); 1300 1301 // After component extension IME is initialized, previous input method should 1302 // be automatically enabled. 1303 InitComponentExtension(); 1304 EXPECT_EQ(xkb_id, manager_->GetCurrentInputMethod().id()); 1305 1306 // However input method should not be enabled before establishment of 1307 // connection with ibus-daemon. 1308 manager_->ChangeInputMethod(ime_id); 1309 // TODO(nona): Write expectation, GetCurrentInputMethod returns |ime_id| even 1310 // the actual input method is not changed. 1311 1312 // After connection with ibus-daemon is established, previous specified input 1313 // method should be enabled automatically. 1314 InitIBusBus(); 1315 EXPECT_EQ(ime_id, manager_->GetCurrentInputMethod().id()); 1316 } 1317 1318 TEST_F(InputMethodManagerImplTest, 1319 AsyncComponentExtentionInitializeAfterIBusDaemonConnection) { 1320 const std::string xkb_id = "xkb:cz::cze"; 1321 const std::string ime_id = nacl_mozc_us_id; 1322 const std::string fallback_id = "xkb:us::eng"; 1323 std::vector<std::string> ids; 1324 ids.push_back(xkb_id); 1325 ids.push_back(ime_id); 1326 EXPECT_TRUE(manager_->EnableInputMethods(ids)); 1327 1328 // If component extension IME is not initialized, even XKB layout cannot be 1329 // enabled. 1330 manager_->ChangeInputMethod(xkb_id); 1331 EXPECT_EQ(fallback_id, manager_->GetCurrentInputMethod().id()); 1332 1333 // Even after connection with ibus-daemon is established, ChangeInputMethod do 1334 // nothing without component extension IME initialization. 1335 InitIBusBus(); 1336 EXPECT_EQ(fallback_id, manager_->GetCurrentInputMethod().id()); 1337 1338 // After component extension IME is initialized, previous specified input 1339 // method should be automatically enabled. 1340 InitComponentExtension(); 1341 EXPECT_EQ(xkb_id, manager_->GetCurrentInputMethod().id()); 1342 } 1343 1344 } // namespace input_method 1345 } // namespace chromeos 1346