diff options
Diffstat (limited to 'buildscripts/generate_resmoke_suites.py')
-rwxr-xr-x | buildscripts/generate_resmoke_suites.py | 140 |
1 files changed, 66 insertions, 74 deletions
diff --git a/buildscripts/generate_resmoke_suites.py b/buildscripts/generate_resmoke_suites.py index 14ae68b04a5..6268de9f258 100755 --- a/buildscripts/generate_resmoke_suites.py +++ b/buildscripts/generate_resmoke_suites.py @@ -10,35 +10,27 @@ from __future__ import absolute_import import argparse import datetime -import glob import itertools import logging -import math import os import sys -import yaml from collections import defaultdict from collections import namedtuple from operator import itemgetter +from jinja2 import Template + from client.github import GithubApi import client.evergreen as evergreen import util.testname as testname import util.time as timeutil -from evergreen_gen_fuzzer_tests import CONFIG_DIRECTORY LOGGER = logging.getLogger(__name__) +TEMPLATES_DIR = "buildscripts/templates/generate_resmoke_suites" TEST_SUITE_DIR = "buildscripts/resmokeconfig/suites" -GENERATED_SUITES_DIR = "buildscripts/resmokeconfig/generated" - -HEADER_TEMPLATE = """# DO NOT EDIT THIS FILE. All manual edits will be lost. -# This file was generated by {file} from -# {suite_file}. -""" - MAX_RUNTIME_KEY = "max_runtime" @@ -255,66 +247,32 @@ def divide_tests_into_suites_by_maxtime(tests, sorted_tests, max_time_seconds, m return suites -def get_suite_filename(suite_name): - """ Get the source suite filename""" - return "{dir}/{suite_name}.yml".format(dir=TEST_SUITE_DIR, suite_name=suite_name) - - -def get_subsuite_filename(directory, suite_name, index, index_width=None): - """Generate filename for sub-suite from directory, suite_name and index. zero fill index if - index_width is not None,""" - filled_index = index - if index_width is not None: - filled_index = str(index).zfill(index_width) - return "{dir}/{suite_name}_{index}.yml".format(dir=directory, - suite_name=suite_name, - index=filled_index) - - -def generate_subsuite_file(suite_file, target_file, roots=None, excludes=None): - """ Read and evaluate the yaml suite file. Over ride selector.roots and selector.excludes - with the provided values. Write the result to target_file.""" - with open(suite_file, "r") as fstream: - suite_config = yaml.load(fstream) - - with open(target_file, 'w') as out: - out.write(HEADER_TEMPLATE.format(file=__file__, suite_file=suite_file)) - if roots: - suite_config['selector']['roots'] = roots - if excludes: - suite_config['selector']['exclude_files'] = excludes - out.write(yaml.dump(suite_config, default_flow_style=False, Dumper=yaml.SafeDumper)) - - -def render_suite(suites, suite_name, dir): - """Render the given suites into yml files that can be used by resmoke.py.""" +def get_misc_model(test_list, extra_model_data=None): + """Build a model that will run any missing tests.""" + model = { + "is_misc": True, + "excluded_tests": test_list, + } - suite_file = get_suite_filename(suite_name) + if extra_model_data: + model.update(extra_model_data) - index_width = int(math.ceil(math.log10(len(suites)))) - for idx, suite in enumerate(suites): - subsuite_file = get_subsuite_filename(dir, suite_name, idx, index_width=index_width) - generate_subsuite_file(suite_file, subsuite_file, roots=suite.tests) + return model -def render_misc_suite(test_list, suite_name, dir): - """Render a misc suite to run any tests that might be added to the directory.""" - suite_file = get_suite_filename(suite_name) +def render_template(model, task, index): + """Render the specified model as a yml file in the test suites directory.""" + template_file = "{dir}/{task}.yml.j2".format(dir=TEMPLATES_DIR, task=task) + target_file = "{dir}/{task}_{index}.yml".format(dir=TEST_SUITE_DIR, task=task, index=index) - subsuite_file = get_subsuite_filename(dir, suite_name, 'misc') - generate_subsuite_file(suite_file, subsuite_file, excludes=test_list) + render(model, template_file, target_file) -def prepare_directory_for_suite(suite_name, dir): - """Ensure that dir exists and that it does not contain any suite related files.""" - if os.path.exists(dir): - suite_files_glob = get_subsuite_filename(dir, suite_name, '[0-9]*') - misc_file_glob = get_subsuite_filename(dir, suite_name, 'misc') - for suite_file in glob.glob(suite_files_glob) + glob.glob(misc_file_glob): - os.remove(suite_file) - LOGGER.debug("Removing %s", dir) - else: - os.makedirs(dir) +def render(model, source, destination): + """Render the specified model with the template at `source` to the file `destination`.""" + with open(source, "r") as inp, open(destination, "w") as out: + template = Template(inp.read(), trim_blocks=True) + out.write(template.render(model)) class Suite(object): @@ -346,6 +304,19 @@ class Suite(object): return len(self.tests) + def get_model(self, extra_model_data=None): + """Get a model of this suite that can be used to render a yml file.""" + + model = {"test_names": self.tests, "variants": []} + for variant in self.variant_runtime: + model["variants"].append( + {"name": variant, "runtime": self.variant_runtime[variant] / 60}) + + if extra_model_data: + model.update(extra_model_data) + + return model + class Main(object): """Orchestrate the execution of generate_resmoke_suites.""" @@ -362,7 +333,7 @@ class Main(object): parser = argparse.ArgumentParser(description=self.main.__doc__) parser.add_argument("--analysis-duration", dest="duration_days", default=14, - help="Number of days to analyze.", type=int) + help="Number of days to analyze.") parser.add_argument("--branch", dest="branch", default="master", help="Branch of project to analyze.") parser.add_argument("--end-commit", dest="end_commit", help="End analysis at this commit.") @@ -381,7 +352,7 @@ class Main(object): help="Max number of suites to divide into.") parser.add_argument("--verbose", dest="verbose", action="store_true", default=False, help="Enable verbose logging.") - parser.add_argument("suites", nargs='+', help="A list of suites to analyze.") + parser.add_argument("task", nargs=1, help="task to analyze.") options = parser.parse_args() @@ -405,6 +376,27 @@ class Main(object): return divide_tests_into_suites_by_maxtime(tests, self.test_list, execution_time_secs, self.options.max_sub_suites) + def render_suites(self, suites, task): + """Render the given suites into yml files that can be used by resmoke.py.""" + for idx, suite in enumerate(suites): + render_template(suite.get_model(self.extra_model_data()), task, idx) + + def render_misc_suite(self, task): + """Render a misc suite to run any tests that might be added to the task directory.""" + model = get_misc_model(self.test_list, self.extra_model_data()) + source = "{dir}/{task}.yml.j2".format(dir=TEMPLATES_DIR, task=task) + target = "{dir}/{task}_misc.yml".format(dir=TEST_SUITE_DIR, task=task) + + render(model, source, target) + + def extra_model_data(self): + """Build extra data to include in the model.""" + return { + "options": self.options, + "start_commit": self.commit_range.start, + "end_commit": self.commit_range.end, + } + def main(self): """Generate resmoke suites that run within a specified target execution time.""" @@ -419,20 +411,20 @@ class Main(object): self.commit_range = CommitRange(options.start_commit, options.end_commit) LOGGER.debug("Starting execution for options %s", options) + task = options.task[0] + today = datetime.datetime.utcnow().replace(microsecond=0) start_date = today - datetime.timedelta(days=options.duration_days) - target = ProjectTarget(options.owner, options.project, options.branch) - for suite in options.suites: - prepare_directory_for_suite(suite, CONFIG_DIRECTORY) + target = ProjectTarget(options.owner, options.project, options.branch) - data = self.get_data(target, start_date, suite, options.variants) - suites = self.calculate_suites(data, options.execution_time_minutes * 60) + data = self.get_data(target, start_date, task, options.variants) + suites = self.calculate_suites(data, options.execution_time_minutes * 60) - LOGGER.debug("Creating %d suites for %s", len(suites), suite) + LOGGER.debug("Creating %d suites", len(suites)) - render_suite(suites, suite, CONFIG_DIRECTORY) - render_misc_suite(self.test_list, suite, CONFIG_DIRECTORY) + self.render_suites(suites, task) + self.render_misc_suite(task) if __name__ == "__main__": |