| # Copyright 2016 The Chromium Authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 |  | 
 | import collections | 
 | import copy | 
 | import unittest | 
 |  | 
 | from activity_lens import (ActivityLens, _EventsTree) | 
 | import clovis_constants | 
 | import test_utils | 
 | import tracing_track | 
 |  | 
 |  | 
 | class ActivityLensTestCase(unittest.TestCase): | 
 |   @classmethod | 
 |   def _EventsFromRawEvents(cls, raw_events): | 
 |     track = tracing_track.TracingTrack(None, | 
 |         clovis_constants.DEFAULT_CATEGORIES) | 
 |     track.Handle( | 
 |         'Tracing.dataCollected', {'params': {'value': raw_events}}) | 
 |     return track.GetEvents() | 
 |  | 
 |   def setUp(self): | 
 |     self.track = tracing_track.TracingTrack(None, | 
 |         clovis_constants.DEFAULT_CATEGORIES) | 
 |  | 
 |   def testGetRendererMainThread(self): | 
 |     first_renderer_tid = 12345 | 
 |     second_renderer_tid = 123456 | 
 |     raw_events =  [ | 
 |         {u'args': {u'name': u'CrBrowserMain'}, | 
 |          u'cat': u'__metadata', | 
 |          u'name': u'thread_name', | 
 |          u'ph': u'M', | 
 |          u'pid': 1, | 
 |          u'tid': 123, | 
 |          u'ts': 0}, | 
 |         {u'args': {u'name': u'CrRendererMain'}, | 
 |          u'cat': u'__metadata', | 
 |          u'name': u'thread_name', | 
 |          u'ph': u'M', | 
 |          u'pid': 1, | 
 |          u'tid': first_renderer_tid, | 
 |          u'ts': 0}, | 
 |         {u'args': {u'name': u'CrRendererMain'}, | 
 |          u'cat': u'__metadata', | 
 |          u'name': u'thread_name', | 
 |          u'ph': u'M', | 
 |          u'pid': 1, | 
 |          u'tid': second_renderer_tid, | 
 |          u'ts': 0}] | 
 |     raw_events += [ | 
 |         {u'args': {u'data': {}}, | 
 |          u'cat': u'devtools.timeline,v8', | 
 |          u'name': u'FunctionCall', | 
 |          u'ph': u'X', | 
 |          u'pid': 1, | 
 |          u'tdur': 0, | 
 |          u'tid': first_renderer_tid, | 
 |          u'ts': 251427174674, | 
 |          u'tts': 5107725}] * 100 | 
 |     raw_events += [ | 
 |         {u'args': {u'data': {}}, | 
 |          u'cat': u'devtools.timeline,v8', | 
 |          u'name': u'FunctionCall', | 
 |          u'ph': u'X', | 
 |          u'pid': 1, | 
 |          u'tdur': 0, | 
 |          u'tid': second_renderer_tid, | 
 |          u'ts': 251427174674, | 
 |          u'tts': 5107725}] * 150 | 
 |     # There are more events from first_renderer_tid when (incorrectly) ignoring | 
 |     # the PID. | 
 |     raw_events += [ | 
 |         {u'args': {u'data': {}}, | 
 |          u'cat': u'devtools.timeline,v8', | 
 |          u'name': u'FunctionCall', | 
 |          u'ph': u'X', | 
 |          u'pid': 12, | 
 |          u'tdur': 0, | 
 |          u'tid': first_renderer_tid, | 
 |          u'ts': 251427174674, | 
 |          u'tts': 5107725}] * 100 | 
 |     events = self._EventsFromRawEvents(raw_events) | 
 |     self.assertEquals((1, second_renderer_tid), | 
 |                       ActivityLens._GetRendererMainThreadId(events)) | 
 |  | 
 |   def testThreadBusyness(self): | 
 |     raw_events =  [ | 
 |         {u'args': {}, | 
 |          u'cat': u'toplevel', | 
 |          u'dur': 200 * 1000, | 
 |          u'name': u'MessageLoop::RunTask', | 
 |          u'ph': u'X', | 
 |          u'pid': 123, | 
 |          u'tid': 123, | 
 |          u'ts': 0, | 
 |          u'tts': 56485}, | 
 |         {u'args': {}, | 
 |          u'cat': u'toplevel', | 
 |          u'dur': 8 * 200, | 
 |          u'name': u'MessageLoop::NestedSomething', | 
 |          u'ph': u'X', | 
 |          u'pid': 123, | 
 |          u'tid': 123, | 
 |          u'ts': 0, | 
 |          u'tts': 0}] | 
 |     events = self._EventsFromRawEvents(raw_events) | 
 |     self.assertEquals(200, ActivityLens._ThreadBusyness(events, 0, 1000)) | 
 |     # Clamping duration. | 
 |     self.assertEquals(100, ActivityLens._ThreadBusyness(events, 0, 100)) | 
 |     self.assertEquals(50, ActivityLens._ThreadBusyness(events, 25, 75)) | 
 |  | 
 |   def testScriptExecuting(self): | 
 |     url = u'http://example.com/script.js' | 
 |     raw_events = [ | 
 |         {u'args': {u'data': {u'scriptName': url}}, | 
 |          u'cat': u'devtools.timeline,v8', | 
 |          u'dur': 250 * 1000, | 
 |          u'name': u'FunctionCall', | 
 |          u'ph': u'X', | 
 |          u'pid': 123, | 
 |          u'tdur': 247, | 
 |          u'tid': 123, | 
 |          u'ts': 0, | 
 |          u'tts': 0}, | 
 |         {u'args': {u'data': {}}, | 
 |          u'cat': u'devtools.timeline,v8', | 
 |          u'dur': 350 * 1000, | 
 |          u'name': u'EvaluateScript', | 
 |          u'ph': u'X', | 
 |          u'pid': 123, | 
 |          u'tdur': 247, | 
 |          u'tid': 123, | 
 |          u'ts': 0, | 
 |          u'tts': 0}] | 
 |     events = self._EventsFromRawEvents(raw_events) | 
 |     self.assertEquals(2, len(ActivityLens._ScriptsExecuting(events, 0, 1000))) | 
 |     self.assertTrue(None in ActivityLens._ScriptsExecuting(events, 0, 1000)) | 
 |     self.assertEquals( | 
 |         350, ActivityLens._ScriptsExecuting(events, 0, 1000)[None]) | 
 |     self.assertTrue(url in ActivityLens._ScriptsExecuting(events, 0, 1000)) | 
 |     self.assertEquals(250, ActivityLens._ScriptsExecuting(events, 0, 1000)[url]) | 
 |     # Aggreagates events. | 
 |     raw_events.append({u'args': {u'data': {}}, | 
 |                        u'cat': u'devtools.timeline,v8', | 
 |                        u'dur': 50 * 1000, | 
 |                        u'name': u'EvaluateScript', | 
 |                        u'ph': u'X', | 
 |                        u'pid': 123, | 
 |                        u'tdur': 247, | 
 |                        u'tid': 123, | 
 |                        u'ts': 0, | 
 |                        u'tts': 0}) | 
 |     events = self._EventsFromRawEvents(raw_events) | 
 |     self.assertEquals( | 
 |         350 + 50, ActivityLens._ScriptsExecuting(events, 0, 1000)[None]) | 
 |  | 
 |   def testParsing(self): | 
 |     css_url = u'http://example.com/style.css' | 
 |     html_url = u'http://example.com/yeah.htnl' | 
 |     raw_events = [ | 
 |         {u'args': {u'data': {u'styleSheetUrl': css_url}}, | 
 |          u'cat': u'blink,devtools.timeline', | 
 |          u'dur': 400 * 1000, | 
 |          u'name': u'ParseAuthorStyleSheet', | 
 |          u'ph': u'X', | 
 |          u'pid': 32723, | 
 |          u'tdur': 49721, | 
 |          u'tid': 32738, | 
 |          u'ts': 0, | 
 |          u'tts': 216148}, | 
 |         {u'args': {u'beginData': {u'url': html_url}}, | 
 |          u'cat': u'devtools.timeline', | 
 |          u'dur': 42 * 1000, | 
 |          u'name': u'ParseHTML', | 
 |          u'ph': u'X', | 
 |          u'pid': 32723, | 
 |          u'tdur': 49721, | 
 |          u'tid': 32738, | 
 |          u'ts': 0, | 
 |          u'tts': 5032310},] | 
 |     events = self._EventsFromRawEvents(raw_events) | 
 |     self.assertEquals(2, len(ActivityLens._Parsing(events, 0, 1000))) | 
 |     self.assertTrue(css_url in ActivityLens._Parsing(events, 0, 1000)) | 
 |     self.assertEquals(400, ActivityLens._Parsing(events, 0, 1000)[css_url]) | 
 |     self.assertTrue(html_url in ActivityLens._Parsing(events, 0, 1000)) | 
 |     self.assertEquals(42, ActivityLens._Parsing(events, 0, 1000)[html_url]) | 
 |  | 
 |   def testBreakdownEdgeActivityByInitiator(self): | 
 |     requests = [test_utils.MakeRequest(0, 1, 10, 20, 30), | 
 |                 test_utils.MakeRequest(0, 1, 50, 60, 70)] | 
 |     raw_events = [ | 
 |         {u'args': {u'beginData': {u'url': requests[0].url}}, | 
 |          u'cat': u'devtools.timeline', | 
 |          u'dur': 12 * 1000, | 
 |          u'name': u'ParseHTML', | 
 |          u'ph': u'X', | 
 |          u'pid': 1, | 
 |          u'tid': 1, | 
 |          u'ts': 25 * 1000}, | 
 |         {u'args': {u'data': {'scriptName': requests[0].url}}, | 
 |          u'cat': u'devtools.timeline,v8', | 
 |          u'dur': 0, | 
 |          u'name': u'EvaluateScript', | 
 |          u'ph': u'X', | 
 |          u'pid': 1, | 
 |          u'tid': 1, | 
 |          u'ts': 0}, | 
 |         {u'cat': u'toplevel', | 
 |          u'dur': 100 * 1000, | 
 |          u'name': u'MessageLoop::RunTask', | 
 |          u'ph': u'X', | 
 |          u'pid': 1, | 
 |          u'tid': 1, | 
 |          u'ts': 0}, | 
 |         {u'args': {u'name': u'CrRendererMain'}, | 
 |          u'cat': u'__metadata', | 
 |          u'name': u'thread_name', | 
 |          u'ph': u'M', | 
 |          u'pid': 1, | 
 |          u'tid': 1, | 
 |          u'ts': 0}] | 
 |     activity = self._ActivityLens(requests, raw_events) | 
 |     dep = (requests[0], requests[1], 'parser') | 
 |     self.assertEquals( | 
 |         {'unrelated_work': 18, 'idle': 0, 'script': 0, 'parsing': 12, | 
 |          'other_url': 0, 'unknown_url': 0}, | 
 |         activity.BreakdownEdgeActivityByInitiator(dep)) | 
 |     dep = (requests[0], requests[1], 'other') | 
 |     # Truncating the event from the parent request end. | 
 |     self.assertEquals( | 
 |         {'unrelated_work': 13, 'idle': 0, 'script': 0, 'parsing': 7, | 
 |          'other_url': 0, 'unknown_url': 0}, | 
 |         activity.BreakdownEdgeActivityByInitiator(dep)) | 
 |     # Unknown URL | 
 |     raw_events[0]['args']['beginData']['url'] = None | 
 |     activity = self._ActivityLens(requests, raw_events) | 
 |     dep = (requests[0], requests[1], 'parser') | 
 |     self.assertEquals( | 
 |         {'unrelated_work': 18, 'idle': 0, 'script': 0, 'parsing': 0, | 
 |          'other_url': 0, 'unknown_url': 12}, | 
 |         activity.BreakdownEdgeActivityByInitiator(dep)) | 
 |     # Script | 
 |     raw_events[1]['ts'] = 40 * 1000 | 
 |     raw_events[1]['dur'] = 6 * 1000 | 
 |     activity = self._ActivityLens(requests, raw_events) | 
 |     dep = (requests[0], requests[1], 'script') | 
 |     self.assertEquals( | 
 |         {'unrelated_work': 7, 'idle': 0, 'script': 6, 'parsing': 0, | 
 |          'other_url': 0, 'unknown_url': 7}, | 
 |         activity.BreakdownEdgeActivityByInitiator(dep)) | 
 |     # Other URL | 
 |     raw_events[1]['args']['data']['scriptName'] = 'http://other.com/url' | 
 |     activity = self._ActivityLens(requests, raw_events) | 
 |     self.assertEquals( | 
 |         {'unrelated_work': 7, 'idle': 0, 'script': 0., 'parsing': 0., | 
 |          'other_url': 6., 'unknown_url': 7.}, | 
 |         activity.BreakdownEdgeActivityByInitiator(dep)) | 
 |  | 
 |   def testMainRendererThreadBusyness(self): | 
 |     raw_events =  [ | 
 |         {u'args': {u'name': u'CrRendererMain'}, | 
 |          u'cat': u'__metadata', | 
 |          u'name': u'thread_name', | 
 |          u'ph': u'M', | 
 |          u'pid': 1, | 
 |          u'tid': 12, | 
 |          u'ts': 0}, | 
 |         {u'args': {}, | 
 |          u'cat': u'toplevel', | 
 |          u'dur': 200 * 1000, | 
 |          u'name': u'MessageLoop::RunTask', | 
 |          u'ph': u'X', | 
 |          u'pid': 1, | 
 |          u'tid': 12, | 
 |          u'ts': 0, | 
 |          u'tts': 56485}, | 
 |         {u'args': {}, | 
 |          u'cat': u'toplevel', | 
 |          u'dur': 8 * 200, | 
 |          u'name': u'MessageLoop::NestedSomething', | 
 |          u'ph': u'X', | 
 |          u'pid': 1, | 
 |          u'tid': 12, | 
 |          u'ts': 0, | 
 |          u'tts': 0}, | 
 |         {u'args': {}, | 
 |          u'cat': u'toplevel', | 
 |          u'dur': 500 * 1000, | 
 |          u'name': u'MessageLoop::RunTask', | 
 |          u'ph': u'X', | 
 |          u'pid': 12, | 
 |          u'tid': 12, | 
 |          u'ts': 0, | 
 |          u'tts': 56485}] | 
 |     lens = self._ActivityLens([], raw_events) | 
 |     # Ignore events from another PID. | 
 |     self.assertEquals(200, lens.MainRendererThreadBusyness(0, 1000)) | 
 |     # Clamping duration. | 
 |     self.assertEquals(100, lens.MainRendererThreadBusyness(0, 100)) | 
 |     self.assertEquals(50, lens.MainRendererThreadBusyness(25, 75)) | 
 |     # Other PID. | 
 |     raw_events[0]['pid'] = 12 | 
 |     lens = self._ActivityLens([], raw_events) | 
 |     self.assertEquals(500, lens.MainRendererThreadBusyness(0, 1000)) | 
 |  | 
 |   def _ActivityLens(self, requests, raw_events): | 
 |     loading_trace = test_utils.LoadingTraceFromEvents( | 
 |         requests, None, raw_events) | 
 |     return ActivityLens(loading_trace) | 
 |  | 
 |  | 
 | class EventsTreeTestCase(unittest.TestCase): | 
 |   FakeEvent = collections.namedtuple( | 
 |       'FakeEvent', ('name', 'start_msec', 'end_msec')) | 
 |   _ROOT_EVENT = FakeEvent('-1', 0, 20) | 
 |   _EVENTS = [ | 
 |       FakeEvent('0', 2, 4), FakeEvent('1', 1, 5), | 
 |       FakeEvent('2', 6, 9), | 
 |       FakeEvent('3', 13, 14), FakeEvent('4', 14, 17), FakeEvent('5', 12, 18)] | 
 |  | 
 |   def setUp(self): | 
 |     self.tree = _EventsTree(self._ROOT_EVENT, copy.deepcopy(self._EVENTS)) | 
 |  | 
 |   def testEventsTreeConstruction(self): | 
 |     self.assertEquals(self._ROOT_EVENT, self.tree.event) | 
 |     self.assertEquals(3, len(self.tree.children)) | 
 |     self.assertEquals(self._EVENTS[1], self.tree.children[0].event) | 
 |     self.assertEquals(self._EVENTS[0], self.tree.children[0].children[0].event) | 
 |     self.assertEquals(self._EVENTS[2], self.tree.children[1].event) | 
 |     self.assertEquals([], self.tree.children[1].children) | 
 |     self.assertEquals(self._EVENTS[5], self.tree.children[2].event) | 
 |     self.assertEquals(2, len(self.tree.children[2].children)) | 
 |  | 
 |   def testDominatingEventsWithNames(self): | 
 |     self.assertListEqual( | 
 |         [self._ROOT_EVENT], self.tree.DominatingEventsWithNames(('-1'))) | 
 |     self.assertListEqual( | 
 |         [self._ROOT_EVENT], self.tree.DominatingEventsWithNames(('-1', '0'))) | 
 |     self.assertListEqual( | 
 |         [self._EVENTS[1], self._EVENTS[5]], | 
 |         self.tree.DominatingEventsWithNames(('1', '5'))) | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |   unittest.main() |