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 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 = [] |
| 85 for target, srcs in dummy_au_worker.DummyAUWorker.delta_list.items(): | 84 for target, srcs in dummy_au_worker.DummyAUWorker.delta_list.items(): |
| 85 # We only need to add the target if we are doing a full update. | |
| 86 if not options.delta: au_test.AUTest.modified_images.add(target) | |
|
petkov
2011/03/16 22:03:48
can you make modified_images local to this routine
sosa
2011/03/16 22:47:02
Done.
| |
| 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: au_test.AUTest.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 au_test.AUTest.modified_images.add(options.base_image) | |
| 100 | |
| 101 # Add public key to all images we are using. | |
| 102 if options.public_key: | |
| 103 for image in au_test.AUTest.modified_images: | |
| 104 manager = public_key_manager.PublicKeyManager(image, options.public_key) | |
| 105 manager.AddKeyToImage() | |
| 106 au_test.AUTest.public_key_managers.append(manager) | |
| 107 | |
| 96 raw_results = parallel_test_job.RunParallelJobs(options.jobs, jobs, args, | 108 raw_results = parallel_test_job.RunParallelJobs(options.jobs, jobs, args, |
| 97 print_status=True) | 109 print_status=True) |
| 98 results = [] | 110 results = [] |
| 99 | 111 |
| 100 # Looking for this line in the output. | 112 # Looking for this line in the output. |
| 101 key_line_re = re.compile('^PREGENERATED_UPDATE=([\w/.]+)') | 113 key_line_re = re.compile('^PREGENERATED_UPDATE=([\w/.]+)') |
| 102 for result in raw_results: | 114 for result in raw_results: |
| 103 (return_code, output, _) = result | 115 (return_code, output, _) = result |
| 104 if return_code != 0: | 116 if return_code != 0: |
| 105 cros_lib.Warning(output) | 117 cros_lib.Warning(output) |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 130 | 142 |
| 131 | 143 |
| 132 def _RunTestsInParallel(options): | 144 def _RunTestsInParallel(options): |
| 133 """Runs the tests given by the options in parallel.""" | 145 """Runs the tests given by the options in parallel.""" |
| 134 threads = [] | 146 threads = [] |
| 135 args = [] | 147 args = [] |
| 136 test_suite = _PrepareTestSuite(options) | 148 test_suite = _PrepareTestSuite(options) |
| 137 for test in test_suite: | 149 for test in test_suite: |
| 138 test_name = test.id() | 150 test_name = test.id() |
| 139 test_case = unittest.TestLoader().loadTestsFromName(test_name) | 151 test_case = unittest.TestLoader().loadTestsFromName(test_name) |
| 140 threads.append(unittest.TextTestRunner().run) | 152 threads.append(unittest.TextTestRunner(verbosity=2).run) |
| 141 args.append(test_case) | 153 args.append(test_case) |
| 142 | 154 |
| 143 results = parallel_test_job.RunParallelJobs(options.jobs, threads, args, | 155 results = parallel_test_job.RunParallelJobs(options.jobs, threads, args, |
| 144 print_status=False) | 156 print_status=False) |
| 145 for test_result in results: | 157 for test_result in results: |
| 146 if not test_result.wasSuccessful(): | 158 if not test_result.wasSuccessful(): |
| 147 cros_lib.Die('Test harness was not successful') | 159 cros_lib.Die('Test harness was not successful') |
| 148 | 160 |
| 149 | 161 |
| 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): | 162 def _CleanPreviousWork(options): |
| 197 """Cleans up previous work from the devserver cache and local image cache.""" | 163 """Cleans up previous work from the devserver cache and local image cache.""" |
| 198 cros_lib.Info('Cleaning up previous work.') | 164 cros_lib.Info('Cleaning up previous work.') |
| 199 # Wipe devserver cache. | 165 # Wipe devserver cache. |
| 200 cros_lib.RunCommandCaptureOutput( | 166 cros_lib.RunCommandCaptureOutput( |
| 201 ['sudo', 'start_devserver', '--clear_cache', '--exit', ], | 167 ['sudo', 'start_devserver', '--clear_cache', '--exit', ], |
| 202 enter_chroot=True, print_cmd=False, combine_stdout_stderr=True) | 168 enter_chroot=True, print_cmd=False, combine_stdout_stderr=True) |
| 203 | 169 |
| 204 # Clean previous vm images if they exist. | 170 # Clean previous vm images if they exist. |
| 205 if options.type == 'vm': | 171 if options.type == 'vm': |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 250 (options, leftover_args) = parser.parse_args() | 216 (options, leftover_args) = parser.parse_args() |
| 251 | 217 |
| 252 if leftover_args: parser.error('Found unsupported flags: %s' % leftover_args) | 218 if leftover_args: parser.error('Found unsupported flags: %s' % leftover_args) |
| 253 | 219 |
| 254 assert options.target_image and os.path.exists(options.target_image), \ | 220 assert options.target_image and os.path.exists(options.target_image), \ |
| 255 'Target image path does not exist' | 221 'Target image path does not exist' |
| 256 if not options.base_image: | 222 if not options.base_image: |
| 257 cros_lib.Info('Base image not specified. Using target as base image.') | 223 cros_lib.Info('Base image not specified. Using target as base image.') |
| 258 options.base_image = options.target_image | 224 options.base_image = options.target_image |
| 259 | 225 |
| 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: | 226 if options.private_key or options.public_key: |
| 264 error_msg = ('Could not find %s key. Both private and public keys must be ' | 227 error_msg = ('Could not find %s key. Both private and public keys must be ' |
| 265 'specified if either is specified.') | 228 'specified if either is specified.') |
| 266 assert options.private_key and os.path.exists(options.private_key), \ | 229 assert options.private_key and os.path.exists(options.private_key), \ |
| 267 error_msg % 'private' | 230 error_msg % 'private' |
| 268 assert options.public_key and os.path.exists(options.public_key), \ | 231 assert options.public_key and os.path.exists(options.public_key), \ |
| 269 error_msg % 'public' | 232 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 | 233 |
| 275 # Clean up previous work if requested. | 234 # Clean up previous work if requested. |
| 276 if options.clean: _CleanPreviousWork(options) | 235 if options.clean: _CleanPreviousWork(options) |
| 277 | 236 |
| 278 # Make sure we have a log directory. | 237 # Make sure we have a log directory. |
| 279 if not os.path.exists(options.test_results_root): | 238 if not os.path.exists(options.test_results_root): |
| 280 os.makedirs(options.test_results_root) | 239 os.makedirs(options.test_results_root) |
| 281 | 240 |
| 282 # Generate cache of updates to use during test harness. | 241 # Pre-generate update modifies images by adding public keys to them. |
| 283 update_cache = _PregenerateUpdates(options) | 242 # Wrap try to make sure we clean this up before we're done. |
| 284 au_worker.AUWorker.SetUpdateCache(update_cache) | 243 try: |
| 244 # Generate cache of updates to use during test harness. | |
| 245 update_cache = _PregenerateUpdates(options) | |
| 246 au_worker.AUWorker.SetUpdateCache(update_cache) | |
| 285 | 247 |
| 286 my_server = dev_server_wrapper.DevServerWrapper( | 248 my_server = dev_server_wrapper.DevServerWrapper( |
| 287 au_test.AUTest.test_results_root) | 249 au_test.AUTest.test_results_root) |
| 288 my_server.start() | 250 my_server.start() |
| 289 try: | 251 try: |
| 290 if options.type == 'vm': | 252 if options.type == 'vm': |
| 291 _RunTestsInParallel(options) | 253 _RunTestsInParallel(options) |
| 292 else: | 254 else: |
| 293 # TODO(sosa) - Take in a machine pool for a real test. | 255 # TODO(sosa) - Take in a machine pool for a real test. |
| 294 # Can't run in parallel with only one remote device. | 256 # Can't run in parallel with only one remote device. |
| 295 test_suite = _PrepareTestSuite(options) | 257 test_suite = _PrepareTestSuite(options) |
| 296 test_result = unittest.TextTestRunner(verbosity=2).run(test_suite) | 258 test_result = unittest.TextTestRunner(verbosity=2).run(test_suite) |
| 297 if not test_result.wasSuccessful(): cros_lib.Die('Test harness failed.') | 259 if not test_result.wasSuccessful(): cros_lib.Die('Test harness failed.') |
| 260 | |
| 261 finally: | |
| 262 my_server.Stop() | |
| 263 | |
| 298 finally: | 264 finally: |
| 299 my_server.Stop() | 265 # Un-modify any target images we modified. We don't need to de-modify |
|
petkov
2011/03/16 22:03:48
Un-modify or de-modify? Pick on non-word and stick
sosa
2011/03/16 22:47:02
I enjoy making up words. Done.
On 2011/03/16 22:0
| |
| 266 # non-targets because they aren't important for archival steps. | |
| 267 cros_lib.Info('Cleaning up. Removing keys added as part of testing.') | |
| 268 target_directory = os.path.dirname(options.target_image) | |
| 269 for key_manager in au_test.AUTest.public_key_managers: | |
| 270 if key_manager.image_path.startswith(target_directory): | |
| 271 key_manager.RemoveKeyFromImage() | |
| 300 | 272 |
| 301 | 273 |
| 302 if __name__ == '__main__': | 274 if __name__ == '__main__': |
| 303 main() | 275 main() |
| OLD | NEW |