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

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: Cleanup. 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
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
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 logging
25 import os
26 import Queue
27 import signal
28 import subprocess
29 import threading
30
31 import pyauto_media
32 import pyauto
33 import pyauto_paths
34 import pyauto_utils
35
36
37 # Settings for each network constraint.
38 _BANDWIDTH_SETTINGS_KBPS = {'Off': 0, 'Low': 256, 'Medium': 2000, 'High': 5000}
39 _LATENCY_SETTINGS_MS = {'Off': 0, 'Low': 43, 'Medium': 105, 'High': 180}
40 _PACKET_LOSS_SETTINGS_PERCENT = {'Off': 0, 'Medium': 2, 'High': 5}
41
42 # Test constraints are all possible combination of the above settings. Each
Nirnimesh 2011/12/06 22:15:05 Leave 2 blank spaces after every period, ie before
DaleCurtis 2011/12/06 23:42:54 Ugh, if you insist. In protest, I'd like to point
Nirnimesh 2011/12/06 23:58:36 I don't like it either. All my initial pyauto code
43 # tuple must be of the form (Bandwidth, Latency, Packet Loss).
Nirnimesh 2011/12/06 22:15:05 s/tuple/Tuple
DaleCurtis 2011/12/06 23:42:54 Tuple is not a proper noun and this is a continuat
44 _TEST_CONSTRAINTS = itertools.product(
45 _BANDWIDTH_SETTINGS_KBPS.values(),
46 _LATENCY_SETTINGS_MS.values(),
47 _PACKET_LOSS_SETTINGS_PERCENT.values())
48
49 _TEST_CONSTRAINT_NAMES = itertools.product(
50 _BANDWIDTH_SETTINGS_KBPS.keys(),
51 _LATENCY_SETTINGS_MS.keys(),
52 _PACKET_LOSS_SETTINGS_PERCENT.keys())
53
54 # HTML test path; relative to src/chrome/test/data. Loads a test video and
55 # records metrics in JavaScript.
56 _TEST_HTML_PATH = os.path.join('media', 'html', 'media_cn.html')
57
58 # Number of threads to use during testing. TODO(dalecurtis): Should be set on
Nirnimesh 2011/12/06 22:15:05 move TODO to next line
DaleCurtis 2011/12/06 23:42:54 Done.
59 # the command line.
60 _TEST_THREADS = 3
61
62 # File name of video to collect metrics for. TODO(dalecurtis): Should be set on
Nirnimesh 2011/12/06 22:15:05 move TODO to next line
DaleCurtis 2011/12/06 23:42:54 Done.
63 # the command line.
64 _TEST_VIDEO = 'dancing.webm'
65
66 # Path to CNS executable relative to source root.
67 _CNS_PATH = os.path.join(
68 'media', 'tools', 'constrained_network_server', 'cns.py')
69
70 # Port to start the CNS on.
71 _CNS_PORT = 9000
72
73 # Base CNS URL, only requires & separated parameter names appended.
74 _CNS_BASE_URL = 'http://127.0.0.1:%d/ServeConstrained?' % _CNS_PORT
75
76
77 class TestWorker(threading.Thread):
78 """Worker thread. For each queue entry: opens tab, runs test, closes tab."""
79
80 # Atomic, monotonically increasing task identifier. Used to ID tabs.
81 _task_id = itertools.count()
82
83 def __init__(self, pyauto_test, tasks, automation_lock, url):
84 """Sets up TestWorker class variables.
85
86 Args:
87 pyauto_test: Reference to a pyauto.PyUITest instance.
88 tasks: Queue containing (settings, name) tuples.
89 automation_lock: Global automation lock for pyauto calls.
90 url: File URL to HTML/JavaScript test code.
91 """
92 threading.Thread.__init__(self)
93 self._tasks = tasks
94 self._automation_lock = automation_lock
95 self._pyauto = pyauto_test
96 self._url = url
97 self.start()
98
99 def _FindTab(self, url):
100 """Returns the tab index for the tab belonging to this thread."""
Nirnimesh 2011/12/06 22:15:05 s/thread/url/ ?
DaleCurtis 2011/12/06 23:42:54 Done.
101 for tab in self._pyauto.GetBrowserInfo()['windows'][0]['tabs']:
102 if tab['url'] == url:
103 return tab['index']
104
105 def _HaveMetrics(self, unique_url):
106 """Returns if metrics are ready or not. Set self.{_ept,_ttp} < 0 pre-run."""
107 with self._automation_lock:
108 tab = self._FindTab(unique_url)
109
110 if self._ept < 0:
111 self._ept = self._pyauto.GetDOMValue('extra_play_time', tab_index=tab)
112 if self._ttp < 0:
113 self._ttp = self._pyauto.GetDOMValue('time_to_playback', tab_index=tab)
114 return self._ept >= 0 and self._ttp >= 0
115
116 def run(self):
117 """Opens tab, starts HTML test, and records metrics for each queue entry.
118
119 No exception handling is done to make sure the main thread exits properly
120 during Chrome crashes or other failures. Doing otherwise has the potential
121 to leave the CNS server running in the background.
122
123 For a clean shutdown, put the magic exit value (None, None) in the queue.
124 """
125 while True:
126 settings, name = self._tasks.get()
127
128 # Listen for magic exit values.
Nirnimesh 2011/12/06 22:15:05 s/Listen/Check/
DaleCurtis 2011/12/06 23:42:54 Done.
129 if (settings, name) == (None, None):
130 break
131
132 # Build video source URL. Values <= 0 mean the setting is disabled.
133 video_url = [_CNS_BASE_URL, 'f=' + _TEST_VIDEO]
134 if settings[0] > 0:
135 video_url.append('bandwidth=%d' % settings[0])
136 if settings[1] > 0:
137 video_url.append('latency=%d' % settings[1])
138 if settings[2] > 0:
139 video_url.append('loss=%d' % settings[2])
140 video_url = '&'.join(video_url)
141
142 # Make the test URL unique so we can figure out our tab index later.
Nirnimesh 2011/12/06 22:15:05 why not save the tab index?
DaleCurtis 2011/12/06 23:42:54 When we talked via chat you said the tab_index cha
Nirnimesh 2011/12/06 23:58:36 I see. Yes, that's correct. To that end, why do y
DaleCurtis 2011/12/07 01:30:05 I could probably get away with not doing so, but w
143 unique_url = '%s?%d' % (self._url, TestWorker._task_id.next())
144
145 # Start the test!
146 with self._automation_lock:
147 self._pyauto.AppendTab(pyauto.GURL(unique_url))
148 self._pyauto.CallJavascriptFunc(
149 'startTest', [video_url], tab_index=self._FindTab(unique_url))
150
151 # Wait until the necessary metrics have been collected. Okay to not lock
Nirnimesh 2011/12/06 22:15:05 2 spaces after .
DaleCurtis 2011/12/06 23:42:54 Done.
152 # here since pyauto.WaitUntil doesn't call into Chrome.
Nirnimesh 2011/12/06 22:15:05 but _HaveMetrics calls into chrome..
DaleCurtis 2011/12/06 23:42:54 ...and makes those calls with the lock.
153 self._ept = self._ttp = -1
154 self._pyauto.WaitUntil(
155 self._HaveMetrics, args=[unique_url], retry_sleep=2)
156
157 # Record results. TODO(dalecurtis): Support reference builds.
Nirnimesh 2011/12/06 22:15:05 move todo to next line
DaleCurtis 2011/12/06 23:42:54 Done.
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 = ['python', os.path.join(pyauto_paths.GetSourceDir(), _CNS_PATH),
Nirnimesh 2011/12/06 22:15:05 s/python/sys.executable
DaleCurtis 2011/12/06 23:42:54 Done.
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 self.Kill(self._server_pid)
195 pyauto.PyUITest.tearDown(self)
Nirnimesh 2011/12/06 22:15:05 destruct in the opposite order. ie tearDown before
DaleCurtis 2011/12/06 23:42:54 Done.
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.
Nirnimesh 2011/12/06 22:15:05 s/synchronize/guard around/ ?
DaleCurtis 2011/12/06 23:42:54 Synchronize works fine: http://en.wikipedia.org/wi
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))
Nirnimesh 2011/12/06 22:15:05 Won't having multiple threads affect the numbers y
DaleCurtis 2011/12/06 23:42:54 Not in the limited testing I've done thus far. We'
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