1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "session/random_keyevents_generator.h"
31
32 #include <string>
33 #include <vector>
34
35 #include "base/logging.h"
36 #include "base/mutex.h"
37 #include "base/port.h"
38 #include "base/util.h"
39 #include "protocol/commands.pb.h"
40
41 namespace mozc {
42 namespace session {
43 namespace {
44
45 #include "session/session_stress_test_data.h"
46
47 // Constants for ProbableKeyEvent.
48 const double kMostPossibleKeyProbability = 0.98;
49 const size_t kProbableKeyEventSize = 8;
50
51 const commands::KeyEvent::SpecialKey kSpecialKeys[] = {
52 commands::KeyEvent::SPACE,
53 commands::KeyEvent::BACKSPACE,
54 commands::KeyEvent::DEL,
55 commands::KeyEvent::DOWN,
56 commands::KeyEvent::END,
57 commands::KeyEvent::ENTER,
58 commands::KeyEvent::ESCAPE,
59 commands::KeyEvent::HOME,
60 commands::KeyEvent::INSERT,
61 commands::KeyEvent::HENKAN,
62 commands::KeyEvent::MUHENKAN,
63 commands::KeyEvent::LEFT,
64 commands::KeyEvent::RIGHT,
65 commands::KeyEvent::UP,
66 commands::KeyEvent::DOWN,
67 commands::KeyEvent::PAGE_UP,
68 commands::KeyEvent::PAGE_DOWN,
69 commands::KeyEvent::TAB,
70 commands::KeyEvent::F1,
71 commands::KeyEvent::F2,
72 commands::KeyEvent::F3,
73 commands::KeyEvent::F4,
74 commands::KeyEvent::F5,
75 commands::KeyEvent::F6,
76 commands::KeyEvent::F7,
77 commands::KeyEvent::F8,
78 commands::KeyEvent::F9,
79 commands::KeyEvent::F10,
80 commands::KeyEvent::F11,
81 commands::KeyEvent::F12,
82 commands::KeyEvent::NUMPAD0,
83 commands::KeyEvent::NUMPAD1,
84 commands::KeyEvent::NUMPAD2,
85 commands::KeyEvent::NUMPAD3,
86 commands::KeyEvent::NUMPAD4,
87 commands::KeyEvent::NUMPAD5,
88 commands::KeyEvent::NUMPAD6,
89 commands::KeyEvent::NUMPAD7,
90 commands::KeyEvent::NUMPAD8,
91 commands::KeyEvent::NUMPAD9,
92 commands::KeyEvent::MULTIPLY,
93 commands::KeyEvent::ADD,
94 commands::KeyEvent::SEPARATOR,
95 commands::KeyEvent::SUBTRACT,
96 commands::KeyEvent::DECIMAL,
97 commands::KeyEvent::DIVIDE,
98 commands::KeyEvent::EQUALS,
99 commands::KeyEvent::COMMA,
100 };
101
GetRandomAsciiKey()102 uint32 GetRandomAsciiKey() {
103 return static_cast<uint32>(' ') +
104 Util::Random(static_cast<uint32>('~' - ' '));
105 }
106
InitSeedWithRandomValue()107 void InitSeedWithRandomValue() {
108 uint32 seed = 0;
109 mozc::Util::GetRandomSequence(reinterpret_cast<char *>(&seed), sizeof(seed));
110 Util::SetRandomSeed(seed);
111 }
112
DoNothing()113 void DoNothing() {
114 // Do nothing.
115 // Used only for marking the seed initialized.
116 }
117
118 once_t seed_init_once = MOZC_ONCE_INIT;
119 } // namespace
120
PrepareForMemoryLeakTest()121 void RandomKeyEventsGenerator::PrepareForMemoryLeakTest() {
122 // Read all kTestSentences and load these to memory.
123 const int size = arraysize(kTestSentences);
124 for (int i = 0; i < size; ++i) {
125 const char *sentence = kTestSentences[i];
126 CHECK_GT(strlen(sentence), 0);
127 }
128 }
129
130 // Generates KeyEvent instances based on |romaji| and stores into |keys|.
TypeRawKeys(StringPiece romaji,bool create_probable_key_events,std::vector<commands::KeyEvent> * keys)131 void TypeRawKeys(StringPiece romaji, bool create_probable_key_events,
132 std::vector<commands::KeyEvent> *keys) {
133 for (ConstChar32Iterator iter(romaji); !iter.Done(); iter.Next()) {
134 const uint32 ucs4 = iter.Get();
135 if (ucs4 < 0x20 || ucs4 > 0x7F) {
136 continue;
137 }
138 commands::KeyEvent key;
139 key.set_key_code(ucs4);
140 if (create_probable_key_events) {
141 commands::KeyEvent::ProbableKeyEvent *probable_key_event =
142 key.add_probable_key_event();
143 probable_key_event->set_key_code(ucs4);
144 probable_key_event->set_probability(kMostPossibleKeyProbability);
145 for (size_t i = 0; i < kProbableKeyEventSize; ++i) {
146 commands::KeyEvent::ProbableKeyEvent *probable_key_event =
147 key.add_probable_key_event();
148 probable_key_event->set_key_code(0x20 + Util::Random(0x7F - 0x20));
149 probable_key_event->set_probability(
150 (1.0 - kMostPossibleKeyProbability) / kProbableKeyEventSize);
151 }
152 }
153 keys->push_back(key);
154 }
155 }
156
157 // Converts from Hiragana to Romaji.
ToRomaji(StringPiece hiragana)158 string ToRomaji(StringPiece hiragana) {
159 string tmp, result;
160 Util::HiraganaToRomanji(hiragana, &tmp);
161 Util::FullWidthToHalfWidth(tmp, &result);
162 return result;
163 }
164
InitSeed(uint32 seed)165 void RandomKeyEventsGenerator::InitSeed(uint32 seed) {
166 Util::SetRandomSeed(seed);
167 CallOnce(&seed_init_once, &DoNothing);
168 }
169
170 // Generates KeyEvent instances based on |sentence| and stores into |keys|.
171 // And Enter key event is appended at the tail.
172 // The instances have ProbableKeyEvent if |create_probable_key_events| is set.
GenerateMobileSequenceInternal(StringPiece sentence,bool create_probable_key_events,std::vector<commands::KeyEvent> * keys)173 void GenerateMobileSequenceInternal(
174 StringPiece sentence, bool create_probable_key_events,
175 std::vector<commands::KeyEvent> *keys) {
176 const string input = ToRomaji(sentence);
177 VLOG(1) << input;
178
179 // Type the sentence
180 TypeRawKeys(input, create_probable_key_events, keys);
181
182 commands::KeyEvent key;
183 key.set_special_key(commands::KeyEvent::ENTER);
184 keys->push_back(key);
185 }
186
GenerateMobileSequence(bool create_probable_key_events,std::vector<commands::KeyEvent> * keys)187 void RandomKeyEventsGenerator::GenerateMobileSequence(
188 bool create_probable_key_events, std::vector<commands::KeyEvent> *keys) {
189 CHECK(keys);
190 keys->clear();
191
192 // If seed was not initialized, set seed randomly.
193 CallOnce(&seed_init_once, &InitSeedWithRandomValue);
194
195 const StringPiece sentence(
196 kTestSentences[Util::Random(arraysize(kTestSentences))]);
197 CHECK(!sentence.empty());
198 for (size_t i = 0; i < sentence.size(); ) {
199 // To simulate mobile key events, split given sentence into smaller parts.
200 // Average 5, Min 1, Max 15
201 const size_t len = Util::Random(5) + Util::Random(5) + Util::Random(5);
202 GenerateMobileSequenceInternal(ClippedSubstr(sentence, i, len),
203 create_probable_key_events, keys);
204 i += len;
205 }
206 }
207
GenerateSequence(std::vector<commands::KeyEvent> * keys)208 void RandomKeyEventsGenerator::GenerateSequence(
209 std::vector<commands::KeyEvent> *keys) {
210 CHECK(keys);
211 keys->clear();
212
213 // If seed was not initialized, set seed randomly.
214 CallOnce(&seed_init_once, &InitSeedWithRandomValue);
215
216 const string sentence =
217 kTestSentences[Util::Random(arraysize(kTestSentences))];
218 CHECK(!sentence.empty());
219
220 const string input = ToRomaji(sentence);
221
222 VLOG(1) << input;
223
224 // Must send ON event first.
225 {
226 commands::KeyEvent key;
227 key.set_special_key(commands::KeyEvent::ON);
228 keys->push_back(key);
229 }
230
231 std::vector<commands::KeyEvent> basic_keys;
232
233 // generate basic input
234 TypeRawKeys(input, false, &basic_keys);
235
236 // basic keys + conversion
237 {
238 for (size_t i = 0; i < basic_keys.size(); ++i) {
239 keys->push_back(basic_keys[i]);
240 }
241
242 for (int i = 0; i < 5; ++i) {
243 const size_t num = Util::Random(30) + 8;
244 for (size_t j = 0; j < num; ++j) {
245 commands::KeyEvent key;
246 key.set_special_key(commands::KeyEvent::SPACE);
247 if (Util::Random(4) == 0) {
248 key.add_modifier_keys(commands::KeyEvent::SHIFT);
249 keys->push_back(key);
250 }
251 }
252 commands::KeyEvent key;
253 key.set_special_key(commands::KeyEvent::RIGHT);
254 keys->push_back(key);
255 }
256
257 commands::KeyEvent key;
258 key.set_special_key(commands::KeyEvent::ENTER);
259 keys->push_back(key);
260 }
261
262 // segment resize
263 {
264 for (size_t i = 0; i < basic_keys.size(); ++i) {
265 keys->push_back(basic_keys[i]);
266 }
267
268 const size_t num = Util::Random(30) + 10;
269 for (size_t i = 0; i < num; ++i) {
270 commands::KeyEvent key;
271 switch (Util::Random(4)) {
272 case 0:
273 key.set_special_key(commands::KeyEvent::LEFT);
274 if (Util::Random(2) == 0) {
275 key.add_modifier_keys(commands::KeyEvent::SHIFT);
276 }
277 break;
278 case 1:
279 key.set_special_key(commands::KeyEvent::RIGHT);
280 if (Util::Random(2) == 0) {
281 key.add_modifier_keys(commands::KeyEvent::SHIFT);
282 }
283 break;
284 default:
285 {
286 const size_t space_num = Util::Random(20) + 3;
287 for (size_t i = 0; i < space_num; ++i) {
288 key.set_special_key(commands::KeyEvent::SPACE);
289 keys->push_back(key);
290 }
291 }
292 break;
293 }
294
295 if (Util::Random(4) == 0) {
296 key.add_modifier_keys(commands::KeyEvent::CTRL);
297 }
298
299 if (Util::Random(10) == 0) {
300 key.add_modifier_keys(commands::KeyEvent::ALT);
301 }
302
303 keys->push_back(key);
304 }
305
306 commands::KeyEvent key;
307 key.set_special_key(commands::KeyEvent::ENTER);
308 keys->push_back(key);
309 }
310
311 // insert + delete
312 {
313 for (size_t i = 0; i < basic_keys.size(); ++i) {
314 keys->push_back(basic_keys[i]);
315 }
316
317 const size_t num = Util::Random(20) + 10;
318 for (size_t i = 0; i < num; ++i) {
319 commands::KeyEvent key;
320 switch (Util::Random(5)) {
321 case 0:
322 key.set_special_key(commands::KeyEvent::LEFT);
323 break;
324 case 1:
325 key.set_special_key(commands::KeyEvent::RIGHT);
326 break;
327 case 2:
328 key.set_special_key(commands::KeyEvent::DEL);
329 break;
330 case 3:
331 key.set_special_key(commands::KeyEvent::BACKSPACE);
332 break;
333 default:
334 {
335 // add any ascii
336 const size_t insert_num = Util::Random(5) + 1;
337 for (size_t i = 0; i < insert_num; ++i) {
338 key.set_key_code(GetRandomAsciiKey());
339 }
340 }
341 }
342 keys->push_back(key);
343 }
344
345 commands::KeyEvent key;
346 key.set_special_key(commands::KeyEvent::ENTER);
347 keys->push_back(key);
348 }
349
350 // basic keys + modifiers
351 {
352 for (size_t i = 0; i < basic_keys.size(); ++i) {
353 commands::KeyEvent key;
354 switch (Util::Random(8)) {
355 case 0:
356 key.set_key_code(kSpecialKeys[Util::Random(arraysize(kSpecialKeys))]);
357 break;
358 case 1:
359 key.set_key_code(GetRandomAsciiKey());
360 break;
361 default:
362 key.CopyFrom(basic_keys[i]);
363 break;
364 }
365
366 if (Util::Random(10) == 0) { // 10%
367 key.add_modifier_keys(commands::KeyEvent::CTRL);
368 }
369
370 if (Util::Random(10) == 0) { // 10%
371 key.add_modifier_keys(commands::KeyEvent::SHIFT);
372 }
373
374 if (Util::Random(50) == 0) { // 2%
375 key.add_modifier_keys(commands::KeyEvent::KEY_DOWN);
376 }
377
378 if (Util::Random(50) == 0) { // 2%
379 key.add_modifier_keys(commands::KeyEvent::KEY_UP);
380 }
381
382 keys->push_back(key);
383 }
384
385 // submit
386 commands::KeyEvent key;
387 key.set_special_key(commands::KeyEvent::ENTER);
388 keys->push_back(key);
389 }
390
391 CHECK_GT(keys->size(), 0);
392 VLOG(1) << "key sequence is generated: " << keys->size();
393 }
394
395 // static
GetTestSentences(size_t * size)396 const char **RandomKeyEventsGenerator::GetTestSentences(size_t *size) {
397 *size = arraysize(kTestSentences);
398 return kTestSentences;
399 }
400 } // namespace session
401 } // namespace mozc
402