summaryrefslogtreecommitdiff
path: root/buildscripts/generate_resmoke_suites.py
diff options
context:
space:
mode:
Diffstat (limited to 'buildscripts/generate_resmoke_suites.py')
-rwxr-xr-xbuildscripts/generate_resmoke_suites.py140
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__":