blob: 6c24b4f19669cab39b28030f1a85cc6505a4a14a [file] [log] [blame]
Dan Albert06f58af2020-06-22 15:10:31 -07001#!/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
Jiyong Park3f9c41d2022-07-16 23:30:09 +090021from copy import copy
Dan Albert06f58af2020-06-22 15:10:31 -070022
Dan Albert06f58af2020-06-22 15:10:31 -070023import symbolfile
Dan Albertead21552021-06-04 14:30:40 -070024from symbolfile import Arch, Tags
25
26import ndkstubgen
Dan Albert06f58af2020-06-22 15:10:31 -070027
28
29# pylint: disable=missing-docstring
30
31
32class GeneratorTest(unittest.TestCase):
Jiyong Park3f9c41d2022-07-16 23:30:09 +090033 def setUp(self) -> None:
34 self.filter = symbolfile.Filter(Arch('arm'), 9, False, False)
35
Dan Albertaf7b36d2020-06-23 11:21:21 -070036 def test_omit_version(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -070037 # Thorough testing of the cases involved here is handled by
38 # OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
39 src_file = io.StringIO()
40 version_file = io.StringIO()
Dan Albertf1d14c72020-07-30 14:32:55 -070041 symbol_list_file = io.StringIO()
42 generator = ndkstubgen.Generator(src_file,
43 version_file, symbol_list_file,
Jiyong Park3f9c41d2022-07-16 23:30:09 +090044 self.filter)
Dan Albert06f58af2020-06-22 15:10:31 -070045
Dan Albertead21552021-06-04 14:30:40 -070046 version = symbolfile.Version('VERSION_PRIVATE', None, Tags(), [
47 symbolfile.Symbol('foo', Tags()),
Dan Albert06f58af2020-06-22 15:10:31 -070048 ])
49 generator.write_version(version)
50 self.assertEqual('', src_file.getvalue())
51 self.assertEqual('', version_file.getvalue())
52
Dan Albertead21552021-06-04 14:30:40 -070053 version = symbolfile.Version('VERSION', None, Tags.from_strs(['x86']),
54 [
55 symbolfile.Symbol('foo', Tags()),
56 ])
Dan Albert06f58af2020-06-22 15:10:31 -070057 generator.write_version(version)
58 self.assertEqual('', src_file.getvalue())
59 self.assertEqual('', version_file.getvalue())
60
Dan Albertead21552021-06-04 14:30:40 -070061 version = symbolfile.Version('VERSION', None,
62 Tags.from_strs(['introduced=14']), [
63 symbolfile.Symbol('foo', Tags()),
64 ])
Dan Albert06f58af2020-06-22 15:10:31 -070065 generator.write_version(version)
66 self.assertEqual('', src_file.getvalue())
67 self.assertEqual('', version_file.getvalue())
68
Dan Albertaf7b36d2020-06-23 11:21:21 -070069 def test_omit_symbol(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -070070 # Thorough testing of the cases involved here is handled by
71 # SymbolPresenceTest.
72 src_file = io.StringIO()
73 version_file = io.StringIO()
Dan Albertf1d14c72020-07-30 14:32:55 -070074 symbol_list_file = io.StringIO()
75 generator = ndkstubgen.Generator(src_file,
76 version_file, symbol_list_file,
Jiyong Park3f9c41d2022-07-16 23:30:09 +090077 self.filter)
Dan Albert06f58af2020-06-22 15:10:31 -070078
Dan Albertead21552021-06-04 14:30:40 -070079 version = symbolfile.Version('VERSION_1', None, Tags(), [
80 symbolfile.Symbol('foo', Tags.from_strs(['x86'])),
Dan Albert06f58af2020-06-22 15:10:31 -070081 ])
82 generator.write_version(version)
83 self.assertEqual('', src_file.getvalue())
84 self.assertEqual('', version_file.getvalue())
85
Dan Albertead21552021-06-04 14:30:40 -070086 version = symbolfile.Version('VERSION_1', None, Tags(), [
87 symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
Dan Albert06f58af2020-06-22 15:10:31 -070088 ])
89 generator.write_version(version)
90 self.assertEqual('', src_file.getvalue())
91 self.assertEqual('', version_file.getvalue())
92
Dan Albertead21552021-06-04 14:30:40 -070093 version = symbolfile.Version('VERSION_1', None, Tags(), [
94 symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
Dan Albert06f58af2020-06-22 15:10:31 -070095 ])
96 generator.write_version(version)
97 self.assertEqual('', src_file.getvalue())
98 self.assertEqual('', version_file.getvalue())
99
Dan Albertead21552021-06-04 14:30:40 -0700100 version = symbolfile.Version('VERSION_1', None, Tags(), [
101 symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
Dan Albert06f58af2020-06-22 15:10:31 -0700102 ])
103 generator.write_version(version)
104 self.assertEqual('', src_file.getvalue())
105 self.assertEqual('', version_file.getvalue())
106
Dan Albertaf7b36d2020-06-23 11:21:21 -0700107 def test_write(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700108 src_file = io.StringIO()
109 version_file = io.StringIO()
Dan Albertf1d14c72020-07-30 14:32:55 -0700110 symbol_list_file = io.StringIO()
111 generator = ndkstubgen.Generator(src_file,
112 version_file, symbol_list_file,
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900113 self.filter)
Dan Albert06f58af2020-06-22 15:10:31 -0700114
115 versions = [
Dan Albertead21552021-06-04 14:30:40 -0700116 symbolfile.Version('VERSION_1', None, Tags(), [
117 symbolfile.Symbol('foo', Tags()),
118 symbolfile.Symbol('bar', Tags.from_strs(['var'])),
119 symbolfile.Symbol('woodly', Tags.from_strs(['weak'])),
120 symbolfile.Symbol('doodly', Tags.from_strs(['weak', 'var'])),
Dan Albert06f58af2020-06-22 15:10:31 -0700121 ]),
Dan Albertead21552021-06-04 14:30:40 -0700122 symbolfile.Version('VERSION_2', 'VERSION_1', Tags(), [
123 symbolfile.Symbol('baz', Tags()),
Dan Albert06f58af2020-06-22 15:10:31 -0700124 ]),
Dan Albertead21552021-06-04 14:30:40 -0700125 symbolfile.Version('VERSION_3', 'VERSION_1', Tags(), [
126 symbolfile.Symbol('qux', Tags.from_strs(['versioned=14'])),
Dan Albert06f58af2020-06-22 15:10:31 -0700127 ]),
128 ]
129
130 generator.write(versions)
131 expected_src = textwrap.dedent("""\
132 void foo() {}
133 int bar = 0;
134 __attribute__((weak)) void woodly() {}
135 __attribute__((weak)) int doodly = 0;
136 void baz() {}
137 void qux() {}
138 """)
139 self.assertEqual(expected_src, src_file.getvalue())
140
141 expected_version = textwrap.dedent("""\
142 VERSION_1 {
143 global:
144 foo;
145 bar;
146 woodly;
147 doodly;
148 };
149 VERSION_2 {
150 global:
151 baz;
152 } VERSION_1;
153 """)
154 self.assertEqual(expected_version, version_file.getvalue())
155
Dan Albertf1d14c72020-07-30 14:32:55 -0700156 expected_allowlist = textwrap.dedent("""\
157 [abi_symbol_list]
158 foo
159 bar
160 woodly
161 doodly
162 baz
163 qux
164 """)
165 self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
166
Dan Albert06f58af2020-06-22 15:10:31 -0700167
168class IntegrationTest(unittest.TestCase):
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900169 def setUp(self) -> None:
170 self.filter = symbolfile.Filter(Arch('arm'), 9, False, False)
171
Dan Albertaf7b36d2020-06-23 11:21:21 -0700172 def test_integration(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700173 api_map = {
174 'O': 9000,
175 'P': 9001,
176 }
177
178 input_file = io.StringIO(textwrap.dedent("""\
179 VERSION_1 {
180 global:
181 foo; # var
182 bar; # x86
183 fizz; # introduced=O
184 buzz; # introduced=P
185 local:
186 *;
187 };
188
189 VERSION_2 { # arm
190 baz; # introduced=9
191 qux; # versioned=14
192 } VERSION_1;
193
194 VERSION_3 { # introduced=14
195 woodly;
196 doodly; # var
197 } VERSION_2;
198
199 VERSION_4 { # versioned=9
200 wibble;
201 wizzes; # llndk
202 waggle; # apex
203 } VERSION_2;
204
205 VERSION_5 { # versioned=14
206 wobble;
207 } VERSION_4;
208 """))
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900209 parser = symbolfile.SymbolFileParser(input_file, api_map, self.filter)
Dan Albert06f58af2020-06-22 15:10:31 -0700210 versions = parser.parse()
211
212 src_file = io.StringIO()
213 version_file = io.StringIO()
Dan Albertf1d14c72020-07-30 14:32:55 -0700214 symbol_list_file = io.StringIO()
215 generator = ndkstubgen.Generator(src_file,
216 version_file, symbol_list_file,
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900217 self.filter)
Dan Albert06f58af2020-06-22 15:10:31 -0700218 generator.write(versions)
219
220 expected_src = textwrap.dedent("""\
221 int foo = 0;
222 void baz() {}
223 void qux() {}
224 void wibble() {}
225 void wobble() {}
226 """)
227 self.assertEqual(expected_src, src_file.getvalue())
228
229 expected_version = textwrap.dedent("""\
230 VERSION_1 {
231 global:
232 foo;
233 };
234 VERSION_2 {
235 global:
236 baz;
237 } VERSION_1;
238 VERSION_4 {
239 global:
240 wibble;
241 } VERSION_2;
242 """)
243 self.assertEqual(expected_version, version_file.getvalue())
244
Dan Albertf1d14c72020-07-30 14:32:55 -0700245 expected_allowlist = textwrap.dedent("""\
246 [abi_symbol_list]
247 foo
248 baz
249 qux
250 wibble
251 wobble
252 """)
253 self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
254
Dan Albertaf7b36d2020-06-23 11:21:21 -0700255 def test_integration_future_api(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700256 api_map = {
257 'O': 9000,
258 'P': 9001,
259 'Q': 9002,
260 }
261
262 input_file = io.StringIO(textwrap.dedent("""\
263 VERSION_1 {
264 global:
265 foo; # introduced=O
266 bar; # introduced=P
267 baz; # introduced=Q
268 local:
269 *;
270 };
271 """))
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900272 f = copy(self.filter)
273 f.api = 9001
274 parser = symbolfile.SymbolFileParser(input_file, api_map, f)
Dan Albert06f58af2020-06-22 15:10:31 -0700275 versions = parser.parse()
276
277 src_file = io.StringIO()
278 version_file = io.StringIO()
Dan Albertf1d14c72020-07-30 14:32:55 -0700279 symbol_list_file = io.StringIO()
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900280 f = copy(self.filter)
281 f.api = 9001
Dan Albertf1d14c72020-07-30 14:32:55 -0700282 generator = ndkstubgen.Generator(src_file,
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900283 version_file, symbol_list_file, f)
Dan Albert06f58af2020-06-22 15:10:31 -0700284 generator.write(versions)
285
286 expected_src = textwrap.dedent("""\
287 void foo() {}
288 void bar() {}
289 """)
290 self.assertEqual(expected_src, src_file.getvalue())
291
292 expected_version = textwrap.dedent("""\
293 VERSION_1 {
294 global:
295 foo;
296 bar;
297 };
298 """)
299 self.assertEqual(expected_version, version_file.getvalue())
300
Dan Albertf1d14c72020-07-30 14:32:55 -0700301 expected_allowlist = textwrap.dedent("""\
302 [abi_symbol_list]
303 foo
304 bar
305 """)
306 self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
307
Dan Albertaf7b36d2020-06-23 11:21:21 -0700308 def test_multiple_definition(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700309 input_file = io.StringIO(textwrap.dedent("""\
310 VERSION_1 {
311 global:
312 foo;
313 foo;
314 bar;
315 baz;
316 qux; # arm
317 local:
318 *;
319 };
320
321 VERSION_2 {
322 global:
323 bar;
324 qux; # arm64
325 } VERSION_1;
326
327 VERSION_PRIVATE {
328 global:
329 baz;
330 } VERSION_2;
331
332 """))
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900333 f = copy(self.filter)
334 f.api = 16
335 parser = symbolfile.SymbolFileParser(input_file, {}, f)
Dan Albert06f58af2020-06-22 15:10:31 -0700336
337 with self.assertRaises(
338 symbolfile.MultiplyDefinedSymbolError) as ex_context:
339 parser.parse()
340 self.assertEqual(['bar', 'foo'],
341 ex_context.exception.multiply_defined_symbols)
342
Dan Albertaf7b36d2020-06-23 11:21:21 -0700343 def test_integration_with_apex(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700344 api_map = {
345 'O': 9000,
346 'P': 9001,
347 }
348
349 input_file = io.StringIO(textwrap.dedent("""\
350 VERSION_1 {
351 global:
352 foo; # var
353 bar; # x86
354 fizz; # introduced=O
355 buzz; # introduced=P
356 local:
357 *;
358 };
359
360 VERSION_2 { # arm
361 baz; # introduced=9
362 qux; # versioned=14
363 } VERSION_1;
364
365 VERSION_3 { # introduced=14
366 woodly;
367 doodly; # var
368 } VERSION_2;
369
370 VERSION_4 { # versioned=9
371 wibble;
372 wizzes; # llndk
373 waggle; # apex
374 bubble; # apex llndk
375 duddle; # llndk apex
376 } VERSION_2;
377
378 VERSION_5 { # versioned=14
379 wobble;
380 } VERSION_4;
381 """))
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900382 f = copy(self.filter)
383 f.apex = True
384 parser = symbolfile.SymbolFileParser(input_file, api_map, f)
Dan Albert06f58af2020-06-22 15:10:31 -0700385 versions = parser.parse()
386
387 src_file = io.StringIO()
388 version_file = io.StringIO()
Dan Albertf1d14c72020-07-30 14:32:55 -0700389 symbol_list_file = io.StringIO()
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900390 f = copy(self.filter)
391 f.apex = True
Dan Albertf1d14c72020-07-30 14:32:55 -0700392 generator = ndkstubgen.Generator(src_file,
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900393 version_file, symbol_list_file, f)
Dan Albert06f58af2020-06-22 15:10:31 -0700394 generator.write(versions)
395
396 expected_src = textwrap.dedent("""\
397 int foo = 0;
398 void baz() {}
399 void qux() {}
400 void wibble() {}
401 void waggle() {}
402 void bubble() {}
403 void duddle() {}
404 void wobble() {}
405 """)
406 self.assertEqual(expected_src, src_file.getvalue())
407
408 expected_version = textwrap.dedent("""\
409 VERSION_1 {
410 global:
411 foo;
412 };
413 VERSION_2 {
414 global:
415 baz;
416 } VERSION_1;
417 VERSION_4 {
418 global:
419 wibble;
420 waggle;
421 bubble;
422 duddle;
423 } VERSION_2;
424 """)
425 self.assertEqual(expected_version, version_file.getvalue())
426
Jiyong Park4ecbdb62022-09-26 20:58:27 +0900427 def test_integration_with_nondk(self) -> None:
428 input_file = io.StringIO(textwrap.dedent("""\
429 VERSION_1 {
430 global:
431 foo;
432 bar; # apex
433 local:
434 *;
435 };
436 """))
437 f = copy(self.filter)
438 f.apex = True
439 f.ndk = False # ndk symbols should be excluded
440 parser = symbolfile.SymbolFileParser(input_file, {}, f)
441 versions = parser.parse()
442
443 src_file = io.StringIO()
444 version_file = io.StringIO()
445 symbol_list_file = io.StringIO()
446 f = copy(self.filter)
447 f.apex = True
448 f.ndk = False # ndk symbols should be excluded
449 generator = ndkstubgen.Generator(src_file,
450 version_file, symbol_list_file, f)
451 generator.write(versions)
452
453 expected_src = textwrap.dedent("""\
454 void bar() {}
455 """)
456 self.assertEqual(expected_src, src_file.getvalue())
457
458 expected_version = textwrap.dedent("""\
459 VERSION_1 {
460 global:
461 bar;
462 };
463 """)
464 self.assertEqual(expected_version, version_file.getvalue())
465
Jooyung Han33eb6152024-03-11 15:46:48 +0900466 def test_integration_with_llndk(self) -> None:
467 input_file = io.StringIO(textwrap.dedent("""\
468 VERSION_34 { # introduced=34
469 global:
470 foo;
471 bar; # llndk
472 };
473 VERSION_35 { # introduced=35
474 global:
475 wiggle;
Justin Yun22abf212024-10-10 16:22:09 +0900476 waggle; # llndk
Jooyung Han33eb6152024-03-11 15:46:48 +0900477 } VERSION_34;
Justin Yun22abf212024-10-10 16:22:09 +0900478 VERSION_36 { # introduced=36
479 global:
480 abc;
481 xyz; # llndk
482 } VERSION_35;
Jooyung Han33eb6152024-03-11 15:46:48 +0900483 """))
484 f = copy(self.filter)
485 f.llndk = True
Justin Yun22abf212024-10-10 16:22:09 +0900486 f.api = 35
Jooyung Han33eb6152024-03-11 15:46:48 +0900487 parser = symbolfile.SymbolFileParser(input_file, {}, f)
488 versions = parser.parse()
489
490 src_file = io.StringIO()
491 version_file = io.StringIO()
492 symbol_list_file = io.StringIO()
493
494 generator = ndkstubgen.Generator(src_file,
495 version_file, symbol_list_file, f)
496 generator.write(versions)
497
498 expected_src = textwrap.dedent("""\
499 void foo() {}
500 void bar() {}
Justin Yun22abf212024-10-10 16:22:09 +0900501 void wiggle() {}
Jooyung Han33eb6152024-03-11 15:46:48 +0900502 void waggle() {}
Jooyung Han33eb6152024-03-11 15:46:48 +0900503 """)
504 self.assertEqual(expected_src, src_file.getvalue())
505
506 expected_version = textwrap.dedent("""\
507 VERSION_34 {
508 global:
509 foo;
510 bar;
511 };
512 VERSION_35 {
513 global:
Justin Yun22abf212024-10-10 16:22:09 +0900514 wiggle;
Jooyung Han33eb6152024-03-11 15:46:48 +0900515 waggle;
Jooyung Han33eb6152024-03-11 15:46:48 +0900516 } VERSION_34;
517 """)
518 self.assertEqual(expected_version, version_file.getvalue())
519
520 def test_integration_with_llndk_with_single_version_block(self) -> None:
521 input_file = io.StringIO(textwrap.dedent("""\
522 LIBANDROID {
523 global:
524 foo; # introduced=34
Justin Yun22abf212024-10-10 16:22:09 +0900525 bar; # introduced=35 llndk
526 baz; # introduced=V
527 qux; # introduced=36
Jooyung Han33eb6152024-03-11 15:46:48 +0900528 };
529 """))
530 f = copy(self.filter)
531 f.llndk = True
Justin Yun22abf212024-10-10 16:22:09 +0900532 f.api = 35
533 parser = symbolfile.SymbolFileParser(input_file, {'V': 35}, f)
Jooyung Han33eb6152024-03-11 15:46:48 +0900534 versions = parser.parse()
535
536 src_file = io.StringIO()
537 version_file = io.StringIO()
538 symbol_list_file = io.StringIO()
539
540 generator = ndkstubgen.Generator(src_file,
541 version_file, symbol_list_file, f)
542 generator.write(versions)
543
544 expected_src = textwrap.dedent("""\
545 void foo() {}
546 void bar() {}
Justin Yun22abf212024-10-10 16:22:09 +0900547 void baz() {}
Jooyung Han33eb6152024-03-11 15:46:48 +0900548 """)
549 self.assertEqual(expected_src, src_file.getvalue())
550
551 expected_version = textwrap.dedent("""\
552 LIBANDROID {
553 global:
554 foo;
555 bar;
Justin Yun22abf212024-10-10 16:22:09 +0900556 baz;
Jooyung Han33eb6152024-03-11 15:46:48 +0900557 };
558 """)
559 self.assertEqual(expected_version, version_file.getvalue())
560
Dan Albert08327ac2021-04-08 14:50:05 -0700561 def test_empty_stub(self) -> None:
562 """Tests that empty stubs can be generated.
563
564 This is not a common case, but libraries whose only behavior is to
565 interpose symbols to alter existing behavior do not need to expose
566 their interposing symbols as API, so it's possible for the stub to be
567 empty while still needing a stub to link against. libsigchain is an
568 example of this.
569 """
570 input_file = io.StringIO(textwrap.dedent("""\
571 VERSION_1 {
572 local:
573 *;
574 };
575 """))
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900576 f = copy(self.filter)
577 f.apex = True
578 parser = symbolfile.SymbolFileParser(input_file, {}, f)
Dan Albert08327ac2021-04-08 14:50:05 -0700579 versions = parser.parse()
580
581 src_file = io.StringIO()
582 version_file = io.StringIO()
583 symbol_list_file = io.StringIO()
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900584 f = copy(self.filter)
585 f.apex = True
Dan Albert08327ac2021-04-08 14:50:05 -0700586 generator = ndkstubgen.Generator(src_file,
587 version_file,
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900588 symbol_list_file, f)
Dan Albert08327ac2021-04-08 14:50:05 -0700589 generator.write(versions)
590
591 self.assertEqual('', src_file.getvalue())
592 self.assertEqual('', version_file.getvalue())
593
Dan Albertaf7b36d2020-06-23 11:21:21 -0700594
595def main() -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700596 suite = unittest.TestLoader().loadTestsFromName(__name__)
597 unittest.TextTestRunner(verbosity=3).run(suite)
598
599
600if __name__ == '__main__':
601 main()