blob: c8cd056a4ecfec46a90c8cd4dae8688d16115f19 [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
21
Dan Albert06f58af2020-06-22 15:10:31 -070022import symbolfile
Dan Albertead21552021-06-04 14:30:40 -070023from symbolfile import Arch, Tags
24
25import ndkstubgen
Dan Albert06f58af2020-06-22 15:10:31 -070026
27
28# pylint: disable=missing-docstring
29
30
31class GeneratorTest(unittest.TestCase):
Dan Albertaf7b36d2020-06-23 11:21:21 -070032 def test_omit_version(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -070033 # Thorough testing of the cases involved here is handled by
34 # OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
35 src_file = io.StringIO()
36 version_file = io.StringIO()
Dan Albertf1d14c72020-07-30 14:32:55 -070037 symbol_list_file = io.StringIO()
38 generator = ndkstubgen.Generator(src_file,
39 version_file, symbol_list_file,
40 Arch('arm'), 9, False, False)
Dan Albert06f58af2020-06-22 15:10:31 -070041
Dan Albertead21552021-06-04 14:30:40 -070042 version = symbolfile.Version('VERSION_PRIVATE', None, Tags(), [
43 symbolfile.Symbol('foo', Tags()),
Dan Albert06f58af2020-06-22 15:10:31 -070044 ])
45 generator.write_version(version)
46 self.assertEqual('', src_file.getvalue())
47 self.assertEqual('', version_file.getvalue())
48
Dan Albertead21552021-06-04 14:30:40 -070049 version = symbolfile.Version('VERSION', None, Tags.from_strs(['x86']),
50 [
51 symbolfile.Symbol('foo', Tags()),
52 ])
Dan Albert06f58af2020-06-22 15:10:31 -070053 generator.write_version(version)
54 self.assertEqual('', src_file.getvalue())
55 self.assertEqual('', version_file.getvalue())
56
Dan Albertead21552021-06-04 14:30:40 -070057 version = symbolfile.Version('VERSION', None,
58 Tags.from_strs(['introduced=14']), [
59 symbolfile.Symbol('foo', Tags()),
60 ])
Dan Albert06f58af2020-06-22 15:10:31 -070061 generator.write_version(version)
62 self.assertEqual('', src_file.getvalue())
63 self.assertEqual('', version_file.getvalue())
64
Dan Albertaf7b36d2020-06-23 11:21:21 -070065 def test_omit_symbol(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -070066 # Thorough testing of the cases involved here is handled by
67 # SymbolPresenceTest.
68 src_file = io.StringIO()
69 version_file = io.StringIO()
Dan Albertf1d14c72020-07-30 14:32:55 -070070 symbol_list_file = io.StringIO()
71 generator = ndkstubgen.Generator(src_file,
72 version_file, symbol_list_file,
73 Arch('arm'), 9, False, False)
Dan Albert06f58af2020-06-22 15:10:31 -070074
Dan Albertead21552021-06-04 14:30:40 -070075 version = symbolfile.Version('VERSION_1', None, Tags(), [
76 symbolfile.Symbol('foo', Tags.from_strs(['x86'])),
Dan Albert06f58af2020-06-22 15:10:31 -070077 ])
78 generator.write_version(version)
79 self.assertEqual('', src_file.getvalue())
80 self.assertEqual('', version_file.getvalue())
81
Dan Albertead21552021-06-04 14:30:40 -070082 version = symbolfile.Version('VERSION_1', None, Tags(), [
83 symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
Dan Albert06f58af2020-06-22 15:10:31 -070084 ])
85 generator.write_version(version)
86 self.assertEqual('', src_file.getvalue())
87 self.assertEqual('', version_file.getvalue())
88
Dan Albertead21552021-06-04 14:30:40 -070089 version = symbolfile.Version('VERSION_1', None, Tags(), [
90 symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
Dan Albert06f58af2020-06-22 15:10:31 -070091 ])
92 generator.write_version(version)
93 self.assertEqual('', src_file.getvalue())
94 self.assertEqual('', version_file.getvalue())
95
Dan Albertead21552021-06-04 14:30:40 -070096 version = symbolfile.Version('VERSION_1', None, Tags(), [
97 symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
Dan Albert06f58af2020-06-22 15:10:31 -070098 ])
99 generator.write_version(version)
100 self.assertEqual('', src_file.getvalue())
101 self.assertEqual('', version_file.getvalue())
102
Dan Albertaf7b36d2020-06-23 11:21:21 -0700103 def test_write(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700104 src_file = io.StringIO()
105 version_file = io.StringIO()
Dan Albertf1d14c72020-07-30 14:32:55 -0700106 symbol_list_file = io.StringIO()
107 generator = ndkstubgen.Generator(src_file,
108 version_file, symbol_list_file,
109 Arch('arm'), 9, False, False)
Dan Albert06f58af2020-06-22 15:10:31 -0700110
111 versions = [
Dan Albertead21552021-06-04 14:30:40 -0700112 symbolfile.Version('VERSION_1', None, Tags(), [
113 symbolfile.Symbol('foo', Tags()),
114 symbolfile.Symbol('bar', Tags.from_strs(['var'])),
115 symbolfile.Symbol('woodly', Tags.from_strs(['weak'])),
116 symbolfile.Symbol('doodly', Tags.from_strs(['weak', 'var'])),
Dan Albert06f58af2020-06-22 15:10:31 -0700117 ]),
Dan Albertead21552021-06-04 14:30:40 -0700118 symbolfile.Version('VERSION_2', 'VERSION_1', Tags(), [
119 symbolfile.Symbol('baz', Tags()),
Dan Albert06f58af2020-06-22 15:10:31 -0700120 ]),
Dan Albertead21552021-06-04 14:30:40 -0700121 symbolfile.Version('VERSION_3', 'VERSION_1', Tags(), [
122 symbolfile.Symbol('qux', Tags.from_strs(['versioned=14'])),
Dan Albert06f58af2020-06-22 15:10:31 -0700123 ]),
124 ]
125
126 generator.write(versions)
127 expected_src = textwrap.dedent("""\
128 void foo() {}
129 int bar = 0;
130 __attribute__((weak)) void woodly() {}
131 __attribute__((weak)) int doodly = 0;
132 void baz() {}
133 void qux() {}
134 """)
135 self.assertEqual(expected_src, src_file.getvalue())
136
137 expected_version = textwrap.dedent("""\
138 VERSION_1 {
139 global:
140 foo;
141 bar;
142 woodly;
143 doodly;
144 };
145 VERSION_2 {
146 global:
147 baz;
148 } VERSION_1;
149 """)
150 self.assertEqual(expected_version, version_file.getvalue())
151
Dan Albertf1d14c72020-07-30 14:32:55 -0700152 expected_allowlist = textwrap.dedent("""\
153 [abi_symbol_list]
154 foo
155 bar
156 woodly
157 doodly
158 baz
159 qux
160 """)
161 self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
162
Dan Albert06f58af2020-06-22 15:10:31 -0700163
164class IntegrationTest(unittest.TestCase):
Dan Albertaf7b36d2020-06-23 11:21:21 -0700165 def test_integration(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700166 api_map = {
167 'O': 9000,
168 'P': 9001,
169 }
170
171 input_file = io.StringIO(textwrap.dedent("""\
172 VERSION_1 {
173 global:
174 foo; # var
175 bar; # x86
176 fizz; # introduced=O
177 buzz; # introduced=P
178 local:
179 *;
180 };
181
182 VERSION_2 { # arm
183 baz; # introduced=9
184 qux; # versioned=14
185 } VERSION_1;
186
187 VERSION_3 { # introduced=14
188 woodly;
189 doodly; # var
190 } VERSION_2;
191
192 VERSION_4 { # versioned=9
193 wibble;
194 wizzes; # llndk
195 waggle; # apex
196 } VERSION_2;
197
198 VERSION_5 { # versioned=14
199 wobble;
200 } VERSION_4;
201 """))
Dan Albertaf7b36d2020-06-23 11:21:21 -0700202 parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
203 9, False, False)
Dan Albert06f58af2020-06-22 15:10:31 -0700204 versions = parser.parse()
205
206 src_file = io.StringIO()
207 version_file = io.StringIO()
Dan Albertf1d14c72020-07-30 14:32:55 -0700208 symbol_list_file = io.StringIO()
209 generator = ndkstubgen.Generator(src_file,
210 version_file, symbol_list_file,
211 Arch('arm'), 9, False, False)
Dan Albert06f58af2020-06-22 15:10:31 -0700212 generator.write(versions)
213
214 expected_src = textwrap.dedent("""\
215 int foo = 0;
216 void baz() {}
217 void qux() {}
218 void wibble() {}
219 void wobble() {}
220 """)
221 self.assertEqual(expected_src, src_file.getvalue())
222
223 expected_version = textwrap.dedent("""\
224 VERSION_1 {
225 global:
226 foo;
227 };
228 VERSION_2 {
229 global:
230 baz;
231 } VERSION_1;
232 VERSION_4 {
233 global:
234 wibble;
235 } VERSION_2;
236 """)
237 self.assertEqual(expected_version, version_file.getvalue())
238
Dan Albertf1d14c72020-07-30 14:32:55 -0700239 expected_allowlist = textwrap.dedent("""\
240 [abi_symbol_list]
241 foo
242 baz
243 qux
244 wibble
245 wobble
246 """)
247 self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
248
Dan Albertaf7b36d2020-06-23 11:21:21 -0700249 def test_integration_future_api(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700250 api_map = {
251 'O': 9000,
252 'P': 9001,
253 'Q': 9002,
254 }
255
256 input_file = io.StringIO(textwrap.dedent("""\
257 VERSION_1 {
258 global:
259 foo; # introduced=O
260 bar; # introduced=P
261 baz; # introduced=Q
262 local:
263 *;
264 };
265 """))
Dan Albertaf7b36d2020-06-23 11:21:21 -0700266 parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
267 9001, False, False)
Dan Albert06f58af2020-06-22 15:10:31 -0700268 versions = parser.parse()
269
270 src_file = io.StringIO()
271 version_file = io.StringIO()
Dan Albertf1d14c72020-07-30 14:32:55 -0700272 symbol_list_file = io.StringIO()
273 generator = ndkstubgen.Generator(src_file,
274 version_file, symbol_list_file,
275 Arch('arm'), 9001, False, False)
Dan Albert06f58af2020-06-22 15:10:31 -0700276 generator.write(versions)
277
278 expected_src = textwrap.dedent("""\
279 void foo() {}
280 void bar() {}
281 """)
282 self.assertEqual(expected_src, src_file.getvalue())
283
284 expected_version = textwrap.dedent("""\
285 VERSION_1 {
286 global:
287 foo;
288 bar;
289 };
290 """)
291 self.assertEqual(expected_version, version_file.getvalue())
292
Dan Albertf1d14c72020-07-30 14:32:55 -0700293 expected_allowlist = textwrap.dedent("""\
294 [abi_symbol_list]
295 foo
296 bar
297 """)
298 self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
299
Dan Albertaf7b36d2020-06-23 11:21:21 -0700300 def test_multiple_definition(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700301 input_file = io.StringIO(textwrap.dedent("""\
302 VERSION_1 {
303 global:
304 foo;
305 foo;
306 bar;
307 baz;
308 qux; # arm
309 local:
310 *;
311 };
312
313 VERSION_2 {
314 global:
315 bar;
316 qux; # arm64
317 } VERSION_1;
318
319 VERSION_PRIVATE {
320 global:
321 baz;
322 } VERSION_2;
323
324 """))
Dan Albertaf7b36d2020-06-23 11:21:21 -0700325 parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
326 False, False)
Dan Albert06f58af2020-06-22 15:10:31 -0700327
328 with self.assertRaises(
329 symbolfile.MultiplyDefinedSymbolError) as ex_context:
330 parser.parse()
331 self.assertEqual(['bar', 'foo'],
332 ex_context.exception.multiply_defined_symbols)
333
Dan Albertaf7b36d2020-06-23 11:21:21 -0700334 def test_integration_with_apex(self) -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700335 api_map = {
336 'O': 9000,
337 'P': 9001,
338 }
339
340 input_file = io.StringIO(textwrap.dedent("""\
341 VERSION_1 {
342 global:
343 foo; # var
344 bar; # x86
345 fizz; # introduced=O
346 buzz; # introduced=P
347 local:
348 *;
349 };
350
351 VERSION_2 { # arm
352 baz; # introduced=9
353 qux; # versioned=14
354 } VERSION_1;
355
356 VERSION_3 { # introduced=14
357 woodly;
358 doodly; # var
359 } VERSION_2;
360
361 VERSION_4 { # versioned=9
362 wibble;
363 wizzes; # llndk
364 waggle; # apex
365 bubble; # apex llndk
366 duddle; # llndk apex
367 } VERSION_2;
368
369 VERSION_5 { # versioned=14
370 wobble;
371 } VERSION_4;
372 """))
Dan Albertaf7b36d2020-06-23 11:21:21 -0700373 parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
374 9, False, True)
Dan Albert06f58af2020-06-22 15:10:31 -0700375 versions = parser.parse()
376
377 src_file = io.StringIO()
378 version_file = io.StringIO()
Dan Albertf1d14c72020-07-30 14:32:55 -0700379 symbol_list_file = io.StringIO()
380 generator = ndkstubgen.Generator(src_file,
381 version_file, symbol_list_file,
382 Arch('arm'), 9, False, True)
Dan Albert06f58af2020-06-22 15:10:31 -0700383 generator.write(versions)
384
385 expected_src = textwrap.dedent("""\
386 int foo = 0;
387 void baz() {}
388 void qux() {}
389 void wibble() {}
390 void waggle() {}
391 void bubble() {}
392 void duddle() {}
393 void wobble() {}
394 """)
395 self.assertEqual(expected_src, src_file.getvalue())
396
397 expected_version = textwrap.dedent("""\
398 VERSION_1 {
399 global:
400 foo;
401 };
402 VERSION_2 {
403 global:
404 baz;
405 } VERSION_1;
406 VERSION_4 {
407 global:
408 wibble;
409 waggle;
410 bubble;
411 duddle;
412 } VERSION_2;
413 """)
414 self.assertEqual(expected_version, version_file.getvalue())
415
Dan Albert08327ac2021-04-08 14:50:05 -0700416 def test_empty_stub(self) -> None:
417 """Tests that empty stubs can be generated.
418
419 This is not a common case, but libraries whose only behavior is to
420 interpose symbols to alter existing behavior do not need to expose
421 their interposing symbols as API, so it's possible for the stub to be
422 empty while still needing a stub to link against. libsigchain is an
423 example of this.
424 """
425 input_file = io.StringIO(textwrap.dedent("""\
426 VERSION_1 {
427 local:
428 *;
429 };
430 """))
431 parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'),
432 9, llndk=False, apex=True)
433 versions = parser.parse()
434
435 src_file = io.StringIO()
436 version_file = io.StringIO()
437 symbol_list_file = io.StringIO()
438 generator = ndkstubgen.Generator(src_file,
439 version_file,
440 symbol_list_file,
441 Arch('arm'),
442 9,
443 llndk=False,
444 apex=True)
445 generator.write(versions)
446
447 self.assertEqual('', src_file.getvalue())
448 self.assertEqual('', version_file.getvalue())
449
Dan Albertaf7b36d2020-06-23 11:21:21 -0700450
451def main() -> None:
Dan Albert06f58af2020-06-22 15:10:31 -0700452 suite = unittest.TestLoader().loadTestsFromName(__name__)
453 unittest.TextTestRunner(verbosity=3).run(suite)
454
455
456if __name__ == '__main__':
457 main()