OLD | NEW |
---|---|
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 Loading... | |
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() |
OLD | NEW |