Record subtest values in Dromaeo tests
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 Nov 2013 05:33:17 +0000 (05:33 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 Nov 2013 05:33:17 +0000 (05:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=124498

Reviewed by Andreas Kling.

PerformanceTests:

Made Dromaeo's test runner report values in DRT.progress via newly added PerfTestRunner.reportValues.

* Dromaeo/resources/dromaeorunner.js:
(.): Moved the definition out of DRT.setup.
(DRT.setup): Ditto.
(DRT.testObject): Extracted from DRT.setup. Set the subtest name and continueTesting.
continueTesting is set true for subtests; i.e. when name is specified.
(DRT.progress): Call PerfTestRunner.reportValues to report subtest results.
(DRT.teardown): Call PerfTestRunner.reportValues instead of measureValueAsync.

* resources/runner.js: Made various changes for newly added PerfTestRunner.reportValues.
(.): Moved the initialization of completedIterations, results, jsHeapResults, and mallocHeapResults into
start since they need to be initialized before running each subtest. Initialize logLines here since we
need to use the same logger for all subtests.
(.start): Initialize the variables mentioned above here. Also respect doNotLogStart used by reportValues.
(ignoreWarmUpAndLog): Added doNotLogProgress. Used by reportValues since it reports all values at once.
(finish): Compute the metric name such as FrameFrame and Runs from unit. Also don't log or notify done
when continueTesting is set on the test object.
(PerfTestRunner.reportValues): Added. Reports all values for the main/sub test.

Tools:

Supported parsing subtest results.

* Scripts/webkitpy/performance_tests/perftest.py: Replaced _metrics with an ordered list of subtests, each of
which contains a dictionary with its name and an ordered list of subtest's metrics.
(PerfTest.__init__): Initialize _metrics as a list.
(PerfTest.run): Go through each subtest and its metrics to create a list of TestMetrics.
(PerfTest._run_with_driver):
(PerfTest._ensure_metrics): Look for a subtest then a metric in _metrics.

* Scripts/webkitpy/performance_tests/perftest_unittest.py:
(TestPerfTest._assert_results_are_correct): Updated the assertions per changes to _metrics.
(TestPerfTest.test_parse_output): Ditto.
(TestPerfTest.test_parse_output_with_subtests): Added the metric and the unit on each subtest result as well as
assertions to ensure subtest results are parsed properly.
(TestReplayPerfTest.test_run_with_driver_accumulates_results): Updated the assertions per changes to _metrics.
(TestReplayPerfTest.test_run_with_driver_accumulates_memory_results): Dittp.

* Scripts/webkitpy/performance_tests/perftestsrunner.py:
(_generate_results_dict): When the metric for a subtest is processed before that of the main test, the url is
incorrectly suffixed with '/'. Fix this later by re-computing the url with TestPerfMetric.test_file_name when
adding new results.

* Scripts/webkitpy/performance_tests/perftestsrunner_integrationtest.py:
(TestWithSubtestsData): Added.
(TestDriver.run_test):
(MainTest.test_run_test_with_subtests): Added.

LayoutTests:

Rebaselined the test.

* fast/harness/perftests/runs-per-second-log-expected.txt:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@159805 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/harness/perftests/runs-per-second-log-expected.txt
PerformanceTests/ChangeLog
PerformanceTests/Dromaeo/resources/dromaeorunner.js
PerformanceTests/resources/runner.js
Tools/ChangeLog
Tools/Scripts/webkitpy/performance_tests/perftest.py
Tools/Scripts/webkitpy/performance_tests/perftest_unittest.py
Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py
Tools/Scripts/webkitpy/performance_tests/perftestsrunner_integrationtest.py

index 123266f973d2d732db1b9a6ef746c4661a38e93d..766c03c0ce84c62f42a8ef77d9abf585811281fb 100644 (file)
@@ -1,3 +1,14 @@
+2013-11-26  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Record subtest values in Dromaeo tests
+        https://bugs.webkit.org/show_bug.cgi?id=124498
+
+        Reviewed by Andreas Kling.
+
+        Rebaselined the test.
+
+        * fast/harness/perftests/runs-per-second-log-expected.txt:
+
 2013-11-26  Nick Diego Yamane  <nick.yamane@openbossa.org>
 
         [MediaStream API] HTMLMediaElement should be able to use MediaStream as source
index 1a7ee62b95d6b7d81bdf3f2ae79185d68c89ed00..d2aa7bfc4dc08954ae9fff3eb3bc2e2307ea9d55 100644 (file)
@@ -1,5 +1,5 @@
 This test verifies PerfTestRunner.runPerSecond() outputs log as expected.
 
 
-:Time -> [2, 4, 5, 8, 10] runs/s
+:Runs -> [2, 4, 5, 8, 10] runs/s
 
index a5cd206dcf50d87b2820a9d18b13f1a89941205a..e72cc3073c9a0aa314d93c01b545ca8ad612e64a 100644 (file)
@@ -1,3 +1,30 @@
+2013-11-26  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Record subtest values in Dromaeo tests
+        https://bugs.webkit.org/show_bug.cgi?id=124498
+
+        Reviewed by Andreas Kling.
+
+        Made Dromaeo's test runner report values in DRT.progress via newly added PerfTestRunner.reportValues.
+
+        * Dromaeo/resources/dromaeorunner.js:
+        (.): Moved the definition out of DRT.setup.
+        (DRT.setup): Ditto.
+        (DRT.testObject): Extracted from DRT.setup. Set the subtest name and continueTesting.
+        continueTesting is set true for subtests; i.e. when name is specified.
+        (DRT.progress): Call PerfTestRunner.reportValues to report subtest results.
+        (DRT.teardown): Call PerfTestRunner.reportValues instead of measureValueAsync.
+
+        * resources/runner.js: Made various changes for newly added PerfTestRunner.reportValues.
+        (.): Moved the initialization of completedIterations, results, jsHeapResults, and mallocHeapResults into
+        start since they need to be initialized before running each subtest. Initialize logLines here since we
+        need to use the same logger for all subtests.
+        (.start): Initialize the variables mentioned above here. Also respect doNotLogStart used by reportValues.
+        (ignoreWarmUpAndLog): Added doNotLogProgress. Used by reportValues since it reports all values at once.
+        (finish): Compute the metric name such as FrameFrame and Runs from unit. Also don't log or notify done
+        when continueTesting is set on the test object.
+        (PerfTestRunner.reportValues): Added. Reports all values for the main/sub test.
+
 2013-11-26  Ryosuke Niwa  <rniwa@webkit.org>
 
         Remove replay performance tests as it's not actively maintained
index cb306e902b8dce35f3f9628ab889c854eadd2694..d74c3e9762ddb1552ceeccac417f59ef18c9f0ba 100644 (file)
@@ -1,11 +1,9 @@
 (function(){
+     var ITERATION_COUNT = 5;
      var DRT  = {
          baseURL: "./resources/dromaeo/web/index.html",
 
          setup: function(testName) {
-             var ITERATION_COUNT = 5;
-             PerfTestRunner.prepareToMeasureValuesAsync({dromaeoIterationCount: ITERATION_COUNT, doNotMeasureMemoryUsage: true, doNotIgnoreInitialRun: true, unit: 'runs/s'});
-
              var iframe = document.createElement("iframe");
              var url = DRT.baseURL + "?" + testName + '&numTests=' + ITERATION_COUNT;
              iframe.setAttribute("src", url);
                  });
          },
 
+         testObject: function(name) {
+             return {dromaeoIterationCount: ITERATION_COUNT, doNotMeasureMemoryUsage: true, doNotIgnoreInitialRun: true, unit: 'runs/s',
+                name: name, continueTesting: !!name};
+         },
+
          start: function() {
              DRT.targetWindow.postMessage({ name: "dromaeo:start" } , "*");
          },
@@ -40,7 +43,7 @@
          progress: function(message) {
             var score = message.status.score;
             if (score)
-                DRT.log(score.name + ' -> [' + score.times.join(', ') + ']');
+                PerfTestRunner.reportValues(this.testObject(score.name), score.times);
          },
 
          teardown: function(data) {
@@ -55,8 +58,7 @@
                  }
              }
 
-             for (var i = 0; i < times.length; ++i)
-                 PerfTestRunner.measureValueAsync(1 / times[i]);
+             PerfTestRunner.reportValues(this.testObject(), times.map(function (time) { return 1 / time; }));
          },
 
          targetDelegateOf: function(functionName) {
index f1c658c66f83918418787621c7fe0ea4f21f8a80..db674b03f38aa9d6a5017b26d7fa9a790b3c00a3 100755 (executable)
@@ -6,14 +6,14 @@ if (window.testRunner) {
 }
 
 (function () {
-    var logLines = null;
+    var logLines = window.testRunner ? [] : null;
     var verboseLogging = false;
-    var completedIterations = -1;
+    var completedIterations;
     var callsPerIteration = 1;
     var currentTest = null;
-    var results = [];
-    var jsHeapResults = [];
-    var mallocHeapResults = [];
+    var results;
+    var jsHeapResults;
+    var mallocHeapResults;
     var iterationCount = undefined;
 
     var PerfTestRunner = {};
@@ -145,7 +145,7 @@ if (window.testRunner) {
         finish();
     }
 
-    function start(test, runner) {
+    function start(test, runner, doNotLogStart) {
         if (!test) {
             logFatalError("Got a bad test object.");
             return;
@@ -154,9 +154,15 @@ if (window.testRunner) {
         // FIXME: We should be using multiple instances of test runner on Dromaeo as well but it's too slow now.
         // FIXME: Don't hard code the number of in-process iterations to use inside a test runner.
         iterationCount = test.dromaeoIterationCount || (window.testRunner ? 5 : 20);
-        logLines = window.testRunner ? [] : null;
+        completedIterations = -1;
+        results = [];
+        jsHeapResults = [];
+        mallocHeapResults = [];
         verboseLogging = !window.testRunner;
-        PerfTestRunner.logInfo("Running " + iterationCount + " times");
+        if (!doNotLogStart) {
+            PerfTestRunner.logInfo('');
+            PerfTestRunner.logInfo("Running " + iterationCount + " times");
+        }
         if (test.doNotIgnoreInitialRun)
             completedIterations++;
         if (runner)
@@ -192,39 +198,50 @@ if (window.testRunner) {
         }, 0);
     }
 
-    function ignoreWarmUpAndLog(measuredValue) {
+    function ignoreWarmUpAndLog(measuredValue, doNotLogProgress) {
         var labeledResult = measuredValue + " " + PerfTestRunner.unit;
-        if (completedIterations <= 0)
-            PerfTestRunner.logDetail(completedIterations, labeledResult + " (Ignored warm-up run)");
-        else {
-            results.push(measuredValue);
-            if (window.internals && !currentTest.doNotMeasureMemoryUsage) {
-                jsHeapResults.push(getUsedJSHeap());
-                mallocHeapResults.push(getUsedMallocHeap());
-            }
-            PerfTestRunner.logDetail(completedIterations, labeledResult);
+        if (completedIterations <= 0) {
+            if (!doNotLogProgress)
+                PerfTestRunner.logDetail(completedIterations, labeledResult + " (Ignored warm-up run)");
+            return;
         }
+
+        results.push(measuredValue);
+        if (window.internals && !currentTest.doNotMeasureMemoryUsage) {
+            jsHeapResults.push(getUsedJSHeap());
+            mallocHeapResults.push(getUsedMallocHeap());
+        }
+        if (!doNotLogProgress)
+            PerfTestRunner.logDetail(completedIterations, labeledResult);
     }
 
     function finish() {
         try {
+            var prefix = currentTest.name || '';
             if (currentTest.description)
                 PerfTestRunner.log("Description: " + currentTest.description);
-            PerfTestRunner.logStatistics(results, PerfTestRunner.unit, ":Time");
+            metric = {'fps': 'FrameRate', 'runs/s': 'Runs', 'ms': 'Time'}[PerfTestRunner.unit]
+            PerfTestRunner.logStatistics(results, PerfTestRunner.unit, prefix + ":" + metric);
             if (jsHeapResults.length) {
-                PerfTestRunner.logStatistics(jsHeapResults, "bytes", ":JSHeap");
-                PerfTestRunner.logStatistics(mallocHeapResults, "bytes", ":Malloc");
+                PerfTestRunner.logStatistics(jsHeapResults, "bytes", prefix + ":JSHeap");
+                PerfTestRunner.logStatistics(mallocHeapResults, "bytes", prefix + ":Malloc");
             }
-            if (logLines)
-                logLines.forEach(logInDocument);
             if (currentTest.done)
                 currentTest.done();
+
+            if (logLines && !currentTest.continueTesting)
+                logLines.forEach(logInDocument);
         } catch (exception) {
             logInDocument("Got an exception while finalizing the test with name=" + exception.name + ", message=" + exception.message);
         }
 
-        if (window.testRunner)
-            testRunner.notifyDone();
+        if (!currentTest.continueTesting) {
+            if (window.testRunner)
+                testRunner.notifyDone();
+            return;
+        }
+
+        currentTest = null;
     }
 
     PerfTestRunner.prepareToMeasureValuesAsync = function (test) {
@@ -250,6 +267,16 @@ if (window.testRunner) {
         return true;
     }
 
+    PerfTestRunner.reportValues = function (test, values) {
+        PerfTestRunner.unit = test.unit;
+        start(test, null, true);
+        for (var i = 0; i < values.length; i++) {
+            completedIterations++;
+            ignoreWarmUpAndLog(values[i], true);
+        }
+        finish();
+    }
+
     PerfTestRunner.measureTime = function (test) {
         PerfTestRunner.unit = "ms";
         start(test, measureTimeOnce);
index edb3d412034b2a2db1be6d7ad8c13ece52c6125f..90362a55d4216e0b083b2ac7bc5e286d5f652c6f 100644 (file)
@@ -1,3 +1,37 @@
+2013-11-26  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Record subtest values in Dromaeo tests
+        https://bugs.webkit.org/show_bug.cgi?id=124498
+
+        Reviewed by Andreas Kling.
+
+        Supported parsing subtest results.
+
+        * Scripts/webkitpy/performance_tests/perftest.py: Replaced _metrics with an ordered list of subtests, each of
+        which contains a dictionary with its name and an ordered list of subtest's metrics.
+        (PerfTest.__init__): Initialize _metrics as a list.
+        (PerfTest.run): Go through each subtest and its metrics to create a list of TestMetrics.
+        (PerfTest._run_with_driver):
+        (PerfTest._ensure_metrics): Look for a subtest then a metric in _metrics.
+
+        * Scripts/webkitpy/performance_tests/perftest_unittest.py:
+        (TestPerfTest._assert_results_are_correct): Updated the assertions per changes to _metrics.
+        (TestPerfTest.test_parse_output): Ditto.
+        (TestPerfTest.test_parse_output_with_subtests): Added the metric and the unit on each subtest result as well as
+        assertions to ensure subtest results are parsed properly.
+        (TestReplayPerfTest.test_run_with_driver_accumulates_results): Updated the assertions per changes to _metrics.
+        (TestReplayPerfTest.test_run_with_driver_accumulates_memory_results): Dittp.
+
+        * Scripts/webkitpy/performance_tests/perftestsrunner.py:
+        (_generate_results_dict): When the metric for a subtest is processed before that of the main test, the url is
+        incorrectly suffixed with '/'. Fix this later by re-computing the url with TestPerfMetric.test_file_name when
+        adding new results.
+
+        * Scripts/webkitpy/performance_tests/perftestsrunner_integrationtest.py:
+        (TestWithSubtestsData): Added.
+        (TestDriver.run_test):
+        (MainTest.test_run_test_with_subtests): Added.
+
 2013-11-26  Ryosuke Niwa  <rniwa@webkit.org>
 
         Enable HTML template element on Windows ports
index 05537e07e32086066dc6cfdd50e7256a24b455cd..4a9c7633f54e0e93d447d30e56bf34c797b59c8d 100644 (file)
@@ -100,8 +100,7 @@ class PerfTest(object):
         self._test_name = test_name
         self._test_path = test_path
         self._description = None
-        self._metrics = {}
-        self._ordered_metrics_name = []
+        self._metrics = []
         self._test_runner_count = test_runner_count
 
     def test_name(self):
@@ -136,13 +135,13 @@ class PerfTest(object):
             _log.info('DESCRIPTION: %s' % self._description)
 
         results = []
-        for metric_name in self._ordered_metrics_name:
-            metric = self._metrics[metric_name]
-            results.append(metric)
-            if should_log:
-                legacy_chromium_bot_compatible_name = self.test_name_without_file_extension().replace('/', ': ')
-                self.log_statistics(legacy_chromium_bot_compatible_name + ': ' + metric.name(),
-                    metric.flattened_iteration_values(), metric.unit())
+        for subtest in self._metrics:
+            for metric in subtest['metrics']:
+                results.append(metric)
+                if should_log and not subtest['name']:
+                    legacy_chromium_bot_compatible_name = self.test_name_without_file_extension().replace('/', ': ')
+                    self.log_statistics(legacy_chromium_bot_compatible_name + ': ' + metric.name(),
+                        metric.flattened_iteration_values(), metric.unit())
 
         return results
 
@@ -169,7 +168,7 @@ class PerfTest(object):
             (median, unit, stdev, unit, sorted_values[0], unit, sorted_values[-1], unit))
 
     _description_regex = re.compile(r'^Description: (?P<description>.*)$', re.IGNORECASE)
-    _metrics_regex = re.compile(r'^:(?P<metric>Time|Malloc|JSHeap) -> \[(?P<values>(\d+(\.\d+)?)(, \d+(\.\d+)?)+)\] (?P<unit>[a-z/]+)')
+    _metrics_regex = re.compile(r'^(?P<subtest>[A-Za-z0-9\(\[].+)?:(?P<metric>[A-Z][A-Za-z]+) -> \[(?P<values>(\d+(\.\d+)?)(, \d+(\.\d+)?)+)\] (?P<unit>[a-z/]+)?$')
 
     def _run_with_driver(self, driver, time_out_ms):
         output = self.run_single(driver, self.test_path(), time_out_ms)
@@ -189,16 +188,27 @@ class PerfTest(object):
                 _log.error('ERROR: ' + line)
                 return False
 
-            metric = self._ensure_metrics(metric_match.group('metric'), metric_match.group('unit'))
+            metric = self._ensure_metrics(metric_match.group('metric'), metric_match.group('subtest'), metric_match.group('unit'))
             metric.append_group(map(lambda value: float(value), metric_match.group('values').split(', ')))
 
         return True
 
-    def _ensure_metrics(self, metric_name, unit=None):
-        if metric_name not in self._metrics:
-            self._metrics[metric_name] = PerfTestMetric(self.test_name_without_file_extension().split('/'), self._test_name, metric_name, unit)
-            self._ordered_metrics_name.append(metric_name)
-        return self._metrics[metric_name]
+    def _ensure_metrics(self, metric_name, subtest_name='', unit=None):
+        try:
+            subtest = next(subtest for subtest in self._metrics if subtest['name'] == subtest_name)
+        except StopIteration:
+            subtest = {'name': subtest_name, 'metrics': []}
+            self._metrics.append(subtest)
+
+        try:
+            return next(metric for metric in subtest['metrics'] if metric.name() == metric_name)
+        except StopIteration:
+            path = self.test_name_without_file_extension().split('/')
+            if subtest_name:
+                path += [subtest_name]
+            metric = PerfTestMetric(path, self._test_name, metric_name, unit)
+            subtest['metrics'].append(metric)
+            return metric
 
     def run_single(self, driver, test_path, time_out_ms, should_run_pixel_test=False):
         return driver.run_test(DriverInput(test_path, time_out_ms, image_hash=None, should_run_pixel_test=should_run_pixel_test), stop_when_done=False)
@@ -236,9 +246,6 @@ class PerfTest(object):
         re.compile(re.escape("""Blocked access to external URL http://www.whatwg.org/specs/web-apps/current-work/""")),
         re.compile(r"CONSOLE MESSAGE: (line \d+: )?Blocked script execution in '[A-Za-z0-9\-\.:]+' because the document's frame is sandboxed and the 'allow-scripts' permission is not set."),
         re.compile(r"CONSOLE MESSAGE: (line \d+: )?Not allowed to load local resource"),
-        # Dromaeo reports values for subtests. Ignore them for now.
-        # FIXME: Remove once subtests are supported
-        re.compile(r'^[A-Za-z0-9\(\[].+( -> )(\[?[0-9\., ]+\])( [a-z/]+)?$'),
     ]
 
     def _filter_output(self, output):
index e7d94f853614d62e06a0551334173adb1be3bbd1..23818e41e462917a1df58623ab331db6bd8844ae 100644 (file)
@@ -91,9 +91,12 @@ class TestPerfTestMetric(unittest.TestCase):
 class TestPerfTest(unittest.TestCase):
     def _assert_results_are_correct(self, test, output):
         test.run_single = lambda driver, path, time_out_ms: output
-        self.assertTrue(test._run_with_driver(None, None))
-        self.assertEqual(test._metrics.keys(), ['Time'])
-        self.assertEqual(test._metrics['Time'].flattened_iteration_values(), [1080, 1120, 1095, 1101, 1104])
+        self.assertTrue(test.run(10))
+        subtests = test._metrics
+        self.assertEqual(map(lambda test: test['name'], subtests), [None])
+        metrics = subtests[0]['metrics']
+        self.assertEqual(map(lambda metric: metric.name(), metrics), ['Time'])
+        self.assertEqual(metrics[0].flattened_iteration_values(), [1080, 1120, 1095, 1101, 1104] * 4)
 
     def test_parse_output(self):
         output = DriverOutput("""
@@ -108,7 +111,9 @@ class TestPerfTest(unittest.TestCase):
             actual_stdout, actual_stderr, actual_logs = output_capture.restore_output()
         self.assertEqual(actual_stdout, '')
         self.assertEqual(actual_stderr, '')
-        self.assertEqual(actual_logs, '')
+        self.assertEqual(actual_logs, """RESULT some-test: Time= 1100.0 ms
+median= 1101.0 ms, stdev= 13.3140211016 ms, min= 1080.0 ms, max= 1120.0 ms
+""")
 
     def _assert_failed_on_line(self, output_text, expected_log):
         output = DriverOutput(output_text, image=None, image_hash=None, audio=None)
@@ -155,28 +160,56 @@ Description: this is a test description.
     def test_parse_output_with_subtests(self):
         output = DriverOutput("""
 Description: this is a test description.
-some test -> [1, 2, 3, 4, 5]
-some other test = else -> [6, 7, 8, 9, 10]
-Array Construction, [] -> [11, 12, 13, 14, 15]
-Concat String -> [15163, 15304, 15386, 15608, 15622]
-jQuery - addClass -> [2785, 2815, 2826, 2841, 2861]
-Dojo - div:only-child -> [7825, 7910, 7950, 7958, 7970]
-Dojo - div:nth-child(2n+1) -> [3620, 3623, 3633, 3641, 3658]
-Dojo - div > div -> [10158, 10172, 10180, 10183, 10231]
-Dojo - div ~ div -> [6673, 6675, 6714, 6848, 6902]
+some test:Time -> [1, 2, 3, 4, 5] ms
+some other test = else:Time -> [6, 7, 8, 9, 10] ms
+some other test = else:Malloc -> [11, 12, 13, 14, 15] bytes
+Array Construction, []:Time -> [11, 12, 13, 14, 15] ms
+Concat String:Time -> [15163, 15304, 15386, 15608, 15622] ms
+jQuery - addClass:Time -> [2785, 2815, 2826, 2841, 2861] ms
+Dojo - div:only-child:Time -> [7825, 7910, 7950, 7958, 7970] ms
+Dojo - div:nth-child(2n+1):Time -> [3620, 3623, 3633, 3641, 3658] ms
+Dojo - div > div:Time -> [10158, 10172, 10180, 10183, 10231] ms
+Dojo - div ~ div:Time -> [6673, 6675, 6714, 6848, 6902] ms
 
 :Time -> [1080, 1120, 1095, 1101, 1104] ms
 """, image=None, image_hash=None, audio=None)
         output_capture = OutputCapture()
         output_capture.capture_output()
         try:
-            test = PerfTest(MockPort(), 'some-test', '/path/some-dir/some-test')
-            self._assert_results_are_correct(test, output)
+            test = PerfTest(MockPort(), 'some-dir/some-test', '/path/some-dir/some-test')
+            test.run_single = lambda driver, path, time_out_ms: output
+            self.assertTrue(test.run(10))
         finally:
             actual_stdout, actual_stderr, actual_logs = output_capture.restore_output()
+
+        subtests = test._metrics
+        self.assertEqual(map(lambda test: test['name'], subtests), ['some test', 'some other test = else',
+            'Array Construction, []', 'Concat String', 'jQuery - addClass', 'Dojo - div:only-child',
+            'Dojo - div:nth-child(2n+1)', 'Dojo - div > div', 'Dojo - div ~ div', None])
+
+        some_test_metrics = subtests[0]['metrics']
+        self.assertEqual(map(lambda metric: metric.name(), some_test_metrics), ['Time'])
+        self.assertEqual(some_test_metrics[0].path(), ['some-dir', 'some-test', 'some test'])
+        self.assertEqual(some_test_metrics[0].flattened_iteration_values(), [1, 2, 3, 4, 5] * 4)
+
+        some_other_test_metrics = subtests[1]['metrics']
+        self.assertEqual(map(lambda metric: metric.name(), some_other_test_metrics), ['Time', 'Malloc'])
+        self.assertEqual(some_other_test_metrics[0].path(), ['some-dir', 'some-test', 'some other test = else'])
+        self.assertEqual(some_other_test_metrics[0].flattened_iteration_values(), [6, 7, 8, 9, 10] * 4)
+        self.assertEqual(some_other_test_metrics[1].path(), ['some-dir', 'some-test', 'some other test = else'])
+        self.assertEqual(some_other_test_metrics[1].flattened_iteration_values(), [11, 12, 13, 14, 15] * 4)
+
+        main_metrics = subtests[len(subtests) - 1]['metrics']
+        self.assertEqual(map(lambda metric: metric.name(), main_metrics), ['Time'])
+        self.assertEqual(main_metrics[0].path(), ['some-dir', 'some-test'])
+        self.assertEqual(main_metrics[0].flattened_iteration_values(), [1080, 1120, 1095, 1101, 1104] * 4)
+
         self.assertEqual(actual_stdout, '')
         self.assertEqual(actual_stderr, '')
-        self.assertEqual(actual_logs, '')
+        self.assertEqual(actual_logs, """DESCRIPTION: this is a test description.
+RESULT some-dir: some-test: Time= 1100.0 ms
+median= 1101.0 ms, stdev= 13.3140211016 ms, min= 1080.0 ms, max= 1120.0 ms
+""")
 
 
 class TestSingleProcessPerfTest(unittest.TestCase):
index 0fe27d4476890cf5231975959bc8b4efe5282f42..19da75260740fa496d49917405017c9a0f3d4fff 100644 (file)
@@ -262,10 +262,11 @@ class PerfTestsRunner(object):
             path = metric.path()
             for i in range(0, len(path)):
                 is_last_token = i + 1 == len(path)
-                url = view_source_url('PerformanceTests/' + (metric.test_file_name() if is_last_token else '/'.join(path[0:i + 1])))
+                url = view_source_url('PerformanceTests/' + '/'.join(path[0:i + 1]))
                 tests.setdefault(path[i], {'url': url})
                 current_test = tests[path[i]]
                 if is_last_token:
+                    current_test['url'] = view_source_url('PerformanceTests/' + metric.test_file_name())
                     current_test.setdefault('metrics', {})
                     assert metric.name() not in current_test['metrics']
                     current_test['metrics'][metric.name()] = {'current': metric.grouped_iteration_values()}
index 0b04bf93eadae906a00bdfa4dd55882ebc2d663e..06d3b5c1b95b7a4446203ddd03e208566d7d2975 100644 (file)
@@ -96,6 +96,26 @@ Finished: 0.1 s
     malloc_results = {'current': [[529000, 511000, 548000, 536000, 521000]] * 4}
 
 
+class TestWithSubtestsData:
+    text = """subtest:Time -> [1, 2, 3, 4, 5] ms
+:Time -> [1080, 1120, 1095, 1101, 1104] ms
+"""
+
+    output = """Running 1 tests
+Running Parser/test-with-subtests.html (1 of 1)
+RESULT Parser: test-with-subtests: Time= 1100.0 ms
+median= 1101.0 ms, stdev= 13.31402 ms, min= 1080.0 ms, max= 1120.0 ms
+Finished: 0.1 s
+"""
+
+    results = {'url': 'http://trac.webkit.org/browser/trunk/PerformanceTests/Parser/test-with-subtests.html',
+        'metrics': {'Time': {'current': [[1080.0, 1120.0, 1095.0, 1101.0, 1104.0]] * 4}},
+        'tests': {
+            'subtest': {
+                'url': 'http://trac.webkit.org/browser/trunk/PerformanceTests/Parser/test-with-subtests.html',
+                'metrics': {'Time': {'current': [[1.0, 2.0, 3.0, 4.0, 5.0]] * 4}}}}}
+
+
 class TestDriver:
     def run_test(self, driver_input, stop_when_done):
         text = ''
@@ -117,6 +137,8 @@ class TestDriver:
             text = SomeParserTestData.text
         elif driver_input.test_name.endswith('memory-test.html'):
             text = MemoryTestData.text
+        elif driver_input.test_name.endswith('test-with-subtests.html'):
+            text = TestWithSubtestsData.text
         return DriverOutput(text, '', '', '', crash=crash, timeout=timeout)
 
     def start(self):
@@ -223,6 +245,24 @@ class MainTest(unittest.TestCase):
         self.assertEqual(parser_tests['memory-test']['metrics']['JSHeap'], MemoryTestData.js_heap_results)
         self.assertEqual(parser_tests['memory-test']['metrics']['Malloc'], MemoryTestData.malloc_results)
 
+    def test_run_test_with_subtests(self):
+        runner, port = self.create_runner_and_setup_results_template()
+        runner._timestamp = 123456789
+        port.host.filesystem.write_text_file(runner._base_path + '/Parser/test-with-subtests.html', 'some content')
+
+        output = OutputCapture()
+        output.capture_output()
+        try:
+            unexpected_result_count = runner.run()
+        finally:
+            stdout, stderr, log = output.restore_output()
+
+        self.assertEqual(unexpected_result_count, 0)
+        self.assertEqual(self._normalize_output(log), TestWithSubtestsData.output + '\nMOCK: user.open_url: file://...\n')
+        parser_tests = self._load_output_json(runner)[0]['tests']['Parser']['tests']
+        self.maxDiff = None
+        self.assertEqual(parser_tests['test-with-subtests'], TestWithSubtestsData.results)
+
     def _test_run_with_json_output(self, runner, filesystem, upload_succeeds=False, results_shown=True, expected_exit_code=0, repeat=1, compare_logs=True):
         filesystem.write_text_file(runner._base_path + '/Parser/some-parser.html', 'some content')
         filesystem.write_text_file(runner._base_path + '/Bindings/event-target-wrapper.html', 'some content')