Edit

kc3-lang/brotli/python/tests/_test_utils.py

Branch :

  • Show log

    Commit

  • Author : Evgenii Kliuchnikov
    Date : 2025-09-25 07:59:41
    Hash : 14068984
    Message : Build and test with PY2.7 PiperOrigin-RevId: 811352084

  • python/tests/_test_utils.py
  • """Common utilities for Brotli tests."""
    
    from __future__ import print_function
    import filecmp
    import glob
    import itertools
    import os
    import sys
    import sysconfig
    import tempfile
    import unittest
    
    # TODO(eustas): use str(pathlib.PurePath(file).parent.parent) for Python 3.4+
    project_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
    test_dir = os.getenv('BROTLI_TESTS_PATH')
    BRO_ARGS = [os.getenv('BROTLI_WRAPPER')]
    
    # Fallbacks
    if test_dir is None:
      test_dir = os.path.join(project_dir, 'tests')
    if BRO_ARGS[0] is None:
      python_exe = sys.executable or 'python'
      bro_path = os.path.join(project_dir, 'python', 'bro.py')
      BRO_ARGS = [python_exe, bro_path]
    
    # Get the platform/version-specific build folder.
    # By default, the distutils build base is in the same location as setup.py.
    platform_lib_name = 'lib.{platform}-{version[0]}.{version[1]}'.format(
        platform=sysconfig.get_platform(), version=sys.version_info
    )
    build_dir = os.path.join(project_dir, 'bin', platform_lib_name)
    
    # Prepend the build folder to sys.path and the PYTHONPATH environment variable.
    if build_dir not in sys.path:
      sys.path.insert(0, build_dir)
    TEST_ENV = dict(os.environ)
    if 'PYTHONPATH' not in TEST_ENV:
      TEST_ENV['PYTHONPATH'] = build_dir
    else:
      TEST_ENV['PYTHONPATH'] = build_dir + os.pathsep + TEST_ENV['PYTHONPATH']
    
    TESTDATA_DIR = os.path.join(test_dir, 'testdata')
    
    TESTDATA_FILES = [
        'empty',  # Empty file
        '10x10y',  # Small text
        'alice29.txt',  # Large text
        'random_org_10k.bin',  # Small data
        'mapsdatazrh',  # Large data
        'ukkonooa',  # Poem
        'cp1251-utf16le',  # Codepage 1251 table saved in UTF16-LE encoding
        'cp852-utf8',  # Codepage 852 table saved in UTF8 encoding
        # TODO(eustas): add test on already compressed content
    ]
    
    # Some files might be missing in a lightweight sources pack.
    TESTDATA_PATH_CANDIDATES = [
        os.path.join(TESTDATA_DIR, f) for f in TESTDATA_FILES
    ]
    
    TESTDATA_PATHS = [
        path for path in TESTDATA_PATH_CANDIDATES if os.path.isfile(path)
    ]
    
    TESTDATA_PATHS_FOR_DECOMPRESSION = glob.glob(
        os.path.join(TESTDATA_DIR, '*.compressed')
    )
    
    TEMP_DIR = tempfile.mkdtemp()
    
    
    def get_temp_compressed_name(filename):
      return os.path.join(TEMP_DIR, os.path.basename(filename + '.bro'))
    
    
    def get_temp_uncompressed_name(filename):
      return os.path.join(TEMP_DIR, os.path.basename(filename + '.unbro'))
    
    
    def bind_method_args(method, *args, **kwargs):
      return lambda self: method(self, *args, **kwargs)
    
    
    # TODO(eustas): migrate to absl.testing.parameterized.
    def generate_test_methods(
        test_case_class, for_decompression=False, variants=None
    ):
      """Adds test methods for each test data file and each variant.
    
      This makes identifying problems with specific compression scenarios easier.
    
      Args:
        test_case_class: The test class to add methods to.
        for_decompression: If True, uses compressed test data files.
        variants: A dictionary where keys are option names and values are lists of
          possible values for that option. Each combination of variants will
          generate a separate test method.
      """
      if for_decompression:
        paths = [
            path for path in TESTDATA_PATHS_FOR_DECOMPRESSION
            if os.path.exists(path.replace('.compressed', ''))
        ]
      else:
        paths = TESTDATA_PATHS
      opts = []
      if variants:
        opts_list = []
        for k, v in variants.items():
          opts_list.append([r for r in itertools.product([k], v)])
        for o in itertools.product(*opts_list):
          opts_name = '_'.join([str(i) for i in itertools.chain(*o)])
          opts_dict = dict(o)
          opts.append([opts_name, opts_dict])
      else:
        opts.append(['', {}])
      for method in [m for m in dir(test_case_class) if m.startswith('_test')]:
        for testdata in paths:
          for opts_name, opts_dict in opts:
            f = os.path.splitext(os.path.basename(testdata))[0]
            name = 'test_{method}_{options}_{file}'.format(
                method=method, options=opts_name, file=f
            )
            func = bind_method_args(
                getattr(test_case_class, method), testdata, **opts_dict
            )
            setattr(test_case_class, name, func)
    
    
    class TestCase(unittest.TestCase):
      """Base class for Brotli test cases.
    
      Provides common setup and teardown logic, including cleaning up temporary
      files and a utility for comparing file contents.
      """
    
      def tearDown(self):
        for f in TESTDATA_PATHS:
          try:
            os.unlink(get_temp_compressed_name(f))
          except OSError:
            pass
          try:
            os.unlink(get_temp_uncompressed_name(f))
          except OSError:
            pass
        # super().tearDown()  # Requires Py3+
    
      def assert_files_match(self, first, second):
        self.assertTrue(
            filecmp.cmp(first, second, shallow=False),
            'File {} differs from {}'.format(first, second),
        )