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

Side by Side Diff: bin/cros_au_test_harness.py

Issue 6277015: Passes cache location to tests and runs the tests in parallel. (Closed) Base URL: http://git.chromium.org/git/crosutils.git@master
Patch Set: Fix sudo issues again. 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 | bin/cros_run_vm_update » ('j') | 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.
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 subprocess
18 import sys 19 import sys
19 import threading 20 import threading
20 import time 21 import time
21 import unittest 22 import unittest
22 import urllib 23 import urllib
23 24
24 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) 25 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib'))
25 from cros_build_lib import Die 26 from cros_build_lib import Die
27 from cros_build_lib import GetIPAddress
26 from cros_build_lib import Info 28 from cros_build_lib import Info
27 from cros_build_lib import ReinterpretPathForChroot 29 from cros_build_lib import ReinterpretPathForChroot
28 from cros_build_lib import RunCommand 30 from cros_build_lib import RunCommand
29 from cros_build_lib import RunCommandCaptureOutput 31 from cros_build_lib import RunCommandCaptureOutput
30 from cros_build_lib import Warning 32 from cros_build_lib import Warning
31 33
32 import cros_test_proxy 34 import cros_test_proxy
33 35
36 global dev_server_cache
37
34 38
35 class UpdateException(Exception): 39 class UpdateException(Exception):
36 """Exception thrown when _UpdateImage or _UpdateUsingPayload fail""" 40 """Exception thrown when _UpdateImage or _UpdateUsingPayload fail"""
37 def __init__(self, code, stdout): 41 def __init__(self, code, stdout):
38 self.code = code 42 self.code = code
39 self.stdout = stdout 43 self.stdout = stdout
40 44
41 45
42 class AUTest(object): 46 class AUTest(object):
43 """Abstract interface that defines an Auto Update test.""" 47 """Abstract interface that defines an Auto Update test."""
(...skipping 418 matching lines...) Expand 10 before | Expand all | Expand 10 after
462 output = RunCommand([ 466 output = RunCommand([
463 '%s/run_remote_tests.sh' % self.crosutils, 467 '%s/run_remote_tests.sh' % self.crosutils,
464 '--remote=%s' % self.remote, 468 '--remote=%s' % self.remote,
465 self.verify_suite, 469 self.verify_suite,
466 ], error_ok=True, enter_chroot=False, redirect_stdout=True) 470 ], error_ok=True, enter_chroot=False, redirect_stdout=True)
467 return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass) 471 return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass)
468 472
469 473
470 class VirtualAUTest(unittest.TestCase, AUTest): 474 class VirtualAUTest(unittest.TestCase, AUTest):
471 """Test harness for updating virtual machines.""" 475 """Test harness for updating virtual machines."""
472 vm_image_path = None
473 476
474 # VM Constants. 477 # VM Constants.
475 _FULL_VDISK_SIZE = 6072 478 _FULL_VDISK_SIZE = 6072
476 _FULL_STATEFULFS_SIZE = 3074 479 _FULL_STATEFULFS_SIZE = 3074
477 _KVM_PID_FILE = '/tmp/harness_pid' 480
481 # Class variables used to acquire individual VM variables per test.
482 _vm_lock = threading.Lock()
483 _next_port = 9222
478 484
479 def _KillExistingVM(self, pid_file): 485 def _KillExistingVM(self, pid_file):
480 if os.path.exists(pid_file): 486 if os.path.exists(pid_file):
481 Warning('Existing %s found. Deleting and killing process' % 487 Warning('Existing %s found. Deleting and killing process' %
482 pid_file) 488 pid_file)
483 RunCommand(['./cros_stop_vm', '--kvm_pid=%s' % pid_file], 489 RunCommand(['./cros_stop_vm', '--kvm_pid=%s' % pid_file],
484 cwd=self.crosutilsbin) 490 cwd=self.crosutilsbin)
485 491
486 assert not os.path.exists(pid_file) 492 assert not os.path.exists(pid_file)
487 493
494 def _AcquireUniquePortAndPidFile(self):
495 """Acquires unique ssh port and pid file for VM."""
496 with VirtualAUTest._vm_lock:
497 self._ssh_port = VirtualAUTest._next_port
498 self._kvm_pid_file = '/tmp/kvm.%d' % self._ssh_port
499 VirtualAUTest._next_port += 1
500
488 def setUp(self): 501 def setUp(self):
489 """Unit test overriden method. Is called before every test.""" 502 """Unit test overriden method. Is called before every test."""
490 AUTest.setUp(self) 503 AUTest.setUp(self)
491 self._KillExistingVM(self._KVM_PID_FILE) 504 self.vm_image_path = None
505 self._AcquireUniquePortAndPidFile()
506 self._KillExistingVM(self._kvm_pid_file)
507
508 def tearDown(self):
509 self._KillExistingVM(self._kvm_pid_file)
492 510
493 @classmethod 511 @classmethod
494 def ProcessOptions(cls, parser, options): 512 def ProcessOptions(cls, parser, options):
495 """Processes vm-specific options.""" 513 """Processes vm-specific options."""
496 AUTest.ProcessOptions(parser, options) 514 AUTest.ProcessOptions(parser, options)
497 cls.board = options.board 515 cls.board = options.board
498 516
499 # Communicate flags to tests. 517 # Communicate flags to tests.
500 cls.graphics_flag = '' 518 cls.graphics_flag = ''
501 if options.no_graphics: cls.graphics_flag = '--no_graphics' 519 if options.no_graphics: cls.graphics_flag = '--no_graphics'
(...skipping 18 matching lines...) Expand all
520 os.path.dirname(image_path)), 538 os.path.dirname(image_path)),
521 '--vdisk_size=%s' % self._FULL_VDISK_SIZE, 539 '--vdisk_size=%s' % self._FULL_VDISK_SIZE,
522 '--statefulfs_size=%s' % self._FULL_STATEFULFS_SIZE, 540 '--statefulfs_size=%s' % self._FULL_STATEFULFS_SIZE,
523 '--board=%s' % self.board, 541 '--board=%s' % self.board,
524 '--test_image'], enter_chroot=True) 542 '--test_image'], enter_chroot=True)
525 543
526 Info('Using %s as base' % self.vm_image_path) 544 Info('Using %s as base' % self.vm_image_path)
527 self.assertTrue(os.path.exists(self.vm_image_path)) 545 self.assertTrue(os.path.exists(self.vm_image_path))
528 546
529 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', 547 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old',
530 proxy_port=None): 548 proxy_port=''):
531 """Updates VM image with image_path.""" 549 """Updates VM image with image_path."""
532 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) 550 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
533 if src_image_path and self._first_update: 551 if src_image_path and self._first_update:
534 src_image_path = self.vm_image_path 552 src_image_path = self.vm_image_path
535 self._first_update = False 553 self._first_update = False
536 554
537 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin, 555 # Check image payload cache first.
538 '--update_image_path=%s' % image_path, 556 update_id = _GenerateUpdateId(target=image_path, src=src_image_path)
539 '--vm_image_path=%s' % self.vm_image_path, 557 cache_path = dev_server_cache[update_id]
540 '--snapshot', 558 if cache_path:
541 self.graphics_flag, 559 Info('Using cache %s' % cache_path)
542 '--persist', 560 update_url = DevServerWrapper.GetDevServerURL(proxy_port, cache_path)
543 '--kvm_pid=%s' % self._KVM_PID_FILE, 561 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin,
544 stateful_change_flag, 562 '--vm_image_path=%s' % self.vm_image_path,
545 '--src_image=%s' % src_image_path, 563 '--snapshot',
546 ] 564 self.graphics_flag,
547 565 '--persist',
548 if proxy_port: 566 '--kvm_pid=%s' % self._kvm_pid_file,
549 cmd.append('--proxy_port=%s' % proxy_port) 567 '--ssh_port=%s' % self._ssh_port,
568 stateful_change_flag,
569 '--update_url=%s' % update_url,
570 ]
571 else:
572 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin,
573 '--update_image_path=%s' % image_path,
574 '--vm_image_path=%s' % self.vm_image_path,
575 '--snapshot',
576 self.graphics_flag,
577 '--persist',
578 '--kvm_pid=%s' % self._kvm_pid_file,
579 '--ssh_port=%s' % self._ssh_port,
580 stateful_change_flag,
581 '--src_image=%s' % src_image_path,
582 '--proxy_port=%s' % proxy_port
583 ]
550 584
551 if self.verbose: 585 if self.verbose:
552 try: 586 try:
553 RunCommand(cmd) 587 RunCommand(cmd)
554 except Exception, e: 588 except Exception, e:
555 raise UpdateException(1, e.message) 589 raise UpdateException(1, e.message)
556 else: 590 else:
557 (code, stdout, stderr) = RunCommandCaptureOutput(cmd) 591 (code, stdout, stderr) = RunCommandCaptureOutput(cmd)
558 if code != 0: 592 if code != 0:
559 raise UpdateException(code, stdout) 593 raise UpdateException(code, stdout)
560 594
561 def _UpdateUsingPayload(self, update_path, stateful_change='old', 595 def _UpdateUsingPayload(self, update_path, stateful_change='old',
562 proxy_port=None): 596 proxy_port=None):
563 """Updates a vm image using cros_run_vm_update.""" 597 """Updates a vm image using cros_run_vm_update."""
564 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) 598 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
565 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin, 599 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin,
566 '--payload=%s' % update_path, 600 '--payload=%s' % update_path,
567 '--vm_image_path=%s' % self.vm_image_path, 601 '--vm_image_path=%s' % self.vm_image_path,
568 '--snapshot', 602 '--snapshot',
569 self.graphics_flag, 603 self.graphics_flag,
570 '--persist', 604 '--persist',
571 '--kvm_pid=%s' % self._KVM_PID_FILE, 605 '--kvm_pid=%s' % self._kvm_pid_file,
606 '--ssh_port=%s' % self._ssh_port,
572 stateful_change_flag, 607 stateful_change_flag,
573 ] 608 ]
574 609
575 if proxy_port: 610 if proxy_port:
576 cmd.append('--proxy_port=%s' % proxy_port) 611 cmd.append('--proxy_port=%s' % proxy_port)
577 612
578 if self.verbose: 613 if self.verbose:
579 try: 614 try:
580 RunCommand(cmd) 615 RunCommand(cmd)
581 except Exception, e: 616 except Exception, e:
582 raise UpdateException(1, e.message) 617 raise UpdateException(1, e.message)
583 else: 618 else:
584 (code, stdout, stderr) = RunCommandCaptureOutput(cmd) 619 (code, stdout, stderr) = RunCommandCaptureOutput(cmd)
585 if code != 0: 620 if code != 0:
586 raise UpdateException(code, stdout) 621 raise UpdateException(code, stdout)
587 622
588 def VerifyImage(self, percent_required_to_pass): 623 def VerifyImage(self, percent_required_to_pass):
589 """Runs vm smoke suite to verify image.""" 624 """Runs vm smoke suite to verify image."""
590 # image_to_live already verifies lsb-release matching. This is just 625 # image_to_live already verifies lsb-release matching. This is just
591 # for additional steps. 626 # for additional steps.
592 627
593 commandWithArgs = ['%s/cros_run_vm_test' % self.crosutilsbin, 628 commandWithArgs = ['%s/cros_run_vm_test' % self.crosutilsbin,
594 '--image_path=%s' % self.vm_image_path, 629 '--image_path=%s' % self.vm_image_path,
595 '--snapshot', 630 '--snapshot',
596 '--persist', 631 '--persist',
597 '--kvm_pid=%s' % self._KVM_PID_FILE, 632 '--kvm_pid=%s' % self._kvm_pid_file,
633 '--ssh_port=%s' % self._ssh_port,
598 self.verify_suite, 634 self.verify_suite,
599 ] 635 ]
600 636
601 if self.graphics_flag: 637 if self.graphics_flag:
602 commandWithArgs.append(self.graphics_flag) 638 commandWithArgs.append(self.graphics_flag)
603 639
604 output = RunCommand(commandWithArgs, error_ok=True, enter_chroot=False, 640 output = RunCommand(commandWithArgs, error_ok=True, enter_chroot=False,
605 redirect_stdout=True) 641 redirect_stdout=True)
606 return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass) 642 return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass)
607 643
608 644
609 class GenerateVirtualAUDeltasTest(VirtualAUTest): 645 class GenerateVirtualAUDeltasTest(VirtualAUTest):
610 """Class the overrides VirtualAUTest and stores deltas we will generate.""" 646 """Class the overrides VirtualAUTest and stores deltas we will generate."""
611 delta_list = {} 647 delta_list = {}
612 648
613 def setUp(self): 649 def setUp(self):
614 AUTest.setUp(self) 650 AUTest.setUp(self)
615 651
652 def tearDown(self):
653 pass
654
616 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', 655 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old',
617 proxy_port=None): 656 proxy_port=None):
618 if src_image_path and self._first_update: 657 if src_image_path and self._first_update:
619 src_image_path = self.vm_image_path 658 src_image_path = self.vm_image_path
620 self._first_update = False 659 self._first_update = False
621 660
622 image_path = ReinterpretPathForChroot(image_path)
623 if src_image_path:
624 src_image_path = ReinterpretPathForChroot(src_image_path)
625 if not self.delta_list.has_key(image_path): 661 if not self.delta_list.has_key(image_path):
626 self.delta_list[image_path] = set([src_image_path]) 662 self.delta_list[image_path] = set([src_image_path])
627 else: 663 else:
628 self.delta_list[image_path].add(src_image_path) 664 self.delta_list[image_path].add(src_image_path)
629 665
630 def AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg): 666 def AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg):
631 pass 667 pass
632 668
633 def VerifyImage(self, percent_required_to_pass): 669 def VerifyImage(self, percent_required_to_pass):
634 pass 670 pass
635 671
636 672
637 class ParallelJob(threading.Thread): 673 class ParallelJob(threading.Thread):
638 """Small wrapper for threading.Thread that releases a semaphore on exit.""" 674 """Small wrapper for threading. Thread that releases a semaphores on exit."""
639 def __init__(self, semaphore, target, args): 675
676 def __init__(self, starting_semaphore, ending_semaphore, target, args):
677 """Initializes an instance of a job.
678
679 Args:
680 starting_semaphore: Semaphore used by caller to wait on such that
681 there isn't more than a certain number of threads running. Should
682 be initialized to a value for the number of threads wanting to be run
683 at a time.
684 ending_semaphore: Semaphore is released every time a job ends. Should be
685 initialized to 0 before starting first job. Should be acquired once for
686 each job. Threading.Thread.join() has a bug where if the run function
687 terminates too quickly join() will hang forever.
688 target: The func to run.
689 args: Args to pass to the fun.
690 """
640 threading.Thread.__init__(self, target=target, args=args) 691 threading.Thread.__init__(self, target=target, args=args)
641 self._target = target 692 self._target = target
642 self._args = args 693 self._args = args
643 self._semaphore = semaphore 694 self._starting_semaphore = starting_semaphore
695 self._ending_semaphore = ending_semaphore
644 self._output = None 696 self._output = None
645 self._completed = False 697 self._completed = False
646 698
647 def run(self): 699 def run(self):
700 """Thread override. Runs the method specified and sets output."""
648 try: 701 try:
649 threading.Thread.run(self) 702 self._output = self._target(*self._args)
650 finally: 703 finally:
704 # From threading.py to avoid a refcycle.
705 del self._target, self._args
706 # Our own clean up.
651 self._Cleanup() 707 self._Cleanup()
652 self._completed = True 708 self._completed = True
653 709
654 def GetOutput(self): 710 def GetOutput(self):
711 """Returns the output of the method run."""
655 assert self._completed, 'GetOutput called before thread was run.' 712 assert self._completed, 'GetOutput called before thread was run.'
656 return self._output 713 return self._output
657 714
658 def _Cleanup(self): 715 def _Cleanup(self):
659 self._semaphore.release() 716 """Releases semaphores for a waiting caller."""
717 self._starting_semaphore.release()
718 self._ending_semaphore.release()
660 719
661 def __str__(self): 720 def __str__(self):
662 return '%s(%s)' % (self._target, self._args) 721 return '%s(%s)' % (self._target, self._args)
663 722
664 723
724 class DevServerWrapper(threading.Thread):
725 """A Simple wrapper around a devserver instance."""
726
727 def __init__(self):
728 self.proc = None
729 threading.Thread.__init__(self)
730
731 def run(self):
732 # Kill previous running instance of devserver if it exists.
733 RunCommand(['sudo', 'pkill', '-f', 'devserver.py'], error_ok=True,
734 print_cmd=False)
735 RunCommand(['sudo',
dgarrett 2011/01/26 19:19:16 Should this also be error_ok, to prevent nasty log
sosa 2011/01/26 19:22:18 I thought maybe too ... but it seems that cherrypy
736 './start_devserver',
737 '--archive_dir=./static',
738 '--client_prefix=ChromeOSUpdateEngine',
739 '--production',
740 ], enter_chroot=True, print_cmd=False)
741
742 def Stop(self):
743 """Kills the devserver instance."""
744 RunCommand(['sudo', 'pkill', '-f', 'devserver.py'], error_ok=True,
745 print_cmd=False)
746
747 @classmethod
748 def GetDevServerURL(cls, port, sub_dir):
749 """Returns the dev server url for a given port and sub directory."""
750 ip_addr = GetIPAddress()
751 if not port: port = 8080
752 url = 'http://%(ip)s:%(port)s/%(dir)s' % {'ip': ip_addr,
753 'port': str(port),
754 'dir': sub_dir}
755 return url
756
757
758 def _GenerateUpdateId(target, src):
759 """Returns a simple representation id of target and src paths."""
760 if src:
761 return '%s->%s' % (target, src)
762 else:
763 return target
764
765
665 def _RunParallelJobs(number_of_sumultaneous_jobs, jobs, jobs_args): 766 def _RunParallelJobs(number_of_sumultaneous_jobs, jobs, jobs_args):
666 """Runs set number of specified jobs in parallel. 767 """Runs set number of specified jobs in parallel.
667 768
668 Args: 769 Args:
669 number_of_simultaneous_jobs: Max number of threads to be run in parallel. 770 number_of_simultaneous_jobs: Max number of threads to be run in parallel.
670 jobs: Array of methods to run. 771 jobs: Array of methods to run.
671 jobs_args: Array of args associated with method calls. 772 jobs_args: Array of args associated with method calls.
672 Returns: 773 Returns:
673 Returns an array of results corresponding to each thread. 774 Returns an array of results corresponding to each thread.
674 """ 775 """
675 def _TwoTupleize(x, y): 776 def _TwoTupleize(x, y):
676 return (x, y) 777 return (x, y)
677 778
678 threads = [] 779 threads = []
679 job_pool_semaphore = threading.Semaphore(number_of_sumultaneous_jobs) 780 job_start_semaphore = threading.Semaphore(number_of_sumultaneous_jobs)
781 join_semaphore = threading.Semaphore(0)
680 assert len(jobs) == len(jobs_args), 'Length of args array is wrong.' 782 assert len(jobs) == len(jobs_args), 'Length of args array is wrong.'
681 783
682 # Create the parallel jobs. 784 # Create the parallel jobs.
683 for job, args in map(_TwoTupleize, jobs, jobs_args): 785 for job, args in map(_TwoTupleize, jobs, jobs_args):
684 thread = ParallelJob(job_pool_semaphore, target=job, args=args) 786 thread = ParallelJob(job_start_semaphore, join_semaphore, target=job,
787 args=args)
685 threads.append(thread) 788 threads.append(thread)
686 789
687 # We use a semaphore to ensure we don't run more jobs that required. 790 # We use a semaphore to ensure we don't run more jobs that required.
688 # After each thread finishes, it releases (increments semaphore). 791 # After each thread finishes, it releases (increments semaphore).
689 # Acquire blocks of num jobs reached and continues when a thread finishes. 792 # Acquire blocks of num jobs reached and continues when a thread finishes.
690 for next_thread in threads: 793 for next_thread in threads:
691 job_pool_semaphore.acquire(blocking=True) 794 job_start_semaphore.acquire(blocking=True)
692 Info('Starting %s' % next_thread) 795 Info('Starting job %s' % next_thread)
693 next_thread.start() 796 next_thread.start()
694 797
695 # Wait on the rest of the threads to finish. 798 # Wait on the rest of the threads to finish.
696 for thread in threads: 799 for thread in threads:
697 thread.join() 800 join_semaphore.acquire(blocking=True)
698 801
699 return [thread.GetOutput() for thread in threads] 802 return [thread.GetOutput() for thread in threads]
700 803
701 804
805 def _PrepareTestSuite(parser, options, test_class):
806 """Returns a prepared test suite given by the options and test class."""
807 test_class.ProcessOptions(parser, options)
808 test_loader = unittest.TestLoader()
809 test_loader.testMethodPrefix = options.test_prefix
810 return test_loader.loadTestsFromTestCase(test_class)
811
812
702 def _PregenerateUpdates(parser, options): 813 def _PregenerateUpdates(parser, options):
703 """Determines all deltas that will be generated and generates them. 814 """Determines all deltas that will be generated and generates them.
704 815
705 This method effectively pre-generates the dev server cache for all tests. 816 This method effectively pre-generates the dev server cache for all tests.
706 817
707 Args: 818 Args:
708 parser: parser from main. 819 parser: parser from main.
709 options: options from parsed parser. 820 options: options from parsed parser.
710 Returns: 821 Returns:
711 Array of output from generating updates. 822 Dictionary of Update Identifiers->Relative cache locations.
712 """ 823 """
713 def _GenerateVMUpdate(target, src): 824 def _GenerateVMUpdate(target, src):
714 """Generates an update using the devserver.""" 825 """Generates an update using the devserver."""
715 RunCommand(['sudo', 826 target = ReinterpretPathForChroot(target)
716 './start_devserver', 827 if src:
717 '--pregenerate_update', 828 src = ReinterpretPathForChroot(src)
718 '--exit', 829
719 '--image=%s' % target, 830 return RunCommand(['sudo',
720 '--src_image=%s' % src, 831 './start_devserver',
721 '--for_vm' 832 '--pregenerate_update',
722 ], enter_chroot=True) 833 '--exit',
834 '--image=%s' % target,
835 '--src_image=%s' % src,
836 '--for_vm',
837 ], redirect_stdout=True, enter_chroot=True,
838 print_cmd=False)
723 839
724 # Get the list of deltas by mocking out update method in test class. 840 # Get the list of deltas by mocking out update method in test class.
725 GenerateVirtualAUDeltasTest.ProcessOptions(parser, options) 841 test_suite = _PrepareTestSuite(parser, options, GenerateVirtualAUDeltasTest)
726 test_loader = unittest.TestLoader()
727 test_loader.testMethodPrefix = options.test_prefix
728 test_suite = test_loader.loadTestsFromTestCase(GenerateVirtualAUDeltasTest)
729 test_result = unittest.TextTestRunner(verbosity=0).run(test_suite) 842 test_result = unittest.TextTestRunner(verbosity=0).run(test_suite)
730 843
731 Info('The following delta updates are required.') 844 Info('The following delta updates are required.')
845 update_ids = []
732 jobs = [] 846 jobs = []
733 args = [] 847 args = []
734 for target, srcs in GenerateVirtualAUDeltasTest.delta_list.items(): 848 for target, srcs in GenerateVirtualAUDeltasTest.delta_list.items():
735 for src in srcs: 849 for src in srcs:
736 if src: 850 update_id = _GenerateUpdateId(target=target, src=src)
737 print >> sys.stderr, 'DELTA AU %s -> %s' % (src, target) 851 print >> sys.stderr, 'AU: %s' % update_id
738 else: 852 update_ids.append(update_id)
739 print >> sys.stderr, 'FULL AU %s' % target
740
741 jobs.append(_GenerateVMUpdate) 853 jobs.append(_GenerateVMUpdate)
742 args.append((target, src)) 854 args.append((target, src))
743 855
744 results = _RunParallelJobs(options.jobs, jobs, args) 856 raw_results = _RunParallelJobs(options.jobs, jobs, args)
745 return results 857 results = []
858
859 # Parse the output.
860 key_line_re = re.compile('^PREGENERATED_UPDATE=([\w/.]+)')
861 for result in raw_results:
862 for line in result.splitlines():
863 match = key_line_re.search(line)
864 if match:
865 # Convert blah/blah/update.gz -> update/blah/blah.
866 path_to_update_gz = match.group(1).rstrip()
867 (path_to_update_dir, _, _) = path_to_update_gz.rpartition('/update.gz')
868 results.append('/'.join(['update', path_to_update_dir]))
869 break
870
871 assert len(raw_results) == len(results), \
872 'Insufficient number cache directories returned.'
873
874 # Build the dictionary from our id's and returned cache paths.
875 cache_dictionary = {}
876 for index, id in enumerate(update_ids):
877 cache_dictionary[id] = results[index]
878
879 return cache_dictionary
880
881
882 def _RunTestsInParallel(parser, options, test_class):
883 """Runs the tests given by the options and test_class in parallel."""
884 threads = []
885 args = []
886 test_suite = _PrepareTestSuite(parser, options, test_class)
887 for test in test_suite:
888 test_name = test.id()
889 test_case = unittest.TestLoader().loadTestsFromName(test_name)
890 threads.append(unittest.TextTestRunner().run)
891 args.append(test_case)
892
893 results = _RunParallelJobs(options.jobs, threads, args)
894 if not (test_result.wasSuccessful() for test_result in results):
895 Die('Test harness was not successful')
746 896
747 897
748 def main(): 898 def main():
749 parser = optparse.OptionParser() 899 parser = optparse.OptionParser()
750 parser.add_option('-b', '--base_image', 900 parser.add_option('-b', '--base_image',
751 help='path to the base image.') 901 help='path to the base image.')
752 parser.add_option('-r', '--board', 902 parser.add_option('-r', '--board',
753 help='board for the images.') 903 help='board for the images.')
754 parser.add_option('--no_delta', action='store_false', default=True, 904 parser.add_option('--no_delta', action='store_false', default=True,
755 dest='delta', 905 dest='delta',
(...skipping 14 matching lines...) Expand all
770 parser.add_option('-p', '--type', default='vm', 920 parser.add_option('-p', '--type', default='vm',
771 help='type of test to run: [vm, real]. Default: vm.') 921 help='type of test to run: [vm, real]. Default: vm.')
772 parser.add_option('--verbose', default=True, action='store_true', 922 parser.add_option('--verbose', default=True, action='store_true',
773 help='Print out rather than capture output as much as ' 923 help='Print out rather than capture output as much as '
774 'possible.') 924 'possible.')
775 (options, leftover_args) = parser.parse_args() 925 (options, leftover_args) = parser.parse_args()
776 926
777 if leftover_args: 927 if leftover_args:
778 parser.error('Found extra options we do not support: %s' % leftover_args) 928 parser.error('Found extra options we do not support: %s' % leftover_args)
779 929
930 # Figure out the test_class.
780 if options.type == 'vm': test_class = VirtualAUTest 931 if options.type == 'vm': test_class = VirtualAUTest
781 elif options.type == 'real': test_class = RealAUTest 932 elif options.type == 'real': test_class = RealAUTest
782 else: parser.error('Could not parse harness type %s.' % options.type) 933 else: parser.error('Could not parse harness type %s.' % options.type)
783 934
784 # TODO(sosa): Caching doesn't really make sense on non-vm images (yet). 935 # TODO(sosa): Caching doesn't really make sense on non-vm images (yet).
785 if options.type == 'vm': 936 global dev_server_cache
786 _PregenerateUpdates(parser, options) 937 if options.type == 'vm' and options.jobs > 1:
938 dev_server_cache = _PregenerateUpdates(parser, options)
939 my_server = DevServerWrapper()
940 my_server.start()
941 try:
942 _RunTestsInParallel(parser, options, test_class)
943 finally:
944 my_server.Stop()
787 945
788 # Run the test suite. 946 else:
789 test_class.ProcessOptions(parser, options) 947 dev_server_cache = None
790 test_loader = unittest.TestLoader() 948 test_suite = _PrepareTestSuite(parser, options, test_class)
791 test_loader.testMethodPrefix = options.test_prefix 949 test_result = unittest.TextTestRunner(verbosity=2).run(test_suite)
792 test_suite = test_loader.loadTestsFromTestCase(test_class) 950 if not test_result.wasSuccessful():
793 test_result = unittest.TextTestRunner(verbosity=2).run(test_suite) 951 Die('Test harness was not successful.')
794 if not test_result.wasSuccessful():
795 Die('Test harness was not successful.')
796 952
797 953
798 if __name__ == '__main__': 954 if __name__ == '__main__':
799 main() 955 main()
OLDNEW
« no previous file with comments | « no previous file | bin/cros_run_vm_update » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698