| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "session/random_keyevents_generator.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/mutex.h" |
| #include "base/port.h" |
| #include "base/util.h" |
| #include "session/commands.pb.h" |
| |
| namespace mozc { |
| namespace session { |
| namespace { |
| |
| #include "session/session_stress_test_data.h" |
| |
| // Constants for ProbableKeyEvent. |
| const double kMostPossibleKeyProbability = 0.98; |
| const size_t kProbableKeyEventSize = 8; |
| |
| const commands::KeyEvent::SpecialKey kSpecialKeys[] = { |
| commands::KeyEvent::SPACE, |
| commands::KeyEvent::BACKSPACE, |
| commands::KeyEvent::DEL, |
| commands::KeyEvent::DOWN, |
| commands::KeyEvent::END, |
| commands::KeyEvent::ENTER, |
| commands::KeyEvent::ESCAPE, |
| commands::KeyEvent::HOME, |
| commands::KeyEvent::INSERT, |
| commands::KeyEvent::HENKAN, |
| commands::KeyEvent::MUHENKAN, |
| commands::KeyEvent::LEFT, |
| commands::KeyEvent::RIGHT, |
| commands::KeyEvent::UP, |
| commands::KeyEvent::DOWN, |
| commands::KeyEvent::PAGE_UP, |
| commands::KeyEvent::PAGE_DOWN, |
| commands::KeyEvent::TAB, |
| commands::KeyEvent::F1, |
| commands::KeyEvent::F2, |
| commands::KeyEvent::F3, |
| commands::KeyEvent::F4, |
| commands::KeyEvent::F5, |
| commands::KeyEvent::F6, |
| commands::KeyEvent::F7, |
| commands::KeyEvent::F8, |
| commands::KeyEvent::F9, |
| commands::KeyEvent::F10, |
| commands::KeyEvent::F11, |
| commands::KeyEvent::F12, |
| commands::KeyEvent::NUMPAD0, |
| commands::KeyEvent::NUMPAD1, |
| commands::KeyEvent::NUMPAD2, |
| commands::KeyEvent::NUMPAD3, |
| commands::KeyEvent::NUMPAD4, |
| commands::KeyEvent::NUMPAD5, |
| commands::KeyEvent::NUMPAD6, |
| commands::KeyEvent::NUMPAD7, |
| commands::KeyEvent::NUMPAD8, |
| commands::KeyEvent::NUMPAD9, |
| commands::KeyEvent::MULTIPLY, |
| commands::KeyEvent::ADD, |
| commands::KeyEvent::SEPARATOR, |
| commands::KeyEvent::SUBTRACT, |
| commands::KeyEvent::DECIMAL, |
| commands::KeyEvent::DIVIDE, |
| commands::KeyEvent::EQUALS, |
| commands::KeyEvent::COMMA, |
| }; |
| |
| uint32 GetRandomAsciiKey() { |
| return static_cast<uint32>(' ') + |
| Util::Random(static_cast<uint32>('~' - ' ')); |
| } |
| |
| void InitSeedWithRandomValue() { |
| uint32 seed = 0; |
| mozc::Util::GetRandomSequence(reinterpret_cast<char *>(&seed), sizeof(seed)); |
| Util::SetRandomSeed(seed); |
| } |
| |
| void DoNothing() { |
| // Do nothing. |
| // Used only for marking the seed initialized. |
| } |
| |
| once_t seed_init_once = MOZC_ONCE_INIT; |
| } // namespace |
| |
| void RandomKeyEventsGenerator::PrepareForMemoryLeakTest() { |
| // Read all kTestSentences and load these to memory. |
| const int size = arraysize(kTestSentences); |
| for (int i = 0; i < size; ++i) { |
| const char *sentence = kTestSentences[i]; |
| CHECK_GT(strlen(sentence), 0); |
| } |
| } |
| |
| // Generates KeyEvent instances based on |romaji| and stores into |keys|. |
| void TypeRawKeys(StringPiece romaji, bool create_probable_key_events, |
| vector<commands::KeyEvent> *keys) { |
| for (ConstChar32Iterator iter(romaji); !iter.Done(); iter.Next()) { |
| const uint32 ucs4 = iter.Get(); |
| if (ucs4 < 0x20 || ucs4 > 0x7F) { |
| continue; |
| } |
| commands::KeyEvent key; |
| key.set_key_code(ucs4); |
| if (create_probable_key_events) { |
| commands::KeyEvent::ProbableKeyEvent *probable_key_event = |
| key.add_probable_key_event(); |
| probable_key_event->set_key_code(ucs4); |
| probable_key_event->set_probability(kMostPossibleKeyProbability); |
| for (size_t i = 0; i < kProbableKeyEventSize; ++i) { |
| commands::KeyEvent::ProbableKeyEvent *probable_key_event = |
| key.add_probable_key_event(); |
| probable_key_event->set_key_code(0x20 + Util::Random(0x7F - 0x20)); |
| probable_key_event->set_probability( |
| (1.0 - kMostPossibleKeyProbability) / kProbableKeyEventSize); |
| } |
| } |
| keys->push_back(key); |
| } |
| } |
| |
| // Converts from Hiragana to Romaji. |
| string ToRomaji(StringPiece hiragana) { |
| string tmp, result; |
| Util::HiraganaToRomanji(hiragana, &tmp); |
| Util::FullWidthToHalfWidth(tmp, &result); |
| return result; |
| } |
| |
| void RandomKeyEventsGenerator::InitSeed(uint32 seed) { |
| Util::SetRandomSeed(seed); |
| CallOnce(&seed_init_once, &DoNothing); |
| } |
| |
| // Generates KeyEvent instances based on |sentence| and stores into |keys|. |
| // And Enter key event is appended at the tail. |
| // The instances have ProbableKeyEvent if |create_probable_key_events| is set. |
| void GenerateMobileSequenceInternal( |
| StringPiece sentence, bool create_probable_key_events, |
| vector<commands::KeyEvent> *keys) { |
| const string input = ToRomaji(sentence); |
| VLOG(1) << input; |
| |
| // Type the sentence |
| TypeRawKeys(input, create_probable_key_events, keys); |
| |
| commands::KeyEvent key; |
| key.set_special_key(commands::KeyEvent::ENTER); |
| keys->push_back(key); |
| } |
| |
| void RandomKeyEventsGenerator::GenerateMobileSequence( |
| bool create_probable_key_events, vector<commands::KeyEvent> *keys) { |
| CHECK(keys); |
| keys->clear(); |
| |
| // If seed was not initialized, set seed randomly. |
| CallOnce(&seed_init_once, &InitSeedWithRandomValue); |
| |
| const StringPiece sentence( |
| kTestSentences[Util::Random(arraysize(kTestSentences))]); |
| CHECK(!sentence.empty()); |
| for (size_t i = 0; i < sentence.size(); ) { |
| // To simulate mobile key events, split given sentence into smaller parts. |
| // Average 5, Min 1, Max 15 |
| const size_t len = Util::Random(5) + Util::Random(5) + Util::Random(5); |
| GenerateMobileSequenceInternal(sentence.substr(i, len), |
| create_probable_key_events, keys); |
| i += len; |
| } |
| } |
| |
| void RandomKeyEventsGenerator::GenerateSequence( |
| vector<commands::KeyEvent> *keys) { |
| CHECK(keys); |
| keys->clear(); |
| |
| // If seed was not initialized, set seed randomly. |
| CallOnce(&seed_init_once, &InitSeedWithRandomValue); |
| |
| const string sentence = |
| kTestSentences[Util::Random(arraysize(kTestSentences))]; |
| CHECK(!sentence.empty()); |
| |
| const string input = ToRomaji(sentence); |
| |
| VLOG(1) << input; |
| |
| // Must send ON event first. |
| { |
| commands::KeyEvent key; |
| key.set_special_key(commands::KeyEvent::ON); |
| keys->push_back(key); |
| } |
| |
| vector<commands::KeyEvent> basic_keys; |
| |
| // generate basic input |
| TypeRawKeys(input, false, &basic_keys); |
| |
| // basic keys + conversion |
| { |
| for (size_t i = 0; i < basic_keys.size(); ++i) { |
| keys->push_back(basic_keys[i]); |
| } |
| |
| for (int i = 0; i < 5; ++i) { |
| const size_t num = Util::Random(30) + 8; |
| for (size_t j = 0; j < num; ++j) { |
| commands::KeyEvent key; |
| key.set_special_key(commands::KeyEvent::SPACE); |
| if (Util::Random(4) == 0) { |
| key.add_modifier_keys(commands::KeyEvent::SHIFT); |
| keys->push_back(key); |
| } |
| } |
| commands::KeyEvent key; |
| key.set_special_key(commands::KeyEvent::RIGHT); |
| keys->push_back(key); |
| } |
| |
| commands::KeyEvent key; |
| key.set_special_key(commands::KeyEvent::ENTER); |
| keys->push_back(key); |
| } |
| |
| // segment resize |
| { |
| for (size_t i = 0; i < basic_keys.size(); ++i) { |
| keys->push_back(basic_keys[i]); |
| } |
| |
| const size_t num = Util::Random(30) + 10; |
| for (size_t i = 0; i < num; ++i) { |
| commands::KeyEvent key; |
| switch (Util::Random(4)) { |
| case 0: |
| key.set_special_key(commands::KeyEvent::LEFT); |
| if (Util::Random(2) == 0) { |
| key.add_modifier_keys(commands::KeyEvent::SHIFT); |
| } |
| break; |
| case 1: |
| key.set_special_key(commands::KeyEvent::RIGHT); |
| if (Util::Random(2) == 0) { |
| key.add_modifier_keys(commands::KeyEvent::SHIFT); |
| } |
| break; |
| default: |
| { |
| const size_t space_num = Util::Random(20) + 3; |
| for (size_t i = 0; i < space_num; ++i) { |
| key.set_special_key(commands::KeyEvent::SPACE); |
| keys->push_back(key); |
| } |
| } |
| break; |
| } |
| |
| if (Util::Random(4) == 0) { |
| key.add_modifier_keys(commands::KeyEvent::CTRL); |
| } |
| |
| if (Util::Random(10) == 0) { |
| key.add_modifier_keys(commands::KeyEvent::ALT); |
| } |
| |
| keys->push_back(key); |
| } |
| |
| commands::KeyEvent key; |
| key.set_special_key(commands::KeyEvent::ENTER); |
| keys->push_back(key); |
| } |
| |
| // insert + delete |
| { |
| for (size_t i = 0; i < basic_keys.size(); ++i) { |
| keys->push_back(basic_keys[i]); |
| } |
| |
| const size_t num = Util::Random(20) + 10; |
| for (size_t i = 0; i < num; ++i) { |
| commands::KeyEvent key; |
| switch (Util::Random(5)) { |
| case 0: |
| key.set_special_key(commands::KeyEvent::LEFT); |
| break; |
| case 1: |
| key.set_special_key(commands::KeyEvent::RIGHT); |
| break; |
| case 2: |
| key.set_special_key(commands::KeyEvent::DEL); |
| break; |
| case 3: |
| key.set_special_key(commands::KeyEvent::BACKSPACE); |
| break; |
| default: |
| { |
| // add any ascii |
| const size_t insert_num = Util::Random(5) + 1; |
| for (size_t i = 0; i < insert_num; ++i) { |
| key.set_key_code(GetRandomAsciiKey()); |
| } |
| } |
| } |
| keys->push_back(key); |
| } |
| |
| commands::KeyEvent key; |
| key.set_special_key(commands::KeyEvent::ENTER); |
| keys->push_back(key); |
| } |
| |
| // basic keys + modifiers |
| { |
| for (size_t i = 0; i < basic_keys.size(); ++i) { |
| commands::KeyEvent key; |
| switch (Util::Random(8)) { |
| case 0: |
| key.set_key_code(kSpecialKeys[Util::Random(arraysize(kSpecialKeys))]); |
| break; |
| case 1: |
| key.set_key_code(GetRandomAsciiKey()); |
| break; |
| default: |
| key.CopyFrom(basic_keys[i]); |
| break; |
| } |
| |
| if (Util::Random(10) == 0) { // 10% |
| key.add_modifier_keys(commands::KeyEvent::CTRL); |
| } |
| |
| if (Util::Random(10) == 0) { // 10% |
| key.add_modifier_keys(commands::KeyEvent::SHIFT); |
| } |
| |
| if (Util::Random(50) == 0) { // 2% |
| key.add_modifier_keys(commands::KeyEvent::KEY_DOWN); |
| } |
| |
| if (Util::Random(50) == 0) { // 2% |
| key.add_modifier_keys(commands::KeyEvent::KEY_UP); |
| } |
| |
| keys->push_back(key); |
| } |
| |
| // submit |
| commands::KeyEvent key; |
| key.set_special_key(commands::KeyEvent::ENTER); |
| keys->push_back(key); |
| } |
| |
| CHECK_GT(keys->size(), 0); |
| VLOG(1) << "key sequence is generated: " << keys->size(); |
| } |
| |
| // static |
| const char **RandomKeyEventsGenerator::GetTestSentences(size_t *size) { |
| *size = arraysize(kTestSentences); |
| return kTestSentences; |
| } |
| } // namespace session |
| } // namespace mozc |