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. |
(...skipping 11 matching lines...) Expand all Loading... |
22 import constants | 22 import constants |
23 sys.path.append(constants.CROSUTILS_LIB_DIR) | 23 sys.path.append(constants.CROSUTILS_LIB_DIR) |
24 import cros_build_lib as cros_lib | 24 import cros_build_lib as cros_lib |
25 | 25 |
26 import au_test | 26 import au_test |
27 import au_worker | 27 import au_worker |
28 import dummy_au_worker | 28 import dummy_au_worker |
29 import dev_server_wrapper | 29 import dev_server_wrapper |
30 import parallel_test_job | 30 import parallel_test_job |
31 import public_key_manager | 31 import public_key_manager |
| 32 import tempfile |
32 import update_exception | 33 import update_exception |
33 | 34 |
34 def _PrepareTestSuite(options, use_dummy_worker=False): | 35 def _PrepareTestSuite(options, use_dummy_worker=False): |
35 """Returns a prepared test suite given by the options and test class.""" | 36 """Returns a prepared test suite given by the options and test class.""" |
36 au_test.AUTest.ProcessOptions(options, use_dummy_worker) | 37 au_test.AUTest.ProcessOptions(options, use_dummy_worker) |
37 test_loader = unittest.TestLoader() | 38 test_loader = unittest.TestLoader() |
38 test_loader.testMethodPrefix = options.test_prefix | 39 test_loader.testMethodPrefix = options.test_prefix |
39 return test_loader.loadTestsFromTestCase(au_test.AUTest) | 40 return test_loader.loadTestsFromTestCase(au_test.AUTest) |
40 | 41 |
41 | 42 |
42 def _PregenerateUpdates(options): | 43 def _PregenerateUpdates(options): |
43 """Determines all deltas that will be generated and generates them. | 44 """Determines all deltas that will be generated and generates them. |
44 | 45 |
45 This method effectively pre-generates the dev server cache for all tests. | 46 This method effectively pre-generates the dev server cache for all tests. |
46 | 47 |
47 Args: | 48 Args: |
48 options: options from parsed parser. | 49 options: options from parsed parser. |
49 Returns: | 50 Returns: |
50 Dictionary of Update Identifiers->Relative cache locations. | 51 Dictionary of Update Identifiers->Relative cache locations. |
51 Raises: | 52 Raises: |
52 update_exception.UpdateException if we fail to generate an update. | 53 update_exception.UpdateException if we fail to generate an update. |
53 """ | 54 """ |
54 def _GenerateVMUpdate(target, src, private_key_path): | 55 def _GenerateVMUpdate(target, src, private_key_path, log_file): |
55 """Generates an update using the devserver.""" | 56 """Returns the error code from generating an update using the devserver.""" |
56 command = ['sudo', | 57 command = ['sudo', |
57 'start_devserver', | 58 'start_devserver', |
58 '--pregenerate_update', | 59 '--pregenerate_update', |
59 '--exit', | 60 '--exit', |
60 ] | 61 ] |
61 # Add actual args to command. | 62 # Add actual args to command. |
62 command.append('--image=%s' % cros_lib.ReinterpretPathForChroot(target)) | 63 command.append('--image=%s' % cros_lib.ReinterpretPathForChroot(target)) |
63 if src: command.append('--src_image=%s' % | 64 if src: command.append('--src_image=%s' % |
64 cros_lib.ReinterpretPathForChroot(src)) | 65 cros_lib.ReinterpretPathForChroot(src)) |
65 if options.type == 'vm': command.append('--for_vm') | 66 if options.type == 'vm': command.append('--for_vm') |
66 if private_key_path: | 67 if private_key_path: |
67 command.append('--private_key=%s' % | 68 command.append('--private_key=%s' % |
68 cros_lib.ReinterpretPathForChroot(private_key_path)) | 69 cros_lib.ReinterpretPathForChroot(private_key_path)) |
69 | 70 |
70 return cros_lib.RunCommandCaptureOutput(command, combine_stdout_stderr=True, | 71 return cros_lib.RunCommand(command, enter_chroot=True, print_cmd=True, |
71 enter_chroot=True, | 72 cwd=cros_lib.GetCrosUtilsPath(), |
72 print_cmd=True, | 73 log_to_file=log_file, error_ok=True, |
73 cwd=cros_lib.GetCrosUtilsPath()) | 74 exit_code=True) |
| 75 |
| 76 def _ProcessGeneratorOutputs(log_files, return_codes): |
| 77 """Processes results from the log files of GenerateVMUpdate calls. |
| 78 |
| 79 Returns an array of cache entries from the log files. |
| 80 """ |
| 81 return_array = [] |
| 82 # Looking for this line in the output. |
| 83 key_line_re = re.compile('^PREGENERATED_UPDATE=([\w/.]+)') |
| 84 for log_file, return_code in map(lambda x, y: (x, y), log_files, |
| 85 return_codes): |
| 86 log_file_handle = open(log_file) |
| 87 output = log_file_handle.read() |
| 88 log_file_handle.close() |
| 89 |
| 90 if return_code != 0: |
| 91 cros_lib.Warning(output) |
| 92 raise update_exception.UpdateException(return_code, |
| 93 'Failed to generate update.') |
| 94 else: |
| 95 for line in output.splitlines(): |
| 96 match = key_line_re.search(line) |
| 97 if match: |
| 98 # Convert blah/blah/update.gz -> update/blah/blah. |
| 99 path_to_update_gz = match.group(1).rstrip() |
| 100 (path_to_update_dir, _, _) = path_to_update_gz.rpartition( |
| 101 '/update.gz') |
| 102 return_array.append('/'.join(['update', path_to_update_dir])) |
| 103 |
| 104 assert len(return_array) == len(log_files), 'Return result size mismatch.' |
| 105 return return_array |
74 | 106 |
75 # Use dummy class to mock out updates that would be run as part of a test. | 107 # Use dummy class to mock out updates that would be run as part of a test. |
76 test_suite = _PrepareTestSuite(options, use_dummy_worker=True) | 108 test_suite = _PrepareTestSuite(options, use_dummy_worker=True) |
77 test_result = unittest.TextTestRunner(verbosity=0).run(test_suite) | 109 test_result = unittest.TextTestRunner(verbosity=0).run(test_suite) |
78 if not test_result.wasSuccessful(): | 110 if not test_result.wasSuccessful(): |
79 raise update_exception.UpdateException(1, | 111 raise update_exception.UpdateException(1, |
80 'Error finding updates to generate.') | 112 'Error finding updates to generate.') |
81 | 113 |
82 cros_lib.Info('The following delta updates are required.') | 114 cros_lib.Info('The following delta updates are required.') |
83 update_ids = [] | 115 update_ids = [] |
84 jobs = [] | 116 jobs = [] |
85 args = [] | 117 args = [] |
| 118 log_files = [] |
86 modified_images = set() | 119 modified_images = set() |
87 for target, srcs in dummy_au_worker.DummyAUWorker.delta_list.items(): | 120 for target, srcs in dummy_au_worker.DummyAUWorker.delta_list.items(): |
88 modified_images.add(target) | 121 modified_images.add(target) |
89 for src_key in srcs: | 122 for src_key in srcs: |
| 123 log_file = tempfile.mktemp('GenerateVMUpdate') |
90 (src, _ , key) = src_key.partition('+') | 124 (src, _ , key) = src_key.partition('+') |
91 if src: modified_images.add(src) | 125 if src: modified_images.add(src) |
92 # TODO(sosa): Add private key as part of caching name once devserver can | 126 # TODO(sosa): Add private key as part of caching name once devserver can |
93 # handle it its own cache. | 127 # handle it its own cache. |
94 update_id = dev_server_wrapper.GenerateUpdateId(target, src, key) | 128 update_id = dev_server_wrapper.GenerateUpdateId(target, src, key) |
95 print >> sys.stderr, 'AU: %s' % update_id | 129 print >> sys.stderr, 'AU: %s' % update_id |
96 update_ids.append(update_id) | 130 update_ids.append(update_id) |
97 jobs.append(_GenerateVMUpdate) | 131 jobs.append(_GenerateVMUpdate) |
98 args.append((target, src, key)) | 132 args.append((target, src, key, log_file)) |
| 133 log_files.append(log_file) |
99 | 134 |
100 # Always add the base image path. This is only useful for non-delta updates. | 135 # Always add the base image path. This is only useful for non-delta updates. |
101 modified_images.add(options.base_image) | 136 modified_images.add(options.base_image) |
102 | 137 |
103 # Add public key to all images we are using. | 138 # Add public key to all images we are using. |
104 if options.public_key: | 139 if options.public_key: |
105 cros_lib.Info('Adding public keys to images for testing.') | 140 cros_lib.Info('Adding public keys to images for testing.') |
106 for image in modified_images: | 141 for image in modified_images: |
107 manager = public_key_manager.PublicKeyManager(image, options.public_key) | 142 manager = public_key_manager.PublicKeyManager(image, options.public_key) |
108 manager.AddKeyToImage() | 143 manager.AddKeyToImage() |
109 au_test.AUTest.public_key_managers.append(manager) | 144 au_test.AUTest.public_key_managers.append(manager) |
110 | 145 |
111 raw_results = parallel_test_job.RunParallelJobs(options.jobs, jobs, args, | 146 error_codes = parallel_test_job.RunParallelJobs(options.jobs, jobs, args) |
112 print_status=True) | 147 results = _ProcessGeneratorOutputs(log_files, error_codes) |
113 results = [] | |
114 | |
115 # Looking for this line in the output. | |
116 key_line_re = re.compile('^PREGENERATED_UPDATE=([\w/.]+)') | |
117 for result in raw_results: | |
118 (return_code, output, _) = result | |
119 if return_code != 0: | |
120 cros_lib.Warning(output) | |
121 raise update_exception.UpdateException(return_code, | |
122 'Failed to generate all updates.') | |
123 else: | |
124 for line in output.splitlines(): | |
125 match = key_line_re.search(line) | |
126 if match: | |
127 # Convert blah/blah/update.gz -> update/blah/blah. | |
128 path_to_update_gz = match.group(1).rstrip() | |
129 (path_to_update_dir, _, _) = path_to_update_gz.rpartition( | |
130 '/update.gz') | |
131 results.append('/'.join(['update', path_to_update_dir])) | |
132 break | |
133 | |
134 # Make sure all generation of updates returned cached locations. | |
135 if len(raw_results) != len(results): | |
136 raise update_exception.UpdateException( | |
137 1, 'Insufficient number cache directories returned.') | |
138 | 148 |
139 # Build the dictionary from our id's and returned cache paths. | 149 # Build the dictionary from our id's and returned cache paths. |
140 cache_dictionary = {} | 150 cache_dictionary = {} |
141 for index, id in enumerate(update_ids): | 151 for index, id in enumerate(update_ids): |
142 cache_dictionary[id] = results[index] | 152 cache_dictionary[id] = results[index] |
143 | 153 |
144 return cache_dictionary | 154 return cache_dictionary |
145 | 155 |
146 | 156 |
147 def _RunTestsInParallel(options): | 157 def _RunTestsInParallel(options): |
148 """Runs the tests given by the options in parallel.""" | 158 """Runs the tests given by the options in parallel.""" |
149 threads = [] | 159 threads = [] |
150 args = [] | 160 args = [] |
151 test_suite = _PrepareTestSuite(options) | 161 test_suite = _PrepareTestSuite(options) |
152 for test in test_suite: | 162 for test in test_suite: |
153 test_name = test.id() | 163 test_name = test.id() |
154 test_case = unittest.TestLoader().loadTestsFromName(test_name) | 164 test_case = unittest.TestLoader().loadTestsFromName(test_name) |
155 threads.append(unittest.TextTestRunner(verbosity=2).run) | 165 threads.append(unittest.TextTestRunner(verbosity=2).run) |
156 args.append(test_case) | 166 args.append(test_case) |
157 | 167 |
158 results = parallel_test_job.RunParallelJobs(options.jobs, threads, args, | 168 results = parallel_test_job.RunParallelJobs(options.jobs, threads, args) |
159 print_status=False) | |
160 for test_result in results: | 169 for test_result in results: |
161 if not test_result.wasSuccessful(): | 170 if not test_result.wasSuccessful(): |
162 cros_lib.Die('Test harness was not successful') | 171 cros_lib.Die('Test harness was not successful') |
163 | 172 |
164 | 173 |
165 def _CleanPreviousWork(options): | 174 def _CleanPreviousWork(options): |
166 """Cleans up previous work from the devserver cache and local image cache.""" | 175 """Cleans up previous work from the devserver cache and local image cache.""" |
167 cros_lib.Info('Cleaning up previous work.') | 176 cros_lib.Info('Cleaning up previous work.') |
168 # Wipe devserver cache. | 177 # Wipe devserver cache. |
169 cros_lib.RunCommandCaptureOutput( | 178 cros_lib.RunCommandCaptureOutput( |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
272 if options.public_key: | 281 if options.public_key: |
273 cros_lib.Info('Cleaning up. Removing keys added as part of testing.') | 282 cros_lib.Info('Cleaning up. Removing keys added as part of testing.') |
274 target_directory = os.path.dirname(options.target_image) | 283 target_directory = os.path.dirname(options.target_image) |
275 for key_manager in au_test.AUTest.public_key_managers: | 284 for key_manager in au_test.AUTest.public_key_managers: |
276 if key_manager.image_path.startswith(target_directory): | 285 if key_manager.image_path.startswith(target_directory): |
277 key_manager.RemoveKeyFromImage() | 286 key_manager.RemoveKeyFromImage() |
278 | 287 |
279 | 288 |
280 if __name__ == '__main__': | 289 if __name__ == '__main__': |
281 main() | 290 main() |
OLD | NEW |