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

Side by Side Diff: chrome/test/functional/media/media_cn_perf.py

Issue 8802030: Introduce new PyAuto test to measure EPT, TTP metrics. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Code review fixes. Created 9 years 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 unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/env python
Ami GONE FROM CHROMIUM 2011/12/07 01:33:08 Ditto filename comment
DaleCurtis 2011/12/07 22:57:32 Done.
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Records metrics on playing media under constrained network conditions.
7
8 Spins up a Constrained Network Server (CNS) and runs through a test matrix of
9 bandwidth, latency, and packet loss settings. Each run records a
10 time-to-playback (TTP) and extra-play-time (EPT) metric in a format consumable
11 by the Chromium perf bots.
12
13 See design document "Testing Video Performance Over Constrained Networks" for
14 more information -- http://goo.gl/EdAWU
Ami GONE FROM CHROMIUM 2011/12/07 01:33:08 Move this to a public doc, or remove pointer from
DaleCurtis 2011/12/07 22:57:32 Removed it, relevant information is already includ
15
16 Since even a small number of different settings yields a large test matrix, the
17 design is threaded... however PyAuto is not, so a global lock is used when calls
18 into PyAuto are necessary. The number of threads can be set by _TEST_THREADS.
19
20 The CNS code is located under: <root>/src/media/tools/constrained_network_server
21 """
22
23 import itertools
24 import os
25 import Queue
26 import subprocess
27 import sys
28 import threading
29
30 import pyauto_media
31 import pyauto
32 import pyauto_paths
33 import pyauto_utils
34
35
36 # Settings for each network constraint.
37 _BANDWIDTH_SETTINGS_KBPS = {'None': 0, 'Low': 256, 'Medium': 2000, 'High': 5000}
38 _LATENCY_SETTINGS_MS = {'None': 0, 'Low': 43, 'Medium': 105, 'High': 180}
39 _PACKET_LOSS_SETTINGS_PERCENT = {'None': 0, 'Medium': 2, 'High': 5}
40
41 # Test constraints are all possible combination of the above settings. Each
42 # tuple must be of the form (Bandwidth, Latency, Packet Loss).
43 _TEST_CONSTRAINTS = itertools.product(
44 _BANDWIDTH_SETTINGS_KBPS.values(),
45 _LATENCY_SETTINGS_MS.values(),
46 _PACKET_LOSS_SETTINGS_PERCENT.values())
47
48 _TEST_CONSTRAINT_NAMES = itertools.product(
49 _BANDWIDTH_SETTINGS_KBPS.keys(),
50 _LATENCY_SETTINGS_MS.keys(),
51 _PACKET_LOSS_SETTINGS_PERCENT.keys())
52
53 # HTML test path; relative to src/chrome/test/data. Loads a test video and
54 # records metrics in JavaScript.
55 _TEST_HTML_PATH = os.path.join('media', 'html', 'media_cn.html')
56
57 # Number of threads to use during testing.
58 # TODO(dalecurtis): Should be set on the command line.
Ami GONE FROM CHROMIUM 2011/12/07 01:33:08 Why?
DaleCurtis 2011/12/07 22:57:32 Good point. I was looking at this as more of a uti
59 _TEST_THREADS = 3
60
61 # File name of video to collect metrics for.
62 # TODO(dalecurtis): Should be set on the command line.
63 _TEST_VIDEO = 'roller.webm'
64
65 # Path to CNS executable relative to source root.
66 _CNS_PATH = os.path.join(
67 'media', 'tools', 'constrained_network_server', 'cns.py')
68
69 # Port to start the CNS on.
70 _CNS_PORT = 9000
Ami GONE FROM CHROMIUM 2011/12/07 01:33:08 Is there a way to get a known-unused port on the b
DaleCurtis 2011/12/07 22:57:32 Short of trying to open a socket on each port and
71
72 # Base CNS URL, only requires & separated parameter names appended.
73 _CNS_BASE_URL = 'http://127.0.0.1:%d/ServeConstrained?' % _CNS_PORT
74
75
76 class TestWorker(threading.Thread):
77 """Worker thread. For each queue entry: opens tab, runs test, closes tab."""
78
79 # Atomic, monotonically increasing task identifier. Used to ID tabs.
80 _task_id = itertools.count()
81
82 def __init__(self, pyauto_test, tasks, automation_lock, url):
83 """Sets up TestWorker class variables.
84
85 Args:
86 pyauto_test: Reference to a pyauto.PyUITest instance.
87 tasks: Queue containing (settings, name) tuples.
88 automation_lock: Global automation lock for pyauto calls.
89 url: File URL to HTML/JavaScript test code.
90 """
91 threading.Thread.__init__(self)
92 self._tasks = tasks
93 self._automation_lock = automation_lock
94 self._pyauto = pyauto_test
95 self._url = url
96 self.start()
97
98 def _FindTab(self, url):
99 """Returns the tab index for the tab belonging to this url."""
100 for tab in self._pyauto.GetBrowserInfo()['windows'][0]['tabs']:
Ami GONE FROM CHROMIUM 2011/12/07 01:33:08 No lock?
DaleCurtis 2011/12/07 22:57:32 Already locked in _HaveMetrics() I'll add a commen
101 if tab['url'] == url:
102 return tab['index']
103
104 def _HaveMetrics(self, unique_url):
105 """Returns true if metrics are ready. Set self.{_ept,_ttp} < 0 pre-run."""
Ami GONE FROM CHROMIUM 2011/12/07 01:33:08 Why not set to -1 in the ctor instead of "pre-run"
DaleCurtis 2011/12/07 22:57:32 It needs to be set for each call. I suspect there
106 with self._automation_lock:
107 tab = self._FindTab(unique_url)
108
109 if self._ept < 0:
110 self._ept = self._pyauto.GetDOMValue('extra_play_time', tab_index=tab)
111 if self._ttp < 0:
112 self._ttp = self._pyauto.GetDOMValue('time_to_playback', tab_index=tab)
113 return self._ept >= 0 and self._ttp >= 0
114
115 def run(self):
116 """Opens tab, starts HTML test, and records metrics for each queue entry.
117
118 No exception handling is done to make sure the main thread exits properly
119 during Chrome crashes or other failures. Doing otherwise has the potential
120 to leave the CNS server running in the background.
121
122 For a clean shutdown, put the magic exit value (None, None) in the queue.
123 """
124 while True:
125 settings, name = self._tasks.get()
126
127 # Check for magic exit values.
128 if (settings, name) == (None, None):
Ami GONE FROM CHROMIUM 2011/12/07 01:33:08 So one worker thread gets the magic and exits, but
DaleCurtis 2011/12/07 22:57:32 A magic exit pair is added to the Queue for each t
129 break
130
131 # Build video source URL. Values <= 0 mean the setting is disabled.
132 video_url = [_CNS_BASE_URL, 'f=' + _TEST_VIDEO]
133 if settings[0] > 0:
134 video_url.append('bandwidth=%d' % settings[0])
135 if settings[1] > 0:
136 video_url.append('latency=%d' % settings[1])
137 if settings[2] > 0:
138 video_url.append('loss=%d' % settings[2])
139 video_url = '&'.join(video_url)
140
141 # Make the test URL unique so we can figure out our tab index later.
142 unique_url = '%s?%d' % (self._url, TestWorker._task_id.next())
143
144 # Start the test!
145 with self._automation_lock:
146 self._pyauto.AppendTab(pyauto.GURL(unique_url))
147 self._pyauto.CallJavascriptFunc(
148 'startTest', [video_url], tab_index=self._FindTab(unique_url))
149
150 # Wait until the necessary metrics have been collected. Okay to not lock
151 # here since pyauto.WaitUntil doesn't call into Chrome.
152 self._ept = self._ttp = -1
153 self._pyauto.WaitUntil(
154 self._HaveMetrics, args=[unique_url], retry_sleep=2)
155
156 # Record results.
157 # TODO(dalecurtis): Support reference builds.
158 series_name = ''.join(name)
159 pyauto_utils.PrintPerfResult('ept', series_name, self._ept, '%')
160 pyauto_utils.PrintPerfResult('ttp', series_name, self._ttp, 'ms')
161
162 # Close the tab.
163 with self._automation_lock:
164 self._pyauto.GetBrowserWindow(0).GetTab(
165 self._FindTab(unique_url)).Close(True)
166
167 # TODO(dalecurtis): Check results for regressions.
168 self._tasks.task_done()
169
170
171 class MediaConstrainedNetworkPerfTest(pyauto.PyUITest):
172 """PyAuto test container. See file doc string for more information."""
173
174 def setUp(self):
175 """Starts the Constrained Network Server (CNS)."""
176 cmd = [sys.executable, os.path.join(pyauto_paths.GetSourceDir(), _CNS_PATH),
177 '--port', str(_CNS_PORT),
178 '--interface', 'lo',
179 '--www-root', os.path.join(self.DataDir(), 'media')]
180 process = subprocess.Popen(cmd, stderr=subprocess.PIPE)
181
182 # Wait for server to start up.
183 line = True
184 while line:
185 line = process.stderr.readline()
186 if 'STARTED' in line:
187 self._server_pid = process.pid
188 pyauto.PyUITest.setUp(self)
189 return
190 self.fail('Failed to start CNS.')
191
192 def tearDown(self):
193 """Stops the Constrained Network Server (CNS)."""
194 pyauto.PyUITest.tearDown(self)
195 self.Kill(self._server_pid)
196
197 def testConstrainedNetworkPerf(self):
198 """Starts CNS, spins up worker threads to run through _TEST_CONSTRAINTS."""
199 # Convert relative test path into an absolute path.
200 test_url = self.GetFileURLForDataPath(_TEST_HTML_PATH)
201
202 # PyAuto doesn't support threads, so we synchronize all automation calls.
203 automation_lock = threading.Lock()
204
205 # Spin up worker threads.
206 tasks = Queue.Queue()
207 threads = []
208 for _ in xrange(_TEST_THREADS):
209 threads.append(TestWorker(self, tasks, automation_lock, test_url))
210
211 for settings, name in zip(_TEST_CONSTRAINTS, _TEST_CONSTRAINT_NAMES):
212 tasks.put((settings, name))
213
214 # Add shutdown magic to end of queue.
215 for thread in threads:
216 tasks.put((None, None))
217
218 # Wait for threads to exit, gracefully or otherwise.
219 for thread in threads:
220 thread.join()
221
222
223 if __name__ == '__main__':
224 # TODO(dalecurtis): Process command line parameters here.
225 pyauto_media.Main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698