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 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 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 = 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 testGetAdjustedInteractionIfContainGesture(self): model = model_module.TimelineModel() renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2) renderer_main.name = 'CrRendererMain' # [ X ] [ Y ] # [ sub_async_slice_X ] # [ record_1] # [ record_6] # [ record_2 ] [ record_3 ] # [ record_4 ] # [ record_5 ] # # Note: X and Y are async slice with name # SyntheticGestureController::running async_slice_X = async_slice.AsyncSlice( 'X', 'SyntheticGestureController::running', 10, duration=20, start_thread=renderer_main, end_thread=renderer_main) sub_async_slice_X = async_slice.AsyncSlice( 'X', 'SyntheticGestureController::running', 10, duration=20, start_thread=renderer_main, end_thread=renderer_main) sub_async_slice_X.parent_slice = async_slice_X async_slice_X.AddSubSlice(sub_async_slice_X) async_slice_Y = async_slice.AsyncSlice( 'X', 'SyntheticGestureController::running', 60, duration=20, start_thread=renderer_main, end_thread=renderer_main) renderer_main.AddAsyncSlice(async_slice_X) renderer_main.AddAsyncSlice(async_slice_Y) model.FinalizeImport(shift_world_to_zero=False) record_1 = tir_module.TimelineInteractionRecord( 'Gesture_included', 15, 25) record_2 = tir_module.TimelineInteractionRecord( 'Gesture_overlapped_left', 5, 25) record_3 = tir_module.TimelineInteractionRecord( 'Gesture_overlapped_right', 25, 35) record_4 = tir_module.TimelineInteractionRecord( 'Gesture_containing', 5, 35) record_5 = tir_module.TimelineInteractionRecord( 'Gesture_non_overlapped', 35, 45) record_6 = tir_module.TimelineInteractionRecord( 'Action_included', 15, 25) adjusted_record_1 = sg_util.GetAdjustedInteractionIfContainGesture( model, record_1) self.assertEquals(adjusted_record_1.start, 10) self.assertEquals(adjusted_record_1.end, 30) self.assertTrue(adjusted_record_1 is not record_1) adjusted_record_2 = sg_util.GetAdjustedInteractionIfContainGesture( model, record_2) self.assertEquals(adjusted_record_2.start, 10) self.assertEquals(adjusted_record_2.end, 30) adjusted_record_3 = sg_util.GetAdjustedInteractionIfContainGesture( model, record_3) self.assertEquals(adjusted_record_3.start, 10) self.assertEquals(adjusted_record_3.end, 30) adjusted_record_4 = sg_util.GetAdjustedInteractionIfContainGesture( model, record_4) self.assertEquals(adjusted_record_4.start, 10) self.assertEquals(adjusted_record_4.end, 30) adjusted_record_5 = sg_util.GetAdjustedInteractionIfContainGesture( model, record_5) self.assertEquals(adjusted_record_5.start, 35) self.assertEquals(adjusted_record_5.end, 45) self.assertTrue(adjusted_record_5 is not record_5) adjusted_record_6 = sg_util.GetAdjustedInteractionIfContainGesture( model, record_6) self.assertEquals(adjusted_record_6.start, 15) self.assertEquals(adjusted_record_6.end, 25) self.assertTrue(adjusted_record_6 is not record_6)
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]) return threads_to_records_map class _TimelineBasedMetrics(object): def __init__(self, domain.model, renderer_thread, interaction_records, results_wrapper, metrics):