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