Chromium Code Reviews| Index: ppapi/tests/test_ime_input_event.cc |
| diff --git a/ppapi/tests/test_ime_input_event.cc b/ppapi/tests/test_ime_input_event.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..57adcadcdcdf6777ecf77b95b651b2e0eff51cec |
| --- /dev/null |
| +++ b/ppapi/tests/test_ime_input_event.cc |
| @@ -0,0 +1,425 @@ |
| +// Copyright (c) 2012 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 "ppapi/tests/test_ime_input_event.h" |
| + |
| +#include "ppapi/c/pp_errors.h" |
| +#include "ppapi/c/ppb_input_event.h" |
| +#include "ppapi/c/dev/ppb_ime_input_event_dev.h" |
|
yzshen1
2012/05/15 18:03:48
sort this section, please.
kinaba
2012/05/16 10:13:57
Done.
|
| +#include "ppapi/c/dev/ppb_testing_dev.h" |
| +#include "ppapi/cpp/input_event.h" |
| +#include "ppapi/cpp/module.h" |
| +#include "ppapi/cpp/dev/ime_input_event_dev.h" |
| +#include "ppapi/tests/test_utils.h" |
| +#include "ppapi/tests/testing_instance.h" |
| + |
| +REGISTER_TEST_CASE(ImeInputEvent); |
| + |
| +namespace { |
| + |
| +const char *(kCompositionChar[]) = { |
| + "\xE6\x96\x87", "\xE5\xAD\x97", "\xE5\x88\x97" |
| +}; |
| + |
| +const char kCompositionText[] = "\xE6\x96\x87\xE5\xAD\x97\xE5\x88\x97"; |
| + |
| +#define FINISHED_WAITING_MESSAGE "TEST_IME_INPUT_EVENT_FINISHED_WAITING" |
| + |
| +} // namespace |
| + |
| +void TestImeInputEvent::RunTests(const std::string& filter) { |
|
yzshen1
2012/05/15 18:03:48
Keep the definitions in the same order as the decl
kinaba
2012/05/16 10:13:57
Done.
|
| + RUN_TEST(ImeCommit, filter); |
| + RUN_TEST(ImeCancel, filter); |
| + RUN_TEST(ImeUnawareCommit, filter); |
| + RUN_TEST(ImeUnawareCancel, filter); |
| +} |
| + |
| +TestImeInputEvent::TestImeInputEvent(TestingInstance* instance) |
| + : TestCase(instance), |
| + input_event_interface_(NULL), |
| + keyboard_input_event_interface_(NULL), |
| + ime_input_event_interface_(NULL) { |
|
yzshen1
2012/05/15 18:03:48
Please also init those boolean members.
kinaba
2012/05/16 10:13:57
Done.
|
| +} |
| + |
| +TestImeInputEvent::~TestImeInputEvent() { |
| + // Remove the special listener that only responds to a |
| + // FINISHED_WAITING_MESSAGE string. See Init for where it gets added. |
| + std::string js_code; |
| + js_code += "var plugin = document.getElementById('plugin');" |
|
yzshen1
2012/05/15 18:03:48
Why do we need to do a '+=' here? (and line 81)
kinaba
2012/05/16 10:13:57
Done. We don't need it.
|
| + "plugin.removeEventListener('message'," |
| + " plugin.wait_for_messages_handler);" |
| + "delete plugin.wait_for_messages_handler;"; |
| + instance_->EvalScript(js_code); |
| +} |
| + |
| +bool TestImeInputEvent::Init() { |
| + input_event_interface_ = static_cast<const PPB_InputEvent*>( |
| + pp::Module::Get()->GetBrowserInterface(PPB_INPUT_EVENT_INTERFACE)); |
| + keyboard_input_event_interface_ = |
| + static_cast<const PPB_KeyboardInputEvent*>( |
| + pp::Module::Get()->GetBrowserInterface( |
| + PPB_KEYBOARD_INPUT_EVENT_INTERFACE)); |
| + ime_input_event_interface_ = static_cast<const PPB_IMEInputEvent_Dev*>( |
| + pp::Module::Get()->GetBrowserInterface( |
| + PPB_IME_INPUT_EVENT_DEV_INTERFACE)); |
| + |
| + bool success = |
| + input_event_interface_ && |
| + keyboard_input_event_interface_ && |
| + ime_input_event_interface_ && |
| + CheckTestingInterface(); |
| + |
| + // Set up a listener for our message that signals that all input events have |
| + // been received. |
| + std::string js_code; |
| + // Note the following code is dependent on some features of test_case.html. |
| + // E.g., it is assumed that the DOM element where the plugin is embedded has |
| + // an id of 'plugin', and there is a function 'IsTestingMessage' that allows |
| + // us to ignore the messages that are intended for use by the testing |
| + // framework itself. |
| + js_code += "var plugin = document.getElementById('plugin');" |
| + "var wait_for_messages_handler = function(message_event) {" |
| + " if (!IsTestingMessage(message_event.data) &&" |
| + " message_event.data === '" FINISHED_WAITING_MESSAGE "') {" |
| + " plugin.postMessage('" FINISHED_WAITING_MESSAGE "');" |
| + " }" |
| + "};" |
| + "plugin.addEventListener('message', wait_for_messages_handler);" |
| + // Stash it on the plugin so we can remove it in the destructor. |
| + "plugin.wait_for_messages_handler = wait_for_messages_handler;"; |
| + instance_->EvalScript(js_code); |
| + |
| + return success; |
| +} |
| + |
| +pp::InputEvent TestImeInputEvent::CreateImeCompositionStartEvent() { |
| + return pp::IMEInputEvent_Dev( |
| + instance_, |
| + PP_INPUTEVENT_TYPE_IME_COMPOSITION_START, |
| + 100, // time_stamp |
| + pp::Var(""), |
| + std::vector<uint32_t>(), |
| + -1, // target_segment |
| + std::make_pair(0U, 0U) // selection |
| + ); |
| +} |
| + |
| +pp::InputEvent TestImeInputEvent::CreateImeCompositionUpdateEvent( |
| + const std::string& text, |
| + const std::vector<uint32_t>& segments, |
| + int32_t target_segment, |
| + const std::pair<uint32_t, uint32_t>& selection) { |
| + return pp::IMEInputEvent_Dev( |
| + instance_, |
| + PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE, |
| + 100, // time_stamp |
| + text, |
| + segments, |
| + target_segment, |
| + selection |
| + ); |
| +} |
| + |
| +pp::InputEvent TestImeInputEvent::CreateImeCompositionEndEvent( |
| + const std::string& text) { |
| + return pp::IMEInputEvent_Dev( |
| + instance_, |
| + PP_INPUTEVENT_TYPE_IME_COMPOSITION_END, |
| + 100, // time_stamp |
| + pp::Var(text), |
| + std::vector<uint32_t>(), |
| + -1, // target_segment |
| + std::make_pair(0U, 0U) // selection |
| + ); |
| +} |
| + |
| +pp::InputEvent TestImeInputEvent::CreateImeTextEvent(const std::string& text) { |
| + return pp::IMEInputEvent_Dev( |
| + instance_, |
| + PP_INPUTEVENT_TYPE_IME_TEXT, |
| + 100, // time_stamp |
| + pp::Var(text), |
| + std::vector<uint32_t>(), |
| + -1, // target_segment |
| + std::make_pair(0U, 0U) // selection |
| + ); |
| +} |
| + |
| +pp::InputEvent TestImeInputEvent::CreateCharEvent(const std::string& text) { |
| + return pp::KeyboardInputEvent( |
| + instance_, |
| + PP_INPUTEVENT_TYPE_CHAR, |
| + 100, // time_stamp |
| + 0, // modifiers |
| + 0, // keycode |
| + pp::Var(text)); |
| +} |
| + |
| +void TestImeInputEvent::GetFocusBySimulatingMouseClick() { |
| + // For receiving IME events, the plugin DOM node needs to be focused. |
| + // The following code is for achieving that by simulating a mouse click event. |
| + input_event_interface_->RequestInputEvents(instance_->pp_instance(), |
| + PP_INPUTEVENT_CLASS_MOUSE); |
| + SimulateInputEvent(pp::MouseInputEvent( |
| + instance_, |
| + PP_INPUTEVENT_TYPE_MOUSEDOWN, |
| + 100, // time_stamp |
| + 0, // modifiers |
| + PP_INPUTEVENT_MOUSEBUTTON_LEFT, |
| + pp::Point( |
| + view_rect_.x() + view_rect_.width() / 2, |
| + view_rect_.y() + view_rect_.height() / 2), |
| + 1, // click count |
| + pp::Point())); // movement |
| +} |
| + |
| +// Simulates the input event and calls PostMessage to let us know when |
| +// we have received all resulting events from the browser. |
| +bool TestImeInputEvent::SimulateInputEvent(const pp::InputEvent& input_event) { |
| + received_unexpected_event_ = false; |
| + received_finish_message_ = false; |
| + testing_interface_->SimulateInputEvent(instance_->pp_instance(), |
| + input_event.pp_resource()); |
| + instance_->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE)); |
| + testing_interface_->RunMessageLoop(instance_->pp_instance()); |
| + return received_finish_message_ && !received_unexpected_event_; |
| +} |
| + |
| +bool TestImeInputEvent::AreEquivalentEvents(PP_Resource received, |
| + PP_Resource expected) { |
| + if (!input_event_interface_->IsInputEvent(received) || |
| + !input_event_interface_->IsInputEvent(expected)) { |
| + return false; |
| + } |
| + |
| + // Test common fields, except modifiers and time stamp, which may be changed |
| + // by the browser. |
| + int32_t received_type = input_event_interface_->GetType(received); |
| + int32_t expected_type = input_event_interface_->GetType(expected); |
| + if (received_type != expected_type) |
| + return false; |
| + |
| + // Test event type-specific fields. |
| + switch (input_event_interface_->GetType(received)) { |
|
yzshen1
2012/05/15 18:03:48
received_type?
kinaba
2012/05/16 10:13:57
Done.
|
| + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: |
| + // COMPOSITION_START does not convey further information. |
| + break; |
| + |
| + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: |
| + case PP_INPUTEVENT_TYPE_IME_TEXT: |
| + // For COMPOSITION_END and TEXT, GetText() has meaning. |
| + return pp::Var(pp::PASS_REF, |
| + ime_input_event_interface_->GetText(received)) == |
| + pp::Var(pp::PASS_REF, |
| + ime_input_event_interface_->GetText(expected)); |
| + |
| + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: |
| + // For COMPOSITION_UPDATE, all fields must be checked. |
| + { |
| + uint32_t receivedSegmentNumber = |
|
yzshen1
2012/05/15 18:03:48
name_variables_this_way (and some other places).
kinaba
2012/05/16 10:13:57
Done.
|
| + ime_input_event_interface_->GetSegmentNumber(received); |
| + uint32_t expectedSegmentNumber = |
| + ime_input_event_interface_->GetSegmentNumber(received); |
|
yzshen1
2012/05/15 18:03:48
expected?
kinaba
2012/05/16 10:13:57
Done. Good catch.
|
| + if (receivedSegmentNumber != expectedSegmentNumber) |
| + return false; |
| + |
| + // The "<=" is not a bug. i-th segment is represented as the pair of |
| + // i-th and (i+1)-th offsets in Pepper IME API. |
| + for (uint32_t i = 0; i <= receivedSegmentNumber; ++i) { |
| + if (ime_input_event_interface_->GetSegmentOffset(received, i) != |
| + ime_input_event_interface_->GetSegmentOffset(expected, i)) |
| + return false; |
| + } |
| + |
| + uint32_t receivedSelectionStart; |
| + uint32_t receivedSelectionEnd; |
| + uint32_t expectedSelectionStart; |
| + uint32_t expectedSelectionEnd; |
| + ime_input_event_interface_->GetSelection( |
| + received, &receivedSelectionStart, &receivedSelectionEnd); |
| + ime_input_event_interface_->GetSelection( |
| + expected, &expectedSelectionStart, &expectedSelectionEnd); |
| + if (receivedSelectionStart != expectedSelectionStart || |
| + receivedSelectionEnd != expectedSelectionEnd) { |
| + return true; |
| + } |
| + |
| + return pp::Var(pp::PASS_REF, |
| + ime_input_event_interface_->GetText(received)) == |
| + pp::Var(pp::PASS_REF, |
| + ime_input_event_interface_->GetText(expected)) && |
| + ime_input_event_interface_->GetTargetSegment(received) == |
| + ime_input_event_interface_->GetTargetSegment(received); |
|
yzshen1
2012/05/15 18:03:48
expected? :)
kinaba
2012/05/16 10:13:57
Done. expected is expected.
|
| + } |
| + |
| + case PP_INPUTEVENT_TYPE_CHAR: |
| + return |
| + keyboard_input_event_interface_->GetKeyCode(received) == |
| + keyboard_input_event_interface_->GetKeyCode(expected) && |
| + pp::Var(pp::PASS_REF, |
| + keyboard_input_event_interface_->GetCharacterText(received)) == |
| + pp::Var(pp::PASS_REF, |
| + keyboard_input_event_interface_->GetCharacterText(expected)); |
| + |
| + default: |
| + break; |
| + } |
| + return true; |
| +} |
| + |
| +bool TestImeInputEvent::HandleInputEvent(const pp::InputEvent& input_event) { |
| + // Check whether the IME related events comes in the expected order. |
| + switch (input_event.GetType()) { |
| + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: |
| + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: |
| + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: |
| + case PP_INPUTEVENT_TYPE_IME_TEXT: |
| + case PP_INPUTEVENT_TYPE_CHAR: |
| + if (expected_events_.empty()) { |
| + received_unexpected_event_ = true; |
| + } else { |
| + received_unexpected_event_ = |
| + !AreEquivalentEvents(input_event.pp_resource(), |
| + expected_events_.front().pp_resource()); |
| + expected_events_.erase(expected_events_.begin()); |
| + } |
| + break; |
| + |
| + default: |
| + break; |
| + } |
| + |
| + // Handle all input events. |
| + return true; |
| +} |
| + |
| +void TestImeInputEvent::HandleMessage(const pp::Var& message_data) { |
| + if (message_data.is_string() && |
| + (message_data.AsString() == FINISHED_WAITING_MESSAGE)) { |
| + testing_interface_->QuitMessageLoop(instance_->pp_instance()); |
| + received_finish_message_ = true; |
| + } |
| +} |
| + |
| +void TestImeInputEvent::DidChangeView(const pp::View& view) { |
| + view_rect_ = view.GetRect(); |
| +} |
| + |
| +std::string TestImeInputEvent::TestImeCommit() { |
| + GetFocusBySimulatingMouseClick(); |
| + |
| + input_event_interface_->RequestInputEvents(instance_->pp_instance(), |
| + PP_INPUTEVENT_CLASS_KEYBOARD | |
| + PP_INPUTEVENT_CLASS_IME); |
| + |
| + std::vector<uint32_t> segments; |
| + segments.push_back(0U); |
| + segments.push_back(3U); |
| + segments.push_back(6U); |
| + segments.push_back(9U); |
| + pp::InputEvent update_event = CreateImeCompositionUpdateEvent( |
| + kCompositionText, segments, 1, std::make_pair(3U, 6U)); |
| + |
| + expected_events_.clear(); |
| + expected_events_.push_back(CreateImeCompositionStartEvent()); |
| + expected_events_.push_back(update_event); |
| + expected_events_.push_back(CreateImeCompositionEndEvent(kCompositionText)); |
| + expected_events_.push_back(CreateImeTextEvent(kCompositionText)); |
| + |
| + // Simulate the case when IME successfully committed some text. |
| + ASSERT_TRUE( |
| + SimulateInputEvent(update_event)); |
| + ASSERT_TRUE( |
| + SimulateInputEvent(CreateImeTextEvent(kCompositionText))); |
| + |
| + ASSERT_TRUE(expected_events_.empty()); |
| + PASS(); |
| +} |
| + |
| +std::string TestImeInputEvent::TestImeCancel() { |
| + GetFocusBySimulatingMouseClick(); |
| + |
| + input_event_interface_->RequestInputEvents(instance_->pp_instance(), |
| + PP_INPUTEVENT_CLASS_KEYBOARD | |
| + PP_INPUTEVENT_CLASS_IME); |
| + |
| + std::vector<uint32_t> segments; |
| + segments.push_back(0U); |
| + segments.push_back(3U); |
| + segments.push_back(6U); |
| + segments.push_back(9U); |
| + pp::InputEvent update_event = CreateImeCompositionUpdateEvent( |
| + kCompositionText, segments, 1, std::make_pair(3U, 6U)); |
| + |
| + expected_events_.clear(); |
| + expected_events_.push_back(CreateImeCompositionStartEvent()); |
| + expected_events_.push_back(update_event); |
| + expected_events_.push_back(CreateImeCompositionEndEvent("")); |
| + |
| + // Simulate the case when IME canceled composition. |
| + ASSERT_TRUE( |
| + SimulateInputEvent(update_event)); |
| + ASSERT_TRUE( |
| + SimulateInputEvent(CreateImeCompositionEndEvent(""))); |
| + |
| + ASSERT_TRUE(expected_events_.empty()); |
| + PASS(); |
| +} |
| + |
| +std::string TestImeInputEvent::TestImeUnawareCommit() { |
| + GetFocusBySimulatingMouseClick(); |
| + |
| + input_event_interface_->ClearInputEventRequest(instance_->pp_instance(), |
| + PP_INPUTEVENT_CLASS_IME); |
| + input_event_interface_->RequestInputEvents(instance_->pp_instance(), |
| + PP_INPUTEVENT_CLASS_KEYBOARD); |
| + |
| + std::vector<uint32_t> segments; |
| + segments.push_back(0U); |
| + segments.push_back(3U); |
| + segments.push_back(6U); |
| + segments.push_back(9U); |
| + pp::InputEvent update_event = CreateImeCompositionUpdateEvent( |
| + kCompositionText, segments, 1, std::make_pair(3U, 6U)); |
| + |
| + expected_events_.clear(); |
| + expected_events_.push_back(CreateCharEvent(kCompositionChar[0])); |
| + expected_events_.push_back(CreateCharEvent(kCompositionChar[1])); |
| + expected_events_.push_back(CreateCharEvent(kCompositionChar[2])); |
| + |
| + // Test for IME-unaware plugins. Commit event is translated to char events. |
| + ASSERT_TRUE(SimulateInputEvent(update_event)); |
| + ASSERT_TRUE(SimulateInputEvent(CreateImeTextEvent(kCompositionText))); |
| + |
| + ASSERT_TRUE(expected_events_.empty()); |
| + PASS(); |
| +} |
| + |
| + |
| +std::string TestImeInputEvent::TestImeUnawareCancel() { |
| + GetFocusBySimulatingMouseClick(); |
| + |
| + input_event_interface_->ClearInputEventRequest(instance_->pp_instance(), |
| + PP_INPUTEVENT_CLASS_IME); |
| + input_event_interface_->RequestInputEvents(instance_->pp_instance(), |
| + PP_INPUTEVENT_CLASS_KEYBOARD); |
| + |
| + std::vector<uint32_t> segments; |
| + segments.push_back(0U); |
| + segments.push_back(3U); |
| + segments.push_back(6U); |
| + segments.push_back(9U); |
| + pp::InputEvent update_event = CreateImeCompositionUpdateEvent( |
| + kCompositionText, segments, 1, std::make_pair(3U, 6U)); |
| + |
| + expected_events_.clear(); |
| + |
| + // Test for IME-unaware plugins. Cancel won't issue any events. |
| + ASSERT_TRUE(SimulateInputEvent(update_event)); |
| + ASSERT_TRUE(SimulateInputEvent(CreateImeCompositionEndEvent(""))); |
| + |
| + ASSERT_TRUE(expected_events_.empty()); |
| + PASS(); |
| +} |
| + |