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