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

Side by Side Diff: bin/cros_au_test_harness.py

Issue 5962019: Revert "Major cleanup of cros_au_test_harness code." (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/crosutils.git@master
Patch Set: Created 9 years, 11 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 | « no previous file | no next file » | 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/python 1 #!/usr/bin/python
2 2
3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 import optparse 7 import optparse
8 import os 8 import os
9 import re 9 import re
10 import sys 10 import sys
11 import thread 11 import thread
12 import time 12 import time
13 import unittest 13 import unittest
14 import urllib 14 import urllib
15 15
16 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) 16 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib'))
17 from cros_build_lib import Die 17 from cros_build_lib import Die
18 from cros_build_lib import Info 18 from cros_build_lib import Info
19 from cros_build_lib import ReinterpretPathForChroot 19 from cros_build_lib import ReinterpretPathForChroot
20 from cros_build_lib import RunCommand 20 from cros_build_lib import RunCommand
21 from cros_build_lib import RunCommandCaptureOutput 21 from cros_build_lib import RunCommandCaptureOutput
22 from cros_build_lib import Warning 22 from cros_build_lib import Warning
23 23
24 import cros_test_proxy 24 import cros_test_proxy
25 25
26 # VM Constants.
27 _FULL_VDISK_SIZE = 6072
28 _FULL_STATEFULFS_SIZE = 3074
29 _KVM_PID_FILE = '/tmp/harness_pid'
30 _VERIFY_SUITE = 'suite_Smoke'
31
32 # Globals to communicate options to unit tests.
33 global base_image_path
34 global board
35 global remote
36 global target_image_path
37 global vm_graphics_flag
26 38
27 class UpdateException(Exception): 39 class UpdateException(Exception):
28 """Exception thrown when _UpdateImage or _UpdateUsingPayload fail""" 40 """Exception thrown when UpdateImage or UpdateUsingPayload fail"""
29 def __init__(self, code, stdout): 41 def __init__(self, code, stdout):
30 self.code = code 42 self.code = code
31 self.stdout = stdout 43 self.stdout = stdout
32 44
33
34 class AUTest(object): 45 class AUTest(object):
35 """Abstract interface that defines an Auto Update test.""" 46 """Abstract interface that defines an Auto Update test."""
47 source_image = ''
48 use_delta_updates = False
36 verbose = False 49 verbose = False
37 50
38 def setUp(self): 51 def setUp(self):
39 unittest.TestCase.setUp(self) 52 unittest.TestCase.setUp(self)
40 # Set these up as they are used often. 53 # Set these up as they are used often.
41 self.crosutils = os.path.join(os.path.dirname(__file__), '..') 54 self.crosutils = os.path.join(os.path.dirname(__file__), '..')
42 self.crosutilsbin = os.path.join(os.path.dirname(__file__)) 55 self.crosutilsbin = os.path.join(os.path.dirname(__file__))
43 self.download_folder = os.path.join(self.crosutils, 'latest_download') 56 self.download_folder = os.path.join(self.crosutils, 'latest_download')
44 if not os.path.exists(self.download_folder): 57 if not os.path.exists(self.download_folder):
45 os.makedirs(self.download_folder) 58 os.makedirs(self.download_folder)
46 59
47 # -------- Helper functions ---------
48
49 def GetStatefulChangeFlag(self, stateful_change): 60 def GetStatefulChangeFlag(self, stateful_change):
50 """Returns the flag to pass to image_to_vm for the stateful change.""" 61 """Returns the flag to pass to image_to_vm for the stateful change."""
51 stateful_change_flag = '' 62 stateful_change_flag = ''
52 if stateful_change: 63 if stateful_change:
53 stateful_change_flag = '--stateful_update_flag=%s' % stateful_change 64 stateful_change_flag = '--stateful_update_flag=%s' % stateful_change
54 65
55 return stateful_change_flag 66 return stateful_change_flag
56 67
57 def _ParseGenerateTestReportOutput(self, output): 68 def ParseGenerateTestReportOutput(self, output):
58 """Returns the percentage of tests that passed based on output.""" 69 """Returns the percentage of tests that passed based on output."""
59 percent_passed = 0 70 percent_passed = 0
60 lines = output.split('\n') 71 lines = output.split('\n')
61 72
62 for line in lines: 73 for line in lines:
63 if line.startswith("Total PASS:"): 74 if line.startswith("Total PASS:"):
64 # FORMAT: ^TOTAL PASS: num_passed/num_total (percent%)$ 75 # FORMAT: ^TOTAL PASS: num_passed/num_total (percent%)$
65 percent_passed = line.split()[3].strip('()%') 76 percent_passed = line.split()[3].strip('()%')
66 Info('Percent of tests passed %s' % percent_passed) 77 Info('Percent of tests passed %s' % percent_passed)
67 break 78 break
68 79
69 return int(percent_passed) 80 return int(percent_passed)
70 81
71 def AssertEnoughTestsPassed(self, unittest, output, percent_required_to_pass): 82 # TODO(sosa) - Remove try and convert function to DeltaUpdateImage().
72 """Helper function that asserts a sufficient number of tests passed. 83 def TryDeltaAndFallbackToFull(self, src_image, image, stateful_change='old'):
84 """Tries the delta update first if set and falls back to full update."""
85 if self.use_delta_updates:
86 try:
87 self.source_image = src_image
88 self._UpdateImageReportError(image, stateful_change)
89 except:
90 Warning('Delta update failed, disabling delta updates and retrying.')
91 self.use_delta_updates = False
92 self.source_image = ''
93 self._UpdateImageReportError(image, stateful_change)
94 else:
95 self._UpdateImageReportError(image, stateful_change)
73 96
74 Args: 97 def _UpdateImageReportError(self, image_path, stateful_change='old',
75 unittest: Handle to the unittest. 98 proxy_port=None):
76 output: stdout from a test run. 99 """Calls UpdateImage and reports any error to the console.
77 percent_required_to_pass: percentage required to pass. This should be
78 fall between 0-100.
79 Returns:
80 percent that passed.
81 """
82 Info('Output from VerifyImage():')
83 print >> sys.stderr, output
84 sys.stderr.flush()
85 percent_passed = self._ParseGenerateTestReportOutput(output)
86 Info('Percent passed: %d vs. Percent required: %d' % (
87 percent_passed, percent_required_to_pass))
88 unittest.assertTrue(percent_passed >= percent_required_to_pass)
89 return percent_passed
90 100
91 def PerformUpdate(self, image_path, src_image_path='', stateful_change='old', 101 Still throws the exception.
92 proxy_port=None):
93 """Performs an update using _UpdateImage and reports any error.
94
95 Subclasses should not override this method but override _UpdateImage
96 instead.
97
98 Args:
99 image_path: Path to the image to update with. This image must be a test
100 image.
101 src_image_path: Optional. If set, perform a delta update using the
102 image specified by the path as the source image.
103 stateful_change: How to modify the stateful partition. Values are:
104 'old': Don't modify stateful partition. Just update normally.
105 'clean': Uses clobber-state to wipe the stateful partition with the
106 exception of code needed for ssh.
107 proxy_port: Port to have the client connect to. For use with
108 CrosTestProxy.
109 Raises an UpdateException if _UpdateImage returns an error.
110 """ 102 """
111 try: 103 try:
112 if not self.use_delta_updates: 104 self.UpdateImage(image_path, stateful_change, proxy_port)
113 src_image_path = ''
114
115 self._UpdateImage(image_path, src_image_path, stateful_change, proxy_port)
116 except UpdateException as err: 105 except UpdateException as err:
117 # If the update fails, print it out 106 # If the update fails, print it out
118 Warning(err.stdout) 107 Warning(err.stdout)
119 raise 108 raise
120 109
121 def AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg): 110 def _AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg):
122 """Attempt a payload update, expect it to fail with expected log""" 111 """Attempt a payload update, expect it to fail with expected log"""
123 try: 112 try:
124 self._UpdateUsingPayload(payload) 113 self.UpdateUsingPayload(payload)
125 except UpdateException as err: 114 except UpdateException as err:
126 # Will raise ValueError if expected is not found. 115 # Will raise ValueError if expected is not found.
127 if re.search(re.escape(expected_msg), err.stdout, re.MULTILINE): 116 if re.search(re.escape(expected_msg), err.stdout, re.MULTILINE):
128 return 117 return
129 118
130 Warning("Didn't find '%s' in:" % expected_msg) 119 Warning("Didn't find '%s' in:" % expected_msg)
131 Warning(err.stdout) 120 Warning(err.stdout)
132 self.fail('We managed to update when failure was expected') 121 self.fail('We managed to update when failure was expected')
133 122
134 def AttemptUpdateWithFilter(self, filter): 123 def _AttemptUpdateWithFilter(self, filter):
135 """Update through a proxy, with a specified filter, and expect success.""" 124 """Update through a proxy, with a specified filter, and expect success."""
136 125
137 self.PrepareBase(self.target_image_path) 126 self.PrepareBase(target_image_path)
138 127
139 # The devserver runs at port 8080 by default. We assume that here, and 128 # The devserver runs at port 8080 by default. We assume that here, and
140 # start our proxy at 8081. We then tell our update tools to have the 129 # start our proxy at 8081. We then tell our update tools to have the
141 # client connect to 8081 instead of 8080. 130 # client connect to 8081 instead of 8080.
142 proxy_port = 8081 131 proxy_port = 8081
143 proxy = cros_test_proxy.CrosTestProxy(port_in=proxy_port, 132 proxy = cros_test_proxy.CrosTestProxy(port_in=proxy_port,
144 address_out='127.0.0.1', 133 address_out='127.0.0.1',
145 port_out=8080, 134 port_out=8080,
146 filter=filter) 135 filter=filter)
147 proxy.serve_forever_in_thread() 136 proxy.serve_forever_in_thread()
148 137
149 # This update is expected to fail... 138 # This update is expected to fail...
150 try: 139 try:
151 self.PerformUpdate(self.target_image_path, proxy_port=proxy_port) 140 self._UpdateImageReportError(target_image_path, proxy_port=proxy_port)
152 finally: 141 finally:
153 proxy.shutdown() 142 proxy.shutdown()
154 143
155 # -------- Functions that subclasses should override ---------
156
157 @classmethod
158 def ProcessOptions(cls, parser, options):
159 """Processes options.
160
161 Static method that should be called from main. Subclasses should also
162 call their parent method if they override it.
163 """
164 cls.verbose = options.verbose
165 cls.base_image_path = options.base_image
166 cls.target_image_path = options.target_image
167 cls.use_delta_updates = options.delta
168 if options.quick_test:
169 cls.verify_suite = 'build_RootFilesystemSize'
170 else:
171 cls.verify_suite = 'suite_Smoke'
172
173 # Sanity checks.
174 if not cls.base_image_path:
175 parser.error('Need path to base image for vm.')
176 elif not os.path.exists(cls.base_image_path):
177 Die('%s does not exist' % cls.base_image_path)
178
179 if not cls.target_image_path:
180 parser.error('Need path to target image to update with.')
181 elif not os.path.exists(cls.target_image_path):
182 Die('%s does not exist' % cls.target_image_path)
183
184 def PrepareBase(self, image_path): 144 def PrepareBase(self, image_path):
185 """Prepares target with base_image_path.""" 145 """Prepares target with base_image_path."""
186 pass 146 pass
187 147
188 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', 148 def UpdateImage(self, image_path, stateful_change='old', proxy_port=None):
189 proxy_port=None): 149 """Updates target with the image given by the image_path.
190 """Implementation of an actual update.
191 150
192 See PerformUpdate for description of args. Subclasses must override this 151 Args:
193 method with the correct update procedure for the class. 152 image_path: Path to the image to update with. This image must be a test
153 image.
154 stateful_change: How to modify the stateful partition. Values are:
155 'old': Don't modify stateful partition. Just update normally.
156 'clean': Uses clobber-state to wipe the stateful partition with the
157 exception of code needed for ssh.
158 proxy_port: Port to have the client connect to. For use with
159 CrosTestProxy.
194 """ 160 """
195 pass 161 pass
196 162
197 def _UpdateUsingPayload(self, update_path, stateful_change='old', 163 def UpdateUsingPayload(self,
164 update_path,
165 stateful_change='old',
198 proxy_port=None): 166 proxy_port=None):
199 """Updates target with the pre-generated update stored in update_path. 167 """Updates target with the pre-generated update stored in update_path
200
201 Subclasses must override this method with the correct update procedure for
202 the class.
203 168
204 Args: 169 Args:
205 update_path: Path to the image to update with. This directory should 170 update_path: Path to the image to update with. This directory should
206 contain both update.gz, and stateful.image.gz 171 contain both update.gz, and stateful.image.gz
207 proxy_port: Port to have the client connect to. For use with 172 proxy_port: Port to have the client connect to. For use with
208 CrosTestProxy. 173 CrosTestProxy.
209 """ 174 """
210 pass 175 pass
211 176
212 def VerifyImage(self, percent_required_to_pass): 177 def VerifyImage(self, percent_required_to_pass):
213 """Verifies the image with tests. 178 """Verifies the image with tests.
214 179
215 Verifies that the test images passes the percent required. Subclasses must 180 Verifies that the test images passes the percent required.
216 override this method with the correct update procedure for the class.
217 181
218 Args: 182 Args:
219 percent_required_to_pass: percentage required to pass. This should be 183 percent_required_to_pass: percentage required to pass. This should be
220 fall between 0-100. 184 fall between 0-100.
221 185
222 Returns: 186 Returns:
223 Returns the percent that passed. 187 Returns the percent that passed.
224 """ 188 """
225 pass 189 pass
226 190
227 # -------- Tests --------- 191 def CommonVerifyImage(self, unittest, output, percent_required_to_pass):
192 """Helper function for VerifyImage that returns percent of tests passed.
193
194 Takes output from a test suite, verifies the number of tests passed is
195 sufficient and outputs info.
196
197 Args:
198 unittest: Handle to the unittest.
199 output: stdout from a test run.
200 percent_required_to_pass: percentage required to pass. This should be
201 fall between 0-100.
202 Returns:
203 percent that passed.
204 """
205 Info('Output from VerifyImage():')
206 print >> sys.stderr, output
207 sys.stderr.flush()
208 percent_passed = self.ParseGenerateTestReportOutput(output)
209 Info('Percent passed: %d vs. Percent required: %d' % (
210 percent_passed, percent_required_to_pass))
211 unittest.assertTrue(percent_passed >=
212 percent_required_to_pass)
213 return percent_passed
228 214
229 def testFullUpdateKeepStateful(self): 215 def testFullUpdateKeepStateful(self):
230 """Tests if we can update normally. 216 """Tests if we can update normally.
231 217
232 This test checks that we can update by updating the stateful partition 218 This test checks that we can update by updating the stateful partition
233 rather than wiping it. 219 rather than wiping it.
234 """ 220 """
235 # Just make sure some tests pass on original image. Some old images 221 # Just make sure some tests pass on original image. Some old images
236 # don't pass many tests. 222 # don't pass many tests.
237 self.PrepareBase(self.base_image_path) 223 self.PrepareBase(base_image_path)
238 # TODO(sosa): move to 100% once we start testing using the autotest paired 224 # TODO(sosa): move to 100% once we start testing using the autotest paired
239 # with the dev channel. 225 # with the dev channel.
240 percent_passed = self.VerifyImage(10) 226 percent_passed = self.VerifyImage(10)
241 227
242 # Update to - all tests should pass on new image. 228 # Update to - all tests should pass on new image.
243 Info('Updating from base image on vm to target image.') 229 Info('Updating from base image on vm to target image.')
244 self.PerformUpdate(self.base_image_path, self.target_image_path) 230 self.TryDeltaAndFallbackToFull(base_image_path, target_image_path)
245 self.VerifyImage(100) 231 self.VerifyImage(100)
246 232
247 # Update from - same percentage should pass that originally passed. 233 # Update from - same percentage should pass that originally passed.
248 Info('Updating from updated image on vm back to base image.') 234 Info('Updating from updated image on vm back to base image.')
249 self.PerformUpdate(self.target_image_path, self.base_image_path) 235 self.TryDeltaAndFallbackToFull(target_image_path, base_image_path)
250 self.VerifyImage(percent_passed) 236 self.VerifyImage(percent_passed)
251 237
252 def testFullUpdateWipeStateful(self): 238 def testFullUpdateWipeStateful(self):
253 """Tests if we can update after cleaning the stateful partition. 239 """Tests if we can update after cleaning the stateful partition.
254 240
255 This test checks that we can update successfully after wiping the 241 This test checks that we can update successfully after wiping the
256 stateful partition. 242 stateful partition.
257 """ 243 """
258 # Just make sure some tests pass on original image. Some old images 244 # Just make sure some tests pass on original image. Some old images
259 # don't pass many tests. 245 # don't pass many tests.
260 self.PrepareBase(self.base_image_path) 246 self.PrepareBase(base_image_path)
261 # TODO(sosa): move to 100% once we start testing using the autotest paired 247 # TODO(sosa): move to 100% once we start testing using the autotest paired
262 # with the dev channel. 248 # with the dev channel.
263 percent_passed = self.VerifyImage(10) 249 percent_passed = self.VerifyImage(10)
264 250
265 # Update to - all tests should pass on new image. 251 # Update to - all tests should pass on new image.
266 Info('Updating from base image on vm to target image and wiping stateful.') 252 Info('Updating from base image on vm to target image and wiping stateful.')
267 self.PerformUpdate(self.base_image_path, self.target_image_path, 'clean') 253 self.TryDeltaAndFallbackToFull(base_image_path, target_image_path, 'clean')
268 self.VerifyImage(100) 254 self.VerifyImage(100)
269 255
270 # Update from - same percentage should pass that originally passed. 256 # Update from - same percentage should pass that originally passed.
271 Info('Updating from updated image back to base image and wiping stateful.') 257 Info('Updating from updated image back to base image and wiping stateful.')
272 self.PerformUpdate(self.target_image_path, self.base_image_path, 'clean') 258 self.TryDeltaAndFallbackToFull(target_image_path, base_image_path, 'clean')
273 self.VerifyImage(percent_passed) 259 self.VerifyImage(percent_passed)
274 260
275 def testPartialUpdate(self): 261 def testPartialUpdate(self):
276 """Tests what happens if we attempt to update with a truncated payload.""" 262 """Tests what happens if we attempt to update with a truncated payload."""
277 # Preload with the version we are trying to test. 263 # Preload with the version we are trying to test.
278 self.PrepareBase(self.target_image_path) 264 self.PrepareBase(target_image_path)
279 265
280 # Image can be updated at: 266 # Image can be updated at:
281 # ~chrome-eng/chromeos/localmirror/autest-images 267 # ~chrome-eng/chromeos/localmirror/autest-images
282 url = 'http://gsdview.appspot.com/chromeos-localmirror/' \ 268 url = 'http://gsdview.appspot.com/chromeos-localmirror/' \
283 'autest-images/truncated_image.gz' 269 'autest-images/truncated_image.gz'
284 payload = os.path.join(self.download_folder, 'truncated_image.gz') 270 payload = os.path.join(self.download_folder, 'truncated_image.gz')
285 271
286 # Read from the URL and write to the local file 272 # Read from the URL and write to the local file
287 urllib.urlretrieve(url, payload) 273 urllib.urlretrieve(url, payload)
288 274
289 expected_msg = 'download_hash_data == update_check_response_hash failed' 275 expected_msg = 'download_hash_data == update_check_response_hash failed'
290 self.AttemptUpdateWithPayloadExpectedFailure(payload, expected_msg) 276 self._AttemptUpdateWithPayloadExpectedFailure(payload, expected_msg)
291 277
292 def testCorruptedUpdate(self): 278 def testCorruptedUpdate(self):
293 """Tests what happens if we attempt to update with a corrupted payload.""" 279 """Tests what happens if we attempt to update with a corrupted payload."""
294 # Preload with the version we are trying to test. 280 # Preload with the version we are trying to test.
295 self.PrepareBase(self.target_image_path) 281 self.PrepareBase(target_image_path)
296 282
297 # Image can be updated at: 283 # Image can be updated at:
298 # ~chrome-eng/chromeos/localmirror/autest-images 284 # ~chrome-eng/chromeos/localmirror/autest-images
299 url = 'http://gsdview.appspot.com/chromeos-localmirror/' \ 285 url = 'http://gsdview.appspot.com/chromeos-localmirror/' \
300 'autest-images/corrupted_image.gz' 286 'autest-images/corrupted_image.gz'
301 payload = os.path.join(self.download_folder, 'corrupted.gz') 287 payload = os.path.join(self.download_folder, 'corrupted.gz')
302 288
303 # Read from the URL and write to the local file 289 # Read from the URL and write to the local file
304 urllib.urlretrieve(url, payload) 290 urllib.urlretrieve(url, payload)
305 291
306 # This update is expected to fail... 292 # This update is expected to fail...
307 expected_msg = 'zlib inflate() error:-3' 293 expected_msg = 'zlib inflate() error:-3'
308 self.AttemptUpdateWithPayloadExpectedFailure(payload, expected_msg) 294 self._AttemptUpdateWithPayloadExpectedFailure(payload, expected_msg)
309 295
310 def testInterruptedUpdate(self): 296 def testInterruptedUpdate(self):
311 """Tests what happens if we interrupt payload delivery 3 times.""" 297 """Tests what happens if we interrupt payload delivery 3 times."""
312 298
313 class InterruptionFilter(cros_test_proxy.Filter): 299 class InterruptionFilter(cros_test_proxy.Filter):
314 """This filter causes the proxy to interrupt the download 3 times 300 """This filter causes the proxy to interrupt the download 3 times
315 301
316 It does this by closing the first three connections to transfer 302 It does this by closing the first three connections to transfer
317 2M total in the outbound connection after they transfer the 303 2M total in the outbound connection after they transfer the
318 2M. 304 2M.
(...skipping 13 matching lines...) Expand all
332 outbound will be closed. 318 outbound will be closed.
333 """ 319 """
334 if self.close_count < 3: 320 if self.close_count < 3:
335 if self.data_size > (2 * 1024 * 1024): 321 if self.data_size > (2 * 1024 * 1024):
336 self.close_count += 1 322 self.close_count += 1
337 return None 323 return None
338 324
339 self.data_size += len(data) 325 self.data_size += len(data)
340 return data 326 return data
341 327
342 self.AttemptUpdateWithFilter(InterruptionFilter()) 328 self._AttemptUpdateWithFilter(InterruptionFilter())
343 329
344 def testDelayedUpdate(self): 330 def testDelayedUpdate(self):
345 """Tests what happens if some data is delayed during update delivery""" 331 """Tests what happens if some data is delayed during update delivery"""
346 332
347 class DelayedFilter(cros_test_proxy.Filter): 333 class DelayedFilter(cros_test_proxy.Filter):
348 """Causes intermittent delays in data transmission. 334 """Causes intermittent delays in data transmission.
349 335
350 It does this by inserting 3 20 second delays when transmitting 336 It does this by inserting 3 20 second delays when transmitting
351 data after 2M has been sent. 337 data after 2M has been sent.
352 """ 338 """
353 def setup(self): 339 def setup(self):
354 """Called once at the start of each connection.""" 340 """Called once at the start of each connection."""
355 self.data_size = 0 341 self.data_size = 0
356 self.delay_count = 0 342 self.delay_count = 0
357 343
358 def OutBound(self, data): 344 def OutBound(self, data):
359 """Called once per packet for outgoing data. 345 """Called once per packet for outgoing data.
360 346
361 The first three packets after we reach 2M transferred 347 The first three packets after we reach 2M transferred
362 are delayed by 20 seconds. 348 are delayed by 20 seconds.
363 """ 349 """
364 if self.delay_count < 3: 350 if self.delay_count < 3:
365 if self.data_size > (2 * 1024 * 1024): 351 if self.data_size > (2 * 1024 * 1024):
366 self.delay_count += 1 352 self.delay_count += 1
367 time.sleep(20) 353 time.sleep(20)
368 354
369 self.data_size += len(data) 355 self.data_size += len(data)
370 return data 356 return data
371 357
372 self.AttemptUpdateWithFilter(DelayedFilter()) 358 self._AttemptUpdateWithFilter(DelayedFilter())
373 359
374 def SimpleTest(self): 360 def SimpleTest(self):
375 """A simple update that updates the target image to itself. 361 """A simple update that updates the target image to itself.
376 362
377 We explicitly don't use test prefix so that isn't run by default. Can be 363 We explicitly don't use test prefix so that isn't run by default. Can be
378 run using test_prefix option. 364 run using test_prefix option.
379 """ 365 """
380 self.PrepareBase(self.target_image_path) 366 self.PrepareBase(target_image_path)
381 self._UpdateImage(self.target_image_path) 367 self.UpdateImage(target_image_path)
382 self.VerifyImage(100) 368 self.VerifyImage(100)
383 369
384 370
385 class RealAUTest(unittest.TestCase, AUTest): 371 class RealAUTest(unittest.TestCase, AUTest):
386 """Test harness for updating real images.""" 372 """Test harness for updating real images."""
387 373
388 def setUp(self): 374 def setUp(self):
389 AUTest.setUp(self) 375 AUTest.setUp(self)
390 376
391 @classmethod
392 def ProcessOptions(cls, parser, options):
393 """Processes non-vm-specific options."""
394 AUTest.ProcessOptions(parser, options)
395 cls.remote = options.remote
396
397 if not cls.remote:
398 parser.error('We require a remote address for real tests.')
399
400 def PrepareBase(self, image_path): 377 def PrepareBase(self, image_path):
401 """Auto-update to base image to prepare for test.""" 378 """Auto-update to base image to prepare for test."""
402 self.PerformUpdate(image_path) 379 self._UpdateImageReportError(image_path)
403 380
404 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', 381 def UpdateImage(self, image_path, stateful_change='old', proxy_port=None):
405 proxy_port=None):
406 """Updates a remote image using image_to_live.sh.""" 382 """Updates a remote image using image_to_live.sh."""
407 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) 383 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
408 cmd = ['%s/image_to_live.sh' % self.crosutils, 384 cmd = ['%s/image_to_live.sh' % self.crosutils,
409 '--image=%s' % image_path, 385 '--image=%s' % image_path,
410 '--remote=%s' % self.remote, 386 '--remote=%s' % remote,
411 stateful_change_flag, 387 stateful_change_flag,
412 '--verify', 388 '--verify',
413 '--src_image=%s' % src_image_path 389 '--src_image=%s' % self.source_image
414 ] 390 ]
415 391
416 if proxy_port: 392 if proxy_port:
417 cmd.append('--proxy_port=%s' % proxy_port) 393 cmd.append('--proxy_port=%s' % proxy_port)
418 394
419 if self.verbose: 395 if self.verbose:
420 try: 396 try:
421 RunCommand(cmd) 397 RunCommand(cmd)
422 except Exception, e: 398 except Exception, e:
423 raise UpdateException(1, e.message) 399 raise UpdateException(1, e.message)
424 else: 400 else:
425 (code, stdout, stderr) = RunCommandCaptureOutput(cmd) 401 (code, stdout, stderr) = RunCommandCaptureOutput(cmd)
426 if code != 0: 402 if code != 0:
427 raise UpdateException(code, stdout) 403 raise UpdateException(code, stdout)
428 404
429 def _UpdateUsingPayload(self, update_path, stateful_change='old', 405 def UpdateUsingPayload(self,
406 update_path,
407 stateful_change='old',
430 proxy_port=None): 408 proxy_port=None):
431 """Updates a remote image using image_to_live.sh.""" 409 """Updates a remote image using image_to_live.sh."""
432 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) 410 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
433 cmd = ['%s/image_to_live.sh' % self.crosutils, 411 cmd = ['%s/image_to_live.sh' % self.crosutils,
434 '--payload=%s' % update_path, 412 '--payload=%s' % update_path,
435 '--remote=%s' % self.remote, 413 '--remote=%s' % remote,
436 stateful_change_flag, 414 stateful_change_flag,
437 '--verify', 415 '--verify',
438 ] 416 ]
439 417
440 if proxy_port: 418 if proxy_port:
441 cmd.append('--proxy_port=%s' % proxy_port) 419 cmd.append('--proxy_port=%s' % proxy_port)
442 420
443 if self.verbose: 421 if self.verbose:
444 try: 422 try:
445 RunCommand(cmd) 423 RunCommand(cmd)
446 except Exception, e: 424 except Exception, e:
447 raise UpdateException(1, e.message) 425 raise UpdateException(1, e.message)
448 else: 426 else:
449 (code, stdout, stderr) = RunCommandCaptureOutput(cmd) 427 (code, stdout, stderr) = RunCommandCaptureOutput(cmd)
450 if code != 0: 428 if code != 0:
451 raise UpdateException(code, stdout) 429 raise UpdateException(code, stdout)
452 430
453 def VerifyImage(self, percent_required_to_pass): 431 def VerifyImage(self, percent_required_to_pass):
454 """Verifies an image using run_remote_tests.sh with verification suite.""" 432 """Verifies an image using run_remote_tests.sh with verification suite."""
455 output = RunCommand([ 433 output = RunCommand([
456 '%s/run_remote_tests.sh' % self.crosutils, 434 '%s/run_remote_tests.sh' % self.crosutils,
457 '--remote=%s' % self.remote, 435 '--remote=%s' % remote,
458 self.verify_suite, 436 _VERIFY_SUITE,
459 ], error_ok=True, enter_chroot=False, redirect_stdout=True) 437 ], error_ok=True, enter_chroot=False, redirect_stdout=True)
460 return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass) 438 return self.CommonVerifyImage(self, output, percent_required_to_pass)
461 439
462 440
463 class VirtualAUTest(unittest.TestCase, AUTest): 441 class VirtualAUTest(unittest.TestCase, AUTest):
464 """Test harness for updating virtual machines.""" 442 """Test harness for updating virtual machines."""
465 vm_image_path = None 443 vm_image_path = None
466 444
467 # VM Constants.
468 _FULL_VDISK_SIZE = 6072
469 _FULL_STATEFULFS_SIZE = 3074
470 _KVM_PID_FILE = '/tmp/harness_pid'
471
472 def _KillExistingVM(self, pid_file): 445 def _KillExistingVM(self, pid_file):
473 if os.path.exists(pid_file): 446 if os.path.exists(pid_file):
474 Warning('Existing %s found. Deleting and killing process' % 447 Warning('Existing %s found. Deleting and killing process' %
475 pid_file) 448 pid_file)
476 RunCommand(['./cros_stop_vm', '--kvm_pid=%s' % pid_file], 449 RunCommand(['./cros_stop_vm', '--kvm_pid=%s' % pid_file],
477 cwd=self.crosutilsbin) 450 cwd=self.crosutilsbin)
478 451
479 assert not os.path.exists(pid_file) 452 assert not os.path.exists(pid_file)
480 453
481 def setUp(self): 454 def setUp(self):
482 """Unit test overriden method. Is called before every test.""" 455 """Unit test overriden method. Is called before every test."""
483 AUTest.setUp(self) 456 AUTest.setUp(self)
484 self._KillExistingVM(self._KVM_PID_FILE) 457 self._KillExistingVM(_KVM_PID_FILE)
485
486 @classmethod
487 def ProcessOptions(cls, parser, options):
488 """Processes vm-specific options."""
489 AUTest.ProcessOptions(parser, options)
490 cls.board = options.board
491
492 # Communicate flags to tests.
493 cls.graphics_flag = ''
494 if options.no_graphics: cls.graphics_flag = '--no_graphics'
495
496 if not cls.board:
497 parser.error('Need board to convert base image to vm.')
498 458
499 def PrepareBase(self, image_path): 459 def PrepareBase(self, image_path):
500 """Creates an update-able VM based on base image.""" 460 """Creates an update-able VM based on base image."""
501 self.vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname( 461 self.vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname(
502 image_path) 462 image_path)
503 463
504 Info('Creating: %s' % self.vm_image_path) 464 Info('Creating: %s' % self.vm_image_path)
505 465
506 if not os.path.exists(self.vm_image_path): 466 if not os.path.exists(self.vm_image_path):
507 Info('Qemu image %s not found, creating one.' % self.vm_image_path) 467 Info('Qemu image %s not found, creating one.' % self.vm_image_path)
508 RunCommand(['%s/image_to_vm.sh' % self.crosutils, 468 RunCommand(['%s/image_to_vm.sh' % self.crosutils,
509 '--full', 469 '--full',
510 '--from=%s' % ReinterpretPathForChroot( 470 '--from=%s' % ReinterpretPathForChroot(
511 os.path.dirname(image_path)), 471 os.path.dirname(image_path)),
512 '--vdisk_size=%s' % self._FULL_VDISK_SIZE, 472 '--vdisk_size=%s' % _FULL_VDISK_SIZE,
513 '--statefulfs_size=%s' % self._FULL_STATEFULFS_SIZE, 473 '--statefulfs_size=%s' % _FULL_STATEFULFS_SIZE,
514 '--board=%s' % self.board, 474 '--board=%s' % board,
515 '--test_image'], enter_chroot=True) 475 '--test_image'], enter_chroot=True)
516 else: 476 else:
517 Info('Using existing VM image %s' % self.vm_image_path) 477 Info('Using existing VM image %s' % self.vm_image_path)
518 478
479
519 Info('Testing for %s' % self.vm_image_path) 480 Info('Testing for %s' % self.vm_image_path)
481
520 self.assertTrue(os.path.exists(self.vm_image_path)) 482 self.assertTrue(os.path.exists(self.vm_image_path))
521 483
522 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', 484 def UpdateImage(self, image_path, stateful_change='old', proxy_port=None):
523 proxy_port=None):
524 """Updates VM image with image_path.""" 485 """Updates VM image with image_path."""
525 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) 486 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
526 if src_image_path == self.base_image_path: 487 if self.source_image == base_image_path:
527 src_image_path = self.vm_image_path 488 self.source_image = self.vm_image_path
528 489
529 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin, 490 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin,
530 '--update_image_path=%s' % image_path, 491 '--update_image_path=%s' % image_path,
531 '--vm_image_path=%s' % self.vm_image_path, 492 '--vm_image_path=%s' % self.vm_image_path,
532 '--snapshot', 493 '--snapshot',
533 self.graphics_flag, 494 vm_graphics_flag,
534 '--persist', 495 '--persist',
535 '--kvm_pid=%s' % self._KVM_PID_FILE, 496 '--kvm_pid=%s' % _KVM_PID_FILE,
536 stateful_change_flag, 497 stateful_change_flag,
537 '--src_image=%s' % src_image_path, 498 '--src_image=%s' % self.source_image,
538 ] 499 ]
539 500
540 if proxy_port: 501 if proxy_port:
541 cmd.append('--proxy_port=%s' % proxy_port) 502 cmd.append('--proxy_port=%s' % proxy_port)
542 503
543 if self.verbose: 504 if self.verbose:
544 try: 505 try:
545 RunCommand(cmd) 506 RunCommand(cmd)
546 except Exception, e: 507 except Exception, e:
547 raise UpdateException(1, e.message) 508 raise UpdateException(1, e.message)
548 else: 509 else:
549 (code, stdout, stderr) = RunCommandCaptureOutput(cmd) 510 (code, stdout, stderr) = RunCommandCaptureOutput(cmd)
550 if code != 0: 511 if code != 0:
551 raise UpdateException(code, stdout) 512 raise UpdateException(code, stdout)
552 513
553 def _UpdateUsingPayload(self, update_path, stateful_change='old', 514 def UpdateUsingPayload(self,
515 update_path,
516 stateful_change='old',
554 proxy_port=None): 517 proxy_port=None):
555 """Updates a remote image using image_to_live.sh.""" 518 """Updates a remote image using image_to_live.sh."""
556 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) 519 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
520 if self.source_image == base_image_path:
521 self.source_image = self.vm_image_path
522
557 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin, 523 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin,
558 '--payload=%s' % update_path, 524 '--payload=%s' % update_path,
559 '--vm_image_path=%s' % self.vm_image_path, 525 '--vm_image_path=%s' % self.vm_image_path,
560 '--snapshot', 526 '--snapshot',
561 self.graphics_flag, 527 vm_graphics_flag,
562 '--persist', 528 '--persist',
563 '--kvm_pid=%s' % self._KVM_PID_FILE, 529 '--kvm_pid=%s' % _KVM_PID_FILE,
564 stateful_change_flag, 530 stateful_change_flag,
531 '--src_image=%s' % self.source_image,
565 ] 532 ]
566 533
567 if proxy_port: 534 if proxy_port:
568 cmd.append('--proxy_port=%s' % proxy_port) 535 cmd.append('--proxy_port=%s' % proxy_port)
569 536
570 if self.verbose: 537 if self.verbose:
571 try: 538 try:
572 RunCommand(cmd) 539 RunCommand(cmd)
573 except Exception, e: 540 except Exception, e:
574 raise UpdateException(1, e.message) 541 raise UpdateException(1, e.message)
575 else: 542 else:
576 (code, stdout, stderr) = RunCommandCaptureOutput(cmd) 543 (code, stdout, stderr) = RunCommandCaptureOutput(cmd)
577 if code != 0: 544 if code != 0:
578 raise UpdateException(code, stdout) 545 raise UpdateException(code, stdout)
579 546
580 def VerifyImage(self, percent_required_to_pass): 547 def VerifyImage(self, percent_required_to_pass):
581 """Runs vm smoke suite to verify image.""" 548 """Runs vm smoke suite to verify image."""
582 # image_to_live already verifies lsb-release matching. This is just 549 # image_to_live already verifies lsb-release matching. This is just
583 # for additional steps. 550 # for additional steps.
584 551
585 commandWithArgs = ['%s/cros_run_vm_test' % self.crosutilsbin, 552 commandWithArgs = ['%s/cros_run_vm_test' % self.crosutilsbin,
586 '--image_path=%s' % self.vm_image_path, 553 '--image_path=%s' % self.vm_image_path,
587 '--snapshot', 554 '--snapshot',
588 '--persist', 555 '--persist',
589 '--kvm_pid=%s' % self._KVM_PID_FILE, 556 '--kvm_pid=%s' % _KVM_PID_FILE,
590 self.verify_suite, 557 _VERIFY_SUITE,
591 ] 558 ]
592 559
593 if self.graphics_flag: 560 if vm_graphics_flag:
594 commandWithArgs.append(self.graphics_flag) 561 commandWithArgs.append(vm_graphics_flag)
595 562
596 output = RunCommand(commandWithArgs, error_ok=True, enter_chroot=False, 563 output = RunCommand(commandWithArgs, error_ok=True, enter_chroot=False,
597 redirect_stdout=True) 564 redirect_stdout=True)
598 return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass) 565 return self.CommonVerifyImage(self, output, percent_required_to_pass)
599 566
600 567
601 def main(): 568 if __name__ == '__main__':
602 parser = optparse.OptionParser() 569 parser = optparse.OptionParser()
603 parser.add_option('-b', '--base_image', 570 parser.add_option('-b', '--base_image',
604 help='path to the base image.') 571 help='path to the base image.')
605 parser.add_option('-r', '--board', 572 parser.add_option('-r', '--board',
606 help='board for the images.') 573 help='board for the images.')
607 parser.add_option('--no_delta', action='store_false', default=True, 574 parser.add_option('--no_delta', action='store_false', default=True,
608 dest='delta', 575 dest='delta',
609 help='Disable using delta updates.') 576 help='Disable using delta updates.')
610 parser.add_option('--no_graphics', action='store_true', 577 parser.add_option('--no_graphics', action='store_true',
611 help='Disable graphics for the vm test.') 578 help='Disable graphics for the vm test.')
612 parser.add_option('-m', '--remote', 579 parser.add_option('-m', '--remote',
613 help='Remote address for real test.') 580 help='Remote address for real test.')
614 parser.add_option('-q', '--quick_test', default=False, action='store_true', 581 parser.add_option('-q', '--quick_test', default=False, action='store_true',
615 help='Use a basic test to verify image.') 582 help='Use a basic test to verify image.')
616 parser.add_option('-t', '--target_image', 583 parser.add_option('-t', '--target_image',
617 help='path to the target image.') 584 help='path to the target image.')
618 parser.add_option('--test_prefix', default='test', 585 parser.add_option('--test_prefix', default='test',
619 help='Only runs tests with specific prefix i.e. ' 586 help='Only runs tests with specific prefix i.e. '
620 'testFullUpdateWipeStateful.') 587 'testFullUpdateWipeStateful.')
621 parser.add_option('-p', '--type', default='vm', 588 parser.add_option('-p', '--type', default='vm',
622 help='type of test to run: [vm, real]. Default: vm.') 589 help='type of test to run: [vm, real]. Default: vm.')
623 parser.add_option('--verbose', default=False, action='store_true', 590 parser.add_option('--verbose', default=False, action='store_true',
624 help='Print out rather than capture output as much as ' 591 help='Print out rather than capture output as much as '
625 'possible.') 592 'possible.')
626 (options, leftover_args) = parser.parse_args() 593 # Set the usage to include flags.
594 parser.set_usage(parser.format_help())
595 # Parse existing sys.argv so we can pass rest to unittest.main.
596 (options, sys.argv) = parser.parse_args(sys.argv)
627 597
628 if leftover_args: 598 AUTest.verbose = options.verbose
629 parser.error('Found extra options we do not support: %s' % leftover_args) 599 base_image_path = options.base_image
600 target_image_path = options.target_image
601 board = options.board
602
603 if not base_image_path:
604 parser.error('Need path to base image for vm.')
605 elif not os.path.exists(base_image_path):
606 Die('%s does not exist' % base_image_path)
607
608 if not target_image_path:
609 parser.error('Need path to target image to update with.')
610 elif not os.path.exists(target_image_path):
611 Die('%s does not exist' % target_image_path)
612
613 if not board:
614 parser.error('Need board to convert base image to vm.')
615
616 # Communicate flags to tests.
617 vm_graphics_flag = ''
618 if options.no_graphics: vm_graphics_flag = '--no_graphics'
619 if options.quick_test: _VERIFY_SUITE = 'build_RootFilesystemSize'
620 AUTest.use_delta_updates = options.delta
621
622 # Only run the test harness we care about.
623 test_loader = unittest.TestLoader()
624 test_loader.testMethodPrefix = options.test_prefix
630 625
631 if options.type == 'vm': test_class = VirtualAUTest 626 if options.type == 'vm': test_class = VirtualAUTest
632 elif options.type == 'real': test_class = RealAUTest 627 elif options.type == 'real': test_class = RealAUTest
633 else: parser.error('Could not parse harness type %s.' % options.type) 628 else: parser.error('Could not parse harness type %s.' % options.type)
634 629
635 test_class.ProcessOptions(parser, options) 630 remote = options.remote
636 631
637 test_loader = unittest.TestLoader()
638 test_loader.testMethodPrefix = options.test_prefix
639 test_suite = test_loader.loadTestsFromTestCase(test_class) 632 test_suite = test_loader.loadTestsFromTestCase(test_class)
640 test_result = unittest.TextTestRunner(verbosity=2).run(test_suite) 633 test_result = unittest.TextTestRunner(verbosity=2).run(test_suite)
641 634
642 if not test_result.wasSuccessful(): 635 if not test_result.wasSuccessful():
643 Die('Test harness was not successful') 636 Die('Test harness was not successful')
644
645
646 if __name__ == '__main__':
647 main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698