blob: c91131feebcfd18f614a632d1f5c5fe67a77ca72 [file] [log] [blame]
Dan Albert06f58af2020-06-22 15:10:31 -07001#
2# Copyright (C) 2016 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16"""Tests for symbolfile."""
17import io
18import textwrap
19import unittest
20
21import symbolfile
22
23# pylint: disable=missing-docstring
24
25
26class DecodeApiLevelTest(unittest.TestCase):
27 def test_decode_api_level(self):
28 self.assertEqual(9, symbolfile.decode_api_level('9', {}))
29 self.assertEqual(9000, symbolfile.decode_api_level('O', {'O': 9000}))
30
31 with self.assertRaises(KeyError):
32 symbolfile.decode_api_level('O', {})
33
34
35class TagsTest(unittest.TestCase):
36 def test_get_tags_no_tags(self):
37 self.assertEqual([], symbolfile.get_tags(''))
38 self.assertEqual([], symbolfile.get_tags('foo bar baz'))
39
40 def test_get_tags(self):
41 self.assertEqual(['foo', 'bar'], symbolfile.get_tags('# foo bar'))
42 self.assertEqual(['bar', 'baz'], symbolfile.get_tags('foo # bar baz'))
43
44 def test_split_tag(self):
45 self.assertTupleEqual(('foo', 'bar'), symbolfile.split_tag('foo=bar'))
46 self.assertTupleEqual(('foo', 'bar=baz'), symbolfile.split_tag('foo=bar=baz'))
47 with self.assertRaises(ValueError):
48 symbolfile.split_tag('foo')
49
50 def test_get_tag_value(self):
51 self.assertEqual('bar', symbolfile.get_tag_value('foo=bar'))
52 self.assertEqual('bar=baz', symbolfile.get_tag_value('foo=bar=baz'))
53 with self.assertRaises(ValueError):
54 symbolfile.get_tag_value('foo')
55
56 def test_is_api_level_tag(self):
57 self.assertTrue(symbolfile.is_api_level_tag('introduced=24'))
58 self.assertTrue(symbolfile.is_api_level_tag('introduced-arm=24'))
59 self.assertTrue(symbolfile.is_api_level_tag('versioned=24'))
60
61 # Shouldn't try to process things that aren't a key/value tag.
62 self.assertFalse(symbolfile.is_api_level_tag('arm'))
63 self.assertFalse(symbolfile.is_api_level_tag('introduced'))
64 self.assertFalse(symbolfile.is_api_level_tag('versioned'))
65
66 # We don't support arch specific `versioned` tags.
67 self.assertFalse(symbolfile.is_api_level_tag('versioned-arm=24'))
68
69 def test_decode_api_level_tags(self):
70 api_map = {
71 'O': 9000,
72 'P': 9001,
73 }
74
75 tags = [
76 'introduced=9',
77 'introduced-arm=14',
78 'versioned=16',
79 'arm',
80 'introduced=O',
81 'introduced=P',
82 ]
83 expected_tags = [
84 'introduced=9',
85 'introduced-arm=14',
86 'versioned=16',
87 'arm',
88 'introduced=9000',
89 'introduced=9001',
90 ]
91 self.assertListEqual(
92 expected_tags, symbolfile.decode_api_level_tags(tags, api_map))
93
94 with self.assertRaises(symbolfile.ParseError):
95 symbolfile.decode_api_level_tags(['introduced=O'], {})
96
97
98class PrivateVersionTest(unittest.TestCase):
99 def test_version_is_private(self):
100 self.assertFalse(symbolfile.version_is_private('foo'))
101 self.assertFalse(symbolfile.version_is_private('PRIVATE'))
102 self.assertFalse(symbolfile.version_is_private('PLATFORM'))
103 self.assertFalse(symbolfile.version_is_private('foo_private'))
104 self.assertFalse(symbolfile.version_is_private('foo_platform'))
105 self.assertFalse(symbolfile.version_is_private('foo_PRIVATE_'))
106 self.assertFalse(symbolfile.version_is_private('foo_PLATFORM_'))
107
108 self.assertTrue(symbolfile.version_is_private('foo_PRIVATE'))
109 self.assertTrue(symbolfile.version_is_private('foo_PLATFORM'))
110
111
112class SymbolPresenceTest(unittest.TestCase):
113 def test_symbol_in_arch(self):
114 self.assertTrue(symbolfile.symbol_in_arch([], 'arm'))
115 self.assertTrue(symbolfile.symbol_in_arch(['arm'], 'arm'))
116
117 self.assertFalse(symbolfile.symbol_in_arch(['x86'], 'arm'))
118
119 def test_symbol_in_api(self):
120 self.assertTrue(symbolfile.symbol_in_api([], 'arm', 9))
121 self.assertTrue(symbolfile.symbol_in_api(['introduced=9'], 'arm', 9))
122 self.assertTrue(symbolfile.symbol_in_api(['introduced=9'], 'arm', 14))
123 self.assertTrue(symbolfile.symbol_in_api(['introduced-arm=9'], 'arm', 14))
124 self.assertTrue(symbolfile.symbol_in_api(['introduced-arm=9'], 'arm', 14))
125 self.assertTrue(symbolfile.symbol_in_api(['introduced-x86=14'], 'arm', 9))
126 self.assertTrue(symbolfile.symbol_in_api(
127 ['introduced-arm=9', 'introduced-x86=21'], 'arm', 14))
128 self.assertTrue(symbolfile.symbol_in_api(
129 ['introduced=9', 'introduced-x86=21'], 'arm', 14))
130 self.assertTrue(symbolfile.symbol_in_api(
131 ['introduced=21', 'introduced-arm=9'], 'arm', 14))
132 self.assertTrue(symbolfile.symbol_in_api(
133 ['future'], 'arm', symbolfile.FUTURE_API_LEVEL))
134
135 self.assertFalse(symbolfile.symbol_in_api(['introduced=14'], 'arm', 9))
136 self.assertFalse(symbolfile.symbol_in_api(['introduced-arm=14'], 'arm', 9))
137 self.assertFalse(symbolfile.symbol_in_api(['future'], 'arm', 9))
138 self.assertFalse(symbolfile.symbol_in_api(
139 ['introduced=9', 'future'], 'arm', 14))
140 self.assertFalse(symbolfile.symbol_in_api(
141 ['introduced-arm=9', 'future'], 'arm', 14))
142 self.assertFalse(symbolfile.symbol_in_api(
143 ['introduced-arm=21', 'introduced-x86=9'], 'arm', 14))
144 self.assertFalse(symbolfile.symbol_in_api(
145 ['introduced=9', 'introduced-arm=21'], 'arm', 14))
146 self.assertFalse(symbolfile.symbol_in_api(
147 ['introduced=21', 'introduced-x86=9'], 'arm', 14))
148
149 # Interesting edge case: this symbol should be omitted from the
150 # library, but this call should still return true because none of the
151 # tags indiciate that it's not present in this API level.
152 self.assertTrue(symbolfile.symbol_in_api(['x86'], 'arm', 9))
153
154 def test_verioned_in_api(self):
155 self.assertTrue(symbolfile.symbol_versioned_in_api([], 9))
156 self.assertTrue(symbolfile.symbol_versioned_in_api(['versioned=9'], 9))
157 self.assertTrue(symbolfile.symbol_versioned_in_api(['versioned=9'], 14))
158
159 self.assertFalse(symbolfile.symbol_versioned_in_api(['versioned=14'], 9))
160
161
162class OmitVersionTest(unittest.TestCase):
163 def test_omit_private(self):
164 self.assertFalse(
165 symbolfile.should_omit_version(
166 symbolfile.Version('foo', None, [], []), 'arm', 9, False,
167 False))
168
169 self.assertTrue(
170 symbolfile.should_omit_version(
171 symbolfile.Version('foo_PRIVATE', None, [], []), 'arm', 9,
172 False, False))
173 self.assertTrue(
174 symbolfile.should_omit_version(
175 symbolfile.Version('foo_PLATFORM', None, [], []), 'arm', 9,
176 False, False))
177
178 self.assertTrue(
179 symbolfile.should_omit_version(
180 symbolfile.Version('foo', None, ['platform-only'], []), 'arm',
181 9, False, False))
182
183 def test_omit_llndk(self):
184 self.assertTrue(
185 symbolfile.should_omit_version(
186 symbolfile.Version('foo', None, ['llndk'], []), 'arm', 9,
187 False, False))
188
189 self.assertFalse(
190 symbolfile.should_omit_version(
191 symbolfile.Version('foo', None, [], []), 'arm', 9, True,
192 False))
193 self.assertFalse(
194 symbolfile.should_omit_version(
195 symbolfile.Version('foo', None, ['llndk'], []), 'arm', 9, True,
196 False))
197
198 def test_omit_apex(self):
199 self.assertTrue(
200 symbolfile.should_omit_version(
201 symbolfile.Version('foo', None, ['apex'], []), 'arm', 9, False,
202 False))
203
204 self.assertFalse(
205 symbolfile.should_omit_version(
206 symbolfile.Version('foo', None, [], []), 'arm', 9, False,
207 True))
208 self.assertFalse(
209 symbolfile.should_omit_version(
210 symbolfile.Version('foo', None, ['apex'], []), 'arm', 9, False,
211 True))
212
213 def test_omit_arch(self):
214 self.assertFalse(
215 symbolfile.should_omit_version(
216 symbolfile.Version('foo', None, [], []), 'arm', 9, False,
217 False))
218 self.assertFalse(
219 symbolfile.should_omit_version(
220 symbolfile.Version('foo', None, ['arm'], []), 'arm', 9, False,
221 False))
222
223 self.assertTrue(
224 symbolfile.should_omit_version(
225 symbolfile.Version('foo', None, ['x86'], []), 'arm', 9, False,
226 False))
227
228 def test_omit_api(self):
229 self.assertFalse(
230 symbolfile.should_omit_version(
231 symbolfile.Version('foo', None, [], []), 'arm', 9, False,
232 False))
233 self.assertFalse(
234 symbolfile.should_omit_version(
235 symbolfile.Version('foo', None, ['introduced=9'], []), 'arm',
236 9, False, False))
237
238 self.assertTrue(
239 symbolfile.should_omit_version(
240 symbolfile.Version('foo', None, ['introduced=14'], []), 'arm',
241 9, False, False))
242
243
244class OmitSymbolTest(unittest.TestCase):
245 def test_omit_llndk(self):
246 self.assertTrue(
247 symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['llndk']),
248 'arm', 9, False, False))
249
250 self.assertFalse(
251 symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
252 9, True, False))
253 self.assertFalse(
254 symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['llndk']),
255 'arm', 9, True, False))
256
257 def test_omit_apex(self):
258 self.assertTrue(
259 symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['apex']),
260 'arm', 9, False, False))
261
262 self.assertFalse(
263 symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
264 9, False, True))
265 self.assertFalse(
266 symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['apex']),
267 'arm', 9, False, True))
268
269 def test_omit_arch(self):
270 self.assertFalse(
271 symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
272 9, False, False))
273 self.assertFalse(
274 symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['arm']),
275 'arm', 9, False, False))
276
277 self.assertTrue(
278 symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['x86']),
279 'arm', 9, False, False))
280
281 def test_omit_api(self):
282 self.assertFalse(
283 symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
284 9, False, False))
285 self.assertFalse(
286 symbolfile.should_omit_symbol(
287 symbolfile.Symbol('foo', ['introduced=9']), 'arm', 9, False,
288 False))
289
290 self.assertTrue(
291 symbolfile.should_omit_symbol(
292 symbolfile.Symbol('foo', ['introduced=14']), 'arm', 9, False,
293 False))
294
295
296class SymbolFileParseTest(unittest.TestCase):
297 def test_next_line(self):
298 input_file = io.StringIO(textwrap.dedent("""\
299 foo
300
301 bar
302 # baz
303 qux
304 """))
305 parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
306 self.assertIsNone(parser.current_line)
307
308 self.assertEqual('foo', parser.next_line().strip())
309 self.assertEqual('foo', parser.current_line.strip())
310
311 self.assertEqual('bar', parser.next_line().strip())
312 self.assertEqual('bar', parser.current_line.strip())
313
314 self.assertEqual('qux', parser.next_line().strip())
315 self.assertEqual('qux', parser.current_line.strip())
316
317 self.assertEqual('', parser.next_line())
318 self.assertEqual('', parser.current_line)
319
320 def test_parse_version(self):
321 input_file = io.StringIO(textwrap.dedent("""\
322 VERSION_1 { # foo bar
323 baz;
324 qux; # woodly doodly
325 };
326
327 VERSION_2 {
328 } VERSION_1; # asdf
329 """))
330 parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
331
332 parser.next_line()
333 version = parser.parse_version()
334 self.assertEqual('VERSION_1', version.name)
335 self.assertIsNone(version.base)
336 self.assertEqual(['foo', 'bar'], version.tags)
337
338 expected_symbols = [
339 symbolfile.Symbol('baz', []),
340 symbolfile.Symbol('qux', ['woodly', 'doodly']),
341 ]
342 self.assertEqual(expected_symbols, version.symbols)
343
344 parser.next_line()
345 version = parser.parse_version()
346 self.assertEqual('VERSION_2', version.name)
347 self.assertEqual('VERSION_1', version.base)
348 self.assertEqual([], version.tags)
349
350 def test_parse_version_eof(self):
351 input_file = io.StringIO(textwrap.dedent("""\
352 VERSION_1 {
353 """))
354 parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
355 parser.next_line()
356 with self.assertRaises(symbolfile.ParseError):
357 parser.parse_version()
358
359 def test_unknown_scope_label(self):
360 input_file = io.StringIO(textwrap.dedent("""\
361 VERSION_1 {
362 foo:
363 }
364 """))
365 parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
366 parser.next_line()
367 with self.assertRaises(symbolfile.ParseError):
368 parser.parse_version()
369
370 def test_parse_symbol(self):
371 input_file = io.StringIO(textwrap.dedent("""\
372 foo;
373 bar; # baz qux
374 """))
375 parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
376
377 parser.next_line()
378 symbol = parser.parse_symbol()
379 self.assertEqual('foo', symbol.name)
380 self.assertEqual([], symbol.tags)
381
382 parser.next_line()
383 symbol = parser.parse_symbol()
384 self.assertEqual('bar', symbol.name)
385 self.assertEqual(['baz', 'qux'], symbol.tags)
386
387 def test_wildcard_symbol_global(self):
388 input_file = io.StringIO(textwrap.dedent("""\
389 VERSION_1 {
390 *;
391 };
392 """))
393 parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
394 parser.next_line()
395 with self.assertRaises(symbolfile.ParseError):
396 parser.parse_version()
397
398 def test_wildcard_symbol_local(self):
399 input_file = io.StringIO(textwrap.dedent("""\
400 VERSION_1 {
401 local:
402 *;
403 };
404 """))
405 parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
406 parser.next_line()
407 version = parser.parse_version()
408 self.assertEqual([], version.symbols)
409
410 def test_missing_semicolon(self):
411 input_file = io.StringIO(textwrap.dedent("""\
412 VERSION_1 {
413 foo
414 };
415 """))
416 parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
417 parser.next_line()
418 with self.assertRaises(symbolfile.ParseError):
419 parser.parse_version()
420
421 def test_parse_fails_invalid_input(self):
422 with self.assertRaises(symbolfile.ParseError):
423 input_file = io.StringIO('foo')
424 parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16,
425 False, False)
426 parser.parse()
427
428 def test_parse(self):
429 input_file = io.StringIO(textwrap.dedent("""\
430 VERSION_1 {
431 local:
432 hidden1;
433 global:
434 foo;
435 bar; # baz
436 };
437
438 VERSION_2 { # wasd
439 # Implicit global scope.
440 woodly;
441 doodly; # asdf
442 local:
443 qwerty;
444 } VERSION_1;
445 """))
446 parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
447 versions = parser.parse()
448
449 expected = [
450 symbolfile.Version('VERSION_1', None, [], [
451 symbolfile.Symbol('foo', []),
452 symbolfile.Symbol('bar', ['baz']),
453 ]),
454 symbolfile.Version('VERSION_2', 'VERSION_1', ['wasd'], [
455 symbolfile.Symbol('woodly', []),
456 symbolfile.Symbol('doodly', ['asdf']),
457 ]),
458 ]
459
460 self.assertEqual(expected, versions)
461
462 def test_parse_llndk_apex_symbol(self):
463 input_file = io.StringIO(textwrap.dedent("""\
464 VERSION_1 {
465 foo;
466 bar; # llndk
467 baz; # llndk apex
468 qux; # apex
469 };
470 """))
471 parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, True)
472
473 parser.next_line()
474 version = parser.parse_version()
475 self.assertEqual('VERSION_1', version.name)
476 self.assertIsNone(version.base)
477
478 expected_symbols = [
479 symbolfile.Symbol('foo', []),
480 symbolfile.Symbol('bar', ['llndk']),
481 symbolfile.Symbol('baz', ['llndk', 'apex']),
482 symbolfile.Symbol('qux', ['apex']),
483 ]
484 self.assertEqual(expected_symbols, version.symbols)
485
486
487def main():
488 suite = unittest.TestLoader().loadTestsFromName(__name__)
489 unittest.TextTestRunner(verbosity=3).run(suite)
490
491
492if __name__ == '__main__':
493 main()