mirror of https://github.com/mongodb/mongo
SERVER-47274: Refactor task generation in evergreen
This commit is contained in:
parent
1fa4585c07
commit
4d82d10588
|
|
@ -1,35 +1,35 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Generate burn in tests to run on certain build variants."""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
from collections import namedtuple
|
||||
import os
|
||||
import sys
|
||||
from typing import Any, Dict, Iterable
|
||||
|
||||
import click
|
||||
|
||||
from evergreen.api import RetryingEvergreenApi
|
||||
from evergreen.api import RetryingEvergreenApi, EvergreenApi
|
||||
from git import Repo
|
||||
from shrub.config import Configuration
|
||||
from shrub.variant import TaskSpec
|
||||
from shrub.v2 import ShrubProject, BuildVariant, ExistingTask
|
||||
|
||||
# Get relative imports to work when the package is not installed on the PYTHONPATH.
|
||||
if __name__ == "__main__" and __package__ is None:
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
from buildscripts.util.fileops import write_file_to_dir
|
||||
import buildscripts.util.read_config as read_config
|
||||
from buildscripts.ciconfig import evergreen
|
||||
from buildscripts.ciconfig.evergreen import EvergreenProjectConfig
|
||||
from buildscripts.ciconfig.evergreen import EvergreenProjectConfig, Variant
|
||||
from buildscripts.burn_in_tests import create_generate_tasks_config, create_tests_by_task, \
|
||||
GenerateConfig, RepeatConfig, DEFAULT_REPO_LOCATIONS
|
||||
|
||||
# pylint: enable=wrong-import-position
|
||||
|
||||
CONFIG_DIRECTORY = "generated_burn_in_tags_config"
|
||||
CONFIG_FILE = "burn_in_tags_gen.json"
|
||||
EVERGREEN_FILE = "etc/evergreen.yml"
|
||||
EVG_CONFIG_FILE = ".evergreen.yml"
|
||||
COMPILE_TASK = "compile_without_package_TG"
|
||||
|
||||
ConfigOptions = namedtuple("ConfigOptions", [
|
||||
"build_variant",
|
||||
|
|
@ -94,86 +94,72 @@ def _create_evg_build_variant_map(expansions_file_data, evergreen_conf):
|
|||
return {}
|
||||
|
||||
|
||||
def _generate_evg_build_variant(shrub_config, build_variant, run_build_variant,
|
||||
burn_in_tags_gen_variant, evg_conf):
|
||||
def _generate_evg_build_variant(
|
||||
source_build_variant: Variant,
|
||||
run_build_variant: str,
|
||||
bypass_build_variant: str,
|
||||
) -> BuildVariant:
|
||||
"""
|
||||
Generate buildvariants for a given shrub config.
|
||||
Generate a shrub build variant for the given run build variant.
|
||||
|
||||
:param shrub_config: Shrub config object that the generated buildvariant will be built upon.
|
||||
:param build_variant: The base variant that the generated run_buildvariant will be based on.
|
||||
:param run_build_variant: The generated buildvariant.
|
||||
:param burn_in_tags_gen_variant: The buildvariant on which the burn_in_tags_gen task runs.
|
||||
:param source_build_variant: The build variant to base configuration on.
|
||||
:param run_build_variant: The build variant to generate.
|
||||
:param bypass_build_variant: The build variant to get compile artifacts from.
|
||||
:return: Shrub build variant configuration.
|
||||
"""
|
||||
base_variant_config = evg_conf.get_variant(build_variant)
|
||||
display_name = f"! {source_build_variant.display_name}"
|
||||
run_on = source_build_variant.run_on
|
||||
modules = source_build_variant.modules
|
||||
|
||||
new_variant_display_name = f"! {base_variant_config.display_name}"
|
||||
new_variant_run_on = base_variant_config.run_on[0]
|
||||
expansions = source_build_variant.expansions
|
||||
expansions["burn_in_bypass"] = bypass_build_variant
|
||||
|
||||
task_spec = TaskSpec("compile_without_package_TG")
|
||||
|
||||
new_variant = shrub_config.variant(run_build_variant).expansion("burn_in_bypass",
|
||||
burn_in_tags_gen_variant)
|
||||
new_variant.display_name(new_variant_display_name)
|
||||
new_variant.run_on(new_variant_run_on)
|
||||
new_variant.task(task_spec)
|
||||
|
||||
base_variant_expansions = base_variant_config.expansions
|
||||
new_variant.expansions(base_variant_expansions)
|
||||
|
||||
modules = base_variant_config.modules
|
||||
new_variant.modules(modules)
|
||||
build_variant = BuildVariant(run_build_variant, display_name, expansions=expansions,
|
||||
modules=modules, run_on=run_on)
|
||||
build_variant.add_existing_task(ExistingTask(COMPILE_TASK))
|
||||
return build_variant
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def _generate_evg_tasks(evergreen_api, shrub_config, expansions_file_data, build_variant_map, repos,
|
||||
evg_conf):
|
||||
def _generate_evg_tasks(evergreen_api: EvergreenApi, shrub_project: ShrubProject,
|
||||
task_expansions: Dict[str, Any], build_variant_map: Dict[str, str],
|
||||
repos: Iterable[Repo], evg_conf: EvergreenProjectConfig) -> None:
|
||||
"""
|
||||
Generate burn in tests tasks for a given shrub config and group of buildvariants.
|
||||
Generate burn in tests tasks for a given shrub config and group of build variants.
|
||||
|
||||
:param evergreen_api: Evergreen.py object.
|
||||
:param shrub_config: Shrub config object that the build variants will be built upon.
|
||||
:param expansions_file_data: Config data file to use.
|
||||
:param shrub_project: Shrub config object that the build variants will be built upon.
|
||||
:param task_expansions: Dictionary of expansions for the running task.
|
||||
:param build_variant_map: Map of base buildvariants to their generated buildvariant.
|
||||
:param repos: Git repositories.
|
||||
"""
|
||||
for build_variant, run_build_variant in build_variant_map.items():
|
||||
config_options = _get_config_options(expansions_file_data, build_variant, run_build_variant)
|
||||
config_options = _get_config_options(task_expansions, build_variant, run_build_variant)
|
||||
tests_by_task = create_tests_by_task(build_variant, repos, evg_conf)
|
||||
if tests_by_task:
|
||||
_generate_evg_build_variant(shrub_config, build_variant, run_build_variant,
|
||||
expansions_file_data["build_variant"], evg_conf)
|
||||
shrub_build_variant = _generate_evg_build_variant(
|
||||
evg_conf.get_variant(build_variant), run_build_variant,
|
||||
task_expansions["build_variant"])
|
||||
gen_config = GenerateConfig(build_variant, config_options.project, run_build_variant,
|
||||
config_options.distro).validate(evg_conf)
|
||||
repeat_config = RepeatConfig(repeat_tests_min=config_options.repeat_tests_min,
|
||||
repeat_tests_max=config_options.repeat_tests_max,
|
||||
repeat_tests_secs=config_options.repeat_tests_secs)
|
||||
|
||||
create_generate_tasks_config(shrub_config, tests_by_task, gen_config, repeat_config,
|
||||
evergreen_api, include_gen_task=False)
|
||||
create_generate_tasks_config(shrub_build_variant, tests_by_task, gen_config,
|
||||
repeat_config, evergreen_api, include_gen_task=False)
|
||||
shrub_project.add_build_variant(shrub_build_variant)
|
||||
|
||||
|
||||
def _write_to_file(shrub_config):
|
||||
"""
|
||||
Save shrub config to file.
|
||||
|
||||
:param shrub_config: Shrub config object.
|
||||
"""
|
||||
if not os.path.exists(CONFIG_DIRECTORY):
|
||||
os.makedirs(CONFIG_DIRECTORY)
|
||||
|
||||
with open(os.path.join(CONFIG_DIRECTORY, CONFIG_FILE), "w") as file_handle:
|
||||
file_handle.write(shrub_config.to_json())
|
||||
|
||||
|
||||
def burn_in(expansions_file_data: Dict[str, Any], evg_conf: EvergreenProjectConfig,
|
||||
def burn_in(task_expansions: Dict[str, Any], evg_conf: EvergreenProjectConfig,
|
||||
evergreen_api: RetryingEvergreenApi, repos: Iterable[Repo]):
|
||||
"""Execute Main program."""
|
||||
|
||||
shrub_config = Configuration()
|
||||
build_variant_map = _create_evg_build_variant_map(expansions_file_data, evg_conf)
|
||||
_generate_evg_tasks(evergreen_api, shrub_config, expansions_file_data, build_variant_map, repos,
|
||||
shrub_project = ShrubProject.empty()
|
||||
build_variant_map = _create_evg_build_variant_map(task_expansions, evg_conf)
|
||||
_generate_evg_tasks(evergreen_api, shrub_project, task_expansions, build_variant_map, repos,
|
||||
evg_conf)
|
||||
_write_to_file(shrub_config)
|
||||
|
||||
write_file_to_dir(CONFIG_DIRECTORY, CONFIG_FILE, shrub_project.json())
|
||||
|
||||
|
||||
@click.command()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Command line utility for determining what jstests have been added or modified."""
|
||||
|
||||
import copy
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import os.path
|
||||
import shlex
|
||||
|
|
@ -18,10 +16,11 @@ import requests
|
|||
import yaml
|
||||
from evergreen.api import RetryingEvergreenApi, EvergreenApi
|
||||
from git import Repo
|
||||
from shrub.config import Configuration
|
||||
import structlog
|
||||
from structlog.stdlib import LoggerFactory
|
||||
|
||||
from shrub.v2 import Task, TaskDependency, BuildVariant, ShrubProject, ExistingTask
|
||||
|
||||
# Get relative imports to work when the package is not installed on the PYTHONPATH.
|
||||
if __name__ == "__main__" and __package__ is None:
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
|
@ -33,10 +32,11 @@ from buildscripts.resmokelib.suitesconfig import create_test_membership_map, get
|
|||
from buildscripts.resmokelib.utils import default_if_none, globstar
|
||||
from buildscripts.ciconfig.evergreen import parse_evergreen_file, ResmokeArgs, \
|
||||
EvergreenProjectConfig, VariantTask
|
||||
from buildscripts.util.fileops import write_file
|
||||
from buildscripts.util.teststats import TestStats
|
||||
from buildscripts.util.taskname import name_generated_task
|
||||
from buildscripts.patch_builds.task_generation import resmoke_commands, TimeoutInfo, TaskList
|
||||
|
||||
from buildscripts.patch_builds.task_generation import (resmoke_commands, TimeoutInfo,
|
||||
validate_task_generation_limit)
|
||||
# pylint: enable=wrong-import-position
|
||||
|
||||
structlog.configure(logger_factory=LoggerFactory())
|
||||
|
|
@ -56,7 +56,6 @@ DEFAULT_VARIANT = "enterprise-rhel-62-64-bit"
|
|||
DEFAULT_REPO_LOCATIONS = [".", "./src/mongo/db/modules/enterprise"]
|
||||
REPEAT_SUITES = 2
|
||||
EVERGREEN_FILE = "etc/evergreen.yml"
|
||||
MAX_TASKS_TO_CREATE = 1000
|
||||
MIN_AVG_TEST_OVERFLOW_SEC = float(60)
|
||||
MIN_AVG_TEST_TIME_SEC = 5 * 60
|
||||
# The executor_file and suite_files defaults are required to make the suite resolver work
|
||||
|
|
@ -397,13 +396,6 @@ def create_task_list(evergreen_conf: EvergreenProjectConfig, build_variant: str,
|
|||
return task_list
|
||||
|
||||
|
||||
def _write_json_file(json_data, pathname):
|
||||
"""Write out a JSON file."""
|
||||
|
||||
with open(pathname, "w") as fstream:
|
||||
json.dump(json_data, fstream, indent=4)
|
||||
|
||||
|
||||
def _set_resmoke_cmd(repeat_config: RepeatConfig, resmoke_args: [str]) -> [str]:
|
||||
"""Build the resmoke command, if a resmoke.py command wasn't passed in."""
|
||||
new_args = [sys.executable, "buildscripts/resmoke.py"]
|
||||
|
|
@ -522,54 +514,93 @@ def _get_task_runtime_history(evg_api: Optional[EvergreenApi], project: str, tas
|
|||
raise
|
||||
|
||||
|
||||
def create_generate_tasks_config(evg_config: Configuration, tests_by_task: Dict,
|
||||
def _create_task(index: int, test_count: int, test: str, task_data: Dict,
|
||||
task_runtime_stats: List[TestStats], generate_config: GenerateConfig,
|
||||
repeat_config: RepeatConfig, task_prefix: str) -> Task:
|
||||
# pylint: disable=too-many-arguments,too-many-locals
|
||||
"""
|
||||
Create the described shrub sub task.
|
||||
|
||||
:param index: Index of task being created.
|
||||
:param test_count: Total number of testing being created.
|
||||
:param test: Test task is being generated for.
|
||||
:param task_data: Data about task to create.
|
||||
:param task_runtime_stats: Historical runtime of test.
|
||||
:param generate_config: Configuration of how to generate the task.
|
||||
:param repeat_config: Configuration of how the task should be repeated.
|
||||
:param task_prefix: String to prefix generated task with.
|
||||
:return: Shrub task for given configuration.
|
||||
"""
|
||||
# TODO: Extract multiversion related code into separate tooling - SERVER-47137
|
||||
multiversion_path = task_data.get("use_multiversion")
|
||||
display_task_name = task_data["display_task_name"]
|
||||
resmoke_args = task_data["resmoke_args"]
|
||||
sub_task_name = name_generated_task(f"{task_prefix}:{display_task_name}", index, test_count,
|
||||
generate_config.run_build_variant)
|
||||
LOGGER.debug("Generating sub-task", sub_task=sub_task_name)
|
||||
|
||||
test_unix_style = test.replace('\\', '/')
|
||||
run_tests_vars = {
|
||||
"resmoke_args":
|
||||
f"{resmoke_args} {repeat_config.generate_resmoke_options()} {test_unix_style}"
|
||||
}
|
||||
if multiversion_path:
|
||||
run_tests_vars["task_path_suffix"] = multiversion_path
|
||||
timeout = _generate_timeouts(repeat_config, test, task_runtime_stats)
|
||||
commands = resmoke_commands("run tests", run_tests_vars, timeout, multiversion_path)
|
||||
dependencies = {TaskDependency("compile")}
|
||||
|
||||
return Task(sub_task_name, commands, dependencies)
|
||||
|
||||
|
||||
def create_generated_tasks(tests_by_task: Dict, task_prefix: str, generate_config: GenerateConfig,
|
||||
repeat_config: RepeatConfig, evg_api: EvergreenApi) -> Set[Task]:
|
||||
"""
|
||||
Create the set of tasks to run the given tests_by_task.
|
||||
|
||||
:param tests_by_task: Dictionary of tests to generate tasks for.
|
||||
:param task_prefix: Prefix all task names with this.
|
||||
:param generate_config: Configuration of what to generate.
|
||||
:param repeat_config: Configuration of how to repeat tests.
|
||||
:param evg_api: Evergreen API.
|
||||
:return: Set of shrub tasks to run tests_by_task.
|
||||
"""
|
||||
tasks: Set[Task] = set()
|
||||
for task in sorted(tests_by_task):
|
||||
task_info = tests_by_task[task]
|
||||
test_list = task_info["tests"]
|
||||
task_runtime_stats = _get_task_runtime_history(evg_api, generate_config.project,
|
||||
task_info["display_task_name"],
|
||||
generate_config.build_variant)
|
||||
test_count = len(test_list)
|
||||
for index, test in enumerate(test_list):
|
||||
tasks.add(
|
||||
_create_task(index, test_count, test, task_info, task_runtime_stats,
|
||||
generate_config, repeat_config, task_prefix))
|
||||
|
||||
return tasks
|
||||
|
||||
|
||||
def create_generate_tasks_config(build_variant: BuildVariant, tests_by_task: Dict,
|
||||
generate_config: GenerateConfig, repeat_config: RepeatConfig,
|
||||
evg_api: Optional[EvergreenApi], include_gen_task: bool = True,
|
||||
task_prefix: str = "burn_in") -> Configuration:
|
||||
task_prefix: str = "burn_in") -> None:
|
||||
# pylint: disable=too-many-arguments,too-many-locals
|
||||
"""
|
||||
Create the config for the Evergreen generate.tasks file.
|
||||
|
||||
:param evg_config: Shrub configuration to add to.
|
||||
:param build_variant: Shrub configuration to add to.
|
||||
:param tests_by_task: Dictionary of tests to generate tasks for.
|
||||
:param generate_config: Configuration of what to generate.
|
||||
:param repeat_config: Configuration of how to repeat tests.
|
||||
:param evg_api: Evergreen API.
|
||||
:param include_gen_task: Should generating task be include in display task.
|
||||
:param task_prefix: Prefix all task names with this.
|
||||
:return: Shrub configuration with added tasks.
|
||||
"""
|
||||
task_list = TaskList(evg_config)
|
||||
resmoke_options = repeat_config.generate_resmoke_options()
|
||||
for task in sorted(tests_by_task):
|
||||
test_list = tests_by_task[task]["tests"]
|
||||
for index, test in enumerate(test_list):
|
||||
# TODO: Extract multiversion related code into separate tooling - SERVER-47137
|
||||
multiversion_path = tests_by_task[task].get("use_multiversion")
|
||||
display_task_name = tests_by_task[task]["display_task_name"]
|
||||
task_runtime_stats = _get_task_runtime_history(
|
||||
evg_api, generate_config.project, display_task_name, generate_config.build_variant)
|
||||
resmoke_args = tests_by_task[task]["resmoke_args"]
|
||||
distro = tests_by_task[task].get("distro", generate_config.distro)
|
||||
# Evergreen always uses a unix shell, even on Windows, so instead of using os.path.join
|
||||
# here, just use the forward slash; otherwise the path separator will be treated as
|
||||
# the escape character on Windows.
|
||||
sub_task_name = name_generated_task(f"{task_prefix}:{display_task_name}", index,
|
||||
len(test_list), generate_config.run_build_variant)
|
||||
LOGGER.debug("Generating sub-task", sub_task=sub_task_name)
|
||||
|
||||
test_unix_style = test.replace('\\', '/')
|
||||
run_tests_vars = {"resmoke_args": f"{resmoke_args} {resmoke_options} {test_unix_style}"}
|
||||
if multiversion_path:
|
||||
run_tests_vars["task_path_suffix"] = multiversion_path
|
||||
timeout = _generate_timeouts(repeat_config, test, task_runtime_stats)
|
||||
commands = resmoke_commands("run tests", run_tests_vars, timeout, multiversion_path)
|
||||
|
||||
task_list.add_task(sub_task_name, commands, ["compile"], distro)
|
||||
|
||||
existing_tasks = [BURN_IN_TESTS_GEN_TASK] if include_gen_task else None
|
||||
task_list.add_to_variant(generate_config.run_build_variant, BURN_IN_TESTS_TASK, existing_tasks)
|
||||
return evg_config
|
||||
tasks = create_generated_tasks(tests_by_task, task_prefix, generate_config, repeat_config,
|
||||
evg_api)
|
||||
existing_tasks = {ExistingTask(BURN_IN_TESTS_GEN_TASK)} if include_gen_task else None
|
||||
build_variant.display_task(BURN_IN_TESTS_TASK, tasks, execution_existing_tasks=existing_tasks)
|
||||
|
||||
|
||||
def create_task_list_for_tests(
|
||||
|
|
@ -626,7 +657,7 @@ def create_tests_by_task(build_variant: str, repos: Iterable[Repo],
|
|||
# pylint: disable=too-many-arguments
|
||||
def create_generate_tasks_file(tests_by_task: Dict, generate_config: GenerateConfig,
|
||||
repeat_config: RepeatConfig, evg_api: Optional[EvergreenApi],
|
||||
task_prefix: str = 'burn_in', include_gen_task: bool = True) -> Dict:
|
||||
task_prefix: str = 'burn_in', include_gen_task: bool = True) -> str:
|
||||
"""
|
||||
Create an Evergreen generate.tasks file to run the given tasks and tests.
|
||||
|
||||
|
|
@ -638,18 +669,18 @@ def create_generate_tasks_file(tests_by_task: Dict, generate_config: GenerateCon
|
|||
:param include_gen_task: Should the generating task be included in the display task.
|
||||
:returns: Configuration to pass to 'generate.tasks'.
|
||||
"""
|
||||
evg_config = Configuration()
|
||||
evg_config = create_generate_tasks_config(
|
||||
evg_config, tests_by_task, generate_config, repeat_config, evg_api,
|
||||
include_gen_task=include_gen_task, task_prefix=task_prefix)
|
||||
build_variant = BuildVariant(generate_config.run_build_variant)
|
||||
create_generate_tasks_config(build_variant, tests_by_task, generate_config, repeat_config,
|
||||
evg_api, include_gen_task=include_gen_task,
|
||||
task_prefix=task_prefix)
|
||||
|
||||
json_config = evg_config.to_map()
|
||||
tasks_to_create = len(json_config.get('tasks', []))
|
||||
if tasks_to_create > MAX_TASKS_TO_CREATE:
|
||||
LOGGER.warning("Attempting to create more tasks than max, aborting", tasks=tasks_to_create,
|
||||
max=MAX_TASKS_TO_CREATE)
|
||||
shrub_project = ShrubProject.empty()
|
||||
shrub_project.add_build_variant(build_variant)
|
||||
|
||||
if not validate_task_generation_limit(shrub_project):
|
||||
sys.exit(1)
|
||||
return json_config
|
||||
|
||||
return shrub_project.json()
|
||||
|
||||
|
||||
def run_tests(tests_by_task: Dict, resmoke_cmd: [str]):
|
||||
|
|
@ -705,7 +736,7 @@ def _get_evg_api(evg_api_config: str, local_mode: bool) -> Optional[EvergreenApi
|
|||
|
||||
def burn_in(repeat_config: RepeatConfig, generate_config: GenerateConfig, resmoke_args: str,
|
||||
generate_tasks_file: str, no_exec: bool, evg_conf: EvergreenProjectConfig,
|
||||
repos: Iterable[Repo], evg_api: EvergreenApi):
|
||||
repos: Iterable[Repo], evg_api: EvergreenApi) -> None:
|
||||
"""
|
||||
Run burn_in_tests with the given configuration.
|
||||
|
||||
|
|
@ -725,9 +756,9 @@ def burn_in(repeat_config: RepeatConfig, generate_config: GenerateConfig, resmok
|
|||
LOGGER.debug("tests and tasks found", tests_by_task=tests_by_task)
|
||||
|
||||
if generate_tasks_file:
|
||||
json_config = create_generate_tasks_file(tests_by_task, generate_config, repeat_config,
|
||||
evg_api)
|
||||
_write_json_file(json_config, generate_tasks_file)
|
||||
json_text = create_generate_tasks_file(tests_by_task, generate_config, repeat_config,
|
||||
evg_api)
|
||||
write_file(generate_tasks_file, json_text)
|
||||
elif not no_exec:
|
||||
run_tests(tests_by_task, resmoke_cmd)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ from typing import Dict
|
|||
import click
|
||||
from evergreen.api import EvergreenApi
|
||||
from git import Repo
|
||||
from shrub.config import Configuration
|
||||
from shrub.variant import DisplayTaskDefinition
|
||||
from shrub.v2 import BuildVariant, ExistingTask, ShrubProject
|
||||
import structlog
|
||||
from structlog.stdlib import LoggerFactory
|
||||
|
||||
|
|
@ -17,9 +16,11 @@ import buildscripts.evergreen_gen_multiversion_tests as gen_multiversion
|
|||
import buildscripts.evergreen_generate_resmoke_tasks as gen_resmoke
|
||||
from buildscripts.burn_in_tests import GenerateConfig, DEFAULT_PROJECT, CONFIG_FILE, _configure_logging, RepeatConfig, \
|
||||
_get_evg_api, EVERGREEN_FILE, DEFAULT_REPO_LOCATIONS, _set_resmoke_cmd, create_tests_by_task, \
|
||||
_write_json_file, run_tests, MAX_TASKS_TO_CREATE
|
||||
run_tests
|
||||
from buildscripts.ciconfig.evergreen import parse_evergreen_file
|
||||
from buildscripts.patch_builds.task_generation import validate_task_generation_limit
|
||||
from buildscripts.resmokelib.suitesconfig import get_named_suites_with_root_level_key
|
||||
from buildscripts.util.fileops import write_file
|
||||
|
||||
structlog.configure(logger_factory=LoggerFactory())
|
||||
LOGGER = structlog.getLogger(__name__)
|
||||
|
|
@ -31,21 +32,18 @@ BURN_IN_MULTIVERSION_TASK = gen_multiversion.BURN_IN_TASK
|
|||
TASK_PATH_SUFFIX = "/data/multiversion"
|
||||
|
||||
|
||||
def create_multiversion_generate_tasks_config(evg_config: Configuration, tests_by_task: Dict,
|
||||
evg_api: EvergreenApi,
|
||||
generate_config: GenerateConfig) -> Configuration:
|
||||
def create_multiversion_generate_tasks_config(tests_by_task: Dict, evg_api: EvergreenApi,
|
||||
generate_config: GenerateConfig) -> BuildVariant:
|
||||
"""
|
||||
Create the multiversion config for the Evergreen generate.tasks file.
|
||||
|
||||
:param evg_config: Shrub configuration to add to.
|
||||
:param tests_by_task: Dictionary of tests to generate tasks for.
|
||||
:param evg_api: Evergreen API.
|
||||
:param generate_config: Configuration of what to generate.
|
||||
:return: Shrub configuration with added tasks.
|
||||
"""
|
||||
|
||||
dt = DisplayTaskDefinition(BURN_IN_MULTIVERSION_TASK)
|
||||
|
||||
build_variant = BuildVariant(generate_config.build_variant)
|
||||
tasks = set()
|
||||
if tests_by_task:
|
||||
# Get the multiversion suites that will run in as part of burn_in_multiversion.
|
||||
multiversion_suites = get_named_suites_with_root_level_key(MULTIVERSION_CONFIG_KEY)
|
||||
|
|
@ -72,8 +70,8 @@ def create_multiversion_generate_tasks_config(evg_config: Configuration, tests_b
|
|||
}
|
||||
config_options.update(gen_resmoke.DEFAULT_CONFIG_VALUES)
|
||||
|
||||
config_generator = gen_multiversion.EvergreenConfigGenerator(
|
||||
evg_api, evg_config, gen_resmoke.ConfigOptions(config_options))
|
||||
config_generator = gen_multiversion.EvergreenMultiversionConfigGenerator(
|
||||
evg_api, gen_resmoke.ConfigOptions(config_options))
|
||||
test_list = tests_by_task[suite["origin"]]["tests"]
|
||||
for test in test_list:
|
||||
# Exclude files that should be blacklisted from multiversion testing.
|
||||
|
|
@ -83,14 +81,14 @@ def create_multiversion_generate_tasks_config(evg_config: Configuration, tests_b
|
|||
suite=suite["multiversion_name"])
|
||||
if test not in files_to_exclude:
|
||||
# Generate the multiversion tasks for each test.
|
||||
config_generator.generate_evg_tasks(test, idx)
|
||||
sub_tasks = config_generator.get_burn_in_tasks(test, idx)
|
||||
tasks = tasks.union(sub_tasks)
|
||||
idx += 1
|
||||
dt.execution_tasks(config_generator.task_names)
|
||||
evg_config.variant(generate_config.build_variant).tasks(config_generator.task_specs)
|
||||
|
||||
dt.execution_task(f"{BURN_IN_MULTIVERSION_TASK}_gen")
|
||||
evg_config.variant(generate_config.build_variant).display_task(dt)
|
||||
return evg_config
|
||||
existing_tasks = {ExistingTask(f"{BURN_IN_MULTIVERSION_TASK}_gen")}
|
||||
build_variant.display_task(BURN_IN_MULTIVERSION_TASK, tasks,
|
||||
execution_existing_tasks=existing_tasks)
|
||||
return build_variant
|
||||
|
||||
|
||||
@click.command()
|
||||
|
|
@ -180,17 +178,16 @@ def main(build_variant, run_build_variant, distro, project, generate_tasks_file,
|
|||
# MULTIVERSION_CONFIG_KEY as a root level key and must be set to true.
|
||||
multiversion_suites = get_named_suites_with_root_level_key(MULTIVERSION_CONFIG_KEY)
|
||||
assert len(multiversion_tasks) == len(multiversion_suites)
|
||||
evg_config = Configuration()
|
||||
evg_config = create_multiversion_generate_tasks_config(evg_config, tests_by_task, evg_api,
|
||||
generate_config)
|
||||
|
||||
json_config = evg_config.to_map()
|
||||
tasks_to_create = len(json_config.get('tasks', []))
|
||||
if tasks_to_create > MAX_TASKS_TO_CREATE:
|
||||
LOGGER.warning("Attempting to create more tasks than max, aborting",
|
||||
tasks=tasks_to_create, max=MAX_TASKS_TO_CREATE)
|
||||
build_variant = create_multiversion_generate_tasks_config(tests_by_task, evg_api,
|
||||
generate_config)
|
||||
shrub_project = ShrubProject.empty()
|
||||
shrub_project.add_build_variant(build_variant)
|
||||
|
||||
if not validate_task_generation_limit(shrub_project):
|
||||
sys.exit(1)
|
||||
_write_json_file(json_config, generate_tasks_file)
|
||||
|
||||
write_file(generate_tasks_file, shrub_project.json())
|
||||
elif not no_exec:
|
||||
run_tests(tests_by_task, resmoke_cmd)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
The API also provides methods to access specific fields present in the mongodb/mongo
|
||||
configuration file.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import distutils.spawn # pylint: disable=no-name-in-module
|
||||
|
|
@ -79,7 +80,7 @@ class EvergreenProjectConfig(object): # pylint: disable=too-many-instance-attri
|
|||
"""Get the list of build variant names."""
|
||||
return list(self._variants_by_name.keys())
|
||||
|
||||
def get_variant(self, variant_name):
|
||||
def get_variant(self, variant_name: str) -> Variant:
|
||||
"""Return the variant with the given name as a Variant instance."""
|
||||
return self._variants_by_name.get(variant_name)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,12 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Generate fuzzer tests to run in evergreen in parallel."""
|
||||
|
||||
import argparse
|
||||
import math
|
||||
import os
|
||||
|
||||
from collections import namedtuple
|
||||
from typing import Set
|
||||
|
||||
from shrub.config import Configuration
|
||||
from shrub.command import CommandDefinition
|
||||
from shrub.task import TaskDependency
|
||||
from shrub.variant import DisplayTaskDefinition
|
||||
from shrub.variant import TaskSpec
|
||||
from shrub.v2 import ShrubProject, FunctionCall, Task, TaskDependency, BuildVariant, ExistingTask
|
||||
|
||||
import buildscripts.evergreen_generate_resmoke_tasks as generate_resmoke
|
||||
from buildscripts.util.fileops import write_file_to_dir
|
||||
import buildscripts.util.read_config as read_config
|
||||
import buildscripts.util.taskname as taskname
|
||||
|
||||
|
|
@ -81,76 +74,74 @@ def _get_config_options(cmd_line_options, config_file): # pylint: disable=too-m
|
|||
timeout_secs, use_multiversion, suite)
|
||||
|
||||
|
||||
def _name_task(parent_name, task_index, total_tasks):
|
||||
def build_fuzzer_sub_task(task_name: str, task_index: int, options: ConfigOptions) -> Task:
|
||||
"""
|
||||
Create a zero-padded sub-task name.
|
||||
Build a shrub task to run the fuzzer.
|
||||
|
||||
:param parent_name: Name of the parent task.
|
||||
:param task_index: Index of this sub-task.
|
||||
:param total_tasks: Total number of sub-tasks being generated.
|
||||
:return: Zero-padded name of sub-task.
|
||||
:param task_name: Parent name of task.
|
||||
:param task_index: Index of sub task being generated.
|
||||
:param options: Options to use for task.
|
||||
:return: Shrub task to run the fuzzer.
|
||||
"""
|
||||
index_width = int(math.ceil(math.log10(total_tasks)))
|
||||
return "{0}_{1}".format(parent_name, str(task_index).zfill(index_width))
|
||||
sub_task_name = taskname.name_generated_task(task_name, task_index, options.num_tasks,
|
||||
options.variant)
|
||||
|
||||
run_jstestfuzz_vars = {
|
||||
"jstestfuzz_vars":
|
||||
"--numGeneratedFiles {0} {1}".format(options.num_files, options.jstestfuzz_vars),
|
||||
"npm_command":
|
||||
options.npm_command,
|
||||
}
|
||||
suite_arg = f"--suites={options.suite}"
|
||||
run_tests_vars = {
|
||||
"continue_on_failure": options.continue_on_failure,
|
||||
"resmoke_args": f"{suite_arg} {options.resmoke_args}",
|
||||
"resmoke_jobs_max": options.resmoke_jobs_max,
|
||||
"should_shuffle": options.should_shuffle,
|
||||
"task_path_suffix": options.use_multiversion,
|
||||
"timeout_secs": options.timeout_secs,
|
||||
"task": options.name
|
||||
} # yapf: disable
|
||||
|
||||
commands = [
|
||||
FunctionCall("do setup"),
|
||||
FunctionCall("do multiversion setup") if options.use_multiversion else None,
|
||||
FunctionCall("setup jstestfuzz"),
|
||||
FunctionCall("run jstestfuzz", run_jstestfuzz_vars),
|
||||
FunctionCall("run generated tests", run_tests_vars)
|
||||
]
|
||||
commands = [command for command in commands if command is not None]
|
||||
|
||||
return Task(sub_task_name, commands, {TaskDependency("compile")})
|
||||
|
||||
|
||||
def generate_evg_tasks(options, evg_config, task_name_suffix=None, display_task=None):
|
||||
def generate_fuzzer_sub_tasks(task_name: str, options: ConfigOptions) -> Set[Task]:
|
||||
"""
|
||||
Generate an evergreen configuration for fuzzers based on the options given.
|
||||
Generate evergreen tasks for fuzzers based on the options given.
|
||||
|
||||
:param task_name: Parent name for tasks being generated.
|
||||
:param options: task options.
|
||||
:return: Set of shrub tasks.
|
||||
"""
|
||||
sub_tasks = {
|
||||
build_fuzzer_sub_task(task_name, index, options)
|
||||
for index in range(options.num_tasks)
|
||||
}
|
||||
return sub_tasks
|
||||
|
||||
|
||||
def create_fuzzer_task(options: ConfigOptions, build_variant: BuildVariant) -> None:
|
||||
"""
|
||||
Generate an evergreen configuration for fuzzers and add it to the given build variant.
|
||||
|
||||
:param options: task options.
|
||||
:param evg_config: evergreen configuration.
|
||||
:param task_name_suffix: suffix to be appended to each task name.
|
||||
:param display_task: an existing display task definition to append to.
|
||||
:return: An evergreen configuration.
|
||||
:param build_variant: Build variant to add tasks to.
|
||||
"""
|
||||
task_names = []
|
||||
task_specs = []
|
||||
task_name = options.name
|
||||
sub_tasks = generate_fuzzer_sub_tasks(task_name, options)
|
||||
|
||||
for task_index in range(options.num_tasks):
|
||||
task_name = options.name if not task_name_suffix else f"{options.name}_{task_name_suffix}"
|
||||
name = taskname.name_generated_task(task_name, task_index, options.num_tasks,
|
||||
options.variant)
|
||||
task_names.append(name)
|
||||
task_specs.append(TaskSpec(name))
|
||||
task = evg_config.task(name)
|
||||
|
||||
commands = [CommandDefinition().function("do setup")]
|
||||
if options.use_multiversion:
|
||||
commands.append(CommandDefinition().function("do multiversion setup"))
|
||||
|
||||
commands.append(CommandDefinition().function("setup jstestfuzz"))
|
||||
commands.append(CommandDefinition().function("run jstestfuzz").vars({
|
||||
"jstestfuzz_vars":
|
||||
"--numGeneratedFiles {0} {1}".format(options.num_files, options.jstestfuzz_vars),
|
||||
"npm_command":
|
||||
options.npm_command
|
||||
}))
|
||||
# Unix path separators are used because Evergreen only runs this script in unix shells,
|
||||
# even on Windows.
|
||||
suite_arg = f"--suites={options.suite}"
|
||||
run_tests_vars = {
|
||||
"continue_on_failure": options.continue_on_failure,
|
||||
"resmoke_args": f"{suite_arg} {options.resmoke_args}",
|
||||
"resmoke_jobs_max": options.resmoke_jobs_max,
|
||||
"should_shuffle": options.should_shuffle,
|
||||
"task_path_suffix": options.use_multiversion,
|
||||
"timeout_secs": options.timeout_secs,
|
||||
"task": options.name
|
||||
} # yapf: disable
|
||||
|
||||
commands.append(CommandDefinition().function("run generated tests").vars(run_tests_vars))
|
||||
task.dependency(TaskDependency("compile")).commands(commands)
|
||||
|
||||
# Create a new DisplayTaskDefinition or append to the one passed in.
|
||||
dt = DisplayTaskDefinition(task_name) if not display_task else display_task
|
||||
dt.execution_tasks(task_names)
|
||||
evg_config.variant(options.variant).tasks(task_specs)
|
||||
if not display_task:
|
||||
dt.execution_task("{0}_gen".format(options.name))
|
||||
evg_config.variant(options.variant).display_task(dt)
|
||||
|
||||
return evg_config
|
||||
build_variant.display_task(task_name, sub_tasks,
|
||||
execution_existing_tasks={ExistingTask(f"{options.name}_gen")})
|
||||
|
||||
|
||||
def main():
|
||||
|
|
@ -184,14 +175,13 @@ def main():
|
|||
options = parser.parse_args()
|
||||
|
||||
config_options = _get_config_options(options, options.expansion_file)
|
||||
evg_config = Configuration()
|
||||
generate_evg_tasks(config_options, evg_config)
|
||||
build_variant = BuildVariant(config_options.variant)
|
||||
create_fuzzer_task(config_options, build_variant)
|
||||
|
||||
if not os.path.exists(CONFIG_DIRECTORY):
|
||||
os.makedirs(CONFIG_DIRECTORY)
|
||||
shrub_project = ShrubProject.empty()
|
||||
shrub_project.add_build_variant(build_variant)
|
||||
|
||||
with open(os.path.join(CONFIG_DIRECTORY, config_options.name + ".json"), "w") as file_handle:
|
||||
file_handle.write(evg_config.to_json())
|
||||
write_file_to_dir(CONFIG_DIRECTORY, f"{config_options.name}.json", shrub_project.json())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -2,35 +2,29 @@
|
|||
"""Generate multiversion tests to run in evergreen in parallel."""
|
||||
|
||||
import datetime
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from typing import Optional, List, Set
|
||||
|
||||
from collections import namedtuple
|
||||
from subprocess import check_output
|
||||
|
||||
import requests
|
||||
import yaml
|
||||
import click
|
||||
import structlog
|
||||
|
||||
from evergreen.api import RetryingEvergreenApi
|
||||
from git import Repo
|
||||
from shrub.config import Configuration
|
||||
from shrub.command import CommandDefinition
|
||||
from shrub.task import TaskDependency
|
||||
from shrub.variant import DisplayTaskDefinition
|
||||
from shrub.variant import TaskSpec
|
||||
from evergreen.api import RetryingEvergreenApi, EvergreenApi
|
||||
from shrub.v2 import ShrubProject, FunctionCall, Task, TaskDependency, BuildVariant, ExistingTask
|
||||
|
||||
from buildscripts.resmokelib import config as _config
|
||||
from buildscripts.resmokelib.multiversionconstants import (LAST_STABLE_MONGO_BINARY,
|
||||
REQUIRES_FCV_TAG)
|
||||
import buildscripts.resmokelib.parser
|
||||
import buildscripts.util.read_config as read_config
|
||||
import buildscripts.util.taskname as taskname
|
||||
from buildscripts.util.fileops import write_file_to_dir
|
||||
import buildscripts.evergreen_generate_resmoke_tasks as generate_resmoke
|
||||
from buildscripts.evergreen_generate_resmoke_tasks import Suite, ConfigOptions
|
||||
import buildscripts.evergreen_gen_fuzzer_tests as gen_fuzzer
|
||||
|
||||
LOGGER = structlog.getLogger(__name__)
|
||||
|
|
@ -71,26 +65,27 @@ def enable_logging():
|
|||
structlog.configure(logger_factory=structlog.stdlib.LoggerFactory())
|
||||
|
||||
|
||||
def prepare_directory_for_suite(directory):
|
||||
"""Ensure that directory exists."""
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
|
||||
def is_suite_sharded(suite_dir, suite_name):
|
||||
def is_suite_sharded(suite_dir: str, suite_name: str) -> bool:
|
||||
"""Return true if a suite uses ShardedClusterFixture."""
|
||||
source_config = generate_resmoke.read_yaml(suite_dir, suite_name + ".yml")
|
||||
return source_config["executor"]["fixture"]["class"] == "ShardedClusterFixture"
|
||||
|
||||
|
||||
def get_multiversion_resmoke_args(is_sharded):
|
||||
def get_version_configs(is_sharded: bool) -> List[str]:
|
||||
"""Get the version configurations to use."""
|
||||
if is_sharded:
|
||||
return SHARDED_MIXED_VERSION_CONFIGS
|
||||
return REPL_MIXED_VERSION_CONFIGS
|
||||
|
||||
|
||||
def get_multiversion_resmoke_args(is_sharded: bool) -> str:
|
||||
"""Return resmoke args used to configure a cluster for multiversion testing."""
|
||||
args_for_sharded_cluster = "--numShards=2 --numReplSetNodes=2 "
|
||||
args_for_replset = "--numReplSetNodes=3 --linearChain=on "
|
||||
return args_for_sharded_cluster if is_sharded else args_for_replset
|
||||
if is_sharded:
|
||||
return "--numShards=2 --numReplSetNodes=2 "
|
||||
return "--numReplSetNodes=3 --linearChain=on "
|
||||
|
||||
|
||||
def get_backports_required_last_stable_hash(task_path_suffix):
|
||||
def get_backports_required_last_stable_hash(task_path_suffix: str):
|
||||
"""Parse the last-stable shell binary to get the commit hash."""
|
||||
last_stable_shell_exec = os.path.join(task_path_suffix, LAST_STABLE_MONGO_BINARY)
|
||||
shell_version = check_output([last_stable_shell_exec, "--version"]).decode('utf-8')
|
||||
|
|
@ -150,86 +145,99 @@ def get_exclude_files(suite_name, task_path_suffix):
|
|||
elem["test_file"] for elem in latest_suite_yaml if elem not in last_stable_suite_yaml)
|
||||
|
||||
|
||||
class EvergreenConfigGenerator(object):
|
||||
def _generate_resmoke_args(suite_file: str, mixed_version_config: str, is_sharded: bool, options,
|
||||
burn_in_test: Optional[str]) -> str:
|
||||
return (f"{options.resmoke_args} --suite={suite_file} --mixedBinVersions={mixed_version_config}"
|
||||
f" --excludeWithAnyTags={EXCLUDE_TAGS} --originSuite={options.suite} "
|
||||
f" {get_multiversion_resmoke_args(is_sharded)} {burn_in_test if burn_in_test else ''}")
|
||||
|
||||
|
||||
class EvergreenMultiversionConfigGenerator(object):
|
||||
"""Generate evergreen configurations for multiversion tests."""
|
||||
|
||||
def __init__(self, evg_api, evg_config, options):
|
||||
"""Create new EvergreenConfigGenerator object."""
|
||||
def __init__(self, evg_api: EvergreenApi, options):
|
||||
"""Create new EvergreenMultiversionConfigGenerator object."""
|
||||
self.evg_api = evg_api
|
||||
self.evg_config = evg_config
|
||||
self.options = options
|
||||
self.task_names = []
|
||||
self.task_specs = []
|
||||
# Strip the "_gen" suffix appended to the name of tasks generated by evergreen.
|
||||
self.task = generate_resmoke.remove_gen_suffix(self.options.task)
|
||||
|
||||
def _generate_sub_task(self, mixed_version_config, task, task_index, suite, num_suites,
|
||||
is_sharded, burn_in_test=None):
|
||||
def _generate_sub_task(self, mixed_version_config: str, task: str, task_index: int, suite: str,
|
||||
num_suites: int, is_sharded: bool,
|
||||
burn_in_test: Optional[str] = None) -> Task:
|
||||
# pylint: disable=too-many-arguments
|
||||
"""Generate a sub task to be run with the provided suite and mixed version config."""
|
||||
"""
|
||||
Generate a sub task to be run with the provided suite and mixed version config.
|
||||
|
||||
:param mixed_version_config: mixed version configuration.
|
||||
:param task: Name of task.
|
||||
:param task_index: Index of task to generate.
|
||||
:param suite: Name of suite being generated.
|
||||
:param num_suites: Number os suites being generated.
|
||||
:param is_sharded: If this is being generated for a sharded configuration.
|
||||
:param burn_in_test: If generation is for burn_in, burn_in options to use.
|
||||
:return: Shrub configuration for task specified.
|
||||
"""
|
||||
# Create a sub task name appended with the task_index and build variant name.
|
||||
task_name = "{0}_{1}".format(task, mixed_version_config)
|
||||
task_name = f"{task}_{mixed_version_config}"
|
||||
sub_task_name = taskname.name_generated_task(task_name, task_index, num_suites,
|
||||
self.options.variant)
|
||||
self.task_names.append(sub_task_name)
|
||||
self.task_specs.append(TaskSpec(sub_task_name))
|
||||
task = self.evg_config.task(sub_task_name)
|
||||
|
||||
gen_task_name = BURN_IN_TASK if burn_in_test is not None else self.task
|
||||
|
||||
commands = [
|
||||
CommandDefinition().function("do setup"),
|
||||
# Fetch and download the proper mongod binaries before running multiversion tests.
|
||||
CommandDefinition().function("do multiversion setup")
|
||||
]
|
||||
run_tests_vars = {
|
||||
"resmoke_args":
|
||||
"{0} --suite={1} --mixedBinVersions={2} --excludeWithAnyTags={3} --originSuite={4} "
|
||||
.format(self.options.resmoke_args, suite, mixed_version_config, EXCLUDE_TAGS,
|
||||
self.options.suite),
|
||||
_generate_resmoke_args(suite, mixed_version_config, is_sharded, self.options,
|
||||
burn_in_test),
|
||||
"task":
|
||||
gen_task_name,
|
||||
}
|
||||
# Update the resmoke args to configure the cluster for multiversion testing.
|
||||
run_tests_vars["resmoke_args"] += get_multiversion_resmoke_args(is_sharded)
|
||||
|
||||
if burn_in_test is not None:
|
||||
run_tests_vars["resmoke_args"] += burn_in_test
|
||||
commands = [
|
||||
FunctionCall("do setup"),
|
||||
# Fetch and download the proper mongod binaries before running multiversion tests.
|
||||
FunctionCall("do multiversion setup"),
|
||||
FunctionCall("run generated tests", run_tests_vars),
|
||||
]
|
||||
|
||||
commands.append(CommandDefinition().function("run generated tests").vars(run_tests_vars))
|
||||
task.dependency(TaskDependency("compile")).commands(commands)
|
||||
return Task(sub_task_name, commands, {TaskDependency("compile")})
|
||||
|
||||
def _write_evergreen_config_to_file(self, task_name):
|
||||
"""Save evergreen config to file."""
|
||||
if not os.path.exists(CONFIG_DIR):
|
||||
os.makedirs(CONFIG_DIR)
|
||||
def _generate_burn_in_execution_tasks(self, version_configs: List[str], suites: List[Suite],
|
||||
burn_in_test: str, burn_in_idx: int,
|
||||
is_sharded: bool) -> Set[Task]:
|
||||
"""
|
||||
Generate shrub tasks for burn_in executions.
|
||||
|
||||
with open(os.path.join(CONFIG_DIR, task_name + ".json"), "w") as file_handle:
|
||||
file_handle.write(self.evg_config.to_json())
|
||||
|
||||
def create_display_task(self, task_name, task_specs, task_list):
|
||||
"""Create the display task definition for the MultiversionConfig object."""
|
||||
dt = DisplayTaskDefinition(task_name).execution_tasks(task_list)\
|
||||
.execution_task("{0}_gen".format(task_name))
|
||||
self.evg_config.variant(self.options.variant).tasks(task_specs).display_task(dt)
|
||||
|
||||
def _generate_burn_in_execution_tasks(self, version_configs, suites, burn_in_test, burn_in_idx,
|
||||
is_sharded):
|
||||
:param version_configs: Version configs to generate for.
|
||||
:param suites: Suites to generate.
|
||||
:param burn_in_test: burn_in_test configuration.
|
||||
:param burn_in_idx: Index of burn_in task being generated.
|
||||
:param is_sharded: If configuration should be generated for sharding tests.
|
||||
:return: Set of generated shrub tasks.
|
||||
"""
|
||||
# pylint: disable=too-many-arguments
|
||||
burn_in_prefix = "burn_in_multiversion"
|
||||
task = "{0}:{1}".format(burn_in_prefix, self.task)
|
||||
task = f"{burn_in_prefix}:{self.task}"
|
||||
|
||||
for version_config in version_configs:
|
||||
# For burn in tasks, it doesn't matter which generated suite yml to use as all the
|
||||
# yaml configurations are the same.
|
||||
source_suite = os.path.join(CONFIG_DIR, suites[0].name + ".yml")
|
||||
# For burn in tasks, it doesn't matter which generated suite yml to use as all the
|
||||
# yaml configurations are the same.
|
||||
source_suite = os.path.join(CONFIG_DIR, suites[0].name + ".yml")
|
||||
tasks = {
|
||||
self._generate_sub_task(version_config, task, burn_in_idx, source_suite, 1, is_sharded,
|
||||
burn_in_test)
|
||||
return self.evg_config
|
||||
for version_config in version_configs
|
||||
}
|
||||
|
||||
def _get_fuzzer_options(self, version_config, is_sharded):
|
||||
fuzzer_config = generate_resmoke.ConfigOptions(self.options.config)
|
||||
return tasks
|
||||
|
||||
def _get_fuzzer_options(self, version_config: str, is_sharded: bool) -> ConfigOptions:
|
||||
"""
|
||||
Get options to generate fuzzer tasks.
|
||||
|
||||
:param version_config: Version configuration to generate for.
|
||||
:param is_sharded: If configuration is for sharded tests.
|
||||
:return: Configuration options to generate fuzzer tasks.
|
||||
"""
|
||||
fuzzer_config = ConfigOptions(self.options.config)
|
||||
fuzzer_config.name = f"{self.options.suite}_multiversion"
|
||||
fuzzer_config.num_files = int(self.options.num_files)
|
||||
fuzzer_config.num_tasks = int(self.options.num_tasks)
|
||||
|
|
@ -238,37 +246,27 @@ class EvergreenConfigGenerator(object):
|
|||
f"--mixedBinVersions={version_config} {add_resmoke_args}"
|
||||
return fuzzer_config
|
||||
|
||||
def _generate_fuzzer_tasks(self, version_configs, is_sharded):
|
||||
dt = DisplayTaskDefinition(self.task)
|
||||
def _generate_fuzzer_tasks(self, build_variant: BuildVariant, version_configs: List[str],
|
||||
is_sharded: bool) -> None:
|
||||
"""
|
||||
Generate fuzzer tasks and add them to the given build variant.
|
||||
|
||||
:param build_variant: Build variant to add tasks to.
|
||||
:param version_configs: Version configurations to generate.
|
||||
:param is_sharded: Should configuration be generated for sharding.
|
||||
"""
|
||||
tasks = set()
|
||||
for version_config in version_configs:
|
||||
fuzzer_config = generate_resmoke.ConfigOptions(self.options.config)
|
||||
fuzzer_config = self._get_fuzzer_options(version_config, is_sharded)
|
||||
gen_fuzzer.generate_evg_tasks(fuzzer_config, self.evg_config,
|
||||
task_name_suffix=version_config, display_task=dt)
|
||||
dt.execution_task(f"{fuzzer_config.name}_gen")
|
||||
self.evg_config.variant(self.options.variant).display_task(dt)
|
||||
return self.evg_config
|
||||
task_name = f"{fuzzer_config.name}_{version_config}"
|
||||
sub_tasks = gen_fuzzer.generate_fuzzer_sub_tasks(task_name, fuzzer_config)
|
||||
tasks = tasks.union(sub_tasks)
|
||||
|
||||
def generate_evg_tasks(self, burn_in_test=None, burn_in_idx=0):
|
||||
# pylint: disable=too-many-locals
|
||||
"""
|
||||
Generate evergreen tasks for multiversion tests.
|
||||
|
||||
The number of tasks generated equals
|
||||
(the number of version configs) * (the number of generated suites).
|
||||
|
||||
:param burn_in_test: The test to be run as part of the burn in multiversion suite.
|
||||
"""
|
||||
is_sharded = is_suite_sharded(TEST_SUITE_DIR, self.options.suite)
|
||||
if is_sharded:
|
||||
version_configs = SHARDED_MIXED_VERSION_CONFIGS
|
||||
else:
|
||||
version_configs = REPL_MIXED_VERSION_CONFIGS
|
||||
|
||||
if self.options.is_jstestfuzz:
|
||||
return self._generate_fuzzer_tasks(version_configs, is_sharded)
|
||||
existing_tasks = {ExistingTask(f"{self.options.suite}_multiversion_gen")}
|
||||
build_variant.display_task(self.task, tasks, execution_existing_tasks=existing_tasks)
|
||||
|
||||
def generate_resmoke_suites(self) -> List[Suite]:
|
||||
"""Generate the resmoke configuration files for this generator."""
|
||||
# Divide tests into suites based on run-time statistics for the last
|
||||
# LOOKBACK_DURATION_DAYS. Tests without enough run-time statistics will be placed
|
||||
# in the misc suite.
|
||||
|
|
@ -282,39 +280,77 @@ class EvergreenConfigGenerator(object):
|
|||
self.options.create_misc_suite)
|
||||
generate_resmoke.write_file_dict(CONFIG_DIR, config_file_dict)
|
||||
|
||||
if burn_in_test is not None:
|
||||
# Generate the subtasks to run burn_in_test against the appropriate mixed version
|
||||
# configurations. The display task is defined later as part of generating the burn
|
||||
# in tests.
|
||||
self._generate_burn_in_execution_tasks(version_configs, suites, burn_in_test,
|
||||
burn_in_idx, is_sharded)
|
||||
return self.evg_config
|
||||
return suites
|
||||
|
||||
def get_burn_in_tasks(self, burn_in_test: str, burn_in_idx: int) -> Set[Task]:
|
||||
"""
|
||||
Get the burn_in tasks being generated.
|
||||
|
||||
:param burn_in_test: Burn in test configuration.
|
||||
:param burn_in_idx: Index of burn_in configuration being generated.
|
||||
:return: Set of shrub tasks for the specified burn_in.
|
||||
"""
|
||||
is_sharded = is_suite_sharded(TEST_SUITE_DIR, self.options.suite)
|
||||
version_configs = get_version_configs(is_sharded)
|
||||
suites = self.generate_resmoke_suites()
|
||||
|
||||
# Generate the subtasks to run burn_in_test against the appropriate mixed version
|
||||
# configurations. The display task is defined later as part of generating the burn
|
||||
# in tests.
|
||||
tasks = self._generate_burn_in_execution_tasks(version_configs, suites, burn_in_test,
|
||||
burn_in_idx, is_sharded)
|
||||
return tasks
|
||||
|
||||
def generate_evg_tasks(self, build_variant: BuildVariant) -> None:
|
||||
# pylint: disable=too-many-locals
|
||||
"""
|
||||
Generate evergreen tasks for multiversion tests.
|
||||
|
||||
The number of tasks generated equals
|
||||
(the number of version configs) * (the number of generated suites).
|
||||
|
||||
:param build_variant: Build variant to add generated configuration to.
|
||||
"""
|
||||
is_sharded = is_suite_sharded(TEST_SUITE_DIR, self.options.suite)
|
||||
version_configs = get_version_configs(is_sharded)
|
||||
|
||||
if self.options.is_jstestfuzz:
|
||||
self._generate_fuzzer_tasks(build_variant, version_configs, is_sharded)
|
||||
|
||||
suites = self.generate_resmoke_suites()
|
||||
sub_tasks = set()
|
||||
for version_config in version_configs:
|
||||
idx = 0
|
||||
for suite in suites:
|
||||
# Generate the newly divided test suites
|
||||
source_suite = os.path.join(CONFIG_DIR, suite.name + ".yml")
|
||||
self._generate_sub_task(version_config, self.task, idx, source_suite, len(suites),
|
||||
is_sharded)
|
||||
sub_tasks.add(
|
||||
self._generate_sub_task(version_config, self.task, idx, source_suite,
|
||||
len(suites), is_sharded))
|
||||
idx += 1
|
||||
|
||||
# Also generate the misc task.
|
||||
misc_suite_name = "{0}_misc".format(self.options.suite)
|
||||
misc_suite = os.path.join(CONFIG_DIR, misc_suite_name + ".yml")
|
||||
self._generate_sub_task(version_config, self.task, idx, misc_suite, 1, is_sharded)
|
||||
sub_tasks.add(
|
||||
self._generate_sub_task(version_config, self.task, idx, misc_suite, 1, is_sharded))
|
||||
idx += 1
|
||||
self.create_display_task(self.task, self.task_specs, self.task_names)
|
||||
return self.evg_config
|
||||
|
||||
def run(self):
|
||||
"""Generate and run multiversion suites that run within a specified target execution time."""
|
||||
build_variant.display_task(self.task, sub_tasks,
|
||||
execution_existing_tasks={ExistingTask(f"{self.task}_gen")})
|
||||
|
||||
def run(self) -> None:
|
||||
"""Generate multiversion suites that run within a specified target execution time."""
|
||||
if not generate_resmoke.should_tasks_be_generated(self.evg_api, self.options.task_id):
|
||||
LOGGER.info("Not generating configuration due to previous successful generation.")
|
||||
return
|
||||
|
||||
self.generate_evg_tasks()
|
||||
self._write_evergreen_config_to_file(self.task)
|
||||
build_variant = BuildVariant(self.options.variant)
|
||||
self.generate_evg_tasks(build_variant)
|
||||
|
||||
shrub_project = ShrubProject.empty()
|
||||
shrub_project.add_build_variant(build_variant)
|
||||
write_file_to_dir(CONFIG_DIR, f"{self.task}.json", shrub_project.json())
|
||||
|
||||
|
||||
@click.group()
|
||||
|
|
@ -328,7 +364,7 @@ def main():
|
|||
help="Location of expansions file generated by evergreen.")
|
||||
@click.option("--evergreen-config", type=str, default=CONFIG_FILE,
|
||||
help="Location of evergreen configuration file.")
|
||||
def run_generate_tasks(expansion_file, evergreen_config=None):
|
||||
def run_generate_tasks(expansion_file: str, evergreen_config: Optional[str] = None):
|
||||
"""
|
||||
Create a configuration for generate tasks to create sub suites for the specified resmoke suite.
|
||||
|
||||
|
|
@ -343,11 +379,9 @@ def run_generate_tasks(expansion_file, evergreen_config=None):
|
|||
:param evergreen_config: Evergreen configuration file.
|
||||
"""
|
||||
evg_api = RetryingEvergreenApi.get_api(config_file=evergreen_config)
|
||||
prepare_directory_for_suite(CONFIG_DIR)
|
||||
evg_config = Configuration()
|
||||
config_options = generate_resmoke.ConfigOptions.from_file(
|
||||
expansion_file, REQUIRED_CONFIG_KEYS, DEFAULT_CONFIG_VALUES, CONFIG_FORMAT_FN)
|
||||
config_generator = EvergreenConfigGenerator(evg_api, evg_config, config_options)
|
||||
config_generator = EvergreenMultiversionConfigGenerator(evg_api, config_options)
|
||||
config_generator.run()
|
||||
|
||||
|
||||
|
|
@ -358,7 +392,7 @@ def run_generate_tasks(expansion_file, evergreen_config=None):
|
|||
help="The directory in which multiversion binaries are stored.")
|
||||
@click.option("--is-generated-suite", type=bool, required=True,
|
||||
help="Indicates whether the suite yaml to update is generated or static.")
|
||||
def generate_exclude_yaml(suite, task_path_suffix, is_generated_suite):
|
||||
def generate_exclude_yaml(suite: str, task_path_suffix: str, is_generated_suite: bool) -> None:
|
||||
# pylint: disable=too-many-locals
|
||||
"""
|
||||
Update the given multiversion suite configuration yaml to exclude tests.
|
||||
|
|
@ -392,8 +426,8 @@ def generate_exclude_yaml(suite, task_path_suffix, is_generated_suite):
|
|||
else:
|
||||
# We expect the generated suites to already have been generated in the generated config
|
||||
# directory.
|
||||
for file_name in os.listdir(CONFIG_DIR):
|
||||
suites_dir = CONFIG_DIR
|
||||
suites_dir = CONFIG_DIR
|
||||
for file_name in os.listdir(suites_dir):
|
||||
# Update the 'exclude_files' for each of the appropriate generated suites.
|
||||
if file_name.endswith('misc.yml'):
|
||||
# New tests will be run as part of misc.yml. We want to make sure to properly
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import os
|
|||
import re
|
||||
import sys
|
||||
from distutils.util import strtobool # pylint: disable=no-name-in-module
|
||||
from typing import Dict, List, Set, Tuple
|
||||
from typing import Dict, List, Set, Sequence, Optional, Any, Match
|
||||
|
||||
import click
|
||||
import requests
|
||||
|
|
@ -23,22 +23,21 @@ import structlog
|
|||
import yaml
|
||||
|
||||
from evergreen.api import EvergreenApi, RetryingEvergreenApi
|
||||
from shrub.config import Configuration
|
||||
from shrub.task import TaskDependency
|
||||
from shrub.variant import DisplayTaskDefinition
|
||||
from shrub.variant import TaskSpec
|
||||
from evergreen.stats import TestStats
|
||||
|
||||
from shrub.v2 import Task, TaskDependency, BuildVariant, ExistingTask, ShrubProject
|
||||
|
||||
# Get relative imports to work when the package is not installed on the PYTHONPATH.
|
||||
if __name__ == "__main__" and __package__ is None:
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import buildscripts.resmokelib.parser as _parser # pylint: disable=wrong-import-position
|
||||
import buildscripts.resmokelib.suitesconfig as suitesconfig # pylint: disable=wrong-import-position
|
||||
import buildscripts.util.read_config as read_config # pylint: disable=wrong-import-position
|
||||
import buildscripts.util.taskname as taskname # pylint: disable=wrong-import-position
|
||||
import buildscripts.util.teststats as teststats # pylint: disable=wrong-import-position
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
import buildscripts.resmokelib.parser as _parser
|
||||
import buildscripts.resmokelib.suitesconfig as suitesconfig
|
||||
from buildscripts.util.fileops import write_file_to_dir
|
||||
import buildscripts.util.read_config as read_config
|
||||
import buildscripts.util.taskname as taskname
|
||||
import buildscripts.util.teststats as teststats
|
||||
from buildscripts.patch_builds.task_generation import TimeoutInfo, resmoke_commands
|
||||
# pylint: enable=wrong-import-position
|
||||
|
||||
|
|
@ -164,6 +163,16 @@ class ConfigOptions(object):
|
|||
"""Whether or not a _misc suite file should be created."""
|
||||
return True
|
||||
|
||||
@property
|
||||
def display_task_name(self):
|
||||
"""Return the name to use as the display task."""
|
||||
return self.task
|
||||
|
||||
@property
|
||||
def gen_task_set(self):
|
||||
"""Return the set of tasks used to generate this configuration."""
|
||||
return {self.task_name}
|
||||
|
||||
@property
|
||||
def variant(self):
|
||||
"""Return build variant is being run on."""
|
||||
|
|
@ -180,17 +189,6 @@ class ConfigOptions(object):
|
|||
|
||||
return config.get(item, None)
|
||||
|
||||
def generate_display_task(self, task_names: List[str]) -> DisplayTaskDefinition:
|
||||
"""
|
||||
Generate a display task with execution tasks.
|
||||
|
||||
:param task_names: The names of the execution tasks to include under the display task.
|
||||
:return: Display task definition for the generated display task.
|
||||
"""
|
||||
return DisplayTaskDefinition(self.task) \
|
||||
.execution_tasks(task_names) \
|
||||
.execution_task("{0}_gen".format(self.task))
|
||||
|
||||
def __getattr__(self, item):
|
||||
"""Determine the value of the given attribute."""
|
||||
return self._lookup(self.config, item)
|
||||
|
|
@ -213,19 +211,7 @@ def enable_logging(verbose):
|
|||
structlog.configure(logger_factory=structlog.stdlib.LoggerFactory())
|
||||
|
||||
|
||||
def write_file(directory: str, filename: str, contents: str):
|
||||
"""
|
||||
Write the given contents to the specified file.
|
||||
|
||||
:param directory: Directory to write file into.
|
||||
:param filename: Name of file to write to.
|
||||
:param contents: Data to write to file.
|
||||
"""
|
||||
with open(os.path.join(directory, filename), "w") as fileh:
|
||||
fileh.write(contents)
|
||||
|
||||
|
||||
def write_file_dict(directory: str, file_dict: Dict[str, str]):
|
||||
def write_file_dict(directory: str, file_dict: Dict[str, str]) -> None:
|
||||
"""
|
||||
Write files in the given dictionary to disk.
|
||||
|
||||
|
|
@ -237,11 +223,8 @@ def write_file_dict(directory: str, file_dict: Dict[str, str]):
|
|||
:param directory: Directory to write files to.
|
||||
:param file_dict: Dictionary of files to write.
|
||||
"""
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
for name, contents in file_dict.items():
|
||||
write_file(directory, name, contents)
|
||||
write_file_to_dir(directory, name, contents)
|
||||
|
||||
|
||||
def read_yaml(directory: str, filename: str) -> Dict:
|
||||
|
|
@ -412,7 +395,7 @@ def generate_resmoke_suite_config(source_config, source_file, roots=None, exclud
|
|||
|
||||
|
||||
def render_suite_files(suites: List, suite_name: str, test_list: List[str], suite_dir,
|
||||
create_misc_suite: bool):
|
||||
create_misc_suite: bool) -> Dict:
|
||||
"""
|
||||
Render the given list of suites.
|
||||
|
||||
|
|
@ -552,12 +535,10 @@ class Suite(object):
|
|||
class EvergreenConfigGenerator(object):
|
||||
"""Generate evergreen configurations."""
|
||||
|
||||
def __init__(self, shrub_config: Configuration, suites: List[Suite], options: ConfigOptions,
|
||||
evg_api: EvergreenApi):
|
||||
def __init__(self, suites: List[Suite], options: ConfigOptions, evg_api: EvergreenApi):
|
||||
"""
|
||||
Create new EvergreenConfigGenerator object.
|
||||
|
||||
:param shrub_config: Shrub configuration the generated Evergreen config will be added to.
|
||||
:param suites: The suite the Evergreen config will be generated for.
|
||||
:param options: The ConfigOptions object containing the config file values.
|
||||
:param evg_api: Evergreen API object.
|
||||
|
|
@ -565,25 +546,38 @@ class EvergreenConfigGenerator(object):
|
|||
self.suites = suites
|
||||
self.options = options
|
||||
self.evg_api = evg_api
|
||||
self.evg_config = shrub_config
|
||||
self.task_specs = []
|
||||
self.task_names = []
|
||||
self.build_tasks = None
|
||||
|
||||
def _set_task_distro(self, task_spec):
|
||||
def _get_distro(self) -> Optional[Sequence[str]]:
|
||||
"""Get the distros that the tasks should be run on."""
|
||||
if self.options.use_large_distro and self.options.large_distro_name:
|
||||
task_spec.distro(self.options.large_distro_name)
|
||||
return [self.options.large_distro_name]
|
||||
return None
|
||||
|
||||
def _generate_resmoke_args(self, suite_file):
|
||||
resmoke_args = "--suite={0}.yml --originSuite={1} {2}".format(
|
||||
suite_file, self.options.suite, self.options.resmoke_args)
|
||||
def _generate_resmoke_args(self, suite_file: str) -> str:
|
||||
"""
|
||||
Generate the resmoke args for the given suite.
|
||||
|
||||
:param suite_file: File containing configuration for test suite.
|
||||
:return: arguments to pass to resmoke.
|
||||
"""
|
||||
resmoke_args = (f"--suite={suite_file}.yml --originSuite={self.options.suite} "
|
||||
f" {self.options.resmoke_args}")
|
||||
if self.options.repeat_suites and not string_contains_any_of_args(
|
||||
resmoke_args, ["repeatSuites", "repeat"]):
|
||||
resmoke_args += " --repeatSuites={0} ".format(self.options.repeat_suites)
|
||||
resmoke_args += f" --repeatSuites={self.options.repeat_suites} "
|
||||
|
||||
return resmoke_args
|
||||
|
||||
def _get_run_tests_vars(self, suite_file):
|
||||
def _get_run_tests_vars(self, suite_file: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Generate a dictionary of the variables to pass to the task.
|
||||
|
||||
:param suite_file: Suite being run.
|
||||
:return: Dictionary containing variables and value to pass to generated task.
|
||||
"""
|
||||
variables = {
|
||||
"resmoke_args": self._generate_resmoke_args(suite_file),
|
||||
"run_multiple_jobs": self.options.run_multiple_jobs,
|
||||
|
|
@ -600,7 +594,8 @@ class EvergreenConfigGenerator(object):
|
|||
|
||||
return variables
|
||||
|
||||
def _get_timeout_command(self, max_test_runtime, expected_suite_runtime, use_default):
|
||||
def _get_timeout_command(self, max_test_runtime: int, expected_suite_runtime: int,
|
||||
use_default: bool) -> TimeoutInfo:
|
||||
"""
|
||||
Add an evergreen command to override the default timeouts to the list of commands.
|
||||
|
||||
|
|
@ -638,38 +633,56 @@ class EvergreenConfigGenerator(object):
|
|||
return TimeoutInfo.default_timeout()
|
||||
|
||||
@staticmethod
|
||||
def _is_task_dependency(task, possible_dependency):
|
||||
return re.match("{0}_(\\d|misc)".format(task), possible_dependency)
|
||||
def _is_task_dependency(task: str, possible_dependency: str) -> Optional[Match[str]]:
|
||||
"""
|
||||
Determine if the given possible_dependency belongs to the given task.
|
||||
|
||||
def _get_tasks_for_depends_on(self, dependent_task):
|
||||
:param task: Name of dependency being checked.
|
||||
:param possible_dependency: Task to check if dependency.
|
||||
:return: None is task is not a dependency.
|
||||
"""
|
||||
return re.match(f"{task}_(\\d|misc)", possible_dependency)
|
||||
|
||||
def _get_tasks_for_depends_on(self, dependent_task: str) -> List[str]:
|
||||
"""
|
||||
Get a list of tasks that belong to the given dependency.
|
||||
|
||||
:param dependent_task: Dependency to check.
|
||||
:return: List of tasks that are a part of the given dependency.
|
||||
"""
|
||||
return [
|
||||
str(task.display_name) for task in self.build_tasks
|
||||
if self._is_task_dependency(dependent_task, str(task.display_name))
|
||||
]
|
||||
|
||||
def _add_dependencies(self, task):
|
||||
task.dependency(TaskDependency("compile"))
|
||||
def _get_dependencies(self) -> Set[TaskDependency]:
|
||||
"""Get the set of dependency tasks for these suites."""
|
||||
dependencies = {TaskDependency("compile")}
|
||||
if not self.options.is_patch:
|
||||
# Don"t worry about task dependencies in patch builds, only mainline.
|
||||
if self.options.depends_on:
|
||||
for dep in self.options.depends_on:
|
||||
depends_on_tasks = self._get_tasks_for_depends_on(dep)
|
||||
for dependency in depends_on_tasks:
|
||||
task.dependency(TaskDependency(dependency))
|
||||
dependencies.add(TaskDependency(dependency))
|
||||
|
||||
return task
|
||||
return dependencies
|
||||
|
||||
def _generate_task(self, sub_suite_name, sub_task_name, target_dir, max_test_runtime=None,
|
||||
expected_suite_runtime=None):
|
||||
"""Generate evergreen config for a resmoke task."""
|
||||
def _generate_task(self, sub_suite_name: str, sub_task_name: str, target_dir: str,
|
||||
max_test_runtime: Optional[int] = None,
|
||||
expected_suite_runtime: Optional[int] = None) -> Task:
|
||||
"""
|
||||
Generate a shrub evergreen config for a resmoke task.
|
||||
|
||||
:param sub_suite_name: Name of suite being generated.
|
||||
:param sub_task_name: Name of task to generate.
|
||||
:param target_dir: Directory containing generated suite files.
|
||||
:param max_test_runtime: Runtime of the longest test in this sub suite.
|
||||
:param expected_suite_runtime: Expected total runtime of this suite.
|
||||
:return: Shrub configuration for the described task.
|
||||
"""
|
||||
# pylint: disable=too-many-arguments
|
||||
LOGGER.debug("Generating task", sub_suite=sub_suite_name)
|
||||
spec = TaskSpec(sub_task_name)
|
||||
self._set_task_distro(spec)
|
||||
self.task_specs.append(spec)
|
||||
|
||||
self.task_names.append(sub_task_name)
|
||||
task = self.evg_config.task(sub_task_name)
|
||||
|
||||
# Evergreen always uses a unix shell, even on Windows, so instead of using os.path.join
|
||||
# here, just use the forward slash; otherwise the path separator will be treated as
|
||||
|
|
@ -683,45 +696,65 @@ class EvergreenConfigGenerator(object):
|
|||
commands = resmoke_commands("run generated tests", run_tests_vars, timeout_info,
|
||||
use_multiversion)
|
||||
|
||||
self._add_dependencies(task).commands(commands)
|
||||
return Task(sub_task_name, commands, self._get_dependencies())
|
||||
|
||||
def _generate_all_tasks(self):
|
||||
for idx, suite in enumerate(self.suites):
|
||||
sub_task_name = taskname.name_generated_task(self.options.task, idx, len(self.suites),
|
||||
self.options.variant)
|
||||
max_runtime = None
|
||||
total_runtime = None
|
||||
if suite.should_overwrite_timeout():
|
||||
max_runtime = suite.max_runtime
|
||||
total_runtime = suite.get_runtime()
|
||||
self._generate_task(suite.name, sub_task_name, self.options.generated_config_dir,
|
||||
max_runtime, total_runtime)
|
||||
def _create_sub_task(self, idx: int, suite: Suite) -> Task:
|
||||
"""
|
||||
Create the sub task for the given suite.
|
||||
|
||||
:param idx: Index of suite to created.
|
||||
:param suite: Suite to create.
|
||||
:return: Shrub configuration for the suite.
|
||||
"""
|
||||
sub_task_name = taskname.name_generated_task(self.options.task, idx, len(self.suites),
|
||||
self.options.variant)
|
||||
max_runtime = None
|
||||
total_runtime = None
|
||||
if suite.should_overwrite_timeout():
|
||||
max_runtime = suite.max_runtime
|
||||
total_runtime = suite.get_runtime()
|
||||
return self._generate_task(suite.name, sub_task_name, self.options.generated_config_dir,
|
||||
max_runtime, total_runtime)
|
||||
|
||||
def _generate_all_tasks(self) -> Set[Task]:
|
||||
"""Get a set of shrub task for all the sub tasks."""
|
||||
tasks = {self._create_sub_task(idx, suite) for idx, suite in enumerate(self.suites)}
|
||||
|
||||
if self.options.create_misc_suite:
|
||||
# Add the misc suite
|
||||
misc_suite_name = f"{os.path.basename(self.options.suite)}_misc"
|
||||
misc_task_name = f"{self.options.task}_misc_{self.options.variant}"
|
||||
self._generate_task(misc_suite_name, misc_task_name, self.options.generated_config_dir)
|
||||
tasks.add(
|
||||
self._generate_task(misc_suite_name, misc_task_name,
|
||||
self.options.generated_config_dir))
|
||||
|
||||
def _generate_variant(self):
|
||||
self._generate_all_tasks()
|
||||
return tasks
|
||||
|
||||
self.evg_config.variant(self.options.variant)\
|
||||
.tasks(self.task_specs)\
|
||||
.display_task(self.options.generate_display_task(self.task_names))
|
||||
def generate_config(self, build_variant: BuildVariant) -> None:
|
||||
"""
|
||||
Generate evergreen configuration.
|
||||
|
||||
def generate_config(self):
|
||||
"""Generate evergreen configuration."""
|
||||
:param build_variant: Build variant to add generated configuration to.
|
||||
"""
|
||||
self.build_tasks = self.evg_api.tasks_by_build(self.options.build_id)
|
||||
self._generate_variant()
|
||||
return self.evg_config
|
||||
|
||||
tasks = self._generate_all_tasks()
|
||||
generating_task = {ExistingTask(task_name) for task_name in self.options.gen_task_set}
|
||||
distros = self._get_distro()
|
||||
build_variant.display_task(self.options.display_task_name, execution_tasks=tasks,
|
||||
execution_existing_tasks=generating_task, distros=distros)
|
||||
|
||||
|
||||
class GenerateSubSuites(object):
|
||||
"""Orchestrate the execution of generate_resmoke_suites."""
|
||||
|
||||
def __init__(self, evergreen_api, config_options):
|
||||
"""Initialize the object."""
|
||||
def __init__(self, evergreen_api: EvergreenApi, config_options: ConfigOptions):
|
||||
"""
|
||||
Initialize the object.
|
||||
|
||||
:param evergreen_api: Evergreen API client.
|
||||
:param config_options: Generation configuration options.
|
||||
"""
|
||||
self.evergreen_api = evergreen_api
|
||||
self.config_options = config_options
|
||||
self.test_list = []
|
||||
|
|
@ -729,8 +762,14 @@ class GenerateSubSuites(object):
|
|||
# Populate config values for methods like list_tests()
|
||||
_parser.set_options()
|
||||
|
||||
def calculate_suites(self, start_date, end_date):
|
||||
"""Divide tests into suites based on statistics for the provided period."""
|
||||
def calculate_suites(self, start_date: datetime, end_date: datetime) -> List[Suite]:
|
||||
"""
|
||||
Divide tests into suites based on statistics for the provided period.
|
||||
|
||||
:param start_date: Time to start historical analysis.
|
||||
:param end_date: Time to end historical analysis.
|
||||
:return: List of sub suites to be generated.
|
||||
"""
|
||||
try:
|
||||
evg_stats = self.get_evg_stats(self.config_options.project, start_date, end_date,
|
||||
self.config_options.task, self.config_options.variant)
|
||||
|
|
@ -751,8 +790,18 @@ class GenerateSubSuites(object):
|
|||
else:
|
||||
raise
|
||||
|
||||
def get_evg_stats(self, project, start_date, end_date, task, variant):
|
||||
"""Collect test execution statistics data from Evergreen."""
|
||||
def get_evg_stats(self, project: str, start_date: datetime, end_date: datetime, task: str,
|
||||
variant: str) -> List[TestStats]:
|
||||
"""
|
||||
Collect test execution statistics data from Evergreen.
|
||||
|
||||
:param project: Evergreen project to query.
|
||||
:param start_date: Time to start historical analysis.
|
||||
:param end_date: Time to end historical analysis.
|
||||
:param task: Task to query.
|
||||
:param variant: Build variant to query.
|
||||
:return: List of test stats for specified task.
|
||||
"""
|
||||
# pylint: disable=too-many-arguments
|
||||
|
||||
days = (end_date - start_date).days
|
||||
|
|
@ -761,8 +810,15 @@ class GenerateSubSuites(object):
|
|||
before_date=end_date.strftime("%Y-%m-%d"), tasks=[task], variants=[variant],
|
||||
group_by="test", group_num_days=days)
|
||||
|
||||
def calculate_suites_from_evg_stats(self, data, execution_time_secs):
|
||||
"""Divide tests into suites that can be run in less than the specified execution time."""
|
||||
def calculate_suites_from_evg_stats(self, data: List[TestStats],
|
||||
execution_time_secs: int) -> List[Suite]:
|
||||
"""
|
||||
Divide tests into suites that can be run in less than the specified execution time.
|
||||
|
||||
:param data: Historical test results for task being split.
|
||||
:param execution_time_secs: Target execution time of each suite (in seconds).
|
||||
:return: List of sub suites calculated.
|
||||
"""
|
||||
test_stats = teststats.TestStats(data)
|
||||
tests_runtimes = self.filter_tests(test_stats.get_tests_runtimes())
|
||||
if not tests_runtimes:
|
||||
|
|
@ -787,7 +843,8 @@ class GenerateSubSuites(object):
|
|||
tests_runtimes)
|
||||
return tests_runtimes
|
||||
|
||||
def filter_existing_tests(self, tests_runtimes):
|
||||
def filter_existing_tests(self, tests_runtimes: List[teststats.TestRuntime]) \
|
||||
-> List[teststats.TestRuntime]:
|
||||
"""Filter out tests that do not exist in the filesystem."""
|
||||
all_tests = [teststats.normalize_test_name(test) for test in self.list_tests()]
|
||||
return [
|
||||
|
|
@ -795,7 +852,7 @@ class GenerateSubSuites(object):
|
|||
if os.path.exists(info.test_name) and info.test_name in all_tests
|
||||
]
|
||||
|
||||
def calculate_fallback_suites(self):
|
||||
def calculate_fallback_suites(self) -> List[Suite]:
|
||||
"""Divide tests into a fixed number of suites."""
|
||||
LOGGER.debug("Splitting tasks based on fallback",
|
||||
fallback=self.config_options.fallback_num_sub_suites)
|
||||
|
|
@ -806,21 +863,31 @@ class GenerateSubSuites(object):
|
|||
suites[idx % num_suites].add_test(test_file, 0)
|
||||
return suites
|
||||
|
||||
def list_tests(self):
|
||||
def list_tests(self) -> List[Dict]:
|
||||
"""List the test files that are part of the suite being split."""
|
||||
return suitesconfig.get_suite(self.config_options.suite).tests
|
||||
|
||||
def generate_task_config(self, shrub_config: Configuration, suites: List[Suite]):
|
||||
def add_suites_to_build_variant(self, suites: List[Suite], build_variant: BuildVariant) -> None:
|
||||
"""
|
||||
Add the given suites to the build variant specified.
|
||||
|
||||
:param suites: Suites to add.
|
||||
:param build_variant: Build variant to add suite to.
|
||||
"""
|
||||
EvergreenConfigGenerator(suites, self.config_options, self.evergreen_api) \
|
||||
.generate_config(build_variant)
|
||||
|
||||
def generate_task_config(self, suites: List[Suite]) -> BuildVariant:
|
||||
"""
|
||||
Generate the evergreen configuration for the new suite.
|
||||
|
||||
:param shrub_config: Shrub configuration the generated Evergreen config will be added to.
|
||||
:param suites: The suite the generated Evergreen config will be generated for.
|
||||
"""
|
||||
EvergreenConfigGenerator(shrub_config, suites, self.config_options,
|
||||
self.evergreen_api).generate_config()
|
||||
build_variant = BuildVariant(self.config_options.variant)
|
||||
self.add_suites_to_build_variant(suites, build_variant)
|
||||
return build_variant
|
||||
|
||||
def generate_suites_config(self, suites: List[Suite]) -> Tuple[dict, str]:
|
||||
def generate_suites_config(self, suites: List[Suite]) -> Dict:
|
||||
"""
|
||||
Generate the suites files and evergreen configuration for the generated task.
|
||||
|
||||
|
|
@ -853,10 +920,10 @@ class GenerateSubSuites(object):
|
|||
|
||||
config_dict_of_suites = self.generate_suites_config(suites)
|
||||
|
||||
shrub_config = Configuration()
|
||||
self.generate_task_config(shrub_config, suites)
|
||||
shrub_config = ShrubProject.empty()
|
||||
shrub_config.add_build_variant(self.generate_task_config(suites))
|
||||
|
||||
config_dict_of_suites[self.config_options.task + ".json"] = shrub_config.to_json()
|
||||
config_dict_of_suites[self.config_options.task + ".json"] = shrub_config.json()
|
||||
write_file_dict(self.config_options.generated_config_dir, config_dict_of_suites)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,19 @@
|
|||
"""Utilities to help generate evergreen tasks."""
|
||||
from typing import Optional, List
|
||||
from __future__ import annotations
|
||||
|
||||
from shrub.command import CommandDefinition
|
||||
from shrub.config import Configuration
|
||||
from shrub.operations import CmdTimeoutUpdate
|
||||
from shrub.task import TaskDependency
|
||||
from shrub.variant import TaskSpec, DisplayTaskDefinition
|
||||
from typing import Any, Dict, Optional, List
|
||||
|
||||
from shrub.v2 import FunctionCall, ShrubProject
|
||||
from shrub.v2.command import timeout_update, ShrubCommand
|
||||
from structlog import get_logger
|
||||
|
||||
LOGGER = get_logger(__name__)
|
||||
MAX_SHRUB_TASKS_FOR_SINGLE_TASK = 1000
|
||||
|
||||
|
||||
def _cmd_by_name(cmd_name):
|
||||
"""
|
||||
Create a command definition of a function with the given name.
|
||||
|
||||
:param cmd_name: Name of function.
|
||||
:return: Command Definition for function.
|
||||
"""
|
||||
return CommandDefinition().function(cmd_name)
|
||||
|
||||
|
||||
def resmoke_commands(run_tests_fn_name, run_tests_vars, timeout_info, use_multiversion=None):
|
||||
def resmoke_commands(run_tests_fn_name: str, run_tests_vars: Dict[str, Any],
|
||||
timeout_info: TimeoutInfo,
|
||||
use_multiversion: Optional[str] = None) -> List[ShrubCommand]:
|
||||
"""
|
||||
Create a list of commands to run a resmoke task.
|
||||
|
||||
|
|
@ -30,9 +25,9 @@ def resmoke_commands(run_tests_fn_name, run_tests_vars, timeout_info, use_multiv
|
|||
"""
|
||||
commands = [
|
||||
timeout_info.cmd,
|
||||
_cmd_by_name("do setup"),
|
||||
_cmd_by_name("do multiversion setup") if use_multiversion else None,
|
||||
_cmd_by_name(run_tests_fn_name).vars(run_tests_vars),
|
||||
FunctionCall("do setup"),
|
||||
FunctionCall("do multiversion setup") if use_multiversion else None,
|
||||
FunctionCall(run_tests_fn_name, run_tests_vars),
|
||||
]
|
||||
|
||||
return [cmd for cmd in commands if cmd]
|
||||
|
|
@ -75,13 +70,7 @@ class TimeoutInfo(object):
|
|||
def cmd(self):
|
||||
"""Create a command that sets timeouts as specified."""
|
||||
if not self.use_defaults:
|
||||
timeout_cmd = CmdTimeoutUpdate()
|
||||
if self.timeout:
|
||||
timeout_cmd.timeout(self.timeout)
|
||||
|
||||
if self.exec_timeout:
|
||||
timeout_cmd.exec_timeout(self.exec_timeout)
|
||||
return timeout_cmd.validate().resolve()
|
||||
return timeout_update(exec_timeout_secs=self.exec_timeout, timeout_secs=self.timeout)
|
||||
|
||||
return None
|
||||
|
||||
|
|
@ -92,70 +81,16 @@ class TimeoutInfo(object):
|
|||
return f"<exec_timeout={self.exec_timeout}, timeout={self.timeout}>"
|
||||
|
||||
|
||||
class TaskList(object):
|
||||
"""A list of evergreen tasks to be generated together."""
|
||||
def validate_task_generation_limit(shrub_project: ShrubProject) -> bool:
|
||||
"""
|
||||
Determine if this shrub configuration generates less than the limit.
|
||||
|
||||
def __init__(self, evg_config: Configuration):
|
||||
"""
|
||||
Create a list of evergreen tasks to create.
|
||||
|
||||
:param evg_config: Evergreen configuration to add tasks to.
|
||||
"""
|
||||
self.evg_config = evg_config
|
||||
self.task_specs = []
|
||||
self.task_names = []
|
||||
|
||||
def add_task(self, name: str, commands: [CommandDefinition],
|
||||
depends_on: Optional[List[str]] = None, distro: Optional[str] = None):
|
||||
"""
|
||||
Add a new task to the task list.
|
||||
|
||||
:param name: Name of task to add.
|
||||
:param commands: List of commands comprising task.
|
||||
:param depends_on: Any dependencies for the task.
|
||||
:param distro: Distro task should be run on.
|
||||
"""
|
||||
task = self.evg_config.task(name)
|
||||
task.commands(commands)
|
||||
|
||||
if depends_on:
|
||||
for dep in depends_on:
|
||||
task.dependency(TaskDependency(dep))
|
||||
|
||||
task_spec = TaskSpec(name)
|
||||
if distro:
|
||||
task_spec.distro(distro)
|
||||
self.task_specs.append(task_spec)
|
||||
self.task_names.append(name)
|
||||
|
||||
def display_task(self, display_name: str, existing_tasks: Optional[List[str]] = None) \
|
||||
-> DisplayTaskDefinition:
|
||||
"""
|
||||
Create a display task for the list of tasks.
|
||||
|
||||
Note: This function should be called after all calls to `add_task` have been done.
|
||||
|
||||
:param display_name: Name of display tasks.
|
||||
:param existing_tasks: Any existing tasks that should be part of the display task.
|
||||
:return: Display task object.
|
||||
"""
|
||||
execution_tasks = self.task_names
|
||||
if existing_tasks:
|
||||
execution_tasks.extend(existing_tasks)
|
||||
|
||||
display_task = DisplayTaskDefinition(display_name).execution_tasks(execution_tasks)
|
||||
return display_task
|
||||
|
||||
def add_to_variant(self, variant_name: str, display_name: Optional[str] = None,
|
||||
existing_tasks: Optional[List[str]] = None):
|
||||
"""
|
||||
Add this task list to a build variant.
|
||||
|
||||
:param variant_name: Variant to add to.
|
||||
:param display_name: Display name to add tasks under.
|
||||
:param existing_tasks: Any existing tasks that should be added to the display group.
|
||||
"""
|
||||
variant = self.evg_config.variant(variant_name)
|
||||
variant.tasks(self.task_specs)
|
||||
if display_name:
|
||||
variant.display_task(self.display_task(display_name, existing_tasks))
|
||||
:param shrub_project: Shrub configuration to validate.
|
||||
:return: True if the configuration is under the limit.
|
||||
"""
|
||||
tasks_to_create = len(shrub_project.all_tasks())
|
||||
if tasks_to_create > MAX_SHRUB_TASKS_FOR_SINGLE_TASK:
|
||||
LOGGER.warning("Attempting to create more tasks than max, aborting", tasks=tasks_to_create,
|
||||
max=MAX_SHRUB_TASKS_FOR_SINGLE_TASK)
|
||||
return False
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Command line utility for determining what jstests should run for the given changed files."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||
from typing import Any, Dict, List, Set
|
||||
|
||||
import click
|
||||
import structlog
|
||||
from structlog.stdlib import LoggerFactory
|
||||
from evergreen.api import EvergreenApi, RetryingEvergreenApi
|
||||
from git import Repo
|
||||
from shrub.config import Configuration
|
||||
from shrub.variant import DisplayTaskDefinition, Variant
|
||||
|
||||
from shrub.v2 import ShrubProject, BuildVariant
|
||||
|
||||
# Get relative imports to work when the package is not installed on the PYTHONPATH.
|
||||
if __name__ == "__main__" and __package__ is None:
|
||||
|
|
@ -29,6 +28,7 @@ from buildscripts.ciconfig.evergreen import (
|
|||
ResmokeArgs,
|
||||
Task,
|
||||
parse_evergreen_file,
|
||||
Variant,
|
||||
)
|
||||
from buildscripts.evergreen_generate_resmoke_tasks import (
|
||||
CONFIG_FORMAT_FN,
|
||||
|
|
@ -127,14 +127,15 @@ class SelectedTestsConfigOptions(ConfigOptions):
|
|||
"""Whether or not a _misc suite file should be created."""
|
||||
return not self.selected_tests_to_run
|
||||
|
||||
def generate_display_task(self, task_names: List[str]) -> DisplayTaskDefinition:
|
||||
"""
|
||||
Generate a display task with execution tasks.
|
||||
@property
|
||||
def display_task_name(self):
|
||||
"""Return the name to use as the display task."""
|
||||
return f"{self.task}_{self.variant}"
|
||||
|
||||
:param task_names: The names of the execution tasks to include under the display task.
|
||||
:return: Display task definition for the generated display task.
|
||||
"""
|
||||
return DisplayTaskDefinition(f"{self.task}_{self.variant}").execution_tasks(task_names)
|
||||
@property
|
||||
def gen_task_set(self):
|
||||
"""Return the set of tasks used to generate this configuration."""
|
||||
return set()
|
||||
|
||||
|
||||
def _configure_logging(verbose: bool):
|
||||
|
|
@ -262,14 +263,15 @@ def _get_evg_task_config(
|
|||
}
|
||||
|
||||
|
||||
def _update_config_with_task(evg_api: EvergreenApi, shrub_config: Configuration,
|
||||
def _update_config_with_task(evg_api: EvergreenApi, build_variant: BuildVariant,
|
||||
config_options: SelectedTestsConfigOptions,
|
||||
config_dict_of_suites_and_tasks: Dict[str, str]):
|
||||
config_dict_of_suites_and_tasks: Dict[str, str]) -> None:
|
||||
"""
|
||||
Generate the suites config and the task shrub config for a given task config.
|
||||
|
||||
:param evg_api: Evergreen API object.
|
||||
:param shrub_config: Shrub configuration for task.
|
||||
:param build_variant: Build variant to add tasks to.
|
||||
:param shrub_project: Shrub configuration for task.
|
||||
:param config_options: Task configuration options.
|
||||
:param config_dict_of_suites_and_tasks: Dict of shrub configs and suite file contents.
|
||||
"""
|
||||
|
|
@ -279,7 +281,7 @@ def _update_config_with_task(evg_api: EvergreenApi, shrub_config: Configuration,
|
|||
config_dict_of_suites = task_generator.generate_suites_config(suites)
|
||||
config_dict_of_suites_and_tasks.update(config_dict_of_suites)
|
||||
|
||||
task_generator.generate_task_config(shrub_config, suites)
|
||||
task_generator.add_suites_to_build_variant(suites, build_variant)
|
||||
|
||||
|
||||
def _get_task_configs_for_test_mappings(selected_tests_variant_expansions: Dict[str, str],
|
||||
|
|
@ -391,7 +393,8 @@ def _get_task_configs(evg_conf: EvergreenProjectConfig,
|
|||
def run(evg_api: EvergreenApi, evg_conf: EvergreenProjectConfig,
|
||||
selected_tests_service: SelectedTestsService,
|
||||
selected_tests_variant_expansions: Dict[str, str], repos: List[Repo],
|
||||
origin_build_variants: List[str]) -> Dict[str, dict]:
|
||||
origin_build_variants: List[str]) -> Dict[str, str]:
|
||||
# pylint: disable=too-many-locals
|
||||
"""
|
||||
Run code to select tasks to run based on test and task mappings for each of the build variants.
|
||||
|
||||
|
|
@ -403,14 +406,15 @@ def run(evg_api: EvergreenApi, evg_conf: EvergreenProjectConfig,
|
|||
:param origin_build_variants: Build variants to collect task info from.
|
||||
:return: Dict of files and file contents for generated tasks.
|
||||
"""
|
||||
shrub_config = Configuration()
|
||||
config_dict_of_suites_and_tasks = {}
|
||||
|
||||
changed_files = find_changed_files_in_repos(repos)
|
||||
changed_files = {_remove_repo_path_prefix(file_path) for file_path in changed_files}
|
||||
LOGGER.debug("Found changed files", files=changed_files)
|
||||
|
||||
shrub_project = ShrubProject()
|
||||
for build_variant in origin_build_variants:
|
||||
shrub_build_variant = BuildVariant(build_variant)
|
||||
build_variant_config = evg_conf.get_variant(build_variant)
|
||||
origin_variant_expansions = build_variant_config.expansions
|
||||
|
||||
|
|
@ -427,10 +431,12 @@ def run(evg_api: EvergreenApi, evg_conf: EvergreenProjectConfig,
|
|||
DEFAULT_CONFIG_VALUES,
|
||||
CONFIG_FORMAT_FN,
|
||||
)
|
||||
_update_config_with_task(evg_api, shrub_config, config_options,
|
||||
_update_config_with_task(evg_api, shrub_build_variant, config_options,
|
||||
config_dict_of_suites_and_tasks)
|
||||
|
||||
config_dict_of_suites_and_tasks["selected_tests_config.json"] = shrub_config.to_json()
|
||||
shrub_project.add_build_variant(shrub_build_variant)
|
||||
|
||||
config_dict_of_suites_and_tasks["selected_tests_config.json"] = shrub_project.json()
|
||||
return config_dict_of_suites_and_tasks
|
||||
|
||||
|
||||
|
|
@ -480,13 +486,11 @@ def main(
|
|||
|
||||
buildscripts.resmokelib.parser.set_options()
|
||||
|
||||
selected_tests_variant_expansions = read_config.read_config_file(expansion_file)
|
||||
origin_build_variants = selected_tests_variant_expansions["selected_tests_buildvariants"].split(
|
||||
" ")
|
||||
task_expansions = read_config.read_config_file(expansion_file)
|
||||
origin_build_variants = task_expansions["selected_tests_buildvariants"].split(" ")
|
||||
|
||||
config_dict_of_suites_and_tasks = run(evg_api, evg_conf, selected_tests_service,
|
||||
selected_tests_variant_expansions, repos,
|
||||
origin_build_variants)
|
||||
task_expansions, repos, origin_build_variants)
|
||||
write_file_dict(SELECTED_TESTS_CONFIG_DIR, config_dict_of_suites_and_tasks)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
"""Unittests for buildscripts.patch_builds.task_generation.py"""
|
||||
import unittest
|
||||
|
||||
from shrub.config import Configuration
|
||||
|
||||
import buildscripts.patch_builds.task_generation as under_test
|
||||
|
||||
# pylint: disable=missing-docstring,protected-access,too-many-lines,no-self-use
|
||||
|
|
@ -63,7 +61,7 @@ class TestTimeoutInfo(unittest.TestCase):
|
|||
timeout = 5
|
||||
timeout_info = under_test.TimeoutInfo.overridden(timeout=timeout)
|
||||
|
||||
cmd = timeout_info.cmd.to_map()
|
||||
cmd = timeout_info.cmd.as_dict()
|
||||
|
||||
self.assertEqual("timeout.update", cmd["command"])
|
||||
self.assertEqual(timeout, cmd["params"]["timeout_secs"])
|
||||
|
|
@ -73,7 +71,7 @@ class TestTimeoutInfo(unittest.TestCase):
|
|||
exec_timeout = 5
|
||||
timeout_info = under_test.TimeoutInfo.overridden(exec_timeout=exec_timeout)
|
||||
|
||||
cmd = timeout_info.cmd.to_map()
|
||||
cmd = timeout_info.cmd.as_dict()
|
||||
|
||||
self.assertEqual("timeout.update", cmd["command"])
|
||||
self.assertEqual(exec_timeout, cmd["params"]["exec_timeout_secs"])
|
||||
|
|
@ -84,7 +82,7 @@ class TestTimeoutInfo(unittest.TestCase):
|
|||
exec_timeout = 5
|
||||
timeout_info = under_test.TimeoutInfo.overridden(exec_timeout=exec_timeout, timeout=timeout)
|
||||
|
||||
cmd = timeout_info.cmd.to_map()
|
||||
cmd = timeout_info.cmd.as_dict()
|
||||
|
||||
self.assertEqual("timeout.update", cmd["command"])
|
||||
self.assertEqual(exec_timeout, cmd["params"]["exec_timeout_secs"])
|
||||
|
|
@ -93,131 +91,3 @@ class TestTimeoutInfo(unittest.TestCase):
|
|||
def test_override_with_no_values(self):
|
||||
with self.assertRaises(ValueError):
|
||||
under_test.TimeoutInfo.overridden()
|
||||
|
||||
|
||||
class TestTaskList(unittest.TestCase):
|
||||
def test_adding_a_task(self):
|
||||
config = Configuration()
|
||||
task_list = under_test.TaskList(config)
|
||||
|
||||
func = "test"
|
||||
task = "task 1"
|
||||
variant = "variant 1"
|
||||
task_list.add_task(task, [under_test._cmd_by_name(func)])
|
||||
task_list.add_to_variant(variant)
|
||||
|
||||
cfg_dict = config.to_map()
|
||||
|
||||
cmd_dict = cfg_dict["tasks"][0]
|
||||
self.assertEqual(task, cmd_dict["name"])
|
||||
self.assertEqual(func, cmd_dict["commands"][0]["func"])
|
||||
|
||||
self.assertEqual(task, cfg_dict["buildvariants"][0]["tasks"][0]["name"])
|
||||
|
||||
def test_adding_a_task_with_distro(self):
|
||||
config = Configuration()
|
||||
task_list = under_test.TaskList(config)
|
||||
|
||||
func = "test"
|
||||
task = "task 1"
|
||||
variant = "variant 1"
|
||||
distro = "distro 1"
|
||||
task_list.add_task(task, [under_test._cmd_by_name(func)], distro=distro)
|
||||
task_list.add_to_variant(variant)
|
||||
|
||||
cfg_dict = config.to_map()
|
||||
|
||||
cmd_dict = cfg_dict["tasks"][0]
|
||||
self.assertEqual(task, cmd_dict["name"])
|
||||
self.assertEqual(func, cmd_dict["commands"][0]["func"])
|
||||
|
||||
self.assertEqual(task, cfg_dict["buildvariants"][0]["tasks"][0]["name"])
|
||||
self.assertIn(distro, cfg_dict["buildvariants"][0]["tasks"][0]["distros"])
|
||||
|
||||
def test_adding_a_task_with_dependecies(self):
|
||||
config = Configuration()
|
||||
task_list = under_test.TaskList(config)
|
||||
|
||||
func = "test"
|
||||
task = "task 1"
|
||||
variant = "variant 1"
|
||||
dependencies = ["dep task 1", "dep task 2"]
|
||||
task_list.add_task(task, [under_test._cmd_by_name(func)], depends_on=dependencies)
|
||||
task_list.add_to_variant(variant)
|
||||
|
||||
cfg_dict = config.to_map()
|
||||
|
||||
cmd_dict = cfg_dict["tasks"][0]
|
||||
self.assertEqual(task, cmd_dict["name"])
|
||||
self.assertEqual(func, cmd_dict["commands"][0]["func"])
|
||||
for dep in dependencies:
|
||||
self.assertIn(dep, {d["name"] for d in cmd_dict["depends_on"]})
|
||||
|
||||
task_dict = cfg_dict["buildvariants"][0]["tasks"][0]
|
||||
self.assertEqual(task, task_dict["name"])
|
||||
|
||||
def test_adding_multiple_tasks(self):
|
||||
config = Configuration()
|
||||
task_list = under_test.TaskList(config)
|
||||
|
||||
func = "test"
|
||||
variant = "variant 1"
|
||||
tasks = ["task 1", "task 2"]
|
||||
for task in tasks:
|
||||
task_list.add_task(task, [under_test._cmd_by_name(func)])
|
||||
|
||||
task_list.add_to_variant(variant)
|
||||
|
||||
cfg_dict = config.to_map()
|
||||
|
||||
self.assertEqual(len(tasks), len(cfg_dict["tasks"]))
|
||||
self.assertEqual(len(tasks), len(cfg_dict["buildvariants"][0]["tasks"]))
|
||||
|
||||
def test_using_display_task(self):
|
||||
config = Configuration()
|
||||
task_list = under_test.TaskList(config)
|
||||
|
||||
func = "test"
|
||||
variant = "variant 1"
|
||||
tasks = ["task 1", "task 2"]
|
||||
for task in tasks:
|
||||
task_list.add_task(task, [under_test._cmd_by_name(func)])
|
||||
|
||||
display_task = "display_task"
|
||||
task_list.add_to_variant(variant, display_task)
|
||||
|
||||
cfg_dict = config.to_map()
|
||||
|
||||
self.assertEqual(len(tasks), len(cfg_dict["tasks"]))
|
||||
variant_dict = cfg_dict["buildvariants"][0]
|
||||
self.assertEqual(len(tasks), len(variant_dict["tasks"]))
|
||||
dt_dict = variant_dict["display_tasks"][0]
|
||||
self.assertEqual(display_task, dt_dict["name"])
|
||||
for task in tasks:
|
||||
self.assertIn(task, dt_dict["execution_tasks"])
|
||||
|
||||
def test_using_display_task_with_existing_tasks(self):
|
||||
config = Configuration()
|
||||
task_list = under_test.TaskList(config)
|
||||
|
||||
func = "test"
|
||||
variant = "variant 1"
|
||||
tasks = ["task 1", "task 2"]
|
||||
for task in tasks:
|
||||
task_list.add_task(task, [under_test._cmd_by_name(func)])
|
||||
|
||||
display_task = "display_task"
|
||||
existing_tasks = ["other task 1", "other task 2"]
|
||||
task_list.add_to_variant(variant, display_task, existing_tasks)
|
||||
|
||||
cfg_dict = config.to_map()
|
||||
|
||||
self.assertEqual(len(tasks), len(cfg_dict["tasks"]))
|
||||
variant_dict = cfg_dict["buildvariants"][0]
|
||||
self.assertEqual(len(tasks), len(variant_dict["tasks"]))
|
||||
dt_dict = variant_dict["display_tasks"][0]
|
||||
self.assertEqual(display_task, dt_dict["name"])
|
||||
for task in tasks:
|
||||
self.assertIn(task, dt_dict["execution_tasks"])
|
||||
for task in existing_tasks:
|
||||
self.assertIn(task, dt_dict["execution_tasks"])
|
||||
|
|
|
|||
|
|
@ -1,23 +1,29 @@
|
|||
"""Unit tests for the burn_in_tags.py script."""
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from shrub.config import Configuration
|
||||
from shrub.v2 import ShrubProject
|
||||
|
||||
import buildscripts.ciconfig.evergreen as _evergreen
|
||||
from buildscripts.tests.test_burn_in_tests import ns as burn_in_tests_ns
|
||||
from buildscripts.ciconfig.evergreen import EvergreenProjectConfig
|
||||
|
||||
import buildscripts.burn_in_tags as under_test
|
||||
|
||||
import buildscripts.ciconfig.evergreen as _evergreen
|
||||
|
||||
from buildscripts.tests.test_burn_in_tests import ns as burn_in_tests_ns
|
||||
|
||||
# pylint: disable=missing-docstring,invalid-name,unused-argument,no-self-use,protected-access
|
||||
|
||||
NS = "buildscripts.burn_in_tags"
|
||||
EMPTY_PROJECT = {
|
||||
"buildvariants": [],
|
||||
"tasks": [],
|
||||
}
|
||||
TEST_FILE_PATH = os.path.join(os.path.dirname(__file__), "test_burn_in_tags_evergreen.yml")
|
||||
|
||||
NS = "buildscripts.burn_in_tags"
|
||||
|
||||
|
||||
def ns(relative_name): # pylint: disable-invalid-name
|
||||
"""Return a full name from a name relative to the test module"s name space."""
|
||||
|
|
@ -40,7 +46,7 @@ def get_expansions_data():
|
|||
} # yapf: disable
|
||||
|
||||
|
||||
def get_evergreen_config():
|
||||
def get_evergreen_config() -> EvergreenProjectConfig:
|
||||
return _evergreen.parse_evergreen_file(TEST_FILE_PATH, evergreen_binary=None)
|
||||
|
||||
|
||||
|
|
@ -77,21 +83,18 @@ class TestGenerateEvgBuildVariants(unittest.TestCase):
|
|||
base_variant = "enterprise-rhel-62-64-bit-inmem"
|
||||
generated_variant = "enterprise-rhel-62-64-bit-inmem-required"
|
||||
burn_in_tags_gen_variant = "enterprise-rhel-62-64-bit"
|
||||
shrub_config = Configuration()
|
||||
variant = evg_conf_mock.get_variant(base_variant)
|
||||
|
||||
under_test._generate_evg_build_variant(shrub_config, base_variant, generated_variant,
|
||||
burn_in_tags_gen_variant, evg_conf_mock)
|
||||
build_variant = under_test._generate_evg_build_variant(variant, generated_variant,
|
||||
burn_in_tags_gen_variant)
|
||||
|
||||
expected_variant_data = get_evergreen_config().get_variant(base_variant)
|
||||
generated_buildvariants = shrub_config.to_map()["buildvariants"]
|
||||
self.assertEqual(len(generated_buildvariants), 1)
|
||||
generated_build_variant = generated_buildvariants[0]
|
||||
generated_build_variant = build_variant.as_dict()
|
||||
self.assertEqual(generated_build_variant["name"], generated_variant)
|
||||
self.assertEqual(generated_build_variant["modules"], expected_variant_data.modules)
|
||||
self.assertEqual(generated_build_variant["modules"], variant.modules)
|
||||
generated_expansions = generated_build_variant["expansions"]
|
||||
burn_in_bypass_expansion_value = generated_expansions.pop("burn_in_bypass")
|
||||
self.assertEqual(burn_in_bypass_expansion_value, burn_in_tags_gen_variant)
|
||||
self.assertEqual(generated_expansions, expected_variant_data.expansions)
|
||||
self.assertEqual(generated_expansions, variant.expansions)
|
||||
|
||||
|
||||
class TestGenerateEvgTasks(unittest.TestCase):
|
||||
|
|
@ -105,13 +108,13 @@ class TestGenerateEvgTasks(unittest.TestCase):
|
|||
"enterprise-rhel-62-64-bit-majority-read-concern-off":
|
||||
"enterprise-rhel-62-64-bit-majority-read-concern-off-required",
|
||||
} # yapf: disable
|
||||
shrub_config = Configuration()
|
||||
shrub_config = ShrubProject()
|
||||
evergreen_api = MagicMock()
|
||||
repo = MagicMock()
|
||||
under_test._generate_evg_tasks(evergreen_api, shrub_config, expansions_file_data,
|
||||
buildvariant_map, [repo], evg_conf_mock)
|
||||
|
||||
self.assertEqual(shrub_config.to_map(), {})
|
||||
self.assertEqual(shrub_config.as_dict(), EMPTY_PROJECT)
|
||||
|
||||
@patch(ns("create_tests_by_task"))
|
||||
def test_generate_evg_tasks_one_test_changed(self, create_tests_by_task_mock):
|
||||
|
|
@ -131,7 +134,7 @@ class TestGenerateEvgTasks(unittest.TestCase):
|
|||
"enterprise-rhel-62-64-bit-majority-read-concern-off":
|
||||
"enterprise-rhel-62-64-bit-majority-read-concern-off-required",
|
||||
} # yapf: disable
|
||||
shrub_config = Configuration()
|
||||
shrub_config = ShrubProject.empty()
|
||||
evergreen_api = MagicMock()
|
||||
repo = MagicMock()
|
||||
evergreen_api.test_stats_by_project.return_value = [
|
||||
|
|
@ -140,13 +143,14 @@ class TestGenerateEvgTasks(unittest.TestCase):
|
|||
under_test._generate_evg_tasks(evergreen_api, shrub_config, expansions_file_data,
|
||||
buildvariant_map, [repo], evg_conf_mock)
|
||||
|
||||
generated_config = shrub_config.to_map()
|
||||
generated_config = shrub_config.as_dict()
|
||||
self.assertEqual(len(generated_config["buildvariants"]), 2)
|
||||
first_generated_build_variant = generated_config["buildvariants"][0]
|
||||
self.assertIn(first_generated_build_variant["name"], buildvariant_map.values())
|
||||
self.assertEqual(first_generated_build_variant["display_tasks"][0]["name"], "burn_in_tests")
|
||||
self.assertEqual(
|
||||
first_generated_build_variant["display_tasks"][0]["execution_tasks"][0],
|
||||
"burn_in:aggregation_mongos_passthrough_0_enterprise-rhel-62-64-bit-inmem-required")
|
||||
f"burn_in:aggregation_mongos_passthrough_0_{first_generated_build_variant['name']}")
|
||||
|
||||
|
||||
EXPANSIONS_FILE_DATA = {
|
||||
|
|
@ -196,7 +200,7 @@ CREATE_TEST_MEMBERSHIP_MAP = {
|
|||
|
||||
|
||||
class TestAcceptance(unittest.TestCase):
|
||||
@patch(ns("_write_to_file"))
|
||||
@patch(ns("write_file_to_dir"))
|
||||
@patch(ns("_create_evg_build_variant_map"))
|
||||
@patch(burn_in_tests_ns("find_changed_tests"))
|
||||
def test_no_tests_run_if_none_changed(self, find_changed_tests_mock,
|
||||
|
|
@ -215,11 +219,11 @@ class TestAcceptance(unittest.TestCase):
|
|||
under_test.burn_in(EXPANSIONS_FILE_DATA, evg_conf_mock, None, repos)
|
||||
|
||||
write_to_file_mock.assert_called_once()
|
||||
shrub_config = write_to_file_mock.call_args[0][0]
|
||||
self.assertEqual('{}', shrub_config.to_json())
|
||||
shrub_config = write_to_file_mock.call_args[0][2]
|
||||
self.assertEqual(EMPTY_PROJECT, json.loads(shrub_config))
|
||||
|
||||
@unittest.skipIf(sys.platform.startswith("win"), "not supported on windows")
|
||||
@patch(ns("_write_to_file"))
|
||||
@patch(ns("write_file_to_dir"))
|
||||
@patch(ns("_create_evg_build_variant_map"))
|
||||
@patch(burn_in_tests_ns("find_changed_tests"))
|
||||
@patch(burn_in_tests_ns("create_test_membership_map"))
|
||||
|
|
@ -244,8 +248,8 @@ class TestAcceptance(unittest.TestCase):
|
|||
under_test.burn_in(EXPANSIONS_FILE_DATA, evg_conf, None, repos)
|
||||
|
||||
write_to_file_mock.assert_called_once()
|
||||
written_config = write_to_file_mock.call_args[0][0]
|
||||
written_config_map = written_config.to_map()
|
||||
written_config = write_to_file_mock.call_args[0][2]
|
||||
written_config_map = json.loads(written_config)
|
||||
|
||||
n_tasks = len(written_config_map["tasks"])
|
||||
# Ensure we are generating at least one task for the test.
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from __future__ import absolute_import
|
|||
|
||||
import collections
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
|
@ -14,7 +15,7 @@ from mock import Mock, patch, MagicMock
|
|||
|
||||
import requests
|
||||
|
||||
from shrub.config import Configuration
|
||||
from shrub.v2 import ShrubProject, BuildVariant
|
||||
|
||||
import buildscripts.burn_in_tests as under_test
|
||||
from buildscripts.ciconfig.evergreen import parse_evergreen_file
|
||||
|
|
@ -78,7 +79,7 @@ class TestAcceptance(unittest.TestCase):
|
|||
def tearDown(self):
|
||||
_parser.set_options()
|
||||
|
||||
@patch(ns("_write_json_file"))
|
||||
@patch(ns("write_file"))
|
||||
def test_no_tests_run_if_none_changed(self, write_json_mock):
|
||||
"""
|
||||
Given a git repository with no changes,
|
||||
|
|
@ -99,13 +100,13 @@ class TestAcceptance(unittest.TestCase):
|
|||
repos, None)
|
||||
|
||||
write_json_mock.assert_called_once()
|
||||
written_config = write_json_mock.call_args[0][0]
|
||||
written_config = json.loads(write_json_mock.call_args[0][1])
|
||||
display_task = written_config["buildvariants"][0]["display_tasks"][0]
|
||||
self.assertEqual(1, len(display_task["execution_tasks"]))
|
||||
self.assertEqual(under_test.BURN_IN_TESTS_GEN_TASK, display_task["execution_tasks"][0])
|
||||
|
||||
@unittest.skipIf(sys.platform.startswith("win"), "not supported on windows")
|
||||
@patch(ns("_write_json_file"))
|
||||
@patch(ns("write_file"))
|
||||
def test_tests_generated_if_a_file_changed(self, write_json_mock):
|
||||
"""
|
||||
Given a git repository with changes,
|
||||
|
|
@ -130,7 +131,7 @@ class TestAcceptance(unittest.TestCase):
|
|||
None)
|
||||
|
||||
write_json_mock.assert_called_once()
|
||||
written_config = write_json_mock.call_args[0][0]
|
||||
written_config = json.loads(write_json_mock.call_args[0][1])
|
||||
n_tasks = len(written_config["tasks"])
|
||||
# Ensure we are generating at least one task for the test.
|
||||
self.assertGreaterEqual(n_tasks, 1)
|
||||
|
|
@ -468,31 +469,31 @@ TESTS_BY_TASK = {
|
|||
class TestCreateGenerateTasksConfig(unittest.TestCase):
|
||||
@unittest.skipIf(sys.platform.startswith("win"), "not supported on windows")
|
||||
def test_no_tasks_given(self):
|
||||
evg_config = Configuration()
|
||||
build_variant = BuildVariant("build variant")
|
||||
gen_config = MagicMock(run_build_variant="variant")
|
||||
repeat_config = MagicMock()
|
||||
|
||||
evg_config = under_test.create_generate_tasks_config(evg_config, {}, gen_config,
|
||||
repeat_config, None)
|
||||
under_test.create_generate_tasks_config(build_variant, {}, gen_config, repeat_config, None)
|
||||
|
||||
evg_config_dict = evg_config.to_map()
|
||||
self.assertNotIn("tasks", evg_config_dict)
|
||||
evg_config_dict = build_variant.as_dict()
|
||||
self.assertEqual(0, len(evg_config_dict["tasks"]))
|
||||
|
||||
@unittest.skipIf(sys.platform.startswith("win"), "not supported on windows")
|
||||
def test_one_task_one_test(self):
|
||||
n_tasks = 1
|
||||
n_tests = 1
|
||||
resmoke_options = "options for resmoke"
|
||||
evg_config = Configuration()
|
||||
build_variant = BuildVariant("build variant")
|
||||
gen_config = MagicMock(run_build_variant="variant", distro=None)
|
||||
repeat_config = MagicMock()
|
||||
repeat_config.generate_resmoke_options.return_value = resmoke_options
|
||||
tests_by_task = create_tests_by_task_mock(n_tasks, n_tests)
|
||||
|
||||
evg_config = under_test.create_generate_tasks_config(evg_config, tests_by_task, gen_config,
|
||||
repeat_config, None)
|
||||
under_test.create_generate_tasks_config(build_variant, tests_by_task, gen_config,
|
||||
repeat_config, None)
|
||||
|
||||
evg_config_dict = evg_config.to_map()
|
||||
shrub_config = ShrubProject.empty().add_build_variant(build_variant)
|
||||
evg_config_dict = shrub_config.as_dict()
|
||||
tasks = evg_config_dict["tasks"]
|
||||
self.assertEqual(n_tasks * n_tests, len(tasks))
|
||||
cmd = tasks[0]["commands"]
|
||||
|
|
@ -504,58 +505,30 @@ class TestCreateGenerateTasksConfig(unittest.TestCase):
|
|||
def test_n_task_m_test(self):
|
||||
n_tasks = 3
|
||||
n_tests = 5
|
||||
evg_config = Configuration()
|
||||
build_variant = BuildVariant("build variant")
|
||||
gen_config = MagicMock(run_build_variant="variant", distro=None)
|
||||
repeat_config = MagicMock()
|
||||
tests_by_task = create_tests_by_task_mock(n_tasks, n_tests)
|
||||
|
||||
evg_config = under_test.create_generate_tasks_config(evg_config, tests_by_task, gen_config,
|
||||
repeat_config, None)
|
||||
under_test.create_generate_tasks_config(build_variant, tests_by_task, gen_config,
|
||||
repeat_config, None)
|
||||
|
||||
evg_config_dict = evg_config.to_map()
|
||||
evg_config_dict = build_variant.as_dict()
|
||||
self.assertEqual(n_tasks * n_tests, len(evg_config_dict["tasks"]))
|
||||
|
||||
|
||||
class TestCreateGenerateTasksFile(unittest.TestCase):
|
||||
@unittest.skipIf(sys.platform.startswith("win"), "not supported on windows")
|
||||
@patch("buildscripts.burn_in_tests.create_generate_tasks_config")
|
||||
def test_gen_tasks_configuration_is_returned(self, gen_tasks_config_mock):
|
||||
@patch(ns("sys.exit"))
|
||||
@patch(ns("create_generate_tasks_config"))
|
||||
@patch(ns("validate_task_generation_limit"))
|
||||
def test_cap_on_task_generate(self, validate_mock, _, exit_mock):
|
||||
evg_api = MagicMock()
|
||||
gen_config = MagicMock(use_multiversion=False)
|
||||
repeat_config = MagicMock()
|
||||
tests_by_task = MagicMock()
|
||||
|
||||
task_list = [f"task_{i}" for i in range(10)]
|
||||
|
||||
evg_config = MagicMock()
|
||||
evg_config.to_map.return_value = {
|
||||
"tasks": task_list,
|
||||
}
|
||||
|
||||
gen_tasks_config_mock.return_value = evg_config
|
||||
|
||||
config = under_test.create_generate_tasks_file(tests_by_task, gen_config, repeat_config,
|
||||
evg_api)
|
||||
|
||||
self.assertEqual(config, evg_config.to_map.return_value)
|
||||
|
||||
@unittest.skipIf(sys.platform.startswith("win"), "not supported on windows")
|
||||
@patch("buildscripts.burn_in_tests.sys.exit")
|
||||
@patch("buildscripts.burn_in_tests.create_generate_tasks_config")
|
||||
def test_cap_on_task_generate(self, gen_tasks_config_mock, exit_mock):
|
||||
evg_api = MagicMock()
|
||||
gen_config = MagicMock(use_multiversion=False)
|
||||
repeat_config = MagicMock()
|
||||
tests_by_task = MagicMock()
|
||||
|
||||
task_list = [f"task_{i}" for i in range(1005)]
|
||||
|
||||
evg_config = MagicMock()
|
||||
evg_config.to_map.return_value = {
|
||||
"tasks": task_list,
|
||||
}
|
||||
|
||||
gen_tasks_config_mock.return_value = evg_config
|
||||
validate_mock.return_value = False
|
||||
|
||||
exit_mock.side_effect = ValueError("exiting")
|
||||
with self.assertRaises(ValueError):
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import unittest
|
|||
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from shrub.config import Configuration
|
||||
from shrub.v2 import BuildVariant, ShrubProject
|
||||
|
||||
import buildscripts.burn_in_tests_multiversion as under_test
|
||||
from buildscripts.burn_in_tests import create_generate_tasks_file, _gather_task_info, create_generate_tasks_config
|
||||
from buildscripts.burn_in_tests import _gather_task_info, create_generate_tasks_config
|
||||
from buildscripts.ciconfig.evergreen import parse_evergreen_file
|
||||
import buildscripts.resmokelib.parser as _parser
|
||||
import buildscripts.evergreen_gen_multiversion_tests as gen_multiversion
|
||||
|
|
@ -102,20 +102,18 @@ def create_variant_task_mock(task_name, suite_name, distro="distro"):
|
|||
|
||||
class TestCreateMultiversionGenerateTasksConfig(unittest.TestCase):
|
||||
def tests_no_tasks_given(self):
|
||||
evg_config = Configuration()
|
||||
gen_config = MagicMock(run_build_variant="variant", fallback_num_sub_suites=1,
|
||||
project="project", build_variant="build_variant", task_id="task_id",
|
||||
target_resmoke_time=60)
|
||||
evg_api = MagicMock()
|
||||
evg_config = under_test.create_multiversion_generate_tasks_config(
|
||||
evg_config, {}, evg_api, gen_config)
|
||||
evg_config_dict = evg_config.to_map()
|
||||
self.assertNotIn("tasks", evg_config_dict)
|
||||
build_variant = under_test.create_multiversion_generate_tasks_config({}, evg_api,
|
||||
gen_config)
|
||||
evg_config_dict = build_variant.as_dict()
|
||||
self.assertEqual(0, len(evg_config_dict["tasks"]))
|
||||
|
||||
def test_tasks_not_in_multiversion_suites(self):
|
||||
n_tasks = 1
|
||||
n_tests = 1
|
||||
evg_config = Configuration()
|
||||
gen_config = MagicMock(run_build_variant="variant", fallback_num_sub_suites=1,
|
||||
project="project", build_variant="build_variant", task_id="task_id",
|
||||
target_resmoke_time=60)
|
||||
|
|
@ -123,28 +121,27 @@ class TestCreateMultiversionGenerateTasksConfig(unittest.TestCase):
|
|||
|
||||
# Create a tests_by_tasks dict that doesn't contain any multiversion suites.
|
||||
tests_by_task = create_tests_by_task_mock(n_tasks, n_tests)
|
||||
evg_config = under_test.create_multiversion_generate_tasks_config(
|
||||
evg_config, tests_by_task, evg_api, gen_config)
|
||||
evg_config_dict = evg_config.to_map()
|
||||
build_variant = under_test.create_multiversion_generate_tasks_config(
|
||||
tests_by_task, evg_api, gen_config)
|
||||
evg_config_dict = build_variant.as_dict()
|
||||
|
||||
# We should not generate any tasks that are not part of the burn_in_multiversion suite.
|
||||
self.assertNotIn("tasks", evg_config_dict)
|
||||
self.assertEqual(0, len(evg_config_dict["tasks"]))
|
||||
|
||||
@patch("buildscripts.evergreen_gen_multiversion_tests.get_backports_required_last_stable_hash")
|
||||
def test_one_task_one_test(self, mock_hash):
|
||||
mock_hash.return_value = MONGO_4_2_HASH
|
||||
n_tasks = 1
|
||||
n_tests = 1
|
||||
evg_config = Configuration()
|
||||
gen_config = MagicMock(run_build_variant="variant", fallback_num_sub_suites=1,
|
||||
project="project", build_variant="build_variant", task_id="task_id",
|
||||
target_resmoke_time=60)
|
||||
evg_api = MagicMock()
|
||||
|
||||
tests_by_task = create_multiversion_tests_by_task_mock(n_tasks, n_tests)
|
||||
evg_config = under_test.create_multiversion_generate_tasks_config(
|
||||
evg_config, tests_by_task, evg_api, gen_config)
|
||||
evg_config_dict = evg_config.to_map()
|
||||
build_variant = under_test.create_multiversion_generate_tasks_config(
|
||||
tests_by_task, evg_api, gen_config)
|
||||
evg_config_dict = build_variant.as_dict()
|
||||
tasks = evg_config_dict["tasks"]
|
||||
self.assertEqual(len(tasks), NUM_REPL_MIXED_VERSION_CONFIGS * n_tests)
|
||||
|
||||
|
|
@ -153,16 +150,15 @@ class TestCreateMultiversionGenerateTasksConfig(unittest.TestCase):
|
|||
mock_hash.return_value = MONGO_4_2_HASH
|
||||
n_tasks = 2
|
||||
n_tests = 1
|
||||
evg_config = Configuration()
|
||||
gen_config = MagicMock(run_build_variant="variant", fallback_num_sub_suites=1,
|
||||
project="project", build_variant="build_variant", task_id="task_id",
|
||||
target_resmoke_time=60)
|
||||
evg_api = MagicMock()
|
||||
|
||||
tests_by_task = create_multiversion_tests_by_task_mock(n_tasks, n_tests)
|
||||
evg_config = under_test.create_multiversion_generate_tasks_config(
|
||||
evg_config, tests_by_task, evg_api, gen_config)
|
||||
evg_config_dict = evg_config.to_map()
|
||||
build_variant = under_test.create_multiversion_generate_tasks_config(
|
||||
tests_by_task, evg_api, gen_config)
|
||||
evg_config_dict = build_variant.as_dict()
|
||||
tasks = evg_config_dict["tasks"]
|
||||
self.assertEqual(
|
||||
len(tasks),
|
||||
|
|
@ -173,16 +169,15 @@ class TestCreateMultiversionGenerateTasksConfig(unittest.TestCase):
|
|||
mock_hash.return_value = MONGO_4_2_HASH
|
||||
n_tasks = 1
|
||||
n_tests = 2
|
||||
evg_config = Configuration()
|
||||
gen_config = MagicMock(run_build_variant="variant", fallback_num_sub_suites=1,
|
||||
project="project", build_variant="build_variant", task_id="task_id",
|
||||
target_resmoke_time=60)
|
||||
evg_api = MagicMock()
|
||||
|
||||
tests_by_task = create_multiversion_tests_by_task_mock(n_tasks, n_tests)
|
||||
evg_config = under_test.create_multiversion_generate_tasks_config(
|
||||
evg_config, tests_by_task, evg_api, gen_config)
|
||||
evg_config_dict = evg_config.to_map()
|
||||
build_variant = under_test.create_multiversion_generate_tasks_config(
|
||||
tests_by_task, evg_api, gen_config)
|
||||
evg_config_dict = build_variant.as_dict()
|
||||
tasks = evg_config_dict["tasks"]
|
||||
self.assertEqual(len(tasks), NUM_REPL_MIXED_VERSION_CONFIGS * n_tests)
|
||||
|
||||
|
|
@ -191,16 +186,15 @@ class TestCreateMultiversionGenerateTasksConfig(unittest.TestCase):
|
|||
mock_hash.return_value = MONGO_4_2_HASH
|
||||
n_tasks = 2
|
||||
n_tests = 3
|
||||
evg_config = Configuration()
|
||||
gen_config = MagicMock(run_build_variant="variant", fallback_num_sub_suites=1,
|
||||
project="project", build_variant="build_variant", task_id="task_id",
|
||||
target_resmoke_time=60)
|
||||
evg_api = MagicMock()
|
||||
|
||||
tests_by_task = create_multiversion_tests_by_task_mock(n_tasks, n_tests)
|
||||
evg_config = under_test.create_multiversion_generate_tasks_config(
|
||||
evg_config, tests_by_task, evg_api, gen_config)
|
||||
evg_config_dict = evg_config.to_map()
|
||||
build_variant = under_test.create_multiversion_generate_tasks_config(
|
||||
tests_by_task, evg_api, gen_config)
|
||||
evg_config_dict = build_variant.as_dict()
|
||||
tasks = evg_config_dict["tasks"]
|
||||
self.assertEqual(
|
||||
len(tasks),
|
||||
|
|
@ -228,7 +222,7 @@ class TestCreateGenerateTasksConfig(unittest.TestCase):
|
|||
def test_multiversion_path_is_used(self):
|
||||
n_tasks = 1
|
||||
n_tests = 1
|
||||
evg_config = Configuration()
|
||||
build_variant = BuildVariant("variant")
|
||||
gen_config = MagicMock(run_build_variant="variant", distro=None)
|
||||
repeat_config = MagicMock()
|
||||
tests_by_task = create_tests_by_task_mock(n_tasks, n_tests)
|
||||
|
|
@ -236,46 +230,14 @@ class TestCreateGenerateTasksConfig(unittest.TestCase):
|
|||
multiversion_path = "multiversion_path"
|
||||
tests_by_task[first_task]["use_multiversion"] = multiversion_path
|
||||
|
||||
evg_config = create_generate_tasks_config(evg_config, tests_by_task, gen_config,
|
||||
repeat_config, None)
|
||||
create_generate_tasks_config(build_variant, tests_by_task, gen_config, repeat_config, None)
|
||||
|
||||
evg_config_dict = evg_config.to_map()
|
||||
shrub_project = ShrubProject.empty().add_build_variant(build_variant)
|
||||
evg_config_dict = shrub_project.as_dict()
|
||||
tasks = evg_config_dict["tasks"]
|
||||
self.assertEqual(n_tasks * n_tests, len(tasks))
|
||||
self.assertEqual(multiversion_path, tasks[0]["commands"][2]["vars"]["task_path_suffix"])
|
||||
|
||||
@unittest.skipIf(sys.platform.startswith("win"), "not supported on windows")
|
||||
@patch(bit_ns("create_generate_tasks_config"))
|
||||
def test_gen_tasks_multiversion_configuration_is_returned(self, gen_tasks_config_mock): # pylint: disable=invalid-name
|
||||
evg_api = MagicMock()
|
||||
gen_config = MagicMock(run_build_variant="variant", project="project",
|
||||
build_variant="build_variant", task_id="task_id",
|
||||
use_multiversion=True)
|
||||
repeat_config = MagicMock()
|
||||
tests_by_task = MagicMock()
|
||||
|
||||
evg_config = MagicMock()
|
||||
evg_config.to_map.return_value = {
|
||||
'buildvariants': [
|
||||
{
|
||||
'name': 'build_variant',
|
||||
'display_tasks': [
|
||||
{
|
||||
'name': 'burn_in_tests_multiversion',
|
||||
'execution_tasks': [
|
||||
'burn_in_tests_multiversion_gen'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
} # yapf: disable
|
||||
|
||||
gen_tasks_config_mock.return_value = evg_config
|
||||
|
||||
config = create_generate_tasks_file(tests_by_task, gen_config, repeat_config, evg_api)
|
||||
self.assertEqual(config, evg_config.to_map.return_value)
|
||||
|
||||
|
||||
class TestGatherTaskInfo(unittest.TestCase):
|
||||
def test_multiversion_task(self):
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@
|
|||
import unittest
|
||||
import mock
|
||||
|
||||
from shrub.config import Configuration
|
||||
from shrub.v2 import BuildVariant, ShrubProject
|
||||
|
||||
from buildscripts import evergreen_gen_fuzzer_tests as gft
|
||||
from buildscripts import evergreen_gen_fuzzer_tests as under_test
|
||||
|
||||
# pylint: disable=missing-docstring,protected-access
|
||||
|
||||
|
||||
class TestGenerateEvgTasks(unittest.TestCase):
|
||||
class TestCreateFuzzerTask(unittest.TestCase):
|
||||
@staticmethod
|
||||
def _create_options_mock():
|
||||
options = mock.Mock
|
||||
|
|
@ -31,10 +31,12 @@ class TestGenerateEvgTasks(unittest.TestCase):
|
|||
return options
|
||||
|
||||
def test_evg_config_is_created_without_multiversion(self):
|
||||
evg_config = Configuration()
|
||||
build_variant = BuildVariant("build variant")
|
||||
options = self._create_options_mock()
|
||||
|
||||
config = gft.generate_evg_tasks(options, evg_config).to_map()
|
||||
under_test.create_fuzzer_task(options, build_variant)
|
||||
shrub_project = ShrubProject.empty().add_build_variant(build_variant)
|
||||
config = shrub_project.as_dict()
|
||||
|
||||
self.assertEqual(options.num_tasks, len(config["tasks"]))
|
||||
|
||||
|
|
@ -54,11 +56,13 @@ class TestGenerateEvgTasks(unittest.TestCase):
|
|||
self.assertIn(options.name + "_gen", buildvariant["display_tasks"][0]["execution_tasks"])
|
||||
|
||||
def test_evg_config_is_created_with_multiversion(self):
|
||||
evg_config = Configuration()
|
||||
build_variant = BuildVariant("build variant")
|
||||
options = self._create_options_mock()
|
||||
options.use_multiversion = "/data/multiversion"
|
||||
|
||||
config = gft.generate_evg_tasks(options, evg_config).to_map()
|
||||
under_test.create_fuzzer_task(options, build_variant)
|
||||
shrub_project = ShrubProject.empty().add_build_variant(build_variant)
|
||||
config = shrub_project.as_dict()
|
||||
|
||||
self.assertEqual("do multiversion setup", config["tasks"][0]["commands"][1]["func"])
|
||||
self.assertEqual("/data/multiversion",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import unittest
|
|||
import requests
|
||||
import yaml
|
||||
from mock import patch, MagicMock
|
||||
from shrub.config import Configuration
|
||||
from shrub.v2 import BuildVariant, ShrubProject
|
||||
from shrub.variant import DisplayTaskDefinition
|
||||
|
||||
from buildscripts.util.teststats import TestRuntime
|
||||
|
|
@ -350,17 +350,6 @@ class TestConfigOptions(unittest.TestCase):
|
|||
self.assertEqual(1, config_options.number)
|
||||
self.assertIsInstance(config_options.number, int)
|
||||
|
||||
def test_generate_display_task(self):
|
||||
config = {"task_name": "my_task"}
|
||||
|
||||
config_options = under_test.ConfigOptions(config)
|
||||
display_task = config_options.generate_display_task(["task_1", "task_2"])
|
||||
|
||||
self.assertEqual("my_task", display_task._name)
|
||||
self.assertIn(config_options.task + "_gen", display_task.to_map()["execution_tasks"])
|
||||
self.assertIn("task_1", display_task.to_map()["execution_tasks"])
|
||||
self.assertIn("task_2", display_task.to_map()["execution_tasks"])
|
||||
|
||||
|
||||
class DivideRemainingTestsAmongSuitesTest(unittest.TestCase):
|
||||
@staticmethod
|
||||
|
|
@ -607,13 +596,15 @@ class EvergreenConfigGeneratorTest(unittest.TestCase):
|
|||
return options
|
||||
|
||||
def test_evg_config_is_created(self):
|
||||
shrub_config = Configuration()
|
||||
options = self.generate_mock_options()
|
||||
suites = self.generate_mock_suites(3)
|
||||
build_variant = BuildVariant("variant")
|
||||
|
||||
config = under_test.EvergreenConfigGenerator(shrub_config, suites, options,
|
||||
MagicMock()).generate_config().to_map()
|
||||
generator = under_test.EvergreenConfigGenerator(suites, options, MagicMock())
|
||||
generator.generate_config(build_variant)
|
||||
|
||||
shrub_project = ShrubProject.empty().add_build_variant(build_variant)
|
||||
config = shrub_project.as_dict()
|
||||
self.assertEqual(len(config["tasks"]), len(suites) + 1)
|
||||
command1 = config["tasks"][0]["commands"][2]
|
||||
self.assertIn(options.resmoke_args, command1["vars"]["resmoke_args"])
|
||||
|
|
@ -622,33 +613,38 @@ class EvergreenConfigGeneratorTest(unittest.TestCase):
|
|||
self.assertEqual("run generated tests", command1["func"])
|
||||
|
||||
def test_evg_config_is_created_with_diff_task_and_suite(self):
|
||||
shrub_config = Configuration()
|
||||
options = self.generate_mock_options()
|
||||
options.task = "task"
|
||||
options.display_task_name = "display task"
|
||||
options.generate_display_task.return_value = DisplayTaskDefinition("task")
|
||||
suites = self.generate_mock_suites(3)
|
||||
build_variant = BuildVariant("variant")
|
||||
|
||||
config = under_test.EvergreenConfigGenerator(shrub_config, suites, options,
|
||||
MagicMock()).generate_config().to_map()
|
||||
generator = under_test.EvergreenConfigGenerator(suites, options, MagicMock())
|
||||
generator.generate_config(build_variant)
|
||||
|
||||
shrub_project = ShrubProject.empty().add_build_variant(build_variant)
|
||||
config = shrub_project.as_dict()
|
||||
self.assertEqual(len(config["tasks"]), len(suites) + 1)
|
||||
display_task = config["buildvariants"][0]["display_tasks"][0]
|
||||
self.assertEqual(options.task, display_task["name"])
|
||||
self.assertEqual(options.display_task_name, display_task["name"])
|
||||
|
||||
task = config["tasks"][0]
|
||||
self.assertIn(options.variant, task["name"])
|
||||
self.assertIn(options.suite, task["commands"][2]["vars"]["resmoke_args"])
|
||||
|
||||
def test_evg_config_can_use_large_distro(self):
|
||||
shrub_config = Configuration()
|
||||
options = self.generate_mock_options()
|
||||
options.use_large_distro = "true"
|
||||
options.large_distro_name = "large distro name"
|
||||
|
||||
suites = self.generate_mock_suites(3)
|
||||
build_variant = BuildVariant("variant")
|
||||
|
||||
config = under_test.EvergreenConfigGenerator(shrub_config, suites, options,
|
||||
MagicMock()).generate_config().to_map()
|
||||
generator = under_test.EvergreenConfigGenerator(suites, options, MagicMock())
|
||||
generator.generate_config(build_variant)
|
||||
|
||||
shrub_project = ShrubProject.empty().add_build_variant(build_variant)
|
||||
config = shrub_project.as_dict()
|
||||
|
||||
self.assertEqual(len(config["tasks"]), len(suites) + 1)
|
||||
self.assertEqual(options.large_distro_name,
|
||||
|
|
@ -665,12 +661,10 @@ class EvergreenConfigGeneratorTest(unittest.TestCase):
|
|||
self.assertTrue(is_task_dependency("sharding", "sharding_misc"))
|
||||
|
||||
def test_get_tasks_depends_on(self):
|
||||
shrub_config = Configuration()
|
||||
options = self.generate_mock_options()
|
||||
suites = self.generate_mock_suites(3)
|
||||
|
||||
cfg_generator = under_test.EvergreenConfigGenerator(shrub_config, suites, options,
|
||||
MagicMock())
|
||||
cfg_generator = under_test.EvergreenConfigGenerator(suites, options, MagicMock())
|
||||
cfg_generator.build_tasks = [
|
||||
MagicMock(display_name="sharding_gen"),
|
||||
MagicMock(display_name="sharding_0"),
|
||||
|
|
@ -688,14 +682,12 @@ class EvergreenConfigGeneratorTest(unittest.TestCase):
|
|||
self.assertIn("sharding_misc", dependent_tasks)
|
||||
|
||||
def test_specified_dependencies_are_added(self):
|
||||
shrub_config = Configuration()
|
||||
options = self.generate_mock_options()
|
||||
options.depends_on = ["sharding"]
|
||||
options.is_patch = False
|
||||
suites = self.generate_mock_suites(3)
|
||||
|
||||
cfg_generator = under_test.EvergreenConfigGenerator(shrub_config, suites, options,
|
||||
MagicMock())
|
||||
cfg_generator = under_test.EvergreenConfigGenerator(suites, options, MagicMock())
|
||||
cfg_generator.build_tasks = [
|
||||
MagicMock(display_name="sharding_gen"),
|
||||
MagicMock(display_name="sharding_0"),
|
||||
|
|
@ -706,19 +698,20 @@ class EvergreenConfigGeneratorTest(unittest.TestCase):
|
|||
MagicMock(display_name="sharding_misc"),
|
||||
]
|
||||
|
||||
cfg_mock = MagicMock()
|
||||
cfg_generator._add_dependencies(cfg_mock)
|
||||
self.assertEqual(4, cfg_mock.dependency.call_count)
|
||||
dependencies = cfg_generator._get_dependencies()
|
||||
self.assertEqual(4, len(dependencies))
|
||||
|
||||
def test_evg_config_has_timeouts_for_repeated_suites(self):
|
||||
shrub_config = Configuration()
|
||||
options = self.generate_mock_options()
|
||||
options.repeat_suites = 5
|
||||
suites = self.generate_mock_suites(3)
|
||||
build_variant = BuildVariant("variant")
|
||||
|
||||
config = under_test.EvergreenConfigGenerator(shrub_config, suites, options,
|
||||
MagicMock()).generate_config().to_map()
|
||||
generator = under_test.EvergreenConfigGenerator(suites, options, MagicMock())
|
||||
generator.generate_config(build_variant)
|
||||
|
||||
shrub_project = ShrubProject.empty().add_build_variant(build_variant)
|
||||
config = shrub_project.as_dict()
|
||||
self.assertEqual(len(config["tasks"]), len(suites) + 1)
|
||||
command1 = config["tasks"][0]["commands"][2]
|
||||
self.assertIn(" --repeatSuites=5 ", command1["vars"]["resmoke_args"])
|
||||
|
|
@ -731,74 +724,86 @@ class EvergreenConfigGeneratorTest(unittest.TestCase):
|
|||
self.assertEqual(expected_exec_timeout, timeout_cmd["params"]["exec_timeout_secs"])
|
||||
|
||||
def test_evg_config_has_fails_if_timeout_too_high(self):
|
||||
shrub_config = Configuration()
|
||||
options = self.generate_mock_options()
|
||||
options.repeat_suites = under_test.MAX_EXPECTED_TIMEOUT
|
||||
suites = self.generate_mock_suites(3)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
under_test.EvergreenConfigGenerator(shrub_config, suites, options,
|
||||
MagicMock()).generate_config()
|
||||
generator = under_test.EvergreenConfigGenerator(suites, options, MagicMock())
|
||||
generator.generate_config(MagicMock())
|
||||
|
||||
def test_evg_config_does_not_fails_if_timeout_too_high_on_mainline(self):
|
||||
shrub_config = Configuration()
|
||||
options = self.generate_mock_options()
|
||||
options.is_patch = False
|
||||
options.repeat_suites = under_test.MAX_EXPECTED_TIMEOUT
|
||||
suites = self.generate_mock_suites(3)
|
||||
build_variant = BuildVariant("variant")
|
||||
|
||||
config = under_test.EvergreenConfigGenerator(shrub_config, suites, options,
|
||||
MagicMock()).generate_config().to_map()
|
||||
generator = under_test.EvergreenConfigGenerator(suites, options, MagicMock())
|
||||
generator.generate_config(build_variant)
|
||||
|
||||
config = build_variant.as_dict()
|
||||
self.assertEqual(len(config["tasks"]), len(suites) + 1)
|
||||
|
||||
def test_evg_config_does_not_overwrite_repeatSuites_resmoke_arg_with_repeatSuites_default(self):
|
||||
shrub_config = Configuration()
|
||||
options = self.generate_mock_options()
|
||||
options.resmoke_args = "resmoke_args --repeatSuites=5"
|
||||
suites = self.generate_mock_suites(1)
|
||||
|
||||
config = under_test.EvergreenConfigGenerator(shrub_config, suites, options,
|
||||
MagicMock()).generate_config().to_map()
|
||||
build_variant = BuildVariant("variant")
|
||||
|
||||
generator = under_test.EvergreenConfigGenerator(suites, options, MagicMock())
|
||||
generator.generate_config(build_variant)
|
||||
|
||||
shrub_project = ShrubProject.empty().add_build_variant(build_variant)
|
||||
config = shrub_project.as_dict()
|
||||
command1 = config["tasks"][0]["commands"][2]
|
||||
self.assertIn("--repeatSuites=5", command1["vars"]["resmoke_args"])
|
||||
self.assertNotIn("--repeatSuites=1", command1["vars"]["resmoke_args"])
|
||||
|
||||
def test_evg_config_does_not_overwrite_repeat_resmoke_arg_with_repeatSuites_default(self):
|
||||
shrub_config = Configuration()
|
||||
options = self.generate_mock_options()
|
||||
options.resmoke_args = "resmoke_args --repeat=5"
|
||||
suites = self.generate_mock_suites(1)
|
||||
build_variant = BuildVariant("variant")
|
||||
|
||||
config = under_test.EvergreenConfigGenerator(shrub_config, suites, options,
|
||||
MagicMock()).generate_config().to_map()
|
||||
generator = under_test.EvergreenConfigGenerator(suites, options, MagicMock())
|
||||
generator.generate_config(build_variant)
|
||||
|
||||
shrub_project = ShrubProject.empty().add_build_variant(build_variant)
|
||||
config = shrub_project.as_dict()
|
||||
command1 = config["tasks"][0]["commands"][2]
|
||||
self.assertIn("--repeat=5", command1["vars"]["resmoke_args"])
|
||||
self.assertNotIn("--repeatSuites=1", command1["vars"]["resmoke_args"])
|
||||
|
||||
def test_suites_without_enough_info_should_not_include_timeouts(self):
|
||||
shrub_config = Configuration()
|
||||
suite_without_timing_info = 1
|
||||
options = self.generate_mock_options()
|
||||
suites = self.generate_mock_suites(3)
|
||||
suites[suite_without_timing_info].should_overwrite_timeout.return_value = False
|
||||
build_variant = BuildVariant("variant")
|
||||
|
||||
config = under_test.EvergreenConfigGenerator(shrub_config, suites, options,
|
||||
MagicMock()).generate_config().to_map()
|
||||
generator = under_test.EvergreenConfigGenerator(suites, options, MagicMock())
|
||||
generator.generate_config(build_variant)
|
||||
|
||||
shrub_project = ShrubProject.empty().add_build_variant(build_variant)
|
||||
config = shrub_project.as_dict()
|
||||
timeout_cmd = config["tasks"][suite_without_timing_info]["commands"][0]
|
||||
self.assertNotIn("command", timeout_cmd)
|
||||
self.assertEqual("do setup", timeout_cmd["func"])
|
||||
|
||||
def test_timeout_info_not_included_if_use_default_timeouts_set(self):
|
||||
shrub_config = Configuration()
|
||||
suite_without_timing_info = 1
|
||||
options = self.generate_mock_options()
|
||||
suites = self.generate_mock_suites(3)
|
||||
options.use_default_timeouts = True
|
||||
build_variant = BuildVariant("variant")
|
||||
|
||||
config = under_test.EvergreenConfigGenerator(shrub_config, suites, options,
|
||||
MagicMock()).generate_config().to_map()
|
||||
generator = under_test.EvergreenConfigGenerator(suites, options, MagicMock())
|
||||
generator.generate_config(build_variant)
|
||||
|
||||
shrub_project = ShrubProject.empty().add_build_variant(build_variant)
|
||||
config = shrub_project.as_dict()
|
||||
timeout_cmd = config["tasks"][suite_without_timing_info]["commands"][0]
|
||||
self.assertNotIn("command", timeout_cmd)
|
||||
self.assertEqual("do setup", timeout_cmd["func"])
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
"""Unit tests for the selected_tests script."""
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
import unittest
|
||||
from typing import Dict, Any
|
||||
|
||||
from mock import MagicMock, patch
|
||||
from shrub.config import Configuration
|
||||
from shrub.v2 import BuildVariant, ShrubProject
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
import buildscripts.ciconfig.evergreen as _evergreen
|
||||
|
|
@ -22,6 +23,16 @@ def ns(relative_name): # pylint: disable=invalid-name
|
|||
return NS + "." + relative_name
|
||||
|
||||
|
||||
def empty_build_variant(variant_name: str) -> Dict[str, Any]:
|
||||
return {
|
||||
"buildvariants": [{
|
||||
"name": variant_name,
|
||||
"tasks": [],
|
||||
}],
|
||||
"tasks": [],
|
||||
}
|
||||
|
||||
|
||||
class TestAcceptance(unittest.TestCase):
|
||||
"""A suite of Acceptance tests for selected_tests."""
|
||||
|
||||
|
|
@ -52,7 +63,9 @@ class TestAcceptance(unittest.TestCase):
|
|||
selected_tests_variant_expansions, repos,
|
||||
origin_build_variants)
|
||||
|
||||
self.assertEqual(config_dict["selected_tests_config.json"], "{}")
|
||||
self.assertEqual(
|
||||
json.loads(config_dict["selected_tests_config.json"]),
|
||||
empty_build_variant(origin_build_variants[0]))
|
||||
|
||||
@unittest.skipIf(sys.platform.startswith("win"), "not supported on windows")
|
||||
def test_when_test_mappings_are_found_for_changed_files(self):
|
||||
|
|
@ -193,17 +206,6 @@ class TestSelectedTestsConfigOptions(unittest.TestCase):
|
|||
|
||||
self.assertFalse(config_options.create_misc_suite)
|
||||
|
||||
@patch(ns("read_config"))
|
||||
def test_generate_display_task(self, read_config_mock):
|
||||
config_options = under_test.SelectedTestsConfigOptions(
|
||||
{"task_name": "my_task", "build_variant": "my_variant"}, {}, {}, {})
|
||||
|
||||
display_task = config_options.generate_display_task(["task_1", "task_2"])
|
||||
|
||||
self.assertEqual("my_task_my_variant", display_task._name)
|
||||
self.assertIn("task_1", display_task.to_map()["execution_tasks"])
|
||||
self.assertIn("task_2", display_task.to_map()["execution_tasks"])
|
||||
|
||||
|
||||
class TestFindSelectedTestFiles(unittest.TestCase):
|
||||
@patch(ns("is_file_a_test_file"))
|
||||
|
|
@ -371,27 +373,6 @@ class TestGetEvgTaskConfig(unittest.TestCase):
|
|||
|
||||
|
||||
class TestUpdateConfigDictWithTask(unittest.TestCase):
|
||||
@patch(ns("SelectedTestsConfigOptions"))
|
||||
@patch(ns("GenerateSubSuites"))
|
||||
def test_suites_and_tasks_are_generated(self, generate_subsuites_mock,
|
||||
selected_tests_config_options_mock):
|
||||
suites_config_mock = {"my_suite_0.yml": "suite file contents"}
|
||||
generate_subsuites_mock.return_value.generate_suites_config.return_value = suites_config_mock
|
||||
|
||||
def generate_task_config(shrub_config, suites):
|
||||
shrub_config.task("my_fake_task")
|
||||
|
||||
generate_subsuites_mock.return_value.generate_task_config.side_effect = generate_task_config
|
||||
|
||||
shrub_config = Configuration()
|
||||
config_dict_of_suites_and_tasks = {}
|
||||
under_test._update_config_with_task(
|
||||
evg_api=MagicMock(), shrub_config=shrub_config, config_options=MagicMock(),
|
||||
config_dict_of_suites_and_tasks=config_dict_of_suites_and_tasks)
|
||||
|
||||
self.assertEqual(config_dict_of_suites_and_tasks, suites_config_mock)
|
||||
self.assertIn("my_fake_task", shrub_config.to_json())
|
||||
|
||||
@patch(ns("SelectedTestsConfigOptions"))
|
||||
@patch(ns("GenerateSubSuites"))
|
||||
def test_no_suites_or_tasks_are_generated(self, generate_subsuites_mock,
|
||||
|
|
@ -403,14 +384,15 @@ class TestUpdateConfigDictWithTask(unittest.TestCase):
|
|||
|
||||
generate_subsuites_mock.return_value.generate_task_config.side_effect = generate_task_config
|
||||
|
||||
shrub_config = Configuration()
|
||||
build_variant = BuildVariant("variant")
|
||||
config_dict_of_suites_and_tasks = {}
|
||||
under_test._update_config_with_task(
|
||||
evg_api=MagicMock(), shrub_config=shrub_config, config_options=MagicMock(),
|
||||
MagicMock(), build_variant, config_options=MagicMock(),
|
||||
config_dict_of_suites_and_tasks=config_dict_of_suites_and_tasks)
|
||||
|
||||
shrub_project = ShrubProject.empty().add_build_variant(build_variant)
|
||||
self.assertEqual(config_dict_of_suites_and_tasks, {})
|
||||
self.assertEqual(shrub_config.to_json(), "{}")
|
||||
self.assertEqual(shrub_project.as_dict(), empty_build_variant("variant"))
|
||||
|
||||
|
||||
class TestGetTaskConfigsForTestMappings(unittest.TestCase):
|
||||
|
|
|
|||
|
|
@ -25,3 +25,30 @@ def get_file_handle(path, append_file=False):
|
|||
"""Open 'path', truncate it if 'append_file' is False, and return file handle."""
|
||||
mode = "a+" if append_file else "w"
|
||||
return open(path, mode)
|
||||
|
||||
|
||||
def write_file(path: str, contents: str) -> None:
|
||||
"""
|
||||
Write the contents provided to the file in the specified path.
|
||||
|
||||
:param path: Path of file to write.
|
||||
:param contents: Contents to write to file.
|
||||
"""
|
||||
with open(path, "w") as file_handle:
|
||||
file_handle.write(contents)
|
||||
|
||||
|
||||
def write_file_to_dir(directory: str, file: str, contents: str) -> None:
|
||||
"""
|
||||
Write the contents provided to the file in the given directory.
|
||||
|
||||
The directory will be created if it does not exist.
|
||||
|
||||
:param directory: Directory to write to.
|
||||
:param file: Name of file to write.
|
||||
:param contents: Contents to write to file.
|
||||
"""
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
write_file(os.path.join(directory, file), contents)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ PyKMIP == 0.4.0 # It's now 0.8.0. We're far enough back to have API conflicts.
|
|||
evergreen.py == 0.3.9
|
||||
jinja2
|
||||
mock
|
||||
shrub.py == 0.2.3
|
||||
shrub.py == 1.0.2
|
||||
ocspresponder == 0.5.0
|
||||
flask == 1.1.1
|
||||
ocspbuilder == 0.10.2
|
||||
|
|
|
|||
Loading…
Reference in New Issue