1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5from __future__ import absolute_import, print_function, unicode_literals
6
7import itertools
8import json
9import logging
10import math
11
12from mozbuild.util import memoize
13from mozpack.path import match as mozpackmatch
14from taskgraph import files_changed
15import taskgraph
16from .. import GECKO
17
18logger = logging.getLogger(__name__)
19
20
21@memoize
22def perfile_number_of_chunks(is_try, try_task_config, head_repository, head_rev, type):
23    if taskgraph.fast and not is_try:
24        # When iterating on taskgraph changes, the exact number of chunks that
25        # test-verify runs usually isn't important, so skip it when going fast.
26        return 3
27    tests_per_chunk = 10.0
28    if type.startswith('test-coverage'):
29        tests_per_chunk = 30.0
30
31    if type.startswith('test-verify-wpt') or type.startswith('test-coverage-wpt'):
32        file_patterns = ['testing/web-platform/tests/**',
33                         'testing/web-platform/mozilla/tests/**']
34    elif type.startswith('test-verify-gpu') or type.startswith('test-coverage-gpu'):
35        file_patterns = ['**/*webgl*/**/test_*',
36                         '**/dom/canvas/**/test_*',
37                         '**/gfx/tests/**/test_*',
38                         '**/devtools/canvasdebugger/**/browser_*',
39                         '**/reftest*/**']
40    elif type.startswith('test-verify') or type.startswith('test-coverage'):
41        file_patterns = ['**/test_*',
42                         '**/browser_*',
43                         '**/crashtest*/**',
44                         'js/src/tests/test/**',
45                         'js/src/tests/non262/**',
46                         'js/src/tests/test262/**']
47    else:
48        # Returning 0 means no tests to run, this captures non test-verify tasks
49        return 1
50
51    changed_files = set()
52    if try_task_config:
53        suite_to_paths = json.loads(try_task_config)
54        specified_files = itertools.chain.from_iterable(suite_to_paths.values())
55        changed_files.update(specified_files)
56
57    if is_try:
58        changed_files.update(files_changed.get_locally_changed_files(GECKO))
59    else:
60        changed_files.update(files_changed.get_changed_files(head_repository,
61                                                             head_rev))
62
63    test_count = 0
64    for pattern in file_patterns:
65        for path in changed_files:
66            # TODO: consider running tests if a manifest changes
67            if path.endswith('.list') or path.endswith('.ini'):
68                continue
69            if path.endswith('^headers^'):
70                continue
71
72            if mozpackmatch(path, pattern):
73                gpu = False
74                if type == 'test-verify-e10s' or type == 'test-coverage-e10s':
75                    # file_patterns for test-verify will pick up some gpu tests, lets ignore
76                    # in the case of reftest, we will not have any in the regular case
77                    gpu_dirs = ['dom/canvas', 'gfx/tests', 'devtools/canvasdebugger', 'webgl']
78                    for gdir in gpu_dirs:
79                        if len(path.split(gdir)) > 1:
80                            gpu = True
81
82                if not gpu:
83                    test_count += 1
84
85    chunks = test_count/tests_per_chunk
86    chunks = int(math.ceil(chunks))
87
88    # Never return 0 chunks on try, so that per-file tests can be pushed to try with
89    # an explicit path, and also so "empty" runs can be checked on try.
90    if is_try and chunks == 0:
91        chunks = 1
92
93    return chunks
94