Add type information to symbolfile and ndkstubgen.
Test: mypy symbolfile Test: pytest Bug: None Change-Id: I6b1045d315e5a10e699d31de9fafc084d82768b2
This commit is contained in:
parent
8bd5095362
commit
af7b36dea5
6 changed files with 378 additions and 300 deletions
|
@ -20,13 +20,16 @@ import json
|
|||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import Iterable, TextIO
|
||||
|
||||
import symbolfile
|
||||
from symbolfile import Arch, Version
|
||||
|
||||
|
||||
class Generator:
|
||||
"""Output generator that writes stub source files and version scripts."""
|
||||
def __init__(self, src_file, version_script, arch, api, llndk, apex):
|
||||
def __init__(self, src_file: TextIO, version_script: TextIO, arch: Arch,
|
||||
api: int, llndk: bool, apex: bool) -> None:
|
||||
self.src_file = src_file
|
||||
self.version_script = version_script
|
||||
self.arch = arch
|
||||
|
@ -34,12 +37,12 @@ class Generator:
|
|||
self.llndk = llndk
|
||||
self.apex = apex
|
||||
|
||||
def write(self, versions):
|
||||
def write(self, versions: Iterable[Version]) -> None:
|
||||
"""Writes all symbol data to the output files."""
|
||||
for version in versions:
|
||||
self.write_version(version)
|
||||
|
||||
def write_version(self, version):
|
||||
def write_version(self, version: Version) -> None:
|
||||
"""Writes a single version block's data to the output files."""
|
||||
if symbolfile.should_omit_version(version, self.arch, self.api,
|
||||
self.llndk, self.apex):
|
||||
|
@ -84,7 +87,7 @@ class Generator:
|
|||
self.version_script.write('}' + base + ';\n')
|
||||
|
||||
|
||||
def parse_args():
|
||||
def parse_args() -> argparse.Namespace:
|
||||
"""Parses and returns command line arguments."""
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
|
@ -100,23 +103,31 @@ def parse_args():
|
|||
parser.add_argument(
|
||||
'--apex', action='store_true', help='Use the APEX variant.')
|
||||
|
||||
# https://github.com/python/mypy/issues/1317
|
||||
# mypy has issues with using os.path.realpath as an argument here.
|
||||
parser.add_argument(
|
||||
'--api-map', type=os.path.realpath, required=True,
|
||||
'--api-map',
|
||||
type=os.path.realpath, # type: ignore
|
||||
required=True,
|
||||
help='Path to the API level map JSON file.')
|
||||
|
||||
parser.add_argument(
|
||||
'symbol_file', type=os.path.realpath, help='Path to symbol file.')
|
||||
'symbol_file',
|
||||
type=os.path.realpath, # type: ignore
|
||||
help='Path to symbol file.')
|
||||
parser.add_argument(
|
||||
'stub_src', type=os.path.realpath,
|
||||
'stub_src',
|
||||
type=os.path.realpath, # type: ignore
|
||||
help='Path to output stub source file.')
|
||||
parser.add_argument(
|
||||
'version_script', type=os.path.realpath,
|
||||
'version_script',
|
||||
type=os.path.realpath, # type: ignore
|
||||
help='Path to output version script.')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
"""Program entry point."""
|
||||
args = parse_args()
|
||||
|
||||
|
|
2
cc/ndkstubgen/mypy.ini
Normal file
2
cc/ndkstubgen/mypy.ini
Normal file
|
@ -0,0 +1,2 @@
|
|||
[mypy]
|
||||
disallow_untyped_defs = True
|
|
@ -21,19 +21,20 @@ import unittest
|
|||
|
||||
import ndkstubgen
|
||||
import symbolfile
|
||||
from symbolfile import Arch, Tag
|
||||
|
||||
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
|
||||
class GeneratorTest(unittest.TestCase):
|
||||
def test_omit_version(self):
|
||||
def test_omit_version(self) -> None:
|
||||
# Thorough testing of the cases involved here is handled by
|
||||
# OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
|
||||
src_file = io.StringIO()
|
||||
version_file = io.StringIO()
|
||||
generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
|
||||
False, False)
|
||||
generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
|
||||
9, False, False)
|
||||
|
||||
version = symbolfile.Version('VERSION_PRIVATE', None, [], [
|
||||
symbolfile.Symbol('foo', []),
|
||||
|
@ -42,74 +43,75 @@ class GeneratorTest(unittest.TestCase):
|
|||
self.assertEqual('', src_file.getvalue())
|
||||
self.assertEqual('', version_file.getvalue())
|
||||
|
||||
version = symbolfile.Version('VERSION', None, ['x86'], [
|
||||
version = symbolfile.Version('VERSION', None, [Tag('x86')], [
|
||||
symbolfile.Symbol('foo', []),
|
||||
])
|
||||
generator.write_version(version)
|
||||
self.assertEqual('', src_file.getvalue())
|
||||
self.assertEqual('', version_file.getvalue())
|
||||
|
||||
version = symbolfile.Version('VERSION', None, ['introduced=14'], [
|
||||
version = symbolfile.Version('VERSION', None, [Tag('introduced=14')], [
|
||||
symbolfile.Symbol('foo', []),
|
||||
])
|
||||
generator.write_version(version)
|
||||
self.assertEqual('', src_file.getvalue())
|
||||
self.assertEqual('', version_file.getvalue())
|
||||
|
||||
def test_omit_symbol(self):
|
||||
def test_omit_symbol(self) -> None:
|
||||
# Thorough testing of the cases involved here is handled by
|
||||
# SymbolPresenceTest.
|
||||
src_file = io.StringIO()
|
||||
version_file = io.StringIO()
|
||||
generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
|
||||
False, False)
|
||||
generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
|
||||
9, False, False)
|
||||
|
||||
version = symbolfile.Version('VERSION_1', None, [], [
|
||||
symbolfile.Symbol('foo', ['x86']),
|
||||
symbolfile.Symbol('foo', [Tag('x86')]),
|
||||
])
|
||||
generator.write_version(version)
|
||||
self.assertEqual('', src_file.getvalue())
|
||||
self.assertEqual('', version_file.getvalue())
|
||||
|
||||
version = symbolfile.Version('VERSION_1', None, [], [
|
||||
symbolfile.Symbol('foo', ['introduced=14']),
|
||||
symbolfile.Symbol('foo', [Tag('introduced=14')]),
|
||||
])
|
||||
generator.write_version(version)
|
||||
self.assertEqual('', src_file.getvalue())
|
||||
self.assertEqual('', version_file.getvalue())
|
||||
|
||||
version = symbolfile.Version('VERSION_1', None, [], [
|
||||
symbolfile.Symbol('foo', ['llndk']),
|
||||
symbolfile.Symbol('foo', [Tag('llndk')]),
|
||||
])
|
||||
generator.write_version(version)
|
||||
self.assertEqual('', src_file.getvalue())
|
||||
self.assertEqual('', version_file.getvalue())
|
||||
|
||||
version = symbolfile.Version('VERSION_1', None, [], [
|
||||
symbolfile.Symbol('foo', ['apex']),
|
||||
symbolfile.Symbol('foo', [Tag('apex')]),
|
||||
])
|
||||
generator.write_version(version)
|
||||
self.assertEqual('', src_file.getvalue())
|
||||
self.assertEqual('', version_file.getvalue())
|
||||
|
||||
def test_write(self):
|
||||
def test_write(self) -> None:
|
||||
src_file = io.StringIO()
|
||||
version_file = io.StringIO()
|
||||
generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
|
||||
False, False)
|
||||
generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
|
||||
9, False, False)
|
||||
|
||||
versions = [
|
||||
symbolfile.Version('VERSION_1', None, [], [
|
||||
symbolfile.Symbol('foo', []),
|
||||
symbolfile.Symbol('bar', ['var']),
|
||||
symbolfile.Symbol('woodly', ['weak']),
|
||||
symbolfile.Symbol('doodly', ['weak', 'var']),
|
||||
symbolfile.Symbol('bar', [Tag('var')]),
|
||||
symbolfile.Symbol('woodly', [Tag('weak')]),
|
||||
symbolfile.Symbol('doodly',
|
||||
[Tag('weak'), Tag('var')]),
|
||||
]),
|
||||
symbolfile.Version('VERSION_2', 'VERSION_1', [], [
|
||||
symbolfile.Symbol('baz', []),
|
||||
]),
|
||||
symbolfile.Version('VERSION_3', 'VERSION_1', [], [
|
||||
symbolfile.Symbol('qux', ['versioned=14']),
|
||||
symbolfile.Symbol('qux', [Tag('versioned=14')]),
|
||||
]),
|
||||
]
|
||||
|
||||
|
@ -141,7 +143,7 @@ class GeneratorTest(unittest.TestCase):
|
|||
|
||||
|
||||
class IntegrationTest(unittest.TestCase):
|
||||
def test_integration(self):
|
||||
def test_integration(self) -> None:
|
||||
api_map = {
|
||||
'O': 9000,
|
||||
'P': 9001,
|
||||
|
@ -178,14 +180,14 @@ class IntegrationTest(unittest.TestCase):
|
|||
wobble;
|
||||
} VERSION_4;
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, api_map, 'arm', 9,
|
||||
False, False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
|
||||
9, False, False)
|
||||
versions = parser.parse()
|
||||
|
||||
src_file = io.StringIO()
|
||||
version_file = io.StringIO()
|
||||
generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
|
||||
False, False)
|
||||
generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
|
||||
9, False, False)
|
||||
generator.write(versions)
|
||||
|
||||
expected_src = textwrap.dedent("""\
|
||||
|
@ -213,7 +215,7 @@ class IntegrationTest(unittest.TestCase):
|
|||
""")
|
||||
self.assertEqual(expected_version, version_file.getvalue())
|
||||
|
||||
def test_integration_future_api(self):
|
||||
def test_integration_future_api(self) -> None:
|
||||
api_map = {
|
||||
'O': 9000,
|
||||
'P': 9001,
|
||||
|
@ -230,14 +232,14 @@ class IntegrationTest(unittest.TestCase):
|
|||
*;
|
||||
};
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, api_map, 'arm', 9001,
|
||||
False, False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
|
||||
9001, False, False)
|
||||
versions = parser.parse()
|
||||
|
||||
src_file = io.StringIO()
|
||||
version_file = io.StringIO()
|
||||
generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9001,
|
||||
False, False)
|
||||
generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
|
||||
9001, False, False)
|
||||
generator.write(versions)
|
||||
|
||||
expected_src = textwrap.dedent("""\
|
||||
|
@ -255,7 +257,7 @@ class IntegrationTest(unittest.TestCase):
|
|||
""")
|
||||
self.assertEqual(expected_version, version_file.getvalue())
|
||||
|
||||
def test_multiple_definition(self):
|
||||
def test_multiple_definition(self) -> None:
|
||||
input_file = io.StringIO(textwrap.dedent("""\
|
||||
VERSION_1 {
|
||||
global:
|
||||
|
@ -280,8 +282,8 @@ class IntegrationTest(unittest.TestCase):
|
|||
} VERSION_2;
|
||||
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False,
|
||||
False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
|
||||
False, False)
|
||||
|
||||
with self.assertRaises(
|
||||
symbolfile.MultiplyDefinedSymbolError) as ex_context:
|
||||
|
@ -289,7 +291,7 @@ class IntegrationTest(unittest.TestCase):
|
|||
self.assertEqual(['bar', 'foo'],
|
||||
ex_context.exception.multiply_defined_symbols)
|
||||
|
||||
def test_integration_with_apex(self):
|
||||
def test_integration_with_apex(self) -> None:
|
||||
api_map = {
|
||||
'O': 9000,
|
||||
'P': 9001,
|
||||
|
@ -328,14 +330,14 @@ class IntegrationTest(unittest.TestCase):
|
|||
wobble;
|
||||
} VERSION_4;
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, api_map, 'arm', 9,
|
||||
False, True)
|
||||
parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
|
||||
9, False, True)
|
||||
versions = parser.parse()
|
||||
|
||||
src_file = io.StringIO()
|
||||
version_file = io.StringIO()
|
||||
generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
|
||||
False, True)
|
||||
generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
|
||||
9, False, True)
|
||||
generator.write(versions)
|
||||
|
||||
expected_src = textwrap.dedent("""\
|
||||
|
@ -369,7 +371,8 @@ class IntegrationTest(unittest.TestCase):
|
|||
""")
|
||||
self.assertEqual(expected_version, version_file.getvalue())
|
||||
|
||||
def main():
|
||||
|
||||
def main() -> None:
|
||||
suite = unittest.TestLoader().loadTestsFromName(__name__)
|
||||
unittest.TextTestRunner(verbosity=3).run(suite)
|
||||
|
||||
|
|
|
@ -14,15 +14,31 @@
|
|||
# limitations under the License.
|
||||
#
|
||||
"""Parser for Android's version script information."""
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
import re
|
||||
from typing import (
|
||||
Dict,
|
||||
Iterable,
|
||||
List,
|
||||
Mapping,
|
||||
NewType,
|
||||
Optional,
|
||||
TextIO,
|
||||
Tuple,
|
||||
)
|
||||
|
||||
|
||||
ApiMap = Mapping[str, int]
|
||||
Arch = NewType('Arch', str)
|
||||
Tag = NewType('Tag', str)
|
||||
|
||||
|
||||
ALL_ARCHITECTURES = (
|
||||
'arm',
|
||||
'arm64',
|
||||
'x86',
|
||||
'x86_64',
|
||||
Arch('arm'),
|
||||
Arch('arm64'),
|
||||
Arch('x86'),
|
||||
Arch('x86_64'),
|
||||
)
|
||||
|
||||
|
||||
|
@ -30,18 +46,36 @@ ALL_ARCHITECTURES = (
|
|||
FUTURE_API_LEVEL = 10000
|
||||
|
||||
|
||||
def logger():
|
||||
def logger() -> logging.Logger:
|
||||
"""Return the main logger for this module."""
|
||||
return logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_tags(line):
|
||||
@dataclass
|
||||
class Symbol:
|
||||
"""A symbol definition from a symbol file."""
|
||||
|
||||
name: str
|
||||
tags: List[Tag]
|
||||
|
||||
|
||||
@dataclass
|
||||
class Version:
|
||||
"""A version block of a symbol file."""
|
||||
|
||||
name: str
|
||||
base: Optional[str]
|
||||
tags: List[Tag]
|
||||
symbols: List[Symbol]
|
||||
|
||||
|
||||
def get_tags(line: str) -> List[Tag]:
|
||||
"""Returns a list of all tags on this line."""
|
||||
_, _, all_tags = line.strip().partition('#')
|
||||
return [e for e in re.split(r'\s+', all_tags) if e.strip()]
|
||||
return [Tag(e) for e in re.split(r'\s+', all_tags) if e.strip()]
|
||||
|
||||
|
||||
def is_api_level_tag(tag):
|
||||
def is_api_level_tag(tag: Tag) -> bool:
|
||||
"""Returns true if this tag has an API level that may need decoding."""
|
||||
if tag.startswith('introduced='):
|
||||
return True
|
||||
|
@ -52,7 +86,7 @@ def is_api_level_tag(tag):
|
|||
return False
|
||||
|
||||
|
||||
def decode_api_level(api, api_map):
|
||||
def decode_api_level(api: str, api_map: ApiMap) -> int:
|
||||
"""Decodes the API level argument into the API level number.
|
||||
|
||||
For the average case, this just decodes the integer value from the string,
|
||||
|
@ -70,12 +104,13 @@ def decode_api_level(api, api_map):
|
|||
return api_map[api]
|
||||
|
||||
|
||||
def decode_api_level_tags(tags, api_map):
|
||||
def decode_api_level_tags(tags: Iterable[Tag], api_map: ApiMap) -> List[Tag]:
|
||||
"""Decodes API level code names in a list of tags.
|
||||
|
||||
Raises:
|
||||
ParseError: An unknown version name was found in a tag.
|
||||
"""
|
||||
decoded_tags = list(tags)
|
||||
for idx, tag in enumerate(tags):
|
||||
if not is_api_level_tag(tag):
|
||||
continue
|
||||
|
@ -83,13 +118,13 @@ def decode_api_level_tags(tags, api_map):
|
|||
|
||||
try:
|
||||
decoded = str(decode_api_level(value, api_map))
|
||||
tags[idx] = '='.join([name, decoded])
|
||||
decoded_tags[idx] = Tag('='.join([name, decoded]))
|
||||
except KeyError:
|
||||
raise ParseError('Unknown version name in tag: {}'.format(tag))
|
||||
return tags
|
||||
raise ParseError(f'Unknown version name in tag: {tag}')
|
||||
return decoded_tags
|
||||
|
||||
|
||||
def split_tag(tag):
|
||||
def split_tag(tag: Tag) -> Tuple[str, str]:
|
||||
"""Returns a key/value tuple of the tag.
|
||||
|
||||
Raises:
|
||||
|
@ -103,7 +138,7 @@ def split_tag(tag):
|
|||
return key, value
|
||||
|
||||
|
||||
def get_tag_value(tag):
|
||||
def get_tag_value(tag: Tag) -> str:
|
||||
"""Returns the value of a key/value tag.
|
||||
|
||||
Raises:
|
||||
|
@ -114,12 +149,13 @@ def get_tag_value(tag):
|
|||
return split_tag(tag)[1]
|
||||
|
||||
|
||||
def version_is_private(version):
|
||||
def version_is_private(version: str) -> bool:
|
||||
"""Returns True if the version name should be treated as private."""
|
||||
return version.endswith('_PRIVATE') or version.endswith('_PLATFORM')
|
||||
|
||||
|
||||
def should_omit_version(version, arch, api, llndk, apex):
|
||||
def should_omit_version(version: Version, arch: Arch, api: int, llndk: bool,
|
||||
apex: bool) -> bool:
|
||||
"""Returns True if the version section should be ommitted.
|
||||
|
||||
We want to omit any sections that do not have any symbols we'll have in the
|
||||
|
@ -145,7 +181,8 @@ def should_omit_version(version, arch, api, llndk, apex):
|
|||
return False
|
||||
|
||||
|
||||
def should_omit_symbol(symbol, arch, api, llndk, apex):
|
||||
def should_omit_symbol(symbol: Symbol, arch: Arch, api: int, llndk: bool,
|
||||
apex: bool) -> bool:
|
||||
"""Returns True if the symbol should be omitted."""
|
||||
no_llndk_no_apex = 'llndk' not in symbol.tags and 'apex' not in symbol.tags
|
||||
keep = no_llndk_no_apex or \
|
||||
|
@ -160,7 +197,7 @@ def should_omit_symbol(symbol, arch, api, llndk, apex):
|
|||
return False
|
||||
|
||||
|
||||
def symbol_in_arch(tags, arch):
|
||||
def symbol_in_arch(tags: Iterable[Tag], arch: Arch) -> bool:
|
||||
"""Returns true if the symbol is present for the given architecture."""
|
||||
has_arch_tags = False
|
||||
for tag in tags:
|
||||
|
@ -175,7 +212,7 @@ def symbol_in_arch(tags, arch):
|
|||
return not has_arch_tags
|
||||
|
||||
|
||||
def symbol_in_api(tags, arch, api):
|
||||
def symbol_in_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool:
|
||||
"""Returns true if the symbol is present for the given API level."""
|
||||
introduced_tag = None
|
||||
arch_specific = False
|
||||
|
@ -197,7 +234,7 @@ def symbol_in_api(tags, arch, api):
|
|||
return api >= int(get_tag_value(introduced_tag))
|
||||
|
||||
|
||||
def symbol_versioned_in_api(tags, api):
|
||||
def symbol_versioned_in_api(tags: Iterable[Tag], api: int) -> bool:
|
||||
"""Returns true if the symbol should be versioned for the given API.
|
||||
|
||||
This models the `versioned=API` tag. This should be a very uncommonly
|
||||
|
@ -223,68 +260,40 @@ class ParseError(RuntimeError):
|
|||
|
||||
class MultiplyDefinedSymbolError(RuntimeError):
|
||||
"""A symbol name was multiply defined."""
|
||||
def __init__(self, multiply_defined_symbols):
|
||||
super(MultiplyDefinedSymbolError, self).__init__(
|
||||
def __init__(self, multiply_defined_symbols: Iterable[str]) -> None:
|
||||
super().__init__(
|
||||
'Version script contains multiple definitions for: {}'.format(
|
||||
', '.join(multiply_defined_symbols)))
|
||||
self.multiply_defined_symbols = multiply_defined_symbols
|
||||
|
||||
|
||||
class Version:
|
||||
"""A version block of a symbol file."""
|
||||
def __init__(self, name, base, tags, symbols):
|
||||
self.name = name
|
||||
self.base = base
|
||||
self.tags = tags
|
||||
self.symbols = symbols
|
||||
|
||||
def __eq__(self, other):
|
||||
if self.name != other.name:
|
||||
return False
|
||||
if self.base != other.base:
|
||||
return False
|
||||
if self.tags != other.tags:
|
||||
return False
|
||||
if self.symbols != other.symbols:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Symbol:
|
||||
"""A symbol definition from a symbol file."""
|
||||
def __init__(self, name, tags):
|
||||
self.name = name
|
||||
self.tags = tags
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.name == other.name and set(self.tags) == set(other.tags)
|
||||
|
||||
|
||||
class SymbolFileParser:
|
||||
"""Parses NDK symbol files."""
|
||||
def __init__(self, input_file, api_map, arch, api, llndk, apex):
|
||||
def __init__(self, input_file: TextIO, api_map: ApiMap, arch: Arch,
|
||||
api: int, llndk: bool, apex: bool) -> None:
|
||||
self.input_file = input_file
|
||||
self.api_map = api_map
|
||||
self.arch = arch
|
||||
self.api = api
|
||||
self.llndk = llndk
|
||||
self.apex = apex
|
||||
self.current_line = None
|
||||
self.current_line: Optional[str] = None
|
||||
|
||||
def parse(self):
|
||||
def parse(self) -> List[Version]:
|
||||
"""Parses the symbol file and returns a list of Version objects."""
|
||||
versions = []
|
||||
while self.next_line() != '':
|
||||
assert self.current_line is not None
|
||||
if '{' in self.current_line:
|
||||
versions.append(self.parse_version())
|
||||
else:
|
||||
raise ParseError(
|
||||
'Unexpected contents at top level: ' + self.current_line)
|
||||
f'Unexpected contents at top level: {self.current_line}')
|
||||
|
||||
self.check_no_duplicate_symbols(versions)
|
||||
return versions
|
||||
|
||||
def check_no_duplicate_symbols(self, versions):
|
||||
def check_no_duplicate_symbols(self, versions: Iterable[Version]) -> None:
|
||||
"""Raises errors for multiply defined symbols.
|
||||
|
||||
This situation is the normal case when symbol versioning is actually
|
||||
|
@ -312,12 +321,13 @@ class SymbolFileParser:
|
|||
raise MultiplyDefinedSymbolError(
|
||||
sorted(list(multiply_defined_symbols)))
|
||||
|
||||
def parse_version(self):
|
||||
def parse_version(self) -> Version:
|
||||
"""Parses a single version section and returns a Version object."""
|
||||
assert self.current_line is not None
|
||||
name = self.current_line.split('{')[0].strip()
|
||||
tags = get_tags(self.current_line)
|
||||
tags = decode_api_level_tags(tags, self.api_map)
|
||||
symbols = []
|
||||
symbols: List[Symbol] = []
|
||||
global_scope = True
|
||||
cpp_symbols = False
|
||||
while self.next_line() != '':
|
||||
|
@ -333,9 +343,7 @@ class SymbolFileParser:
|
|||
cpp_symbols = False
|
||||
else:
|
||||
base = base.rstrip(';').rstrip()
|
||||
if base == '':
|
||||
base = None
|
||||
return Version(name, base, tags, symbols)
|
||||
return Version(name, base or None, tags, symbols)
|
||||
elif 'extern "C++" {' in self.current_line:
|
||||
cpp_symbols = True
|
||||
elif not cpp_symbols and ':' in self.current_line:
|
||||
|
@ -354,8 +362,9 @@ class SymbolFileParser:
|
|||
pass
|
||||
raise ParseError('Unexpected EOF in version block.')
|
||||
|
||||
def parse_symbol(self):
|
||||
def parse_symbol(self) -> Symbol:
|
||||
"""Parses a single symbol line and returns a Symbol object."""
|
||||
assert self.current_line is not None
|
||||
if ';' not in self.current_line:
|
||||
raise ParseError(
|
||||
'Expected ; to terminate symbol: ' + self.current_line)
|
||||
|
@ -368,7 +377,7 @@ class SymbolFileParser:
|
|||
tags = decode_api_level_tags(tags, self.api_map)
|
||||
return Symbol(name, tags)
|
||||
|
||||
def next_line(self):
|
||||
def next_line(self) -> str:
|
||||
"""Returns the next non-empty non-comment line.
|
||||
|
||||
A return value of '' indicates EOF.
|
||||
|
|
2
cc/symbolfile/mypy.ini
Normal file
2
cc/symbolfile/mypy.ini
Normal file
|
@ -0,0 +1,2 @@
|
|||
[mypy]
|
||||
disallow_untyped_defs = True
|
|
@ -19,12 +19,13 @@ import textwrap
|
|||
import unittest
|
||||
|
||||
import symbolfile
|
||||
from symbolfile import Arch, Tag
|
||||
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
|
||||
class DecodeApiLevelTest(unittest.TestCase):
|
||||
def test_decode_api_level(self):
|
||||
def test_decode_api_level(self) -> None:
|
||||
self.assertEqual(9, symbolfile.decode_api_level('9', {}))
|
||||
self.assertEqual(9000, symbolfile.decode_api_level('O', {'O': 9000}))
|
||||
|
||||
|
@ -33,70 +34,73 @@ class DecodeApiLevelTest(unittest.TestCase):
|
|||
|
||||
|
||||
class TagsTest(unittest.TestCase):
|
||||
def test_get_tags_no_tags(self):
|
||||
def test_get_tags_no_tags(self) -> None:
|
||||
self.assertEqual([], symbolfile.get_tags(''))
|
||||
self.assertEqual([], symbolfile.get_tags('foo bar baz'))
|
||||
|
||||
def test_get_tags(self):
|
||||
def test_get_tags(self) -> None:
|
||||
self.assertEqual(['foo', 'bar'], symbolfile.get_tags('# foo bar'))
|
||||
self.assertEqual(['bar', 'baz'], symbolfile.get_tags('foo # bar baz'))
|
||||
|
||||
def test_split_tag(self):
|
||||
self.assertTupleEqual(('foo', 'bar'), symbolfile.split_tag('foo=bar'))
|
||||
self.assertTupleEqual(('foo', 'bar=baz'), symbolfile.split_tag('foo=bar=baz'))
|
||||
def test_split_tag(self) -> None:
|
||||
self.assertTupleEqual(('foo', 'bar'),
|
||||
symbolfile.split_tag(Tag('foo=bar')))
|
||||
self.assertTupleEqual(('foo', 'bar=baz'),
|
||||
symbolfile.split_tag(Tag('foo=bar=baz')))
|
||||
with self.assertRaises(ValueError):
|
||||
symbolfile.split_tag('foo')
|
||||
symbolfile.split_tag(Tag('foo'))
|
||||
|
||||
def test_get_tag_value(self):
|
||||
self.assertEqual('bar', symbolfile.get_tag_value('foo=bar'))
|
||||
self.assertEqual('bar=baz', symbolfile.get_tag_value('foo=bar=baz'))
|
||||
def test_get_tag_value(self) -> None:
|
||||
self.assertEqual('bar', symbolfile.get_tag_value(Tag('foo=bar')))
|
||||
self.assertEqual('bar=baz',
|
||||
symbolfile.get_tag_value(Tag('foo=bar=baz')))
|
||||
with self.assertRaises(ValueError):
|
||||
symbolfile.get_tag_value('foo')
|
||||
symbolfile.get_tag_value(Tag('foo'))
|
||||
|
||||
def test_is_api_level_tag(self):
|
||||
self.assertTrue(symbolfile.is_api_level_tag('introduced=24'))
|
||||
self.assertTrue(symbolfile.is_api_level_tag('introduced-arm=24'))
|
||||
self.assertTrue(symbolfile.is_api_level_tag('versioned=24'))
|
||||
def test_is_api_level_tag(self) -> None:
|
||||
self.assertTrue(symbolfile.is_api_level_tag(Tag('introduced=24')))
|
||||
self.assertTrue(symbolfile.is_api_level_tag(Tag('introduced-arm=24')))
|
||||
self.assertTrue(symbolfile.is_api_level_tag(Tag('versioned=24')))
|
||||
|
||||
# Shouldn't try to process things that aren't a key/value tag.
|
||||
self.assertFalse(symbolfile.is_api_level_tag('arm'))
|
||||
self.assertFalse(symbolfile.is_api_level_tag('introduced'))
|
||||
self.assertFalse(symbolfile.is_api_level_tag('versioned'))
|
||||
self.assertFalse(symbolfile.is_api_level_tag(Tag('arm')))
|
||||
self.assertFalse(symbolfile.is_api_level_tag(Tag('introduced')))
|
||||
self.assertFalse(symbolfile.is_api_level_tag(Tag('versioned')))
|
||||
|
||||
# We don't support arch specific `versioned` tags.
|
||||
self.assertFalse(symbolfile.is_api_level_tag('versioned-arm=24'))
|
||||
self.assertFalse(symbolfile.is_api_level_tag(Tag('versioned-arm=24')))
|
||||
|
||||
def test_decode_api_level_tags(self):
|
||||
def test_decode_api_level_tags(self) -> None:
|
||||
api_map = {
|
||||
'O': 9000,
|
||||
'P': 9001,
|
||||
}
|
||||
|
||||
tags = [
|
||||
'introduced=9',
|
||||
'introduced-arm=14',
|
||||
'versioned=16',
|
||||
'arm',
|
||||
'introduced=O',
|
||||
'introduced=P',
|
||||
Tag('introduced=9'),
|
||||
Tag('introduced-arm=14'),
|
||||
Tag('versioned=16'),
|
||||
Tag('arm'),
|
||||
Tag('introduced=O'),
|
||||
Tag('introduced=P'),
|
||||
]
|
||||
expected_tags = [
|
||||
'introduced=9',
|
||||
'introduced-arm=14',
|
||||
'versioned=16',
|
||||
'arm',
|
||||
'introduced=9000',
|
||||
'introduced=9001',
|
||||
Tag('introduced=9'),
|
||||
Tag('introduced-arm=14'),
|
||||
Tag('versioned=16'),
|
||||
Tag('arm'),
|
||||
Tag('introduced=9000'),
|
||||
Tag('introduced=9001'),
|
||||
]
|
||||
self.assertListEqual(
|
||||
expected_tags, symbolfile.decode_api_level_tags(tags, api_map))
|
||||
|
||||
with self.assertRaises(symbolfile.ParseError):
|
||||
symbolfile.decode_api_level_tags(['introduced=O'], {})
|
||||
symbolfile.decode_api_level_tags([Tag('introduced=O')], {})
|
||||
|
||||
|
||||
class PrivateVersionTest(unittest.TestCase):
|
||||
def test_version_is_private(self):
|
||||
def test_version_is_private(self) -> None:
|
||||
self.assertFalse(symbolfile.version_is_private('foo'))
|
||||
self.assertFalse(symbolfile.version_is_private('PRIVATE'))
|
||||
self.assertFalse(symbolfile.version_is_private('PLATFORM'))
|
||||
|
@ -110,191 +114,227 @@ class PrivateVersionTest(unittest.TestCase):
|
|||
|
||||
|
||||
class SymbolPresenceTest(unittest.TestCase):
|
||||
def test_symbol_in_arch(self):
|
||||
self.assertTrue(symbolfile.symbol_in_arch([], 'arm'))
|
||||
self.assertTrue(symbolfile.symbol_in_arch(['arm'], 'arm'))
|
||||
def test_symbol_in_arch(self) -> None:
|
||||
self.assertTrue(symbolfile.symbol_in_arch([], Arch('arm')))
|
||||
self.assertTrue(symbolfile.symbol_in_arch([Tag('arm')], Arch('arm')))
|
||||
|
||||
self.assertFalse(symbolfile.symbol_in_arch(['x86'], 'arm'))
|
||||
self.assertFalse(symbolfile.symbol_in_arch([Tag('x86')], Arch('arm')))
|
||||
|
||||
def test_symbol_in_api(self):
|
||||
self.assertTrue(symbolfile.symbol_in_api([], 'arm', 9))
|
||||
self.assertTrue(symbolfile.symbol_in_api(['introduced=9'], 'arm', 9))
|
||||
self.assertTrue(symbolfile.symbol_in_api(['introduced=9'], 'arm', 14))
|
||||
self.assertTrue(symbolfile.symbol_in_api(['introduced-arm=9'], 'arm', 14))
|
||||
self.assertTrue(symbolfile.symbol_in_api(['introduced-arm=9'], 'arm', 14))
|
||||
self.assertTrue(symbolfile.symbol_in_api(['introduced-x86=14'], 'arm', 9))
|
||||
self.assertTrue(symbolfile.symbol_in_api(
|
||||
['introduced-arm=9', 'introduced-x86=21'], 'arm', 14))
|
||||
self.assertTrue(symbolfile.symbol_in_api(
|
||||
['introduced=9', 'introduced-x86=21'], 'arm', 14))
|
||||
self.assertTrue(symbolfile.symbol_in_api(
|
||||
['introduced=21', 'introduced-arm=9'], 'arm', 14))
|
||||
self.assertTrue(symbolfile.symbol_in_api(
|
||||
['future'], 'arm', symbolfile.FUTURE_API_LEVEL))
|
||||
def test_symbol_in_api(self) -> None:
|
||||
self.assertTrue(symbolfile.symbol_in_api([], Arch('arm'), 9))
|
||||
self.assertTrue(
|
||||
symbolfile.symbol_in_api([Tag('introduced=9')], Arch('arm'), 9))
|
||||
self.assertTrue(
|
||||
symbolfile.symbol_in_api([Tag('introduced=9')], Arch('arm'), 14))
|
||||
self.assertTrue(
|
||||
symbolfile.symbol_in_api([Tag('introduced-arm=9')], Arch('arm'),
|
||||
14))
|
||||
self.assertTrue(
|
||||
symbolfile.symbol_in_api([Tag('introduced-arm=9')], Arch('arm'),
|
||||
14))
|
||||
self.assertTrue(
|
||||
symbolfile.symbol_in_api([Tag('introduced-x86=14')], Arch('arm'),
|
||||
9))
|
||||
self.assertTrue(
|
||||
symbolfile.symbol_in_api(
|
||||
[Tag('introduced-arm=9'),
|
||||
Tag('introduced-x86=21')], Arch('arm'), 14))
|
||||
self.assertTrue(
|
||||
symbolfile.symbol_in_api(
|
||||
[Tag('introduced=9'),
|
||||
Tag('introduced-x86=21')], Arch('arm'), 14))
|
||||
self.assertTrue(
|
||||
symbolfile.symbol_in_api(
|
||||
[Tag('introduced=21'),
|
||||
Tag('introduced-arm=9')], Arch('arm'), 14))
|
||||
self.assertTrue(
|
||||
symbolfile.symbol_in_api([Tag('future')], Arch('arm'),
|
||||
symbolfile.FUTURE_API_LEVEL))
|
||||
|
||||
self.assertFalse(symbolfile.symbol_in_api(['introduced=14'], 'arm', 9))
|
||||
self.assertFalse(symbolfile.symbol_in_api(['introduced-arm=14'], 'arm', 9))
|
||||
self.assertFalse(symbolfile.symbol_in_api(['future'], 'arm', 9))
|
||||
self.assertFalse(symbolfile.symbol_in_api(
|
||||
['introduced=9', 'future'], 'arm', 14))
|
||||
self.assertFalse(symbolfile.symbol_in_api(
|
||||
['introduced-arm=9', 'future'], 'arm', 14))
|
||||
self.assertFalse(symbolfile.symbol_in_api(
|
||||
['introduced-arm=21', 'introduced-x86=9'], 'arm', 14))
|
||||
self.assertFalse(symbolfile.symbol_in_api(
|
||||
['introduced=9', 'introduced-arm=21'], 'arm', 14))
|
||||
self.assertFalse(symbolfile.symbol_in_api(
|
||||
['introduced=21', 'introduced-x86=9'], 'arm', 14))
|
||||
self.assertFalse(
|
||||
symbolfile.symbol_in_api([Tag('introduced=14')], Arch('arm'), 9))
|
||||
self.assertFalse(
|
||||
symbolfile.symbol_in_api([Tag('introduced-arm=14')], Arch('arm'),
|
||||
9))
|
||||
self.assertFalse(
|
||||
symbolfile.symbol_in_api([Tag('future')], Arch('arm'), 9))
|
||||
self.assertFalse(
|
||||
symbolfile.symbol_in_api(
|
||||
[Tag('introduced=9'), Tag('future')], Arch('arm'), 14))
|
||||
self.assertFalse(
|
||||
symbolfile.symbol_in_api([Tag('introduced-arm=9'),
|
||||
Tag('future')], Arch('arm'), 14))
|
||||
self.assertFalse(
|
||||
symbolfile.symbol_in_api(
|
||||
[Tag('introduced-arm=21'),
|
||||
Tag('introduced-x86=9')], Arch('arm'), 14))
|
||||
self.assertFalse(
|
||||
symbolfile.symbol_in_api(
|
||||
[Tag('introduced=9'),
|
||||
Tag('introduced-arm=21')], Arch('arm'), 14))
|
||||
self.assertFalse(
|
||||
symbolfile.symbol_in_api(
|
||||
[Tag('introduced=21'),
|
||||
Tag('introduced-x86=9')], Arch('arm'), 14))
|
||||
|
||||
# Interesting edge case: this symbol should be omitted from the
|
||||
# library, but this call should still return true because none of the
|
||||
# tags indiciate that it's not present in this API level.
|
||||
self.assertTrue(symbolfile.symbol_in_api(['x86'], 'arm', 9))
|
||||
self.assertTrue(symbolfile.symbol_in_api([Tag('x86')], Arch('arm'), 9))
|
||||
|
||||
def test_verioned_in_api(self):
|
||||
def test_verioned_in_api(self) -> None:
|
||||
self.assertTrue(symbolfile.symbol_versioned_in_api([], 9))
|
||||
self.assertTrue(symbolfile.symbol_versioned_in_api(['versioned=9'], 9))
|
||||
self.assertTrue(symbolfile.symbol_versioned_in_api(['versioned=9'], 14))
|
||||
self.assertTrue(
|
||||
symbolfile.symbol_versioned_in_api([Tag('versioned=9')], 9))
|
||||
self.assertTrue(
|
||||
symbolfile.symbol_versioned_in_api([Tag('versioned=9')], 14))
|
||||
|
||||
self.assertFalse(symbolfile.symbol_versioned_in_api(['versioned=14'], 9))
|
||||
self.assertFalse(
|
||||
symbolfile.symbol_versioned_in_api([Tag('versioned=14')], 9))
|
||||
|
||||
|
||||
class OmitVersionTest(unittest.TestCase):
|
||||
def test_omit_private(self):
|
||||
def test_omit_private(self) -> None:
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, [], []), 'arm', 9, False,
|
||||
symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
|
||||
False))
|
||||
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo_PRIVATE', None, [], []), 'arm', 9,
|
||||
False, False))
|
||||
symbolfile.Version('foo_PRIVATE', None, [], []), Arch('arm'),
|
||||
9, False, False))
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo_PLATFORM', None, [], []), 'arm', 9,
|
||||
False, False))
|
||||
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, ['platform-only'], []), 'arm',
|
||||
symbolfile.Version('foo_PLATFORM', None, [], []), Arch('arm'),
|
||||
9, False, False))
|
||||
|
||||
def test_omit_llndk(self):
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, ['llndk'], []), 'arm', 9,
|
||||
False, False))
|
||||
symbolfile.Version('foo', None, [Tag('platform-only')], []),
|
||||
Arch('arm'), 9, False, False))
|
||||
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, [], []), 'arm', 9, True,
|
||||
False))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, ['llndk'], []), 'arm', 9, True,
|
||||
False))
|
||||
|
||||
def test_omit_apex(self):
|
||||
def test_omit_llndk(self) -> None:
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, ['apex'], []), 'arm', 9, False,
|
||||
False))
|
||||
symbolfile.Version('foo', None, [Tag('llndk')], []),
|
||||
Arch('arm'), 9, False, False))
|
||||
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, [], []), 'arm', 9, False,
|
||||
symbolfile.Version('foo', None, [], []), Arch('arm'), 9, True,
|
||||
False))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, [Tag('llndk')], []),
|
||||
Arch('arm'), 9, True, False))
|
||||
|
||||
def test_omit_apex(self) -> None:
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, [Tag('apex')], []),
|
||||
Arch('arm'), 9, False, False))
|
||||
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
|
||||
True))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, ['apex'], []), 'arm', 9, False,
|
||||
True))
|
||||
symbolfile.Version('foo', None, [Tag('apex')], []),
|
||||
Arch('arm'), 9, False, True))
|
||||
|
||||
def test_omit_arch(self):
|
||||
def test_omit_arch(self) -> None:
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, [], []), 'arm', 9, False,
|
||||
symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
|
||||
False))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, ['arm'], []), 'arm', 9, False,
|
||||
False))
|
||||
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, ['x86'], []), 'arm', 9, False,
|
||||
False))
|
||||
|
||||
def test_omit_api(self):
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, [], []), 'arm', 9, False,
|
||||
False))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, ['introduced=9'], []), 'arm',
|
||||
symbolfile.Version('foo', None, [Tag('arm')], []), Arch('arm'),
|
||||
9, False, False))
|
||||
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, ['introduced=14'], []), 'arm',
|
||||
symbolfile.Version('foo', None, [Tag('x86')], []), Arch('arm'),
|
||||
9, False, False))
|
||||
|
||||
def test_omit_api(self) -> None:
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
|
||||
False))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, [Tag('introduced=9')], []),
|
||||
Arch('arm'), 9, False, False))
|
||||
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_version(
|
||||
symbolfile.Version('foo', None, [Tag('introduced=14')], []),
|
||||
Arch('arm'), 9, False, False))
|
||||
|
||||
|
||||
class OmitSymbolTest(unittest.TestCase):
|
||||
def test_omit_llndk(self):
|
||||
def test_omit_llndk(self) -> None:
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['llndk']),
|
||||
'arm', 9, False, False))
|
||||
symbolfile.should_omit_symbol(
|
||||
symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9,
|
||||
False, False))
|
||||
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
|
||||
9, True, False))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['llndk']),
|
||||
'arm', 9, True, False))
|
||||
|
||||
def test_omit_apex(self):
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['apex']),
|
||||
'arm', 9, False, False))
|
||||
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
|
||||
9, False, True))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['apex']),
|
||||
'arm', 9, False, True))
|
||||
|
||||
def test_omit_arch(self):
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
|
||||
9, False, False))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['arm']),
|
||||
'arm', 9, False, False))
|
||||
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['x86']),
|
||||
'arm', 9, False, False))
|
||||
|
||||
def test_omit_api(self):
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
|
||||
9, False, False))
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
|
||||
Arch('arm'), 9, True, False))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(
|
||||
symbolfile.Symbol('foo', ['introduced=9']), 'arm', 9, False,
|
||||
symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9, True,
|
||||
False))
|
||||
|
||||
def test_omit_apex(self) -> None:
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_symbol(
|
||||
symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False,
|
||||
False))
|
||||
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
|
||||
Arch('arm'), 9, False, True))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(
|
||||
symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False,
|
||||
True))
|
||||
|
||||
def test_omit_arch(self) -> None:
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
|
||||
Arch('arm'), 9, False, False))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(
|
||||
symbolfile.Symbol('foo', [Tag('arm')]), Arch('arm'), 9, False,
|
||||
False))
|
||||
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_symbol(
|
||||
symbolfile.Symbol('foo', ['introduced=14']), 'arm', 9, False,
|
||||
symbolfile.Symbol('foo', [Tag('x86')]), Arch('arm'), 9, False,
|
||||
False))
|
||||
|
||||
def test_omit_api(self) -> None:
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
|
||||
Arch('arm'), 9, False, False))
|
||||
self.assertFalse(
|
||||
symbolfile.should_omit_symbol(
|
||||
symbolfile.Symbol('foo', [Tag('introduced=9')]), Arch('arm'),
|
||||
9, False, False))
|
||||
|
||||
self.assertTrue(
|
||||
symbolfile.should_omit_symbol(
|
||||
symbolfile.Symbol('foo', [Tag('introduced=14')]), Arch('arm'),
|
||||
9, False, False))
|
||||
|
||||
|
||||
class SymbolFileParseTest(unittest.TestCase):
|
||||
def test_next_line(self):
|
||||
def test_next_line(self) -> None:
|
||||
input_file = io.StringIO(textwrap.dedent("""\
|
||||
foo
|
||||
|
||||
|
@ -302,10 +342,12 @@ class SymbolFileParseTest(unittest.TestCase):
|
|||
# baz
|
||||
qux
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
|
||||
False, False)
|
||||
self.assertIsNone(parser.current_line)
|
||||
|
||||
self.assertEqual('foo', parser.next_line().strip())
|
||||
assert parser.current_line is not None
|
||||
self.assertEqual('foo', parser.current_line.strip())
|
||||
|
||||
self.assertEqual('bar', parser.next_line().strip())
|
||||
|
@ -317,7 +359,7 @@ class SymbolFileParseTest(unittest.TestCase):
|
|||
self.assertEqual('', parser.next_line())
|
||||
self.assertEqual('', parser.current_line)
|
||||
|
||||
def test_parse_version(self):
|
||||
def test_parse_version(self) -> None:
|
||||
input_file = io.StringIO(textwrap.dedent("""\
|
||||
VERSION_1 { # foo bar
|
||||
baz;
|
||||
|
@ -327,7 +369,8 @@ class SymbolFileParseTest(unittest.TestCase):
|
|||
VERSION_2 {
|
||||
} VERSION_1; # asdf
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
|
||||
False, False)
|
||||
|
||||
parser.next_line()
|
||||
version = parser.parse_version()
|
||||
|
@ -337,7 +380,7 @@ class SymbolFileParseTest(unittest.TestCase):
|
|||
|
||||
expected_symbols = [
|
||||
symbolfile.Symbol('baz', []),
|
||||
symbolfile.Symbol('qux', ['woodly', 'doodly']),
|
||||
symbolfile.Symbol('qux', [Tag('woodly'), Tag('doodly')]),
|
||||
]
|
||||
self.assertEqual(expected_symbols, version.symbols)
|
||||
|
||||
|
@ -347,32 +390,35 @@ class SymbolFileParseTest(unittest.TestCase):
|
|||
self.assertEqual('VERSION_1', version.base)
|
||||
self.assertEqual([], version.tags)
|
||||
|
||||
def test_parse_version_eof(self):
|
||||
def test_parse_version_eof(self) -> None:
|
||||
input_file = io.StringIO(textwrap.dedent("""\
|
||||
VERSION_1 {
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
|
||||
False, False)
|
||||
parser.next_line()
|
||||
with self.assertRaises(symbolfile.ParseError):
|
||||
parser.parse_version()
|
||||
|
||||
def test_unknown_scope_label(self):
|
||||
def test_unknown_scope_label(self) -> None:
|
||||
input_file = io.StringIO(textwrap.dedent("""\
|
||||
VERSION_1 {
|
||||
foo:
|
||||
}
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
|
||||
False, False)
|
||||
parser.next_line()
|
||||
with self.assertRaises(symbolfile.ParseError):
|
||||
parser.parse_version()
|
||||
|
||||
def test_parse_symbol(self):
|
||||
def test_parse_symbol(self) -> None:
|
||||
input_file = io.StringIO(textwrap.dedent("""\
|
||||
foo;
|
||||
bar; # baz qux
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
|
||||
False, False)
|
||||
|
||||
parser.next_line()
|
||||
symbol = parser.parse_symbol()
|
||||
|
@ -384,48 +430,51 @@ class SymbolFileParseTest(unittest.TestCase):
|
|||
self.assertEqual('bar', symbol.name)
|
||||
self.assertEqual(['baz', 'qux'], symbol.tags)
|
||||
|
||||
def test_wildcard_symbol_global(self):
|
||||
def test_wildcard_symbol_global(self) -> None:
|
||||
input_file = io.StringIO(textwrap.dedent("""\
|
||||
VERSION_1 {
|
||||
*;
|
||||
};
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
|
||||
False, False)
|
||||
parser.next_line()
|
||||
with self.assertRaises(symbolfile.ParseError):
|
||||
parser.parse_version()
|
||||
|
||||
def test_wildcard_symbol_local(self):
|
||||
def test_wildcard_symbol_local(self) -> None:
|
||||
input_file = io.StringIO(textwrap.dedent("""\
|
||||
VERSION_1 {
|
||||
local:
|
||||
*;
|
||||
};
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
|
||||
False, False)
|
||||
parser.next_line()
|
||||
version = parser.parse_version()
|
||||
self.assertEqual([], version.symbols)
|
||||
|
||||
def test_missing_semicolon(self):
|
||||
def test_missing_semicolon(self) -> None:
|
||||
input_file = io.StringIO(textwrap.dedent("""\
|
||||
VERSION_1 {
|
||||
foo
|
||||
};
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
|
||||
False, False)
|
||||
parser.next_line()
|
||||
with self.assertRaises(symbolfile.ParseError):
|
||||
parser.parse_version()
|
||||
|
||||
def test_parse_fails_invalid_input(self):
|
||||
def test_parse_fails_invalid_input(self) -> None:
|
||||
with self.assertRaises(symbolfile.ParseError):
|
||||
input_file = io.StringIO('foo')
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16,
|
||||
False, False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'),
|
||||
16, False, False)
|
||||
parser.parse()
|
||||
|
||||
def test_parse(self):
|
||||
def test_parse(self) -> None:
|
||||
input_file = io.StringIO(textwrap.dedent("""\
|
||||
VERSION_1 {
|
||||
local:
|
||||
|
@ -443,23 +492,24 @@ class SymbolFileParseTest(unittest.TestCase):
|
|||
qwerty;
|
||||
} VERSION_1;
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
|
||||
False, False)
|
||||
versions = parser.parse()
|
||||
|
||||
expected = [
|
||||
symbolfile.Version('VERSION_1', None, [], [
|
||||
symbolfile.Symbol('foo', []),
|
||||
symbolfile.Symbol('bar', ['baz']),
|
||||
symbolfile.Symbol('bar', [Tag('baz')]),
|
||||
]),
|
||||
symbolfile.Version('VERSION_2', 'VERSION_1', ['wasd'], [
|
||||
symbolfile.Version('VERSION_2', 'VERSION_1', [Tag('wasd')], [
|
||||
symbolfile.Symbol('woodly', []),
|
||||
symbolfile.Symbol('doodly', ['asdf']),
|
||||
symbolfile.Symbol('doodly', [Tag('asdf')]),
|
||||
]),
|
||||
]
|
||||
|
||||
self.assertEqual(expected, versions)
|
||||
|
||||
def test_parse_llndk_apex_symbol(self):
|
||||
def test_parse_llndk_apex_symbol(self) -> None:
|
||||
input_file = io.StringIO(textwrap.dedent("""\
|
||||
VERSION_1 {
|
||||
foo;
|
||||
|
@ -468,7 +518,8 @@ class SymbolFileParseTest(unittest.TestCase):
|
|||
qux; # apex
|
||||
};
|
||||
"""))
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, True)
|
||||
parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
|
||||
False, True)
|
||||
|
||||
parser.next_line()
|
||||
version = parser.parse_version()
|
||||
|
@ -477,14 +528,14 @@ class SymbolFileParseTest(unittest.TestCase):
|
|||
|
||||
expected_symbols = [
|
||||
symbolfile.Symbol('foo', []),
|
||||
symbolfile.Symbol('bar', ['llndk']),
|
||||
symbolfile.Symbol('baz', ['llndk', 'apex']),
|
||||
symbolfile.Symbol('qux', ['apex']),
|
||||
symbolfile.Symbol('bar', [Tag('llndk')]),
|
||||
symbolfile.Symbol('baz', [Tag('llndk'), Tag('apex')]),
|
||||
symbolfile.Symbol('qux', [Tag('apex')]),
|
||||
]
|
||||
self.assertEqual(expected_symbols, version.symbols)
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
suite = unittest.TestLoader().loadTestsFromName(__name__)
|
||||
unittest.TextTestRunner(verbosity=3).run(suite)
|
||||
|
||||
|
|
Loading…
Reference in a new issue