Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(229)

Side by Side Diff: bin/cros_au_test_harness.py

Issue 6381013: This change squashes output while pregenerationg updates. (Closed) Base URL: http://git.chromium.org/git/crosutils.git@master
Patch Set: Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698