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 = [
33            "testing/web-platform/tests/**",
34            "testing/web-platform/mozilla/tests/**",
35        ]
36    elif type.startswith("test-verify-gpu") or type.startswith("test-coverage-gpu"):
37        file_patterns = [
38            "**/*webgl*/**/test_*",
39            "**/dom/canvas/**/test_*",
40            "**/gfx/tests/**/test_*",
41            "**/devtools/canvasdebugger/**/browser_*",
42            "**/reftest*/**",
43        ]
44    elif type.startswith("test-verify") or type.startswith("test-coverage"):
45        file_patterns = [
46            "**/test_*",
47            "**/browser_*",
48            "**/crashtest*/**",
49            "js/src/tests/test/**",
50            "js/src/tests/non262/**",
51            "js/src/tests/test262/**",
52        ]
53    else:
54        # Returning 0 means no tests to run, this captures non test-verify tasks
55        return 1
56
57    changed_files = set()
58    if try_task_config:
59        suite_to_paths = json.loads(try_task_config)
60        specified_files = itertools.chain.from_iterable(suite_to_paths.values())
61        changed_files.update(specified_files)
62
63    if is_try:
64        changed_files.update(files_changed.get_locally_changed_files(GECKO))
65    else:
66        changed_files.update(files_changed.get_changed_files(head_repository, head_rev))
67
68    test_count = 0
69    for pattern in file_patterns:
70        for path in changed_files:
71            # TODO: consider running tests if a manifest changes
72            if path.endswith(".list") or path.endswith(".ini"):
73                continue
74            if path.endswith("^headers^"):
75                continue
76
77            if mozpackmatch(path, pattern):
78                gpu = False
79                if type == "test-verify-e10s" or type == "test-coverage-e10s":
80                    # file_patterns for test-verify will pick up some gpu tests, lets ignore
81                    # in the case of reftest, we will not have any in the regular case
82                    gpu_dirs = [
83                        "dom/canvas",
84                        "gfx/tests",
85                        "devtools/canvasdebugger",
86                        "webgl",
87                    ]
88                    for gdir in gpu_dirs:
89                        if len(path.split(gdir)) > 1:
90                            gpu = True
91
92                if not gpu:
93                    test_count += 1
94
95    chunks = test_count / tests_per_chunk
96    chunks = int(math.ceil(chunks))
97
98    # Never return 0 chunks on try, so that per-file tests can be pushed to try with
99    # an explicit path, and also so "empty" runs can be checked on try.
100    if is_try and chunks == 0:
101        chunks = 1
102
103    return chunks
104