// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/metrics/histogram_samples.h" #include "base/metrics/statistics_recorder.h" #include "chrome/browser/chromeos/input_method/input_method_configuration.h" #include "chrome/browser/chromeos/input_method/input_method_engine.h" #include "chrome/browser/chromeos/input_method/input_method_engine_interface.h" #include "chrome/browser/chromeos/input_method/mock_input_method_manager.h" #include "chromeos/ime/extension_ime_util.h" #include "chromeos/ime/mock_component_extension_ime_manager_delegate.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/ime/chromeos/mock_ime_input_context_handler.h" namespace chromeos { namespace input_method { namespace { const char kTestExtensionId[] = "mppnpdlheglhdfmldimlhpnegondlapf"; const char kTestExtensionId2[] = "dmpipdbjkoajgdeppkffbjhngfckdloi"; const char kTestImeEngineId[] = "test_engine_id"; const char* kHistogramNames[] = { "InputMethod.Enable.test_engine_id", "InputMethod.Commit.test_engine_id", "InputMethod.CommitCharacter.test_engine_id", }; scoped_ptr GetHistogramSamples( const char* histogram_name) { base::HistogramBase* histogram = base::StatisticsRecorder::FindHistogram(histogram_name); EXPECT_NE(static_cast(NULL), histogram); return histogram->SnapshotSamples().Pass(); } enum CallsBitmap { NONE = 0U, ACTIVATE = 1U, DEACTIVATED = 2U, ONFOCUS = 4U, ONBLUR = 8U }; void InitInputMethod() { ComponentExtensionIMEManager* comp_ime_manager = new ComponentExtensionIMEManager; MockComponentExtIMEManagerDelegate* delegate = new MockComponentExtIMEManagerDelegate; ComponentExtensionIME ext1; ext1.id = kTestExtensionId; ComponentExtensionEngine ext1_engine1; ext1_engine1.engine_id = kTestImeEngineId; ext1_engine1.language_codes.push_back("en-US"); ext1_engine1.layouts.push_back("us"); ext1.engines.push_back(ext1_engine1); std::vector ime_list; ime_list.push_back(ext1); delegate->set_ime_list(ime_list); comp_ime_manager->Initialize( scoped_ptr(delegate).Pass()); MockInputMethodManager* manager = new MockInputMethodManager; manager->SetComponentExtensionIMEManager( scoped_ptr(comp_ime_manager).Pass()); InitializeForTesting(manager); } class TestObserver : public InputMethodEngineInterface::Observer { public: TestObserver() : calls_bitmap_(NONE) {} virtual ~TestObserver() {} virtual void OnActivate(const std::string& engine_id) OVERRIDE { calls_bitmap_ |= ACTIVATE; } virtual void OnDeactivated(const std::string& engine_id) OVERRIDE { calls_bitmap_ |= DEACTIVATED; } virtual void OnFocus( const InputMethodEngineInterface::InputContext& context) OVERRIDE { calls_bitmap_ |= ONFOCUS; } virtual void OnBlur(int context_id) OVERRIDE { calls_bitmap_ |= ONBLUR; } virtual void OnKeyEvent( const std::string& engine_id, const InputMethodEngineInterface::KeyboardEvent& event, input_method::KeyEventHandle* key_data) OVERRIDE {} virtual void OnInputContextUpdate( const InputMethodEngineInterface::InputContext& context) OVERRIDE {} virtual void OnCandidateClicked( const std::string& engine_id, int candidate_id, InputMethodEngineInterface::MouseButtonEvent button) OVERRIDE {} virtual void OnMenuItemActivated( const std::string& engine_id, const std::string& menu_id) OVERRIDE {} virtual void OnSurroundingTextChanged( const std::string& engine_id, const std::string& text, int cursor_pos, int anchor_pos) OVERRIDE {} virtual void OnReset(const std::string& engine_id) OVERRIDE {} unsigned char GetCallsBitmapAndReset() { unsigned char ret = calls_bitmap_; calls_bitmap_ = NONE; return ret; } private: unsigned char calls_bitmap_; DISALLOW_COPY_AND_ASSIGN(TestObserver); }; class InputMethodEngineTest : public testing::Test { public: InputMethodEngineTest() : observer_(NULL), input_view_("inputview.html") { languages_.push_back("en-US"); layouts_.push_back("us"); InitInputMethod(); IMEBridge::Initialize(); mock_ime_input_context_handler_.reset(new MockIMEInputContextHandler()); IMEBridge::Get()->SetInputContextHandler( mock_ime_input_context_handler_.get()); base::StatisticsRecorder::Initialize(); for (size_t i = 0; i < arraysize(kHistogramNames); i++) { base::Histogram::FactoryGet( kHistogramNames[i], 0, 1000000, 50, base::HistogramBase::kNoFlags) ->Add(0); initial_histogram_samples_[i] = GetHistogramSamples(kHistogramNames[i]).Pass(); initial_histogram_samples_map_[kHistogramNames[i]] = initial_histogram_samples_[i].get(); } } virtual ~InputMethodEngineTest() { IMEBridge::Get()->SetInputContextHandler(NULL); engine_.reset(); Shutdown(); } protected: scoped_ptr GetHistogramSamplesDelta( const char* histogram_name) { scoped_ptr delta_samples( GetHistogramSamples(histogram_name)); delta_samples->Subtract(*initial_histogram_samples_map_[histogram_name]); return delta_samples.Pass(); } void ExpectNewSample(const char* histogram_name, base::HistogramBase::Sample sample, int total_count, int sample_count) { scoped_ptr delta_samples( GetHistogramSamplesDelta(histogram_name)); EXPECT_EQ(total_count, delta_samples->TotalCount()); EXPECT_EQ(sample_count, delta_samples->GetCount(sample)); } void CreateEngine(bool whitelisted) { engine_.reset(new InputMethodEngine()); observer_ = new TestObserver(); scoped_ptr observer_ptr(observer_); engine_->Initialize(observer_ptr.Pass(), "", whitelisted ? kTestExtensionId : kTestExtensionId2, kTestImeEngineId, languages_, layouts_, options_page_, input_view_); } void FocusIn(ui::TextInputType input_type) { IMEEngineHandlerInterface::InputContext input_context( input_type, ui::TEXT_INPUT_MODE_DEFAULT); engine_->FocusIn(input_context); IMEBridge::Get()->SetCurrentTextInputType(input_type); } scoped_ptr engine_; TestObserver* observer_; std::vector languages_; std::vector layouts_; GURL options_page_; GURL input_view_; scoped_ptr initial_histogram_samples_[arraysize(kHistogramNames)]; std::map initial_histogram_samples_map_; scoped_ptr mock_ime_input_context_handler_; private: DISALLOW_COPY_AND_ASSIGN(InputMethodEngineTest); }; } // namespace TEST_F(InputMethodEngineTest, TestSwitching) { CreateEngine(false); // Enable/disable with focus. FocusIn(ui::TEXT_INPUT_TYPE_URL); EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset()); engine_->Enable(); EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset()); engine_->Disable(); EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset()); // Enable/disable without focus. engine_->FocusOut(); EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset()); engine_->Enable(); EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset()); engine_->Disable(); EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset()); // Focus change when enabled. engine_->Enable(); EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset()); engine_->FocusOut(); EXPECT_EQ(ONBLUR, observer_->GetCallsBitmapAndReset()); // Focus change when disabled. engine_->Disable(); EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset()); FocusIn(ui::TEXT_INPUT_TYPE_TEXT); EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset()); engine_->FocusOut(); EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset()); } TEST_F(InputMethodEngineTest, TestSwitching_Password_3rd_Party) { CreateEngine(false); // Enable/disable with focus. FocusIn(ui::TEXT_INPUT_TYPE_PASSWORD); EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset()); engine_->Enable(); EXPECT_EQ(ACTIVATE, observer_->GetCallsBitmapAndReset()); engine_->Disable(); EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset()); // Focus change when enabled. engine_->Enable(); EXPECT_EQ(ACTIVATE, observer_->GetCallsBitmapAndReset()); engine_->FocusOut(); EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset()); FocusIn(ui::TEXT_INPUT_TYPE_PASSWORD); EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset()); engine_->Disable(); EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset()); } TEST_F(InputMethodEngineTest, TestSwitching_Password_Whitelisted) { CreateEngine(true); // Enable/disable with focus. FocusIn(ui::TEXT_INPUT_TYPE_PASSWORD); EXPECT_EQ(NONE, observer_->GetCallsBitmapAndReset()); engine_->Enable(); EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset()); engine_->Disable(); EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset()); // Focus change when enabled. engine_->Enable(); EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset()); engine_->FocusOut(); EXPECT_EQ(ONBLUR, observer_->GetCallsBitmapAndReset()); FocusIn(ui::TEXT_INPUT_TYPE_PASSWORD); EXPECT_EQ(ONFOCUS, observer_->GetCallsBitmapAndReset()); engine_->Disable(); EXPECT_EQ(DEACTIVATED, observer_->GetCallsBitmapAndReset()); } TEST_F(InputMethodEngineTest, TestHistograms) { CreateEngine(true); FocusIn(ui::TEXT_INPUT_TYPE_TEXT); engine_->Enable(); std::string error; ExpectNewSample("InputMethod.Enable.test_engine_id", 1, 1, 1); engine_->CommitText(1, "input", &error); engine_->CommitText(1, "入力", &error); engine_->CommitText(1, "input入力", &error); ExpectNewSample("InputMethod.Commit.test_engine_id", 1, 3, 3); ExpectNewSample("InputMethod.CommitCharacter.test_engine_id", 5, 3, 1); ExpectNewSample("InputMethod.CommitCharacter.test_engine_id", 2, 3, 1); ExpectNewSample("InputMethod.CommitCharacter.test_engine_id", 7, 3, 1); } } // namespace input_method } // namespace chromeos