Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. | 3 # Copyright (c) 2011 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 """This module runs a suite of Auto Update tests. | 7 """This module runs a suite of Auto Update tests. |
| 8 | 8 |
| 9 The tests can be run on either a virtual machine or actual device depending | 9 The tests can be run on either a virtual machine or actual device depending |
| 10 on parameters given. Specific tests can be run by invoking --test_prefix. | 10 on parameters given. Specific tests can be run by invoking --test_prefix. |
| 11 Verbose is useful for many of the tests if you want to see individual commands | 11 Verbose is useful for many of the tests if you want to see individual commands |
| 12 being run during the update process. | 12 being run during the update process. |
| 13 """ | 13 """ |
| 14 | 14 |
| 15 import optparse | 15 import optparse |
| 16 import os | 16 import os |
| 17 import re | 17 import re |
| 18 import sys | 18 import sys |
| 19 import tempfile | |
| 20 import unittest | 19 import unittest |
| 21 | 20 |
| 22 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) | 21 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) |
| 23 import cros_build_lib as cros_lib | 22 import cros_build_lib as cros_lib |
| 24 | 23 |
| 25 import au_test | 24 import au_test |
| 26 import au_worker | 25 import au_worker |
| 27 import dummy_au_worker | 26 import dummy_au_worker |
| 28 import dev_server_wrapper | 27 import dev_server_wrapper |
| 29 import parallel_test_job | 28 import parallel_test_job |
| 29 import public_key_manager | |
| 30 import update_exception | 30 import update_exception |
| 31 | 31 |
| 32 | |
| 33 def _PrepareTestSuite(options, use_dummy_worker=False): | 32 def _PrepareTestSuite(options, use_dummy_worker=False): |
| 34 """Returns a prepared test suite given by the options and test class.""" | 33 """Returns a prepared test suite given by the options and test class.""" |
| 35 au_test.AUTest.ProcessOptions(options, use_dummy_worker) | 34 au_test.AUTest.ProcessOptions(options, use_dummy_worker) |
| 36 test_loader = unittest.TestLoader() | 35 test_loader = unittest.TestLoader() |
| 37 test_loader.testMethodPrefix = options.test_prefix | 36 test_loader.testMethodPrefix = options.test_prefix |
| 38 return test_loader.loadTestsFromTestCase(au_test.AUTest) | 37 return test_loader.loadTestsFromTestCase(au_test.AUTest) |
| 39 | 38 |
| 40 | 39 |
| 41 def _PregenerateUpdates(options): | 40 def _PregenerateUpdates(options): |
| 42 """Determines all deltas that will be generated and generates them. | 41 """Determines all deltas that will be generated and generates them. |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 75 test_suite = _PrepareTestSuite(options, use_dummy_worker=True) | 74 test_suite = _PrepareTestSuite(options, use_dummy_worker=True) |
| 76 test_result = unittest.TextTestRunner(verbosity=0).run(test_suite) | 75 test_result = unittest.TextTestRunner(verbosity=0).run(test_suite) |
| 77 if not test_result.wasSuccessful(): | 76 if not test_result.wasSuccessful(): |
| 78 raise update_exception.UpdateException(1, | 77 raise update_exception.UpdateException(1, |
| 79 'Error finding updates to generate.') | 78 'Error finding updates to generate.') |
| 80 | 79 |
| 81 cros_lib.Info('The following delta updates are required.') | 80 cros_lib.Info('The following delta updates are required.') |
| 82 update_ids = [] | 81 update_ids = [] |
| 83 jobs = [] | 82 jobs = [] |
| 84 args = [] | 83 args = [] |
| 84 modified_images = set ([]) | |
|
petkov
2011/03/16 22:56:12
you could just say set(), I think
| |
| 85 for target, srcs in dummy_au_worker.DummyAUWorker.delta_list.items(): | 85 for target, srcs in dummy_au_worker.DummyAUWorker.delta_list.items(): |
| 86 modified_images.add(target) | |
| 86 for src_key in srcs: | 87 for src_key in srcs: |
| 87 (src, _ , key) = src_key.partition('+') | 88 (src, _ , key) = src_key.partition('+') |
| 89 if src: modified_images.add(src) | |
| 88 # TODO(sosa): Add private key as part of caching name once devserver can | 90 # TODO(sosa): Add private key as part of caching name once devserver can |
| 89 # handle it its own cache. | 91 # handle it its own cache. |
| 90 update_id = dev_server_wrapper.GenerateUpdateId(target, src, key) | 92 update_id = dev_server_wrapper.GenerateUpdateId(target, src, key) |
| 91 print >> sys.stderr, 'AU: %s' % update_id | 93 print >> sys.stderr, 'AU: %s' % update_id |
| 92 update_ids.append(update_id) | 94 update_ids.append(update_id) |
| 93 jobs.append(_GenerateVMUpdate) | 95 jobs.append(_GenerateVMUpdate) |
| 94 args.append((target, src, key)) | 96 args.append((target, src, key)) |
| 95 | 97 |
| 98 # Always add the base image path. This is only useful for non-delta updates. | |
| 99 modified_images.add(options.base_image) | |
| 100 | |
| 101 # Add public key to all images we are using. | |
| 102 if options.public_key: | |
| 103 cros_lib.Info('Adding public keys to images for testing.') | |
| 104 for image in modified_images: | |
| 105 manager = public_key_manager.PublicKeyManager(image, options.public_key) | |
| 106 manager.AddKeyToImage() | |
| 107 au_test.AUTest.public_key_managers.append(manager) | |
| 108 | |
| 96 raw_results = parallel_test_job.RunParallelJobs(options.jobs, jobs, args, | 109 raw_results = parallel_test_job.RunParallelJobs(options.jobs, jobs, args, |
| 97 print_status=True) | 110 print_status=True) |
| 98 results = [] | 111 results = [] |
| 99 | 112 |
| 100 # Looking for this line in the output. | 113 # Looking for this line in the output. |
| 101 key_line_re = re.compile('^PREGENERATED_UPDATE=([\w/.]+)') | 114 key_line_re = re.compile('^PREGENERATED_UPDATE=([\w/.]+)') |
| 102 for result in raw_results: | 115 for result in raw_results: |
| 103 (return_code, output, _) = result | 116 (return_code, output, _) = result |
| 104 if return_code != 0: | 117 if return_code != 0: |
| 105 cros_lib.Warning(output) | 118 cros_lib.Warning(output) |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 130 | 143 |
| 131 | 144 |
| 132 def _RunTestsInParallel(options): | 145 def _RunTestsInParallel(options): |
| 133 """Runs the tests given by the options in parallel.""" | 146 """Runs the tests given by the options in parallel.""" |
| 134 threads = [] | 147 threads = [] |
| 135 args = [] | 148 args = [] |
| 136 test_suite = _PrepareTestSuite(options) | 149 test_suite = _PrepareTestSuite(options) |
| 137 for test in test_suite: | 150 for test in test_suite: |
| 138 test_name = test.id() | 151 test_name = test.id() |
| 139 test_case = unittest.TestLoader().loadTestsFromName(test_name) | 152 test_case = unittest.TestLoader().loadTestsFromName(test_name) |
| 140 threads.append(unittest.TextTestRunner().run) | 153 threads.append(unittest.TextTestRunner(verbosity=2).run) |
| 141 args.append(test_case) | 154 args.append(test_case) |
| 142 | 155 |
| 143 results = parallel_test_job.RunParallelJobs(options.jobs, threads, args, | 156 results = parallel_test_job.RunParallelJobs(options.jobs, threads, args, |
| 144 print_status=False) | 157 print_status=False) |
| 145 for test_result in results: | 158 for test_result in results: |
| 146 if not test_result.wasSuccessful(): | 159 if not test_result.wasSuccessful(): |
| 147 cros_lib.Die('Test harness was not successful') | 160 cros_lib.Die('Test harness was not successful') |
| 148 | 161 |
| 149 | 162 |
| 150 def _InsertPublicKeyIntoImage(image_path, key_path): | |
| 151 """Inserts public key into image @ static update_engine location.""" | |
| 152 from_dir = os.path.dirname(image_path) | |
| 153 image = os.path.basename(image_path) | |
| 154 crosutils_dir = os.path.abspath(__file__).rsplit('/', 2)[0] | |
| 155 target_key_path = 'usr/share/update_engine/update-payload-key.pub.pem' | |
| 156 | |
| 157 # Temporary directories for this function. | |
| 158 rootfs_dir = tempfile.mkdtemp(suffix='rootfs', prefix='tmp') | |
| 159 stateful_dir = tempfile.mkdtemp(suffix='stateful', prefix='tmp') | |
| 160 | |
| 161 cros_lib.Info('Copying %s into %s' % (key_path, image_path)) | |
| 162 try: | |
| 163 cros_lib.RunCommand(['./mount_gpt_image.sh', | |
| 164 '--from=%s' % from_dir, | |
| 165 '--image=%s' % image, | |
| 166 '--rootfs_mountpt=%s' % rootfs_dir, | |
| 167 '--stateful_mountpt=%s' % stateful_dir, | |
| 168 ], print_cmd=False, redirect_stdout=True, | |
| 169 redirect_stderr=True, cwd=crosutils_dir) | |
| 170 path = os.path.join(rootfs_dir, target_key_path) | |
| 171 dir_path = os.path.dirname(path) | |
| 172 cros_lib.RunCommand(['sudo', 'mkdir', '--parents', dir_path], | |
| 173 print_cmd=False) | |
| 174 cros_lib.RunCommand(['sudo', 'cp', '--force', '-p', key_path, path], | |
| 175 print_cmd=False) | |
| 176 finally: | |
| 177 # Unmount best effort regardless. | |
| 178 cros_lib.RunCommand(['./mount_gpt_image.sh', | |
| 179 '--unmount', | |
| 180 '--rootfs_mountpt=%s' % rootfs_dir, | |
| 181 '--stateful_mountpt=%s' % stateful_dir, | |
| 182 ], print_cmd=False, redirect_stdout=True, | |
| 183 redirect_stderr=True, cwd=crosutils_dir) | |
| 184 # Clean up our directories. | |
| 185 os.rmdir(rootfs_dir) | |
| 186 os.rmdir(stateful_dir) | |
| 187 | |
| 188 cros_lib.RunCommand(['bin/cros_make_image_bootable', | |
| 189 cros_lib.ReinterpretPathForChroot(from_dir), | |
| 190 image], | |
| 191 print_cmd=False, redirect_stdout=True, | |
| 192 redirect_stderr=True, enter_chroot=True, | |
| 193 cwd=crosutils_dir) | |
| 194 | |
| 195 | |
| 196 def _CleanPreviousWork(options): | 163 def _CleanPreviousWork(options): |
| 197 """Cleans up previous work from the devserver cache and local image cache.""" | 164 """Cleans up previous work from the devserver cache and local image cache.""" |
| 198 cros_lib.Info('Cleaning up previous work.') | 165 cros_lib.Info('Cleaning up previous work.') |
| 199 # Wipe devserver cache. | 166 # Wipe devserver cache. |
| 200 cros_lib.RunCommandCaptureOutput( | 167 cros_lib.RunCommandCaptureOutput( |
| 201 ['sudo', 'start_devserver', '--clear_cache', '--exit', ], | 168 ['sudo', 'start_devserver', '--clear_cache', '--exit', ], |
| 202 enter_chroot=True, print_cmd=False, combine_stdout_stderr=True) | 169 enter_chroot=True, print_cmd=False, combine_stdout_stderr=True) |
| 203 | 170 |
| 204 # Clean previous vm images if they exist. | 171 # Clean previous vm images if they exist. |
| 205 if options.type == 'vm': | 172 if options.type == 'vm': |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 250 (options, leftover_args) = parser.parse_args() | 217 (options, leftover_args) = parser.parse_args() |
| 251 | 218 |
| 252 if leftover_args: parser.error('Found unsupported flags: %s' % leftover_args) | 219 if leftover_args: parser.error('Found unsupported flags: %s' % leftover_args) |
| 253 | 220 |
| 254 assert options.target_image and os.path.exists(options.target_image), \ | 221 assert options.target_image and os.path.exists(options.target_image), \ |
| 255 'Target image path does not exist' | 222 'Target image path does not exist' |
| 256 if not options.base_image: | 223 if not options.base_image: |
| 257 cros_lib.Info('Base image not specified. Using target as base image.') | 224 cros_lib.Info('Base image not specified. Using target as base image.') |
| 258 options.base_image = options.target_image | 225 options.base_image = options.target_image |
| 259 | 226 |
| 260 # Sanity checks on keys and insert them onto the image. The caches must be | |
| 261 # cleaned so we know that the vm images and payloads match the possibly new | |
| 262 # key. | |
| 263 if options.private_key or options.public_key: | 227 if options.private_key or options.public_key: |
| 264 error_msg = ('Could not find %s key. Both private and public keys must be ' | 228 error_msg = ('Could not find %s key. Both private and public keys must be ' |
| 265 'specified if either is specified.') | 229 'specified if either is specified.') |
| 266 assert options.private_key and os.path.exists(options.private_key), \ | 230 assert options.private_key and os.path.exists(options.private_key), \ |
| 267 error_msg % 'private' | 231 error_msg % 'private' |
| 268 assert options.public_key and os.path.exists(options.public_key), \ | 232 assert options.public_key and os.path.exists(options.public_key), \ |
| 269 error_msg % 'public' | 233 error_msg % 'public' |
| 270 _InsertPublicKeyIntoImage(options.target_image, options.public_key) | |
| 271 if options.target_image != options.base_image: | |
| 272 _InsertPublicKeyIntoImage(options.base_image, options.public_key) | |
| 273 options.clean = True | |
| 274 | 234 |
| 275 # Clean up previous work if requested. | 235 # Clean up previous work if requested. |
| 276 if options.clean: _CleanPreviousWork(options) | 236 if options.clean: _CleanPreviousWork(options) |
| 277 | 237 |
| 278 # Make sure we have a log directory. | 238 # Make sure we have a log directory. |
| 279 if not os.path.exists(options.test_results_root): | 239 if not os.path.exists(options.test_results_root): |
| 280 os.makedirs(options.test_results_root) | 240 os.makedirs(options.test_results_root) |
| 281 | 241 |
| 282 # Generate cache of updates to use during test harness. | 242 # Pre-generate update modifies images by adding public keys to them. |
| 283 update_cache = _PregenerateUpdates(options) | 243 # Wrap try to make sure we clean this up before we're done. |
| 284 au_worker.AUWorker.SetUpdateCache(update_cache) | 244 try: |
| 245 # Generate cache of updates to use during test harness. | |
| 246 update_cache = _PregenerateUpdates(options) | |
| 247 au_worker.AUWorker.SetUpdateCache(update_cache) | |
| 285 | 248 |
| 286 my_server = dev_server_wrapper.DevServerWrapper( | 249 my_server = dev_server_wrapper.DevServerWrapper( |
| 287 au_test.AUTest.test_results_root) | 250 au_test.AUTest.test_results_root) |
| 288 my_server.start() | 251 my_server.start() |
| 289 try: | 252 try: |
| 290 if options.type == 'vm': | 253 if options.type == 'vm': |
| 291 _RunTestsInParallel(options) | 254 _RunTestsInParallel(options) |
| 292 else: | 255 else: |
| 293 # TODO(sosa) - Take in a machine pool for a real test. | 256 # TODO(sosa) - Take in a machine pool for a real test. |
| 294 # Can't run in parallel with only one remote device. | 257 # Can't run in parallel with only one remote device. |
| 295 test_suite = _PrepareTestSuite(options) | 258 test_suite = _PrepareTestSuite(options) |
| 296 test_result = unittest.TextTestRunner(verbosity=2).run(test_suite) | 259 test_result = unittest.TextTestRunner(verbosity=2).run(test_suite) |
| 297 if not test_result.wasSuccessful(): cros_lib.Die('Test harness failed.') | 260 if not test_result.wasSuccessful(): cros_lib.Die('Test harness failed.') |
| 261 | |
| 262 finally: | |
| 263 my_server.Stop() | |
| 264 | |
| 298 finally: | 265 finally: |
| 299 my_server.Stop() | 266 # Un-modify any target images we modified. We don't need to un-modify |
| 267 # non-targets because they aren't important for archival steps. | |
| 268 if options.public_key: | |
| 269 cros_lib.Info('Cleaning up. Removing keys added as part of testing.') | |
| 270 target_directory = os.path.dirname(options.target_image) | |
| 271 for key_manager in au_test.AUTest.public_key_managers: | |
| 272 if key_manager.image_path.startswith(target_directory): | |
| 273 key_manager.RemoveKeyFromImage() | |
| 300 | 274 |
| 301 | 275 |
| 302 if __name__ == '__main__': | 276 if __name__ == '__main__': |
| 303 main() | 277 main() |
| OLD | NEW |