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