1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4import time
5import unittest
6
7from telemetry import decorators
8from telemetry.page import page as page_module
9from telemetry.page import legacy_page_test
10from telemetry.testing import page_test_test_case
11from telemetry.timeline import async_slice
12from telemetry.timeline import model as model_module
13from telemetry.timeline import tracing_config
14from telemetry.web_perf import smooth_gesture_util as sg_util
15from telemetry.web_perf import timeline_interaction_record as tir_module
16
17
18class SmoothGestureUtilTest(unittest.TestCase):
19  def testGetAdjustedInteractionIfContainGesture(self):
20    model = model_module.TimelineModel()
21    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
22    renderer_main.name = 'CrRendererMain'
23
24    #      [          X          ]                   [   Y  ]
25    #      [  sub_async_slice_X  ]
26    #          [   record_1]
27    #          [   record_6]
28    #  [  record_2 ]          [ record_3 ]
29    #  [           record_4              ]
30    #                                [ record_5 ]
31    #
32    # Note: X and Y are async slice with name
33    # SyntheticGestureController::running
34
35    async_slice_X = async_slice.AsyncSlice(
36      'X', 'SyntheticGestureController::running', 10, duration=20,
37      start_thread=renderer_main, end_thread=renderer_main)
38
39    sub_async_slice_X = async_slice.AsyncSlice(
40      'X', 'SyntheticGestureController::running', 10, duration=20,
41      start_thread=renderer_main, end_thread=renderer_main)
42    sub_async_slice_X.parent_slice = async_slice_X
43    async_slice_X.AddSubSlice(sub_async_slice_X)
44
45    async_slice_Y = async_slice.AsyncSlice(
46      'X', 'SyntheticGestureController::running', 60, duration=20,
47      start_thread=renderer_main, end_thread=renderer_main)
48
49    renderer_main.AddAsyncSlice(async_slice_X)
50    renderer_main.AddAsyncSlice(async_slice_Y)
51
52    model.FinalizeImport(shift_world_to_zero=False)
53
54    record_1 = tir_module.TimelineInteractionRecord('Gesture_included', 15, 25)
55    record_2 = tir_module.TimelineInteractionRecord(
56      'Gesture_overlapped_left', 5, 25)
57    record_3 = tir_module.TimelineInteractionRecord(
58      'Gesture_overlapped_right', 25, 35)
59    record_4 = tir_module.TimelineInteractionRecord(
60      'Gesture_containing', 5, 35)
61    record_5 = tir_module.TimelineInteractionRecord(
62      'Gesture_non_overlapped', 35, 45)
63    record_6 = tir_module.TimelineInteractionRecord('Action_included', 15, 25)
64
65    adjusted_record_1 = sg_util.GetAdjustedInteractionIfContainGesture(
66      model, record_1)
67    self.assertEquals(adjusted_record_1.start, 10)
68    self.assertEquals(adjusted_record_1.end, 30)
69    self.assertTrue(adjusted_record_1 is not record_1)
70
71    adjusted_record_2 = sg_util.GetAdjustedInteractionIfContainGesture(
72      model, record_2)
73    self.assertEquals(adjusted_record_2.start, 10)
74    self.assertEquals(adjusted_record_2.end, 30)
75
76    adjusted_record_3 = sg_util.GetAdjustedInteractionIfContainGesture(
77      model, record_3)
78    self.assertEquals(adjusted_record_3.start, 10)
79    self.assertEquals(adjusted_record_3.end, 30)
80
81    adjusted_record_4 = sg_util.GetAdjustedInteractionIfContainGesture(
82      model, record_4)
83    self.assertEquals(adjusted_record_4.start, 10)
84    self.assertEquals(adjusted_record_4.end, 30)
85
86    adjusted_record_5 = sg_util.GetAdjustedInteractionIfContainGesture(
87      model, record_5)
88    self.assertEquals(adjusted_record_5.start, 35)
89    self.assertEquals(adjusted_record_5.end, 45)
90    self.assertTrue(adjusted_record_5 is not record_5)
91
92    adjusted_record_6 = sg_util.GetAdjustedInteractionIfContainGesture(
93      model, record_6)
94    self.assertEquals(adjusted_record_6.start, 15)
95    self.assertEquals(adjusted_record_6.end, 25)
96    self.assertTrue(adjusted_record_6 is not record_6)
97
98
99class ScrollingPage(page_module.Page):
100  def __init__(self, url, page_set, base_dir):
101    super(ScrollingPage, self).__init__(url, page_set, base_dir)
102
103  def RunPageInteractions(self, action_runner):
104    with action_runner.CreateGestureInteraction('ScrollAction'):
105      # Add 0.5s gap between when Gesture records are issued to when we actually
106      # scroll the page.
107      time.sleep(0.5)
108      action_runner.ScrollPage()
109      time.sleep(0.5)
110
111
112class SmoothGestureTest(page_test_test_case.PageTestTestCase):
113
114  @decorators.Disabled('chromeos')  # crbug.com/483212
115  @decorators.Isolated  # Needed because of py_trace_event
116  def testSmoothGestureAdjusted(self):
117    ps = self.CreateEmptyPageSet()
118    ps.AddStory(ScrollingPage(
119      'file://scrollable_page.html', ps, base_dir=ps.base_dir))
120    models = []
121    tab_ids = []
122    class ScrollingGestureTestMeasurement(legacy_page_test.LegacyPageTest):
123      def __init__(self):
124        # pylint: disable=bad-super-call
125        super(ScrollingGestureTestMeasurement, self).__init__()
126
127      def WillNavigateToPage(self, page, tab):
128        del page  # unused
129        config = tracing_config.TracingConfig()
130        config.enable_chrome_trace = True
131        tab.browser.platform.tracing_controller.StartTracing(config)
132
133      def ValidateAndMeasurePage(self, page, tab, results):
134        del page, results  # unused
135        models.append(model_module.TimelineModel(
136          tab.browser.platform.tracing_controller.StopTracing()))
137        tab_ids.append(tab.id)
138
139    self.RunMeasurement(ScrollingGestureTestMeasurement(), ps)
140    timeline_model = models[0]
141    renderer_thread = timeline_model.GetRendererThreadFromTabId(
142        tab_ids[0])
143    smooth_record = None
144    for e in renderer_thread.async_slices:
145      if tir_module.IsTimelineInteractionRecord(e.name):
146        smooth_record = tir_module.TimelineInteractionRecord.FromAsyncEvent(e)
147    self.assertIsNotNone(smooth_record)
148    adjusted_smooth_gesture = (
149      sg_util.GetAdjustedInteractionIfContainGesture(
150        timeline_model, smooth_record))
151    # Test that the scroll gesture starts at at least 500ms after the start of
152    # the interaction record and ends at at least 500ms before the end of
153    # interaction record.
154    self.assertLessEqual(
155      500, adjusted_smooth_gesture.start - smooth_record.start)
156    self.assertLessEqual(
157      500, smooth_record.end - adjusted_smooth_gesture.end)
158