mongo/buildscripts/tests/test_evergreen_gen_multiver...

273 lines
10 KiB
Python

''' Tests for the multiversion generators '''
import os
import unittest
from tempfile import TemporaryDirectory, NamedTemporaryFile
from mock import patch, MagicMock
from click.testing import CliRunner
from buildscripts import evergreen_gen_multiversion_tests as under_test
import buildscripts.evergreen_generate_resmoke_tasks as generate_resmoke
from buildscripts.util.fileops import read_yaml_file
# pylint: disable=missing-docstring, no-self-use
class TestRun(unittest.TestCase):
def setUp(self):
self._tmpdir = TemporaryDirectory()
def tearDown(self):
self._tmpdir.cleanup()
under_test.CONFIG_DIR = generate_resmoke.DEFAULT_CONFIG_VALUES
@patch.object(under_test.EvergreenMultiversionConfigGenerator, 'generate_evg_tasks')
@patch('buildscripts.evergreen_generate_resmoke_tasks.should_tasks_be_generated')
@patch('buildscripts.evergreen_gen_multiversion_tests.write_file_to_dir')
def test_empty_result_config_fails(self, generate_evg_tasks, should_tasks_be_generated,
write_file_to_dir):
# pylint: disable=unused-argument
''' Hijacks the write_file_to_dir function to prevent the configuration
from being written to disk, and ensure the command fails '''
under_test.CONFIG_DIR = self._tmpdir.name
# NamedTemporaryFile doesn't work too well on Windows. We need to
# close the fd's so that run_generate_tasks can open the files,
# so we override the delete-on-close behaviour on Windows, and manually
# handle cleanup later
is_windows = os.name == 'nt'
with NamedTemporaryFile(mode='w',
delete=not is_windows) as expansions_file, NamedTemporaryFile(
mode='w', delete=not is_windows) as evg_conf:
expansions_file.write(EXPANSIONS)
expansions_file.flush()
should_tasks_be_generated.return_value = True
if is_windows:
# on windows we need to close the fd's so that
# run_generate_tasks can open the file handle
expansions_file.close()
evg_conf.close()
runner = CliRunner()
result = runner.invoke(
under_test.run_generate_tasks,
['--expansion-file', expansions_file.name, '--evergreen-config', evg_conf.name])
self.assertEqual(result.exit_code, 1, result)
self.assertTrue(isinstance(result.exception, RuntimeError))
self.assertEqual(
str(result.exception),
f"Multiversion suite generator unexpectedly yielded no configuration in '{self._tmpdir.name}'"
)
self.assertEqual(write_file_to_dir.call_count, 1)
if is_windows:
# on windows we need to manually delete these files, since
# we've disabled the delete-on-close mechanics
os.remove(expansions_file.name)
os.remove(evg_conf.name)
class TestGenerateExcludeYaml(unittest.TestCase):
def setUp(self):
self._tmpdir = TemporaryDirectory()
def tearDown(self):
if self._tmpdir is not None:
self._tmpdir.cleanup()
def assert_contents(self, expected):
actual = read_yaml_file(os.path.join(self._tmpdir.name, under_test.EXCLUDE_TAGS_FILE))
self.assertEqual(actual, expected)
def patch_and_run(self, latest, last_stable):
"""
Helper to patch and run the test.
"""
mock_multiversion_methods = {
'get_backports_required_last_stable_hash': MagicMock(),
'get_last_stable_yaml': MagicMock(return_value=last_stable)
}
with patch.multiple('buildscripts.evergreen_gen_multiversion_tests',
**mock_multiversion_methods):
with patch('buildscripts.evergreen_gen_multiversion_tests.read_yaml_file',
return_value=latest) as mock_read_yaml:
output = os.path.join(self._tmpdir.name, under_test.EXCLUDE_TAGS_FILE)
runner = CliRunner()
result = runner.invoke(
under_test.generate_exclude_yaml,
[f"--output={output}", '--task-path-suffix=/data/multiversion'])
self.assertEqual(result.exit_code, 0, result)
mock_read_yaml.assert_called_once()
mock_multiversion_methods[
'get_backports_required_last_stable_hash'].assert_called_once()
mock_multiversion_methods['get_last_stable_yaml'].assert_called_once()
def test_create_yaml_suite1(self):
latest_yaml = {
'all': [{'ticket': 'fake_ticket0', 'test_file': 'jstests/fake_file0.js'}], 'suites': {
'suite1': [{'ticket': 'fake_ticket1', 'test_file': 'jstests/fake_file1.js'},
{'ticket': 'fake_ticket2', 'test_file': 'jstests/fake_file2.js'}]
}
}
last_stable_yaml = {
'all': [{'ticket': 'fake_ticket0', 'test_file': 'jstests/fake_file0.js'}], 'suites': {
'suite1': [{'ticket': 'fake_ticket2', 'test_file': 'jstests/fake_file2.js'}]
}
}
expected = {
'selector': {
'js_test': {'jstests/fake_file1.js': ['suite1_backport_required_multiversion']}
}
}
self.patch_and_run(latest_yaml, last_stable_yaml)
self.assert_contents(expected)
def test_create_yaml_suite1_and_suite2(self):
latest_yaml = {
'all': [{'ticket': 'fake_ticket0', 'test_file': 'jstests/fake_file0.js'}], 'suites': {
'suite1': [{'ticket': 'fake_ticket1', 'test_file': 'jstests/fake_file1.js'},
{'ticket': 'fake_ticket2', 'test_file': 'jstests/fake_file2.js'}],
'suite2': [{'ticket': 'fake_ticket1', 'test_file': 'jstests/fake_file1.js'}]
}
}
last_stable_yaml = {
'all': [{'ticket': 'fake_ticket0', 'test_file': 'jstests/fake_file0.js'}], 'suites': {
'suite1': [{'ticket': 'fake_ticket2', 'test_file': 'jstests/fake_file2.js'}]
}
}
expected = {
'selector': {
'js_test': {
'jstests/fake_file1.js': [
'suite1_backport_required_multiversion',
'suite2_backport_required_multiversion'
]
}
}
}
self.patch_and_run(latest_yaml, last_stable_yaml)
self.assert_contents(expected)
def test_both_all_are_none(self):
latest_yaml = {
'all': None, 'suites': {
'suite1': [{'ticket': 'fake_ticket1', 'test_file': 'jstests/fake_file1.js'},
{'ticket': 'fake_ticket2', 'test_file': 'jstests/fake_file2.js'}]
}
}
last_stable_yaml = {
'all': None, 'suites': {
'suite1': [{'ticket': 'fake_ticket2', 'test_file': 'jstests/fake_file2.js'}]
}
}
expected = {
'selector': {
'js_test': {'jstests/fake_file1.js': ['suite1_backport_required_multiversion']}
}
}
self.patch_and_run(latest_yaml, last_stable_yaml)
self.assert_contents(expected)
def test_old_all_is_none(self):
latest_yaml = {
'all': [{'ticket': 'fake_ticket0', 'test_file': 'jstests/fake_file0.js'}], 'suites': {
'suite1': [{'ticket': 'fake_ticket1', 'test_file': 'jstests/fake_file1.js'},
{'ticket': 'fake_ticket2', 'test_file': 'jstests/fake_file2.js'}]
}
}
last_stable_yaml = {
'all': None, 'suites': {
'suite1': [{'ticket': 'fake_ticket2', 'test_file': 'jstests/fake_file2.js'}]
}
}
expected = {
'selector': {
'js_test': {
'jstests/fake_file1.js': ['suite1_backport_required_multiversion'],
'jstests/fake_file0.js': ['backport_required_multiversion']
}
}
}
self.patch_and_run(latest_yaml, last_stable_yaml)
self.assert_contents(expected)
def test_create_yaml_suite1_and_all(self):
latest_yaml = {
'all': [{'ticket': 'fake_ticket0', 'test_file': 'jstests/fake_file0.js'},
{'ticket': 'fake_ticket4', 'test_file': 'jstests/fake_file4.js'}], 'suites': {
'suite1': [{'ticket': 'fake_ticket1', 'test_file': 'jstests/fake_file1.js'},
{'ticket': 'fake_ticket2', 'test_file': 'jstests/fake_file2.js'}]
}
}
last_stable_yaml = {
'all': [{'ticket': 'fake_ticket0', 'test_file': 'jstests/fake_file0.js'}], 'suites': {
'suite1': [{'ticket': 'fake_ticket2', 'test_file': 'jstests/fake_file2.js'}]
}
}
expected = {
'selector': {
'js_test': {
'jstests/fake_file1.js': ['suite1_backport_required_multiversion'],
'jstests/fake_file4.js': ['backport_required_multiversion']
}
}
}
self.patch_and_run(latest_yaml, last_stable_yaml)
self.assert_contents(expected)
# Can delete after backporting the changed yml syntax.
def test_not_backported(self):
latest_yaml = {
'all': [{'ticket': 'fake_ticket0', 'test_file': 'jstests/fake_file0.js'}], 'suites': {
'suite1': [{'ticket': 'fake_ticket2', 'test_file': 'jstests/fake_file2.js'},
{'ticket': 'fake_ticket3', 'test_file': 'jstests/fake_file3.js'}]
}
}
last_stable_yaml = {
'suite1': [{'ticket': 'fake_ticket2', 'test_file': 'jstests/fake_file2.js'}]
}
expected = {
'selector': {
'js_test': {
'jstests/fake_file0.js': ['backport_required_multiversion'],
'jstests/fake_file3.js': ['suite1_backport_required_multiversion']
}
}
}
self.patch_and_run(latest_yaml, last_stable_yaml)
self.assert_contents(expected)
EXPANSIONS = """task: t
build_variant: bv
fallback_num_sub_suites: 5
project: p
task_id: t0
task_name: t
use_multiversion: "true"
"""
if __name__ == '__main__':
unittest.main()