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