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

Side by Side Diff: scripts/slave/run_slavelastic.py

Issue 10035003: Split up Each Swarm Test into Two Steps (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Renaming broken test :( Created 8 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « scripts/slave/get_swarm_results.py ('k') | scripts/slave/run_slavelastic_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 # run_slavelastic.py: Runs a test based off of a slavelastic manifest file. 5 # run_slavelastic.py: Runs a test based off of a slavelastic manifest file.
6 6
7 from __future__ import with_statement 7 from __future__ import with_statement
8 import json # pylint: disable=F0401 8 import json
9 import optparse 9 import optparse
10 import os 10 import os
11 import platform 11 import platform
12 import random 12 import random
13 import socket 13 import socket
14 import sys 14 import sys
15 import time 15 import time
16 import urllib2 16 import urllib2
17 import zipfile 17 import zipfile
18 18
(...skipping 21 matching lines...) Expand all
40 } 40 }
41 41
42 # This can cause problems when 42 # This can cause problems when
43 # |current_platform| != |switches_dict['os_image']| 43 # |current_platform| != |switches_dict['os_image']|
44 # crbug.com/117442 44 # crbug.com/117442
45 current_platform = platform_mapping[sys.platform] 45 current_platform = platform_mapping[sys.platform]
46 switches_dict = { 46 switches_dict = {
47 'num_shards': switches.num_shards, 47 'num_shards': switches.num_shards,
48 'os_image': current_platform, 48 'os_image': current_platform,
49 } 49 }
50 self.name = filename 50 self.manifest_name = filename
51 51
52 self.g_shards = switches.num_shards 52 self.g_shards = switches.num_shards
53 # Random name for the output zip file 53 # Random name for the output zip file
54 self.zipfile_name = 'swarm_tempfile_%s.zip' % ''.join(random.choice( 54 self.zipfile_name = 'swarm_tempfile_%s.zip' % ''.join(random.choice(
55 'abcdefghijklmnopqrstuvwxyz0123456789') for x in range(10)) 55 'abcdefghijklmnopqrstuvwxyz0123456789') for x in range(10))
56 self.tasks = [] 56 self.tasks = []
57 self.current_platform = current_platform 57 self.current_platform = current_platform
58 self.target_platform = switches_dict['os_image'] 58 self.target_platform = switches_dict['os_image']
59 self.working_dir = switches.working_dir 59 self.working_dir = switches.working_dir
60 self.test_name = switches.test_name
60 61
61 def add_task(self, task_name, actions): 62 def add_task(self, task_name, actions):
62 """Appends a new task to the swarm manifest file.""" 63 """Appends a new task to the swarm manifest file."""
63 self.tasks.append({ 64 self.tasks.append({
64 'test_name': task_name, 65 'test_name': task_name,
65 'action': actions, 66 'action': actions,
66 }) 67 })
67 68
68 def zip(self): 69 def zip(self):
69 """Zip up all the files in self.files""" 70 """Zip up all the files in self.files"""
70 start_time = time.time() 71 start_time = time.time()
71 72
72 zip_file = zipfile.ZipFile(self.zipfile_name, 'w') 73 zip_file = zipfile.ZipFile(self.zipfile_name, 'w')
73 zip_file.write(self.name) 74 zip_file.write(self.manifest_name)
74 zip_file.write(self.run_test_path) 75 zip_file.write(self.run_test_path)
75 zip_file.close() 76 zip_file.close()
76 77
77 print 'Zipping completed, time elapsed: %f' % (time.time() - start_time) 78 print 'Zipping completed, time elapsed: %f' % (time.time() - start_time)
78 79
79 def cleanup(self): 80 def cleanup(self):
80 os.remove(self.zipfile_name) 81 os.remove(self.zipfile_name)
81 82
82 def to_json(self): 83 def to_json(self):
83 """Export the current configuration into a swarm-readable manifest file""" 84 """Export the current configuration into a swarm-readable manifest file"""
84 hostname = socket.gethostbyname(socket.gethostname()) 85 hostname = socket.gethostbyname(socket.gethostname())
85 # pylint: disable=E1103 86 # pylint: disable=E1103
86 filepath = os.path.relpath(self.zipfile_name, '../..').replace('\\', '/') 87 filepath = os.path.relpath(self.zipfile_name, '../..').replace('\\', '/')
87 88
88 url = 'http://%s/hashtable/' % hostname 89 url = 'http://%s/hashtable/' % hostname
89 self.add_task( 90 self.add_task(
90 'Run Test', 91 'Run Test',
91 ['python', self.run_test_path, '-m', self.name, '-r', url]) 92 ['python', self.run_test_path, '-m', self.manifest_name, '-r', url])
92 93
93 # Clean up 94 # Clean up
94 if self.current_platform == 'Linux' or self.current_platform == 'Mac': 95 if self.current_platform == 'Linux' or self.current_platform == 'Mac':
95 cleanup_commands = ['rm', '-rf'] 96 cleanup_commands = ['rm', '-rf']
96 elif self.current_platform == 'Windows': 97 elif self.current_platform == 'Windows':
97 cleanup_commands = ['del'] 98 cleanup_commands = ['del']
98 self.add_task('Clean Up', cleanup_commands + [self.zipfile_name]) 99 self.add_task('Clean Up', cleanup_commands + [self.zipfile_name])
99 100
100 # Call kill_processes.py if on windows 101 # Call kill_processes.py if on windows
101 if self.target_platform == 'Windows': 102 if self.target_platform == 'Windows':
102 self.add_task('Kill Processes', 103 self.add_task('Kill Processes',
103 [sys.executable, '..\\b\\build\\scripts\\slave\\kill_processes.py']) 104 [sys.executable, '..\\b\\build\\scripts\\slave\\kill_processes.py'])
104 105
105 # Construct test case 106 # Construct test case
106 test_case = { 107 test_case = {
107 'test_case_name': self.name, 108 'test_case_name': self.test_name,
108 'data': [ 109 'data': [
109 'http://%s/%s' % (hostname, filepath), 110 'http://%s/%s' % (hostname, filepath),
110 ], 111 ],
111 'tests': self.tasks, 112 'tests': self.tasks,
112 'env_vars': { 113 'env_vars': {
113 'GTEST_TOTAL_SHARDS': '%(num_instances)s', 114 'GTEST_TOTAL_SHARDS': '%(num_instances)s',
114 'GTEST_SHARD_INDEX': '%(instance_index)s', 115 'GTEST_SHARD_INDEX': '%(instance_index)s',
115 }, 116 },
116 'configurations': [ 117 'configurations': [
117 { 118 {
118 'min_instances': self.g_shards, 119 'min_instances': self.g_shards,
119 'max_instances': self.g_shards, 120 'max_instances': self.g_shards,
120 'config_name': self.target_platform, 121 'config_name': self.target_platform,
121 'dimensions': { 122 'dimensions': {
122 'os': self.target_platform, 123 'os': self.target_platform,
123 }, 124 },
124 }, 125 },
125 ], 126 ],
126 'working_dir': self.working_dir, 127 'working_dir': self.working_dir,
127 'cleanup': 'data', 128 'cleanup': 'data',
128 } 129 }
129 130
130 return json.dumps(test_case) 131 return json.dumps(test_case)
131 132
132 133
133 def _get_first_number(line):
134 for part in line.split():
135 if part.isdigit():
136 return int(part)
137
138 print 'No number in :'
139 print line
140 return 0
141
142
143 class TestSummary(object):
144 def __init__(self):
145 self.test_passed_count = 0
146 self.failed_tests = []
147 self.disabled_test_count = 0
148 self.ignored_test_count = 0
149
150 def AddSummaryData(self, buf):
151 lines = buf.splitlines()
152
153 for line in lines:
154 if '[ PASSED ]' in line:
155 self.test_passed_count += _get_first_number(line)
156 elif '[ FAILED ]' in line:
157 if ', listed below' not in line:
158 self.failed_tests.append(line)
159 elif 'DISABLED' in line:
160 self.disabled_test_count += _get_first_number(line)
161 elif 'failures' in line:
162 self.ignored_test_count += _get_first_number(line)
163
164 def Output(self):
165 output = []
166
167 output.append('[ PASSED ] %i tests.' % self.test_passed_count)
168 if self.failed_tests:
169 output.append('[ FAILED ] failed tests listed below:')
170 output.extend(self.failed_tests)
171 output.append('%i FAILED TESTS' % len(self.failed_tests))
172
173 if self.disabled_test_count:
174 output.append('%i DISABLED TESTS' % self.disabled_test_count)
175
176 if self.ignored_test_count:
177 output.append('%i tests with ignored failures (FAILS prefix)' %
178 self.ignored_test_count)
179
180 return output
181
182 def exit_code(self):
183 return int(bool(self.failed_tests))
184
185
186 # TODO(csharp) The sharing_supervisor.py also has test parsing code, they should
187 # be shared.
188 def TestRunOutput(output):
189 """Go through the given output and only return the output from the Test Run
190 Step.
191 """
192 test_run_output = []
193
194 in_step = False
195 step_name = ''
196 for line in output.splitlines():
197 if in_step:
198 if '[ OK ] ' + step_name in line:
199 break
200 else:
201 test_run_output.append(line)
202 elif '[ RUN ] ' in line and 'Run Test' in line:
203 in_step = True
204 i = len('[ RUN ] ')
205 step_name = line[i:]
206
207 return '\n'.join(test_run_output)
208
209
210 def main(): 134 def main():
211 """Packages up a Slavelastic test and send it to swarm. Receive output from 135 """Packages up a Slavelastic test and send it to swarm. Receive output from
212 all shards and print it to stdout. 136 all shards and print it to stdout.
213 137
214 Args 138 Args
215 slavelastic manifest file 139 slavelastic manifest file
216 number of shards 140 number of shards
217 ... 141 ...
218 """ 142 """
219 # Parses arguments 143 # Parses arguments
220 parser = optparse.OptionParser(usage='%prog [options] [filename]', 144 parser = optparse.OptionParser(usage='%prog [options] [filename]',
221 description=DESCRIPTION) 145 description=DESCRIPTION)
222 parser.add_option('-w', '--working_dir', default='swarm_tests', 146 parser.add_option('-w', '--working_dir', default='swarm_tests',
223 help='Desired working direction on the swarm slave side. ' 147 help='Desired working direction on the swarm slave side. '
224 'Defaults to %default.') 148 'Defaults to %default.')
225 parser.add_option('-m', '--min_shards', type='int', default=1, 149 parser.add_option('-m', '--min_shards', type='int', default=1,
226 help='Minimum number of shards to request. CURRENTLY NOT ' 150 help='Minimum number of shards to request. CURRENTLY NOT '
227 'SUPPORTED.') 151 'SUPPORTED.')
228 parser.add_option('-s', '--num_shards', type='int', default=1, 152 parser.add_option('-s', '--num_shards', type='int', default=1,
229 help='Desired number of shards to request. Must be ' 153 help='Desired number of shards to request. Must be '
230 'greater than or equal to min_shards.') 154 'greater than or equal to min_shards.')
231 parser.add_option('-o', '--os_image', 155 parser.add_option('-o', '--os_image',
232 help='Swarm OS image to request. Defaults to the ' 156 help='Swarm OS image to request. Defaults to the '
233 'current platform.') 157 'current platform.')
234 parser.add_option('-n', '--hostname', default='localhost', 158 parser.add_option('-u', '--url', default='http://localhost:8080',
235 help='Specify the hostname of the Swarm server. ' 159 help='Specify the url of the Swarm server. '
236 'Defaults to %default') 160 'Defaults to %default')
237 parser.add_option('-p', '--port', type='int', default=8080, 161 parser.add_option('-t', '--test_name',
238 help='Specify the port of the Swarm server. ' 162 help='Specify the name to give the swarm test request. '
239 'Defaults to %default') 163 'Defaults to the given filename')
240 parser.add_option('-v', '--verbose', action='store_true', 164 parser.add_option('-v', '--verbose', action='store_true',
241 help='Print verbose logging') 165 help='Print verbose logging')
242 (options, args) = parser.parse_args() 166 (options, args) = parser.parse_args()
243 if not args: 167 if not args:
244 parser.error('Must specify one filename.') 168 parser.error('Must specify one filename.')
245 elif len(args) > 1: 169 elif len(args) > 1:
246 parser.error('Must specify only one filename.') 170 parser.error('Must specify only one filename.')
247 filename = args[0] 171 filename = args[0]
248 if not options.os_image: 172 if not options.os_image:
249 options.os_image = '%s %d' % (platform.uname()[0], 32) 173 options.os_image = '%s %d' % (platform.uname()[0], 32)
174 if not options.test_name:
175 options.test_name = filename
250 176
251 # Parses manifest file 177 # Parses manifest file
252 print "Parsing file %s..." % filename 178 print "Parsing file %s..." % filename
253 manifest = Manifest(filename, options) 179 manifest = Manifest(filename, options)
254 180
255 # Zip up relevent files 181 # Zip up relevent files
256 print "Zipping up files..." 182 print "Zipping up files..."
257 manifest.zip() 183 manifest.zip()
258 184
259 # Send test requests off to swarm. 185 # Send test requests off to swarm.
260 print 'Sending test requests to swarm' 186 print 'Sending test requests to swarm'
261 base_url = 'http://%s:%d' % (options.hostname, options.port) 187 test_url = options.url.rstrip('/') + '/test'
262 test_url = base_url + '/test'
263 manifest_text = manifest.to_json() 188 manifest_text = manifest.to_json()
264 result = urllib2.urlopen(test_url, manifest_text).read() 189 result = urllib2.urlopen(test_url, manifest_text).read()
265 190
266 # Check that we can read the output as a JSON string 191 # Check that we can read the output as a JSON string
267 try: 192 try:
268 test_keys = json.loads(result) 193 json.loads(result)
269 except (ValueError, TypeError), e: 194 except (ValueError, TypeError), e:
270 print 'Request failed:' 195 print 'Request failed:'
271 print result 196 print result
197 print e
272 return 1 198 return 1
273 199
274 running_test_keys = test_keys['test_keys'] 200 return 0
275
276 # TODO(csharp) Get hostnames from key through swarm
277 hostnames = ['localhost' for i in range(options.num_shards)]
278
279 # TODO(csharp) Get exit codes from key through swarm
280 exit_codes = [0 for i in range(options.num_shards)]
281
282 # Listen to output_destination
283 summary_total = TestSummary()
284 for index in range(options.num_shards):
285 print
286 print '===================================================================='
287 print 'Begin output from shard index %d (%s)' % (index, hostnames[i])
288 print '===================================================================='
289 print
290 while True:
291 try:
292 key_url = '%s/get_result?r=%s' % (base_url,
293 running_test_keys[index]['test_key'])
294 output = urllib2.urlopen(key_url).read()
295
296 if output:
297 cleaned_output = TestRunOutput(output)
298 summary_index = cleaned_output.rfind('[ PASSED ]')
299 summary_total.AddSummaryData(cleaned_output[summary_index:])
300 sys.stdout.write(cleaned_output[:summary_index - 1])
301 break
302 else:
303 # Test is not yet done, wait a bit before checking again.
304 time.sleep(0.5)
305 except urllib2.HTTPError, e:
306 print 'Calling %s threw %s' % (key_url, e)
307 print
308 print '===================================================================='
309 print 'End output from shard index %d (%s). Return %d' % (index,
310 hostnames[i], exit_codes[i])
311 print '===================================================================='
312 print
313 manifest.cleanup() # Delete temp zip file
314
315 print '\n'.join(summary_total.Output())
316 print
317
318 if options.verbose:
319 print 'All tests completed:'
320 for i in range(options.num_shards):
321 print 'Shard index %d (%s): Exit code: %d' % (i,
322 hostnames[i], exit_codes[i])
323
324 # TODO(csharp) replace with max exit code once exit_codes gets real values
325 return summary_total.exit_code()
326 201
327 if __name__ == '__main__': 202 if __name__ == '__main__':
328 sys.exit(main()) 203 sys.exit(main())
OLDNEW
« no previous file with comments | « scripts/slave/get_swarm_results.py ('k') | scripts/slave/run_slavelastic_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698