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 ndkstubgen.py."""
18import io
19import textwrap
20import unittest
21
22import ndkstubgen
23import symbolfile
24from symbolfile import Arch, Tag
25
26
27# pylint: disable=missing-docstring
28
29
30class GeneratorTest(unittest.TestCase):
31    def test_omit_version(self) -> None:
32        # Thorough testing of the cases involved here is handled by
33        # OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
34        src_file = io.StringIO()
35        version_file = io.StringIO()
36        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
37                                         9, False, False)
38
39        version = symbolfile.Version('VERSION_PRIVATE', None, [], [
40            symbolfile.Symbol('foo', []),
41        ])
42        generator.write_version(version)
43        self.assertEqual('', src_file.getvalue())
44        self.assertEqual('', version_file.getvalue())
45
46        version = symbolfile.Version('VERSION', None, [Tag('x86')], [
47            symbolfile.Symbol('foo', []),
48        ])
49        generator.write_version(version)
50        self.assertEqual('', src_file.getvalue())
51        self.assertEqual('', version_file.getvalue())
52
53        version = symbolfile.Version('VERSION', None, [Tag('introduced=14')], [
54            symbolfile.Symbol('foo', []),
55        ])
56        generator.write_version(version)
57        self.assertEqual('', src_file.getvalue())
58        self.assertEqual('', version_file.getvalue())
59
60    def test_omit_symbol(self) -> None:
61        # Thorough testing of the cases involved here is handled by
62        # SymbolPresenceTest.
63        src_file = io.StringIO()
64        version_file = io.StringIO()
65        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
66                                         9, False, False)
67
68        version = symbolfile.Version('VERSION_1', None, [], [
69            symbolfile.Symbol('foo', [Tag('x86')]),
70        ])
71        generator.write_version(version)
72        self.assertEqual('', src_file.getvalue())
73        self.assertEqual('', version_file.getvalue())
74
75        version = symbolfile.Version('VERSION_1', None, [], [
76            symbolfile.Symbol('foo', [Tag('introduced=14')]),
77        ])
78        generator.write_version(version)
79        self.assertEqual('', src_file.getvalue())
80        self.assertEqual('', version_file.getvalue())
81
82        version = symbolfile.Version('VERSION_1', None, [], [
83            symbolfile.Symbol('foo', [Tag('llndk')]),
84        ])
85        generator.write_version(version)
86        self.assertEqual('', src_file.getvalue())
87        self.assertEqual('', version_file.getvalue())
88
89        version = symbolfile.Version('VERSION_1', None, [], [
90            symbolfile.Symbol('foo', [Tag('apex')]),
91        ])
92        generator.write_version(version)
93        self.assertEqual('', src_file.getvalue())
94        self.assertEqual('', version_file.getvalue())
95
96    def test_write(self) -> None:
97        src_file = io.StringIO()
98        version_file = io.StringIO()
99        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
100                                         9, False, False)
101
102        versions = [
103            symbolfile.Version('VERSION_1', None, [], [
104                symbolfile.Symbol('foo', []),
105                symbolfile.Symbol('bar', [Tag('var')]),
106                symbolfile.Symbol('woodly', [Tag('weak')]),
107                symbolfile.Symbol('doodly',
108                                  [Tag('weak'), Tag('var')]),
109            ]),
110            symbolfile.Version('VERSION_2', 'VERSION_1', [], [
111                symbolfile.Symbol('baz', []),
112            ]),
113            symbolfile.Version('VERSION_3', 'VERSION_1', [], [
114                symbolfile.Symbol('qux', [Tag('versioned=14')]),
115            ]),
116        ]
117
118        generator.write(versions)
119        expected_src = textwrap.dedent("""\
120            void foo() {}
121            int bar = 0;
122            __attribute__((weak)) void woodly() {}
123            __attribute__((weak)) int doodly = 0;
124            void baz() {}
125            void qux() {}
126        """)
127        self.assertEqual(expected_src, src_file.getvalue())
128
129        expected_version = textwrap.dedent("""\
130            VERSION_1 {
131                global:
132                    foo;
133                    bar;
134                    woodly;
135                    doodly;
136            };
137            VERSION_2 {
138                global:
139                    baz;
140            } VERSION_1;
141        """)
142        self.assertEqual(expected_version, version_file.getvalue())
143
144
145class IntegrationTest(unittest.TestCase):
146    def test_integration(self) -> None:
147        api_map = {
148            'O': 9000,
149            'P': 9001,
150        }
151
152        input_file = io.StringIO(textwrap.dedent("""\
153            VERSION_1 {
154                global:
155                    foo; # var
156                    bar; # x86
157                    fizz; # introduced=O
158                    buzz; # introduced=P
159                local:
160                    *;
161            };
162
163            VERSION_2 { # arm
164                baz; # introduced=9
165                qux; # versioned=14
166            } VERSION_1;
167
168            VERSION_3 { # introduced=14
169                woodly;
170                doodly; # var
171            } VERSION_2;
172
173            VERSION_4 { # versioned=9
174                wibble;
175                wizzes; # llndk
176                waggle; # apex
177            } VERSION_2;
178
179            VERSION_5 { # versioned=14
180                wobble;
181            } VERSION_4;
182        """))
183        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
184                                             9, False, False)
185        versions = parser.parse()
186
187        src_file = io.StringIO()
188        version_file = io.StringIO()
189        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
190                                         9, False, False)
191        generator.write(versions)
192
193        expected_src = textwrap.dedent("""\
194            int foo = 0;
195            void baz() {}
196            void qux() {}
197            void wibble() {}
198            void wobble() {}
199        """)
200        self.assertEqual(expected_src, src_file.getvalue())
201
202        expected_version = textwrap.dedent("""\
203            VERSION_1 {
204                global:
205                    foo;
206            };
207            VERSION_2 {
208                global:
209                    baz;
210            } VERSION_1;
211            VERSION_4 {
212                global:
213                    wibble;
214            } VERSION_2;
215        """)
216        self.assertEqual(expected_version, version_file.getvalue())
217
218    def test_integration_future_api(self) -> None:
219        api_map = {
220            'O': 9000,
221            'P': 9001,
222            'Q': 9002,
223        }
224
225        input_file = io.StringIO(textwrap.dedent("""\
226            VERSION_1 {
227                global:
228                    foo; # introduced=O
229                    bar; # introduced=P
230                    baz; # introduced=Q
231                local:
232                    *;
233            };
234        """))
235        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
236                                             9001, False, False)
237        versions = parser.parse()
238
239        src_file = io.StringIO()
240        version_file = io.StringIO()
241        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
242                                         9001, False, False)
243        generator.write(versions)
244
245        expected_src = textwrap.dedent("""\
246            void foo() {}
247            void bar() {}
248        """)
249        self.assertEqual(expected_src, src_file.getvalue())
250
251        expected_version = textwrap.dedent("""\
252            VERSION_1 {
253                global:
254                    foo;
255                    bar;
256            };
257        """)
258        self.assertEqual(expected_version, version_file.getvalue())
259
260    def test_multiple_definition(self) -> None:
261        input_file = io.StringIO(textwrap.dedent("""\
262            VERSION_1 {
263                global:
264                    foo;
265                    foo;
266                    bar;
267                    baz;
268                    qux; # arm
269                local:
270                    *;
271            };
272
273            VERSION_2 {
274                global:
275                    bar;
276                    qux; # arm64
277            } VERSION_1;
278
279            VERSION_PRIVATE {
280                global:
281                    baz;
282            } VERSION_2;
283
284        """))
285        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
286                                             False, False)
287
288        with self.assertRaises(
289                symbolfile.MultiplyDefinedSymbolError) as ex_context:
290            parser.parse()
291        self.assertEqual(['bar', 'foo'],
292                         ex_context.exception.multiply_defined_symbols)
293
294    def test_integration_with_apex(self) -> None:
295        api_map = {
296            'O': 9000,
297            'P': 9001,
298        }
299
300        input_file = io.StringIO(textwrap.dedent("""\
301            VERSION_1 {
302                global:
303                    foo; # var
304                    bar; # x86
305                    fizz; # introduced=O
306                    buzz; # introduced=P
307                local:
308                    *;
309            };
310
311            VERSION_2 { # arm
312                baz; # introduced=9
313                qux; # versioned=14
314            } VERSION_1;
315
316            VERSION_3 { # introduced=14
317                woodly;
318                doodly; # var
319            } VERSION_2;
320
321            VERSION_4 { # versioned=9
322                wibble;
323                wizzes; # llndk
324                waggle; # apex
325                bubble; # apex llndk
326                duddle; # llndk apex
327            } VERSION_2;
328
329            VERSION_5 { # versioned=14
330                wobble;
331            } VERSION_4;
332        """))
333        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
334                                             9, False, True)
335        versions = parser.parse()
336
337        src_file = io.StringIO()
338        version_file = io.StringIO()
339        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
340                                         9, False, True)
341        generator.write(versions)
342
343        expected_src = textwrap.dedent("""\
344            int foo = 0;
345            void baz() {}
346            void qux() {}
347            void wibble() {}
348            void waggle() {}
349            void bubble() {}
350            void duddle() {}
351            void wobble() {}
352        """)
353        self.assertEqual(expected_src, src_file.getvalue())
354
355        expected_version = textwrap.dedent("""\
356            VERSION_1 {
357                global:
358                    foo;
359            };
360            VERSION_2 {
361                global:
362                    baz;
363            } VERSION_1;
364            VERSION_4 {
365                global:
366                    wibble;
367                    waggle;
368                    bubble;
369                    duddle;
370            } VERSION_2;
371        """)
372        self.assertEqual(expected_version, version_file.getvalue())
373
374
375def main() -> None:
376    suite = unittest.TestLoader().loadTestsFromName(__name__)
377    unittest.TextTestRunner(verbosity=3).run(suite)
378
379
380if __name__ == '__main__':
381    main()
382