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/session_usage_observer.h"
31 
32 #include <memory>
33 #include <string>
34 
35 #include "base/clock.h"
36 #include "base/clock_mock.h"
37 #include "base/logging.h"
38 #include "base/scheduler.h"
39 #include "base/scheduler_stub.h"
40 #include "base/system_util.h"
41 #include "base/util.h"
42 #include "config/stats_config_util.h"
43 #include "config/stats_config_util_mock.h"
44 #include "protocol/commands.pb.h"
45 #include "testing/base/public/googletest.h"
46 #include "testing/base/public/gunit.h"
47 #include "usage_stats/usage_stats.h"
48 #include "usage_stats/usage_stats.pb.h"
49 #include "usage_stats/usage_stats_testing_util.h"
50 
51 using mozc::usage_stats::Stats;
52 using mozc::usage_stats::UsageStats;
53 
54 DECLARE_string(test_tmpdir);
55 
56 namespace mozc {
57 namespace session {
58 
59 class SessionUsageObserverTest : public testing::Test {
60  protected:
SetUp()61   virtual void SetUp() {
62     SystemUtil::SetUserProfileDirectory(FLAGS_test_tmpdir);
63     UsageStats::ClearAllStatsForTest();
64 
65     Clock::SetClockForUnitTest(nullptr);
66 
67     scheduler_stub_.reset(new SchedulerStub);
68     Scheduler::SetSchedulerHandler(scheduler_stub_.get());
69 
70     stats_config_util_mock_.reset(new config::StatsConfigUtilMock);
71     config::StatsConfigUtil::SetHandler(stats_config_util_mock_.get());
72   }
73 
TearDown()74   virtual void TearDown() {
75     Clock::SetClockForUnitTest(nullptr);
76     Scheduler::SetSchedulerHandler(nullptr);
77     config::StatsConfigUtil::SetHandler(nullptr);
78 
79     UsageStats::ClearAllStatsForTest();
80   }
81 
EnsureSave() const82   void EnsureSave() const {
83     // Make sure to save stats.
84     const uint32 kWaitngUsecForEnsureSave = 10 * 60 * 1000;
85     scheduler_stub_->PutClockForward(kWaitngUsecForEnsureSave);
86   }
87 
SetDoubleValueStats(uint32 num,double total,double square_total,Stats::DoubleValueStats * double_stats)88   void SetDoubleValueStats(
89       uint32 num, double total, double square_total,
90       Stats::DoubleValueStats *double_stats) {
91     DCHECK(double_stats);
92     double_stats->set_num(num);
93     double_stats->set_total(total);
94     double_stats->set_square_total(square_total);
95   }
96 
SetEventStats(uint32 source_id,uint32 sx_num,double sx_total,double sx_square_total,uint32 sy_num,double sy_total,double sy_square_total,uint32 dx_num,double dx_total,double dx_square_total,uint32 dy_num,double dy_total,double dy_square_total,uint32 tl_num,double tl_total,double tl_square_total,Stats::TouchEventStats * event_stats)97   void SetEventStats(
98      uint32 source_id,
99      uint32 sx_num, double sx_total, double sx_square_total,
100      uint32 sy_num, double sy_total, double sy_square_total,
101      uint32 dx_num, double dx_total, double dx_square_total,
102      uint32 dy_num, double dy_total, double dy_square_total,
103      uint32 tl_num, double tl_total, double tl_square_total,
104      Stats::TouchEventStats *event_stats) {
105     event_stats->set_source_id(source_id);
106     SetDoubleValueStats(sx_num, sx_total, sx_square_total,
107                         event_stats->mutable_start_x_stats());
108     SetDoubleValueStats(sy_num, sy_total, sy_square_total,
109                         event_stats->mutable_start_y_stats());
110     SetDoubleValueStats(dx_num, dx_total, dx_square_total,
111                         event_stats->mutable_direction_x_stats());
112     SetDoubleValueStats(dy_num, dy_total, dy_square_total,
113                         event_stats->mutable_direction_y_stats());
114     SetDoubleValueStats(tl_num, tl_total, tl_square_total,
115                         event_stats->mutable_time_length_stats());
116   }
117 
118   std::unique_ptr<SchedulerStub> scheduler_stub_;
119   std::unique_ptr<config::StatsConfigUtilMock> stats_config_util_mock_;
120 };
121 
TEST_F(SessionUsageObserverTest,DoNotSaveWhenDeleted)122 TEST_F(SessionUsageObserverTest, DoNotSaveWhenDeleted) {
123   stats_config_util_mock_->SetEnabled(false);
124 
125   std::unique_ptr<SessionUsageObserver> observer(new SessionUsageObserver);
126 
127   // Add command
128   commands::Command command;
129   command.mutable_input()->set_type(commands::Input::NONE);
130   command.mutable_input()->set_id(0);
131   command.mutable_output()->set_consumed(true);
132   for (int i = 0; i < 5; ++i) {
133     observer->EvalCommandHandler(command);
134     EXPECT_STATS_NOT_EXIST("SessionAllEvent");
135   }
136 
137   observer.reset();
138   EXPECT_STATS_NOT_EXIST("SessionAllEvent");
139 }
140 
TEST_F(SessionUsageObserverTest,ClientSideStatsInfolist)141 TEST_F(SessionUsageObserverTest, ClientSideStatsInfolist) {
142   std::unique_ptr<SessionUsageObserver> observer(new SessionUsageObserver);
143 
144   // create session
145   {
146     commands::Command command;
147     command.mutable_input()->set_type(commands::Input::CREATE_SESSION);
148     command.mutable_input()->set_id(1);
149     command.mutable_output()->set_id(1);
150     observer->EvalCommandHandler(command);
151   }
152 
153   const uint64 kSeconds = 0;
154   const uint32 kMicroSeconds = 0;
155   ClockMock clock(kSeconds, kMicroSeconds);
156   Clock::SetClockForUnitTest(&clock);
157 
158   // prepare command
159   commands::Command orig_show_command, orig_hide_command;
160   orig_show_command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
161   orig_show_command.mutable_input()->set_id(1);
162   orig_show_command.mutable_input()->mutable_command()->set_type(
163       commands::SessionCommand::USAGE_STATS_EVENT);
164   orig_show_command.mutable_input()->mutable_command()->set_usage_stats_event(
165       commands::SessionCommand::INFOLIST_WINDOW_SHOW);
166   orig_show_command.mutable_output()->set_consumed(false);
167   EXPECT_TRUE(orig_show_command.output().has_consumed());
168   EXPECT_FALSE(orig_show_command.output().consumed());
169   EXPECT_TRUE(orig_show_command.input().has_id());
170   orig_hide_command.CopyFrom(orig_show_command);
171   orig_hide_command.mutable_input()->mutable_command()->set_usage_stats_event(
172       commands::SessionCommand::INFOLIST_WINDOW_HIDE);
173 
174   {  // show infolist, wait 1,100,000 usec and hide infolist.
175     commands::Command show_command, hide_command;
176     show_command.CopyFrom(orig_show_command);
177     hide_command.CopyFrom(orig_hide_command);
178 
179     observer->EvalCommandHandler(show_command);
180     EXPECT_STATS_NOT_EXIST("InfolistWindowDurationMSec");
181     clock.PutClockForward(1, 100000);
182     observer->EvalCommandHandler(hide_command);
183     EXPECT_TIMING_STATS("InfolistWindowDurationMSec", 1100, 1, 1100, 1100);
184   }
185 
186   {  // show infolist, wait 1,200,000 usec and hide infolist.
187     commands::Command show_command, hide_command;
188     show_command.CopyFrom(orig_show_command);
189     hide_command.CopyFrom(orig_hide_command);
190 
191     observer->EvalCommandHandler(show_command);
192     clock.PutClockForward(1, 200000);
193     observer->EvalCommandHandler(hide_command);
194     EXPECT_TIMING_STATS("InfolistWindowDurationMSec", 2300, 2, 1100, 1200);
195   }
196 }
197 
TEST_F(SessionUsageObserverTest,ClientSideStatsSoftwareKeyboardLayout)198 TEST_F(SessionUsageObserverTest, ClientSideStatsSoftwareKeyboardLayout) {
199   SessionUsageObserver observer;
200 
201   // create session
202   commands::Command command;
203   command.mutable_input()->set_type(commands::Input::CREATE_SESSION);
204   command.mutable_input()->set_id(1);
205   command.mutable_output()->set_id(1);
206   observer.EvalCommandHandler(command);
207 
208   EXPECT_STATS_NOT_EXIST("SoftwareKeyboardLayoutLandscape");
209   EXPECT_STATS_NOT_EXIST("SoftwareKeyboardLayoutPortrait");
210 
211   command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
212   commands::SessionCommand *session_command =
213       command.mutable_input()->mutable_command();
214   session_command->set_type(commands::SessionCommand::USAGE_STATS_EVENT);
215   session_command->set_usage_stats_event(
216       commands::SessionCommand::SOFTWARE_KEYBOARD_LAYOUT_LANDSCAPE);
217   session_command->set_usage_stats_event_int_value(1);
218   observer.EvalCommandHandler(command);
219   EXPECT_INTEGER_STATS("SoftwareKeyboardLayoutLandscape", 1);
220   EXPECT_STATS_NOT_EXIST("SoftwareKeyboardLayoutPortrait");
221 
222   session_command->set_usage_stats_event_int_value(2);
223   observer.EvalCommandHandler(command);
224   EXPECT_INTEGER_STATS("SoftwareKeyboardLayoutLandscape", 2);
225   EXPECT_STATS_NOT_EXIST("SoftwareKeyboardLayoutPortrait");
226 
227   session_command->set_usage_stats_event(
228       commands::SessionCommand::SOFTWARE_KEYBOARD_LAYOUT_PORTRAIT);
229   session_command->set_usage_stats_event_int_value(3);
230   observer.EvalCommandHandler(command);
231   EXPECT_INTEGER_STATS("SoftwareKeyboardLayoutLandscape", 2);
232   EXPECT_INTEGER_STATS("SoftwareKeyboardLayoutPortrait", 3);
233 }
234 
TEST_F(SessionUsageObserverTest,SubmittedCandidateRow)235 TEST_F(SessionUsageObserverTest, SubmittedCandidateRow) {
236   SessionUsageObserver observer;
237 
238   // create session
239   commands::Command command;
240   command.mutable_input()->set_type(commands::Input::CREATE_SESSION);
241   command.mutable_input()->set_id(1);
242   command.mutable_output()->set_id(1);
243   observer.EvalCommandHandler(command);
244 
245   EXPECT_STATS_NOT_EXIST("SubmittedCandidateRow0");
246   EXPECT_STATS_NOT_EXIST("SubmittedCandidateRow1");
247   EXPECT_STATS_NOT_EXIST("SubmittedCandidateRow2");
248   EXPECT_STATS_NOT_EXIST("SubmittedCandidateRow3");
249   EXPECT_STATS_NOT_EXIST("SubmittedCandidateRow4");
250   EXPECT_STATS_NOT_EXIST("SubmittedCandidateRow5");
251   EXPECT_STATS_NOT_EXIST("SubmittedCandidateRow6");
252   EXPECT_STATS_NOT_EXIST("SubmittedCandidateRow7");
253   EXPECT_STATS_NOT_EXIST("SubmittedCandidateRow8");
254   EXPECT_STATS_NOT_EXIST("SubmittedCandidateRow9");
255   EXPECT_STATS_NOT_EXIST("SubmittedCandidateRowGE10");
256 
257   command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
258   commands::SessionCommand *session_command =
259       command.mutable_input()->mutable_command();
260   session_command->set_type(commands::SessionCommand::USAGE_STATS_EVENT);
261   session_command->set_usage_stats_event(
262       commands::SessionCommand::SUBMITTED_CANDIDATE_ROW_0);
263   observer.EvalCommandHandler(command);
264   EXPECT_COUNT_STATS("SubmittedCandidateRow0", 1);
265 
266   observer.EvalCommandHandler(command);
267   EXPECT_COUNT_STATS("SubmittedCandidateRow0", 2);
268 }
269 
TEST_F(SessionUsageObserverTest,LogTouchEvent)270 TEST_F(SessionUsageObserverTest, LogTouchEvent) {
271   std::unique_ptr<SessionUsageObserver> observer(new SessionUsageObserver);
272 
273   // create session
274   {
275     commands::Command command;
276     command.mutable_input()->set_type(commands::Input::CREATE_SESSION);
277     command.mutable_input()->set_id(1);
278     command.mutable_output()->set_id(1);
279     observer->EvalCommandHandler(command);
280   }
281   // set keyboard
282   {
283     commands::Command command;
284     command.mutable_input()->set_type(commands::Input::SET_REQUEST);
285     command.mutable_input()->set_id(1);
286     command.mutable_input()->mutable_request()->set_keyboard_name("KB1");
287     observer->EvalCommandHandler(command);
288   }
289   {
290     commands::Command command;
291     command.mutable_input()->set_type(commands::Input::SEND_KEY);
292     command.mutable_input()->set_id(1);
293     commands::Input::TouchEvent *touch_event =
294         command.mutable_input()->add_touch_events();
295     touch_event->set_source_id(10);
296     commands::Input::TouchPosition *pos1 = touch_event->add_stroke();
297     pos1->set_action(commands::Input::TOUCH_DOWN);
298     pos1->set_x(1);
299     pos1->set_y(2);
300     pos1->set_timestamp(0);
301     commands::Input::TouchPosition *pos2 = touch_event->add_stroke();
302     pos2->set_action(commands::Input::TOUCH_UP);
303     pos2->set_x(2);
304     pos2->set_y(1);
305     pos2->set_timestamp(1500);
306     observer->EvalCommandHandler(command);
307   }
308   {
309     commands::Command command;
310     command.mutable_input()->set_type(commands::Input::SEND_KEY);
311     command.mutable_input()->set_id(1);
312     commands::Input::TouchEvent *touch_event =
313         command.mutable_input()->add_touch_events();
314     touch_event->set_source_id(10);
315     commands::Input::TouchPosition *pos1 = touch_event->add_stroke();
316     pos1->set_action(commands::Input::TOUCH_DOWN);
317     pos1->set_x(2);
318     pos1->set_y(2);
319     pos1->set_timestamp(0);
320     commands::Input::TouchPosition *pos2 = touch_event->add_stroke();
321     pos2->set_action(commands::Input::TOUCH_MOVE);
322     pos2->set_x(2);
323     pos2->set_y(2);
324     pos2->set_timestamp(1000);
325     commands::Input::TouchPosition *pos3 = touch_event->add_stroke();
326     pos3->set_action(commands::Input::TOUCH_MOVE);
327     pos3->set_x(1);
328     pos3->set_y(1);
329     pos3->set_timestamp(2000);
330     observer->EvalCommandHandler(command);
331   }
332   // change keyboard
333   {
334     commands::Command command;
335     command.mutable_input()->set_type(commands::Input::SET_REQUEST);
336     command.mutable_input()->set_id(1);
337     command.mutable_input()->mutable_request()->set_keyboard_name("KB2");
338     commands::Input::TouchEvent *touch_event =
339         command.mutable_input()->add_touch_events();
340     touch_event->set_source_id(100);
341     commands::Input::TouchPosition *pos1 = touch_event->add_stroke();
342     pos1->set_action(commands::Input::TOUCH_DOWN);
343     pos1->set_x(1);
344     pos1->set_y(1);
345     pos1->set_timestamp(0);
346     commands::Input::TouchPosition *pos2 = touch_event->add_stroke();
347     pos2->set_action(commands::Input::TOUCH_UP);
348     pos2->set_x(1);
349     pos2->set_y(1);
350     pos2->set_timestamp(1000);
351     observer->EvalCommandHandler(command);
352   }
353   {
354     commands::Command command;
355     command.mutable_input()->set_type(commands::Input::SEND_KEY);
356     command.mutable_input()->set_id(1);
357     commands::Input::TouchEvent *touch_event =
358         command.mutable_input()->add_touch_events();
359     touch_event->set_source_id(10);
360     commands::Input::TouchPosition *pos1 = touch_event->add_stroke();
361     pos1->set_action(commands::Input::TOUCH_DOWN);
362     pos1->set_x(1);
363     pos1->set_y(2);
364     pos1->set_timestamp(0);
365     commands::Input::TouchPosition *pos2 = touch_event->add_stroke();
366     pos2->set_action(commands::Input::TOUCH_UP);
367     pos2->set_x(2);
368     pos2->set_y(1);
369     pos2->set_timestamp(1500);
370     observer->EvalCommandHandler(command);
371   }
372   {
373     commands::Command command;
374     command.mutable_input()->set_type(commands::Input::SEND_KEY);
375     command.mutable_input()->set_id(1);
376     commands::Input::TouchEvent *touch_event =
377         command.mutable_input()->add_touch_events();
378     touch_event->set_source_id(20);
379     commands::Input::TouchPosition *pos1 = touch_event->add_stroke();
380     pos1->set_action(commands::Input::TOUCH_DOWN);
381     pos1->set_x(2);
382     pos1->set_y(2);
383     pos1->set_timestamp(0);
384     commands::Input::TouchPosition *pos2 = touch_event->add_stroke();
385     pos2->set_action(commands::Input::TOUCH_UP);
386     pos2->set_x(1);
387     pos2->set_y(1);
388     pos2->set_timestamp(2000);
389 
390     // add preedit
391     command.mutable_output()->mutable_preedit();
392     command.mutable_output()->set_consumed(true);
393     observer->EvalCommandHandler(command);
394   }
395   {
396     // send BACKSPACE
397     commands::Command command;
398     command.mutable_input()->set_type(commands::Input::SEND_KEY);
399     command.mutable_input()->set_id(1);
400     command.mutable_input()->mutable_key()->set_special_key(
401         commands::KeyEvent::BACKSPACE);
402     observer->EvalCommandHandler(command);
403   }
404   {
405     commands::Command command;
406     command.mutable_input()->set_type(commands::Input::SEND_KEY);
407     command.mutable_input()->set_id(1);
408     commands::Input::TouchEvent *touch_event =
409         command.mutable_input()->add_touch_events();
410     touch_event->set_source_id(30);
411     commands::Input::TouchPosition *pos1 = touch_event->add_stroke();
412     pos1->set_action(commands::Input::TOUCH_DOWN);
413     pos1->set_x(2);
414     pos1->set_y(2);
415     pos1->set_timestamp(0);
416     commands::Input::TouchPosition *pos2 = touch_event->add_stroke();
417     pos2->set_action(commands::Input::TOUCH_MOVE);
418     pos2->set_x(1);
419     pos2->set_y(1);
420     pos2->set_timestamp(1000);
421     commands::Input::TouchPosition *pos3 = touch_event->add_stroke();
422     pos3->set_action(commands::Input::TOUCH_UP);
423     pos3->set_x(1);
424     pos3->set_y(3);
425     pos3->set_timestamp(2000);
426     observer->EvalCommandHandler(command);
427   }
428   {
429     commands::Command command;
430     command.mutable_input()->set_type(commands::Input::NONE);
431     command.mutable_input()->set_id(1);
432     observer->EvalCommandHandler(command);
433   }
434 
435   EXPECT_STATS_NOT_EXIST("VirtualKeyboardStats");
436   EXPECT_STATS_NOT_EXIST("VirtualKeyboardMissStats");
437   EnsureSave();
438 
439   {
440     Stats stats;
441     UsageStats::GetVirtualKeyboardForTest("VirtualKeyboardStats", &stats);
442     ASSERT_EQ(2, stats.virtual_keyboard_stats_size());
443     ASSERT_EQ(2, stats.virtual_keyboard_stats(0).touch_event_stats_size());
444 
445     Stats::TouchEventStats expected_event_stats;
446     SetEventStats(10, 2, 3, 5, 2, 4, 8, 2, 0, 2, 2, -2, 2, 2, 3.5, 6.25,
447                   &expected_event_stats);
448     EXPECT_EQ(
449         expected_event_stats.DebugString(),
450         stats.virtual_keyboard_stats(0).touch_event_stats(0).DebugString());
451 
452     SetEventStats(100, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1,
453                   &expected_event_stats);
454     EXPECT_EQ(
455         expected_event_stats.DebugString(),
456         stats.virtual_keyboard_stats(0).touch_event_stats(1).DebugString());
457 
458     ASSERT_EQ(2, stats.virtual_keyboard_stats(1).touch_event_stats_size());
459 
460     SetEventStats(10, 1, 1, 1, 1, 2, 4, 1, 1, 1, 1, -1, 1, 1, 1.5, 2.25,
461                   &expected_event_stats);
462     EXPECT_EQ(
463         expected_event_stats.DebugString(),
464         stats.virtual_keyboard_stats(1).touch_event_stats(0).DebugString());
465 
466     SetEventStats(30, 1, 2, 4, 1, 2, 4, 1, -1, 1, 1, 1, 1, 1, 2, 4,
467                   &expected_event_stats);
468     EXPECT_EQ(
469         expected_event_stats.DebugString(),
470         stats.virtual_keyboard_stats(1).touch_event_stats(1).DebugString());
471   }
472   {
473     Stats stats;
474     UsageStats::GetVirtualKeyboardForTest("VirtualKeyboardMissStats", &stats);
475     ASSERT_EQ(1, stats.virtual_keyboard_stats_size());
476     ASSERT_EQ(1, stats.virtual_keyboard_stats(0).touch_event_stats_size());
477     Stats::TouchEventStats expected_event_stats;
478     SetEventStats(20, 1, 2, 4, 1, 2, 4, 1, -1, 1, 1, -1, 1, 1, 2, 4,
479                   &expected_event_stats);
480     EXPECT_EQ(
481         expected_event_stats.DebugString(),
482         stats.virtual_keyboard_stats(0).touch_event_stats(0).DebugString());
483   }
484 }
485 
TEST_F(SessionUsageObserverTest,LogTouchEventPasswordField)486 TEST_F(SessionUsageObserverTest, LogTouchEventPasswordField) {
487   std::unique_ptr<SessionUsageObserver> observer(new SessionUsageObserver);
488 
489   // create session
490   {
491     commands::Command command;
492     command.mutable_input()->set_type(commands::Input::CREATE_SESSION);
493     command.mutable_input()->set_id(1);
494     command.mutable_output()->set_id(1);
495     observer->EvalCommandHandler(command);
496   }
497   // set keyboard
498   {
499     commands::Command command;
500     command.mutable_input()->set_type(commands::Input::SET_REQUEST);
501     command.mutable_input()->set_id(1);
502     command.mutable_input()->mutable_request()->set_keyboard_name("KB1");
503     observer->EvalCommandHandler(command);
504   }
505   {
506     commands::Command command;
507     command.mutable_input()->set_type(commands::Input::SEND_KEY);
508     command.mutable_input()->set_id(1);
509     commands::Input::TouchEvent *touch_event =
510         command.mutable_input()->add_touch_events();
511     touch_event->set_source_id(10);
512     commands::Input::TouchPosition *pos1 = touch_event->add_stroke();
513     pos1->set_action(commands::Input::TOUCH_DOWN);
514     pos1->set_x(1);
515     pos1->set_y(1);
516     pos1->set_timestamp(0);
517     commands::Input::TouchPosition *pos2 = touch_event->add_stroke();
518     pos2->set_action(commands::Input::TOUCH_UP);
519     pos2->set_x(1);
520     pos2->set_y(1);
521     pos2->set_timestamp(1000);
522     observer->EvalCommandHandler(command);
523   }
524   {
525     commands::Command command;
526     command.mutable_input()->set_type(commands::Input::SEND_KEY);
527     command.mutable_input()->set_id(1);
528     commands::Input::TouchEvent *touch_event =
529         command.mutable_input()->add_touch_events();
530     touch_event->set_source_id(20);
531     commands::Input::TouchPosition *pos1 = touch_event->add_stroke();
532     pos1->set_action(commands::Input::TOUCH_DOWN);
533     pos1->set_x(1);
534     pos1->set_y(1);
535     pos1->set_timestamp(0);
536     commands::Input::TouchPosition *pos2 = touch_event->add_stroke();
537     pos2->set_action(commands::Input::TOUCH_MOVE);
538     pos2->set_x(1);
539     pos2->set_y(1);
540     pos2->set_timestamp(1000);
541     observer->EvalCommandHandler(command);
542   }
543   {
544     // Changes INPUT_FIELD_TYPE to PASSWORD
545     commands::Command command;
546     command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
547     command.mutable_input()->set_id(1);
548     command.mutable_input()->mutable_command()->set_type(
549         commands::SessionCommand::SWITCH_INPUT_FIELD_TYPE);
550     command.mutable_input()->mutable_context()->set_input_field_type(
551         commands::Context::PASSWORD);
552     observer->EvalCommandHandler(command);
553   }
554   {
555     commands::Command command;
556     command.mutable_input()->set_type(commands::Input::SEND_KEY);
557     command.mutable_input()->set_id(1);
558     commands::Input::TouchEvent *touch_event =
559         command.mutable_input()->add_touch_events();
560     touch_event->set_source_id(30);
561     commands::Input::TouchPosition *pos1 = touch_event->add_stroke();
562     pos1->set_action(commands::Input::TOUCH_DOWN);
563     pos1->set_x(1);
564     pos1->set_y(1);
565     pos1->set_timestamp(0);
566     commands::Input::TouchPosition *pos2 = touch_event->add_stroke();
567     pos2->set_action(commands::Input::TOUCH_UP);
568     pos2->set_x(1);
569     pos2->set_y(1);
570     pos2->set_timestamp(1000);
571     observer->EvalCommandHandler(command);
572   }
573   {
574     // Changes INPUT_FIELD_TYPE to NORMAL
575     commands::Command command;
576     command.mutable_input()->set_type(commands::Input::SEND_COMMAND);
577     command.mutable_input()->set_id(1);
578     command.mutable_input()->mutable_command()->set_type(
579         commands::SessionCommand::SWITCH_INPUT_FIELD_TYPE);
580     command.mutable_input()->mutable_context()->set_input_field_type(
581         commands::Context::NORMAL);
582     observer->EvalCommandHandler(command);
583   }
584   {
585     commands::Command command;
586     command.mutable_input()->set_type(commands::Input::SEND_KEY);
587     command.mutable_input()->set_id(1);
588     commands::Input::TouchEvent *touch_event =
589         command.mutable_input()->add_touch_events();
590     touch_event->set_source_id(40);
591     commands::Input::TouchPosition *pos1 = touch_event->add_stroke();
592     pos1->set_action(commands::Input::TOUCH_DOWN);
593     pos1->set_x(1);
594     pos1->set_y(1);
595     pos1->set_timestamp(0);
596     commands::Input::TouchPosition *pos2 = touch_event->add_stroke();
597     pos2->set_action(commands::Input::TOUCH_UP);
598     pos2->set_x(1);
599     pos2->set_y(1);
600     pos2->set_timestamp(1000);
601     observer->EvalCommandHandler(command);
602   }
603   {
604     commands::Command command;
605     command.mutable_input()->set_type(commands::Input::NONE);
606     command.mutable_input()->set_id(1);
607     observer->EvalCommandHandler(command);
608   }
609 
610   EXPECT_STATS_NOT_EXIST("VirtualKeyboardStats");
611   EXPECT_STATS_NOT_EXIST("VirtualKeyboardMissStats");
612   EnsureSave();
613 
614   {
615     Stats stats;
616     UsageStats::GetVirtualKeyboardForTest("VirtualKeyboardStats", &stats);
617     ASSERT_EQ(1, stats.virtual_keyboard_stats_size());
618     ASSERT_EQ(3, stats.virtual_keyboard_stats(0).touch_event_stats_size());
619 
620     Stats::TouchEventStats expected_event_stats;
621     SetEventStats(10, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1,
622                   &expected_event_stats);
623     EXPECT_EQ(
624         expected_event_stats.DebugString(),
625         stats.virtual_keyboard_stats(0).touch_event_stats(0).DebugString());
626     SetEventStats(20, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1,
627                   &expected_event_stats);
628     EXPECT_EQ(
629         expected_event_stats.DebugString(),
630         stats.virtual_keyboard_stats(0).touch_event_stats(1).DebugString());
631     SetEventStats(40, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1,
632                   &expected_event_stats);
633     EXPECT_EQ(
634         expected_event_stats.DebugString(),
635         stats.virtual_keyboard_stats(0).touch_event_stats(2).DebugString());
636   }
637 }
638 
639 }  // namespace session
640 }  // namespace mozc
641