Chromium Code Reviews| Index: tools/binary_size/libsupersize/integration_test.py |
| diff --git a/tools/binary_size/libsupersize/integration_test.py b/tools/binary_size/libsupersize/integration_test.py |
| index e9ad9254a8ce250353f30d5f69efff4dc323d461..ad06650ac41c6a0d11bb86fa3aacc31f1833b14c 100755 |
| --- a/tools/binary_size/libsupersize/integration_test.py |
| +++ b/tools/binary_size/libsupersize/integration_test.py |
| @@ -3,6 +3,7 @@ |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| +import contextlib |
| import copy |
| import difflib |
| import glob |
| @@ -10,20 +11,25 @@ import itertools |
| import logging |
| import os |
| import unittest |
| +import re |
| import subprocess |
| import sys |
| import tempfile |
| import archive |
| import describe |
| +import diff |
| import file_format |
| import models |
| -import paths |
| _SCRIPT_DIR = os.path.dirname(__file__) |
| _TEST_DATA_DIR = os.path.join(_SCRIPT_DIR, 'testdata') |
| +_TEST_OUTPUT_DIR = os.path.join(_TEST_DATA_DIR, 'mock_output_directory') |
| +_TEST_TOOL_PREFIX = os.path.join( |
| + os.path.abspath(_TEST_DATA_DIR), 'mock_toolchain', '') |
| _TEST_MAP_PATH = os.path.join(_TEST_DATA_DIR, 'test.map') |
| +_TEST_ELF_PATH = os.path.join(_TEST_OUTPUT_DIR, 'elf') |
| update_goldens = False |
| @@ -35,66 +41,117 @@ def _AssertGolden(expected_lines, actual_lines): |
| ''.join(difflib.unified_diff(expected, actual, 'expected', 'actual'))) |
| -def _CompareWithGolden(func): |
| - name = func.__name__.replace('test_', '') |
| - golden_path = os.path.join(_TEST_DATA_DIR, name + '.golden') |
| +def _CompareWithGolden(name=None): |
| + def real_decorator(func): |
| + basename = name |
| + if not basename: |
| + basename = func.__name__.replace('test_', '') |
| + golden_path = os.path.join(_TEST_DATA_DIR, basename+ '.golden') |
|
estevenson
2017/04/28 17:06:11
nit: space after basename.
agrieve
2017/04/28 19:26:59
Done.
|
| - def inner(self): |
| - actual_lines = func(self) |
| + def inner(self): |
| + actual_lines = func(self) |
| + actual_lines = (re.sub(r'(elf_mtime=).*', r'\1{redacted}', l) |
| + for l in actual_lines) |
| - if update_goldens: |
| - with open(golden_path, 'w') as file_obj: |
| - describe.WriteLines(actual_lines, file_obj.write) |
| - logging.info('Wrote %s', golden_path) |
| - else: |
| - with open(golden_path) as file_obj: |
| - _AssertGolden(file_obj, actual_lines) |
| - return inner |
| + if update_goldens: |
| + with open(golden_path, 'w') as file_obj: |
| + describe.WriteLines(actual_lines, file_obj.write) |
| + logging.info('Wrote %s', golden_path) |
| + else: |
| + with open(golden_path) as file_obj: |
| + _AssertGolden(file_obj, actual_lines) |
| + return inner |
| + return real_decorator |
| + |
| + |
| +@contextlib.contextmanager |
| +def _AddMocksToPath(): |
| + prev_path = os.environ['PATH'] |
| + os.environ['PATH'] = _TEST_TOOL_PREFIX[:-1] + os.path.pathsep + prev_path |
| + yield |
| + os.environ['PATH'] = prev_path |
| -def _RunApp(name, *args): |
| +def _RunApp(name, args, debug_measures=False): |
| argv = [os.path.join(_SCRIPT_DIR, 'main.py'), name, '--no-pypy'] |
| argv.extend(args) |
| - return subprocess.check_output(argv).splitlines() |
| + with _AddMocksToPath(): |
| + env = None |
| + if debug_measures: |
| + env = os.environ.copy() |
| + env['SUPERSIZE_DISABLE_ASYNC'] = '1' |
| + env['SUPERSIZE_MEASURE_GZIP'] = '1' |
| + |
| + return subprocess.check_output(argv, env=env).splitlines() |
| class IntegrationTest(unittest.TestCase): |
| - size_info = None |
| + cached_size_info = [None, None, None] |
| - def _CloneSizeInfo(self): |
| - if not IntegrationTest.size_info: |
| - lazy_paths = paths.LazyPaths(output_directory=_TEST_DATA_DIR) |
| - IntegrationTest.size_info = ( |
| - archive.CreateSizeInfo(_TEST_MAP_PATH, lazy_paths)) |
| - return copy.deepcopy(IntegrationTest.size_info) |
| + def _CloneSizeInfo(self, use_output_directory=True, use_elf=True): |
| + assert not use_elf or use_output_directory |
| + i = int(use_output_directory) + int(use_elf) |
| + if not IntegrationTest.cached_size_info[i]: |
| + elf_path = _TEST_ELF_PATH if use_elf else None |
| + output_directory = _TEST_OUTPUT_DIR if use_output_directory else None |
| + IntegrationTest.cached_size_info[i] = archive.CreateSizeInfo( |
| + _TEST_MAP_PATH, elf_path, _TEST_TOOL_PREFIX, output_directory) |
| + if use_elf: |
| + with _AddMocksToPath(): |
| + IntegrationTest.cached_size_info[i].metadata = archive.CreateMetadata( |
| + _TEST_MAP_PATH, elf_path, None, _TEST_TOOL_PREFIX, |
| + output_directory) |
| + return copy.deepcopy(IntegrationTest.cached_size_info[i]) |
| - @_CompareWithGolden |
| - def test_Archive(self): |
| + def _DoArchiveTest(self, use_output_directory=True, use_elf=True, |
| + debug_measures=False): |
| with tempfile.NamedTemporaryFile(suffix='.size') as temp_file: |
| - _RunApp('archive', temp_file.name, '--output-directory', _TEST_DATA_DIR, |
| - '--map-file', _TEST_MAP_PATH) |
| + args = [temp_file.name, '--map-file', _TEST_MAP_PATH] |
| + if use_output_directory: |
| + # Let autodetection find output_directory when --elf-file is used. |
| + if not use_elf: |
| + args += ['--output-directory', _TEST_OUTPUT_DIR] |
| + else: |
| + args += ['--no-source-paths'] |
| + if use_elf: |
| + args += ['--elf-file', _TEST_ELF_PATH] |
| + _RunApp('archive', args, debug_measures=debug_measures) |
| size_info = archive.LoadAndPostProcessSizeInfo(temp_file.name) |
| # Check that saving & loading is the same as directly parsing the .map. |
| - expected_size_info = self._CloneSizeInfo() |
| + expected_size_info = self._CloneSizeInfo( |
| + use_output_directory=use_output_directory, use_elf=use_elf) |
| self.assertEquals(expected_size_info.metadata, size_info.metadata) |
| - expected = '\n'.join(describe.GenerateLines( |
| - expected_size_info, verbose=True, recursive=True)), |
| - actual = '\n'.join(describe.GenerateLines( |
| - size_info, verbose=True, recursive=True)), |
| + expected = list(describe.GenerateLines( |
| + expected_size_info, verbose=True, recursive=True)) |
| + actual = list(describe.GenerateLines( |
| + size_info, verbose=True, recursive=True)) |
| self.assertEquals(expected, actual) |
| sym_strs = (repr(sym) for sym in size_info.symbols) |
| stats = describe.DescribeSizeInfoCoverage(size_info) |
| - return itertools.chain(stats, sym_strs) |
| + if size_info.metadata: |
| + metadata = describe.DescribeMetadata(size_info.metadata) |
| + else: |
| + metadata = [] |
| + return itertools.chain(metadata, stats, sym_strs) |
| - def test_Archive_NoSourcePaths(self): |
| - # Just tests that it doesn't crash. |
| - with tempfile.NamedTemporaryFile(suffix='.size') as temp_file: |
| - _RunApp('archive', temp_file.name, '--no-source-paths', |
| - '--map-file', _TEST_MAP_PATH) |
| - archive.LoadAndPostProcessSizeInfo(temp_file.name) |
| + @_CompareWithGolden() |
| + def test_Archive(self): |
| + return self._DoArchiveTest(use_output_directory=False, use_elf=False) |
| + |
| + @_CompareWithGolden() |
| + def test_Archive_OutputDirectory(self): |
| + return self._DoArchiveTest(use_elf=False) |
| + |
| + @_CompareWithGolden() |
| + def test_Archive_Elf(self): |
| + return self._DoArchiveTest() |
| - @_CompareWithGolden |
| + @_CompareWithGolden(name='Archive_Elf') |
| + def test_Archive_Elf_DebugMeasures(self): |
| + return self._DoArchiveTest(debug_measures=True) |
| + |
| + @_CompareWithGolden() |
| def test_Console(self): |
| with tempfile.NamedTemporaryFile(suffix='.size') as size_file, \ |
| tempfile.NamedTemporaryFile(suffix='.txt') as output_file: |
| @@ -104,34 +161,85 @@ class IntegrationTest(unittest.TestCase): |
| 'ExpandRegex("_foo_")', |
| 'Print(size_info, to_file=%r)' % output_file.name, |
| ] |
| - ret = _RunApp('console', size_file.name, '--query', '; '.join(query)) |
| + ret = _RunApp('console', [size_file.name, '--query', '; '.join(query)]) |
| with open(output_file.name) as f: |
| ret.extend(l.rstrip() for l in f) |
| return ret |
| - @_CompareWithGolden |
| + @_CompareWithGolden() |
| def test_Diff_NullDiff(self): |
| with tempfile.NamedTemporaryFile(suffix='.size') as temp_file: |
| file_format.SaveSizeInfo(self._CloneSizeInfo(), temp_file.name) |
| - return _RunApp('diff', temp_file.name, temp_file.name) |
| + return _RunApp('diff', [temp_file.name, temp_file.name]) |
| - @_CompareWithGolden |
| - def test_ActualDiff(self): |
| - size_info1 = self._CloneSizeInfo() |
| - size_info2 = self._CloneSizeInfo() |
| + @_CompareWithGolden() |
| + def test_Diff_Basic(self): |
| + size_info1 = self._CloneSizeInfo(use_elf=False) |
| + size_info2 = self._CloneSizeInfo(use_elf=False) |
| size_info1.metadata = {"foo": 1, "bar": [1,2,3], "baz": "yes"} |
| size_info2.metadata = {"foo": 1, "bar": [1,3], "baz": "yes"} |
| size_info1.symbols -= size_info1.symbols[:2] |
| size_info2.symbols -= size_info2.symbols[-3:] |
| size_info1.symbols[1].size -= 10 |
| - diff = models.Diff(size_info1, size_info2) |
| - return describe.GenerateLines(diff, verbose=True) |
| + d = diff.Diff(size_info1, size_info2) |
| + return describe.GenerateLines(d, verbose=True) |
| + |
| + def test_Diff_Aliases1(self): |
| + symbols1 = self._CloneSizeInfo().symbols |
| + symbols2 = self._CloneSizeInfo().symbols |
| + |
| + # Removing 1 alias should not change the size. |
| + a1, _, _ = symbols2.Filter(lambda s: s.num_aliases == 3)[0].aliases |
| + symbols2 -= [a1] |
| + a1.aliases.remove(a1) |
| + d = diff.Diff(symbols1, symbols2) |
| + self.assertEquals(d.size, 0) |
| + self.assertEquals(d.removed_count, 1) |
| + |
| + # Adding one alias should not change size. |
| + d = diff.Diff(symbols2, symbols1) |
| + self.assertEquals(d.size, 0) |
| + self.assertEquals(d.added_count, 1) |
| + |
| + def test_Diff_Aliases2(self): |
| + symbols1 = self._CloneSizeInfo().symbols |
| + symbols2 = self._CloneSizeInfo().symbols |
| + |
| + # Removing 2 aliases should not change the size. |
| + a1, a2, _ = symbols2.Filter(lambda s: s.num_aliases == 3)[0].aliases |
| + symbols2 -= [a1, a2] |
| + a1.aliases.remove(a1) |
| + a1.aliases.remove(a2) |
| + d = diff.Diff(symbols1, symbols2) |
| + self.assertEquals(d.size, 0) |
| + self.assertEquals(d.removed_count, 2) |
| + |
| + # Adding 2 aliases should not change size. |
| + d = diff.Diff(symbols2, symbols1) |
| + self.assertEquals(d.size, 0) |
| + self.assertEquals(d.added_count, 2) |
| + |
| + def test_Diff_Aliases3(self): |
| + symbols1 = self._CloneSizeInfo().symbols |
| + symbols2 = self._CloneSizeInfo().symbols |
| + |
| + # Removing all 3 aliases should change the size. |
| + a1, a2, a3 = symbols2.Filter(lambda s: s.num_aliases == 3)[0].aliases |
| + symbols2 -= [a1, a2, a3] |
| + d = diff.Diff(symbols1, symbols2) |
| + self.assertEquals(d.size, -a1.size) |
| + self.assertEquals(d.removed_count, 3) |
| + |
| + # Adding all 3 aliases should change size. |
| + d = diff.Diff(symbols2, symbols1) |
| + self.assertEquals(d.size, a1.size) |
| + self.assertEquals(d.added_count, 3) |
| - @_CompareWithGolden |
| + @_CompareWithGolden() |
| def test_FullDescription(self): |
| return describe.GenerateLines(self._CloneSizeInfo()) |
| - @_CompareWithGolden |
| + @_CompareWithGolden() |
| def test_SymbolGroupMethods(self): |
| all_syms = self._CloneSizeInfo().symbols |
| global_syms = all_syms.WhereNameMatches('GLOBAL') |