| 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 683 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 694 self._starting_semaphore = starting_semaphore | 694 self._starting_semaphore = starting_semaphore |
| 695 self._ending_semaphore = ending_semaphore | 695 self._ending_semaphore = ending_semaphore |
| 696 self._output = None | 696 self._output = None |
| 697 self._completed = False | 697 self._completed = False |
| 698 | 698 |
| 699 def run(self): | 699 def run(self): |
| 700 """Thread override. Runs the method specified and sets output.""" | 700 """Thread override. Runs the method specified and sets output.""" |
| 701 try: | 701 try: |
| 702 self._output = self._target(*self._args) | 702 self._output = self._target(*self._args) |
| 703 finally: | 703 finally: |
| 704 # From threading.py to avoid a refcycle. | |
| 705 del self._target, self._args | |
| 706 # Our own clean up. | 704 # Our own clean up. |
| 707 self._Cleanup() | 705 self._Cleanup() |
| 708 self._completed = True | 706 self._completed = True |
| 707 # From threading.py to avoid a refcycle. |
| 708 del self._target, self._args |
| 709 | 709 |
| 710 def GetOutput(self): | 710 def GetOutput(self): |
| 711 """Returns the output of the method run.""" | 711 """Returns the output of the method run.""" |
| 712 assert self._completed, 'GetOutput called before thread was run.' | 712 assert self._completed, 'GetOutput called before thread was run.' |
| 713 return self._output | 713 return self._output |
| 714 | 714 |
| 715 def _Cleanup(self): | 715 def _Cleanup(self): |
| 716 """Releases semaphores for a waiting caller.""" | 716 """Releases semaphores for a waiting caller.""" |
| 717 Info('Completed job %s' % self) |
| 717 self._starting_semaphore.release() | 718 self._starting_semaphore.release() |
| 718 self._ending_semaphore.release() | 719 self._ending_semaphore.release() |
| 719 | 720 |
| 720 def __str__(self): | 721 def __str__(self): |
| 721 return '%s(%s)' % (self._target, self._args) | 722 return '%s(%s)' % (self._target, self._args) |
| 722 | 723 |
| 723 | 724 |
| 724 class DevServerWrapper(threading.Thread): | 725 class DevServerWrapper(threading.Thread): |
| 725 """A Simple wrapper around a devserver instance.""" | 726 """A Simple wrapper around a devserver instance.""" |
| 726 | 727 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 756 | 757 |
| 757 | 758 |
| 758 def _GenerateUpdateId(target, src): | 759 def _GenerateUpdateId(target, src): |
| 759 """Returns a simple representation id of target and src paths.""" | 760 """Returns a simple representation id of target and src paths.""" |
| 760 if src: | 761 if src: |
| 761 return '%s->%s' % (target, src) | 762 return '%s->%s' % (target, src) |
| 762 else: | 763 else: |
| 763 return target | 764 return target |
| 764 | 765 |
| 765 | 766 |
| 766 def _RunParallelJobs(number_of_sumultaneous_jobs, jobs, jobs_args): | 767 def _RunParallelJobs(number_of_sumultaneous_jobs, jobs, jobs_args, print_status)
: |
| 768 |
| 767 """Runs set number of specified jobs in parallel. | 769 """Runs set number of specified jobs in parallel. |
| 768 | 770 |
| 769 Args: | 771 Args: |
| 770 number_of_simultaneous_jobs: Max number of threads to be run in parallel. | 772 number_of_simultaneous_jobs: Max number of threads to be run in parallel. |
| 771 jobs: Array of methods to run. | 773 jobs: Array of methods to run. |
| 772 jobs_args: Array of args associated with method calls. | 774 jobs_args: Array of args associated with method calls. |
| 775 print_status: True if you'd like this to print out .'s as it runs jobs. |
| 773 Returns: | 776 Returns: |
| 774 Returns an array of results corresponding to each thread. | 777 Returns an array of results corresponding to each thread. |
| 775 """ | 778 """ |
| 776 def _TwoTupleize(x, y): | 779 def _TwoTupleize(x, y): |
| 777 return (x, y) | 780 return (x, y) |
| 778 | 781 |
| 779 threads = [] | 782 threads = [] |
| 780 job_start_semaphore = threading.Semaphore(number_of_sumultaneous_jobs) | 783 job_start_semaphore = threading.Semaphore(number_of_sumultaneous_jobs) |
| 781 join_semaphore = threading.Semaphore(0) | 784 join_semaphore = threading.Semaphore(0) |
| 782 assert len(jobs) == len(jobs_args), 'Length of args array is wrong.' | 785 assert len(jobs) == len(jobs_args), 'Length of args array is wrong.' |
| 783 | 786 |
| 784 # Create the parallel jobs. | 787 # Create the parallel jobs. |
| 785 for job, args in map(_TwoTupleize, jobs, jobs_args): | 788 for job, args in map(_TwoTupleize, jobs, jobs_args): |
| 786 thread = ParallelJob(job_start_semaphore, join_semaphore, target=job, | 789 thread = ParallelJob(job_start_semaphore, join_semaphore, target=job, |
| 787 args=args) | 790 args=args) |
| 788 threads.append(thread) | 791 threads.append(thread) |
| 789 | 792 |
| 793 # Cache sudo access. |
| 794 RunCommand(['sudo', 'echo', 'Starting test harness'], |
| 795 print_cmd=False, redirect_stdout=True, redirect_stderr=True) |
| 796 |
| 790 # We use a semaphore to ensure we don't run more jobs that required. | 797 # We use a semaphore to ensure we don't run more jobs that required. |
| 791 # After each thread finishes, it releases (increments semaphore). | 798 # After each thread finishes, it releases (increments semaphore). |
| 792 # Acquire blocks of num jobs reached and continues when a thread finishes. | 799 # Acquire blocks of num jobs reached and continues when a thread finishes. |
| 793 for next_thread in threads: | 800 for next_thread in threads: |
| 794 job_start_semaphore.acquire(blocking=True) | 801 job_start_semaphore.acquire(blocking=True) |
| 795 Info('Starting job %s' % next_thread) | 802 Info('Starting job %s' % next_thread) |
| 796 next_thread.start() | 803 next_thread.start() |
| 797 | 804 |
| 798 # Wait on the rest of the threads to finish. | 805 # Wait on the rest of the threads to finish. |
| 806 Info('Waiting for threads to complete.') |
| 799 for thread in threads: | 807 for thread in threads: |
| 800 join_semaphore.acquire(blocking=True) | 808 while not join_semaphore.acquire(blocking=False): |
| 809 time.sleep(5) |
| 810 if print_status: |
| 811 print >> sys.stderr, '.', |
| 801 | 812 |
| 802 return [thread.GetOutput() for thread in threads] | 813 return [thread.GetOutput() for thread in threads] |
| 803 | 814 |
| 804 | 815 |
| 805 def _PrepareTestSuite(parser, options, test_class): | 816 def _PrepareTestSuite(parser, options, test_class): |
| 806 """Returns a prepared test suite given by the options and test class.""" | 817 """Returns a prepared test suite given by the options and test class.""" |
| 807 test_class.ProcessOptions(parser, options) | 818 test_class.ProcessOptions(parser, options) |
| 808 test_loader = unittest.TestLoader() | 819 test_loader = unittest.TestLoader() |
| 809 test_loader.testMethodPrefix = options.test_prefix | 820 test_loader.testMethodPrefix = options.test_prefix |
| 810 return test_loader.loadTestsFromTestCase(test_class) | 821 return test_loader.loadTestsFromTestCase(test_class) |
| 811 | 822 |
| 812 | 823 |
| 813 def _PregenerateUpdates(parser, options): | 824 def _PregenerateUpdates(parser, options): |
| 814 """Determines all deltas that will be generated and generates them. | 825 """Determines all deltas that will be generated and generates them. |
| 815 | 826 |
| 816 This method effectively pre-generates the dev server cache for all tests. | 827 This method effectively pre-generates the dev server cache for all tests. |
| 817 | 828 |
| 818 Args: | 829 Args: |
| 819 parser: parser from main. | 830 parser: parser from main. |
| 820 options: options from parsed parser. | 831 options: options from parsed parser. |
| 821 Returns: | 832 Returns: |
| 822 Dictionary of Update Identifiers->Relative cache locations. | 833 Dictionary of Update Identifiers->Relative cache locations. |
| 834 Raises: |
| 835 UpdateException if we fail to generate an update. |
| 823 """ | 836 """ |
| 824 def _GenerateVMUpdate(target, src): | 837 def _GenerateVMUpdate(target, src): |
| 825 """Generates an update using the devserver.""" | 838 """Generates an update using the devserver.""" |
| 826 target = ReinterpretPathForChroot(target) | 839 target = ReinterpretPathForChroot(target) |
| 827 if src: | 840 if src: |
| 828 src = ReinterpretPathForChroot(src) | 841 src = ReinterpretPathForChroot(src) |
| 829 | 842 |
| 830 return RunCommand(['sudo', | 843 return RunCommandCaptureOutput(['sudo', |
| 831 './start_devserver', | 844 './start_devserver', |
| 832 '--pregenerate_update', | 845 '--pregenerate_update', |
| 833 '--exit', | 846 '--exit', |
| 834 '--image=%s' % target, | 847 '--image=%s' % target, |
| 835 '--src_image=%s' % src, | 848 '--src_image=%s' % src, |
| 836 '--for_vm', | 849 '--for_vm', |
| 837 ], redirect_stdout=True, enter_chroot=True, | 850 ], combine_stdout_stderr=True, |
| 838 print_cmd=False) | 851 enter_chroot=True, |
| 852 print_cmd=False) |
| 839 | 853 |
| 840 # Get the list of deltas by mocking out update method in test class. | 854 # Get the list of deltas by mocking out update method in test class. |
| 841 test_suite = _PrepareTestSuite(parser, options, GenerateVirtualAUDeltasTest) | 855 test_suite = _PrepareTestSuite(parser, options, GenerateVirtualAUDeltasTest) |
| 842 test_result = unittest.TextTestRunner(verbosity=0).run(test_suite) | 856 test_result = unittest.TextTestRunner(verbosity=0).run(test_suite) |
| 843 | 857 |
| 844 Info('The following delta updates are required.') | 858 Info('The following delta updates are required.') |
| 845 update_ids = [] | 859 update_ids = [] |
| 846 jobs = [] | 860 jobs = [] |
| 847 args = [] | 861 args = [] |
| 848 for target, srcs in GenerateVirtualAUDeltasTest.delta_list.items(): | 862 for target, srcs in GenerateVirtualAUDeltasTest.delta_list.items(): |
| 849 for src in srcs: | 863 for src in srcs: |
| 850 update_id = _GenerateUpdateId(target=target, src=src) | 864 update_id = _GenerateUpdateId(target=target, src=src) |
| 851 print >> sys.stderr, 'AU: %s' % update_id | 865 print >> sys.stderr, 'AU: %s' % update_id |
| 852 update_ids.append(update_id) | 866 update_ids.append(update_id) |
| 853 jobs.append(_GenerateVMUpdate) | 867 jobs.append(_GenerateVMUpdate) |
| 854 args.append((target, src)) | 868 args.append((target, src)) |
| 855 | 869 |
| 856 raw_results = _RunParallelJobs(options.jobs, jobs, args) | 870 raw_results = _RunParallelJobs(options.jobs, jobs, args, print_status=True) |
| 857 results = [] | 871 results = [] |
| 858 | 872 |
| 859 # Parse the output. | 873 # Looking for this line in the output. |
| 860 key_line_re = re.compile('^PREGENERATED_UPDATE=([\w/.]+)') | 874 key_line_re = re.compile('^PREGENERATED_UPDATE=([\w/.]+)') |
| 861 for result in raw_results: | 875 for result in raw_results: |
| 862 for line in result.splitlines(): | 876 (return_code, output, _) = result |
| 863 match = key_line_re.search(line) | 877 if return_code != 0: |
| 864 if match: | 878 Warning(output) |
| 865 # Convert blah/blah/update.gz -> update/blah/blah. | 879 raise UpdateException(return_code, 'Failed to generate all updates.') |
| 866 path_to_update_gz = match.group(1).rstrip() | 880 else: |
| 867 (path_to_update_dir, _, _) = path_to_update_gz.rpartition('/update.gz') | 881 for line in output.splitlines(): |
| 868 results.append('/'.join(['update', path_to_update_dir])) | 882 match = key_line_re.search(line) |
| 869 break | 883 if match: |
| 884 # Convert blah/blah/update.gz -> update/blah/blah. |
| 885 path_to_update_gz = match.group(1).rstrip() |
| 886 (path_to_update_dir, _, _) = path_to_update_gz.rpartition( |
| 887 '/update.gz') |
| 888 results.append('/'.join(['update', path_to_update_dir])) |
| 889 break |
| 870 | 890 |
| 871 assert len(raw_results) == len(results), \ | 891 # Make sure all generation of updates returned cached locations. |
| 872 'Insufficient number cache directories returned.' | 892 if len(raw_results) != len(results): |
| 893 raise UpdateException(1, 'Insufficient number cache directories returned.') |
| 873 | 894 |
| 874 # Build the dictionary from our id's and returned cache paths. | 895 # Build the dictionary from our id's and returned cache paths. |
| 875 cache_dictionary = {} | 896 cache_dictionary = {} |
| 876 for index, id in enumerate(update_ids): | 897 for index, id in enumerate(update_ids): |
| 877 cache_dictionary[id] = results[index] | 898 cache_dictionary[id] = results[index] |
| 878 | 899 |
| 879 return cache_dictionary | 900 return cache_dictionary |
| 880 | 901 |
| 881 | 902 |
| 882 def _RunTestsInParallel(parser, options, test_class): | 903 def _RunTestsInParallel(parser, options, test_class): |
| 883 """Runs the tests given by the options and test_class in parallel.""" | 904 """Runs the tests given by the options and test_class in parallel.""" |
| 884 threads = [] | 905 threads = [] |
| 885 args = [] | 906 args = [] |
| 886 test_suite = _PrepareTestSuite(parser, options, test_class) | 907 test_suite = _PrepareTestSuite(parser, options, test_class) |
| 887 for test in test_suite: | 908 for test in test_suite: |
| 888 test_name = test.id() | 909 test_name = test.id() |
| 889 test_case = unittest.TestLoader().loadTestsFromName(test_name) | 910 test_case = unittest.TestLoader().loadTestsFromName(test_name) |
| 890 threads.append(unittest.TextTestRunner().run) | 911 threads.append(unittest.TextTestRunner().run) |
| 891 args.append(test_case) | 912 args.append(test_case) |
| 892 | 913 |
| 893 results = _RunParallelJobs(options.jobs, threads, args) | 914 results = _RunParallelJobs(options.jobs, threads, args, print_status=False) |
| 894 if not (test_result.wasSuccessful() for test_result in results): | 915 if not (test_result.wasSuccessful() for test_result in results): |
| 895 Die('Test harness was not successful') | 916 Die('Test harness was not successful') |
| 896 | 917 |
| 897 | 918 |
| 898 def main(): | 919 def main(): |
| 899 parser = optparse.OptionParser() | 920 parser = optparse.OptionParser() |
| 900 parser.add_option('-b', '--base_image', | 921 parser.add_option('-b', '--base_image', |
| 901 help='path to the base image.') | 922 help='path to the base image.') |
| 902 parser.add_option('-r', '--board', | 923 parser.add_option('-r', '--board', |
| 903 help='board for the images.') | 924 help='board for the images.') |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 946 else: | 967 else: |
| 947 dev_server_cache = None | 968 dev_server_cache = None |
| 948 test_suite = _PrepareTestSuite(parser, options, test_class) | 969 test_suite = _PrepareTestSuite(parser, options, test_class) |
| 949 test_result = unittest.TextTestRunner(verbosity=2).run(test_suite) | 970 test_result = unittest.TextTestRunner(verbosity=2).run(test_suite) |
| 950 if not test_result.wasSuccessful(): | 971 if not test_result.wasSuccessful(): |
| 951 Die('Test harness was not successful.') | 972 Die('Test harness was not successful.') |
| 952 | 973 |
| 953 | 974 |
| 954 if __name__ == '__main__': | 975 if __name__ == '__main__': |
| 955 main() | 976 main() |
| OLD | NEW |