11cb0ef41Sopenharmony_ci# Copyright 2012 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci# Redistribution and use in source and binary forms, with or without
31cb0ef41Sopenharmony_ci# modification, are permitted provided that the following conditions are
41cb0ef41Sopenharmony_ci# met:
51cb0ef41Sopenharmony_ci#
61cb0ef41Sopenharmony_ci#     * Redistributions of source code must retain the above copyright
71cb0ef41Sopenharmony_ci#       notice, this list of conditions and the following disclaimer.
81cb0ef41Sopenharmony_ci#     * Redistributions in binary form must reproduce the above
91cb0ef41Sopenharmony_ci#       copyright notice, this list of conditions and the following
101cb0ef41Sopenharmony_ci#       disclaimer in the documentation and/or other materials provided
111cb0ef41Sopenharmony_ci#       with the distribution.
121cb0ef41Sopenharmony_ci#     * Neither the name of Google Inc. nor the names of its
131cb0ef41Sopenharmony_ci#       contributors may be used to endorse or promote products derived
141cb0ef41Sopenharmony_ci#       from this software without specific prior written permission.
151cb0ef41Sopenharmony_ci#
161cb0ef41Sopenharmony_ci# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
171cb0ef41Sopenharmony_ci# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
181cb0ef41Sopenharmony_ci# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
191cb0ef41Sopenharmony_ci# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
201cb0ef41Sopenharmony_ci# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
211cb0ef41Sopenharmony_ci# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
221cb0ef41Sopenharmony_ci# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
231cb0ef41Sopenharmony_ci# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
241cb0ef41Sopenharmony_ci# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
251cb0ef41Sopenharmony_ci# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
261cb0ef41Sopenharmony_ci# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ciimport fnmatch
301cb0ef41Sopenharmony_ciimport imp
311cb0ef41Sopenharmony_ciimport itertools
321cb0ef41Sopenharmony_ciimport os
331cb0ef41Sopenharmony_cifrom contextlib import contextmanager
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_cifrom . import command
361cb0ef41Sopenharmony_cifrom . import statusfile
371cb0ef41Sopenharmony_cifrom . import utils
381cb0ef41Sopenharmony_cifrom ..objects.testcase import TestCase
391cb0ef41Sopenharmony_cifrom .variants import ALL_VARIANTS, ALL_VARIANT_FLAGS
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci
421cb0ef41Sopenharmony_ciSTANDARD_VARIANT = set(["default"])
431cb0ef41Sopenharmony_ci
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ciclass VariantsGenerator(object):
461cb0ef41Sopenharmony_ci  def __init__(self, variants):
471cb0ef41Sopenharmony_ci    self._all_variants = [v for v in variants if v in ALL_VARIANTS]
481cb0ef41Sopenharmony_ci    self._standard_variant = [v for v in variants if v in STANDARD_VARIANT]
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci  def gen(self, test):
511cb0ef41Sopenharmony_ci    """Generator producing (variant, flags, procid suffix) tuples."""
521cb0ef41Sopenharmony_ci    flags_set = self._get_flags_set(test)
531cb0ef41Sopenharmony_ci    for n, variant in enumerate(self._get_variants(test)):
541cb0ef41Sopenharmony_ci      yield (variant, flags_set[variant][0], n)
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_ci  def _get_flags_set(self, test):
571cb0ef41Sopenharmony_ci    return ALL_VARIANT_FLAGS
581cb0ef41Sopenharmony_ci
591cb0ef41Sopenharmony_ci  def _get_variants(self, test):
601cb0ef41Sopenharmony_ci    if test.only_standard_variant:
611cb0ef41Sopenharmony_ci      return self._standard_variant
621cb0ef41Sopenharmony_ci    return self._all_variants
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ciclass TestCombiner(object):
661cb0ef41Sopenharmony_ci  def get_group_key(self, test):
671cb0ef41Sopenharmony_ci    """To indicate what tests can be combined with each other we define a group
681cb0ef41Sopenharmony_ci    key for each test. Tests with the same group key can be combined. Test
691cb0ef41Sopenharmony_ci    without a group key (None) is not combinable with any other test.
701cb0ef41Sopenharmony_ci    """
711cb0ef41Sopenharmony_ci    raise NotImplementedError()
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_ci  def combine(self, name, tests):
741cb0ef41Sopenharmony_ci    """Returns test combined from `tests`. Since we identify tests by their
751cb0ef41Sopenharmony_ci    suite and name, `name` parameter should be unique within one suite.
761cb0ef41Sopenharmony_ci    """
771cb0ef41Sopenharmony_ci    return self._combined_test_class()(name, tests)
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ci  def _combined_test_class(self):
801cb0ef41Sopenharmony_ci    raise NotImplementedError()
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ciclass TestLoader(object):
841cb0ef41Sopenharmony_ci  """Base class for loading TestSuite tests after applying test suite
851cb0ef41Sopenharmony_ci  transformations."""
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci  def __init__(self, suite, test_class, test_config, test_root):
881cb0ef41Sopenharmony_ci    self.suite = suite
891cb0ef41Sopenharmony_ci    self.test_class = test_class
901cb0ef41Sopenharmony_ci    self.test_config = test_config
911cb0ef41Sopenharmony_ci    self.test_root = test_root
921cb0ef41Sopenharmony_ci    self.test_count_estimation = len(list(self._list_test_filenames()))
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ci  def _list_test_filenames(self):
951cb0ef41Sopenharmony_ci    """Implemented by the subclassed TestLoaders to list filenames.
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci    Filenames are expected to be sorted and are deterministic."""
981cb0ef41Sopenharmony_ci    raise NotImplementedError
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ci  def _should_filter_by_name(self, name):
1011cb0ef41Sopenharmony_ci    return False
1021cb0ef41Sopenharmony_ci
1031cb0ef41Sopenharmony_ci  def _should_filter_by_test(self, test):
1041cb0ef41Sopenharmony_ci    return False
1051cb0ef41Sopenharmony_ci
1061cb0ef41Sopenharmony_ci  def _filename_to_testname(self, filename):
1071cb0ef41Sopenharmony_ci    """Hook for subclasses to write their own filename transformation
1081cb0ef41Sopenharmony_ci    logic before the test creation."""
1091cb0ef41Sopenharmony_ci    return filename
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci  # TODO: not needed for every TestLoader, extract it into a subclass.
1121cb0ef41Sopenharmony_ci  def _path_to_name(self, path):
1131cb0ef41Sopenharmony_ci    if utils.IsWindows():
1141cb0ef41Sopenharmony_ci      return path.replace(os.path.sep, "/")
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci    return path
1171cb0ef41Sopenharmony_ci
1181cb0ef41Sopenharmony_ci  def _create_test(self, path, suite, **kwargs):
1191cb0ef41Sopenharmony_ci    """Converts paths into test objects using the given options"""
1201cb0ef41Sopenharmony_ci    return self.test_class(
1211cb0ef41Sopenharmony_ci      suite, path, self._path_to_name(path), self.test_config, **kwargs)
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_ci  def list_tests(self):
1241cb0ef41Sopenharmony_ci    """Loads and returns the test objects for a TestSuite"""
1251cb0ef41Sopenharmony_ci    # TODO: detect duplicate tests.
1261cb0ef41Sopenharmony_ci    for filename in self._list_test_filenames():
1271cb0ef41Sopenharmony_ci      if self._should_filter_by_name(filename):
1281cb0ef41Sopenharmony_ci        continue
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_ci      testname = self._filename_to_testname(filename)
1311cb0ef41Sopenharmony_ci      case = self._create_test(testname, self.suite)
1321cb0ef41Sopenharmony_ci      if self._should_filter_by_test(case):
1331cb0ef41Sopenharmony_ci        continue
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci      yield case
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ci
1381cb0ef41Sopenharmony_ciclass GenericTestLoader(TestLoader):
1391cb0ef41Sopenharmony_ci  """Generic TestLoader implementing the logic for listing filenames"""
1401cb0ef41Sopenharmony_ci  @property
1411cb0ef41Sopenharmony_ci  def excluded_files(self):
1421cb0ef41Sopenharmony_ci    return set()
1431cb0ef41Sopenharmony_ci
1441cb0ef41Sopenharmony_ci  @property
1451cb0ef41Sopenharmony_ci  def excluded_dirs(self):
1461cb0ef41Sopenharmony_ci    return set()
1471cb0ef41Sopenharmony_ci
1481cb0ef41Sopenharmony_ci  @property
1491cb0ef41Sopenharmony_ci  def excluded_suffixes(self):
1501cb0ef41Sopenharmony_ci    return set()
1511cb0ef41Sopenharmony_ci
1521cb0ef41Sopenharmony_ci  @property
1531cb0ef41Sopenharmony_ci  def test_dirs(self):
1541cb0ef41Sopenharmony_ci    return [self.test_root]
1551cb0ef41Sopenharmony_ci
1561cb0ef41Sopenharmony_ci  @property
1571cb0ef41Sopenharmony_ci  def extensions(self):
1581cb0ef41Sopenharmony_ci    return []
1591cb0ef41Sopenharmony_ci
1601cb0ef41Sopenharmony_ci  def __find_extension(self, filename):
1611cb0ef41Sopenharmony_ci    for extension in self.extensions:
1621cb0ef41Sopenharmony_ci      if filename.endswith(extension):
1631cb0ef41Sopenharmony_ci        return extension
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_ci    return False
1661cb0ef41Sopenharmony_ci
1671cb0ef41Sopenharmony_ci  def _should_filter_by_name(self, filename):
1681cb0ef41Sopenharmony_ci    if not self.__find_extension(filename):
1691cb0ef41Sopenharmony_ci      return True
1701cb0ef41Sopenharmony_ci
1711cb0ef41Sopenharmony_ci    for suffix in self.excluded_suffixes:
1721cb0ef41Sopenharmony_ci      if filename.endswith(suffix):
1731cb0ef41Sopenharmony_ci        return True
1741cb0ef41Sopenharmony_ci
1751cb0ef41Sopenharmony_ci    if os.path.basename(filename) in self.excluded_files:
1761cb0ef41Sopenharmony_ci      return True
1771cb0ef41Sopenharmony_ci
1781cb0ef41Sopenharmony_ci    return False
1791cb0ef41Sopenharmony_ci
1801cb0ef41Sopenharmony_ci  def _filename_to_testname(self, filename):
1811cb0ef41Sopenharmony_ci    extension = self.__find_extension(filename)
1821cb0ef41Sopenharmony_ci    if not extension:
1831cb0ef41Sopenharmony_ci      return filename
1841cb0ef41Sopenharmony_ci
1851cb0ef41Sopenharmony_ci    return filename[:-len(extension)]
1861cb0ef41Sopenharmony_ci
1871cb0ef41Sopenharmony_ci  def _to_relpath(self, abspath, test_root):
1881cb0ef41Sopenharmony_ci    return os.path.relpath(abspath, test_root)
1891cb0ef41Sopenharmony_ci
1901cb0ef41Sopenharmony_ci  def _list_test_filenames(self):
1911cb0ef41Sopenharmony_ci    for test_dir in sorted(self.test_dirs):
1921cb0ef41Sopenharmony_ci      test_root = os.path.join(self.test_root, test_dir)
1931cb0ef41Sopenharmony_ci      for dirname, dirs, files in os.walk(test_root, followlinks=True):
1941cb0ef41Sopenharmony_ci        dirs.sort()
1951cb0ef41Sopenharmony_ci        for dir in dirs:
1961cb0ef41Sopenharmony_ci          if dir in self.excluded_dirs or dir.startswith('.'):
1971cb0ef41Sopenharmony_ci            dirs.remove(dir)
1981cb0ef41Sopenharmony_ci
1991cb0ef41Sopenharmony_ci        files.sort()
2001cb0ef41Sopenharmony_ci        for filename in files:
2011cb0ef41Sopenharmony_ci          abspath = os.path.join(dirname, filename)
2021cb0ef41Sopenharmony_ci
2031cb0ef41Sopenharmony_ci          yield self._to_relpath(abspath, test_root)
2041cb0ef41Sopenharmony_ci
2051cb0ef41Sopenharmony_ci
2061cb0ef41Sopenharmony_ciclass JSTestLoader(GenericTestLoader):
2071cb0ef41Sopenharmony_ci  @property
2081cb0ef41Sopenharmony_ci  def extensions(self):
2091cb0ef41Sopenharmony_ci    return [".js", ".mjs"]
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci
2121cb0ef41Sopenharmony_ciclass TestGenerator(object):
2131cb0ef41Sopenharmony_ci  def __init__(self, test_count_estimate, slow_tests, fast_tests):
2141cb0ef41Sopenharmony_ci    self.test_count_estimate = test_count_estimate
2151cb0ef41Sopenharmony_ci    self.slow_tests = slow_tests
2161cb0ef41Sopenharmony_ci    self.fast_tests = fast_tests
2171cb0ef41Sopenharmony_ci    self._rebuild_iterator()
2181cb0ef41Sopenharmony_ci
2191cb0ef41Sopenharmony_ci  def _rebuild_iterator(self):
2201cb0ef41Sopenharmony_ci    self._iterator = itertools.chain(self.slow_tests, self.fast_tests)
2211cb0ef41Sopenharmony_ci
2221cb0ef41Sopenharmony_ci  def __iter__(self):
2231cb0ef41Sopenharmony_ci    return self
2241cb0ef41Sopenharmony_ci
2251cb0ef41Sopenharmony_ci  def __next__(self):
2261cb0ef41Sopenharmony_ci    return self.next()
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ci  def next(self):
2291cb0ef41Sopenharmony_ci    return next(self._iterator)
2301cb0ef41Sopenharmony_ci
2311cb0ef41Sopenharmony_ci  def merge(self, test_generator):
2321cb0ef41Sopenharmony_ci    self.test_count_estimate += test_generator.test_count_estimate
2331cb0ef41Sopenharmony_ci    self.slow_tests = itertools.chain(
2341cb0ef41Sopenharmony_ci      self.slow_tests, test_generator.slow_tests)
2351cb0ef41Sopenharmony_ci    self.fast_tests = itertools.chain(
2361cb0ef41Sopenharmony_ci      self.fast_tests, test_generator.fast_tests)
2371cb0ef41Sopenharmony_ci    self._rebuild_iterator()
2381cb0ef41Sopenharmony_ci
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ci@contextmanager
2411cb0ef41Sopenharmony_cidef _load_testsuite_module(name, root):
2421cb0ef41Sopenharmony_ci  f = None
2431cb0ef41Sopenharmony_ci  try:
2441cb0ef41Sopenharmony_ci    (f, pathname, description) = imp.find_module("testcfg", [root])
2451cb0ef41Sopenharmony_ci    yield imp.load_module(name + "_testcfg", f, pathname, description)
2461cb0ef41Sopenharmony_ci  finally:
2471cb0ef41Sopenharmony_ci    if f:
2481cb0ef41Sopenharmony_ci      f.close()
2491cb0ef41Sopenharmony_ci
2501cb0ef41Sopenharmony_ciclass TestSuite(object):
2511cb0ef41Sopenharmony_ci  @staticmethod
2521cb0ef41Sopenharmony_ci  def Load(root, test_config, framework_name):
2531cb0ef41Sopenharmony_ci    name = root.split(os.path.sep)[-1]
2541cb0ef41Sopenharmony_ci    with _load_testsuite_module(name, root) as module:
2551cb0ef41Sopenharmony_ci      return module.GetSuite(name, root, test_config, framework_name)
2561cb0ef41Sopenharmony_ci
2571cb0ef41Sopenharmony_ci  def __init__(self, name, root, test_config, framework_name):
2581cb0ef41Sopenharmony_ci    self.name = name  # string
2591cb0ef41Sopenharmony_ci    self.root = root  # string containing path
2601cb0ef41Sopenharmony_ci    self.test_config = test_config
2611cb0ef41Sopenharmony_ci    self.framework_name = framework_name  # name of the test runner impl
2621cb0ef41Sopenharmony_ci    self.tests = None  # list of TestCase objects
2631cb0ef41Sopenharmony_ci    self.statusfile = None
2641cb0ef41Sopenharmony_ci
2651cb0ef41Sopenharmony_ci    self._test_loader = self._test_loader_class()(
2661cb0ef41Sopenharmony_ci      self, self._test_class(), self.test_config, self.root)
2671cb0ef41Sopenharmony_ci
2681cb0ef41Sopenharmony_ci  def status_file(self):
2691cb0ef41Sopenharmony_ci    return "%s/%s.status" % (self.root, self.name)
2701cb0ef41Sopenharmony_ci
2711cb0ef41Sopenharmony_ci  @property
2721cb0ef41Sopenharmony_ci  def _test_loader_class(self):
2731cb0ef41Sopenharmony_ci    raise NotImplementedError
2741cb0ef41Sopenharmony_ci
2751cb0ef41Sopenharmony_ci  def ListTests(self):
2761cb0ef41Sopenharmony_ci    return self._test_loader.list_tests()
2771cb0ef41Sopenharmony_ci
2781cb0ef41Sopenharmony_ci  def __initialize_test_count_estimation(self):
2791cb0ef41Sopenharmony_ci    # Retrieves a single test to initialize the test generator.
2801cb0ef41Sopenharmony_ci    next(iter(self.ListTests()), None)
2811cb0ef41Sopenharmony_ci
2821cb0ef41Sopenharmony_ci  def __calculate_test_count(self):
2831cb0ef41Sopenharmony_ci    self.__initialize_test_count_estimation()
2841cb0ef41Sopenharmony_ci    return self._test_loader.test_count_estimation
2851cb0ef41Sopenharmony_ci
2861cb0ef41Sopenharmony_ci  def load_tests_from_disk(self, statusfile_variables):
2871cb0ef41Sopenharmony_ci    self.statusfile = statusfile.StatusFile(
2881cb0ef41Sopenharmony_ci      self.status_file(), statusfile_variables)
2891cb0ef41Sopenharmony_ci
2901cb0ef41Sopenharmony_ci    test_count = self.__calculate_test_count()
2911cb0ef41Sopenharmony_ci    slow_tests = (test for test in self.ListTests() if test.is_slow)
2921cb0ef41Sopenharmony_ci    fast_tests = (test for test in self.ListTests() if not test.is_slow)
2931cb0ef41Sopenharmony_ci    return TestGenerator(test_count, slow_tests, fast_tests)
2941cb0ef41Sopenharmony_ci
2951cb0ef41Sopenharmony_ci  def get_variants_gen(self, variants):
2961cb0ef41Sopenharmony_ci    return self._variants_gen_class()(variants)
2971cb0ef41Sopenharmony_ci
2981cb0ef41Sopenharmony_ci  def _variants_gen_class(self):
2991cb0ef41Sopenharmony_ci    return VariantsGenerator
3001cb0ef41Sopenharmony_ci
3011cb0ef41Sopenharmony_ci  def test_combiner_available(self):
3021cb0ef41Sopenharmony_ci    return bool(self._test_combiner_class())
3031cb0ef41Sopenharmony_ci
3041cb0ef41Sopenharmony_ci  def get_test_combiner(self):
3051cb0ef41Sopenharmony_ci    cls = self._test_combiner_class()
3061cb0ef41Sopenharmony_ci    if cls:
3071cb0ef41Sopenharmony_ci      return cls()
3081cb0ef41Sopenharmony_ci    return None
3091cb0ef41Sopenharmony_ci
3101cb0ef41Sopenharmony_ci  def _test_combiner_class(self):
3111cb0ef41Sopenharmony_ci    """Returns Combiner subclass. None if suite doesn't support combining
3121cb0ef41Sopenharmony_ci    tests.
3131cb0ef41Sopenharmony_ci    """
3141cb0ef41Sopenharmony_ci    return None
3151cb0ef41Sopenharmony_ci
3161cb0ef41Sopenharmony_ci  def _test_class(self):
3171cb0ef41Sopenharmony_ci    raise NotImplementedError
318