File: _test_utils.py

package info (click to toggle)
brotli 1.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 3,228 kB
  • sloc: ansic: 36,236; python: 817; sh: 97; makefile: 62; cpp: 14
file content (153 lines) | stat: -rw-r--r-- 4,793 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
"""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),
    )