diff options
author | Eric Snow <ericsnowcurrently@gmail.com> | 2020-10-23 00:42:51 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-23 00:42:51 (GMT) |
commit | 345cd37abe324ad4f60f80e2c3133b8849e54e9b (patch) | |
tree | 5d965e662dca9dcac19e7eddd63a3d9d0b816fed /Lib/test/test_tools | |
parent | ec388cfb4ede56dace2bb78851ff6f38fa2a6abe (diff) | |
download | cpython-345cd37abe324ad4f60f80e2c3133b8849e54e9b.zip cpython-345cd37abe324ad4f60f80e2c3133b8849e54e9b.tar.gz cpython-345cd37abe324ad4f60f80e2c3133b8849e54e9b.tar.bz2 |
bpo-36876: Fix the C analyzer tool. (GH-22841)
The original tool wasn't working right and it was simpler to create a new one, partially re-using some of the old code. At this point the tool runs properly on the master. (Try: ./python Tools/c-analyzer/c-analyzer.py analyze.) It take ~40 seconds on my machine to analyze the full CPython code base.
Note that we'll need to iron out some OS-specific stuff (e.g. preprocessor). We're okay though since this tool isn't used yet in our workflow. We will also need to verify the analysis results in detail before activating the check in CI, though I'm pretty sure it's close.
https://bugs.python.org/issue36876
Diffstat (limited to 'Lib/test/test_tools')
20 files changed, 0 insertions, 4314 deletions
diff --git a/Lib/test/test_tools/test_c_analyzer/__init__.py b/Lib/test/test_tools/test_c_analyzer/__init__.py deleted file mode 100644 index d0b4c04..0000000 --- a/Lib/test/test_tools/test_c_analyzer/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -import contextlib -import os.path -import test.test_tools -from test.support import load_package_tests - - -@contextlib.contextmanager -def tool_imports_for_tests(): - test.test_tools.skip_if_missing('c-analyzer') - with test.test_tools.imports_under_tool('c-analyzer'): - yield - - -def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tools/test_c_analyzer/__main__.py b/Lib/test/test_tools/test_c_analyzer/__main__.py deleted file mode 100644 index b5b017d..0000000 --- a/Lib/test/test_tools/test_c_analyzer/__main__.py +++ /dev/null @@ -1,5 +0,0 @@ -from . import load_tests -import unittest - - -unittest.main() diff --git a/Lib/test/test_tools/test_c_analyzer/test_common/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_common/__init__.py deleted file mode 100644 index bc502ef..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_common/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os.path -from test.support import load_package_tests - - -def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tools/test_c_analyzer/test_common/test_files.py b/Lib/test/test_tools/test_c_analyzer/test_common/test_files.py deleted file mode 100644 index 0c97d2a..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_common/test_files.py +++ /dev/null @@ -1,470 +0,0 @@ -import os.path -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.common.files import ( - iter_files, _walk_tree, glob_tree, - ) - - -def fixpath(filename): - return filename.replace('/', os.path.sep) - - -class IterFilesTests(unittest.TestCase): - - maxDiff = None - - _return_walk = None - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - - def set_files(self, *filesperroot): - roots = [] - result = [] - for root, files in filesperroot: - root = fixpath(root) - roots.append(root) - result.append([os.path.join(root, fixpath(f)) - for f in files]) - self._return_walk = result - return roots - - def _walk(self, root, *, suffix=None, walk=None): - self.calls.append(('_walk', (root, suffix, walk))) - return iter(self._return_walk.pop(0)) - - def _glob(self, root, *, suffix=None): - self.calls.append(('_glob', (root, suffix))) - return iter(self._return_walk.pop(0)) - - def test_typical(self): - dirnames = self.set_files( - ('spam', ['file1.c', 'file2.c']), - ('eggs', ['ham/file3.h']), - ) - suffixes = ('.c', '.h') - - files = list(iter_files(dirnames, suffixes, - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file2.c'), - fixpath('eggs/ham/file3.h'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', None, _walk_tree)), - ('_walk', ('eggs', None, _walk_tree)), - ]) - - def test_single_root(self): - self._return_walk = [ - [fixpath('spam/file1.c'), fixpath('spam/file2.c')], - ] - - files = list(iter_files('spam', '.c', - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file2.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', '.c', _walk_tree)), - ]) - - def test_one_root(self): - self._return_walk = [ - [fixpath('spam/file1.c'), fixpath('spam/file2.c')], - ] - - files = list(iter_files(['spam'], '.c', - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file2.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', '.c', _walk_tree)), - ]) - - def test_multiple_roots(self): - dirnames = self.set_files( - ('spam', ['file1.c', 'file2.c']), - ('eggs', ['ham/file3.c']), - ) - - files = list(iter_files(dirnames, '.c', - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file2.c'), - fixpath('eggs/ham/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', '.c', _walk_tree)), - ('_walk', ('eggs', '.c', _walk_tree)), - ]) - - def test_no_roots(self): - files = list(iter_files([], '.c', - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, []) - self.assertEqual(self.calls, []) - - def test_single_suffix(self): - self._return_walk = [ - [fixpath('spam/file1.c'), - fixpath('spam/eggs/file3.c'), - ], - ] - - files = list(iter_files('spam', '.c', - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/eggs/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', '.c', _walk_tree)), - ]) - - def test_one_suffix(self): - self._return_walk = [ - [fixpath('spam/file1.c'), - fixpath('spam/file1.h'), - fixpath('spam/file1.o'), - fixpath('spam/eggs/file3.c'), - ], - ] - - files = list(iter_files('spam', ['.c'], - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/eggs/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', None, _walk_tree)), - ]) - - def test_multiple_suffixes(self): - self._return_walk = [ - [fixpath('spam/file1.c'), - fixpath('spam/file1.h'), - fixpath('spam/file1.o'), - fixpath('spam/eggs/file3.c'), - ], - ] - - files = list(iter_files('spam', ('.c', '.h'), - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file1.h'), - fixpath('spam/eggs/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', None, _walk_tree)), - ]) - - def test_no_suffix(self): - expected = [fixpath('spam/file1.c'), - fixpath('spam/file1.h'), - fixpath('spam/file1.o'), - fixpath('spam/eggs/file3.c'), - ] - for suffix in (None, '', ()): - with self.subTest(suffix): - self.calls.clear() - self._return_walk = [list(expected)] - - files = list(iter_files('spam', suffix, - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, expected) - self.assertEqual(self.calls, [ - ('_walk', ('spam', suffix, _walk_tree)), - ]) - - def test_relparent(self): - dirnames = self.set_files( - ('/x/y/z/spam', ['file1.c', 'file2.c']), - ('/x/y/z/eggs', ['ham/file3.c']), - ) - - files = list(iter_files(dirnames, '.c', fixpath('/x/y'), - _glob=self._glob, - _walk=self._walk)) - - self.assertEqual(files, [ - fixpath('z/spam/file1.c'), - fixpath('z/spam/file2.c'), - fixpath('z/eggs/ham/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', (fixpath('/x/y/z/spam'), '.c', _walk_tree)), - ('_walk', (fixpath('/x/y/z/eggs'), '.c', _walk_tree)), - ]) - - def test_glob(self): - dirnames = self.set_files( - ('spam', ['file1.c', 'file2.c']), - ('eggs', ['ham/file3.c']), - ) - - files = list(iter_files(dirnames, '.c', - get_files=glob_tree, - _walk=self._walk, - _glob=self._glob)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file2.c'), - fixpath('eggs/ham/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_glob', ('spam', '.c')), - ('_glob', ('eggs', '.c')), - ]) - - - def test_alt_walk_func(self): - dirnames = self.set_files( - ('spam', ['file1.c', 'file2.c']), - ('eggs', ['ham/file3.c']), - ) - def get_files(root): - return None - - files = list(iter_files(dirnames, '.c', - get_files=get_files, - _walk=self._walk, - _glob=self._glob)) - - self.assertEqual(files, [ - fixpath('spam/file1.c'), - fixpath('spam/file2.c'), - fixpath('eggs/ham/file3.c'), - ]) - self.assertEqual(self.calls, [ - ('_walk', ('spam', '.c', get_files)), - ('_walk', ('eggs', '.c', get_files)), - ]) - - - - - - -# def test_no_dirnames(self): -# dirnames = [] -# filter_by_name = None -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, []) -# self.assertEqual(self.calls, []) -# -# def test_no_filter(self): -# self._return_walk = [ -# [('spam', (), ('file1', 'file2.c', 'file3.h', 'file4.o')), -# ], -# ] -# dirnames = [ -# 'spam', -# ] -# filter_by_name = None -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, [ -# fixpath('spam/file1'), -# fixpath('spam/file2.c'), -# fixpath('spam/file3.h'), -# fixpath('spam/file4.o'), -# ]) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ]) -# -# def test_no_files(self): -# self._return_walk = [ -# [('spam', (), ()), -# ], -# [(fixpath('eggs/ham'), (), ()), -# ], -# ] -# dirnames = [ -# 'spam', -# fixpath('eggs/ham'), -# ] -# filter_by_name = None -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, []) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ('_walk', (fixpath('eggs/ham'),)), -# ]) -# -# def test_tree(self): -# self._return_walk = [ -# [('spam', ('sub1', 'sub2', 'sub3'), ('file1',)), -# (fixpath('spam/sub1'), ('sub1sub1',), ('file2', 'file3')), -# (fixpath('spam/sub1/sub1sub1'), (), ('file4',)), -# (fixpath('spam/sub2'), (), ()), -# (fixpath('spam/sub3'), (), ('file5',)), -# ], -# [(fixpath('eggs/ham'), (), ('file6',)), -# ], -# ] -# dirnames = [ -# 'spam', -# fixpath('eggs/ham'), -# ] -# filter_by_name = None -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, [ -# fixpath('spam/file1'), -# fixpath('spam/sub1/file2'), -# fixpath('spam/sub1/file3'), -# fixpath('spam/sub1/sub1sub1/file4'), -# fixpath('spam/sub3/file5'), -# fixpath('eggs/ham/file6'), -# ]) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ('_walk', (fixpath('eggs/ham'),)), -# ]) -# -# def test_filter_suffixes(self): -# self._return_walk = [ -# [('spam', (), ('file1', 'file2.c', 'file3.h', 'file4.o')), -# ], -# ] -# dirnames = [ -# 'spam', -# ] -# filter_by_name = ('.c', '.h') -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, [ -# fixpath('spam/file2.c'), -# fixpath('spam/file3.h'), -# ]) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ]) -# -# def test_some_filtered(self): -# self._return_walk = [ -# [('spam', (), ('file1', 'file2', 'file3', 'file4')), -# ], -# ] -# dirnames = [ -# 'spam', -# ] -# def filter_by_name(filename, results=[False, True, False, True]): -# self.calls.append(('filter_by_name', (filename,))) -# return results.pop(0) -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, [ -# fixpath('spam/file2'), -# fixpath('spam/file4'), -# ]) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ('filter_by_name', ('file1',)), -# ('filter_by_name', ('file2',)), -# ('filter_by_name', ('file3',)), -# ('filter_by_name', ('file4',)), -# ]) -# -# def test_none_filtered(self): -# self._return_walk = [ -# [('spam', (), ('file1', 'file2', 'file3', 'file4')), -# ], -# ] -# dirnames = [ -# 'spam', -# ] -# def filter_by_name(filename, results=[True, True, True, True]): -# self.calls.append(('filter_by_name', (filename,))) -# return results.pop(0) -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, [ -# fixpath('spam/file1'), -# fixpath('spam/file2'), -# fixpath('spam/file3'), -# fixpath('spam/file4'), -# ]) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ('filter_by_name', ('file1',)), -# ('filter_by_name', ('file2',)), -# ('filter_by_name', ('file3',)), -# ('filter_by_name', ('file4',)), -# ]) -# -# def test_all_filtered(self): -# self._return_walk = [ -# [('spam', (), ('file1', 'file2', 'file3', 'file4')), -# ], -# ] -# dirnames = [ -# 'spam', -# ] -# def filter_by_name(filename, results=[False, False, False, False]): -# self.calls.append(('filter_by_name', (filename,))) -# return results.pop(0) -# -# files = list(iter_files(dirnames, filter_by_name, -# _walk=self._walk)) -# -# self.assertEqual(files, []) -# self.assertEqual(self.calls, [ -# ('_walk', ('spam',)), -# ('filter_by_name', ('file1',)), -# ('filter_by_name', ('file2',)), -# ('filter_by_name', ('file3',)), -# ('filter_by_name', ('file4',)), -# ]) diff --git a/Lib/test/test_tools/test_c_analyzer/test_common/test_info.py b/Lib/test/test_tools/test_c_analyzer/test_common/test_info.py deleted file mode 100644 index 69dbb58..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_common/test_info.py +++ /dev/null @@ -1,197 +0,0 @@ -import string -import unittest - -from ..util import PseudoStr, StrProxy, Object -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.common.info import ( - UNKNOWN, - ID, - ) - - -class IDTests(unittest.TestCase): - - VALID_ARGS = ( - 'x/y/z/spam.c', - 'func', - 'eggs', - ) - VALID_KWARGS = dict(zip(ID._fields, VALID_ARGS)) - VALID_EXPECTED = VALID_ARGS - - def test_from_raw(self): - tests = [ - ('', None), - (None, None), - ('spam', (None, None, 'spam')), - (('spam',), (None, None, 'spam')), - (('x/y/z/spam.c', 'spam'), ('x/y/z/spam.c', None, 'spam')), - (self.VALID_ARGS, self.VALID_EXPECTED), - (self.VALID_KWARGS, self.VALID_EXPECTED), - ] - for raw, expected in tests: - with self.subTest(raw): - id = ID.from_raw(raw) - - self.assertEqual(id, expected) - - def test_minimal(self): - id = ID( - filename=None, - funcname=None, - name='eggs', - ) - - self.assertEqual(id, ( - None, - None, - 'eggs', - )) - - def test_init_typical_global(self): - id = ID( - filename='x/y/z/spam.c', - funcname=None, - name='eggs', - ) - - self.assertEqual(id, ( - 'x/y/z/spam.c', - None, - 'eggs', - )) - - def test_init_typical_local(self): - id = ID( - filename='x/y/z/spam.c', - funcname='func', - name='eggs', - ) - - self.assertEqual(id, ( - 'x/y/z/spam.c', - 'func', - 'eggs', - )) - - def test_init_all_missing(self): - for value in ('', None): - with self.subTest(repr(value)): - id = ID( - filename=value, - funcname=value, - name=value, - ) - - self.assertEqual(id, ( - None, - None, - None, - )) - - def test_init_all_coerced(self): - tests = [ - ('str subclass', - dict( - filename=PseudoStr('x/y/z/spam.c'), - funcname=PseudoStr('func'), - name=PseudoStr('eggs'), - ), - ('x/y/z/spam.c', - 'func', - 'eggs', - )), - ('non-str', - dict( - filename=StrProxy('x/y/z/spam.c'), - funcname=Object(), - name=('a', 'b', 'c'), - ), - ('x/y/z/spam.c', - '<object>', - "('a', 'b', 'c')", - )), - ] - for summary, kwargs, expected in tests: - with self.subTest(summary): - id = ID(**kwargs) - - for field in ID._fields: - value = getattr(id, field) - self.assertIs(type(value), str) - self.assertEqual(tuple(id), expected) - - def test_iterable(self): - id = ID(**self.VALID_KWARGS) - - filename, funcname, name = id - - values = (filename, funcname, name) - for value, expected in zip(values, self.VALID_EXPECTED): - self.assertEqual(value, expected) - - def test_fields(self): - id = ID('a', 'b', 'z') - - self.assertEqual(id.filename, 'a') - self.assertEqual(id.funcname, 'b') - self.assertEqual(id.name, 'z') - - def test_validate_typical(self): - id = ID( - filename='x/y/z/spam.c', - funcname='func', - name='eggs', - ) - - id.validate() # This does not fail. - - def test_validate_missing_field(self): - for field in ID._fields: - with self.subTest(field): - id = ID(**self.VALID_KWARGS) - id = id._replace(**{field: None}) - - if field == 'funcname': - id.validate() # The field can be missing (not set). - id = id._replace(filename=None) - id.validate() # Both fields can be missing (not set). - continue - - with self.assertRaises(TypeError): - id.validate() - - def test_validate_bad_field(self): - badch = tuple(c for c in string.punctuation + string.digits) - notnames = ( - '1a', - 'a.b', - 'a-b', - '&a', - 'a++', - ) + badch - tests = [ - ('filename', ()), # Any non-empty str is okay. - ('funcname', notnames), - ('name', notnames), - ] - seen = set() - for field, invalid in tests: - for value in invalid: - seen.add(value) - with self.subTest(f'{field}={value!r}'): - id = ID(**self.VALID_KWARGS) - id = id._replace(**{field: value}) - - with self.assertRaises(ValueError): - id.validate() - - for field, invalid in tests: - valid = seen - set(invalid) - for value in valid: - with self.subTest(f'{field}={value!r}'): - id = ID(**self.VALID_KWARGS) - id = id._replace(**{field: value}) - - id.validate() # This does not fail. diff --git a/Lib/test/test_tools/test_c_analyzer/test_common/test_show.py b/Lib/test/test_tools/test_c_analyzer/test_common/test_show.py deleted file mode 100644 index 91ca2f3..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_common/test_show.py +++ /dev/null @@ -1,54 +0,0 @@ -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.variables import info - from c_analyzer.common.show import ( - basic, - ) - - -TYPICAL = [ - info.Variable.from_parts('src1/spam.c', None, 'var1', 'static const char *'), - info.Variable.from_parts('src1/spam.c', 'ham', 'initialized', 'static int'), - info.Variable.from_parts('src1/spam.c', None, 'var2', 'static PyObject *'), - info.Variable.from_parts('src1/eggs.c', 'tofu', 'ready', 'static int'), - info.Variable.from_parts('src1/spam.c', None, 'freelist', 'static (PyTupleObject *)[10]'), - info.Variable.from_parts('src1/sub/ham.c', None, 'var1', 'static const char const *'), - info.Variable.from_parts('src2/jam.c', None, 'var1', 'static int'), - info.Variable.from_parts('src2/jam.c', None, 'var2', 'static MyObject *'), - info.Variable.from_parts('Include/spam.h', None, 'data', 'static const int'), - ] - - -class BasicTests(unittest.TestCase): - - maxDiff = None - - def setUp(self): - self.lines = [] - - def print(self, line): - self.lines.append(line) - - def test_typical(self): - basic(TYPICAL, - _print=self.print) - - self.assertEqual(self.lines, [ - 'src1/spam.c:var1 static const char *', - 'src1/spam.c:ham():initialized static int', - 'src1/spam.c:var2 static PyObject *', - 'src1/eggs.c:tofu():ready static int', - 'src1/spam.c:freelist static (PyTupleObject *)[10]', - 'src1/sub/ham.c:var1 static const char const *', - 'src2/jam.c:var1 static int', - 'src2/jam.c:var2 static MyObject *', - 'Include/spam.h:data static const int', - ]) - - def test_no_rows(self): - basic([], - _print=self.print) - - self.assertEqual(self.lines, []) diff --git a/Lib/test/test_tools/test_c_analyzer/test_cpython/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_cpython/__init__.py deleted file mode 100644 index bc502ef..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_cpython/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os.path -from test.support import load_package_tests - - -def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tools/test_c_analyzer/test_cpython/test___main__.py b/Lib/test/test_tools/test_c_analyzer/test_cpython/test___main__.py deleted file mode 100644 index 6d69ed7..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_cpython/test___main__.py +++ /dev/null @@ -1,296 +0,0 @@ -import sys -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.variables import info - from cpython import SOURCE_DIRS - from cpython.supported import IGNORED_FILE - from cpython.known import DATA_FILE as KNOWN_FILE - from cpython.__main__ import ( - cmd_check, cmd_show, parse_args, main, - ) - - -TYPICAL = [ - (info.Variable.from_parts('src1/spam.c', None, 'var1', 'const char *'), - True, - ), - (info.Variable.from_parts('src1/spam.c', 'ham', 'initialized', 'int'), - True, - ), - (info.Variable.from_parts('src1/spam.c', None, 'var2', 'PyObject *'), - False, - ), - (info.Variable.from_parts('src1/eggs.c', 'tofu', 'ready', 'int'), - True, - ), - (info.Variable.from_parts('src1/spam.c', None, 'freelist', '(PyTupleObject *)[10]'), - False, - ), - (info.Variable.from_parts('src1/sub/ham.c', None, 'var1', 'const char const *'), - True, - ), - (info.Variable.from_parts('src2/jam.c', None, 'var1', 'int'), - True, - ), - (info.Variable.from_parts('src2/jam.c', None, 'var2', 'MyObject *'), - False, - ), - (info.Variable.from_parts('Include/spam.h', None, 'data', 'const int'), - True, - ), - ] - - -class CMDBase(unittest.TestCase): - - maxDiff = None - -# _return_known_from_file = None -# _return_ignored_from_file = None - _return_find = () - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - -# def _known_from_file(self, *args): -# self.calls.append(('_known_from_file', args)) -# return self._return_known_from_file or {} -# -# def _ignored_from_file(self, *args): -# self.calls.append(('_ignored_from_file', args)) -# return self._return_ignored_from_file or {} - - def _find(self, known, ignored, skip_objects=False): - self.calls.append(('_find', (known, ignored, skip_objects))) - return self._return_find - - def _show(self, *args): - self.calls.append(('_show', args)) - - def _print(self, *args): - self.calls.append(('_print', args)) - - -class CheckTests(CMDBase): - - def test_defaults(self): - self._return_find = [] - - cmd_check('check', - _find=self._find, - _show=self._show, - _print=self._print, - ) - - self.assertEqual( - self.calls[0], - ('_find', (KNOWN_FILE, IGNORED_FILE, False)), - ) - - def test_all_supported(self): - self._return_find = [(v, s) for v, s in TYPICAL if s] - dirs = ['src1', 'src2', 'Include'] - - cmd_check('check', - known='known.tsv', - ignored='ignored.tsv', - _find=self._find, - _show=self._show, - _print=self._print, - ) - - self.assertEqual(self.calls, [ - ('_find', ('known.tsv', 'ignored.tsv', False)), - #('_print', ('okay',)), - ]) - - def test_some_unsupported(self): - self._return_find = TYPICAL - - with self.assertRaises(SystemExit) as cm: - cmd_check('check', - known='known.tsv', - ignored='ignored.tsv', - _find=self._find, - _show=self._show, - _print=self._print, - ) - - unsupported = [v for v, s in TYPICAL if not s] - self.assertEqual(self.calls, [ - ('_find', ('known.tsv', 'ignored.tsv', False)), - ('_print', ('ERROR: found unsupported global variables',)), - ('_print', ()), - ('_show', (sorted(unsupported),)), - ('_print', (' (3 total)',)), - ]) - self.assertEqual(cm.exception.code, 1) - - -class ShowTests(CMDBase): - - def test_defaults(self): - self._return_find = [] - - cmd_show('show', - _find=self._find, - _show=self._show, - _print=self._print, - ) - - self.assertEqual( - self.calls[0], - ('_find', (KNOWN_FILE, IGNORED_FILE, False)), - ) - - def test_typical(self): - self._return_find = TYPICAL - - cmd_show('show', - known='known.tsv', - ignored='ignored.tsv', - _find=self._find, - _show=self._show, - _print=self._print, - ) - - supported = [v for v, s in TYPICAL if s] - unsupported = [v for v, s in TYPICAL if not s] - self.assertEqual(self.calls, [ - ('_find', ('known.tsv', 'ignored.tsv', False)), - ('_print', ('supported:',)), - ('_print', ('----------',)), - ('_show', (sorted(supported),)), - ('_print', (' (6 total)',)), - ('_print', ()), - ('_print', ('unsupported:',)), - ('_print', ('------------',)), - ('_show', (sorted(unsupported),)), - ('_print', (' (3 total)',)), - ]) - - -class ParseArgsTests(unittest.TestCase): - - maxDiff = None - - def test_no_args(self): - self.errmsg = None - def fail(msg): - self.errmsg = msg - sys.exit(msg) - - with self.assertRaises(SystemExit): - parse_args('cg', [], _fail=fail) - - self.assertEqual(self.errmsg, 'missing command') - - def test_check_no_args(self): - cmd, cmdkwargs = parse_args('cg', [ - 'check', - ]) - - self.assertEqual(cmd, 'check') - self.assertEqual(cmdkwargs, { - 'ignored': IGNORED_FILE, - 'known': KNOWN_FILE, - #'dirs': SOURCE_DIRS, - }) - - def test_check_full_args(self): - cmd, cmdkwargs = parse_args('cg', [ - 'check', - '--ignored', 'spam.tsv', - '--known', 'eggs.tsv', - #'dir1', - #'dir2', - #'dir3', - ]) - - self.assertEqual(cmd, 'check') - self.assertEqual(cmdkwargs, { - 'ignored': 'spam.tsv', - 'known': 'eggs.tsv', - #'dirs': ['dir1', 'dir2', 'dir3'] - }) - - def test_show_no_args(self): - cmd, cmdkwargs = parse_args('cg', [ - 'show', - ]) - - self.assertEqual(cmd, 'show') - self.assertEqual(cmdkwargs, { - 'ignored': IGNORED_FILE, - 'known': KNOWN_FILE, - #'dirs': SOURCE_DIRS, - 'skip_objects': False, - }) - - def test_show_full_args(self): - cmd, cmdkwargs = parse_args('cg', [ - 'show', - '--ignored', 'spam.tsv', - '--known', 'eggs.tsv', - #'dir1', - #'dir2', - #'dir3', - ]) - - self.assertEqual(cmd, 'show') - self.assertEqual(cmdkwargs, { - 'ignored': 'spam.tsv', - 'known': 'eggs.tsv', - #'dirs': ['dir1', 'dir2', 'dir3'], - 'skip_objects': False, - }) - - -def new_stub_commands(*names): - calls = [] - def cmdfunc(cmd, **kwargs): - calls.append((cmd, kwargs)) - commands = {name: cmdfunc for name in names} - return commands, calls - - -class MainTests(unittest.TestCase): - - def test_no_command(self): - with self.assertRaises(ValueError): - main(None, {}) - - def test_check(self): - commands, calls = new_stub_commands('check', 'show') - - cmdkwargs = { - 'ignored': 'spam.tsv', - 'known': 'eggs.tsv', - 'dirs': ['dir1', 'dir2', 'dir3'], - } - main('check', cmdkwargs, _COMMANDS=commands) - - self.assertEqual(calls, [ - ('check', cmdkwargs), - ]) - - def test_show(self): - commands, calls = new_stub_commands('check', 'show') - - cmdkwargs = { - 'ignored': 'spam.tsv', - 'known': 'eggs.tsv', - 'dirs': ['dir1', 'dir2', 'dir3'], - } - main('show', cmdkwargs, _COMMANDS=commands) - - self.assertEqual(calls, [ - ('show', cmdkwargs), - ]) diff --git a/Lib/test/test_tools/test_c_analyzer/test_cpython/test_functional.py b/Lib/test/test_tools/test_c_analyzer/test_cpython/test_functional.py deleted file mode 100644 index 9279790..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_cpython/test_functional.py +++ /dev/null @@ -1,34 +0,0 @@ -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - pass - - -class SelfCheckTests(unittest.TestCase): - - @unittest.expectedFailure - def test_known(self): - # Make sure known macros & vartypes aren't hiding unknown local types. - # XXX finish! - raise NotImplementedError - - @unittest.expectedFailure - def test_compare_nm_results(self): - # Make sure the "show" results match the statics found by "nm" command. - # XXX Skip if "nm" is not available. - # XXX finish! - raise NotImplementedError - - -class DummySourceTests(unittest.TestCase): - - @unittest.expectedFailure - def test_check(self): - # XXX finish! - raise NotImplementedError - - @unittest.expectedFailure - def test_show(self): - # XXX finish! - raise NotImplementedError diff --git a/Lib/test/test_tools/test_c_analyzer/test_cpython/test_supported.py b/Lib/test/test_tools/test_c_analyzer/test_cpython/test_supported.py deleted file mode 100644 index a244b97..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_cpython/test_supported.py +++ /dev/null @@ -1,98 +0,0 @@ -import re -import textwrap -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.common.info import ID - from c_analyzer.variables.info import Variable - from cpython.supported import ( - is_supported, ignored_from_file, - ) - - -class IsSupportedTests(unittest.TestCase): - - @unittest.expectedFailure - def test_supported(self): - statics = [ - Variable('src1/spam.c', None, 'var1', 'const char *'), - Variable('src1/spam.c', None, 'var1', 'int'), - ] - for static in statics: - with self.subTest(static): - result = is_supported(static) - - self.assertTrue(result) - - @unittest.expectedFailure - def test_not_supported(self): - statics = [ - Variable('src1/spam.c', None, 'var1', 'PyObject *'), - Variable('src1/spam.c', None, 'var1', 'PyObject[10]'), - ] - for static in statics: - with self.subTest(static): - result = is_supported(static) - - self.assertFalse(result) - - -class IgnoredFromFileTests(unittest.TestCase): - - maxDiff = None - - _return_read_tsv = () - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - - def _read_tsv(self, *args): - self.calls.append(('_read_tsv', args)) - return self._return_read_tsv - - def test_typical(self): - lines = textwrap.dedent(''' - filename funcname name kind reason - file1.c - var1 variable ... - file1.c func1 local1 variable | - file1.c - var2 variable ??? - file1.c func2 local2 variable | - file2.c - var1 variable reasons - ''').strip().splitlines() - lines = [re.sub(r'\s{1,8}', '\t', line, 4).replace('|', '') - for line in lines] - self._return_read_tsv = [tuple(v.strip() for v in line.split('\t')) - for line in lines[1:]] - - ignored = ignored_from_file('spam.c', _read_tsv=self._read_tsv) - - self.assertEqual(ignored, { - 'variables': { - ID('file1.c', '', 'var1'): '...', - ID('file1.c', 'func1', 'local1'): '', - ID('file1.c', '', 'var2'): '???', - ID('file1.c', 'func2', 'local2'): '', - ID('file2.c', '', 'var1'): 'reasons', - }, - }) - self.assertEqual(self.calls, [ - ('_read_tsv', ('spam.c', 'filename\tfuncname\tname\tkind\treason')), - ]) - - def test_empty(self): - self._return_read_tsv = [] - - ignored = ignored_from_file('spam.c', _read_tsv=self._read_tsv) - - self.assertEqual(ignored, { - 'variables': {}, - }) - self.assertEqual(self.calls, [ - ('_read_tsv', ('spam.c', 'filename\tfuncname\tname\tkind\treason')), - ]) diff --git a/Lib/test/test_tools/test_c_analyzer/test_parser/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_parser/__init__.py deleted file mode 100644 index bc502ef..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_parser/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os.path -from test.support import load_package_tests - - -def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tools/test_c_analyzer/test_parser/test_declarations.py b/Lib/test/test_tools/test_c_analyzer/test_parser/test_declarations.py deleted file mode 100644 index 674fcb1..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_parser/test_declarations.py +++ /dev/null @@ -1,795 +0,0 @@ -import textwrap -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.parser.declarations import ( - iter_global_declarations, iter_local_statements, - parse_func, _parse_var, parse_compound, - iter_variables, - ) - - -class TestCaseBase(unittest.TestCase): - - maxDiff = None - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - - -class IterGlobalDeclarationsTests(TestCaseBase): - - def test_functions(self): - tests = [ - (textwrap.dedent(''' - void func1() { - return; - } - '''), - textwrap.dedent(''' - void func1() { - return; - } - ''').strip(), - ), - (textwrap.dedent(''' - static unsigned int * _func1( - const char *arg1, - int *arg2 - long long arg3 - ) - { - return _do_something(arg1, arg2, arg3); - } - '''), - textwrap.dedent(''' - static unsigned int * _func1( const char *arg1, int *arg2 long long arg3 ) { - return _do_something(arg1, arg2, arg3); - } - ''').strip(), - ), - (textwrap.dedent(''' - static PyObject * - _func1(const char *arg1, PyObject *arg2) - { - static int initialized = 0; - if (!initialized) { - initialized = 1; - _init(arg1); - } - - PyObject *result = _do_something(arg1, arg2); - Py_INCREF(result); - return result; - } - '''), - textwrap.dedent(''' - static PyObject * _func1(const char *arg1, PyObject *arg2) { - static int initialized = 0; - if (!initialized) { - initialized = 1; - _init(arg1); - } - PyObject *result = _do_something(arg1, arg2); - Py_INCREF(result); - return result; - } - ''').strip(), - ), - ] - for lines, expected in tests: - body = textwrap.dedent( - expected.partition('{')[2].rpartition('}')[0] - ).strip() - expected = (expected, body) - with self.subTest(lines): - lines = lines.splitlines() - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, [expected]) - - @unittest.expectedFailure - def test_declarations(self): - tests = [ - 'int spam;', - 'long long spam;', - 'static const int const *spam;', - 'int spam;', - 'typedef int myint;', - 'typedef PyObject * (*unaryfunc)(PyObject *);', - # typedef struct - # inline struct - # enum - # inline enum - ] - for text in tests: - expected = (text, - ' '.join(l.strip() for l in text.splitlines())) - with self.subTest(lines): - lines = lines.splitlines() - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, [expected]) - - @unittest.expectedFailure - def test_declaration_multiple_vars(self): - lines = ['static const int const *spam, *ham=NULL, eggs = 3;'] - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, [ - ('static const int const *spam;', None), - ('static const int *ham=NULL;', None), - ('static const int eggs = 3;', None), - ]) - - def test_mixed(self): - lines = textwrap.dedent(''' - int spam; - static const char const *eggs; - - PyObject * start(void) { - static int initialized = 0; - if (initialized) { - initialized = 1; - init(); - } - return _start(); - } - - char* ham; - - static int stop(char *reason) { - ham = reason; - return _stop(); - } - ''').splitlines() - expected = [ - (textwrap.dedent(''' - PyObject * start(void) { - static int initialized = 0; - if (initialized) { - initialized = 1; - init(); - } - return _start(); - } - ''').strip(), - textwrap.dedent(''' - static int initialized = 0; - if (initialized) { - initialized = 1; - init(); - } - return _start(); - ''').strip(), - ), - (textwrap.dedent(''' - static int stop(char *reason) { - ham = reason; - return _stop(); - } - ''').strip(), - textwrap.dedent(''' - ham = reason; - return _stop(); - ''').strip(), - ), - ] - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, expected) - #self.assertEqual([stmt for stmt, _ in stmts], - # [stmt for stmt, _ in expected]) - #self.assertEqual([body for _, body in stmts], - # [body for _, body in expected]) - - def test_no_statements(self): - lines = [] - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, []) - - def test_bogus(self): - tests = [ - (textwrap.dedent(''' - int spam; - static const char const *eggs; - - PyObject * start(void) { - static int initialized = 0; - if (initialized) { - initialized = 1; - init(); - } - return _start(); - } - - char* ham; - - static int _stop(void) { - // missing closing bracket - - static int stop(char *reason) { - ham = reason; - return _stop(); - } - '''), - [(textwrap.dedent(''' - PyObject * start(void) { - static int initialized = 0; - if (initialized) { - initialized = 1; - init(); - } - return _start(); - } - ''').strip(), - textwrap.dedent(''' - static int initialized = 0; - if (initialized) { - initialized = 1; - init(); - } - return _start(); - ''').strip(), - ), - # Neither "stop()" nor "_stop()" are here. - ], - ), - ] - for lines, expected in tests: - with self.subTest(lines): - lines = lines.splitlines() - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, expected) - #self.assertEqual([stmt for stmt, _ in stmts], - # [stmt for stmt, _ in expected]) - #self.assertEqual([body for _, body in stmts], - # [body for _, body in expected]) - - def test_ignore_comments(self): - tests = [ - ('// msg', None), - ('// int stmt;', None), - (' // ... ', None), - ('// /*', None), - ('/* int stmt; */', None), - (""" - /** - * ... - * int stmt; - */ - """, None), - ] - for lines, expected in tests: - with self.subTest(lines): - lines = lines.splitlines() - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, [expected] if expected else []) - - -class IterLocalStatementsTests(TestCaseBase): - - def test_vars(self): - tests = [ - # POTS - 'int spam;', - 'unsigned int spam;', - 'char spam;', - 'float spam;', - - # typedefs - 'uint spam;', - 'MyType spam;', - - # complex - 'struct myspam spam;', - 'union choice spam;', - # inline struct - # inline union - # enum? - ] - # pointers - tests.extend([ - # POTS - 'int * spam;', - 'unsigned int * spam;', - 'char *spam;', - 'char const *spam = "spamspamspam...";', - # typedefs - 'MyType *spam;', - # complex - 'struct myspam *spam;', - 'union choice *spam;', - # packed with details - 'const char const *spam;', - # void pointer - 'void *data = NULL;', - # function pointers - 'int (* func)(char *arg1);', - 'char * (* func)(void);', - ]) - # storage class - tests.extend([ - 'static int spam;', - 'extern int spam;', - 'static unsigned int spam;', - 'static struct myspam spam;', - ]) - # type qualifier - tests.extend([ - 'const int spam;', - 'const unsigned int spam;', - 'const struct myspam spam;', - ]) - # combined - tests.extend([ - 'const char *spam = eggs;', - 'static const char const *spam = "spamspamspam...";', - 'extern const char const *spam;', - 'static void *data = NULL;', - 'static int (const * func)(char *arg1) = func1;', - 'static char * (* func)(void);', - ]) - for line in tests: - expected = line - with self.subTest(line): - stmts = list(iter_local_statements([line])) - - self.assertEqual(stmts, [(expected, None)]) - - @unittest.expectedFailure - def test_vars_multiline_var(self): - lines = textwrap.dedent(''' - PyObject * - spam - = NULL; - ''').splitlines() - expected = 'PyObject * spam = NULL;' - - stmts = list(iter_local_statements(lines)) - - self.assertEqual(stmts, [(expected, None)]) - - @unittest.expectedFailure - def test_declaration_multiple_vars(self): - lines = ['static const int const *spam, *ham=NULL, ham2[]={1, 2, 3}, ham3[2]={1, 2}, eggs = 3;'] - - stmts = list(iter_global_declarations(lines)) - - self.assertEqual(stmts, [ - ('static const int const *spam;', None), - ('static const int *ham=NULL;', None), - ('static const int ham[]={1, 2, 3};', None), - ('static const int ham[2]={1, 2};', None), - ('static const int eggs = 3;', None), - ]) - - @unittest.expectedFailure - def test_other_simple(self): - raise NotImplementedError - - @unittest.expectedFailure - def test_compound(self): - raise NotImplementedError - - @unittest.expectedFailure - def test_mixed(self): - raise NotImplementedError - - def test_no_statements(self): - lines = [] - - stmts = list(iter_local_statements(lines)) - - self.assertEqual(stmts, []) - - @unittest.expectedFailure - def test_bogus(self): - raise NotImplementedError - - def test_ignore_comments(self): - tests = [ - ('// msg', None), - ('// int stmt;', None), - (' // ... ', None), - ('// /*', None), - ('/* int stmt; */', None), - (""" - /** - * ... - * int stmt; - */ - """, None), - # mixed with statements - ('int stmt; // ...', ('int stmt;', None)), - ( 'int stmt; /* ... */', ('int stmt;', None)), - ( '/* ... */ int stmt;', ('int stmt;', None)), - ] - for lines, expected in tests: - with self.subTest(lines): - lines = lines.splitlines() - - stmts = list(iter_local_statements(lines)) - - self.assertEqual(stmts, [expected] if expected else []) - - -class ParseFuncTests(TestCaseBase): - - def test_typical(self): - tests = [ - ('PyObject *\nspam(char *a)\n{\nreturn _spam(a);\n}', - 'return _spam(a);', - ('spam', 'PyObject * spam(char *a)'), - ), - ] - for stmt, body, expected in tests: - with self.subTest(stmt): - name, signature = parse_func(stmt, body) - - self.assertEqual((name, signature), expected) - - -class ParseVarTests(TestCaseBase): - - def test_typical(self): - tests = [ - # POTS - ('int spam;', ('spam', 'int')), - ('unsigned int spam;', ('spam', 'unsigned int')), - ('char spam;', ('spam', 'char')), - ('float spam;', ('spam', 'float')), - - # typedefs - ('uint spam;', ('spam', 'uint')), - ('MyType spam;', ('spam', 'MyType')), - - # complex - ('struct myspam spam;', ('spam', 'struct myspam')), - ('union choice spam;', ('spam', 'union choice')), - # inline struct - # inline union - # enum? - ] - # pointers - tests.extend([ - # POTS - ('int * spam;', ('spam', 'int *')), - ('unsigned int * spam;', ('spam', 'unsigned int *')), - ('char *spam;', ('spam', 'char *')), - ('char const *spam = "spamspamspam...";', ('spam', 'char const *')), - # typedefs - ('MyType *spam;', ('spam', 'MyType *')), - # complex - ('struct myspam *spam;', ('spam', 'struct myspam *')), - ('union choice *spam;', ('spam', 'union choice *')), - # packed with details - ('const char const *spam;', ('spam', 'const char const *')), - # void pointer - ('void *data = NULL;', ('data', 'void *')), - # function pointers - ('int (* func)(char *);', ('func', 'int (*)(char *)')), - ('char * (* func)(void);', ('func', 'char * (*)(void)')), - ]) - # storage class - tests.extend([ - ('static int spam;', ('spam', 'static int')), - ('extern int spam;', ('spam', 'extern int')), - ('static unsigned int spam;', ('spam', 'static unsigned int')), - ('static struct myspam spam;', ('spam', 'static struct myspam')), - ]) - # type qualifier - tests.extend([ - ('const int spam;', ('spam', 'const int')), - ('const unsigned int spam;', ('spam', 'const unsigned int')), - ('const struct myspam spam;', ('spam', 'const struct myspam')), - ]) - # combined - tests.extend([ - ('const char *spam = eggs;', ('spam', 'const char *')), - ('static const char const *spam = "spamspamspam...";', - ('spam', 'static const char const *')), - ('extern const char const *spam;', - ('spam', 'extern const char const *')), - ('static void *data = NULL;', ('data', 'static void *')), - ('static int (const * func)(char *) = func1;', - ('func', 'static int (const *)(char *)')), - ('static char * (* func)(void);', - ('func', 'static char * (*)(void)')), - ]) - for stmt, expected in tests: - with self.subTest(stmt): - name, vartype = _parse_var(stmt) - - self.assertEqual((name, vartype), expected) - - -@unittest.skip('not finished') -class ParseCompoundTests(TestCaseBase): - - def test_typical(self): - headers, bodies = parse_compound(stmt, blocks) - ... - - -class IterVariablesTests(TestCaseBase): - - _return_iter_source_lines = None - _return_iter_global = None - _return_iter_local = None - _return_parse_func = None - _return_parse_var = None - _return_parse_compound = None - - def _iter_source_lines(self, filename): - self.calls.append( - ('_iter_source_lines', (filename,))) - return self._return_iter_source_lines.splitlines() - - def _iter_global(self, lines): - self.calls.append( - ('_iter_global', (lines,))) - try: - return self._return_iter_global.pop(0) - except IndexError: - return ('???', None) - - def _iter_local(self, lines): - self.calls.append( - ('_iter_local', (lines,))) - try: - return self._return_iter_local.pop(0) - except IndexError: - return ('???', None) - - def _parse_func(self, stmt, body): - self.calls.append( - ('_parse_func', (stmt, body))) - try: - return self._return_parse_func.pop(0) - except IndexError: - return ('???', '???') - - def _parse_var(self, lines): - self.calls.append( - ('_parse_var', (lines,))) - try: - return self._return_parse_var.pop(0) - except IndexError: - return ('???', '???') - - def _parse_compound(self, stmt, blocks): - self.calls.append( - ('_parse_compound', (stmt, blocks))) - try: - return self._return_parse_compound.pop(0) - except IndexError: - return (['???'], ['???']) - - def test_empty_file(self): - self._return_iter_source_lines = '' - self._return_iter_global = [ - [], - ] - self._return_parse_func = None - self._return_parse_var = None - self._return_parse_compound = None - - srcvars = list(iter_variables('spam.c', - _iter_source_lines=self._iter_source_lines, - _iter_global=self._iter_global, - _iter_local=self._iter_local, - _parse_func=self._parse_func, - _parse_var=self._parse_var, - _parse_compound=self._parse_compound, - )) - - self.assertEqual(srcvars, []) - self.assertEqual(self.calls, [ - ('_iter_source_lines', ('spam.c',)), - ('_iter_global', ([],)), - ]) - - def test_no_statements(self): - content = textwrap.dedent(''' - ... - ''') - self._return_iter_source_lines = content - self._return_iter_global = [ - [], - ] - self._return_parse_func = None - self._return_parse_var = None - self._return_parse_compound = None - - srcvars = list(iter_variables('spam.c', - _iter_source_lines=self._iter_source_lines, - _iter_global=self._iter_global, - _iter_local=self._iter_local, - _parse_func=self._parse_func, - _parse_var=self._parse_var, - _parse_compound=self._parse_compound, - )) - - self.assertEqual(srcvars, []) - self.assertEqual(self.calls, [ - ('_iter_source_lines', ('spam.c',)), - ('_iter_global', (content.splitlines(),)), - ]) - - def test_typical(self): - content = textwrap.dedent(''' - ... - ''') - self._return_iter_source_lines = content - self._return_iter_global = [ - [('<lines 1>', None), # var1 - ('<lines 2>', None), # non-var - ('<lines 3>', None), # var2 - ('<lines 4>', '<body 1>'), # func1 - ('<lines 9>', None), # var4 - ], - ] - self._return_iter_local = [ - # func1 - [('<lines 5>', None), # var3 - ('<lines 6>', [('<header 1>', '<block 1>')]), # if - ('<lines 8>', None), # non-var - ], - # if - [('<lines 7>', None), # var2 ("collision" with global var) - ], - ] - self._return_parse_func = [ - ('func1', '<sig 1>'), - ] - self._return_parse_var = [ - ('var1', '<vartype 1>'), - (None, None), - ('var2', '<vartype 2>'), - ('var3', '<vartype 3>'), - ('var2', '<vartype 2b>'), - ('var4', '<vartype 4>'), - (None, None), - (None, None), - (None, None), - ('var5', '<vartype 5>'), - ] - self._return_parse_compound = [ - ([[ - 'if (', - '<simple>', - ')', - ], - ], - ['<block 1>']), - ] - - srcvars = list(iter_variables('spam.c', - _iter_source_lines=self._iter_source_lines, - _iter_global=self._iter_global, - _iter_local=self._iter_local, - _parse_func=self._parse_func, - _parse_var=self._parse_var, - _parse_compound=self._parse_compound, - )) - - self.assertEqual(srcvars, [ - (None, 'var1', '<vartype 1>'), - (None, 'var2', '<vartype 2>'), - ('func1', 'var3', '<vartype 3>'), - ('func1', 'var2', '<vartype 2b>'), - ('func1', 'var4', '<vartype 4>'), - (None, 'var5', '<vartype 5>'), - ]) - self.assertEqual(self.calls, [ - ('_iter_source_lines', ('spam.c',)), - ('_iter_global', (content.splitlines(),)), - ('_parse_var', ('<lines 1>',)), - ('_parse_var', ('<lines 2>',)), - ('_parse_var', ('<lines 3>',)), - ('_parse_func', ('<lines 4>', '<body 1>')), - ('_iter_local', (['<body 1>'],)), - ('_parse_var', ('<lines 5>',)), - ('_parse_compound', ('<lines 6>', [('<header 1>', '<block 1>')])), - ('_parse_var', ('if (',)), - ('_parse_var', ('<simple>',)), - ('_parse_var', (')',)), - ('_parse_var', ('<lines 8>',)), - ('_iter_local', (['<block 1>'],)), - ('_parse_var', ('<lines 7>',)), - ('_parse_var', ('<lines 9>',)), - ]) - - def test_no_locals(self): - content = textwrap.dedent(''' - ... - ''') - self._return_iter_source_lines = content - self._return_iter_global = [ - [('<lines 1>', None), # var1 - ('<lines 2>', None), # non-var - ('<lines 3>', None), # var2 - ('<lines 4>', '<body 1>'), # func1 - ], - ] - self._return_iter_local = [ - # func1 - [('<lines 5>', None), # non-var - ('<lines 6>', [('<header 1>', '<block 1>')]), # if - ('<lines 8>', None), # non-var - ], - # if - [('<lines 7>', None), # non-var - ], - ] - self._return_parse_func = [ - ('func1', '<sig 1>'), - ] - self._return_parse_var = [ - ('var1', '<vartype 1>'), - (None, None), - ('var2', '<vartype 2>'), - (None, None), - (None, None), - (None, None), - (None, None), - (None, None), - (None, None), - ] - self._return_parse_compound = [ - ([[ - 'if (', - '<simple>', - ')', - ], - ], - ['<block 1>']), - ] - - srcvars = list(iter_variables('spam.c', - _iter_source_lines=self._iter_source_lines, - _iter_global=self._iter_global, - _iter_local=self._iter_local, - _parse_func=self._parse_func, - _parse_var=self._parse_var, - _parse_compound=self._parse_compound, - )) - - self.assertEqual(srcvars, [ - (None, 'var1', '<vartype 1>'), - (None, 'var2', '<vartype 2>'), - ]) - self.assertEqual(self.calls, [ - ('_iter_source_lines', ('spam.c',)), - ('_iter_global', (content.splitlines(),)), - ('_parse_var', ('<lines 1>',)), - ('_parse_var', ('<lines 2>',)), - ('_parse_var', ('<lines 3>',)), - ('_parse_func', ('<lines 4>', '<body 1>')), - ('_iter_local', (['<body 1>'],)), - ('_parse_var', ('<lines 5>',)), - ('_parse_compound', ('<lines 6>', [('<header 1>', '<block 1>')])), - ('_parse_var', ('if (',)), - ('_parse_var', ('<simple>',)), - ('_parse_var', (')',)), - ('_parse_var', ('<lines 8>',)), - ('_iter_local', (['<block 1>'],)), - ('_parse_var', ('<lines 7>',)), - ]) diff --git a/Lib/test/test_tools/test_c_analyzer/test_parser/test_preprocessor.py b/Lib/test/test_tools/test_c_analyzer/test_parser/test_preprocessor.py deleted file mode 100644 index b7f950f..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_parser/test_preprocessor.py +++ /dev/null @@ -1,1561 +0,0 @@ -import textwrap -import unittest -import sys - -from ..util import wrapped_arg_combos, StrProxy -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.parser.preprocessor import ( - iter_lines, - # directives - parse_directive, PreprocessorDirective, - Constant, Macro, IfDirective, Include, OtherDirective, - ) - - -class TestCaseBase(unittest.TestCase): - - maxDiff = None - - def reset(self): - self._calls = [] - self.errors = None - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - - errors = None - - def try_next_exc(self): - if not self.errors: - return - if exc := self.errors.pop(0): - raise exc - - def check_calls(self, *expected): - self.assertEqual(self.calls, list(expected)) - self.assertEqual(self.errors or [], []) - - -class IterLinesTests(TestCaseBase): - - parsed = None - - def check_calls(self, *expected): - super().check_calls(*expected) - self.assertEqual(self.parsed or [], []) - - def _parse_directive(self, line): - self.calls.append( - ('_parse_directive', line)) - self.try_next_exc() - return self.parsed.pop(0) - - def test_no_lines(self): - lines = [] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, []) - self.check_calls() - - def test_no_directives(self): - lines = textwrap.dedent(''' - - // xyz - typedef enum { - SPAM - EGGS - } kind; - - struct info { - kind kind; - int status; - }; - - typedef struct spam { - struct info info; - } myspam; - - static int spam = 0; - - /** - * ... - */ - static char * - get_name(int arg, - char *default, - ) - { - return default - } - - int check(void) { - return 0; - } - - ''')[1:-1].splitlines() - expected = [(lno, line, None, ()) - for lno, line in enumerate(lines, 1)] - expected[1] = (2, ' ', None, ()) - expected[20] = (21, ' ', None, ()) - del expected[19] - del expected[18] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, expected) - self.check_calls() - - def test_single_directives(self): - tests = [ - ('#include <stdio>', Include('<stdio>')), - ('#define SPAM 1', Constant('SPAM', '1')), - ('#define SPAM() 1', Macro('SPAM', (), '1')), - ('#define SPAM(a, b) a = b;', Macro('SPAM', ('a', 'b'), 'a = b;')), - ('#if defined(SPAM)', IfDirective('if', 'defined(SPAM)')), - ('#ifdef SPAM', IfDirective('ifdef', 'SPAM')), - ('#ifndef SPAM', IfDirective('ifndef', 'SPAM')), - ('#elseif defined(SPAM)', IfDirective('elseif', 'defined(SPAM)')), - ('#else', OtherDirective('else', None)), - ('#endif', OtherDirective('endif', None)), - ('#error ...', OtherDirective('error', '...')), - ('#warning ...', OtherDirective('warning', '...')), - ('#__FILE__ ...', OtherDirective('__FILE__', '...')), - ('#__LINE__ ...', OtherDirective('__LINE__', '...')), - ('#__DATE__ ...', OtherDirective('__DATE__', '...')), - ('#__TIME__ ...', OtherDirective('__TIME__', '...')), - ('#__TIMESTAMP__ ...', OtherDirective('__TIMESTAMP__', '...')), - ] - for line, directive in tests: - with self.subTest(line): - self.reset() - self.parsed = [ - directive, - ] - text = textwrap.dedent(''' - static int spam = 0; - {} - static char buffer[256]; - ''').strip().format(line) - lines = text.strip().splitlines() - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, 'static int spam = 0;', None, ()), - (2, line, directive, ()), - ((3, 'static char buffer[256];', None, ('defined(SPAM)',)) - if directive.kind in ('if', 'ifdef', 'elseif') - else (3, 'static char buffer[256];', None, ('! defined(SPAM)',)) - if directive.kind == 'ifndef' - else (3, 'static char buffer[256];', None, ())), - ]) - self.check_calls( - ('_parse_directive', line), - ) - - def test_directive_whitespace(self): - line = ' # define eggs ( a , b ) { a = b ; } ' - directive = Macro('eggs', ('a', 'b'), '{ a = b; }') - self.parsed = [ - directive, - ] - lines = [line] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, line, directive, ()), - ]) - self.check_calls( - ('_parse_directive', '#define eggs ( a , b ) { a = b ; }'), - ) - - @unittest.skipIf(sys.platform == 'win32', 'needs fix under Windows') - def test_split_lines(self): - directive = Macro('eggs', ('a', 'b'), '{ a = b; }') - self.parsed = [ - directive, - ] - text = textwrap.dedent(r''' - static int spam = 0; - #define eggs(a, b) \ - { \ - a = b; \ - } - static char buffer[256]; - ''').strip() - lines = [line + '\n' for line in text.splitlines()] - lines[-1] = lines[-1][:-1] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, 'static int spam = 0;\n', None, ()), - (5, '#define eggs(a, b) { a = b; }\n', directive, ()), - (6, 'static char buffer[256];', None, ()), - ]) - self.check_calls( - ('_parse_directive', '#define eggs(a, b) { a = b; }'), - ) - - def test_nested_conditions(self): - directives = [ - IfDirective('ifdef', 'SPAM'), - IfDirective('if', 'SPAM == 1'), - IfDirective('elseif', 'SPAM == 2'), - OtherDirective('else', None), - OtherDirective('endif', None), - OtherDirective('endif', None), - ] - self.parsed = list(directives) - text = textwrap.dedent(r''' - static int spam = 0; - - #ifdef SPAM - static int start = 0; - # if SPAM == 1 - static char buffer[10]; - # elif SPAM == 2 - static char buffer[100]; - # else - static char buffer[256]; - # endif - static int end = 0; - #endif - - static int eggs = 0; - ''').strip() - lines = [line for line in text.splitlines() if line.strip()] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, 'static int spam = 0;', None, ()), - (2, '#ifdef SPAM', directives[0], ()), - (3, 'static int start = 0;', None, ('defined(SPAM)',)), - (4, '# if SPAM == 1', directives[1], ('defined(SPAM)',)), - (5, 'static char buffer[10];', None, ('defined(SPAM)', 'SPAM == 1')), - (6, '# elif SPAM == 2', directives[2], ('defined(SPAM)', 'SPAM == 1')), - (7, 'static char buffer[100];', None, ('defined(SPAM)', '! (SPAM == 1)', 'SPAM == 2')), - (8, '# else', directives[3], ('defined(SPAM)', '! (SPAM == 1)', 'SPAM == 2')), - (9, 'static char buffer[256];', None, ('defined(SPAM)', '! (SPAM == 1)', '! (SPAM == 2)')), - (10, '# endif', directives[4], ('defined(SPAM)', '! (SPAM == 1)', '! (SPAM == 2)')), - (11, 'static int end = 0;', None, ('defined(SPAM)',)), - (12, '#endif', directives[5], ('defined(SPAM)',)), - (13, 'static int eggs = 0;', None, ()), - ]) - self.check_calls( - ('_parse_directive', '#ifdef SPAM'), - ('_parse_directive', '#if SPAM == 1'), - ('_parse_directive', '#elif SPAM == 2'), - ('_parse_directive', '#else'), - ('_parse_directive', '#endif'), - ('_parse_directive', '#endif'), - ) - - def test_split_blocks(self): - directives = [ - IfDirective('ifdef', 'SPAM'), - OtherDirective('else', None), - OtherDirective('endif', None), - ] - self.parsed = list(directives) - text = textwrap.dedent(r''' - void str_copy(char *buffer, *orig); - - int init(char *name) { - static int initialized = 0; - if (initialized) { - return 0; - } - #ifdef SPAM - static char buffer[10]; - str_copy(buffer, char); - } - - void copy(char *buffer, *orig) { - strncpy(buffer, orig, 9); - buffer[9] = 0; - } - - #else - static char buffer[256]; - str_copy(buffer, char); - } - - void copy(char *buffer, *orig) { - strcpy(buffer, orig); - } - - #endif - ''').strip() - lines = [line for line in text.splitlines() if line.strip()] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, 'void str_copy(char *buffer, *orig);', None, ()), - (2, 'int init(char *name) {', None, ()), - (3, ' static int initialized = 0;', None, ()), - (4, ' if (initialized) {', None, ()), - (5, ' return 0;', None, ()), - (6, ' }', None, ()), - - (7, '#ifdef SPAM', directives[0], ()), - - (8, ' static char buffer[10];', None, ('defined(SPAM)',)), - (9, ' str_copy(buffer, char);', None, ('defined(SPAM)',)), - (10, '}', None, ('defined(SPAM)',)), - (11, 'void copy(char *buffer, *orig) {', None, ('defined(SPAM)',)), - (12, ' strncpy(buffer, orig, 9);', None, ('defined(SPAM)',)), - (13, ' buffer[9] = 0;', None, ('defined(SPAM)',)), - (14, '}', None, ('defined(SPAM)',)), - - (15, '#else', directives[1], ('defined(SPAM)',)), - - (16, ' static char buffer[256];', None, ('! (defined(SPAM))',)), - (17, ' str_copy(buffer, char);', None, ('! (defined(SPAM))',)), - (18, '}', None, ('! (defined(SPAM))',)), - (19, 'void copy(char *buffer, *orig) {', None, ('! (defined(SPAM))',)), - (20, ' strcpy(buffer, orig);', None, ('! (defined(SPAM))',)), - (21, '}', None, ('! (defined(SPAM))',)), - - (22, '#endif', directives[2], ('! (defined(SPAM))',)), - ]) - self.check_calls( - ('_parse_directive', '#ifdef SPAM'), - ('_parse_directive', '#else'), - ('_parse_directive', '#endif'), - ) - - @unittest.skipIf(sys.platform == 'win32', 'needs fix under Windows') - def test_basic(self): - directives = [ - Include('<stdio.h>'), - IfDirective('ifdef', 'SPAM'), - IfDirective('if', '! defined(HAM) || !HAM'), - Constant('HAM', '0'), - IfDirective('elseif', 'HAM < 0'), - Constant('HAM', '-1'), - OtherDirective('else', None), - OtherDirective('endif', None), - OtherDirective('endif', None), - IfDirective('if', 'defined(HAM) && (HAM < 0 || ! HAM)'), - OtherDirective('undef', 'HAM'), - OtherDirective('endif', None), - IfDirective('ifndef', 'HAM'), - OtherDirective('endif', None), - ] - self.parsed = list(directives) - text = textwrap.dedent(r''' - #include <stdio.h> - print("begin"); - #ifdef SPAM - print("spam"); - #if ! defined(HAM) || !HAM - # DEFINE HAM 0 - #elseif HAM < 0 - # DEFINE HAM -1 - #else - print("ham HAM"); - #endif - #endif - - #if defined(HAM) && \ - (HAM < 0 || ! HAM) - print("ham?"); - #undef HAM - # endif - - #ifndef HAM - print("no ham"); - #endif - print("end"); - ''')[1:-1] - lines = [line + '\n' for line in text.splitlines()] - lines[-1] = lines[-1][:-1] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, '#include <stdio.h>\n', Include('<stdio.h>'), ()), - (2, 'print("begin");\n', None, ()), - # - (3, '#ifdef SPAM\n', - IfDirective('ifdef', 'SPAM'), - ()), - (4, ' print("spam");\n', - None, - ('defined(SPAM)',)), - (5, ' #if ! defined(HAM) || !HAM\n', - IfDirective('if', '! defined(HAM) || !HAM'), - ('defined(SPAM)',)), - (6, '# DEFINE HAM 0\n', - Constant('HAM', '0'), - ('defined(SPAM)', '! defined(HAM) || !HAM')), - (7, ' #elseif HAM < 0\n', - IfDirective('elseif', 'HAM < 0'), - ('defined(SPAM)', '! defined(HAM) || !HAM')), - (8, '# DEFINE HAM -1\n', - Constant('HAM', '-1'), - ('defined(SPAM)', '! (! defined(HAM) || !HAM)', 'HAM < 0')), - (9, ' #else\n', - OtherDirective('else', None), - ('defined(SPAM)', '! (! defined(HAM) || !HAM)', 'HAM < 0')), - (10, ' print("ham HAM");\n', - None, - ('defined(SPAM)', '! (! defined(HAM) || !HAM)', '! (HAM < 0)')), - (11, ' #endif\n', - OtherDirective('endif', None), - ('defined(SPAM)', '! (! defined(HAM) || !HAM)', '! (HAM < 0)')), - (12, '#endif\n', - OtherDirective('endif', None), - ('defined(SPAM)',)), - # - (13, '\n', None, ()), - # - (15, '#if defined(HAM) && (HAM < 0 || ! HAM)\n', - IfDirective('if', 'defined(HAM) && (HAM < 0 || ! HAM)'), - ()), - (16, ' print("ham?");\n', - None, - ('defined(HAM) && (HAM < 0 || ! HAM)',)), - (17, ' #undef HAM\n', - OtherDirective('undef', 'HAM'), - ('defined(HAM) && (HAM < 0 || ! HAM)',)), - (18, '# endif\n', - OtherDirective('endif', None), - ('defined(HAM) && (HAM < 0 || ! HAM)',)), - # - (19, '\n', None, ()), - # - (20, '#ifndef HAM\n', - IfDirective('ifndef', 'HAM'), - ()), - (21, ' print("no ham");\n', - None, - ('! defined(HAM)',)), - (22, '#endif\n', - OtherDirective('endif', None), - ('! defined(HAM)',)), - # - (23, 'print("end");', None, ()), - ]) - - @unittest.skipIf(sys.platform == 'win32', 'needs fix under Windows') - def test_typical(self): - # We use Include/compile.h from commit 66c4f3f38b86. It has - # a good enough mix of code without being too large. - directives = [ - IfDirective('ifndef', 'Py_COMPILE_H'), - Constant('Py_COMPILE_H', None), - - IfDirective('ifndef', 'Py_LIMITED_API'), - - Include('"code.h"'), - - IfDirective('ifdef', '__cplusplus'), - OtherDirective('endif', None), - - Constant('PyCF_MASK', '(CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)'), - Constant('PyCF_MASK_OBSOLETE', '(CO_NESTED)'), - Constant('PyCF_SOURCE_IS_UTF8', ' 0x0100'), - Constant('PyCF_DONT_IMPLY_DEDENT', '0x0200'), - Constant('PyCF_ONLY_AST', '0x0400'), - Constant('PyCF_IGNORE_COOKIE', '0x0800'), - Constant('PyCF_TYPE_COMMENTS', '0x1000'), - Constant('PyCF_ALLOW_TOP_LEVEL_AWAIT', '0x2000'), - - IfDirective('ifndef', 'Py_LIMITED_API'), - OtherDirective('endif', None), - - Constant('FUTURE_NESTED_SCOPES', '"nested_scopes"'), - Constant('FUTURE_GENERATORS', '"generators"'), - Constant('FUTURE_DIVISION', '"division"'), - Constant('FUTURE_ABSOLUTE_IMPORT', '"absolute_import"'), - Constant('FUTURE_WITH_STATEMENT', '"with_statement"'), - Constant('FUTURE_PRINT_FUNCTION', '"print_function"'), - Constant('FUTURE_UNICODE_LITERALS', '"unicode_literals"'), - Constant('FUTURE_BARRY_AS_BDFL', '"barry_as_FLUFL"'), - Constant('FUTURE_GENERATOR_STOP', '"generator_stop"'), - Constant('FUTURE_ANNOTATIONS', '"annotations"'), - - Macro('PyAST_Compile', ('mod', 's', 'f', 'ar'), 'PyAST_CompileEx(mod, s, f, -1, ar)'), - - Constant('PY_INVALID_STACK_EFFECT', 'INT_MAX'), - - IfDirective('ifdef', '__cplusplus'), - OtherDirective('endif', None), - - OtherDirective('endif', None), # ifndef Py_LIMITED_API - - Constant('Py_single_input', '256'), - Constant('Py_file_input', '257'), - Constant('Py_eval_input', '258'), - Constant('Py_func_type_input', '345'), - - OtherDirective('endif', None), # ifndef Py_COMPILE_H - ] - self.parsed = list(directives) - text = textwrap.dedent(r''' - #ifndef Py_COMPILE_H - #define Py_COMPILE_H - - #ifndef Py_LIMITED_API - #include "code.h" - - #ifdef __cplusplus - extern "C" { - #endif - - /* Public interface */ - struct _node; /* Declare the existence of this type */ - PyAPI_FUNC(PyCodeObject *) PyNode_Compile(struct _node *, const char *); - /* XXX (ncoghlan): Unprefixed type name in a public API! */ - - #define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | \ - CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | \ - CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | \ - CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS) - #define PyCF_MASK_OBSOLETE (CO_NESTED) - #define PyCF_SOURCE_IS_UTF8 0x0100 - #define PyCF_DONT_IMPLY_DEDENT 0x0200 - #define PyCF_ONLY_AST 0x0400 - #define PyCF_IGNORE_COOKIE 0x0800 - #define PyCF_TYPE_COMMENTS 0x1000 - #define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000 - - #ifndef Py_LIMITED_API - typedef struct { - int cf_flags; /* bitmask of CO_xxx flags relevant to future */ - int cf_feature_version; /* minor Python version (PyCF_ONLY_AST) */ - } PyCompilerFlags; - #endif - - /* Future feature support */ - - typedef struct { - int ff_features; /* flags set by future statements */ - int ff_lineno; /* line number of last future statement */ - } PyFutureFeatures; - - #define FUTURE_NESTED_SCOPES "nested_scopes" - #define FUTURE_GENERATORS "generators" - #define FUTURE_DIVISION "division" - #define FUTURE_ABSOLUTE_IMPORT "absolute_import" - #define FUTURE_WITH_STATEMENT "with_statement" - #define FUTURE_PRINT_FUNCTION "print_function" - #define FUTURE_UNICODE_LITERALS "unicode_literals" - #define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL" - #define FUTURE_GENERATOR_STOP "generator_stop" - #define FUTURE_ANNOTATIONS "annotations" - - struct _mod; /* Declare the existence of this type */ - #define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar) - PyAPI_FUNC(PyCodeObject *) PyAST_CompileEx( - struct _mod *mod, - const char *filename, /* decoded from the filesystem encoding */ - PyCompilerFlags *flags, - int optimize, - PyArena *arena); - PyAPI_FUNC(PyCodeObject *) PyAST_CompileObject( - struct _mod *mod, - PyObject *filename, - PyCompilerFlags *flags, - int optimize, - PyArena *arena); - PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromAST( - struct _mod * mod, - const char *filename /* decoded from the filesystem encoding */ - ); - PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromASTObject( - struct _mod * mod, - PyObject *filename - ); - - /* _Py_Mangle is defined in compile.c */ - PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name); - - #define PY_INVALID_STACK_EFFECT INT_MAX - PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg); - PyAPI_FUNC(int) PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump); - - PyAPI_FUNC(int) _PyAST_Optimize(struct _mod *, PyArena *arena, int optimize); - - #ifdef __cplusplus - } - #endif - - #endif /* !Py_LIMITED_API */ - - /* These definitions must match corresponding definitions in graminit.h. */ - #define Py_single_input 256 - #define Py_file_input 257 - #define Py_eval_input 258 - #define Py_func_type_input 345 - - #endif /* !Py_COMPILE_H */ - ''').strip() - lines = [line + '\n' for line in text.splitlines()] - lines[-1] = lines[-1][:-1] - - results = list( - iter_lines(lines, _parse_directive=self._parse_directive)) - - self.assertEqual(results, [ - (1, '#ifndef Py_COMPILE_H\n', - IfDirective('ifndef', 'Py_COMPILE_H'), - ()), - (2, '#define Py_COMPILE_H\n', - Constant('Py_COMPILE_H', None), - ('! defined(Py_COMPILE_H)',)), - (3, '\n', - None, - ('! defined(Py_COMPILE_H)',)), - (4, '#ifndef Py_LIMITED_API\n', - IfDirective('ifndef', 'Py_LIMITED_API'), - ('! defined(Py_COMPILE_H)',)), - (5, '#include "code.h"\n', - Include('"code.h"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (6, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (7, '#ifdef __cplusplus\n', - IfDirective('ifdef', '__cplusplus'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (8, 'extern "C" {\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', 'defined(__cplusplus)')), - (9, '#endif\n', - OtherDirective('endif', None), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', 'defined(__cplusplus)')), - (10, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (11, ' \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (12, 'struct _node; \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (13, 'PyAPI_FUNC(PyCodeObject *) PyNode_Compile(struct _node *, const char *);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (14, ' \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (15, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (19, '#define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)\n', - Constant('PyCF_MASK', '(CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (20, '#define PyCF_MASK_OBSOLETE (CO_NESTED)\n', - Constant('PyCF_MASK_OBSOLETE', '(CO_NESTED)'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (21, '#define PyCF_SOURCE_IS_UTF8 0x0100\n', - Constant('PyCF_SOURCE_IS_UTF8', ' 0x0100'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (22, '#define PyCF_DONT_IMPLY_DEDENT 0x0200\n', - Constant('PyCF_DONT_IMPLY_DEDENT', '0x0200'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (23, '#define PyCF_ONLY_AST 0x0400\n', - Constant('PyCF_ONLY_AST', '0x0400'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (24, '#define PyCF_IGNORE_COOKIE 0x0800\n', - Constant('PyCF_IGNORE_COOKIE', '0x0800'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (25, '#define PyCF_TYPE_COMMENTS 0x1000\n', - Constant('PyCF_TYPE_COMMENTS', '0x1000'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (26, '#define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000\n', - Constant('PyCF_ALLOW_TOP_LEVEL_AWAIT', '0x2000'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (27, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (28, '#ifndef Py_LIMITED_API\n', - IfDirective('ifndef', 'Py_LIMITED_API'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (29, 'typedef struct {\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')), - (30, ' int cf_flags; \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')), - (31, ' int cf_feature_version; \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')), - (32, '} PyCompilerFlags;\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')), - (33, '#endif\n', - OtherDirective('endif', None), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')), - (34, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (35, ' \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (36, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (37, 'typedef struct {\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (38, ' int ff_features; \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (39, ' int ff_lineno; \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (40, '} PyFutureFeatures;\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (41, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (42, '#define FUTURE_NESTED_SCOPES "nested_scopes"\n', - Constant('FUTURE_NESTED_SCOPES', '"nested_scopes"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (43, '#define FUTURE_GENERATORS "generators"\n', - Constant('FUTURE_GENERATORS', '"generators"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (44, '#define FUTURE_DIVISION "division"\n', - Constant('FUTURE_DIVISION', '"division"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (45, '#define FUTURE_ABSOLUTE_IMPORT "absolute_import"\n', - Constant('FUTURE_ABSOLUTE_IMPORT', '"absolute_import"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (46, '#define FUTURE_WITH_STATEMENT "with_statement"\n', - Constant('FUTURE_WITH_STATEMENT', '"with_statement"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (47, '#define FUTURE_PRINT_FUNCTION "print_function"\n', - Constant('FUTURE_PRINT_FUNCTION', '"print_function"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (48, '#define FUTURE_UNICODE_LITERALS "unicode_literals"\n', - Constant('FUTURE_UNICODE_LITERALS', '"unicode_literals"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (49, '#define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL"\n', - Constant('FUTURE_BARRY_AS_BDFL', '"barry_as_FLUFL"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (50, '#define FUTURE_GENERATOR_STOP "generator_stop"\n', - Constant('FUTURE_GENERATOR_STOP', '"generator_stop"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (51, '#define FUTURE_ANNOTATIONS "annotations"\n', - Constant('FUTURE_ANNOTATIONS', '"annotations"'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (52, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (53, 'struct _mod; \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (54, '#define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar)\n', - Macro('PyAST_Compile', ('mod', 's', 'f', 'ar'), 'PyAST_CompileEx(mod, s, f, -1, ar)'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (55, 'PyAPI_FUNC(PyCodeObject *) PyAST_CompileEx(\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (56, ' struct _mod *mod,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (57, ' const char *filename, \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (58, ' PyCompilerFlags *flags,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (59, ' int optimize,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (60, ' PyArena *arena);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (61, 'PyAPI_FUNC(PyCodeObject *) PyAST_CompileObject(\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (62, ' struct _mod *mod,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (63, ' PyObject *filename,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (64, ' PyCompilerFlags *flags,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (65, ' int optimize,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (66, ' PyArena *arena);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (67, 'PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromAST(\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (68, ' struct _mod * mod,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (69, ' const char *filename \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (70, ' );\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (71, 'PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromASTObject(\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (72, ' struct _mod * mod,\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (73, ' PyObject *filename\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (74, ' );\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (75, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (76, ' \n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (77, 'PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (78, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (79, '#define PY_INVALID_STACK_EFFECT INT_MAX\n', - Constant('PY_INVALID_STACK_EFFECT', 'INT_MAX'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (80, 'PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (81, 'PyAPI_FUNC(int) PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (82, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (83, 'PyAPI_FUNC(int) _PyAST_Optimize(struct _mod *, PyArena *arena, int optimize);\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (84, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (85, '#ifdef __cplusplus\n', - IfDirective('ifdef', '__cplusplus'), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (86, '}\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', 'defined(__cplusplus)')), - (87, '#endif\n', - OtherDirective('endif', None), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', 'defined(__cplusplus)')), - (88, '\n', - None, - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (89, '#endif \n', - OtherDirective('endif', None), - ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')), - (90, '\n', - None, - ('! defined(Py_COMPILE_H)',)), - (91, ' \n', - None, - ('! defined(Py_COMPILE_H)',)), - (92, '#define Py_single_input 256\n', - Constant('Py_single_input', '256'), - ('! defined(Py_COMPILE_H)',)), - (93, '#define Py_file_input 257\n', - Constant('Py_file_input', '257'), - ('! defined(Py_COMPILE_H)',)), - (94, '#define Py_eval_input 258\n', - Constant('Py_eval_input', '258'), - ('! defined(Py_COMPILE_H)',)), - (95, '#define Py_func_type_input 345\n', - Constant('Py_func_type_input', '345'), - ('! defined(Py_COMPILE_H)',)), - (96, '\n', - None, - ('! defined(Py_COMPILE_H)',)), - (97, '#endif ', - OtherDirective('endif', None), - ('! defined(Py_COMPILE_H)',)), - ]) - self.check_calls( - ('_parse_directive', '#ifndef Py_COMPILE_H'), - ('_parse_directive', '#define Py_COMPILE_H'), - ('_parse_directive', '#ifndef Py_LIMITED_API'), - ('_parse_directive', '#include "code.h"'), - ('_parse_directive', '#ifdef __cplusplus'), - ('_parse_directive', '#endif'), - ('_parse_directive', '#define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)'), - ('_parse_directive', '#define PyCF_MASK_OBSOLETE (CO_NESTED)'), - ('_parse_directive', '#define PyCF_SOURCE_IS_UTF8 0x0100'), - ('_parse_directive', '#define PyCF_DONT_IMPLY_DEDENT 0x0200'), - ('_parse_directive', '#define PyCF_ONLY_AST 0x0400'), - ('_parse_directive', '#define PyCF_IGNORE_COOKIE 0x0800'), - ('_parse_directive', '#define PyCF_TYPE_COMMENTS 0x1000'), - ('_parse_directive', '#define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000'), - ('_parse_directive', '#ifndef Py_LIMITED_API'), - ('_parse_directive', '#endif'), - ('_parse_directive', '#define FUTURE_NESTED_SCOPES "nested_scopes"'), - ('_parse_directive', '#define FUTURE_GENERATORS "generators"'), - ('_parse_directive', '#define FUTURE_DIVISION "division"'), - ('_parse_directive', '#define FUTURE_ABSOLUTE_IMPORT "absolute_import"'), - ('_parse_directive', '#define FUTURE_WITH_STATEMENT "with_statement"'), - ('_parse_directive', '#define FUTURE_PRINT_FUNCTION "print_function"'), - ('_parse_directive', '#define FUTURE_UNICODE_LITERALS "unicode_literals"'), - ('_parse_directive', '#define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL"'), - ('_parse_directive', '#define FUTURE_GENERATOR_STOP "generator_stop"'), - ('_parse_directive', '#define FUTURE_ANNOTATIONS "annotations"'), - ('_parse_directive', '#define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar)'), - ('_parse_directive', '#define PY_INVALID_STACK_EFFECT INT_MAX'), - ('_parse_directive', '#ifdef __cplusplus'), - ('_parse_directive', '#endif'), - ('_parse_directive', '#endif'), - ('_parse_directive', '#define Py_single_input 256'), - ('_parse_directive', '#define Py_file_input 257'), - ('_parse_directive', '#define Py_eval_input 258'), - ('_parse_directive', '#define Py_func_type_input 345'), - ('_parse_directive', '#endif'), - ) - - -class ParseDirectiveTests(unittest.TestCase): - - def test_directives(self): - tests = [ - # includes - ('#include "internal/pycore_pystate.h"', Include('"internal/pycore_pystate.h"')), - ('#include <stdio>', Include('<stdio>')), - - # defines - ('#define SPAM int', Constant('SPAM', 'int')), - ('#define SPAM', Constant('SPAM', '')), - ('#define SPAM(x, y) run(x, y)', Macro('SPAM', ('x', 'y'), 'run(x, y)')), - ('#undef SPAM', None), - - # conditionals - ('#if SPAM', IfDirective('if', 'SPAM')), - # XXX complex conditionls - ('#ifdef SPAM', IfDirective('ifdef', 'SPAM')), - ('#ifndef SPAM', IfDirective('ifndef', 'SPAM')), - ('#elseif SPAM', IfDirective('elseif', 'SPAM')), - # XXX complex conditionls - ('#else', OtherDirective('else', '')), - ('#endif', OtherDirective('endif', '')), - - # other - ('#error oops!', None), - ('#warning oops!', None), - ('#pragma ...', None), - ('#__FILE__ ...', None), - ('#__LINE__ ...', None), - ('#__DATE__ ...', None), - ('#__TIME__ ...', None), - ('#__TIMESTAMP__ ...', None), - - # extra whitespace - (' # include <stdio> ', Include('<stdio>')), - ('#else ', OtherDirective('else', '')), - ('#endif ', OtherDirective('endif', '')), - ('#define SPAM int ', Constant('SPAM', 'int')), - ('#define SPAM ', Constant('SPAM', '')), - ] - for line, expected in tests: - if expected is None: - kind, _, text = line[1:].partition(' ') - expected = OtherDirective(kind, text) - with self.subTest(line): - directive = parse_directive(line) - - self.assertEqual(directive, expected) - - def test_bad_directives(self): - tests = [ - # valid directives with bad text - '#define 123', - '#else spam', - '#endif spam', - ] - for kind in PreprocessorDirective.KINDS: - # missing leading "#" - tests.append(kind) - if kind in ('else', 'endif'): - continue - # valid directives with missing text - tests.append('#' + kind) - tests.append('#' + kind + ' ') - for line in tests: - with self.subTest(line): - with self.assertRaises(ValueError): - parse_directive(line) - - def test_not_directives(self): - tests = [ - '', - ' ', - 'directive', - 'directive?', - '???', - ] - for line in tests: - with self.subTest(line): - with self.assertRaises(ValueError): - parse_directive(line) - - -class ConstantTests(unittest.TestCase): - - def test_type(self): - directive = Constant('SPAM', '123') - - self.assertIs(type(directive), Constant) - self.assertIsInstance(directive, PreprocessorDirective) - - def test_attrs(self): - d = Constant('SPAM', '123') - kind, name, value = d.kind, d.name, d.value - - self.assertEqual(kind, 'define') - self.assertEqual(name, 'SPAM') - self.assertEqual(value, '123') - - def test_text(self): - tests = [ - (('SPAM', '123'), 'SPAM 123'), - (('SPAM',), 'SPAM'), - ] - for args, expected in tests: - with self.subTest(args): - d = Constant(*args) - text = d.text - - self.assertEqual(text, expected) - - def test_iter(self): - kind, name, value = Constant('SPAM', '123') - - self.assertEqual(kind, 'define') - self.assertEqual(name, 'SPAM') - self.assertEqual(value, '123') - - def test_defaults(self): - kind, name, value = Constant('SPAM') - - self.assertEqual(kind, 'define') - self.assertEqual(name, 'SPAM') - self.assertIs(value, None) - - def test_coerce(self): - tests = [] - # coerced name, value - for args in wrapped_arg_combos('SPAM', '123'): - tests.append((args, ('SPAM', '123'))) - # missing name, value - for name in ('', ' ', None, StrProxy(' '), ()): - for value in ('', ' ', None, StrProxy(' '), ()): - tests.append( - ((name, value), (None, None))) - # whitespace - tests.extend([ - ((' SPAM ', ' 123 '), ('SPAM', '123')), - ]) - - for args, expected in tests: - with self.subTest(args): - d = Constant(*args) - - self.assertEqual(d[1:], expected) - for i, exp in enumerate(expected, start=1): - if exp is not None: - self.assertIs(type(d[i]), str) - - def test_valid(self): - tests = [ - ('SPAM', '123'), - # unusual name - ('_SPAM_', '123'), - ('X_1', '123'), - # unusual value - ('SPAM', None), - ] - for args in tests: - with self.subTest(args): - directive = Constant(*args) - - directive.validate() - - def test_invalid(self): - tests = [ - # invalid name - ((None, '123'), TypeError), - (('_', '123'), ValueError), - (('1', '123'), ValueError), - (('_1_', '123'), ValueError), - # There is no invalid value (including None). - ] - for args, exctype in tests: - with self.subTest(args): - directive = Constant(*args) - - with self.assertRaises(exctype): - directive.validate() - - -class MacroTests(unittest.TestCase): - - def test_type(self): - directive = Macro('SPAM', ('x', 'y'), '123') - - self.assertIs(type(directive), Macro) - self.assertIsInstance(directive, PreprocessorDirective) - - def test_attrs(self): - d = Macro('SPAM', ('x', 'y'), '123') - kind, name, args, body = d.kind, d.name, d.args, d.body - - self.assertEqual(kind, 'define') - self.assertEqual(name, 'SPAM') - self.assertEqual(args, ('x', 'y')) - self.assertEqual(body, '123') - - def test_text(self): - tests = [ - (('SPAM', ('x', 'y'), '123'), 'SPAM(x, y) 123'), - (('SPAM', ('x', 'y'),), 'SPAM(x, y)'), - ] - for args, expected in tests: - with self.subTest(args): - d = Macro(*args) - text = d.text - - self.assertEqual(text, expected) - - def test_iter(self): - kind, name, args, body = Macro('SPAM', ('x', 'y'), '123') - - self.assertEqual(kind, 'define') - self.assertEqual(name, 'SPAM') - self.assertEqual(args, ('x', 'y')) - self.assertEqual(body, '123') - - def test_defaults(self): - kind, name, args, body = Macro('SPAM', ('x', 'y')) - - self.assertEqual(kind, 'define') - self.assertEqual(name, 'SPAM') - self.assertEqual(args, ('x', 'y')) - self.assertIs(body, None) - - def test_coerce(self): - tests = [] - # coerce name and body - for args in wrapped_arg_combos('SPAM', ('x', 'y'), '123'): - tests.append( - (args, ('SPAM', ('x', 'y'), '123'))) - # coerce args - tests.extend([ - (('SPAM', 'x', '123'), - ('SPAM', ('x',), '123')), - (('SPAM', 'x,y', '123'), - ('SPAM', ('x', 'y'), '123')), - ]) - # coerce arg names - for argnames in wrapped_arg_combos('x', 'y'): - tests.append( - (('SPAM', argnames, '123'), - ('SPAM', ('x', 'y'), '123'))) - # missing name, body - for name in ('', ' ', None, StrProxy(' '), ()): - for argnames in (None, ()): - for body in ('', ' ', None, StrProxy(' '), ()): - tests.append( - ((name, argnames, body), - (None, (), None))) - # missing args - tests.extend([ - (('SPAM', None, '123'), - ('SPAM', (), '123')), - (('SPAM', (), '123'), - ('SPAM', (), '123')), - ]) - # missing arg names - for arg in ('', ' ', None, StrProxy(' '), ()): - tests.append( - (('SPAM', (arg,), '123'), - ('SPAM', (None,), '123'))) - tests.extend([ - (('SPAM', ('x', '', 'z'), '123'), - ('SPAM', ('x', None, 'z'), '123')), - ]) - # whitespace - tests.extend([ - ((' SPAM ', (' x ', ' y '), ' 123 '), - ('SPAM', ('x', 'y'), '123')), - (('SPAM', 'x, y', '123'), - ('SPAM', ('x', 'y'), '123')), - ]) - - for args, expected in tests: - with self.subTest(args): - d = Macro(*args) - - self.assertEqual(d[1:], expected) - for i, exp in enumerate(expected, start=1): - if i == 2: - self.assertIs(type(d[i]), tuple) - elif exp is not None: - self.assertIs(type(d[i]), str) - - def test_init_bad_args(self): - tests = [ - ('SPAM', StrProxy('x'), '123'), - ('SPAM', object(), '123'), - ] - for args in tests: - with self.subTest(args): - with self.assertRaises(TypeError): - Macro(*args) - - def test_valid(self): - tests = [ - # unusual name - ('SPAM', ('x', 'y'), 'run(x, y)'), - ('_SPAM_', ('x', 'y'), 'run(x, y)'), - ('X_1', ('x', 'y'), 'run(x, y)'), - # unusual args - ('SPAM', (), 'run(x, y)'), - ('SPAM', ('_x_', 'y_1'), 'run(x, y)'), - ('SPAM', 'x', 'run(x, y)'), - ('SPAM', 'x, y', 'run(x, y)'), - # unusual body - ('SPAM', ('x', 'y'), None), - ] - for args in tests: - with self.subTest(args): - directive = Macro(*args) - - directive.validate() - - def test_invalid(self): - tests = [ - # invalid name - ((None, ('x', 'y'), '123'), TypeError), - (('_', ('x', 'y'), '123'), ValueError), - (('1', ('x', 'y'), '123'), ValueError), - (('_1', ('x', 'y'), '123'), ValueError), - # invalid args - (('SPAM', (None, 'y'), '123'), ValueError), - (('SPAM', ('x', '_'), '123'), ValueError), - (('SPAM', ('x', '1'), '123'), ValueError), - (('SPAM', ('x', '_1_'), '123'), ValueError), - # There is no invalid body (including None). - ] - for args, exctype in tests: - with self.subTest(args): - directive = Macro(*args) - - with self.assertRaises(exctype): - directive.validate() - - -class IfDirectiveTests(unittest.TestCase): - - def test_type(self): - directive = IfDirective('if', '1') - - self.assertIs(type(directive), IfDirective) - self.assertIsInstance(directive, PreprocessorDirective) - - def test_attrs(self): - d = IfDirective('if', '1') - kind, condition = d.kind, d.condition - - self.assertEqual(kind, 'if') - self.assertEqual(condition, '1') - #self.assertEqual(condition, (ArithmeticCondition('1'),)) - - def test_text(self): - tests = [ - (('if', 'defined(SPAM) && 1 || (EGGS > 3 && defined(HAM))'), - 'defined(SPAM) && 1 || (EGGS > 3 && defined(HAM))'), - ] - for kind in IfDirective.KINDS: - tests.append( - ((kind, 'SPAM'), 'SPAM')) - for args, expected in tests: - with self.subTest(args): - d = IfDirective(*args) - text = d.text - - self.assertEqual(text, expected) - - def test_iter(self): - kind, condition = IfDirective('if', '1') - - self.assertEqual(kind, 'if') - self.assertEqual(condition, '1') - #self.assertEqual(condition, (ArithmeticCondition('1'),)) - - #def test_complex_conditions(self): - # ... - - def test_coerce(self): - tests = [] - for kind in IfDirective.KINDS: - if kind == 'ifdef': - cond = 'defined(SPAM)' - elif kind == 'ifndef': - cond = '! defined(SPAM)' - else: - cond = 'SPAM' - for args in wrapped_arg_combos(kind, 'SPAM'): - tests.append((args, (kind, cond))) - tests.extend([ - ((' ' + kind + ' ', ' SPAM '), (kind, cond)), - ]) - for raw in ('', ' ', None, StrProxy(' '), ()): - tests.append(((kind, raw), (kind, None))) - for kind in ('', ' ', None, StrProxy(' '), ()): - tests.append(((kind, 'SPAM'), (None, 'SPAM'))) - for args, expected in tests: - with self.subTest(args): - d = IfDirective(*args) - - self.assertEqual(tuple(d), expected) - for i, exp in enumerate(expected): - if exp is not None: - self.assertIs(type(d[i]), str) - - def test_valid(self): - tests = [] - for kind in IfDirective.KINDS: - tests.extend([ - (kind, 'SPAM'), - (kind, '_SPAM_'), - (kind, 'X_1'), - (kind, '()'), - (kind, '--'), - (kind, '???'), - ]) - for args in tests: - with self.subTest(args): - directive = IfDirective(*args) - - directive.validate() - - def test_invalid(self): - tests = [] - # kind - tests.extend([ - ((None, 'SPAM'), TypeError), - (('_', 'SPAM'), ValueError), - (('-', 'SPAM'), ValueError), - (('spam', 'SPAM'), ValueError), - ]) - for kind in PreprocessorDirective.KINDS: - if kind in IfDirective.KINDS: - continue - tests.append( - ((kind, 'SPAM'), ValueError)) - # condition - for kind in IfDirective.KINDS: - tests.extend([ - ((kind, None), TypeError), - # Any other condition is valid. - ]) - for args, exctype in tests: - with self.subTest(args): - directive = IfDirective(*args) - - with self.assertRaises(exctype): - directive.validate() - - -class IncludeTests(unittest.TestCase): - - def test_type(self): - directive = Include('<stdio>') - - self.assertIs(type(directive), Include) - self.assertIsInstance(directive, PreprocessorDirective) - - def test_attrs(self): - d = Include('<stdio>') - kind, file, text = d.kind, d.file, d.text - - self.assertEqual(kind, 'include') - self.assertEqual(file, '<stdio>') - self.assertEqual(text, '<stdio>') - - def test_iter(self): - kind, file = Include('<stdio>') - - self.assertEqual(kind, 'include') - self.assertEqual(file, '<stdio>') - - def test_coerce(self): - tests = [] - for arg, in wrapped_arg_combos('<stdio>'): - tests.append((arg, '<stdio>')) - tests.extend([ - (' <stdio> ', '<stdio>'), - ]) - for arg in ('', ' ', None, StrProxy(' '), ()): - tests.append((arg, None )) - for arg, expected in tests: - with self.subTest(arg): - _, file = Include(arg) - - self.assertEqual(file, expected) - if expected is not None: - self.assertIs(type(file), str) - - def test_valid(self): - tests = [ - '<stdio>', - '"spam.h"', - '"internal/pycore_pystate.h"', - ] - for arg in tests: - with self.subTest(arg): - directive = Include(arg) - - directive.validate() - - def test_invalid(self): - tests = [ - (None, TypeError), - # We currently don't check the file. - ] - for arg, exctype in tests: - with self.subTest(arg): - directive = Include(arg) - - with self.assertRaises(exctype): - directive.validate() - - -class OtherDirectiveTests(unittest.TestCase): - - def test_type(self): - directive = OtherDirective('undef', 'SPAM') - - self.assertIs(type(directive), OtherDirective) - self.assertIsInstance(directive, PreprocessorDirective) - - def test_attrs(self): - d = OtherDirective('undef', 'SPAM') - kind, text = d.kind, d.text - - self.assertEqual(kind, 'undef') - self.assertEqual(text, 'SPAM') - - def test_iter(self): - kind, text = OtherDirective('undef', 'SPAM') - - self.assertEqual(kind, 'undef') - self.assertEqual(text, 'SPAM') - - def test_coerce(self): - tests = [] - for kind in OtherDirective.KINDS: - if kind in ('else', 'endif'): - continue - for args in wrapped_arg_combos(kind, '...'): - tests.append((args, (kind, '...'))) - tests.extend([ - ((' ' + kind + ' ', ' ... '), (kind, '...')), - ]) - for raw in ('', ' ', None, StrProxy(' '), ()): - tests.append(((kind, raw), (kind, None))) - for kind in ('else', 'endif'): - for args in wrapped_arg_combos(kind, None): - tests.append((args, (kind, None))) - tests.extend([ - ((' ' + kind + ' ', None), (kind, None)), - ]) - for kind in ('', ' ', None, StrProxy(' '), ()): - tests.append(((kind, '...'), (None, '...'))) - for args, expected in tests: - with self.subTest(args): - d = OtherDirective(*args) - - self.assertEqual(tuple(d), expected) - for i, exp in enumerate(expected): - if exp is not None: - self.assertIs(type(d[i]), str) - - def test_valid(self): - tests = [] - for kind in OtherDirective.KINDS: - if kind in ('else', 'endif'): - continue - tests.extend([ - (kind, '...'), - (kind, '???'), - (kind, 'SPAM'), - (kind, '1 + 1'), - ]) - for kind in ('else', 'endif'): - tests.append((kind, None)) - for args in tests: - with self.subTest(args): - directive = OtherDirective(*args) - - directive.validate() - - def test_invalid(self): - tests = [] - # kind - tests.extend([ - ((None, '...'), TypeError), - (('_', '...'), ValueError), - (('-', '...'), ValueError), - (('spam', '...'), ValueError), - ]) - for kind in PreprocessorDirective.KINDS: - if kind in OtherDirective.KINDS: - continue - tests.append( - ((kind, None), ValueError)) - # text - for kind in OtherDirective.KINDS: - if kind in ('else', 'endif'): - tests.extend([ - # Any text is invalid. - ((kind, 'SPAM'), ValueError), - ((kind, '...'), ValueError), - ]) - else: - tests.extend([ - ((kind, None), TypeError), - # Any other text is valid. - ]) - for args, exctype in tests: - with self.subTest(args): - directive = OtherDirective(*args) - - with self.assertRaises(exctype): - directive.validate() diff --git a/Lib/test/test_tools/test_c_analyzer/test_symbols/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_symbols/__init__.py deleted file mode 100644 index bc502ef..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_symbols/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os.path -from test.support import load_package_tests - - -def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tools/test_c_analyzer/test_symbols/test_info.py b/Lib/test/test_tools/test_c_analyzer/test_symbols/test_info.py deleted file mode 100644 index 1282a89..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_symbols/test_info.py +++ /dev/null @@ -1,192 +0,0 @@ -import string -import unittest - -from ..util import PseudoStr, StrProxy, Object -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.common.info import ID - from c_analyzer.symbols.info import Symbol - - -class SymbolTests(unittest.TestCase): - - VALID_ARGS = ( - ID('x/y/z/spam.c', 'func', 'eggs'), - Symbol.KIND.VARIABLE, - False, - ) - VALID_KWARGS = dict(zip(Symbol._fields, VALID_ARGS)) - VALID_EXPECTED = VALID_ARGS - - def test_init_typical_binary_local(self): - id = ID(None, None, 'spam') - symbol = Symbol( - id=id, - kind=Symbol.KIND.VARIABLE, - external=False, - ) - - self.assertEqual(symbol, ( - id, - Symbol.KIND.VARIABLE, - False, - )) - - def test_init_typical_binary_global(self): - id = ID('Python/ceval.c', None, 'spam') - symbol = Symbol( - id=id, - kind=Symbol.KIND.VARIABLE, - external=False, - ) - - self.assertEqual(symbol, ( - id, - Symbol.KIND.VARIABLE, - False, - )) - - def test_init_coercion(self): - tests = [ - ('str subclass', - dict( - id=PseudoStr('eggs'), - kind=PseudoStr('variable'), - external=0, - ), - (ID(None, None, 'eggs'), - Symbol.KIND.VARIABLE, - False, - )), - ('with filename', - dict( - id=('x/y/z/spam.c', 'eggs'), - kind=PseudoStr('variable'), - external=0, - ), - (ID('x/y/z/spam.c', None, 'eggs'), - Symbol.KIND.VARIABLE, - False, - )), - ('non-str 1', - dict( - id=('a', 'b', 'c'), - kind=StrProxy('variable'), - external=0, - ), - (ID('a', 'b', 'c'), - Symbol.KIND.VARIABLE, - False, - )), - ('non-str 2', - dict( - id=('a', 'b', 'c'), - kind=Object(), - external=0, - ), - (ID('a', 'b', 'c'), - '<object>', - False, - )), - ] - for summary, kwargs, expected in tests: - with self.subTest(summary): - symbol = Symbol(**kwargs) - - for field in Symbol._fields: - value = getattr(symbol, field) - if field == 'external': - self.assertIs(type(value), bool) - elif field == 'id': - self.assertIs(type(value), ID) - else: - self.assertIs(type(value), str) - self.assertEqual(tuple(symbol), expected) - - def test_init_all_missing(self): - id = ID(None, None, 'spam') - - symbol = Symbol(id) - - self.assertEqual(symbol, ( - id, - Symbol.KIND.VARIABLE, - None, - )) - - def test_fields(self): - id = ID('z', 'x', 'a') - - symbol = Symbol(id, 'b', False) - - self.assertEqual(symbol.id, id) - self.assertEqual(symbol.kind, 'b') - self.assertIs(symbol.external, False) - - def test___getattr__(self): - id = ID('z', 'x', 'a') - symbol = Symbol(id, 'b', False) - - filename = symbol.filename - funcname = symbol.funcname - name = symbol.name - - self.assertEqual(filename, 'z') - self.assertEqual(funcname, 'x') - self.assertEqual(name, 'a') - - def test_validate_typical(self): - id = ID('z', 'x', 'a') - - symbol = Symbol( - id=id, - kind=Symbol.KIND.VARIABLE, - external=False, - ) - - symbol.validate() # This does not fail. - - def test_validate_missing_field(self): - for field in Symbol._fields: - with self.subTest(field): - symbol = Symbol(**self.VALID_KWARGS) - symbol = symbol._replace(**{field: None}) - - with self.assertRaises(TypeError): - symbol.validate() - - def test_validate_bad_field(self): - badch = tuple(c for c in string.punctuation + string.digits) - notnames = ( - '1a', - 'a.b', - 'a-b', - '&a', - 'a++', - ) + badch - tests = [ - ('id', notnames), - ('kind', ('bogus',)), - ] - seen = set() - for field, invalid in tests: - for value in invalid: - if field != 'kind': - seen.add(value) - with self.subTest(f'{field}={value!r}'): - symbol = Symbol(**self.VALID_KWARGS) - symbol = symbol._replace(**{field: value}) - - with self.assertRaises(ValueError): - symbol.validate() - - for field, invalid in tests: - if field == 'kind': - continue - valid = seen - set(invalid) - for value in valid: - with self.subTest(f'{field}={value!r}'): - symbol = Symbol(**self.VALID_KWARGS) - symbol = symbol._replace(**{field: value}) - - symbol.validate() # This does not fail. diff --git a/Lib/test/test_tools/test_c_analyzer/test_variables/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_variables/__init__.py deleted file mode 100644 index bc502ef..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_variables/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os.path -from test.support import load_package_tests - - -def load_tests(*args): - return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tools/test_c_analyzer/test_variables/test_find.py b/Lib/test/test_tools/test_c_analyzer/test_variables/test_find.py deleted file mode 100644 index 7a13cf3..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_variables/test_find.py +++ /dev/null @@ -1,124 +0,0 @@ -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.variables import info - from c_analyzer.variables.find import ( - vars_from_binary, - ) - - -class _Base(unittest.TestCase): - - maxDiff = None - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - - -class VarsFromBinaryTests(_Base): - - _return_iter_vars = () - _return_get_symbol_resolver = None - - def setUp(self): - super().setUp() - - self.kwargs = dict( - _iter_vars=self._iter_vars, - _get_symbol_resolver=self._get_symbol_resolver, - ) - - def _iter_vars(self, binfile, resolve, handle_id): - self.calls.append(('_iter_vars', (binfile, resolve, handle_id))) - return [(v, v.id) for v in self._return_iter_vars] - - def _get_symbol_resolver(self, known=None, dirnames=(), *, - handle_var, - filenames=None, - check_filename=None, - perfilecache=None, - ): - self.calls.append(('_get_symbol_resolver', - (known, dirnames, handle_var, filenames, - check_filename, perfilecache))) - return self._return_get_symbol_resolver - - def test_typical(self): - resolver = self._return_get_symbol_resolver = object() - variables = self._return_iter_vars = [ - info.Variable.from_parts('dir1/spam.c', None, 'var1', 'int'), - info.Variable.from_parts('dir1/spam.c', None, 'var2', 'static int'), - info.Variable.from_parts('dir1/spam.c', None, 'var3', 'char *'), - info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', 'const char *'), - info.Variable.from_parts('dir1/eggs.c', None, 'var1', 'static int'), - info.Variable.from_parts('dir1/eggs.c', 'func1', 'var2', 'static char *'), - ] - known = object() - filenames = object() - - found = list(vars_from_binary('python', - known=known, - filenames=filenames, - **self.kwargs)) - - self.assertEqual(found, [ - info.Variable.from_parts('dir1/spam.c', None, 'var1', 'int'), - info.Variable.from_parts('dir1/spam.c', None, 'var2', 'static int'), - info.Variable.from_parts('dir1/spam.c', None, 'var3', 'char *'), - info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', 'const char *'), - info.Variable.from_parts('dir1/eggs.c', None, 'var1', 'static int'), - info.Variable.from_parts('dir1/eggs.c', 'func1', 'var2', 'static char *'), - ]) - self.assertEqual(self.calls, [ - ('_get_symbol_resolver', (filenames, known, info.Variable.from_id, None, None, {})), - ('_iter_vars', ('python', resolver, None)), - ]) - -# self._return_iter_symbols = [ -# s_info.Symbol(('dir1/spam.c', None, 'var1'), 'variable', False), -# s_info.Symbol(('dir1/spam.c', None, 'var2'), 'variable', False), -# s_info.Symbol(('dir1/spam.c', None, 'func1'), 'function', False), -# s_info.Symbol(('dir1/spam.c', None, 'func2'), 'function', True), -# s_info.Symbol(('dir1/spam.c', None, 'var3'), 'variable', False), -# s_info.Symbol(('dir1/spam.c', 'func2', 'var4'), 'variable', False), -# s_info.Symbol(('dir1/ham.c', None, 'var1'), 'variable', True), -# s_info.Symbol(('dir1/eggs.c', None, 'var1'), 'variable', False), -# s_info.Symbol(('dir1/eggs.c', None, 'xyz'), 'other', False), -# s_info.Symbol(('dir1/eggs.c', '???', 'var2'), 'variable', False), -# s_info.Symbol(('???', None, 'var_x'), 'variable', False), -# s_info.Symbol(('???', '???', 'var_y'), 'variable', False), -# s_info.Symbol((None, None, '???'), 'other', False), -# ] -# known = object() -# -# vars_from_binary('python', knownvars=known, **this.kwargs) -# found = list(globals_from_symbols(['dir1'], self.iter_symbols)) -# -# self.assertEqual(found, [ -# info.Variable.from_parts('dir1/spam.c', None, 'var1', '???'), -# info.Variable.from_parts('dir1/spam.c', None, 'var2', '???'), -# info.Variable.from_parts('dir1/spam.c', None, 'var3', '???'), -# info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', '???'), -# info.Variable.from_parts('dir1/eggs.c', None, 'var1', '???'), -# ]) -# self.assertEqual(self.calls, [ -# ('iter_symbols', (['dir1'],)), -# ]) -# -# def test_no_symbols(self): -# self._return_iter_symbols = [] -# -# found = list(globals_from_symbols(['dir1'], self.iter_symbols)) -# -# self.assertEqual(found, []) -# self.assertEqual(self.calls, [ -# ('iter_symbols', (['dir1'],)), -# ]) - - # XXX need functional test diff --git a/Lib/test/test_tools/test_c_analyzer/test_variables/test_info.py b/Lib/test/test_tools/test_c_analyzer/test_variables/test_info.py deleted file mode 100644 index d424d8e..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_variables/test_info.py +++ /dev/null @@ -1,244 +0,0 @@ -import string -import unittest - -from ..util import PseudoStr, StrProxy, Object -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.common.info import UNKNOWN, ID - from c_analyzer.variables.info import ( - normalize_vartype, Variable - ) - - -class NormalizeVartypeTests(unittest.TestCase): - - def test_basic(self): - tests = [ - (None, None), - ('', ''), - ('int', 'int'), - (PseudoStr('int'), 'int'), - (StrProxy('int'), 'int'), - ] - for vartype, expected in tests: - with self.subTest(vartype): - normalized = normalize_vartype(vartype) - - self.assertEqual(normalized, expected) - - -class VariableTests(unittest.TestCase): - - VALID_ARGS = ( - ('x/y/z/spam.c', 'func', 'eggs'), - 'static', - 'int', - ) - VALID_KWARGS = dict(zip(Variable._fields, VALID_ARGS)) - VALID_EXPECTED = VALID_ARGS - - def test_init_typical_global(self): - for storage in ('static', 'extern', 'implicit'): - with self.subTest(storage): - static = Variable( - id=ID( - filename='x/y/z/spam.c', - funcname=None, - name='eggs', - ), - storage=storage, - vartype='int', - ) - - self.assertEqual(static, ( - ('x/y/z/spam.c', None, 'eggs'), - storage, - 'int', - )) - - def test_init_typical_local(self): - for storage in ('static', 'local'): - with self.subTest(storage): - static = Variable( - id=ID( - filename='x/y/z/spam.c', - funcname='func', - name='eggs', - ), - storage=storage, - vartype='int', - ) - - self.assertEqual(static, ( - ('x/y/z/spam.c', 'func', 'eggs'), - storage, - 'int', - )) - - def test_init_all_missing(self): - for value in ('', None): - with self.subTest(repr(value)): - static = Variable( - id=value, - storage=value, - vartype=value, - ) - - self.assertEqual(static, ( - None, - None, - None, - )) - - def test_init_all_coerced(self): - id = ID('x/y/z/spam.c', 'func', 'spam') - tests = [ - ('str subclass', - dict( - id=( - PseudoStr('x/y/z/spam.c'), - PseudoStr('func'), - PseudoStr('spam'), - ), - storage=PseudoStr('static'), - vartype=PseudoStr('int'), - ), - (id, - 'static', - 'int', - )), - ('non-str 1', - dict( - id=id, - storage=Object(), - vartype=Object(), - ), - (id, - '<object>', - '<object>', - )), - ('non-str 2', - dict( - id=id, - storage=StrProxy('static'), - vartype=StrProxy('variable'), - ), - (id, - 'static', - 'variable', - )), - ('non-str', - dict( - id=id, - storage=('a', 'b', 'c'), - vartype=('x', 'y', 'z'), - ), - (id, - "('a', 'b', 'c')", - "('x', 'y', 'z')", - )), - ] - for summary, kwargs, expected in tests: - with self.subTest(summary): - static = Variable(**kwargs) - - for field in Variable._fields: - value = getattr(static, field) - if field == 'id': - self.assertIs(type(value), ID) - else: - self.assertIs(type(value), str) - self.assertEqual(tuple(static), expected) - - def test_iterable(self): - static = Variable(**self.VALID_KWARGS) - - id, storage, vartype = static - - values = (id, storage, vartype) - for value, expected in zip(values, self.VALID_EXPECTED): - self.assertEqual(value, expected) - - def test_fields(self): - static = Variable(('a', 'b', 'z'), 'x', 'y') - - self.assertEqual(static.id, ('a', 'b', 'z')) - self.assertEqual(static.storage, 'x') - self.assertEqual(static.vartype, 'y') - - def test___getattr__(self): - static = Variable(('a', 'b', 'z'), 'x', 'y') - - self.assertEqual(static.filename, 'a') - self.assertEqual(static.funcname, 'b') - self.assertEqual(static.name, 'z') - - def test_validate_typical(self): - validstorage = ('static', 'extern', 'implicit', 'local') - self.assertEqual(set(validstorage), set(Variable.STORAGE)) - - for storage in validstorage: - with self.subTest(storage): - static = Variable( - id=ID( - filename='x/y/z/spam.c', - funcname='func', - name='eggs', - ), - storage=storage, - vartype='int', - ) - - static.validate() # This does not fail. - - def test_validate_missing_field(self): - for field in Variable._fields: - with self.subTest(field): - static = Variable(**self.VALID_KWARGS) - static = static._replace(**{field: None}) - - with self.assertRaises(TypeError): - static.validate() - for field in ('storage', 'vartype'): - with self.subTest(field): - static = Variable(**self.VALID_KWARGS) - static = static._replace(**{field: UNKNOWN}) - - with self.assertRaises(TypeError): - static.validate() - - def test_validate_bad_field(self): - badch = tuple(c for c in string.punctuation + string.digits) - notnames = ( - '1a', - 'a.b', - 'a-b', - '&a', - 'a++', - ) + badch - tests = [ - ('id', ()), # Any non-empty str is okay. - ('storage', ('external', 'global') + notnames), - ('vartype', ()), # Any non-empty str is okay. - ] - seen = set() - for field, invalid in tests: - for value in invalid: - seen.add(value) - with self.subTest(f'{field}={value!r}'): - static = Variable(**self.VALID_KWARGS) - static = static._replace(**{field: value}) - - with self.assertRaises(ValueError): - static.validate() - - for field, invalid in tests: - if field == 'id': - continue - valid = seen - set(invalid) - for value in valid: - with self.subTest(f'{field}={value!r}'): - static = Variable(**self.VALID_KWARGS) - static = static._replace(**{field: value}) - - static.validate() # This does not fail. diff --git a/Lib/test/test_tools/test_c_analyzer/test_variables/test_known.py b/Lib/test/test_tools/test_c_analyzer/test_variables/test_known.py deleted file mode 100644 index 49ff45c..0000000 --- a/Lib/test/test_tools/test_c_analyzer/test_variables/test_known.py +++ /dev/null @@ -1,139 +0,0 @@ -import re -import textwrap -import unittest - -from .. import tool_imports_for_tests -with tool_imports_for_tests(): - from c_analyzer.common.info import ID - from c_analyzer.variables.info import Variable - from c_analyzer.variables.known import ( - read_file, - from_file, - ) - -class _BaseTests(unittest.TestCase): - - maxDiff = None - - @property - def calls(self): - try: - return self._calls - except AttributeError: - self._calls = [] - return self._calls - - -class ReadFileTests(_BaseTests): - - _return_read_tsv = () - - def _read_tsv(self, *args): - self.calls.append(('_read_tsv', args)) - return self._return_read_tsv - - def test_typical(self): - lines = textwrap.dedent(''' - filename funcname name kind declaration - file1.c - var1 variable static int - file1.c func1 local1 variable static int - file1.c - var2 variable int - file1.c func2 local2 variable char * - file2.c - var1 variable char * - ''').strip().splitlines() - lines = [re.sub(r'\s+', '\t', line, 4) for line in lines] - self._return_read_tsv = [tuple(v.strip() for v in line.split('\t')) - for line in lines[1:]] - - known = list(read_file('known.tsv', _read_tsv=self._read_tsv)) - - self.assertEqual(known, [ - ('variable', ID('file1.c', '', 'var1'), 'static int'), - ('variable', ID('file1.c', 'func1', 'local1'), 'static int'), - ('variable', ID('file1.c', '', 'var2'), 'int'), - ('variable', ID('file1.c', 'func2', 'local2'), 'char *'), - ('variable', ID('file2.c', '', 'var1'), 'char *'), - ]) - self.assertEqual(self.calls, [ - ('_read_tsv', - ('known.tsv', 'filename\tfuncname\tname\tkind\tdeclaration')), - ]) - - def test_empty(self): - self._return_read_tsv = [] - - known = list(read_file('known.tsv', _read_tsv=self._read_tsv)) - - self.assertEqual(known, []) - self.assertEqual(self.calls, [ - ('_read_tsv', ('known.tsv', 'filename\tfuncname\tname\tkind\tdeclaration')), - ]) - - -class FromFileTests(_BaseTests): - - _return_read_file = () - _return_handle_var = () - - def _read_file(self, infile): - self.calls.append(('_read_file', (infile,))) - return iter(self._return_read_file) - - def _handle_var(self, varid, decl): - self.calls.append(('_handle_var', (varid, decl))) - var = self._return_handle_var.pop(0) - return var - - def test_typical(self): - expected = [ - Variable.from_parts('file1.c', '', 'var1', 'static int'), - Variable.from_parts('file1.c', 'func1', 'local1', 'static int'), - Variable.from_parts('file1.c', '', 'var2', 'int'), - Variable.from_parts('file1.c', 'func2', 'local2', 'char *'), - Variable.from_parts('file2.c', '', 'var1', 'char *'), - ] - self._return_read_file = [('variable', v.id, v.vartype) - for v in expected] -# ('variable', ID('file1.c', '', 'var1'), 'static int'), -# ('variable', ID('file1.c', 'func1', 'local1'), 'static int'), -# ('variable', ID('file1.c', '', 'var2'), 'int'), -# ('variable', ID('file1.c', 'func2', 'local2'), 'char *'), -# ('variable', ID('file2.c', '', 'var1'), 'char *'), -# ] - self._return_handle_var = list(expected) # a copy - - known = from_file('known.tsv', - handle_var=self._handle_var, - _read_file=self._read_file, - ) - - self.assertEqual(known, { - 'variables': {v.id: v for v in expected}, - }) -# Variable.from_parts('file1.c', '', 'var1', 'static int'), -# Variable.from_parts('file1.c', 'func1', 'local1', 'static int'), -# Variable.from_parts('file1.c', '', 'var2', 'int'), -# Variable.from_parts('file1.c', 'func2', 'local2', 'char *'), -# Variable.from_parts('file2.c', '', 'var1', 'char *'), -# ]}, -# }) - self.assertEqual(self.calls, [ - ('_read_file', ('known.tsv',)), - *[('_handle_var', (v.id, v.vartype)) - for v in expected], - ]) - - def test_empty(self): - self._return_read_file = [] - - known = from_file('known.tsv', - handle_var=self._handle_var, - _read_file=self._read_file, - ) - - self.assertEqual(known, { - 'variables': {}, - }) - self.assertEqual(self.calls, [ - ('_read_file', ('known.tsv',)), - ]) diff --git a/Lib/test/test_tools/test_c_analyzer/util.py b/Lib/test/test_tools/test_c_analyzer/util.py deleted file mode 100644 index ba73b0a..0000000 --- a/Lib/test/test_tools/test_c_analyzer/util.py +++ /dev/null @@ -1,60 +0,0 @@ -import itertools - - -class PseudoStr(str): - pass - - -class StrProxy: - def __init__(self, value): - self.value = value - def __str__(self): - return self.value - def __bool__(self): - return bool(self.value) - - -class Object: - def __repr__(self): - return '<object>' - - -def wrapped_arg_combos(*args, - wrappers=(PseudoStr, StrProxy), - skip=(lambda w, i, v: not isinstance(v, str)), - ): - """Yield every possible combination of wrapped items for the given args. - - Effectively, the wrappers are applied to the args according to the - powerset of the args indicies. So the result includes the args - completely unwrapped. - - If "skip" is supplied (default is to skip all non-str values) and - it returns True for a given arg index/value then that arg will - remain unwrapped, - - Only unique results are returned. If an arg was skipped for one - of the combinations then it could end up matching one of the other - combinations. In that case only one of them will be yielded. - """ - if not args: - return - indices = list(range(len(args))) - # The powerset (from recipe in the itertools docs). - combos = itertools.chain.from_iterable(itertools.combinations(indices, r) - for r in range(len(indices)+1)) - seen = set() - for combo in combos: - for wrap in wrappers: - indexes = [] - applied = list(args) - for i in combo: - arg = args[i] - if skip and skip(wrap, i, arg): - continue - indexes.append(i) - applied[i] = wrap(arg) - key = (wrap, tuple(indexes)) - if key not in seen: - yield tuple(applied) - seen.add(key) |