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

Side by Side Diff: bin/cros_au_test_harness.py

Issue 5373008: Add testPartialUpdate, testCorruptedUpdate. (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/crosutils.git@master
Patch Set: Code review changes and fixes for VMs. Created 10 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | bin/cros_run_vm_update » ('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/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 re
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 UpdateUsingPayload 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_folder = os.path.join(self.crosutilsbin, 'latest_download')
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
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)
80 89
81 def PrepareBase(self): 90 def _UpdateImageReportError(self, image_path, stateful_change='old'):
91 """Calls UpdateImage and reports any error to the console.
92
93 Still throws the exception.
94 """
95 try:
96 self.UpdateImage(image_path, stateful_change)
97 except UpdateException as err:
98 # If the update fails, print it out
99 Warning(err.stdout)
100 raise
101
102 def _AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg):
103 # This update is expected to fail...
104 try:
105 self.UpdateUsingPayload(payload)
106 except UpdateException as err:
107 # Will raise ValueError if expected is not found.
108 if re.search(re.escape(expected_msg), err.stdout, re.MULTILINE):
109 return
110
111 Warning("Didn't find '%s' in:" % expected_msg)
112 Warning(err.stdout)
113 self.fail('We managed to update when failure was expected')
114
115 def PrepareBase(self, image_path):
82 """Prepares target with base_image_path.""" 116 """Prepares target with base_image_path."""
83 pass 117 pass
84 118
85 def UpdateImage(self, image_path, stateful_change='old'): 119 def UpdateImage(self, image_path, stateful_change='old'):
86 """Updates target with the image given by the image_path. 120 """Updates target with the image given by the image_path.
87 121
88 Args: 122 Args:
89 image_path: Path to the image to update with. This image must be a test 123 image_path: Path to the image to update with. This image must be a test
90 image. 124 image.
91 stateful_change: How to modify the stateful partition. Values are: 125 stateful_change: How to modify the stateful partition. Values are:
92 'old': Don't modify stateful partition. Just update normally. 126 'old': Don't modify stateful partition. Just update normally.
93 'clean': Uses clobber-state to wipe the stateful partition with the 127 'clean': Uses clobber-state to wipe the stateful partition with the
94 exception of code needed for ssh. 128 exception of code needed for ssh.
95 """ 129 """
96 pass 130 pass
97 131
132 def UpdateUsingPayload(self, update_path, stateful_change='old'):
133 """Updates target with the pre-generated update stored in update_path
134
135 Args:
136 update_path: Path to the image to update with. This directory should
137 contain both update.gz, and stateful.image.gz
138 """
139 pass
140
98 def VerifyImage(self, percent_required_to_pass): 141 def VerifyImage(self, percent_required_to_pass):
99 """Verifies the image with tests. 142 """Verifies the image with tests.
100 143
101 Verifies that the test images passes the percent required. 144 Verifies that the test images passes the percent required.
102 145
103 Args: 146 Args:
104 percent_required_to_pass: percentage required to pass. This should be 147 percent_required_to_pass: percentage required to pass. This should be
105 fall between 0-100. 148 fall between 0-100.
106 149
107 Returns: 150 Returns:
(...skipping 25 matching lines...) Expand all
133 return percent_passed 176 return percent_passed
134 177
135 def testFullUpdateKeepStateful(self): 178 def testFullUpdateKeepStateful(self):
136 """Tests if we can update normally. 179 """Tests if we can update normally.
137 180
138 This test checks that we can update by updating the stateful partition 181 This test checks that we can update by updating the stateful partition
139 rather than wiping it. 182 rather than wiping it.
140 """ 183 """
141 # Just make sure some tests pass on original image. Some old images 184 # Just make sure some tests pass on original image. Some old images
142 # don't pass many tests. 185 # don't pass many tests.
143 self.PrepareBase() 186 self.PrepareBase(base_image_path)
144 # TODO(sosa): move to 100% once we start testing using the autotest paired 187 # TODO(sosa): move to 100% once we start testing using the autotest paired
145 # with the dev channel. 188 # with the dev channel.
146 percent_passed = self.VerifyImage(10) 189 percent_passed = self.VerifyImage(10)
147 190
148 # Update to - all tests should pass on new image. 191 # Update to - all tests should pass on new image.
149 Info('Updating from base image on vm to target image.') 192 Info('Updating from base image on vm to target image.')
150 self.TryDeltaAndFallbackToFull(base_image_path, target_image_path) 193 self.TryDeltaAndFallbackToFull(base_image_path, target_image_path)
151 self.VerifyImage(100) 194 self.VerifyImage(100)
152 195
153 # Update from - same percentage should pass that originally passed. 196 # Update from - same percentage should pass that originally passed.
154 Info('Updating from updated image on vm back to base image.') 197 Info('Updating from updated image on vm back to base image.')
155 self.TryDeltaAndFallbackToFull(target_image_path, base_image_path) 198 self.TryDeltaAndFallbackToFull(target_image_path, base_image_path)
156 self.VerifyImage(percent_passed) 199 self.VerifyImage(percent_passed)
157 200
158 def testFullUpdateWipeStateful(self): 201 def testFullUpdateWipeStateful(self):
159 """Tests if we can update after cleaning the stateful partition. 202 """Tests if we can update after cleaning the stateful partition.
160 203
161 This test checks that we can update successfully after wiping the 204 This test checks that we can update successfully after wiping the
162 stateful partition. 205 stateful partition.
163 """ 206 """
164 # Just make sure some tests pass on original image. Some old images 207 # Just make sure some tests pass on original image. Some old images
165 # don't pass many tests. 208 # don't pass many tests.
166 self.PrepareBase() 209 self.PrepareBase(base_image_path)
sosa 2010/12/03 01:07:38 use image_path?
167 # TODO(sosa): move to 100% once we start testing using the autotest paired 210 # TODO(sosa): move to 100% once we start testing using the autotest paired
168 # with the dev channel. 211 # with the dev channel.
169 percent_passed = self.VerifyImage(10) 212 percent_passed = self.VerifyImage(10)
170 213
171 # Update to - all tests should pass on new image. 214 # Update to - all tests should pass on new image.
172 Info('Updating from base image on vm to target image and wiping stateful.') 215 Info('Updating from base image on vm to target image and wiping stateful.')
173 self.TryDeltaAndFallbackToFull(base_image_path, target_image_path, 'clean') 216 self.TryDeltaAndFallbackToFull(base_image_path, target_image_path, 'clean')
174 self.VerifyImage(100) 217 self.VerifyImage(100)
175 218
176 # Update from - same percentage should pass that originally passed. 219 # Update from - same percentage should pass that originally passed.
177 Info('Updating from updated image back to base image and wiping stateful.') 220 Info('Updating from updated image back to base image and wiping stateful.')
178 self.TryDeltaAndFallbackToFull(target_image_path, base_image_path, 'clean') 221 self.TryDeltaAndFallbackToFull(target_image_path, base_image_path, 'clean')
179 self.VerifyImage(percent_passed) 222 self.VerifyImage(percent_passed)
180 223
224 def testPartialUpdate(self):
225 """Tests what happens if we attempt to update with a truncated payload."""
226 # Preload with the version we are trying to test.
227 self.PrepareBase(target_image_path)
228
229 # Image can be updated at:
230 # ~chrome-eng/chromeos/localmirror/autest-images
231 url = 'http://gsdview.appspot.com/chromeos-localmirror/' \
232 'autest-images/truncated_image.gz'
233 payload = os.path.join(self.download_folder, 'truncated_image.gz')
234
235 # Read from the URL and write to the local file
236 urllib.urlretrieve(url, payload)
237
238 expected_msg='download_hash_data == update_check_response_hash failed'
239 self._AttemptUpdateWithPayloadExpectedFailure(payload, expected_msg)
240
241 def testCorruptedUpdate(self):
242 """Tests what happens if we attempt to update with a corrupted payload."""
243 # Preload with the version we are trying to test.
244 self.PrepareBase(target_image_path)
245
246 # Image can be updated at:
247 # ~chrome-eng/chromeos/localmirror/autest-images
248 url = 'http://gsdview.appspot.com/chromeos-localmirror/' \
249 'autest-images/corrupted_image.gz'
250 payload = os.path.join(self.download_folder, 'corrupted.gz')
251
252 # Read from the URL and write to the local file
253 urllib.urlretrieve(url, payload)
254
255 # This update is expected to fail...
256 expected_msg='zlib inflate() error:-3'
257 self._AttemptUpdateWithPayloadExpectedFailure(payload, expected_msg)
181 258
182 class RealAUTest(unittest.TestCase, AUTest): 259 class RealAUTest(unittest.TestCase, AUTest):
183 """Test harness for updating real images.""" 260 """Test harness for updating real images."""
184 261
185 def setUp(self): 262 def setUp(self):
186 AUTest.setUp(self) 263 AUTest.setUp(self)
187 264
188 def PrepareBase(self): 265 def PrepareBase(self, image_path):
189 """Auto-update to base image to prepare for test.""" 266 """Auto-update to base image to prepare for test."""
190 self.UpdateImage(base_image_path) 267 self._UpdateImageReportError(image_path)
191 268
192 def UpdateImage(self, image_path, stateful_change='old'): 269 def UpdateImage(self, image_path, stateful_change='old'):
193 """Updates a remote image using image_to_live.sh.""" 270 """Updates a remote image using image_to_live.sh."""
194 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) 271 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
195 272
196 RunCommand([ 273 (code, stdout, stderr) = RunCommandCaptureOutput([
197 '%s/image_to_live.sh' % self.crosutils, 274 '%s/image_to_live.sh' % self.crosutils,
198 '--image=%s' % image_path, 275 '--image=%s' % image_path,
199 '--remote=%s' % remote, 276 '--remote=%s' % remote,
200 stateful_change_flag, 277 stateful_change_flag,
201 '--verify', 278 '--verify',
202 '--src_image=%s' % self.source_image, 279 '--src_image=%s' % self.source_image
203 ], enter_chroot=False) 280 ])
204 281
282 if code != 0:
283 raise UpdateException(code, stdout)
284
285 def UpdateUsingPayload(self, update_path, stateful_change='old'):
286 """Updates a remote image using image_to_live.sh."""
287 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
288
289 (code, stdout, stderr) = RunCommandCaptureOutput([
290 '%s/image_to_live.sh' % self.crosutils,
291 '--payload=%s' % update_path,
292 '--remote=%s' % remote,
293 stateful_change_flag,
294 '--verify',
295 ])
296
297 if code != 0:
298 raise UpdateException(code, stdout)
205 299
206 def VerifyImage(self, percent_required_to_pass): 300 def VerifyImage(self, percent_required_to_pass):
207 """Verifies an image using run_remote_tests.sh with verification suite.""" 301 """Verifies an image using run_remote_tests.sh with verification suite."""
208 output = RunCommand([ 302 output = RunCommand([
209 '%s/run_remote_tests.sh' % self.crosutils, 303 '%s/run_remote_tests.sh' % self.crosutils,
210 '--remote=%s' % remote, 304 '--remote=%s' % remote,
211 _VERIFY_SUITE, 305 _VERIFY_SUITE,
212 ], error_ok=True, enter_chroot=False, redirect_stdout=True) 306 ], error_ok=True, enter_chroot=False, redirect_stdout=True)
213 return self.CommonVerifyImage(self, output, percent_required_to_pass) 307 return self.CommonVerifyImage(self, output, percent_required_to_pass)
214 308
(...skipping 11 matching lines...) Expand all
226 if pid: 320 if pid:
227 RunCommand(['sudo', 'kill', pid.strip()], error_ok=True, 321 RunCommand(['sudo', 'kill', pid.strip()], error_ok=True,
228 enter_chroot=False) 322 enter_chroot=False)
229 RunCommand(['sudo', 'rm', pid_file], enter_chroot=False) 323 RunCommand(['sudo', 'rm', pid_file], enter_chroot=False)
230 324
231 def setUp(self): 325 def setUp(self):
232 """Unit test overriden method. Is called before every test.""" 326 """Unit test overriden method. Is called before every test."""
233 AUTest.setUp(self) 327 AUTest.setUp(self)
234 self._KillExistingVM(_KVM_PID_FILE) 328 self._KillExistingVM(_KVM_PID_FILE)
235 329
236 def PrepareBase(self): 330 def PrepareBase(self, image_path):
237 """Creates an update-able VM based on base image.""" 331 """Creates an update-able VM based on base image."""
238 self.vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname( 332 self.vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname(
239 base_image_path) 333 image_path)
334
335 Info('Creating: %s' % self.vm_image_path)
240 336
241 if not os.path.exists(self.vm_image_path): 337 if not os.path.exists(self.vm_image_path):
242 Info('Qemu image %s not found, creating one.' % self.vm_image_path) 338 Info('Qemu image %s not found, creating one.' % self.vm_image_path)
243 RunCommand(['%s/image_to_vm.sh' % self.crosutils, 339 RunCommand(['%s/image_to_vm.sh' % self.crosutils,
244 '--full', 340 '--full',
245 '--from=%s' % ReinterpretPathForChroot( 341 '--from=%s' % ReinterpretPathForChroot(
246 os.path.dirname(base_image_path)), 342 os.path.dirname(image_path)),
247 '--vdisk_size=%s' % _FULL_VDISK_SIZE, 343 '--vdisk_size=%s' % _FULL_VDISK_SIZE,
248 '--statefulfs_size=%s' % _FULL_STATEFULFS_SIZE, 344 '--statefulfs_size=%s' % _FULL_STATEFULFS_SIZE,
249 '--board=%s' % board, 345 '--board=%s' % board,
250 '--test_image'], enter_chroot=True) 346 '--test_image'], enter_chroot=True)
251 else: 347 else:
252 Info('Using existing VM image %s' % self.vm_image_path) 348 Info('Using existing VM image %s' % self.vm_image_path)
253 349
350
351 Info('Testing for %s' % self.vm_image_path)
352
254 self.assertTrue(os.path.exists(self.vm_image_path)) 353 self.assertTrue(os.path.exists(self.vm_image_path))
255 354
256 def UpdateImage(self, image_path, stateful_change='old'): 355 def UpdateImage(self, image_path, stateful_change='old'):
257 """Updates VM image with image_path.""" 356 """Updates VM image with image_path."""
258 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) 357 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
259 if self.source_image == base_image_path: 358 if self.source_image == base_image_path:
260 self.source_image = self.vm_image_path 359 self.source_image = self.vm_image_path
261 360
262 RunCommand(['%s/cros_run_vm_update' % self.crosutilsbin, 361 (code, stdout, stderr) = RunCommandCaptureOutput([
263 '--update_image_path=%s' % image_path, 362 '%s/cros_run_vm_update' % self.crosutilsbin,
264 '--vm_image_path=%s' % self.vm_image_path, 363 '--update_image_path=%s' % image_path,
265 '--snapshot', 364 '--vm_image_path=%s' % self.vm_image_path,
266 vm_graphics_flag, 365 '--snapshot',
267 '--persist', 366 vm_graphics_flag,
268 '--kvm_pid=%s' % _KVM_PID_FILE, 367 '--persist',
269 stateful_change_flag, 368 '--kvm_pid=%s' % _KVM_PID_FILE,
270 '--src_image=%s' % self.source_image, 369 stateful_change_flag,
271 ], enter_chroot=False) 370 '--src_image=%s' % self.source_image,
371 ])
372
373 if code != 0:
374 raise UpdateException(code, stdout)
375
376 def UpdateUsingPayload(self, update_path, stateful_change='old'):
377 """Updates a remote image using image_to_live.sh."""
378 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
379 if self.source_image == base_image_path:
380 self.source_image = self.vm_image_path
381
382 (code, stdout, stderr) = RunCommandCaptureOutput([
383 '%s/cros_run_vm_update' % self.crosutilsbin,
384 '--payload=%s' % update_path,
385 '--vm_image_path=%s' % self.vm_image_path,
386 '--snapshot',
387 vm_graphics_flag,
388 '--persist',
389 '--kvm_pid=%s' % _KVM_PID_FILE,
390 stateful_change_flag,
391 '--src_image=%s' % self.source_image,
392 ])
393
394 if code != 0:
395 raise UpdateException(code, stdout)
272 396
273 def VerifyImage(self, percent_required_to_pass): 397 def VerifyImage(self, percent_required_to_pass):
274 """Runs vm smoke suite to verify image.""" 398 """Runs vm smoke suite to verify image."""
275 # image_to_live already verifies lsb-release matching. This is just 399 # image_to_live already verifies lsb-release matching. This is just
276 # for additional steps. 400 # for additional steps.
277 401
278 commandWithArgs = ['%s/cros_run_vm_test' % self.crosutilsbin, 402 commandWithArgs = ['%s/cros_run_vm_test' % self.crosutilsbin,
279 '--image_path=%s' % self.vm_image_path, 403 '--image_path=%s' % self.vm_image_path,
280 '--snapshot', 404 '--snapshot',
281 '--persist', 405 '--persist',
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
348 else: 472 else:
349 remote = options.remote 473 remote = options.remote
350 474
351 suite = unittest.TestLoader().loadTestsFromTestCase(RealAUTest) 475 suite = unittest.TestLoader().loadTestsFromTestCase(RealAUTest)
352 test_result = unittest.TextTestRunner(verbosity=2).run(suite) 476 test_result = unittest.TextTestRunner(verbosity=2).run(suite)
353 else: 477 else:
354 parser.error('Could not parse harness type %s.' % options.type) 478 parser.error('Could not parse harness type %s.' % options.type)
355 479
356 if not test_result.wasSuccessful(): 480 if not test_result.wasSuccessful():
357 Die('Test harness was not successful') 481 Die('Test harness was not successful')
OLDNEW
« no previous file with comments | « no previous file | bin/cros_run_vm_update » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698