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