def testParse(self): self.assertTrue(tir_module.IsTimelineInteractionRecord( 'Interaction.Foo')) self.assertTrue(tir_module.IsTimelineInteractionRecord( 'Interaction.Foo/Bar')) self.assertFalse(tir_module.IsTimelineInteractionRecord( 'SomethingRandom'))
def Stop(self, tab, results): # End the smooth marker for all actions. self._interaction.End() # Stop tracing. timeline_data = tab.browser.platform.tracing_controller.Stop() results.AddValue(trace.TraceValue( results.current_page, timeline_data)) self._model = TimelineModel(timeline_data) self._renderer_process = self._model.GetRendererProcessFromTabId(tab.id) renderer_thread = self.model.GetRendererThreadFromTabId(tab.id) run_smooth_actions_record = None self._smooth_records = [] for event in renderer_thread.async_slices: if not tir_module.IsTimelineInteractionRecord(event.name): continue r = tir_module.TimelineInteractionRecord.FromAsyncEvent(event) if r.label == RUN_SMOOTH_ACTIONS: assert run_smooth_actions_record is None, ( 'TimelineController cannot issue more than 1 %s record' % RUN_SMOOTH_ACTIONS) run_smooth_actions_record = r else: self._smooth_records.append( smooth_gesture_util.GetAdjustedInteractionIfContainGesture( self.model, r)) # If there is no other smooth records, we make measurements on time range # marked by timeline_controller itself. # TODO(nednguyen): when crbug.com/239179 is marked fixed, makes sure that # page sets are responsible for issueing the markers themselves. if len(self._smooth_records) == 0 and run_smooth_actions_record: self._smooth_records = [run_smooth_actions_record]
def testIssuingInteractionRecord(self): action_runner = action_runner_module.ActionRunner(self._tab) self.Navigate('interaction_enabled_page.html') action_runner.Wait(1) self._browser.StartTracing(tracing_backend.DEFAULT_TRACE_CATEGORIES) interaction = action_runner.BeginInteraction('TestInteraction', is_smooth=True) interaction.End() trace_data = self._browser.StopTracing() timeline_model = model.TimelineModel(trace_data) records = [] renderer_thread = timeline_model.GetRendererThreadFromTabId( self._tab.id) for event in renderer_thread.async_slices: if not tir_module.IsTimelineInteractionRecord(event.name): continue records.append( tir_module.TimelineInteractionRecord.FromAsyncEvent(event)) self.assertEqual( 1, len(records), 'Fail to issue the interaction record on tracing timeline.' ' Trace data:\n%s' % repr(trace_data.EventData())) self.assertEqual('TestInteraction', records[0].logical_name) self.assertTrue(records[0].is_smooth)
def Stop(self, tab): # End the smooth marker for all actions. runner = action_runner.ActionRunner(None, tab) runner.EndInteraction(RUN_SMOOTH_ACTIONS, [tir_module.IS_SMOOTH]) # Stop tracing. timeline_data = tab.browser.StopTracing() self._model = TimelineModel(timeline_data) self._renderer_process = self._model.GetRendererProcessFromTab(tab) renderer_thread = self.model.GetRendererThreadFromTab(tab) run_smooth_actions_record = None self._smooth_records = [] for event in renderer_thread.async_slices: if not tir_module.IsTimelineInteractionRecord(event.name): continue r = tir_module.TimelineInteractionRecord.FromEvent(event) if r.logical_name == RUN_SMOOTH_ACTIONS: assert run_smooth_actions_record is None, ( 'TimelineController cannot issue more than 1 %s record' % RUN_SMOOTH_ACTIONS) run_smooth_actions_record = r elif r.is_smooth: self._smooth_records.append( smooth_gesture_util.GetAdjustedInteractionIfContainGesture( self.model, r)) # If there is no other smooth records, we make measurements on time range # marked by timeline_controller itself. # TODO(nednguyen): when crbug.com/239179 is marked fixed, makes sure that # page sets are responsible for issueing the markers themselves. if len(self._smooth_records) == 0 and run_smooth_actions_record: self._smooth_records = [run_smooth_actions_record]
def FindTimelineInteractionRecords(self): # TODO(nduca): Add support for page-load interaction record. return [ tir_module.TimelineInteractionRecord.FromEvent(event) for event in self._renderer_thread.async_slices if tir_module.IsTimelineInteractionRecord(event.name) ]
def GetInteractionRecords(self, trace_data): timeline_model = model.TimelineModel(trace_data) renderer_thread = timeline_model.GetFirstRendererThread(self._tab.id) return [ tir_module.TimelineInteractionRecord.FromAsyncEvent(e) for e in renderer_thread.async_slices if tir_module.IsTimelineInteractionRecord(e.name) ]
def Stop(self, tab, results): # End the smooth marker for all actions. if self._enable_auto_issuing_record: self._interaction.End() # Stop tracing. timeline_data = tab.browser.platform.tracing_controller.StopTracing() # TODO(charliea): This is part of a three-sided Chromium/Telemetry patch # where we're changing the return type of StopTracing from a TraceValue to a # (TraceValue, nonfatal_exception_list) tuple. Once the tuple return value # lands in Chromium, the non-tuple logic should be deleted. if isinstance(timeline_data, tuple): timeline_data = timeline_data[0] # TODO(#763375): Rely on results.telemetry_info.trace_local_path/etc. kwargs = {} if hasattr(results.telemetry_info, 'trace_local_path'): kwargs['file_path'] = results.telemetry_info.trace_local_path kwargs['remote_path'] = results.telemetry_info.trace_remote_path kwargs['upload_bucket'] = results.telemetry_info.upload_bucket kwargs['cloud_url'] = results.telemetry_info.trace_remote_url results.AddValue( trace.TraceValue(results.current_page, timeline_data, **kwargs)) self._model = TimelineModel(timeline_data) self._renderer_process = self._model.GetRendererProcessFromTabId( tab.id) renderer_thread = self.model.GetRendererThreadFromTabId(tab.id) run_smooth_actions_record = None self._smooth_records = [] for event in renderer_thread.async_slices: if not tir_module.IsTimelineInteractionRecord(event.name): continue r = tir_module.TimelineInteractionRecord.FromAsyncEvent(event) if r.label == RUN_SMOOTH_ACTIONS: assert run_smooth_actions_record is None, ( 'TimelineController cannot issue more than 1 %s record' % RUN_SMOOTH_ACTIONS) run_smooth_actions_record = r else: self._smooth_records.append( smooth_gesture_util.GetAdjustedInteractionIfContainGesture( self.model, r)) # If there is no other smooth records, we make measurements on time range # marked by timeline_controller itself. # TODO(nednguyen): when crbug.com/239179 is marked fixed, makes sure that # page sets are responsible for issueing the markers themselves. if len(self._smooth_records) == 0 and run_smooth_actions_record: self._smooth_records = [run_smooth_actions_record] if len(self._smooth_records) == 0: raise legacy_page_test.Failure( 'No interaction record was created.')
def _CollectRecordsFromRendererThreads(model, renderer_thread): records = [] for event in renderer_thread.async_slices: if tir_module.IsTimelineInteractionRecord(event.name): interaction = tir_module.TimelineInteractionRecord.FromAsyncEvent(event) # Adjust the interaction record to match the synthetic gesture # controller if needed. interaction = ( smooth_gesture_util.GetAdjustedInteractionIfContainGesture( model, interaction)) records.append(interaction) return records
def AddResults(self, tab, results): # Add results of smoothness metric. This computes the smoothness metric for # the time ranges of gestures, if there is at least one, else the the time # ranges from the first action to the last action. renderer_thread = self._timeline_model.GetRendererThreadFromTabId( tab.id) run_smooth_actions_record = None smooth_records = [] for event in renderer_thread.async_slices: if not tir_module.IsTimelineInteractionRecord(event.name): continue r = tir_module.TimelineInteractionRecord.FromAsyncEvent(event) if r.logical_name == RUN_SMOOTH_ACTIONS: assert run_smooth_actions_record is None, ( 'SmoothnessController cannot issue more than 1 %s record' % RUN_SMOOTH_ACTIONS) run_smooth_actions_record = r elif r.is_smooth: smooth_records.append( smooth_gesture_util.GetAdjustedInteractionIfContainGesture( self._timeline_model, r)) # If there is no other smooth records, we make measurements on time range # marked smoothness_controller itself. # TODO(nednguyen): when crbug.com/239179 is marked fixed, makes sure that # page sets are responsible for issueing the markers themselves. if len(smooth_records) == 0: if run_smooth_actions_record is None: sys.stderr.write('Raw tracing data:\n') sys.stderr.write(repr(self._tracing_timeline_data.EventData())) sys.stderr.write('\n') raise Exception( 'SmoothnessController failed to issue markers for the ' 'whole interaction.') else: smooth_records = [run_smooth_actions_record] # Create an interaction_record for this legacy measurement. Since we don't # wrap the results that is sent to smoothnes metric, the logical_name will # not be used. smoothness_metric = smoothness.SmoothnessMetric() smoothness_metric.AddResults(self._timeline_model, renderer_thread, smooth_records, results) if tab.browser.platform.IsRawDisplayFrameRateSupported(): for r in tab.browser.platform.GetRawDisplayFrameRateMeasurements(): if r.value is None: raise MissingDisplayFrameRateError(r.name) results.Add(r.name, r.unit, r.value)
def testSmoothGestureAdjusted(self): ps = self.CreateEmptyPageSet() ps.AddUserStory( ScrollingPage('file://scrollable_page.html', ps, base_dir=ps.base_dir)) models = [] tab_ids = [] class ScrollingGestureTestMeasurement(page_test.PageTest): def __init__(self): # pylint: disable=bad-super-call super(ScrollingGestureTestMeasurement, self).__init__('RunPageInteractions', False) def WillRunActions(self, _page, tab): options = tracing_options.TracingOptions() options.enable_chrome_trace = True tab.browser.platform.tracing_controller.Start( options, tracing_category_filter.TracingCategoryFilter()) def DidRunActions(self, _page, tab): models.append( model_module.TimelineModel( tab.browser.platform.tracing_controller.Stop())) tab_ids.append(tab.id) def ValidateAndMeasurePage(self, _page, _tab, _results): pass self.RunMeasurement(ScrollingGestureTestMeasurement(), ps) timeline_model = models[0] renderer_thread = timeline_model.GetRendererThreadFromTabId(tab_ids[0]) smooth_record = None for e in renderer_thread.async_slices: if tir_module.IsTimelineInteractionRecord(e.name): smooth_record = tir_module.TimelineInteractionRecord.FromAsyncEvent( e) self.assertIsNotNone(smooth_record) adjusted_smooth_gesture = ( sg_util.GetAdjustedInteractionIfContainGesture( timeline_model, smooth_record)) # Test that the scroll gesture starts at at least 500ms after the start of # the interaction record and ends at at least 500ms before the end of # interaction record. self.assertLessEqual( 500, adjusted_smooth_gesture.start - smooth_record.start) self.assertLessEqual(500, smooth_record.end - adjusted_smooth_gesture.end)
def testSmoothGestureAdjusted(self): ps = self.CreateEmptyPageSet() ps.AddStory( ScrollingPage('file://scrollable_page.html', ps, base_dir=ps.base_dir)) models = [] tab_ids = [] class ScrollingGestureTestMeasurement(legacy_page_test.LegacyPageTest): def __init__(self): # pylint: disable=bad-super-call super(ScrollingGestureTestMeasurement, self).__init__() def WillNavigateToPage(self, page, tab): del page # unused config = tracing_config.TracingConfig() config.enable_chrome_trace = True tab.browser.platform.tracing_controller.StartTracing(config) def ValidateAndMeasurePage(self, page, tab, results): del page, results # unused models.append( model_module.TimelineModel( tab.browser.platform.tracing_controller.StopTracing() [0])) tab_ids.append(tab.id) self.RunMeasurement(ScrollingGestureTestMeasurement(), ps) timeline_model = models[0] renderer_thread = timeline_model.GetFirstRendererThread(tab_ids[0]) smooth_record = None for e in renderer_thread.async_slices: if tir_module.IsTimelineInteractionRecord(e.name): smooth_record = tir_module.TimelineInteractionRecord.FromAsyncEvent( e) self.assertIsNotNone(smooth_record) adjusted_smooth_gesture = ( sg_util.GetAdjustedInteractionIfContainGesture( timeline_model, smooth_record)) # Test that the scroll gesture starts at at least 500ms after the start of # the interaction record and ends at at least 500ms before the end of # interaction record. self.assertLessEqual( 500, adjusted_smooth_gesture.start - smooth_record.start) self.assertLessEqual(500, smooth_record.end - adjusted_smooth_gesture.end)
def AddResults(self, tab, results): # Add results of smoothness metric. This computes the smoothness metric for # the time ranges of gestures, if there is at least one, else the the time # ranges from the first action to the last action. results.AddValue( trace.TraceValue(results.current_page, self._trace_data)) renderer_thread = self._timeline_model.GetRendererThreadFromTabId( tab.id) smooth_records = [] for event in renderer_thread.async_slices: if not tir_module.IsTimelineInteractionRecord(event.name): continue r = tir_module.TimelineInteractionRecord.FromAsyncEvent(event) smooth_records.append( smooth_gesture_util.GetAdjustedInteractionIfContainGesture( self._timeline_model, r)) # If there is no other smooth records, we make measurements on time range # marked smoothness_controller itself. # TODO(nednguyen): when crbug.com/239179 is marked fixed, makes sure that # page sets are responsible for issueing the markers themselves. if len(smooth_records) == 0: raise page_test.Failure('Page failed to issue any markers.') # Check to make sure all smooth records have same label and repeatable if # there are more than one. need_repeatable_flag = len(smooth_records) > 1 record_label = smooth_records[0].label for r in smooth_records: if r.label != record_label: raise page_test.Failure( 'SmoothController does not support multiple interactions with ' 'different label. Interactions: %s' % repr(smooth_records)) if need_repeatable_flag and not r.repeatable: raise page_test.Failure( 'If there are more than one interaction record, each interaction ' 'must has repeatable flag. Interactions: %s' % repr(smooth_records)) # Create an interaction_record for this legacy measurement. Since we don't # wrap the results that are sent to smoothness metric, the label will # not be used. smoothness_metric = smoothness.SmoothnessMetric() smoothness_metric.AddResults(self._timeline_model, renderer_thread, smooth_records, results)
def _GetRendererThreadsToInteractionRecordsMap(model): threads_to_records_map = collections.defaultdict(list) interaction_labels_of_previous_threads = set() for curr_thread in model.GetAllThreads(): for event in curr_thread.async_slices: # TODO(nduca): Add support for page-load interaction record. if tir_module.IsTimelineInteractionRecord(event.name): interaction = tir_module.TimelineInteractionRecord.FromAsyncEvent(event) threads_to_records_map[curr_thread].append(interaction) if interaction.label in interaction_labels_of_previous_threads: raise InvalidInteractions( 'Interaction record label %s is duplicated on different ' 'threads' % interaction.label) if curr_thread in threads_to_records_map: interaction_labels_of_previous_threads.update( r.label for r in threads_to_records_map[curr_thread]) return threads_to_records_map
def _ExtractInteractionsRecordFromThread(thread, timeline_model): run_smooth_actions_record = None records = [] for event in thread.async_slices: if not tir_module.IsTimelineInteractionRecord(event.name): continue assert event.start_thread assert event.start_thread is event.end_thread r = smooth_gesture_util.GetAdjustedInteractionIfContainGesture( timeline_model, tir_module.TimelineInteractionRecord.FromAsyncEvent(event)) if r.label == smoothness_controller.RUN_SMOOTH_ACTIONS: assert run_smooth_actions_record is None, ( 'There can\'t be more than 1 %s record' % smoothness_controller.RUN_SMOOTH_ACTIONS) run_smooth_actions_record = r else: records.append(r) if not records: # Only include run_smooth_actions_record (label = # smoothness_controller.RUN_SMOOTH_ACTIONS) if there is no other records records = [run_smooth_actions_record] return records
def _GetRendererThreadsToInteractionRecordsMap(model): threads_to_records_map = defaultdict(list) interaction_labels_of_previous_threads = set() for curr_thread in model.GetAllThreads(): for event in curr_thread.async_slices: # TODO(nduca): Add support for page-load interaction record. if tir_module.IsTimelineInteractionRecord(event.name): interaction = tir_module.TimelineInteractionRecord.FromAsyncEvent(event) # Adjust the interaction record to match the synthetic gesture # controller if needed. interaction = ( smooth_gesture_util.GetAdjustedInteractionIfContainGesture( model, interaction)) threads_to_records_map[curr_thread].append(interaction) if interaction.label in interaction_labels_of_previous_threads: raise InvalidInteractions( 'Interaction record label %s is duplicated on different ' 'threads' % interaction.label) if curr_thread in threads_to_records_map: interaction_labels_of_previous_threads.update( r.label for r in threads_to_records_map[curr_thread]) return threads_to_records_map
def AddValue(self, value): assert self._tir_label if value.tir_label: assert value.tir_label == self._tir_label else: value.tir_label = self._tir_label self._results.AddValue(value) def _GetRendererThreadsToInteractionRecordsMap(domain.model): threads_to_records_map = defaultdict(list) interaction_labels_of_previous_threads = set() for curr_thread in domain.model.GetAllThreads(): for event in curr_thread.async_slices: # TODO(nduca): Add support for page-load interaction record. if tir_module.IsTimelineInteractionRecord(event.name): interaction = tir_module.TimelineInteractionRecord.FromAsyncEvent(event) # Adjust the interaction record to match the synthetic gesture # controller if needed. interaction = ( smooth_gesture_util.GetAdjustedInteractionIfContainGesture( domain.model, interaction)) threads_to_records_map[curr_thread].append(interaction) if interaction.label in interaction_labels_of_previous_threads: raise InvalidInteractions( 'Interaction record label %s is duplicated on different ' 'threads' % interaction.label) if curr_thread in threads_to_records_map: interaction_labels_of_previous_threads.update( r.label for r in threads_to_records_map[curr_thread])