| 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..d0f6702cd4f5e1af6095e3d47e6fab17cf469480 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')
|
|
|
| - 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')
|
|
|