Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Side by Side Diff: tools/binary_size/libsupersize/integration_test.py

Issue 2851473003: supersize: Track symbol aliases and shared symbols (Closed)
Patch Set: tweak nm interfface Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2017 The Chromium Authors. All rights reserved. 2 # Copyright 2017 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 import contextlib
6 import copy 7 import copy
7 import difflib 8 import difflib
8 import glob 9 import glob
9 import itertools 10 import itertools
10 import logging 11 import logging
11 import os 12 import os
12 import unittest 13 import unittest
14 import re
13 import subprocess 15 import subprocess
14 import sys 16 import sys
15 import tempfile 17 import tempfile
16 18
17 import archive 19 import archive
18 import describe 20 import describe
21 import diff
19 import file_format 22 import file_format
20 import models 23 import models
21 import paths
22 24
23 25
24 _SCRIPT_DIR = os.path.dirname(__file__) 26 _SCRIPT_DIR = os.path.dirname(__file__)
25 _TEST_DATA_DIR = os.path.join(_SCRIPT_DIR, 'testdata') 27 _TEST_DATA_DIR = os.path.join(_SCRIPT_DIR, 'testdata')
28 _TEST_OUTPUT_DIR = os.path.join(_TEST_DATA_DIR, 'mock_output_directory')
29 _TEST_TOOL_PREFIX = os.path.join(
30 os.path.abspath(_TEST_DATA_DIR), 'mock_toolchain', '')
26 _TEST_MAP_PATH = os.path.join(_TEST_DATA_DIR, 'test.map') 31 _TEST_MAP_PATH = os.path.join(_TEST_DATA_DIR, 'test.map')
32 _TEST_ELF_PATH = os.path.join(_TEST_OUTPUT_DIR, 'elf')
27 33
28 update_goldens = False 34 update_goldens = False
29 35
30 36
31 def _AssertGolden(expected_lines, actual_lines): 37 def _AssertGolden(expected_lines, actual_lines):
32 expected = list(expected_lines) 38 expected = list(expected_lines)
33 actual = list(l + '\n' for l in actual_lines) 39 actual = list(l + '\n' for l in actual_lines)
34 assert actual == expected, ('Did not match .golden.\n' + 40 assert actual == expected, ('Did not match .golden.\n' +
35 ''.join(difflib.unified_diff(expected, actual, 'expected', 'actual'))) 41 ''.join(difflib.unified_diff(expected, actual, 'expected', 'actual')))
36 42
37 43
38 def _CompareWithGolden(func): 44 def _CompareWithGolden(name=None):
39 name = func.__name__.replace('test_', '') 45 def real_decorator(func):
40 golden_path = os.path.join(_TEST_DATA_DIR, name + '.golden') 46 basename = name
47 if not basename:
48 basename = func.__name__.replace('test_', '')
49 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.
41 50
42 def inner(self): 51 def inner(self):
43 actual_lines = func(self) 52 actual_lines = func(self)
53 actual_lines = (re.sub(r'(elf_mtime=).*', r'\1{redacted}', l)
54 for l in actual_lines)
44 55
45 if update_goldens: 56 if update_goldens:
46 with open(golden_path, 'w') as file_obj: 57 with open(golden_path, 'w') as file_obj:
47 describe.WriteLines(actual_lines, file_obj.write) 58 describe.WriteLines(actual_lines, file_obj.write)
48 logging.info('Wrote %s', golden_path) 59 logging.info('Wrote %s', golden_path)
49 else: 60 else:
50 with open(golden_path) as file_obj: 61 with open(golden_path) as file_obj:
51 _AssertGolden(file_obj, actual_lines) 62 _AssertGolden(file_obj, actual_lines)
52 return inner 63 return inner
64 return real_decorator
53 65
54 66
55 def _RunApp(name, *args): 67 @contextlib.contextmanager
68 def _AddMocksToPath():
69 prev_path = os.environ['PATH']
70 os.environ['PATH'] = _TEST_TOOL_PREFIX[:-1] + os.path.pathsep + prev_path
71 yield
72 os.environ['PATH'] = prev_path
73
74
75 def _RunApp(name, args, debug_measures=False):
56 argv = [os.path.join(_SCRIPT_DIR, 'main.py'), name, '--no-pypy'] 76 argv = [os.path.join(_SCRIPT_DIR, 'main.py'), name, '--no-pypy']
57 argv.extend(args) 77 argv.extend(args)
58 return subprocess.check_output(argv).splitlines() 78 with _AddMocksToPath():
79 env = None
80 if debug_measures:
81 env = os.environ.copy()
82 env['SUPERSIZE_DISABLE_ASYNC'] = '1'
83 env['SUPERSIZE_MEASURE_GZIP'] = '1'
84
85 return subprocess.check_output(argv, env=env).splitlines()
59 86
60 87
61 class IntegrationTest(unittest.TestCase): 88 class IntegrationTest(unittest.TestCase):
62 size_info = None 89 cached_size_info = [None, None, None]
63 90
64 def _CloneSizeInfo(self): 91 def _CloneSizeInfo(self, use_output_directory=True, use_elf=True):
65 if not IntegrationTest.size_info: 92 assert not use_elf or use_output_directory
66 lazy_paths = paths.LazyPaths(output_directory=_TEST_DATA_DIR) 93 i = int(use_output_directory) + int(use_elf)
67 IntegrationTest.size_info = ( 94 if not IntegrationTest.cached_size_info[i]:
68 archive.CreateSizeInfo(_TEST_MAP_PATH, lazy_paths)) 95 elf_path = _TEST_ELF_PATH if use_elf else None
69 return copy.deepcopy(IntegrationTest.size_info) 96 output_directory = _TEST_OUTPUT_DIR if use_output_directory else None
97 IntegrationTest.cached_size_info[i] = archive.CreateSizeInfo(
98 _TEST_MAP_PATH, elf_path, _TEST_TOOL_PREFIX, output_directory)
99 if use_elf:
100 with _AddMocksToPath():
101 IntegrationTest.cached_size_info[i].metadata = archive.CreateMetadata(
102 _TEST_MAP_PATH, elf_path, None, _TEST_TOOL_PREFIX,
103 output_directory)
104 return copy.deepcopy(IntegrationTest.cached_size_info[i])
70 105
71 @_CompareWithGolden 106 def _DoArchiveTest(self, use_output_directory=True, use_elf=True,
72 def test_Archive(self): 107 debug_measures=False):
73 with tempfile.NamedTemporaryFile(suffix='.size') as temp_file: 108 with tempfile.NamedTemporaryFile(suffix='.size') as temp_file:
74 _RunApp('archive', temp_file.name, '--output-directory', _TEST_DATA_DIR, 109 args = [temp_file.name, '--map-file', _TEST_MAP_PATH]
75 '--map-file', _TEST_MAP_PATH) 110 if use_output_directory:
111 # Let autodetection find output_directory when --elf-file is used.
112 if not use_elf:
113 args += ['--output-directory', _TEST_OUTPUT_DIR]
114 else:
115 args += ['--no-source-paths']
116 if use_elf:
117 args += ['--elf-file', _TEST_ELF_PATH]
118 _RunApp('archive', args, debug_measures=debug_measures)
76 size_info = archive.LoadAndPostProcessSizeInfo(temp_file.name) 119 size_info = archive.LoadAndPostProcessSizeInfo(temp_file.name)
77 # Check that saving & loading is the same as directly parsing the .map. 120 # Check that saving & loading is the same as directly parsing the .map.
78 expected_size_info = self._CloneSizeInfo() 121 expected_size_info = self._CloneSizeInfo(
122 use_output_directory=use_output_directory, use_elf=use_elf)
79 self.assertEquals(expected_size_info.metadata, size_info.metadata) 123 self.assertEquals(expected_size_info.metadata, size_info.metadata)
80 expected = '\n'.join(describe.GenerateLines( 124 expected = list(describe.GenerateLines(
81 expected_size_info, verbose=True, recursive=True)), 125 expected_size_info, verbose=True, recursive=True))
82 actual = '\n'.join(describe.GenerateLines( 126 actual = list(describe.GenerateLines(
83 size_info, verbose=True, recursive=True)), 127 size_info, verbose=True, recursive=True))
84 self.assertEquals(expected, actual) 128 self.assertEquals(expected, actual)
85 129
86 sym_strs = (repr(sym) for sym in size_info.symbols) 130 sym_strs = (repr(sym) for sym in size_info.symbols)
87 stats = describe.DescribeSizeInfoCoverage(size_info) 131 stats = describe.DescribeSizeInfoCoverage(size_info)
88 return itertools.chain(stats, sym_strs) 132 if size_info.metadata:
133 metadata = describe.DescribeMetadata(size_info.metadata)
134 else:
135 metadata = []
136 return itertools.chain(metadata, stats, sym_strs)
89 137
90 def test_Archive_NoSourcePaths(self): 138 @_CompareWithGolden()
91 # Just tests that it doesn't crash. 139 def test_Archive(self):
92 with tempfile.NamedTemporaryFile(suffix='.size') as temp_file: 140 return self._DoArchiveTest(use_output_directory=False, use_elf=False)
93 _RunApp('archive', temp_file.name, '--no-source-paths',
94 '--map-file', _TEST_MAP_PATH)
95 archive.LoadAndPostProcessSizeInfo(temp_file.name)
96 141
97 @_CompareWithGolden 142 @_CompareWithGolden()
143 def test_Archive_OutputDirectory(self):
144 return self._DoArchiveTest(use_elf=False)
145
146 @_CompareWithGolden()
147 def test_Archive_Elf(self):
148 return self._DoArchiveTest()
149
150 @_CompareWithGolden(name='Archive_Elf')
151 def test_Archive_Elf_DebugMeasures(self):
152 return self._DoArchiveTest(debug_measures=True)
153
154 @_CompareWithGolden()
98 def test_Console(self): 155 def test_Console(self):
99 with tempfile.NamedTemporaryFile(suffix='.size') as size_file, \ 156 with tempfile.NamedTemporaryFile(suffix='.size') as size_file, \
100 tempfile.NamedTemporaryFile(suffix='.txt') as output_file: 157 tempfile.NamedTemporaryFile(suffix='.txt') as output_file:
101 file_format.SaveSizeInfo(self._CloneSizeInfo(), size_file.name) 158 file_format.SaveSizeInfo(self._CloneSizeInfo(), size_file.name)
102 query = [ 159 query = [
103 'ShowExamples()', 160 'ShowExamples()',
104 'ExpandRegex("_foo_")', 161 'ExpandRegex("_foo_")',
105 'Print(size_info, to_file=%r)' % output_file.name, 162 'Print(size_info, to_file=%r)' % output_file.name,
106 ] 163 ]
107 ret = _RunApp('console', size_file.name, '--query', '; '.join(query)) 164 ret = _RunApp('console', [size_file.name, '--query', '; '.join(query)])
108 with open(output_file.name) as f: 165 with open(output_file.name) as f:
109 ret.extend(l.rstrip() for l in f) 166 ret.extend(l.rstrip() for l in f)
110 return ret 167 return ret
111 168
112 @_CompareWithGolden 169 @_CompareWithGolden()
113 def test_Diff_NullDiff(self): 170 def test_Diff_NullDiff(self):
114 with tempfile.NamedTemporaryFile(suffix='.size') as temp_file: 171 with tempfile.NamedTemporaryFile(suffix='.size') as temp_file:
115 file_format.SaveSizeInfo(self._CloneSizeInfo(), temp_file.name) 172 file_format.SaveSizeInfo(self._CloneSizeInfo(), temp_file.name)
116 return _RunApp('diff', temp_file.name, temp_file.name) 173 return _RunApp('diff', [temp_file.name, temp_file.name])
117 174
118 @_CompareWithGolden 175 @_CompareWithGolden()
119 def test_ActualDiff(self): 176 def test_Diff_Basic(self):
120 size_info1 = self._CloneSizeInfo() 177 size_info1 = self._CloneSizeInfo(use_elf=False)
121 size_info2 = self._CloneSizeInfo() 178 size_info2 = self._CloneSizeInfo(use_elf=False)
122 size_info1.metadata = {"foo": 1, "bar": [1,2,3], "baz": "yes"} 179 size_info1.metadata = {"foo": 1, "bar": [1,2,3], "baz": "yes"}
123 size_info2.metadata = {"foo": 1, "bar": [1,3], "baz": "yes"} 180 size_info2.metadata = {"foo": 1, "bar": [1,3], "baz": "yes"}
124 size_info1.symbols -= size_info1.symbols[:2] 181 size_info1.symbols -= size_info1.symbols[:2]
125 size_info2.symbols -= size_info2.symbols[-3:] 182 size_info2.symbols -= size_info2.symbols[-3:]
126 size_info1.symbols[1].size -= 10 183 size_info1.symbols[1].size -= 10
127 diff = models.Diff(size_info1, size_info2) 184 d = diff.Diff(size_info1, size_info2)
128 return describe.GenerateLines(diff, verbose=True) 185 return describe.GenerateLines(d, verbose=True)
129 186
130 @_CompareWithGolden 187 def test_Diff_Aliases1(self):
188 symbols1 = self._CloneSizeInfo().symbols
189 symbols2 = self._CloneSizeInfo().symbols
190
191 # Removing 1 alias should not change the size.
192 a1, _, _ = symbols2.Filter(lambda s: s.num_aliases == 3)[0].aliases
193 symbols2 -= [a1]
194 a1.aliases.remove(a1)
195 d = diff.Diff(symbols1, symbols2)
196 self.assertEquals(d.size, 0)
197 self.assertEquals(d.removed_count, 1)
198
199 # Adding one alias should not change size.
200 d = diff.Diff(symbols2, symbols1)
201 self.assertEquals(d.size, 0)
202 self.assertEquals(d.added_count, 1)
203
204 def test_Diff_Aliases2(self):
205 symbols1 = self._CloneSizeInfo().symbols
206 symbols2 = self._CloneSizeInfo().symbols
207
208 # Removing 2 aliases should not change the size.
209 a1, a2, _ = symbols2.Filter(lambda s: s.num_aliases == 3)[0].aliases
210 symbols2 -= [a1, a2]
211 a1.aliases.remove(a1)
212 a1.aliases.remove(a2)
213 d = diff.Diff(symbols1, symbols2)
214 self.assertEquals(d.size, 0)
215 self.assertEquals(d.removed_count, 2)
216
217 # Adding 2 aliases should not change size.
218 d = diff.Diff(symbols2, symbols1)
219 self.assertEquals(d.size, 0)
220 self.assertEquals(d.added_count, 2)
221
222 def test_Diff_Aliases3(self):
223 symbols1 = self._CloneSizeInfo().symbols
224 symbols2 = self._CloneSizeInfo().symbols
225
226 # Removing all 3 aliases should change the size.
227 a1, a2, a3 = symbols2.Filter(lambda s: s.num_aliases == 3)[0].aliases
228 symbols2 -= [a1, a2, a3]
229 d = diff.Diff(symbols1, symbols2)
230 self.assertEquals(d.size, -a1.size)
231 self.assertEquals(d.removed_count, 3)
232
233 # Adding all 3 aliases should change size.
234 d = diff.Diff(symbols2, symbols1)
235 self.assertEquals(d.size, a1.size)
236 self.assertEquals(d.added_count, 3)
237
238 @_CompareWithGolden()
131 def test_FullDescription(self): 239 def test_FullDescription(self):
132 return describe.GenerateLines(self._CloneSizeInfo()) 240 return describe.GenerateLines(self._CloneSizeInfo())
133 241
134 @_CompareWithGolden 242 @_CompareWithGolden()
135 def test_SymbolGroupMethods(self): 243 def test_SymbolGroupMethods(self):
136 all_syms = self._CloneSizeInfo().symbols 244 all_syms = self._CloneSizeInfo().symbols
137 global_syms = all_syms.WhereNameMatches('GLOBAL') 245 global_syms = all_syms.WhereNameMatches('GLOBAL')
138 # Tests Filter(), Inverted(), and __sub__(). 246 # Tests Filter(), Inverted(), and __sub__().
139 non_global_syms = global_syms.Inverted() 247 non_global_syms = global_syms.Inverted()
140 self.assertEqual(non_global_syms, (all_syms - global_syms)) 248 self.assertEqual(non_global_syms, (all_syms - global_syms))
141 # Tests Sorted() and __add__(). 249 # Tests Sorted() and __add__().
142 self.assertEqual(all_syms.Sorted(), 250 self.assertEqual(all_syms.Sorted(),
143 (global_syms + non_global_syms).Sorted()) 251 (global_syms + non_global_syms).Sorted())
144 # Tests GroupByNamespace() and __len__(). 252 # Tests GroupByNamespace() and __len__().
(...skipping 17 matching lines...) Expand all
162 global update_goldens 270 global update_goldens
163 update_goldens = True 271 update_goldens = True
164 for f in glob.glob(os.path.join(_TEST_DATA_DIR, '*.golden')): 272 for f in glob.glob(os.path.join(_TEST_DATA_DIR, '*.golden')):
165 os.unlink(f) 273 os.unlink(f)
166 274
167 unittest.main(argv=argv, verbosity=2) 275 unittest.main(argv=argv, verbosity=2)
168 276
169 277
170 if __name__ == '__main__': 278 if __name__ == '__main__':
171 main() 279 main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698