1// Copyright 2014 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 "base/logging.h"
6#include "base/metrics/histogram.h"
7#include "base/metrics/histogram_samples.h"
8#include "base/metrics/statistics_recorder.h"
9#include "base/test/histogram_tester.h"
10#include "chrome/browser/chromeos/input_method/input_method_configuration.h"
11#include "chrome/browser/chromeos/input_method/input_method_engine.h"
12#include "chrome/browser/chromeos/input_method/input_method_engine_interface.h"
13#include "chrome/browser/chromeos/input_method/mock_input_method_manager.h"
14#include "chromeos/ime/extension_ime_util.h"
15#include "chromeos/ime/mock_component_extension_ime_manager_delegate.h"
16#include "testing/gtest/include/gtest/gtest.h"
17#include "ui/base/ime/chromeos/mock_ime_input_context_handler.h"
18
19namespace chromeos {
20
21namespace input_method {
22namespace {
23
24const char kTestExtensionId[] = "mppnpdlheglhdfmldimlhpnegondlapf";
25const char kTestExtensionId2[] = "dmpipdbjkoajgdeppkffbjhngfckdloi";
26const char kTestImeComponentId[] = "test_engine_id";
27
28enum CallsBitmap {
29  NONE = 0U,
30  ACTIVATE = 1U,
31  DEACTIVATED = 2U,
32  ONFOCUS = 4U,
33  ONBLUR = 8U
34};
35
36void InitInputMethod() {
37  ComponentExtensionIMEManager* comp_ime_manager =
38      new ComponentExtensionIMEManager;
39  MockComponentExtIMEManagerDelegate* delegate =
40      new MockComponentExtIMEManagerDelegate;
41
42  ComponentExtensionIME ext1;
43  ext1.id = kTestExtensionId;
44
45  ComponentExtensionEngine ext1_engine1;
46  ext1_engine1.engine_id = kTestImeComponentId;
47  ext1_engine1.language_codes.push_back("en-US");
48  ext1_engine1.layouts.push_back("us");
49  ext1.engines.push_back(ext1_engine1);
50
51  std::vector<ComponentExtensionIME> ime_list;
52  ime_list.push_back(ext1);
53  delegate->set_ime_list(ime_list);
54  comp_ime_manager->Initialize(
55      scoped_ptr<ComponentExtensionIMEManagerDelegate>(delegate).Pass());
56
57  MockInputMethodManager* manager = new MockInputMethodManager;
58  manager->SetComponentExtensionIMEManager(
59      scoped_ptr<ComponentExtensionIMEManager>(comp_ime_manager).Pass());
60  InitializeForTesting(manager);
61}
62
63class TestObserver : public InputMethodEngineInterface::Observer {
64 public:
65  TestObserver() : calls_bitmap_(NONE) {}
66  virtual ~TestObserver() {}
67
68  virtual void OnActivate(const std::string& engine_id) OVERRIDE {
69    calls_bitmap_ |= ACTIVATE;
70  }
71  virtual void OnDeactivated(const std::string& engine_id) OVERRIDE {
72    calls_bitmap_ |= DEACTIVATED;
73  }
74  virtual void OnFocus(
75      const InputMethodEngineInterface::InputContext& context) OVERRIDE {
76    calls_bitmap_ |= ONFOCUS;
77  }
78  virtual void OnBlur(int context_id) OVERRIDE {
79    calls_bitmap_ |= ONBLUR;
80  }
81  virtual void OnKeyEvent(
82      const std::string& engine_id,
83      const InputMethodEngineInterface::KeyboardEvent& event,
84      input_method::KeyEventHandle* key_data) OVERRIDE {}
85  virtual void OnInputContextUpdate(
86      const InputMethodEngineInterface::InputContext& context) OVERRIDE {}
87  virtual void OnCandidateClicked(
88      const std::string& engine_id,
89      int candidate_id,
90      InputMethodEngineInterface::MouseButtonEvent button) OVERRIDE {}
91  virtual void OnMenuItemActivated(
92      const std::string& engine_id,
93      const std::string& menu_id) OVERRIDE {}
94  virtual void OnSurroundingTextChanged(
95      const std::string& engine_id,
96      const std::string& text,
97      int cursor_pos,
98      int anchor_pos) OVERRIDE {}
99  virtual void OnReset(const std::string& engine_id) OVERRIDE {}
100
101  unsigned char GetCallsBitmapAndReset() {
102    unsigned char ret = calls_bitmap_;
103    calls_bitmap_ = NONE;
104    return ret;
105  }
106
107 private:
108  unsigned char calls_bitmap_;
109
110  DISALLOW_COPY_AND_ASSIGN(TestObserver);
111};
112
113class InputMethodEngineTest :  public testing::Test {
114 public:
115  InputMethodEngineTest() : observer_(NULL), input_view_("inputview.html") {
116    languages_.push_back("en-US");
117    layouts_.push_back("us");
118    InitInputMethod();
119    IMEBridge::Initialize();
120    mock_ime_input_context_handler_.reset(new MockIMEInputContextHandler());
121    IMEBridge::Get()->SetInputContextHandler(
122        mock_ime_input_context_handler_.get());
123  }
124  virtual ~InputMethodEngineTest() {
125    IMEBridge::Get()->SetInputContextHandler(NULL);
126    engine_.reset();
127    Shutdown();
128  }
129
130 protected:
131  void CreateEngine(bool whitelisted) {
132    engine_.reset(new InputMethodEngine());
133    observer_ = new TestObserver();
134    scoped_ptr<InputMethodEngineInterface::Observer> observer_ptr(observer_);
135    engine_->Initialize(observer_ptr.Pass(),
136                        whitelisted ? kTestExtensionId : kTestExtensionId2);
137  }
138
139  void FocusIn(ui::TextInputType input_type) {
140    IMEEngineHandlerInterface::InputContext input_context(
141        input_type, ui::TEXT_INPUT_MODE_DEFAULT);
142    engine_->FocusIn(input_context);
143    IMEBridge::Get()->SetCurrentTextInputType(input_type);
144  }
145
146  scoped_ptr<InputMethodEngine> engine_;
147
148  TestObserver* observer_;
149  std::vector<std::string> languages_;
150  std::vector<std::string> layouts_;
151  GURL options_page_;
152  GURL input_view_;
153
154  scoped_ptr<MockIMEInputContextHandler> mock_ime_input_context_handler_;
155
156 private:
157  DISALLOW_COPY_AND_ASSIGN(InputMethodEngineTest);
158};
159
160}  // namespace
161
162TEST_F(InputMethodEngineTest, TestSwitching) {
163  CreateEngine(false);
164  // Enable/disable with focus.
165  FocusIn(ui::TEXT_INPUT_TYPE_URL);
166  EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset());
167  engine_->Enable(kTestImeComponentId);
168  EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset());
169  engine_->Disable();
170  EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset());
171  // Enable/disable without focus.
172  engine_->FocusOut();
173  EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset());
174  engine_->Enable(kTestImeComponentId);
175  EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset());
176  engine_->Disable();
177  EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset());
178  // Focus change when enabled.
179  engine_->Enable(kTestImeComponentId);
180  EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset());
181  engine_->FocusOut();
182  EXPECT_EQ(ONBLUR, observer_->GetCallsBitmapAndReset());
183  // Focus change when disabled.
184  engine_->Disable();
185  EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset());
186  FocusIn(ui::TEXT_INPUT_TYPE_TEXT);
187  EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset());
188  engine_->FocusOut();
189  EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset());
190}
191
192TEST_F(InputMethodEngineTest, TestSwitching_Password_3rd_Party) {
193  CreateEngine(false);
194  // Enable/disable with focus.
195  FocusIn(ui::TEXT_INPUT_TYPE_PASSWORD);
196  EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset());
197  engine_->Enable(kTestImeComponentId);
198  EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset());
199  engine_->Disable();
200  EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset());
201  // Focus change when enabled.
202  engine_->Enable(kTestImeComponentId);
203  EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset());
204  engine_->FocusOut();
205  EXPECT_EQ(ONBLUR, observer_->GetCallsBitmapAndReset());
206  FocusIn(ui::TEXT_INPUT_TYPE_PASSWORD);
207  EXPECT_EQ(ONFOCUS, observer_->GetCallsBitmapAndReset());
208  engine_->Disable();
209  EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset());
210}
211
212TEST_F(InputMethodEngineTest, TestSwitching_Password_Whitelisted) {
213  CreateEngine(true);
214  // Enable/disable with focus.
215  FocusIn(ui::TEXT_INPUT_TYPE_PASSWORD);
216  EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset());
217  engine_->Enable(kTestImeComponentId);
218  EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset());
219  engine_->Disable();
220  EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset());
221  // Focus change when enabled.
222  engine_->Enable(kTestImeComponentId);
223  EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset());
224  engine_->FocusOut();
225  EXPECT_EQ(ONBLUR, observer_->GetCallsBitmapAndReset());
226  FocusIn(ui::TEXT_INPUT_TYPE_PASSWORD);
227  EXPECT_EQ(ONFOCUS, observer_->GetCallsBitmapAndReset());
228  engine_->Disable();
229  EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset());
230}
231
232TEST_F(InputMethodEngineTest, TestHistograms) {
233  CreateEngine(true);
234  FocusIn(ui::TEXT_INPUT_TYPE_TEXT);
235  engine_->Enable(kTestImeComponentId);
236  std::vector<InputMethodEngineInterface::SegmentInfo> segments;
237  engine_->SetComposition(
238      engine_->GetCotextIdForTesting(), "test", 0, 0, 0, segments, NULL);
239  std::string error;
240  base::HistogramTester histograms;
241  engine_->CommitText(1, "input", &error);
242  engine_->CommitText(1,
243                      "\xE5\x85\xA5\xE5\x8A\x9B",  // 2 UTF-8 characters
244                      &error);
245  engine_->CommitText(1, "input\xE5\x85\xA5\xE5\x8A\x9B", &error);
246  histograms.ExpectTotalCount("InputMethod.CommitLength", 3);
247  histograms.ExpectBucketCount("InputMethod.CommitLength", 5, 1);
248  histograms.ExpectBucketCount("InputMethod.CommitLength", 2, 1);
249  histograms.ExpectBucketCount("InputMethod.CommitLength", 7, 1);
250}
251
252}  // namespace input_method
253}  // namespace chromeos
254