Index: tools/telemetry/telemetry/web_perf/metrics/fast_metric_unittest.py |
diff --git a/tools/telemetry/telemetry/web_perf/metrics/fast_metric_unittest.py b/tools/telemetry/telemetry/web_perf/metrics/fast_metric_unittest.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3bd2ca9b46c64d6f802030cce0ef0b646ecce58b |
--- /dev/null |
+++ b/tools/telemetry/telemetry/web_perf/metrics/fast_metric_unittest.py |
@@ -0,0 +1,216 @@ |
+# Copyright 2014 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 logging |
+import StringIO |
+import unittest |
+ |
+from telemetry.results import page_test_results |
+from telemetry.timeline import model as model_module |
+from telemetry.timeline import async_slice |
+from telemetry.web_perf import timeline_interaction_record as tir_module |
+from telemetry.web_perf.metrics import fast_metric |
+ |
+ |
+class RendererThreadHelper(object): |
+ def __init__(self, wall_start, wall_duration, thread_start, thread_duration): |
+ self._model = model_module.TimelineModel() |
+ renderer_process = self._model.GetOrCreateProcess(1) |
+ self._renderer_thread = renderer_process.GetOrCreateThread(2) |
+ self._renderer_thread.name = 'CrRendererMain' |
+ self._renderer_thread.BeginSlice('cat1', 'x.y', wall_start, thread_start) |
+ self._renderer_thread.EndSlice(wall_start + wall_duration, |
+ thread_start + thread_duration) |
+ self._async_slices = [] |
+ |
+ def AddInteraction(self, logical_name='LogicalName', **kwargs): |
+ # Rename kwargs for AsyncSlice. |
+ kwargs['timestamp'] = kwargs.pop('wall_start') |
+ kwargs['duration'] = kwargs.pop('wall_duration') |
+ |
+ self._async_slices.append(async_slice.AsyncSlice( |
+ 'cat', 'Interaction.%s/is_fast' % logical_name, |
+ start_thread=self._renderer_thread, end_thread=self._renderer_thread, |
+ **kwargs)) |
+ |
+ def AddEvent(self, category, name, wall_start, wall_duration, thread_start, |
+ thread_duration): |
+ self._renderer_thread.BeginSlice(category, name, wall_start, thread_start) |
+ self._renderer_thread.EndSlice(wall_start + wall_duration, |
+ thread_start + thread_duration) |
+ |
+ def MeasureFakePage(self, metric): |
+ self._renderer_thread.async_slices.extend(self._async_slices) |
+ self._model.FinalizeImport() |
+ interaction_records = [ |
+ tir_module.TimelineInteractionRecord.FromAsyncEvent(s) |
+ for s in self._async_slices] |
+ results = page_test_results.PageTestResults() |
+ fake_page = None |
+ results.WillRunPage(fake_page) |
+ metric.AddResults(self._model, self._renderer_thread, interaction_records, |
+ results) |
+ results.DidRunPage(fake_page) |
+ return results |
+ |
+ |
+class FastMetricTests(unittest.TestCase): |
+ |
+ def setUp(self): |
+ self.log_output = StringIO.StringIO() |
+ self.stream_handler = logging.StreamHandler(self.log_output) |
+ logging.getLogger().addHandler(self.stream_handler) |
+ |
+ def tearDown(self): |
+ logging.getLogger().removeHandler(self.stream_handler) |
+ self.log_output.close() |
+ |
+ def LogOutput(self): |
+ return self.log_output.getvalue() |
+ |
+ def ActualValues(self, results): |
+ return sorted( |
+ (v.name, v.units, v.value) |
+ for v in results.all_page_specific_values |
+ ) |
+ |
+ def testAddResultsWithThreadTime(self): |
+ # Wall time diagram: |
+ # 1 2 3 4 |
+ # 01234567890123456789012345678901234567890123456789 |
+ # [ x.y ] |
+ # [ Interaction.LogicalName/is_fast ] |
+ renderer_thread_helper = RendererThreadHelper( |
+ wall_start=5, wall_duration=35, thread_start=0, thread_duration=54) |
+ renderer_thread_helper.AddInteraction( |
+ wall_start=32, wall_duration=37, thread_start=51, thread_duration=33) |
+ |
+ metric = fast_metric.FastMetric() |
+ results = renderer_thread_helper.MeasureFakePage(metric) |
+ |
+ expected_values = [ |
+ ('fast-cpu_time', 'ms', 3), # 54 - 51; thread overlap |
+ ('fast-duration', 'ms', 37), # total interaction wall duration |
+ ('fast-idle_time', 'ms', 29), # (32 + 37) - (5 + 35); interaction wall |
+ # time outside of renderer wall time. |
+ ('fast-incremental_marking', 'ms', 0.0), |
+ ('fast-incremental_marking_outside_idle', 'ms', 0.0), |
+ ('fast-mark_compactor', 'ms', 0.0), |
+ ('fast-mark_compactor_outside_idle', 'ms', 0.0), |
+ ('fast-scavenger', 'ms', 0.0), |
+ ('fast-scavenger_outside_idle', 'ms', 0.0), |
+ ('fast-total_garbage_collection', 'ms', 0.0), |
+ ('fast-total_garbage_collection_outside_idle', 'ms', 0.0) |
+ ] |
+ self.assertEqual(expected_values, self.ActualValues(results)) |
+ |
+ def testAddResultsWithoutThreadTime(self): |
+ # Wall time diagram: |
+ # 1 2 3 4 |
+ # 01234567890123456789012345678901234567890123456789 |
+ # [ x.y ] |
+ # [ Interaction.LogicalName/is_fast ] |
+ renderer_thread_helper = RendererThreadHelper( |
+ wall_start=5, wall_duration=35, thread_start=0, thread_duration=54) |
+ renderer_thread_helper.AddInteraction( |
+ wall_start=32, wall_duration=37) # no thread_start, no thread_duration |
+ |
+ metric = fast_metric.FastMetric() |
+ results = renderer_thread_helper.MeasureFakePage(metric) |
+ |
+ expected_values = [ |
+ # cpu_time is skipped because there is no thread time. |
+ ('fast-duration', 'ms', 37), # total interaction wall duration |
+ ('fast-idle_time', 'ms', 29), # (32 + 37) - (5 + 35); interaction wall |
+ # time outside of renderer wall time. |
+ ('fast-incremental_marking', 'ms', 0.0), |
+ ('fast-incremental_marking_outside_idle', 'ms', 0.0), |
+ ('fast-mark_compactor', 'ms', 0.0), |
+ ('fast-mark_compactor_outside_idle', 'ms', 0.0), |
+ ('fast-scavenger', 'ms', 0.0), |
+ ('fast-scavenger_outside_idle', 'ms', 0.0), |
+ ('fast-total_garbage_collection', 'ms', 0.0), |
+ ('fast-total_garbage_collection_outside_idle', 'ms', 0.0) |
+ ] |
+ self.assertEqual(expected_values, self.ActualValues(results)) |
+ self.assertIn('Main thread cpu_time cannot be computed for records', |
+ self.LogOutput()) |
+ |
+ def testAddResultsWithMultipleInteractions(self): |
+ # Wall time diagram: |
+ # 1 2 3 4 |
+ # 01234567890123456789012345678901234567890123456789 |
+ # [ x.y ] |
+ # [ Interaction.Foo/is_fast ] [ Interaction.Bar/is_fast ] |
+ renderer_thread_helper = RendererThreadHelper( |
+ wall_start=2, wall_duration=45, thread_start=0, thread_duration=101) |
+ renderer_thread_helper.AddInteraction( |
+ logical_name='Foo', |
+ wall_start=6, wall_duration=27, thread_start=51, thread_duration=33) |
+ renderer_thread_helper.AddInteraction( |
+ logical_name='Bar', |
+ wall_start=38, wall_duration=27, thread_start=90, thread_duration=33) |
+ |
+ metric = fast_metric.FastMetric() |
+ results = renderer_thread_helper.MeasureFakePage(metric) |
+ |
+ expected_values = [ |
+ ('fast-cpu_time', 'ms', 44), # thread overlap |
+ ('fast-duration', 'ms', 54), # 27 + 27; total interaction wall duration |
+ ('fast-idle_time', 'ms', 18), # (38 + 27) - (2 + 45); interaction wall |
+ # time outside of renderer wall time. |
+ ('fast-incremental_marking', 'ms', 0.0), |
+ ('fast-incremental_marking_outside_idle', 'ms', 0.0), |
+ ('fast-mark_compactor', 'ms', 0.0), |
+ ('fast-mark_compactor_outside_idle', 'ms', 0.0), |
+ ('fast-scavenger', 'ms', 0.0), |
+ ('fast-scavenger_outside_idle', 'ms', 0.0), |
+ ('fast-total_garbage_collection', 'ms', 0.0), |
+ ('fast-total_garbage_collection_outside_idle', 'ms', 0.0) |
+ ] |
+ self.assertEqual(expected_values, self.ActualValues(results)) |
+ |
+ def testAddResultsWithGarbeCollectionEvents(self): |
+ # Thread time diagram: |
+ # 1 2 3 4 5 |
+ # 012345678901234567890123456789012345678901234567890123456789 |
+ # [ x.y ] |
+ # [ Interaction.Foo/is_fast ] |
+ # [ Idle ] [Idle] [Idle] |
+ # [ S ] [S] [ I ] [I] [MC ] [MC] |
+ renderer_thread_helper = RendererThreadHelper( |
+ wall_start=1, wall_duration=57, thread_start=1, thread_duration=57) |
+ renderer_thread_helper.AddInteraction( |
+ logical_name='Foo', |
+ wall_start=3, wall_duration=58, thread_start=3, thread_duration=58) |
+ renderer_thread_helper.AddEvent('v8', 'V8.GCIdleNotification', 3, 7, 3, 7) |
+ renderer_thread_helper.AddEvent('v8', 'V8.GCIdleNotification', 22, 5, 22, 5) |
+ renderer_thread_helper.AddEvent('v8', 'V8.GCIdleNotification', 41, 5, 41, 5) |
+ renderer_thread_helper.AddEvent('v8', 'V8.GCScavenger', 5, 4, 5, 4) |
+ renderer_thread_helper.AddEvent('v8', 'V8.GCScavenger', 15, 2, 15, 2) |
+ renderer_thread_helper.AddEvent('v8', 'V8.GCIncrementalMarking', 23, 4, 23, |
+ 4) |
+ renderer_thread_helper.AddEvent('v8', 'V8.GCIncrementalMarking', 34, 2, 34, |
+ 2) |
+ renderer_thread_helper.AddEvent('v8', 'V8.GCCompactor', 42, 4, 42, 4) |
+ renderer_thread_helper.AddEvent('v8', 'V8.GCCompactor', 52, 3, 52, 3) |
+ |
+ metric = fast_metric.FastMetric() |
+ results = renderer_thread_helper.MeasureFakePage(metric) |
+ |
+ expected_values = [ |
+ ('fast-cpu_time', 'ms', 55), # thread overlap |
+ ('fast-duration', 'ms', 58), # total interaction wall duration |
+ ('fast-idle_time', 'ms', 3), # (3 + 58) - (1 + 57); interaction wall |
+ # time outside of renderer wall time. |
+ ('fast-incremental_marking', 'ms', 6.0), |
+ ('fast-incremental_marking_outside_idle', 'ms', 2.0), |
+ ('fast-mark_compactor', 'ms', 7.0), |
+ ('fast-mark_compactor_outside_idle', 'ms', 3.0), |
+ ('fast-scavenger', 'ms', 6.0), |
+ ('fast-scavenger_outside_idle', 'ms', 2.0), |
+ ('fast-total_garbage_collection', 'ms', 19.0), |
+ ('fast-total_garbage_collection_outside_idle', 'ms', 7.0) |
+ ] |
+ self.assertEqual(expected_values, self.ActualValues(results)) |