tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

bundled_test_runner.py (4916B)


      1 # Copyright 2024 The Chromium Authors
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 """Provides a way to run multiple tests as a bundle. It forwards all the
      5   arguments to the run_test.py, but overrides the test runner it uses.
      6 
      7   Since this class runs in a higher level as the regular run_test.py, it would
      8   be more clear to not include it into the run_test.py and avoid
      9   cycle-dependency.
     10 
     11   Use of this test runner may break sharding."""
     12 
     13 import argparse
     14 import logging
     15 import os
     16 import sys
     17 
     18 from subprocess import CompletedProcess
     19 from typing import List, NamedTuple, Optional
     20 from urllib.parse import urlparse
     21 
     22 import run_test
     23 from run_executable_test import ExecutableTestRunner
     24 from test_runner import TestRunner
     25 
     26 
     27 class TestCase(NamedTuple):
     28    """Defines a TestCase, it executes the package with optional arguments."""
     29 
     30    # The test package in the format of fuchsia-pkg://...#meta/...cm.
     31    package: str
     32 
     33    # Optional arguments to pass to the test run. It can include multiple
     34    # arguments separated by any whitespaces.
     35    args: str = ''
     36 
     37 
     38 class _BundledTestRunner(TestRunner):
     39    """A TestRunner implementation to run multiple test cases. It always run all
     40       tests even some of them failed. The return code is the return code of the
     41       last non-zero test run."""
     42 
     43    # private, use run_tests.get_test_runner function instead.
     44    # Keep the order of parameters consistent with ExecutableTestRunner.
     45    # pylint: disable=too-many-arguments
     46    # TODO(crbug.com/346806329): May consider using a structure to capture the
     47    # arguments.
     48    def __init__(self, out_dir: str, tests: List[TestCase],
     49                 target_id: Optional[str], code_coverage_dir: Optional[str],
     50                 logs_dir: Optional[str], package_deps: List[str],
     51                 test_realm: Optional[str]):
     52        super().__init__(
     53            out_dir, [], [], target_id,
     54            _BundledTestRunner._merge_packages(tests, package_deps))
     55        assert tests
     56        self._tests = tests
     57        self._code_coverage_dir = code_coverage_dir
     58        self._logs_dir = logs_dir
     59        self._test_realm = test_realm
     60 
     61    @staticmethod
     62    def _merge_packages(tests: List[TestCase],
     63                        package_deps: List[str]) -> List[str]:
     64        packages = list(package_deps)
     65        # Include test packages if they have not been defined in the
     66        # package_deps.
     67        packages.extend(
     68            {urlparse(x.package).path.lstrip('/') + '.far'
     69             for x in tests} - {os.path.basename(x)
     70                                for x in packages})
     71        return packages
     72 
     73    def run_test(self) -> CompletedProcess:
     74        returncode = 0
     75        for test in self._tests:
     76            assert test.package.endswith('.cm')
     77            test_runner = ExecutableTestRunner(
     78                self._out_dir, test.args.split(), test.package,
     79                self._target_id, self._code_coverage_dir, self._logs_dir,
     80                self._package_deps, self._test_realm)
     81            # It's a little bit wasteful to resolve all the packages once per
     82            # test package, but it's easier.
     83            result = test_runner.run_test().returncode
     84            logging.info('Result of test %s is %s', test, result)
     85            if result != 0:
     86                returncode = result
     87        return CompletedProcess(args='', returncode=returncode)
     88 
     89 
     90 def run_tests(tests: List[TestCase]) -> int:
     91    """Runs multiple tests.
     92 
     93       Args:
     94         tests: The definition of each test case.
     95 
     96       Note:
     97         All the packages in tests will always be included, and it's expected
     98         that the far files sharing the same name as the package in TestCase
     99         except for the suffix. E.g. test1.far is the far file of
    100         fuchsia-pkg://fuchsia.com/test1#meta/some.cm.
    101 
    102         Duplicated packages in either --packages or tests are allowed as long
    103         as they are targeting the same file; otherwise the test run would
    104         trigger an assertion failure.
    105 
    106         Far files in the --packages can be either absolute paths or relative
    107         paths starting from --out-dir."""
    108    # The 'bundled-tests' is a place holder and has no specific meaning; the
    109    # run_test._get_test_runner is overridden.
    110    sys.argv.append('bundled-tests')
    111 
    112    def get_test_runner(runner_args: argparse.Namespace, *_) -> TestRunner:
    113        # test_args are not used, and each TestCase should have its own args.
    114        return _BundledTestRunner(runner_args.out_dir, tests,
    115                                  runner_args.target_id,
    116                                  runner_args.code_coverage_dir,
    117                                  runner_args.logs_dir, runner_args.packages,
    118                                  runner_args.test_realm)
    119 
    120    # pylint: disable=protected-access
    121    run_test._get_test_runner = get_test_runner
    122    return run_test.main()