(self, result)
| 115 | test.is_parallel = True |
| 116 | |
| 117 | def run(self, result): |
| 118 | # The 'spawn' method is used on windows and it can be useful to set this on |
| 119 | # all platforms when debugging multiprocessing issues. Without this we |
| 120 | # default to 'fork' on unix which is better because global state is |
| 121 | # inherited by the child process, but can lead to hard-to-debug windows-only |
| 122 | # issues. |
| 123 | # multiprocessing.set_start_method('spawn') |
| 124 | |
| 125 | # No need to worry about stdout/stderr buffering since are a not |
| 126 | # actually running the test here, only setting the results. |
| 127 | buffer = result.buffer |
| 128 | result.buffer = False |
| 129 | |
| 130 | result.core_time = 0 |
| 131 | tests = self.get_sorted_tests() |
| 132 | self.num_tests = self.countTestCases() |
| 133 | contains_browser_test = any(test.is_browser_test() for test in tests) |
| 134 | use_cores = cap_max_workers_in_pool(min(self.num_tests, num_cores()), contains_browser_test) |
| 135 | errlog(f'Using {use_cores} parallel test processes') |
| 136 | with multiprocessing.Manager() as manager: |
| 137 | # Give each worker a unique ID. |
| 138 | worker_id_counter = manager.Value('i', 0) # 'i' for integer, starting at 0 |
| 139 | worker_id_lock = manager.Lock() |
| 140 | with multiprocessing.Pool( |
| 141 | processes=use_cores, |
| 142 | initializer=browser_common.init_worker, |
| 143 | initargs=(worker_id_counter, worker_id_lock), |
| 144 | ) as pool: |
| 145 | if python_multiprocessing_structures_are_buggy(): |
| 146 | # When multiprocessing shared structures are buggy we don't support failfast |
| 147 | # or the progress bar. |
| 148 | allowed_failures_counter = None |
| 149 | if self.max_failures < 2**31 - 1: |
| 150 | errlog('The version of python being used is not compatible with --failfast and --max-failures options. See https://github.com/python/cpython/issues/71936') |
| 151 | sys.exit(1) |
| 152 | else: |
| 153 | allowed_failures_counter = manager.Value('i', self.max_failures) |
| 154 | |
| 155 | results = [] |
| 156 | args = ((t, allowed_failures_counter, buffer) for t in tests) |
| 157 | for res in pool.imap_unordered(run_test, args, chunksize=1): |
| 158 | # results may be be None if # of allowed errors was exceeded |
| 159 | # and the harness aborted. |
| 160 | if res: |
| 161 | if res.test_result not in {'success', 'skipped'} and allowed_failures_counter is not None: |
| 162 | # Signal existing multiprocess pool runners so that they can exit early if needed. |
| 163 | allowed_failures_counter.value -= 1 |
| 164 | res.integrate_result(result) |
| 165 | results.append(res) |
| 166 | |
| 167 | # Send a task to each worker to tear down the browser and server. This |
| 168 | # relies on the implementation detail in the worker pool that all workers |
| 169 | # are cycled through once. |
| 170 | num_tear_downs = sum(pool.apply(tear_down, ()) for _ in range(use_cores)) |
| 171 | # Assert the assumed behavior above hasn't changed. |
| 172 | if num_tear_downs != use_cores and not buffer: |
| 173 | errlog(f'Expected {use_cores} teardowns, got {num_tear_downs}') |
| 174 |
nothing calls this directly
no test coverage detected