Bug 1646813 - Part 2. write tests for taskgraph/utils/chunking.py r=ahal

Changes:
  - added tests that exercise manifest loading, mozinfo guessing and the overall process of chunking.
  - tests added for both web-platform and traditional mochitest/xpcshell suites.

Differential Revision: https://phabricator.services.mozilla.com/D80985
This commit is contained in:
Edwin Takahashi 2020-06-26 21:52:20 +00:00
Родитель 4a20f4f8c0
Коммит 5e8e090932
1 изменённых файлов: 230 добавлений и 12 удалений

Просмотреть файл

@ -4,6 +4,8 @@
from __future__ import absolute_import, division, print_function, unicode_literals from __future__ import absolute_import, division, print_function, unicode_literals
import re
from itertools import combinations from itertools import combinations
from six.moves import range from six.moves import range
@ -60,6 +62,122 @@ def unchunked_manifests():
return inner return inner
@pytest.fixture(scope='module')
def mock_task_definition():
"""Builds a mock task definition for use in testing.
Args:
platform (str): represents the build platform.
bits (int): software bits.
build_type (str): opt or debug.
suite (str): name of the unittest suite.
test_platform (str, optional): full name of the platform and major version.
variant (str, optional): specify fission or vanilla.
Returns:
dict: mocked task definition.
"""
def inner(platform, bits, build_type, suite, test_platform='', variant=''):
bits = str(bits)
test_variant = [suite, 'e10s']
if 'fis' in variant:
test_variant.insert(1, 'fis')
output = {
'build-attributes': {
'build_platform': platform + bits,
'build_type': build_type,
},
'attributes': {
'e10s': True,
'unittest_variant': variant
},
'test-name': '-'.join(test_variant),
'test-platform': ''.join([test_platform, '-', bits, '/', build_type])
}
return output
return inner
@pytest.fixture(scope='module')
def mock_mozinfo():
"""Returns a mocked mozinfo object, similar to guess_mozinfo_from_task().
Args:
os (str): typically one of 'win, linux, mac, android'.
processor (str): processor architecture.
asan (bool, optional): addressanitizer build.
bits (int, optional): defaults to 64.
ccov (bool, optional): code coverage build.
debug (bool, optional): debug type build.
fission (bool, optional): process fission.
headless (bool, optional): headless browser testing without displays.
tsan (bool, optional): threadsanitizer build.
Returns:
dict: Dictionary mimickign the results from guess_mozinfo_from_task.
"""
def inner(os, processor, asan=False, bits=64, ccov=False, debug=False,
fission=False, headless=False, tsan=False):
return {
'os': os,
'processor': processor,
'toolkit': '',
'asan': asan,
'bits': bits,
'ccov': ccov,
'debug': debug,
'e10s': True,
'fission': fission,
'headless': headless,
'tsan': tsan,
'webrender': False,
'appname': 'firefox',
}
return inner
@pytest.mark.parametrize('params,exception', [
[('win', 32, 'opt', 'web-platform-tests', '', ''), None],
[('win', 64, 'opt', 'web-platform-tests', '', ''), None],
[('linux', 64, 'debug', 'mochitest-plain', '', ''), None],
[('mac', 64, 'debug', 'mochitest-plain', '', ''), None],
[('mac', 64, 'opt', 'mochitest-plain-headless', '', ''), None],
[('android', 64, 'debug', 'xpcshell', '', ''), None],
[('and', 64, 'debug', 'awsy', '', ''), ValueError],
[('', 64, 'opt', '', '', ''), ValueError],
[('linux-ccov', 64, 'opt', '', '', ''), None],
[('linux-asan', 64, 'opt', '', '', ''), None],
[('win-tsan', 64, 'opt', '', '', ''), None],
[('mac-ccov', 64, 'opt', '', '', ''), None],
[('android', 64, 'opt', 'crashtest', 'arm64', 'fission'), None],
[('win-aarch64', 64, 'opt', 'crashtest', '', ''), None],
])
def test_guess_mozinfo_from_task(params, exception, mock_task_definition):
"""Tests the mozinfo guessing process.
"""
# Set up a mocked task object.
task = mock_task_definition(*params)
if exception:
with pytest.raises(exception):
result = chunking.guess_mozinfo_from_task(task)
else:
result = chunking.guess_mozinfo_from_task(task)
assert result['bits'] == (32 if '32' in task['test-platform'] else 64)
assert result['os'] in ('android', 'linux', 'mac', 'win')
# Ensure the outcome of special build variants being present match what
# guess_mozinfo_from_task method returns for these attributes.
assert ('asan' in task['build-attributes']['build_platform']) == result['asan']
assert ('tsan' in task['build-attributes']['build_platform']) == result['tsan']
assert ('ccov' in task['build-attributes']['build_platform']) == result['ccov']
assert result['fission'] == any(task['attributes']['unittest_variant'])
assert result['e10s']
@pytest.mark.parametrize('platform', ['unix', 'windows', 'android']) @pytest.mark.parametrize('platform', ['unix', 'windows', 'android'])
@pytest.mark.parametrize('suite', ['crashtest', 'reftest', 'web-platform-tests', 'xpcshell']) @pytest.mark.parametrize('suite', ['crashtest', 'reftest', 'web-platform-tests', 'xpcshell'])
def test_get_runtimes(platform, suite): def test_get_runtimes(platform, suite):
@ -68,29 +186,28 @@ def test_get_runtimes(platform, suite):
assert chunking.get_runtimes(platform, suite) assert chunking.get_runtimes(platform, suite)
@pytest.mark.parametrize('test_cases', [ @pytest.mark.parametrize('platform,suite,exception', [
('nonexistent_platform', 'nonexistent_suite', KeyError), ('nonexistent_platform', 'nonexistent_suite', KeyError),
('unix', 'nonexistent_suite', KeyError), ('unix', 'nonexistent_suite', KeyError),
('unix', '', TypeError), ('unix', '', TypeError),
('', '', TypeError), ('', '', TypeError),
('', 'nonexistent_suite', TypeError) ('', 'nonexistent_suite', TypeError),
]) ])
def test_get_runtimes_invalid(test_cases): def test_get_runtimes_invalid(platform, suite, exception):
"""Ensure get_runtimes() method raises an exception if improper request is made. """Ensure get_runtimes() method raises an exception if improper request is made.
""" """
platform = test_cases[0] with pytest.raises(exception):
suite = test_cases[1]
expected = test_cases[2]
try:
chunking.get_runtimes(platform, suite) chunking.get_runtimes(platform, suite)
except Exception as e:
assert isinstance(e, expected)
@pytest.mark.parametrize('suite', ['web-platform-tests', 'web-platform-tests-reftests']) @pytest.mark.parametrize('suite', [
'web-platform-tests',
'web-platform-tests-reftest',
'web-platform-tests-wdspec',
'web-platform-tests-crashtest',
])
@pytest.mark.parametrize('chunks', [1, 3, 6, 20]) @pytest.mark.parametrize('chunks', [1, 3, 6, 20])
def test_chunk_manifests_wpt(mock_manifest_runtimes, unchunked_manifests, suite, chunks): def test_mock_chunk_manifests_wpt(unchunked_manifests, suite, chunks):
"""Tests web-platform-tests and its subsuites chunking process. """Tests web-platform-tests and its subsuites chunking process.
""" """
# Setup. # Setup.
@ -123,5 +240,106 @@ def test_chunk_manifests_wpt(mock_manifest_runtimes, unchunked_manifests, suite,
assert expected == chunked_manifests assert expected == chunked_manifests
@pytest.mark.parametrize('suite', [
'mochitest-devtools-chrome',
'mochitest-browser-chrome',
'mochitest-plain',
'mochitest-chrome',
'xpcshell',
])
@pytest.mark.parametrize('chunks', [1, 3, 6, 20])
def test_mock_chunk_manifests(mock_manifest_runtimes, unchunked_manifests, suite, chunks):
"""Tests non-WPT tests and its subsuites chunking process.
"""
# Setup.
manifests = unchunked_manifests(suite)
# Call the method under test on unchunked manifests.
chunked_manifests = chunking.chunk_manifests(suite, 'unix', chunks, manifests)
# Assertions and end test.
assert chunked_manifests
if chunks > len(manifests):
# If chunk count exceeds number of manifests, not all chunks will have
# manifests.
with pytest.raises(AssertionError):
assert all(chunked_manifests)
else:
assert all(chunked_manifests)
@pytest.mark.parametrize('suite', [
'web-platform-tests',
'web-platform-tests-reftest',
'xpcshell',
'mochitest-plain',
'mochitest-devtools-chrome',
'mochitest-browser-chrome',
'mochitest-chrome',
])
@pytest.mark.parametrize('platform', [
('mac', 'x86_64'),
('win', 'x86_64'),
('win', 'x86'),
('win', 'aarch64'),
('linux', 'x86_64'),
('linux', 'x86'),
])
def test_get_manifests(suite, platform, mock_mozinfo):
"""Tests the DefaultLoader class' ability to load manifests.
"""
mozinfo = mock_mozinfo(*platform)
loader = chunking.DefaultLoader([])
manifests = loader.get_manifests(suite, frozenset(mozinfo.items()))
assert manifests
assert manifests['active']
if 'web-platform' in suite:
assert manifests['skipped'] == []
else:
assert manifests['skipped']
items = manifests['active']
if suite == 'xpcshell':
assert all([re.search(r'xpcshell(.*)?.ini', m) for m in items])
if 'mochitest' in suite:
assert all([re.search(r'(mochitest|chrome|browser).*.ini', m) for m in items])
if 'web-platform' in suite:
assert all([m.startswith('/') and m.count('/') <= 4 for m in items])
@pytest.mark.parametrize('suite', [
'mochitest-devtools-chrome',
'mochitest-browser-chrome',
'mochitest-plain',
'mochitest-chrome',
'web-platform-tests',
'web-platform-tests-reftest',
'xpcshell',
])
@pytest.mark.parametrize('platform', [
('mac', 'x86_64'),
('win', 'x86_64'),
('linux', 'x86_64'),
])
@pytest.mark.parametrize('chunks', [1, 3, 6, 20])
def test_chunk_manifests(suite, platform, chunks, mock_mozinfo):
"""Tests chunking with real manifests.
"""
mozinfo = mock_mozinfo(*platform)
loader = chunking.DefaultLoader([])
manifests = loader.get_manifests(suite, frozenset(mozinfo.items()))
chunked_manifests = chunking.chunk_manifests(suite, platform, chunks,
manifests['active'])
# Assertions and end test.
assert chunked_manifests
assert len(chunked_manifests) == chunks
assert all(chunked_manifests)
if __name__ == '__main__': if __name__ == '__main__':
main() main()