OLD | NEW |
---|---|
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 | 2 |
3 # Copyright (c) 2010 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 shutil | |
9 import sys | 10 import sys |
10 import unittest | 11 import unittest |
12 import urllib | |
11 | 13 |
12 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) | 14 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) |
13 from cros_build_lib import Die | 15 from cros_build_lib import Die |
14 from cros_build_lib import Info | 16 from cros_build_lib import Info |
15 from cros_build_lib import ReinterpretPathForChroot | 17 from cros_build_lib import ReinterpretPathForChroot |
16 from cros_build_lib import RunCommand | 18 from cros_build_lib import RunCommand |
19 from cros_build_lib import RunCommandCaptureOutput | |
17 from cros_build_lib import Warning | 20 from cros_build_lib import Warning |
18 | 21 |
19 # VM Constants. | 22 # VM Constants. |
20 _FULL_VDISK_SIZE = 6072 | 23 _FULL_VDISK_SIZE = 6072 |
21 _FULL_STATEFULFS_SIZE = 3074 | 24 _FULL_STATEFULFS_SIZE = 3074 |
22 _KVM_PID_FILE = '/tmp/harness_pid' | 25 _KVM_PID_FILE = '/tmp/harness_pid' |
23 _VERIFY_SUITE = 'suite_Smoke' | 26 _VERIFY_SUITE = 'suite_Smoke' |
24 | 27 |
25 # Globals to communicate options to unit tests. | 28 # Globals to communicate options to unit tests. |
26 global base_image_path | 29 global base_image_path |
27 global board | 30 global board |
28 global remote | 31 global remote |
29 global target_image_path | 32 global target_image_path |
30 global vm_graphics_flag | 33 global vm_graphics_flag |
31 | 34 |
35 class UpdateException(Exception): | |
36 """Exception thrown when UpdateImage or UpdatePayload fail""" | |
37 def __init__(self, code, stdout): | |
38 self.code = code | |
39 self.stdout = stdout | |
32 | 40 |
33 class AUTest(object): | 41 class AUTest(object): |
34 """Abstract interface that defines an Auto Update test.""" | 42 """Abstract interface that defines an Auto Update test.""" |
35 source_image = '' | 43 source_image = '' |
36 use_delta_updates = False | 44 use_delta_updates = False |
37 | 45 |
38 def setUp(self): | 46 def setUp(self): |
39 unittest.TestCase.setUp(self) | 47 unittest.TestCase.setUp(self) |
40 # Set these up as they are used often. | 48 # Set these up as they are used often. |
41 self.crosutils = os.path.join(os.path.dirname(__file__), '..') | 49 self.crosutils = os.path.join(os.path.dirname(__file__), '..') |
42 self.crosutilsbin = os.path.join(os.path.dirname(__file__)) | 50 self.crosutilsbin = os.path.join(os.path.dirname(__file__)) |
51 self.download = os.path.join(self.crosutilsbin, 'latest_download') | |
sosa
2010/12/01 18:37:39
self.download_folder?
dgarrett
2010/12/02 22:20:20
Done.
| |
43 | 52 |
44 def GetStatefulChangeFlag(self, stateful_change): | 53 def GetStatefulChangeFlag(self, stateful_change): |
45 """Returns the flag to pass to image_to_vm for the stateful change.""" | 54 """Returns the flag to pass to image_to_vm for the stateful change.""" |
46 stateful_change_flag = '' | 55 stateful_change_flag = '' |
47 if stateful_change: | 56 if stateful_change: |
48 stateful_change_flag = '--stateful_update_flag=%s' % stateful_change | 57 stateful_change_flag = '--stateful_update_flag=%s' % stateful_change |
49 | 58 |
50 return stateful_change_flag | 59 return stateful_change_flag |
51 | 60 |
52 def ParseGenerateTestReportOutput(self, output): | 61 def ParseGenerateTestReportOutput(self, output): |
(...skipping 14 matching lines...) Expand all Loading... | |
67 def TryDeltaAndFallbackToFull(self, src_image, image, stateful_change='old'): | 76 def TryDeltaAndFallbackToFull(self, src_image, image, stateful_change='old'): |
68 """Tries the delta update first if set and falls back to full update.""" | 77 """Tries the delta update first if set and falls back to full update.""" |
69 if self.use_delta_updates: | 78 if self.use_delta_updates: |
70 try: | 79 try: |
71 self.source_image = src_image | 80 self.source_image = src_image |
72 self.UpdateImage(image) | 81 self.UpdateImage(image) |
73 except: | 82 except: |
74 Warning('Delta update failed, disabling delta updates and retrying.') | 83 Warning('Delta update failed, disabling delta updates and retrying.') |
75 self.use_delta_updates = False | 84 self.use_delta_updates = False |
76 self.source_image = '' | 85 self.source_image = '' |
77 self.UpdateImage(image) | 86 self.UpdateImageReportError(image) |
78 else: | 87 else: |
79 self.UpdateImage(image) | 88 self.UpdateImageReportError(image) |
89 | |
90 def UpdateImageReportError(self, image_path, stateful_change='old'): | |
sosa
2010/12/01 18:37:39
needs docstring .. may also want to make private
dgarrett
2010/12/02 22:20:20
Done.
| |
91 try: | |
92 self.UpdateImage(image_path, stateful_change) | |
93 except UpdateException as err: | |
94 # If the update fails, print it out | |
95 print err.stdout | |
sosa
2010/12/01 18:37:39
Use warning?
dgarrett
2010/12/02 22:20:20
Done.
| |
96 raise | |
80 | 97 |
81 def PrepareBase(self): | 98 def PrepareBase(self): |
82 """Prepares target with base_image_path.""" | 99 """Prepares target with base_image_path.""" |
83 pass | 100 pass |
84 | 101 |
85 def UpdateImage(self, image_path, stateful_change='old'): | 102 def UpdateImage(self, image_path, stateful_change='old'): |
86 """Updates target with the image given by the image_path. | 103 """Updates target with the image given by the image_path. |
87 | 104 |
88 Args: | 105 Args: |
89 image_path: Path to the image to update with. This image must be a test | 106 image_path: Path to the image to update with. This image must be a test |
90 image. | 107 image. |
91 stateful_change: How to modify the stateful partition. Values are: | 108 stateful_change: How to modify the stateful partition. Values are: |
92 'old': Don't modify stateful partition. Just update normally. | 109 'old': Don't modify stateful partition. Just update normally. |
93 'clean': Uses clobber-state to wipe the stateful partition with the | 110 'clean': Uses clobber-state to wipe the stateful partition with the |
94 exception of code needed for ssh. | 111 exception of code needed for ssh. |
95 """ | 112 """ |
96 pass | 113 pass |
97 | 114 |
115 def UpdatePayload(self, update_path, stateful_change='old'): | |
sosa
2010/12/01 18:37:39
Maybe UpdateUsingPayload
dgarrett
2010/12/02 22:20:20
Done.
| |
116 """Updates target with the pre-generated update stored in update_path | |
117 | |
118 Args: | |
119 update_path: Path to the image to update with. This directory should | |
120 contain both update.gz, and stateful.image.gz | |
121 """ | |
122 pass | |
123 | |
98 def VerifyImage(self, percent_required_to_pass): | 124 def VerifyImage(self, percent_required_to_pass): |
99 """Verifies the image with tests. | 125 """Verifies the image with tests. |
100 | 126 |
101 Verifies that the test images passes the percent required. | 127 Verifies that the test images passes the percent required. |
102 | 128 |
103 Args: | 129 Args: |
104 percent_required_to_pass: percentage required to pass. This should be | 130 percent_required_to_pass: percentage required to pass. This should be |
105 fall between 0-100. | 131 fall between 0-100. |
106 | 132 |
107 Returns: | 133 Returns: |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
171 # Update to - all tests should pass on new image. | 197 # Update to - all tests should pass on new image. |
172 Info('Updating from base image on vm to target image and wiping stateful.') | 198 Info('Updating from base image on vm to target image and wiping stateful.') |
173 self.TryDeltaAndFallbackToFull(base_image_path, target_image_path, 'clean') | 199 self.TryDeltaAndFallbackToFull(base_image_path, target_image_path, 'clean') |
174 self.VerifyImage(100) | 200 self.VerifyImage(100) |
175 | 201 |
176 # Update from - same percentage should pass that originally passed. | 202 # Update from - same percentage should pass that originally passed. |
177 Info('Updating from updated image back to base image and wiping stateful.') | 203 Info('Updating from updated image back to base image and wiping stateful.') |
178 self.TryDeltaAndFallbackToFull(target_image_path, base_image_path, 'clean') | 204 self.TryDeltaAndFallbackToFull(target_image_path, base_image_path, 'clean') |
179 self.VerifyImage(percent_passed) | 205 self.VerifyImage(percent_passed) |
180 | 206 |
207 def testPartialUpdate(self): | |
208 """Tests what happens if we attempt to update with a truncated payload. | |
sosa
2010/12/01 18:37:39
Only needs to be a one liner (i.e end triple quote
dgarrett
2010/12/02 22:20:20
Done.
| |
209 """ | |
210 # Preload with the version we are trying to test. | |
211 self.UpdateImageReportError(target_image_path) | |
sosa
2010/12/01 18:37:39
Move into PrepareBase and use PrepareBase (e.g. we
| |
212 percent_passed = self.VerifyImage(100) | |
213 | |
214 # Image can be updated at: | |
215 # ~chrome-eng/chromeos/localmirror/autest-images | |
216 url = 'http://gsdview.appspot.com/chromeos-localmirror/autest-images/truncat ed_image.gz' | |
sosa
2010/12/01 18:37:39
80 chars
dgarrett
2010/12/02 22:20:20
Done.
| |
217 payload = os.path.join(self.download, 'truncated_image.gz') | |
218 | |
219 # Read from the URL and write to the local file | |
220 shutil.copyfileobj(urllib.urlopen(url), open(payload, 'w')) | |
sosa
2010/12/01 18:37:39
Maybe use urllib.urlretrieve?
dgarrett
2010/12/02 22:20:20
Nice, I looked for something like that, but missed
| |
221 | |
222 # This update is expected to fail... | |
223 try: | |
224 self.UpdatePayload(payload) | |
225 except UpdateException as err: | |
226 # Will raise ValueError if expected is not found. | |
227 try: | |
228 expectedMsg='download_hash_data == update_check_response_hash failed' | |
sosa
2010/12/01 18:37:39
Might be easier to read with an expected regex rat
| |
229 err.stdout.index(expectedMsg) | |
230 except ValueError: | |
231 print "Didn't find '%s' in:" % expectedMsg | |
sosa
2010/12/01 18:37:39
Use Info or Warning
dgarrett
2010/12/02 22:20:20
Done.
| |
232 print err.stdout | |
233 raise | |
234 return | |
235 | |
236 unittest.fail('We managed to update with a partial payload') | |
237 | |
238 def testCorruptedUpdate(self): | |
239 """Tests what happens if we attempt to update with a corrupted payload. | |
sosa
2010/12/01 18:37:39
Don't need a second line for docstring
dgarrett
2010/12/02 22:20:20
Done.
| |
240 """ | |
241 # Preload with the version we are trying to test. | |
242 self.UpdateImageReportError(target_image_path) | |
sosa
2010/12/01 18:37:39
PrepareBase
dgarrett
2010/12/02 22:20:20
Done.
| |
243 percent_passed = self.VerifyImage(100) | |
244 | |
245 # Image can be updated at: | |
246 # ~chrome-eng/chromeos/localmirror/autest-images | |
247 url = 'http://gsdview.appspot.com/chromeos-localmirror/autest-images/corrupt ed_image.gz' | |
sosa
2010/12/01 18:37:39
80 chars
dgarrett
2010/12/02 22:20:20
Done.
| |
248 payload = os.path.join(self.download, 'corrupted.gz') | |
249 | |
250 # Read from the URL and write to the local file | |
251 shutil.copyfileobj(urllib.urlopen(url), open(payload, 'w')) | |
252 | |
253 # This update is expected to fail... | |
254 try: | |
255 self.UpdatePayload(payload) | |
256 except UpdateException as err: | |
257 # Will raise ValueError if expected is not found. | |
258 try: | |
259 # There are a number of different errors that can occur here, but | |
260 # we're just trying to test the whole thing. | |
261 expectedMsg='zlib inflate() error:-3' | |
sosa
2010/12/01 18:37:39
expected_msg
dgarrett
2010/12/02 22:20:20
Done.
| |
262 err.stdout.index(expectedMsg) | |
263 except ValueError: | |
264 print "Didn't find '%s' in:" % expectedMsg | |
265 print err.stdoutKeepStateful | |
266 raise | |
267 return | |
268 | |
269 unittest.fail('We managed to update with a corrupted payload') | |
sosa
2010/12/01 18:37:39
This code seems almost the same as the test before
dgarrett
2010/12/02 22:20:20
Done.
| |
181 | 270 |
182 class RealAUTest(unittest.TestCase, AUTest): | 271 class RealAUTest(unittest.TestCase, AUTest): |
183 """Test harness for updating real images.""" | 272 """Test harness for updating real images.""" |
184 | 273 |
185 def setUp(self): | 274 def setUp(self): |
186 AUTest.setUp(self) | 275 AUTest.setUp(self) |
187 | 276 |
188 def PrepareBase(self): | 277 def PrepareBase(self): |
189 """Auto-update to base image to prepare for test.""" | 278 """Auto-update to base image to prepare for test.""" |
190 self.UpdateImage(base_image_path) | 279 self.UpdateImageReportError(base_image_path) |
191 | 280 |
192 def UpdateImage(self, image_path, stateful_change='old'): | 281 def UpdateImage(self, image_path, stateful_change='old'): |
193 """Updates a remote image using image_to_live.sh.""" | 282 """Updates a remote image using image_to_live.sh.""" |
194 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) | 283 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
195 | 284 |
196 RunCommand([ | 285 (code, stdout, stderr) = RunCommandCaptureOutput([ |
197 '%s/image_to_live.sh' % self.crosutils, | 286 '%s/image_to_live.sh' % self.crosutils, |
198 '--image=%s' % image_path, | 287 '--image=%s' % image_path, |
199 '--remote=%s' % remote, | 288 '--remote=%s' % remote, |
200 stateful_change_flag, | 289 stateful_change_flag, |
201 '--verify', | 290 '--verify', |
202 '--src_image=%s' % self.source_image, | 291 '--src_image=%s' % self.source_image |
203 ], enter_chroot=False) | 292 ]) |
204 | 293 |
294 if code != 0: | |
295 raise UpdateException(code, stdout) | |
296 | |
297 def UpdatePayload(self, update_path, stateful_change='old'): | |
298 """Updates a remote image using image_to_live.sh.""" | |
299 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) | |
300 | |
301 (code, stdout, stderr) = RunCommandCaptureOutput([ | |
302 '%s/image_to_live.sh' % self.crosutils, | |
303 '--payload=%s' % update_path, | |
304 '--remote=%s' % remote, | |
305 stateful_change_flag, | |
306 '--verify', | |
307 ]) | |
308 | |
309 if code != 0: | |
310 raise UpdateException(code, stdout) | |
205 | 311 |
206 def VerifyImage(self, percent_required_to_pass): | 312 def VerifyImage(self, percent_required_to_pass): |
207 """Verifies an image using run_remote_tests.sh with verification suite.""" | 313 """Verifies an image using run_remote_tests.sh with verification suite.""" |
208 output = RunCommand([ | 314 output = RunCommand([ |
209 '%s/run_remote_tests.sh' % self.crosutils, | 315 '%s/run_remote_tests.sh' % self.crosutils, |
210 '--remote=%s' % remote, | 316 '--remote=%s' % remote, |
211 _VERIFY_SUITE, | 317 _VERIFY_SUITE, |
212 ], error_ok=True, enter_chroot=False, redirect_stdout=True) | 318 ], error_ok=True, enter_chroot=False, redirect_stdout=True) |
213 return self.CommonVerifyImage(self, output, percent_required_to_pass) | 319 return self.CommonVerifyImage(self, output, percent_required_to_pass) |
214 | 320 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
252 Info('Using existing VM image %s' % self.vm_image_path) | 358 Info('Using existing VM image %s' % self.vm_image_path) |
253 | 359 |
254 self.assertTrue(os.path.exists(self.vm_image_path)) | 360 self.assertTrue(os.path.exists(self.vm_image_path)) |
255 | 361 |
256 def UpdateImage(self, image_path, stateful_change='old'): | 362 def UpdateImage(self, image_path, stateful_change='old'): |
257 """Updates VM image with image_path.""" | 363 """Updates VM image with image_path.""" |
258 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) | 364 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
259 if self.source_image == base_image_path: | 365 if self.source_image == base_image_path: |
260 self.source_image = self.vm_image_path | 366 self.source_image = self.vm_image_path |
261 | 367 |
262 RunCommand(['%s/cros_run_vm_update' % self.crosutilsbin, | 368 (code, stdout, stderr) = RunCommandCaptureOutput([ |
263 '--update_image_path=%s' % image_path, | 369 '%s/cros_run_vm_update' % self.crosutilsbin, |
264 '--vm_image_path=%s' % self.vm_image_path, | 370 '--update_image_path=%s' % image_path, |
265 '--snapshot', | 371 '--vm_image_path=%s' % self.vm_image_path, |
266 vm_graphics_flag, | 372 '--snapshot', |
267 '--persist', | 373 vm_graphics_flag, |
268 '--kvm_pid=%s' % _KVM_PID_FILE, | 374 '--persist', |
269 stateful_change_flag, | 375 '--kvm_pid=%s' % _KVM_PID_FILE, |
270 '--src_image=%s' % self.source_image, | 376 stateful_change_flag, |
271 ], enter_chroot=False) | 377 '--src_image=%s' % self.source_image, |
378 ]) | |
379 | |
380 if code != 0: | |
381 raise UpdateException(code, stdout) | |
382 | |
383 def UpdatePayload(self, update_path, stateful_change='old'): | |
384 """Updates a remote image using image_to_live.sh.""" | |
385 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) | |
386 | |
387 (code, stdout, stderr) = RunCommandCaptureOutput([ | |
sosa
2010/12/01 18:37:39
Does this work for VM's? The port isn't correct .
dgarrett
2010/12/02 22:20:20
It did not, and the fix was larger than I was expe
| |
388 '%s/image_to_live.sh' % self.crosutils, | |
389 '--payload=%s' % update_path, | |
390 stateful_change_flag, | |
391 '--for_vm', | |
392 '--verify', | |
393 ]) | |
394 | |
395 if code != 0: | |
396 raise UpdateException(code, stdout) | |
272 | 397 |
273 def VerifyImage(self, percent_required_to_pass): | 398 def VerifyImage(self, percent_required_to_pass): |
274 """Runs vm smoke suite to verify image.""" | 399 """Runs vm smoke suite to verify image.""" |
275 # image_to_live already verifies lsb-release matching. This is just | 400 # image_to_live already verifies lsb-release matching. This is just |
276 # for additional steps. | 401 # for additional steps. |
277 | 402 |
278 commandWithArgs = ['%s/cros_run_vm_test' % self.crosutilsbin, | 403 commandWithArgs = ['%s/cros_run_vm_test' % self.crosutilsbin, |
279 '--image_path=%s' % self.vm_image_path, | 404 '--image_path=%s' % self.vm_image_path, |
280 '--snapshot', | 405 '--snapshot', |
281 '--persist', | 406 '--persist', |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
348 else: | 473 else: |
349 remote = options.remote | 474 remote = options.remote |
350 | 475 |
351 suite = unittest.TestLoader().loadTestsFromTestCase(RealAUTest) | 476 suite = unittest.TestLoader().loadTestsFromTestCase(RealAUTest) |
352 test_result = unittest.TextTestRunner(verbosity=2).run(suite) | 477 test_result = unittest.TextTestRunner(verbosity=2).run(suite) |
353 else: | 478 else: |
354 parser.error('Could not parse harness type %s.' % options.type) | 479 parser.error('Could not parse harness type %s.' % options.type) |
355 | 480 |
356 if not test_result.wasSuccessful(): | 481 if not test_result.wasSuccessful(): |
357 Die('Test harness was not successful') | 482 Die('Test harness was not successful') |
OLD | NEW |