diff options
author | Robert Collins <robertc@robertcollins.net> | 2012-12-20 00:58:05 +1300 |
---|---|---|
committer | Robert Collins <robertc@robertcollins.net> | 2012-12-20 00:58:05 +1300 |
commit | 655ae45d67da64285f1e08cb1d0c2338883454d5 (patch) | |
tree | d334f5c32a9e946846df61adcd09814f265bc988 | |
parent | 4148a486499d6257696b7d26f6e518ea67b9ce64 (diff) | |
download | testrepository-655ae45d67da64285f1e08cb1d0c2338883454d5.tar.gz |
Refactor, making the test run case also spin up in advance.
-rw-r--r-- | testrepository/testcommand.py | 47 | ||||
-rw-r--r-- | testrepository/tests/test_testcommand.py | 2 |
2 files changed, 21 insertions, 28 deletions
diff --git a/testrepository/testcommand.py b/testrepository/testcommand.py index b34aba4..8c511e8 100644 --- a/testrepository/testcommand.py +++ b/testrepository/testcommand.py @@ -151,8 +151,8 @@ class TestListingFixture(Fixture): matches all your criteria. Filters are automatically applied by run_tests(), or can be applied by calling filter_tests(test_ids). :param instance_source: A source of test run instances. Must support - obtain_instances(count) -> [id, id, id] and release_instances([id, - ...]) calls. + obtain_instance(max_concurrency) -> id and release_instance(id) + calls. """ self.test_ids = test_ids self.template = cmd_template @@ -266,8 +266,7 @@ class TestListingFixture(Fixture): """ if '$LISTOPT' not in self.template: raise ValueError("LISTOPT not configured in .testr.conf") - instance, list_cmd = self._per_instance_command(self.list_cmd, - self.concurrency) + instance, list_cmd = self._per_instance_command(self.list_cmd) try: self.ui.output_values([('running', list_cmd)]) run_proc = self.ui.subprocess_Popen(list_cmd, shell=True, @@ -278,9 +277,9 @@ class TestListingFixture(Fixture): return ids finally: if instance: - self._instance_source.release_instances([instance]) + self._instance_source.release_instance(instance) - def _per_instance_command(self, cmd, concurrency=1): + def _per_instance_command(self, cmd): """Customise cmd to with an instance-id. :param concurrency: The number of instances to ask for (used to avoid @@ -288,11 +287,8 @@ class TestListingFixture(Fixture): """ if self._instance_source is None: return None, cmd - instance = None - instances = self._instance_source.obtain_instances(concurrency) - if instances is not None: - instance = instances[0] - self._instance_source.release_instances(instances[1:]) + instance = self._instance_source.obtain_instance(self.concurrency) + if instance is not None: try: instance_prefix = self._parser.get( 'DEFAULT', 'instance_execute') @@ -331,7 +327,7 @@ class TestListingFixture(Fixture): run_proc.stdin.close() if instance: return [CallWhenProcFinishes(run_proc, - lambda:self._instance_source.release_instances([instance]))] + lambda:self._instance_source.release_instance(instance))] else: return [run_proc] test_id_groups = self.partition_tests(test_ids, self.concurrency) @@ -526,25 +522,21 @@ class TestCommand(Fixture): return set() return set([tag.strip() for tag in tags.split()]) - def obtain_instances(self, count): + def obtain_instance(self, concurrency): """If possible, get one or more test run environment instance ids. Note this is not threadsafe: calling it from multiple threads would likely result in shared results. """ - orig_count = count - # Cached first. - available_instances = self._instances - self._allocated_instances - result = list(available_instances)[:count] - count = count - len(result) - while count > 0: + while len(self._instances) < concurrency: try: cmd = self.get_parser().get('DEFAULT', 'instance_provision') except ConfigParser.NoOptionError: # Instance allocation not configured return None variable_regex = '\$INSTANCE_COUNT' - cmd = re.sub(variable_regex, str(count), cmd) + cmd = re.sub(variable_regex, + str(concurrency - len(self._instances)), cmd) self.ui.output_values([('running', cmd)]) proc = self.ui.subprocess_Popen( cmd, shell=True, stdout=subprocess.PIPE) @@ -554,11 +546,11 @@ class TestCommand(Fixture): proc.returncode) new_instances = set([item.strip() for item in out.split()]) self._instances.update(new_instances) - available_instances = self._instances - self._allocated_instances - available_instances = available_instances - set(result) - result.extend(list(available_instances)[:count]) - count = orig_count - len(result) - self._allocated_instances.update(result) + # Cached first. + available_instances = self._instances - self._allocated_instances + # We only ask for instances when one should be available. + result = available_instances.pop() + self._allocated_instances.add(result) return result def make_result(self, receiver): @@ -594,7 +586,6 @@ class TestCommand(Fixture): receiver, filter_success=False, filter_predicate=predicate) return receiver - def release_instances(self, instance_ids): + def release_instance(self, instance_id): """Return instance_ids to the pool for reuse.""" - for instance_id in instance_ids: - self._allocated_instances.remove(instance_id) + self._allocated_instances.remove(instance_id) diff --git a/testrepository/tests/test_testcommand.py b/testrepository/tests/test_testcommand.py index 219bd7d..d259966 100644 --- a/testrepository/tests/test_testcommand.py +++ b/testrepository/tests/test_testcommand.py @@ -221,6 +221,8 @@ class TestTestCommand(ResourcedTestCase): # testr list-tests is non-parallel, so needs 1 instance. # testr run triggering list-tests will want to run parallel on all, so # avoid latency by asking for whatever concurrency is up front. + # This covers the case for non-listing runs as well, as the code path + # is common. self.dirty() ui = UI(options= [('concurrency', 2), ('parallel', True)]) ui.here = self.tempdir |