blob: f07ab41f347b1a5116cd7aa46cbc8a26777f4fd4 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import copy
import glob
import io
import itertools
import os
import unittest
import re
import shutil
import subprocess
import sys
import tempfile
import zipfile
import archive
import data_quality
import describe
import diff
import file_format
import models
import pakfile
import test_util
import zip_util
_SCRIPT_DIR = os.path.dirname(__file__)
_TEST_DATA_DIR = test_util.TEST_DATA_DIR
_TEST_SOURCE_DIR = test_util.TEST_SOURCE_DIR
_TEST_OUTPUT_DIR = test_util.TEST_OUTPUT_DIR
_TEST_APK_ROOT_DIR = os.path.join(_TEST_DATA_DIR, 'mock_apk')
_TEST_MAP_PATH = os.path.join(_TEST_DATA_DIR, 'test.map')
_TEST_PAK_INFO_PATH = os.path.join(
_TEST_OUTPUT_DIR, 'size-info/test.apk.pak.info')
_TEST_ELF_FILE_BEGIN = os.path.join(_TEST_OUTPUT_DIR, 'elf.begin')
_TEST_APK_LOCALE_PAK_SUBPATH = 'assets/en-US.pak'
_TEST_APK_PAK_SUBPATH = 'assets/resources.pak'
_TEST_APK_LOCALE_PAK_PATH = os.path.join(_TEST_APK_ROOT_DIR,
_TEST_APK_LOCALE_PAK_SUBPATH)
_TEST_APK_PAK_PATH = os.path.join(_TEST_APK_ROOT_DIR, _TEST_APK_PAK_SUBPATH)
_TEST_ON_DEMAND_MANIFEST_PATH = os.path.join(_TEST_DATA_DIR,
'AndroidManifest_OnDemand.xml')
_TEST_ALWAYS_INSTALLED_MANIFEST_PATH = os.path.join(
_TEST_DATA_DIR, 'AndroidManifest_AlwaysInstalled.xml')
# The following files are dynamically created.
_TEST_ELF_PATH = os.path.join(_TEST_OUTPUT_DIR, 'elf')
_TEST_APK_PATH = os.path.join(_TEST_OUTPUT_DIR, 'test.apk')
_TEST_NOT_ON_DEMAND_SPLIT_APK_PATH = os.path.join(_TEST_OUTPUT_DIR,
'not_on_demand.apk')
_TEST_ON_DEMAND_SPLIT_APK_PATH = os.path.join(_TEST_OUTPUT_DIR, 'on_demand.apk')
_TEST_MINIMAL_APKS_PATH = os.path.join(_TEST_OUTPUT_DIR, 'Bundle.minimal.apks')
_TEST_SSARGS_PATH = os.path.join(_TEST_OUTPUT_DIR, 'test.ssargs')
# Generated file paths relative to apk
_TEST_APK_SO_PATH = 'lib/armeabi-v7a/test.so'
_TEST_APK_SMALL_SO_PATH = 'lib/x86/smalltest.so'
_TEST_APK_DEX_PATH = 'classes.dex'
_TEST_APK_OTHER_FILE_PATH = 'assets/icudtl.dat'
_TEST_APK_RES_FILE_PATH = 'res/drawable-v13/test.xml'
_TEST_CONFIG_JSON = os.path.join(_TEST_DATA_DIR, 'supersize.json')
_TEST_PATH_DEFAULTS = {
'assets/icudtl.dat': '../../third_party/icu/android/icudtl.dat',
}
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)
actual_lines = (re.sub(r'(elf_mtime=).*', r'\1{redacted}', l)
for l in actual_lines)
actual_lines = (re.sub(r'(Loaded from ).*', r'\1{redacted}', l)
for l in actual_lines)
test_util.Golden.CheckOrUpdate(golden_path, actual_lines)
return inner
return real_decorator
def _RunApp(name, args, debug_measures=False):
argv = [os.path.join(_SCRIPT_DIR, 'main.py'), name]
argv.extend(args)
if '-v' in sys.argv:
argv.append('-v')
with test_util.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).decode('utf-8').splitlines()
def _AllMetadata(size_info):
return [c.metadata for c in size_info.containers]
class IntegrationTest(unittest.TestCase):
maxDiff = None # Don't trucate diffs in errors.
cached_size_info = {}
@staticmethod
def _CreateBlankData(power_of_two):
data = '\0'
for _ in range(power_of_two):
data = data + data
return data
@staticmethod
def _SafeRemoveFiles(file_names):
for file_name in file_names:
if os.path.exists(file_name):
os.remove(file_name)
@classmethod
def setUpClass(cls):
shutil.copy(_TEST_ELF_FILE_BEGIN, _TEST_ELF_PATH)
# Exactly 128MB of data (2^27), extra bytes will be accounted in overhead.
with open(_TEST_ELF_PATH, 'a') as elf_file:
elf_file.write(IntegrationTest._CreateBlankData(27))
with zipfile.ZipFile(_TEST_APK_PATH, 'w') as apk_file:
apk_file.write(_TEST_ELF_PATH, _TEST_APK_SO_PATH)
# Exactly 4MB of data (2^22), with some zipalign overhead.
info = zipfile.ZipInfo(_TEST_APK_SMALL_SO_PATH)
info.extra = b'\x00' * 16
apk_file.writestr(info, IntegrationTest._CreateBlankData(22))
# Exactly 1MB of data (2^20).
apk_file.writestr(
_TEST_APK_OTHER_FILE_PATH, IntegrationTest._CreateBlankData(20))
# Exactly 1KB of data (2^10).
apk_file.writestr(
_TEST_APK_RES_FILE_PATH, IntegrationTest._CreateBlankData(10))
locale_pak_rel_path = os.path.relpath(
_TEST_APK_LOCALE_PAK_PATH, _TEST_APK_ROOT_DIR)
apk_file.write(_TEST_APK_LOCALE_PAK_PATH, locale_pak_rel_path)
pak_rel_path = os.path.relpath(_TEST_APK_PAK_PATH, _TEST_APK_ROOT_DIR)
apk_file.write(_TEST_APK_PAK_PATH, pak_rel_path)
# Exactly 8MB of data (2^23).
apk_file.writestr(
_TEST_APK_DEX_PATH, IntegrationTest._CreateBlankData(23))
with zipfile.ZipFile(_TEST_NOT_ON_DEMAND_SPLIT_APK_PATH, 'w') as z:
z.write(_TEST_ALWAYS_INSTALLED_MANIFEST_PATH, 'AndroidManifest.xml')
with zipfile.ZipFile(_TEST_ON_DEMAND_SPLIT_APK_PATH, 'w') as z:
z.write(_TEST_ON_DEMAND_MANIFEST_PATH, 'AndroidManifest.xml')
with zipfile.ZipFile(_TEST_MINIMAL_APKS_PATH, 'w') as apk_file:
apk_file.writestr('toc.pb', 'x' * 80)
apk_file.write(_TEST_APK_PATH, 'splits/base-master.apk')
apk_file.writestr('splits/base-en.apk', 'x' * 10) # Ignored.
apk_file.write(_TEST_NOT_ON_DEMAND_SPLIT_APK_PATH,
'splits/base-hi.apk') # Not Ignored.
apk_file.write(_TEST_NOT_ON_DEMAND_SPLIT_APK_PATH,
'splits/not_on_demand-master.apk')
apk_file.write(_TEST_ON_DEMAND_SPLIT_APK_PATH,
'splits/on_demand-master.apk')
apk_file.writestr('splits/vr-en.apk', 'x' * 40)
@classmethod
def tearDownClass(cls):
IntegrationTest._SafeRemoveFiles([
_TEST_ELF_PATH,
_TEST_APK_PATH,
_TEST_NOT_ON_DEMAND_SPLIT_APK_PATH,
_TEST_ON_DEMAND_SPLIT_APK_PATH,
_TEST_MINIMAL_APKS_PATH,
])
def _CloneSizeInfo(self,
*,
use_output_directory=True,
use_elf=False,
use_apk=False,
use_minimal_apks=False,
use_pak=False,
use_aux_elf=False,
ignore_linker_map=False):
assert not use_elf or use_output_directory
assert not (use_apk and use_pak)
assert not (use_apk and use_minimal_apks)
cache_key = (use_output_directory, use_elf, use_apk, use_minimal_apks,
use_pak, use_aux_elf, ignore_linker_map)
if cache_key not in IntegrationTest.cached_size_info:
output_directory = _TEST_OUTPUT_DIR if use_output_directory else None
def iter_specs():
pak_spec = None
if use_pak or use_apk or use_minimal_apks:
pak_spec = archive.PakSpec()
if use_pak:
pak_spec.pak_paths = [_TEST_APK_LOCALE_PAK_PATH, _TEST_APK_PAK_PATH]
pak_spec.pak_info_path = _TEST_PAK_INFO_PATH
else:
pak_spec.apk_pak_paths = [
_TEST_APK_LOCALE_PAK_SUBPATH, _TEST_APK_PAK_SUBPATH
]
native_spec = archive.NativeSpec()
# TODO(crbug.com/1193507): Remove when we implement string literal
# tracking without map files.
if ignore_linker_map:
native_spec.track_string_literals = False
else:
native_spec.map_path = _TEST_MAP_PATH
native_spec.linker_name = 'gold'
if use_elf or use_aux_elf:
native_spec.elf_path = _TEST_ELF_PATH
apk_spec = None
if use_apk or use_minimal_apks:
apk_spec = archive.ApkSpec(apk_path=_TEST_APK_PATH)
apk_spec.path_defaults = _TEST_PATH_DEFAULTS
apk_spec.ignore_apk_paths.update(
['classes.dex', _TEST_APK_SO_PATH, _TEST_APK_SMALL_SO_PATH])
if pak_spec and pak_spec.apk_pak_paths:
apk_spec.ignore_apk_paths.update(pak_spec.apk_pak_paths)
if output_directory:
orig_path = _TEST_APK_PATH
if use_minimal_apks:
orig_path = _TEST_MINIMAL_APKS_PATH.replace(
'.minimal.apks', '.aab')
apk_spec.size_info_prefix = os.path.join(
output_directory, 'size-info', os.path.basename(orig_path))
native_spec.apk_so_path = _TEST_APK_SO_PATH
small_native_spec = archive.NativeSpec(
apk_so_path=_TEST_APK_SMALL_SO_PATH)
if use_minimal_apks:
apk_spec.minimal_apks_path = _TEST_MINIMAL_APKS_PATH
apk_spec.split_name = 'base'
container_name = ''
if use_apk:
container_name = 'test.apk'
if use_minimal_apks:
container_name = 'Bundle.minimal.apks/base.apk'
container_spec = archive.ContainerSpec(
container_name=container_name,
apk_spec=apk_spec,
pak_spec=pak_spec,
native_spec=native_spec,
source_directory=_TEST_SOURCE_DIR,
output_directory=output_directory)
if not apk_spec:
yield container_spec
return
container_spec.native_spec = None
yield container_spec
container_spec = copy.copy(container_spec)
container_spec.container_name = (
f'{container_name}/test.so (armeabi-v7a)')
container_spec.pak_spec = None
container_spec.native_spec = native_spec
yield container_spec
container_spec = copy.copy(container_spec)
container_spec.container_name = f'{container_name}/smalltest.so (x86)'
container_spec.native_spec = small_native_spec
yield container_spec
container_spec = None
if use_minimal_apks:
for split_name, apk_path in [
('base-hi', _TEST_NOT_ON_DEMAND_SPLIT_APK_PATH),
('not_on_demand', _TEST_NOT_ON_DEMAND_SPLIT_APK_PATH),
('on_demand', _TEST_ON_DEMAND_SPLIT_APK_PATH),
]:
apk_spec = archive.ApkSpec(
minimal_apks_path=_TEST_MINIMAL_APKS_PATH,
apk_path=apk_path,
split_name=split_name,
size_info_prefix=apk_spec.size_info_prefix)
container_name = 'Bundle.minimal.apks/%s.apk' % split_name
if split_name == 'on_demand':
container_name += '?'
apk_spec.default_component = 'DEFAULT'
yield archive.ContainerSpec(container_name=container_name,
apk_spec=apk_spec,
pak_spec=None,
native_spec=None,
source_directory=_TEST_SOURCE_DIR,
output_directory=output_directory)
with test_util.AddMocksToPath(), \
zip_util.ApkFileManager() as apk_file_manager:
build_config = archive.CreateBuildConfig(output_directory,
_TEST_SOURCE_DIR)
container_specs = list(iter_specs())
size_info = archive.CreateSizeInfo(container_specs, build_config,
apk_file_manager)
IntegrationTest.cached_size_info[cache_key] = size_info
return copy.deepcopy(IntegrationTest.cached_size_info[cache_key])
def _DoArchive(self,
archive_path,
*,
use_output_directory=True,
use_elf=False,
use_map=False,
use_apk=False,
use_ssargs=False,
use_minimal_apks=False,
use_pak=False,
use_aux_elf=None,
ignore_linker_map=False,
debug_measures=False,
include_padding=False):
args = [
archive_path,
'--source-directory',
_TEST_SOURCE_DIR,
'--json-config',
_TEST_CONFIG_JSON,
'--abi-filter',
'armeabi-v7a',
]
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-output-directory']
if use_ssargs:
args += ['-f', _TEST_SSARGS_PATH]
if use_apk:
args += ['-f', _TEST_APK_PATH]
if use_minimal_apks:
args += ['-f', _TEST_MINIMAL_APKS_PATH]
if use_elf:
args += ['-f', _TEST_ELF_PATH]
if use_map:
args += ['-f', _TEST_MAP_PATH]
if use_pak:
args += ['--pak-file', _TEST_APK_LOCALE_PAK_PATH,
'--pak-file', _TEST_APK_PAK_PATH,
'--pak-info-file', _TEST_PAK_INFO_PATH]
if ignore_linker_map:
args += ['--no-map-file']
elif not use_ssargs and not use_map:
args += ['--aux-map-file', _TEST_MAP_PATH]
if use_aux_elf:
args += ['--aux-elf-file', _TEST_ELF_PATH]
if include_padding:
args += ['--include-padding']
_RunApp('archive', args, debug_measures=debug_measures)
def _DoArchiveTest(self,
*,
use_output_directory=True,
use_map=False,
use_elf=False,
use_apk=False,
use_minimal_apks=False,
use_pak=False,
use_aux_elf=False,
ignore_linker_map=False,
debug_measures=False,
include_padding=False):
with tempfile.NamedTemporaryFile(suffix='.size') as temp_file:
self._DoArchive(temp_file.name,
use_output_directory=use_output_directory,
use_map=use_map,
use_elf=use_elf,
use_apk=use_apk,
use_minimal_apks=use_minimal_apks,
use_pak=use_pak,
use_aux_elf=use_aux_elf,
ignore_linker_map=ignore_linker_map,
debug_measures=debug_measures,
include_padding=include_padding)
size_info = archive.LoadAndPostProcessSizeInfo(temp_file.name)
# Check that saving & loading is the same as directly parsing.
expected_size_info = self._CloneSizeInfo(
use_output_directory=use_output_directory,
use_elf=use_elf,
use_apk=use_apk,
use_minimal_apks=use_minimal_apks,
use_pak=use_pak,
use_aux_elf=use_aux_elf,
ignore_linker_map=ignore_linker_map)
self.assertEqual(_AllMetadata(expected_size_info), _AllMetadata(size_info))
# Don't cluster.
expected_size_info.symbols = expected_size_info.raw_symbols
size_info.symbols = size_info.raw_symbols
expected = list(describe.GenerateLines(expected_size_info, verbose=True))
actual = list(describe.GenerateLines(size_info, verbose=True))
self.assertEqual(expected, actual)
sym_strs = (repr(sym) for sym in size_info.symbols)
stats = data_quality.DescribeSizeInfoCoverage(size_info)
if len(size_info.containers) == 1:
# If there's only one container, merge the its metadata into build_config.
merged_data_desc = describe.DescribeDict(size_info.metadata_legacy)
else:
merged_data_desc = describe.DescribeDict(size_info.build_config)
for m in _AllMetadata(size_info):
merged_data_desc.extend(describe.DescribeDict(m))
return itertools.chain(merged_data_desc, stats, sym_strs)
@_CompareWithGolden()
def test_Archive(self):
return self._DoArchiveTest(use_output_directory=False, use_map=True)
@_CompareWithGolden()
def test_Archive_OutputDirectory(self):
return self._DoArchiveTest(use_map=True)
@_CompareWithGolden()
def test_Archive_Elf(self):
return self._DoArchiveTest(use_elf=True)
@_CompareWithGolden()
def test_Archive_Elf_No_Map(self):
return self._DoArchiveTest(use_elf=True, ignore_linker_map=True)
@_CompareWithGolden()
def test_Archive_Apk(self):
return self._DoArchiveTest(use_apk=True, use_aux_elf=True)
@_CompareWithGolden()
def test_Archive_MinimalApks(self):
return self._DoArchiveTest(use_minimal_apks=True, use_aux_elf=True)
@_CompareWithGolden()
def test_Archive_Pak_Files(self):
return self._DoArchiveTest(use_pak=True, use_aux_elf=True)
@_CompareWithGolden(name='Archive_Elf')
def test_Archive_Elf_DebugMeasures(self):
return self._DoArchiveTest(use_elf=True, debug_measures=True)
@_CompareWithGolden(name='Archive_Apk')
def test_ArchiveSparse(self):
return self._DoArchiveTest(use_apk=True,
use_aux_elf=True,
include_padding=True)
def test_SaveDeltaSizeInfo(self):
# Check that saving & loading is the same as directly parsing.
orig_info1 = self._CloneSizeInfo(use_apk=True, use_aux_elf=True)
orig_info2 = copy.deepcopy(orig_info1)
# Remove the last 5 symbols to ensure a small but non-empty diff.
orig_info2.raw_symbols._symbols[-5:] = []
orig_delta = diff.Diff(orig_info1, orig_info2)
with tempfile.NamedTemporaryFile(suffix='.sizediff') as sizediff_file:
file_format.SaveDeltaSizeInfo(orig_delta, sizediff_file.name)
new_info1, new_info2 = archive.LoadAndPostProcessDeltaSizeInfo(
sizediff_file.name)
new_delta = diff.Diff(new_info1, new_info2)
# File format discards unchanged symbols.
orig_delta.raw_symbols = orig_delta.raw_symbols.WhereDiffStatusIs(
models.DIFF_STATUS_UNCHANGED).Inverted()
self.assertEqual(list(describe.GenerateLines(orig_delta, verbose=True)),
list(describe.GenerateLines(new_delta, verbose=True)))
@_CompareWithGolden()
def test_Console(self):
with tempfile.NamedTemporaryFile(suffix='.size') as size_file, \
tempfile.NamedTemporaryFile(suffix='.txt') as output_file:
file_format.SaveSizeInfo(self._CloneSizeInfo(use_elf=True),
size_file.name)
query = [
'ShowExamples()',
'ExpandRegex("_foo_")',
'canned_queries.CategorizeGenerated()',
'canned_queries.CategorizeByChromeComponent()',
'canned_queries.LargeFiles()',
'canned_queries.TemplatesByName()',
'canned_queries.StaticInitializers()',
'canned_queries.PakByPath()',
'Print(ReadStringLiterals(elf_path={}))'.format(repr(_TEST_ELF_PATH)),
'Print(size_info, to_file=%r)' % output_file.name,
]
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()
def test_Csv(self):
with tempfile.NamedTemporaryFile(suffix='.size') as size_file, \
tempfile.NamedTemporaryFile(suffix='.txt') as output_file:
file_format.SaveSizeInfo(self._CloneSizeInfo(use_elf=True),
size_file.name)
query = [
'Csv(size_info, to_file=%r)' % output_file.name,
]
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()
def test_Diff_NullDiff(self):
with tempfile.NamedTemporaryFile(suffix='.size') as temp_file:
file_format.SaveSizeInfo(self._CloneSizeInfo(use_elf=True),
temp_file.name)
return _RunApp('diff', [temp_file.name, temp_file.name])
# Runs archive 3 times, and asserts the contents are the same each time.
def test_Idempotent(self):
prev_contents = None
for _ in range(3):
with tempfile.NamedTemporaryFile(suffix='.size') as temp_file:
self._DoArchive(temp_file.name, use_map=True)
contents = temp_file.read()
self.assertTrue(prev_contents is None or contents == prev_contents)
prev_contents = contents
@_CompareWithGolden()
def test_Diff_Basic(self):
size_info1 = self._CloneSizeInfo(use_pak=True)
size_info2 = self._CloneSizeInfo(use_pak=True)
size_info2.build_config['git_revision'] = 'xyz789'
container1 = size_info1.containers[0]
container2 = size_info2.containers[0]
container1.metadata = {"foo": 1, "bar": [1, 2, 3], "baz": "yes"}
container2.metadata = {"foo": 1, "bar": [1, 3], "baz": "yes"}
size_info1.raw_symbols -= size_info1.raw_symbols.WhereNameMatches(
r'pLinuxKernelCmpxchg|pLinuxKernelMemoryBarrier')
size_info2.raw_symbols -= size_info2.raw_symbols.WhereNameMatches(
r'IDS_AW_WEBPAGE_PARENTAL_|IDS_WEB_FONT_FAMILY|IDS_WEB_FONT_SIZE')
changed_sym = size_info1.raw_symbols.WhereNameMatches('Patcher::Name_')[0]
changed_sym.size -= 10
padding_sym = size_info2.raw_symbols.WhereNameMatches('symbol gap 0')[0]
padding_sym.padding += 20
padding_sym.size += 20
# Test pak symbols changing .grd files. They should not show as changed.
pak_sym = size_info2.raw_symbols.WhereNameMatches(
r'IDR_PDF_COMPOSITOR_MANIFEST')[0]
pak_sym.full_name = pak_sym.full_name.replace('.grd', '2.grd')
# Serialize & de-serialize so that name normalization runs again for the pak
# symbol.
bytesio = io.BytesIO()
file_format.SaveSizeInfo(size_info2, 'path', file_obj=bytesio)
bytesio.seek(0)
size_info2 = archive.LoadAndPostProcessSizeInfo('path', file_obj=bytesio)
d = diff.Diff(size_info1, size_info2)
d.raw_symbols = d.raw_symbols.Sorted()
self.assertEqual((1, 2, 3), d.raw_symbols.CountsByDiffStatus()[1:])
changed_sym = d.raw_symbols.WhereNameMatches('Patcher::Name_')[0]
padding_sym = d.raw_symbols.WhereNameMatches('symbol gap 0')[0]
bss_sym = d.raw_symbols.WhereInSection(models.SECTION_BSS)[0]
# Padding-only deltas should sort after all non-padding changes.
padding_idx = d.raw_symbols.index(padding_sym)
changed_idx = d.raw_symbols.index(changed_sym)
bss_idx = d.raw_symbols.index(bss_sym)
self.assertLess(changed_idx, padding_idx)
# And before bss.
self.assertLess(padding_idx, bss_idx)
return describe.GenerateLines(d, verbose=True)
@_CompareWithGolden()
def test_FullDescription(self):
size_info = self._CloneSizeInfo(use_elf=True)
# Show both clustered and non-clustered so that they can be compared.
size_info.symbols = size_info.raw_symbols
return itertools.chain(
describe.GenerateLines(size_info, verbose=True),
describe.GenerateLines(size_info.symbols._Clustered(), recursive=True,
verbose=True),
)
@_CompareWithGolden()
def test_SymbolGroupMethods(self):
all_syms = self._CloneSizeInfo(use_elf=True).symbols
global_syms = all_syms.WhereNameMatches('GLOBAL')
# Tests Filter(), Inverted(), and __sub__().
non_global_syms = global_syms.Inverted()
self.assertEqual(non_global_syms, (all_syms - global_syms))
# Tests Sorted() and __add__().
self.assertEqual(all_syms.Sorted(),
(global_syms + non_global_syms).Sorted())
# Tests GroupedByName() and __len__().
return itertools.chain(
['GroupedByName()'],
describe.GenerateLines(all_syms.GroupedByName()),
['GroupedByName(depth=1)'],
describe.GenerateLines(all_syms.GroupedByName(depth=1)),
['GroupedByName(depth=-1)'],
describe.GenerateLines(all_syms.GroupedByName(depth=-1)),
['GroupedByName(depth=1, min_count=2)'],
describe.GenerateLines(all_syms.GroupedByName(depth=1, min_count=2)),
)
@_CompareWithGolden()
def test_ArchiveContainers(self):
with tempfile.NamedTemporaryFile(suffix='.size') as temp_file:
self._DoArchive(temp_file.name,
use_output_directory=True,
use_ssargs=True)
size_info = archive.LoadAndPostProcessSizeInfo(temp_file.name)
# Don't cluster.
size_info.symbols = size_info.raw_symbols
sym_strs = (repr(sym) for sym in size_info.symbols)
build_config = describe.DescribeDict(size_info.build_config)
metadata = itertools.chain.from_iterable(
itertools.chain([c.name], describe.DescribeDict(c.metadata))
for c in size_info.containers)
return itertools.chain(
['BuildConfig:'],
build_config,
['Metadata:'],
metadata,
['Symbols:'],
sym_strs,
)
def main():
argv = sys.argv
if len(argv) > 1 and argv[1] == '--update':
argv.pop(0)
test_util.Golden.EnableUpdate()
for f in glob.glob(os.path.join(_TEST_DATA_DIR, '*.golden')):
os.unlink(f)
unittest.main(argv=argv)
if __name__ == '__main__':
main()