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.
4
5import unittest
6
7from telemetry.timeline import model as model_module
8from telemetry.timeline import slice as slice_module
9from telemetry.timeline import async_slice
10from telemetry.web_perf import timeline_interaction_record as tir_module
11
12
13class ParseTests(unittest.TestCase):
14
15  def testParse(self):
16    self.assertTrue(tir_module.IsTimelineInteractionRecord(
17        'Interaction.Foo'))
18    self.assertTrue(tir_module.IsTimelineInteractionRecord(
19        'Interaction.Foo/Bar'))
20    self.assertFalse(tir_module.IsTimelineInteractionRecord(
21        'SomethingRandom'))
22
23
24class TimelineInteractionRecordTests(unittest.TestCase):
25
26  def CreateSimpleRecordWithName(self, event_name):
27    s = async_slice.AsyncSlice(
28        'cat', event_name,
29        timestamp=0, duration=200, thread_start=20, thread_duration=100)
30    return tir_module.TimelineInteractionRecord.FromAsyncEvent(s)
31
32  def CreateTestSliceFromTimeRanges(
33      self, parent_thread, time_start, time_end, thread_start, thread_end):
34    duration = time_end - time_start
35    thread_duration = thread_end - thread_start
36    return slice_module.Slice(parent_thread, 'Test', 'foo', time_start,
37                              duration, thread_start, thread_duration)
38
39  def testCreate(self):
40    r = self.CreateSimpleRecordWithName('Interaction.LogicalName')
41    self.assertEquals('LogicalName', r.label)
42    self.assertEquals(False, r.is_smooth)
43    self.assertEquals(False, r.is_responsive)
44
45    r = self.CreateSimpleRecordWithName('Interaction.LogicalName/is_smooth')
46    self.assertEquals('LogicalName', r.label)
47    self.assertEquals(True, r.is_smooth)
48    self.assertEquals(False, r.is_responsive)
49
50    r = self.CreateSimpleRecordWithName(
51        'Interaction.LogicalNameWith/Slash/is_smooth')
52    self.assertEquals('LogicalNameWith/Slash', r.label)
53    self.assertEquals(True, r.is_smooth)
54    self.assertEquals(False, r.is_responsive)
55
56    r = self.CreateSimpleRecordWithName(
57        'Interaction.LogicalNameWith/Slash/is_smooth,is_responsive')
58    self.assertEquals('LogicalNameWith/Slash', r.label)
59    self.assertEquals(True, r.is_smooth)
60    self.assertEquals(True, r.is_responsive)
61
62  def testGetJavaScriptMarker(self):
63    smooth_marker = tir_module.GetJavaScriptMarker(
64        'MyLabel', [tir_module.IS_SMOOTH])
65    self.assertEquals('Interaction.MyLabel/is_smooth', smooth_marker)
66    slr_marker = tir_module.GetJavaScriptMarker(
67        'MyLabel', [tir_module.IS_SMOOTH, tir_module.IS_RESPONSIVE])
68    self.assertEquals('Interaction.MyLabel/is_smooth,is_responsive', slr_marker)
69
70  def testGetOverlappedThreadTimeForSliceInSameThread(self):
71    # Create a renderer thread.
72    model = model_module.TimelineModel()
73    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
74    model.FinalizeImport()
75
76   # Make a record that starts at 30ms and ends at 60ms in thread time.
77    s = async_slice.AsyncSlice(
78        'cat', 'Interaction.Test/is_smooth',
79        timestamp=0, duration=200, start_thread=renderer_main,
80        end_thread=renderer_main, thread_start=30, thread_duration=30)
81    record = tir_module.TimelineInteractionRecord.FromAsyncEvent(s)
82
83    # Non overlapped range on the left of event.
84    s1 = self.CreateTestSliceFromTimeRanges(renderer_main, 0, 100, 10, 20)
85    self.assertEquals(0, record.GetOverlappedThreadTimeForSlice(s1))
86
87    # Non overlapped range on the right of event.
88    s2 = self.CreateTestSliceFromTimeRanges(renderer_main, 0, 100, 70, 90)
89    self.assertEquals(0, record.GetOverlappedThreadTimeForSlice(s2))
90
91    # Overlapped range on the left of event.
92    s3 = self.CreateTestSliceFromTimeRanges(renderer_main, 0, 100, 20, 50)
93    self.assertEquals(20, record.GetOverlappedThreadTimeForSlice(s3))
94
95    # Overlapped range in the middle of event.
96    s4 = self.CreateTestSliceFromTimeRanges(renderer_main, 0, 100, 40, 50)
97    self.assertEquals(10, record.GetOverlappedThreadTimeForSlice(s4))
98
99    # Overlapped range on the left of event.
100    s5 = self.CreateTestSliceFromTimeRanges(renderer_main, 0, 100, 50, 90)
101    self.assertEquals(10, record.GetOverlappedThreadTimeForSlice(s5))
102
103  def testRepr(self):
104    # Create a renderer thread.
105    model = model_module.TimelineModel()
106    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
107    model.FinalizeImport()
108
109    s = async_slice.AsyncSlice(
110        'cat', 'Interaction.Test/is_smooth',
111        timestamp=0, duration=200, start_thread=renderer_main,
112        end_thread=renderer_main, thread_start=30, thread_duration=30)
113    record = tir_module.TimelineInteractionRecord.FromAsyncEvent(s)
114    expected_repr = (
115        'TimelineInteractionRecord(label=\'Test\', '
116        'start=0.000000, end=200.000000, flags=is_smooth, '
117        'async_event=TimelineEvent(name=\'Interaction.Test/is_smooth\','
118        ' start=0.000000, duration=200, thread_start=30, thread_duration=30))')
119    self.assertEquals(expected_repr, repr(record))
120
121
122  def testGetOverlappedThreadTimeForSliceInDifferentThread(self):
123    # Create a renderer thread and another thread.
124    model = model_module.TimelineModel()
125    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
126    another_thread = model.GetOrCreateProcess(1).GetOrCreateThread(3)
127    model.FinalizeImport()
128
129   # Make a record that starts at 50ms and ends at 150ms in wall time, and is
130   # scheduled 75% of the time (hence thread_duration = 100ms*75% = 75ms).
131    s = async_slice.AsyncSlice(
132        'cat', 'Interaction.Test/is_smooth',
133        timestamp=50, duration=100, start_thread=renderer_main,
134        end_thread=renderer_main, thread_start=55, thread_duration=75)
135    record = tir_module.TimelineInteractionRecord.FromAsyncEvent(s)
136
137    # Non overlapped range on the left of event.
138    s1 = self.CreateTestSliceFromTimeRanges(another_thread, 25, 40, 28, 30)
139    self.assertEquals(0, record.GetOverlappedThreadTimeForSlice(s1))
140
141    # Non overlapped range on the right of event.
142    s2 = self.CreateTestSliceFromTimeRanges(another_thread, 200, 300, 270, 290)
143    self.assertEquals(0, record.GetOverlappedThreadTimeForSlice(s2))
144
145    # Overlapped range on the left of event, and slice is scheduled 50% of the
146    # time.
147    # The overlapped wall-time duration is 50ms.
148    # The overlapped thread-time duration is 50ms * 75% * 50% = 18.75
149    s3 = self.CreateTestSliceFromTimeRanges(another_thread, 0, 100, 20, 70)
150    self.assertEquals(18.75, record.GetOverlappedThreadTimeForSlice(s3))
151
152    # Overlapped range in the middle of event, and slice is scheduled 20% of the
153    # time.
154    # The overlapped wall-time duration is 40ms.
155    # The overlapped thread-time duration is 40ms * 75% * 20% = 6
156    s4 = self.CreateTestSliceFromTimeRanges(another_thread, 100, 140, 120, 128)
157    self.assertEquals(6, record.GetOverlappedThreadTimeForSlice(s4))
158
159    # Overlapped range on the left of event, and slice is scheduled 100% of the
160    # time.
161    # The overlapped wall-time duration is 32ms.
162    # The overlapped thread-time duration is 32ms * 75% * 100% = 24
163    s5 = self.CreateTestSliceFromTimeRanges(another_thread, 118, 170, 118, 170)
164    self.assertEquals(24, record.GetOverlappedThreadTimeForSlice(s5))
165