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

Unified Diff: bin/cros_au_test_harness.py

Issue 6264005: Pre-generate updates for test harness in parallel. (Closed) Base URL: http://git.chromium.org/git/crosutils.git@master
Patch Set: Remove unused fun 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: bin/cros_au_test_harness.py
diff --git a/bin/cros_au_test_harness.py b/bin/cros_au_test_harness.py
index f274b14e1af9e2abcb4235fb629900a7ad688e78..4b9c1d8d9ec3ec162aff2578cb36a7a90b7dd6b1 100755
--- a/bin/cros_au_test_harness.py
+++ b/bin/cros_au_test_harness.py
@@ -16,7 +16,7 @@ import optparse
import os
import re
import sys
-import thread
+import threading
import time
import unittest
import urllib
@@ -134,9 +134,10 @@ class AUTest(object):
# Will raise ValueError if expected is not found.
if re.search(re.escape(expected_msg), err.stdout, re.MULTILINE):
return
+ except Exception as err:
dgarrett 2011/01/20 03:34:27 After reading this again, the new exception handle
sosa 2011/01/20 03:59:49 Done.
+ Warning("Didn't find '%s' in:" % expected_msg)
+ Warning(err.stdout)
- Warning("Didn't find '%s' in:" % expected_msg)
- Warning(err.stdout)
self.fail('We managed to update when failure was expected')
def AttemptUpdateWithFilter(self, filter, proxy_port=8081):
@@ -155,7 +156,7 @@ class AUTest(object):
# This update is expected to fail...
try:
- self.PerformUpdate(self.target_image_path, self.base_image_path,
+ self.PerformUpdate(self.target_image_path, self.target_image_path,
proxy_port=proxy_port)
finally:
proxy.shutdown()
@@ -248,12 +249,10 @@ class AUTest(object):
percent_passed = self.VerifyImage(10)
# Update to - all tests should pass on new image.
- Info('Updating from base image on vm to target image.')
self.PerformUpdate(self.target_image_path, self.base_image_path)
self.VerifyImage(100)
# Update from - same percentage should pass that originally passed.
- Info('Updating from updated image on vm back to base image.')
self.PerformUpdate(self.base_image_path, self.target_image_path)
self.VerifyImage(percent_passed)
@@ -271,12 +270,10 @@ class AUTest(object):
percent_passed = self.VerifyImage(10)
# Update to - all tests should pass on new image.
- Info('Updating from base image on vm to target image and wiping stateful.')
self.PerformUpdate(self.target_image_path, self.base_image_path, 'clean')
self.VerifyImage(100)
# Update from - same percentage should pass that originally passed.
- Info('Updating from updated image back to base image and wiping stateful.')
self.PerformUpdate(self.base_image_path, self.target_image_path, 'clean')
self.VerifyImage(percent_passed)
@@ -508,33 +505,30 @@ class VirtualAUTest(unittest.TestCase, AUTest):
def PrepareBase(self, image_path):
"""Creates an update-able VM based on base image."""
+ self._first_update = True
dgarrett 2011/01/20 03:34:27 A comment explaining the purpose of _first_image w
sosa 2011/01/20 03:59:49 Done.
self.vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname(
image_path)
-
- Info('Creating: %s' % self.vm_image_path)
-
if not os.path.exists(self.vm_image_path):
- Info('Qemu image %s not found, creating one.' % self.vm_image_path)
+ Info('Creating %s' % vm_image_path)
RunCommand(['%s/image_to_vm.sh' % self.crosutils,
- '--full',
- '--from=%s' % ReinterpretPathForChroot(
- os.path.dirname(image_path)),
- '--vdisk_size=%s' % self._FULL_VDISK_SIZE,
- '--statefulfs_size=%s' % self._FULL_STATEFULFS_SIZE,
- '--board=%s' % self.board,
- '--test_image'], enter_chroot=True)
- else:
- Info('Using existing VM image %s' % self.vm_image_path)
-
- Info('Testing for %s' % self.vm_image_path)
+ '--full',
+ '--from=%s' % ReinterpretPathForChroot(
+ os.path.dirname(image_path)),
+ '--vdisk_size=%s' % self._FULL_VDISK_SIZE,
+ '--statefulfs_size=%s' % self._FULL_STATEFULFS_SIZE,
+ '--board=%s' % self.board,
+ '--test_image'], enter_chroot=True)
dgarrett 2011/01/20 03:34:27 arguments should be indented to match the open [.
sosa 2011/01/20 03:59:49 Done.
+
+ Info('Using %s as base' % self.vm_image_path)
self.assertTrue(os.path.exists(self.vm_image_path))
def _UpdateImage(self, image_path, src_image_path='', stateful_change='old',
proxy_port=None):
"""Updates VM image with image_path."""
stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
- if src_image_path == self.base_image_path:
+ if self._first_update:
src_image_path = self.vm_image_path
+ self._first_update = False
cmd = ['%s/cros_run_vm_update' % self.crosutilsbin,
'--update_image_path=%s' % image_path,
@@ -608,6 +602,139 @@ class VirtualAUTest(unittest.TestCase, AUTest):
return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass)
+class GenerateVirtualAUDeltasTest(VirtualAUTest):
+ """Class the overrides VirtualAUTest and stores deltas we will generate."""
+ delta_list = {}
+
+ def setUp(self):
+ AUTest.setUp(self)
+
+ def _UpdateImage(self, image_path, src_image_path='', stateful_change='old',
+ proxy_port=None):
+ if self._first_update:
+ src_image_path = self.vm_image_path
+ self._first_update = False
+
+ image_path = ReinterpretPathForChroot(image_path)
+ if src_image_path:
+ src_image_path = ReinterpretPathForChroot(src_image_path)
+ if not self.delta_list.has_key(image_path):
+ self.delta_list[image_path] = set([src_image_path])
+ else:
+ self.delta_list[image_path].add(src_image_path)
+
+ def AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg):
+ pass
+
+ def VerifyImage(self, percent_required_to_pass):
+ pass
+
+
+class ParallelJob(threading.Thread):
+ """Small wrapper for threading.Thread that releases a semaphore on exit."""
+ def __init__(self, semaphore, target, args):
+ threading.Thread.__init__(self, target=target, args=args)
+ self._target = target
+ self._args = args
+ self._semaphore = semaphore
+ self._output = None
+ self._completed = False
+
+ def run(self):
+ try:
+ threading.Thread.run(self)
+ finally:
+ self._Cleanup()
+ self._completed = True
+
+ def GetOutput(self):
+ assert self._completed, 'GetOutput called before thread was run.'
+ return self._output
+
+ def _Cleanup(self):
+ self._semaphore.release()
+
+ def __str__(self):
+ return '%s(%s)' % (self._target, self._args)
+
+
+def _RunParallelJobs(number_of_sumultaneous_jobs, jobs, jobs_args):
+ """Runs set number of specified jobs in parallel.
+
+ Args:
+ number_of_simultaneous_jobs: Max number of threads to be run in parallel.
+ jobs: Array of methods to run.
+ jobs_args: Array of args associated with method calls.
+ Returns:
+ Returns an array of results corresponding to each thread.
+ """
+ def _TwoTupleize(x, y):
+ return (x, y)
+
+ threads = []
+ job_pool_semaphore = threading.Semaphore(number_of_sumultaneous_jobs)
+ assert len(jobs) == len(jobs_args), 'Length of args array is wrong.'
+
+ # Create the parallel jobs.
+ for job, args in map(_TwoTupleize, jobs, jobs_args):
+ thread = ParallelJob(job_pool_semaphore, target=job, args=args)
+ threads.append(thread)
+
+ # We use a semaphore to ensure we don't run more jobs that required.
+ # After each thread finishes, it releases (increments semaphore).
+ # Acquire blocks of num jobs reached and continues when a thread finishes.
+ for next_thread in threads:
+ job_pool_semaphore.acquire(blocking=True)
+ Info('Starting %s' % next_thread)
+ next_thread.start()
+
+ # Wait on the rest of the threads to finish.
+ for thread in threads:
+ thread.join()
+
+ return [thread.GetOutput() for thread in threads]
+
+
+def _PregenerateUpdates(parser, options):
+ """Determines all deltas that will be generated and generates them.
+
+ This method effectively pre-generates the dev server cache for all tests.
+
+ Args:
+ parser: parser from main.
+ options: options from parsed parser.
+ Returns:
+ Array of output from generating updates.
+ """
+ def _GenerateVMUpdate(target, src):
+ """Generates an update using the devserver."""
+ RunCommand(['./start_devserver',
+ '--pregenerate_update',
+ '--exit',
+ '--image=%s' % src,
+ '--src_image=%s' % target,
+ ], redirect_stderr=True)
+
+ # Get the list of deltas by mocking out update method in test class.
+ GenerateVirtualAUDeltasTest.ProcessOptions(parser, options)
+ test_loader = unittest.TestLoader()
+ test_loader.testMethodPrefix = options.test_prefix
+ test_suite = test_loader.loadTestsFromTestCase(GenerateVirtualAUDeltasTest)
+ test_result = unittest.TextTestRunner(verbosity=0).run(test_suite)
+
+ Info('The following delta updates are required.')
+ jobs = []
+ args = []
+ for target, srcs in GenerateVirtualAUDeltasTest.delta_list.items():
+ for src in srcs:
+ print >> sys.stderr, '%s -> %s' % (src, target)
+ jobs.append(_GenerateVMUpdate)
+ args.append((target, src))
+
+ results = _RunParallelJobs(options.jobs, jobs, args)
+ return results
+
+
def main():
parser = optparse.OptionParser()
parser.add_option('-b', '--base_image',
@@ -619,10 +746,12 @@ def main():
help='Disable using delta updates.')
parser.add_option('--no_graphics', action='store_true',
help='Disable graphics for the vm test.')
- parser.add_option('-m', '--remote',
- help='Remote address for real test.')
+ parser.add_option('-j', '--jobs', default=8, type=int,
+ help='Number of simultaneous jobs')
parser.add_option('-q', '--quick_test', default=False, action='store_true',
help='Use a basic test to verify image.')
+ parser.add_option('-m', '--remote',
+ help='Remote address for real test.')
parser.add_option('-t', '--target_image',
help='path to the target image.')
parser.add_option('--test_prefix', default='test',
@@ -642,15 +771,19 @@ def main():
elif options.type == 'real': test_class = RealAUTest
else: parser.error('Could not parse harness type %s.' % options.type)
- test_class.ProcessOptions(parser, options)
+ # TODO(sosa): Caching doesn't work with non-delta updates. Also, it doesn't
+ # really make sense on non-vm images (yet).
+ if options.delta and options.type == 'vm':
+ _PregenerateUpdates(parser, options)
+ # Run the test suite.
+ test_class.ProcessOptions(parser, options)
test_loader = unittest.TestLoader()
test_loader.testMethodPrefix = options.test_prefix
test_suite = test_loader.loadTestsFromTestCase(test_class)
test_result = unittest.TextTestRunner(verbosity=2).run(test_suite)
-
if not test_result.wasSuccessful():
- Die('Test harness was not successful')
+ Die('Test harness was not successful.')
if __name__ == '__main__':
« 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