blob: 22f31d9f1a2aeea22f026dace8bb9376cf503a03 [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;
476 waggle;
477 waggle; # llndk=202404
478 bubble; # llndk=202404
479 duddle;
480 duddle; # llndk=202504
481 } VERSION_34;
482 """))
483 f = copy(self.filter)
484 f.llndk = True
485 f.api = 202404
486 parser = symbolfile.SymbolFileParser(input_file, {}, f)
487 versions = parser.parse()
488
489 src_file = io.StringIO()
490 version_file = io.StringIO()
491 symbol_list_file = io.StringIO()
492
493 generator = ndkstubgen.Generator(src_file,
494 version_file, symbol_list_file, f)
495 generator.write(versions)
496
497 expected_src = textwrap.dedent("""\
498 void foo() {}
499 void bar() {}
500 void waggle() {}
501 void bubble() {}
502 """)
503 self.assertEqual(expected_src, src_file.getvalue())
504
505 expected_version = textwrap.dedent("""\
506 VERSION_34 {
507 global:
508 foo;
509 bar;
510 };
511 VERSION_35 {
512 global:
513 waggle;
514 bubble;
515 } VERSION_34;
516 """)
517 self.assertEqual(expected_version, version_file.getvalue())
518
519 def test_integration_with_llndk_with_single_version_block(self) -> None:
520 input_file = io.StringIO(textwrap.dedent("""\
521 LIBANDROID {
522 global:
523 foo; # introduced=34
524 bar; # introduced=35
525 bar; # llndk=202404
526 baz; # introduced=35
527 };
528 """))
529 f = copy(self.filter)
530 f.llndk = True
531 f.api = 202404
532 parser = symbolfile.SymbolFileParser(input_file, {}, f)
533 versions = parser.parse()
534
535 src_file = io.StringIO()
536 version_file = io.StringIO()
537 symbol_list_file = io.StringIO()
538
539 generator = ndkstubgen.Generator(src_file,
540 version_file, symbol_list_file, f)
541 generator.write(versions)
542
543 expected_src = textwrap.dedent("""\
544 void foo() {}
545 void bar() {}
546 """)
547 self.assertEqual(expected_src, src_file.getvalue())
548
549 expected_version = textwrap.dedent("""\
550 LIBANDROID {
551 global:
552 foo;
553 bar;
554 };
555 """)
556 self.assertEqual(expected_version, version_file.getvalue())
557
Dan Albert08327ac2021-04-08 14:50:05 -0700558 def test_empty_stub(self) -> None:
559 """Tests that empty stubs can be generated.
560
561 This is not a common case, but libraries whose only behavior is to
562 interpose symbols to alter existing behavior do not need to expose
563 their interposing symbols as API, so it's possible for the stub to be
564 empty while still needing a stub to link against. libsigchain is an
565 example of this.
566 """
567 input_file = io.StringIO(textwrap.dedent("""\
568 VERSION_1 {
569 local:
570 *;
571 };
572 """))
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900573 f = copy(self.filter)
574 f.apex = True
575 parser = symbolfile.SymbolFileParser(input_file, {}, f)
Dan Albert08327ac2021-04-08 14:50:05 -0700576 versions = parser.parse()
577
578 src_file = io.StringIO()
579 version_file = io.StringIO()
580 symbol_list_file = io.StringIO()
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900581 f = copy(self.filter)
582 f.apex = True
Dan Albert08327ac2021-04-08 14:50:05 -0700583 generator = ndkstubgen.Generator(src_file,
584 version_file,
Jiyong Park3f9c41d2022-07-16 23:30:09 +0900585 symbol_list_file, f)
Dan Albert08327ac2021-04-08 14:50:05 -0700586 generator.write(versions)
587
588 self.assertEqual('', src_file.getvalue())
589 self.assertEqual('', version_file.getvalue())
590
Dan Albertaf7b36d2020-06-23 11:21:21 -0700591
592def main() -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700593 suite = unittest.TestLoader().loadTestsFromName(__name__)
594 unittest.TextTestRunner(verbosity=3).run(suite)
595
596
597if __name__ == '__main__':
598 main()