• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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."""
18import cStringIO
19import textwrap
20import unittest
21
22import gen_stub_libs as gsl
23
24
25# pylint: disable=missing-docstring
26
27
28class 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
37class 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
100class 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
114class 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
164class 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
201class 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
367class 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            ]),
434            gsl.Version('VERSION_2', 'VERSION_1', [], [
435                gsl.Symbol('baz', []),
436            ]),
437            gsl.Version('VERSION_3', 'VERSION_1', [], [
438                gsl.Symbol('qux', ['versioned=14']),
439            ]),
440        ]
441
442        generator.write(versions)
443        expected_src = textwrap.dedent("""\
444            void foo() {}
445            int bar = 0;
446            void baz() {}
447            void qux() {}
448        """)
449        self.assertEqual(expected_src, src_file.getvalue())
450
451        expected_version = textwrap.dedent("""\
452            VERSION_1 {
453                global:
454                    foo;
455                    bar;
456            };
457            VERSION_2 {
458                global:
459                    baz;
460            } VERSION_1;
461        """)
462        self.assertEqual(expected_version, version_file.getvalue())
463
464
465class IntegrationTest(unittest.TestCase):
466    def test_integration(self):
467        api_map = {
468            'O': 9000,
469            'P': 9001,
470        }
471
472        input_file = cStringIO.StringIO(textwrap.dedent("""\
473            VERSION_1 {
474                global:
475                    foo; # var
476                    bar; # x86
477                    fizz; # introduced=O
478                    buzz; # introduced=P
479                local:
480                    *;
481            };
482
483            VERSION_2 { # arm
484                baz; # introduced=9
485                qux; # versioned=14
486            } VERSION_1;
487
488            VERSION_3 { # introduced=14
489                woodly;
490                doodly; # var
491            } VERSION_2;
492
493            VERSION_4 { # versioned=9
494                wibble;
495                wizzes; # vndk
496            } VERSION_2;
497
498            VERSION_5 { # versioned=14
499                wobble;
500            } VERSION_4;
501        """))
502        parser = gsl.SymbolFileParser(input_file, api_map)
503        versions = parser.parse()
504
505        src_file = cStringIO.StringIO()
506        version_file = cStringIO.StringIO()
507        generator = gsl.Generator(src_file, version_file, 'arm', 9, False)
508        generator.write(versions)
509
510        expected_src = textwrap.dedent("""\
511            int foo = 0;
512            void baz() {}
513            void qux() {}
514            void wibble() {}
515            void wobble() {}
516        """)
517        self.assertEqual(expected_src, src_file.getvalue())
518
519        expected_version = textwrap.dedent("""\
520            VERSION_1 {
521                global:
522                    foo;
523            };
524            VERSION_2 {
525                global:
526                    baz;
527            } VERSION_1;
528            VERSION_4 {
529                global:
530                    wibble;
531            } VERSION_2;
532        """)
533        self.assertEqual(expected_version, version_file.getvalue())
534
535    def test_integration_future_api(self):
536        api_map = {
537            'O': 9000,
538            'P': 9001,
539            'Q': 9002,
540        }
541
542        input_file = cStringIO.StringIO(textwrap.dedent("""\
543            VERSION_1 {
544                global:
545                    foo; # introduced=O
546                    bar; # introduced=P
547                    baz; # introduced=Q
548                local:
549                    *;
550            };
551        """))
552        parser = gsl.SymbolFileParser(input_file, api_map)
553        versions = parser.parse()
554
555        src_file = cStringIO.StringIO()
556        version_file = cStringIO.StringIO()
557        generator = gsl.Generator(src_file, version_file, 'arm', 9001, False)
558        generator.write(versions)
559
560        expected_src = textwrap.dedent("""\
561            void foo() {}
562            void bar() {}
563        """)
564        self.assertEqual(expected_src, src_file.getvalue())
565
566        expected_version = textwrap.dedent("""\
567            VERSION_1 {
568                global:
569                    foo;
570                    bar;
571            };
572        """)
573        self.assertEqual(expected_version, version_file.getvalue())
574
575
576def main():
577    suite = unittest.TestLoader().loadTestsFromName(__name__)
578    unittest.TextTestRunner(verbosity=3).run(suite)
579
580
581if __name__ == '__main__':
582    main()
583