1# 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() 494