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 19 namespace chromeos { 20 21 namespace input_method { 22 namespace { 23 24 const char kTestExtensionId[] = "mppnpdlheglhdfmldimlhpnegondlapf"; 25 const char kTestExtensionId2[] = "dmpipdbjkoajgdeppkffbjhngfckdloi"; 26 const char kTestImeComponentId[] = "test_engine_id"; 27 28 enum CallsBitmap { 29 NONE = 0U, 30 ACTIVATE = 1U, 31 DEACTIVATED = 2U, 32 ONFOCUS = 4U, 33 ONBLUR = 8U 34 }; 35 36 void 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 63 class 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 113 class 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 162 TEST_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 192 TEST_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 212 TEST_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 232 TEST_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