Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(131)

Unified Diff: chrome/test/vr/perf/latency/webvr_latency_test.py

Issue 2823883003: Make VR latency results compatible with perf dashboard, refactor into classes (Closed)
Patch Set: Address nits Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/test/vr/perf/latency/run_latency_test.py ('k') | testing/buildbot/gn_isolate_map.pyl » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/test/vr/perf/latency/webvr_latency_test.py
diff --git a/chrome/test/vr/perf/latency/webvr_latency_test.py b/chrome/test/vr/perf/latency/webvr_latency_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..0a289a070b25e61536647716280b198f2f5248ee
--- /dev/null
+++ b/chrome/test/vr/perf/latency/webvr_latency_test.py
@@ -0,0 +1,197 @@
+# Copyright 2017 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 motopho_thread as mt
+import robot_arm as ra
+
+import json
+import glob
+import logging
+import numpy
+import os
+import re
+import subprocess
+import sys
+import time
+
+
+MOTOPHO_THREAD_TIMEOUT = 30
+
+
+def GetTtyDevices(tty_pattern, vendor_ids):
+ """Finds all devices connected to tty that match a pattern and device id.
+
+ If a serial device is connected to the computer via USB, this function
+ will check all tty devices that match tty_pattern, and return the ones
+ that have vendor identification number in the list vendor_ids.
+
+ Args:
+ tty_pattern: The search pattern, such as r'ttyACM\d+'.
+ vendor_ids: The list of 16-bit USB vendor ids, such as [0x2a03].
+
+ Returns:
+ A list of strings of tty devices, for example ['ttyACM0'].
+ """
+ product_string = 'PRODUCT='
+ sys_class_dir = '/sys/class/tty/'
+
+ tty_devices = glob.glob(sys_class_dir + '*')
+
+ matcher = re.compile('.*' + tty_pattern)
+ tty_matches = [x for x in tty_devices if matcher.search(x)]
+ tty_matches = [x[len(sys_class_dir):] for x in tty_matches]
+
+ found_devices = []
+ for match in tty_matches:
+ class_filename = sys_class_dir + match + '/device/uevent'
+ with open(class_filename, 'r') as uevent_file:
+ # Look for the desired product id in the uevent text.
+ for line in uevent_file:
+ if product_string in line:
+ ids = line[len(product_string):].split('/')
+ ids = [int(x, 16) for x in ids]
+
+ for desired_id in vendor_ids:
+ if desired_id in ids:
+ found_devices.append(match)
+
+ return found_devices
+
+
+class WebVrLatencyTest(object):
+ """Base class for all WebVR latency tests.
+
+ This is meant to be subclassed for each platform the test is run on. While
+ the latency test itself is cross-platform, the setup and teardown for
+ tests is platform-dependent.
+ """
+ def __init__(self, args):
+ self.args = args
+ self._num_samples = args.num_samples
+ self._flicker_app_url = args.url
+ assert (self._num_samples > 0),'Number of samples must be greater than 0'
+ self._device_name = 'generic_device'
+
+ # Connect to the Arduino that drives the servos
+ devices = GetTtyDevices(r'ttyACM\d+', [0x2a03, 0x2341])
+ assert (len(devices) == 1),'Found %d devices, expected 1' % len(devices)
+ self.robot_arm = ra.RobotArm(devices[0])
+
+ def RunTest(self):
+ """Runs the steps to start Chrome, measure/save latency, and clean up."""
+ self._Setup()
+ self._Run()
+ self._Teardown()
+
+ def _Setup(self):
+ """Perform any platform-specific setup."""
+ raise NotImplementedError(
+ 'Platform-specific setup must be implemented in subclass')
+
+ def _Run(self):
+ """Run the latency test.
+
+ Handles the actual latency measurement, which is identical across
+ different platforms, as well as result saving.
+ """
+ # Motopho scripts use relative paths, so switch to the Motopho directory
+ os.chdir(self.args.motopho_path)
+
+ # Set up the thread that runs the Motopho script
+ motopho_thread = mt.MotophoThread(self._num_samples)
+ motopho_thread.start()
+
+ # Run multiple times so we can get an average and standard deviation
+ for _ in xrange(self._num_samples):
+ self.robot_arm.ResetPosition()
+ # Start the Motopho script
+ motopho_thread.StartIteration()
+ # Let the Motopho be stationary so the script can calculate the bias
+ time.sleep(3)
+ motopho_thread.BlockNextIteration()
+ # Move so we can measure latency
+ self.robot_arm.StartMotophoMovement()
+ if not motopho_thread.WaitForIterationEnd(MOTOPHO_THREAD_TIMEOUT):
+ # TODO(bsheedy): Look into ways to prevent Motopho from not sending any
+ # data until unplugged and replugged into the machine after a reboot.
+ logging.error('Motopho thread timeout, '
+ 'Motopho may need to be replugged.')
+ self.robot_arm.StopAllMovement()
+ time.sleep(1)
+ self._SaveResults(motopho_thread.latencies, motopho_thread.correlations)
+
+ def _Teardown(self):
+ """Performs any platform-specific teardown."""
+ raise NotImplementedError(
+ 'Platform-specific setup must be implemented in subclass')
+
+ def _RunCommand(self, cmd):
+ """Runs the given cmd list and returns its output.
+
+ Prints the command's output and exits if any error occurs.
+
+ Returns:
+ A string containing the stdout and stderr of the command.
+ """
+ try:
+ return subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as e:
+ logging.error('Failed command output: %s', e.output)
+ raise e
+
+ def _SetChromeCommandLineFlags(self, flags):
+ raise NotImplementedError(
+ 'Command-line flag setting must be implemented in subclass')
+
+ def _SaveResults(self, latencies, correlations):
+ """Saves the results to a JSON file.
+
+ Saved JSON object is compatible with Chrome perf dashboard if
+ put in as the 'chart_data' value. Also logs the raw data and its
+ average/standard deviation.
+ """
+ avg_latency = sum(latencies) / len(latencies)
+ std_latency = numpy.std(latencies)
+ avg_correlation = sum(correlations) / len(correlations)
+ std_correlation = numpy.std(correlations)
+ logging.info('Raw latencies: %s\nRaw correlations: %s\n'
+ 'Avg latency: %f +/- %f\nAvg correlation: %f +/- %f',
+ str(latencies), str(correlations), avg_latency, std_latency,
+ avg_correlation, std_correlation)
+
+ if not (self.args.output_dir and os.path.isdir(self.args.output_dir)):
+ logging.warning('No output directory set, not saving results to file')
+ return
+
+ results = {
+ 'format_version': '1.0',
+ 'benchmark_name': 'webvr_latency',
+ 'benchmark_description': 'Measures the motion-to-photon latency of WebVR',
+ 'charts': {
+ 'correlation': {
+ 'summary': {
+ 'improvement_direction': 'up',
+ 'name': 'correlation',
+ 'std': std_correlation,
+ 'type': 'list_of_scalar_values',
+ 'units': '',
+ 'values': correlations,
+ },
+ },
+ 'latency': {
+ 'summary': {
+ 'improvement_direction': 'down',
+ 'name': 'latency',
+ 'std': std_latency,
+ 'type': 'list_of_scalar_values',
+ 'units': 'ms',
+ 'values': latencies,
+ },
+ }
+ }
+ }
+
+ with file(os.path.join(self.args.output_dir,
+ self.args.results_file), 'w') as outfile:
+ json.dump(results, outfile)
« no previous file with comments | « chrome/test/vr/perf/latency/run_latency_test.py ('k') | testing/buildbot/gn_isolate_map.pyl » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698