first commit
This commit is contained in:
@ -0,0 +1,37 @@
|
||||
"""Support functions for testing scripts in the Tools directory."""
|
||||
import contextlib
|
||||
import importlib
|
||||
import os.path
|
||||
import unittest
|
||||
from test import support
|
||||
|
||||
basepath = os.path.normpath(
|
||||
os.path.dirname( # <src/install dir>
|
||||
os.path.dirname( # Lib
|
||||
os.path.dirname( # test
|
||||
os.path.dirname(__file__))))) # test_tools
|
||||
|
||||
toolsdir = os.path.join(basepath, 'Tools')
|
||||
scriptsdir = os.path.join(toolsdir, 'scripts')
|
||||
|
||||
def skip_if_missing(tool=None):
|
||||
if tool:
|
||||
tooldir = os.path.join(toolsdir, tool)
|
||||
else:
|
||||
tool = 'scripts'
|
||||
tooldir = scriptsdir
|
||||
if not os.path.isdir(tooldir):
|
||||
raise unittest.SkipTest(f'{tool} directory could not be found')
|
||||
|
||||
@contextlib.contextmanager
|
||||
def imports_under_tool(name, *subdirs):
|
||||
tooldir = os.path.join(toolsdir, name, *subdirs)
|
||||
with support.DirsOnSysPath(tooldir) as cm:
|
||||
yield cm
|
||||
|
||||
def import_tool(toolname):
|
||||
with support.DirsOnSysPath(scriptsdir):
|
||||
return importlib.import_module(toolname)
|
||||
|
||||
def load_tests(*args):
|
||||
return support.load_package_tests(os.path.dirname(__file__), *args)
|
||||
@ -0,0 +1,4 @@
|
||||
from test.test_tools import load_tests
|
||||
import unittest
|
||||
|
||||
unittest.main()
|
||||
@ -0,0 +1,15 @@
|
||||
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)
|
||||
@ -0,0 +1,5 @@
|
||||
from . import load_tests
|
||||
import unittest
|
||||
|
||||
|
||||
unittest.main()
|
||||
@ -0,0 +1,6 @@
|
||||
import os.path
|
||||
from test.support import load_package_tests
|
||||
|
||||
|
||||
def load_tests(*args):
|
||||
return load_package_tests(os.path.dirname(__file__), *args)
|
||||
@ -0,0 +1,470 @@
|
||||
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',)),
|
||||
# ])
|
||||
@ -0,0 +1,197 @@
|
||||
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.
|
||||
@ -0,0 +1,54 @@
|
||||
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, [])
|
||||
@ -0,0 +1,6 @@
|
||||
import os.path
|
||||
from test.support import load_package_tests
|
||||
|
||||
|
||||
def load_tests(*args):
|
||||
return load_package_tests(os.path.dirname(__file__), *args)
|
||||
@ -0,0 +1,296 @@
|
||||
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),
|
||||
])
|
||||
@ -0,0 +1,34 @@
|
||||
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
|
||||
@ -0,0 +1,98 @@
|
||||
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')),
|
||||
])
|
||||
@ -0,0 +1,6 @@
|
||||
import os.path
|
||||
from test.support import load_package_tests
|
||||
|
||||
|
||||
def load_tests(*args):
|
||||
return load_package_tests(os.path.dirname(__file__), *args)
|
||||
@ -0,0 +1,795 @@
|
||||
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>',)),
|
||||
])
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@
|
||||
import os.path
|
||||
from test.support import load_package_tests
|
||||
|
||||
|
||||
def load_tests(*args):
|
||||
return load_package_tests(os.path.dirname(__file__), *args)
|
||||
@ -0,0 +1,192 @@
|
||||
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.
|
||||
@ -0,0 +1,6 @@
|
||||
import os.path
|
||||
from test.support import load_package_tests
|
||||
|
||||
|
||||
def load_tests(*args):
|
||||
return load_package_tests(os.path.dirname(__file__), *args)
|
||||
@ -0,0 +1,124 @@
|
||||
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
|
||||
@ -0,0 +1,244 @@
|
||||
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.
|
||||
@ -0,0 +1,139 @@
|
||||
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',)),
|
||||
])
|
||||
@ -0,0 +1,60 @@
|
||||
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)
|
||||
@ -0,0 +1,92 @@
|
||||
'''Test Tools/scripts/fixcid.py.'''
|
||||
|
||||
from io import StringIO
|
||||
import os, os.path
|
||||
import runpy
|
||||
import sys
|
||||
from test import support
|
||||
from test.test_tools import skip_if_missing, scriptsdir
|
||||
import unittest
|
||||
|
||||
skip_if_missing()
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
def test_parse_strings(self):
|
||||
old1 = 'int xx = "xx\\"xx"[xx];\n'
|
||||
old2 = "int xx = 'x\\'xx' + xx;\n"
|
||||
output = self.run_script(old1 + old2)
|
||||
new1 = 'int yy = "xx\\"xx"[yy];\n'
|
||||
new2 = "int yy = 'x\\'xx' + yy;\n"
|
||||
self.assertMultiLineEqual(output,
|
||||
"1\n"
|
||||
"< {old1}"
|
||||
"> {new1}"
|
||||
"{new1}"
|
||||
"2\n"
|
||||
"< {old2}"
|
||||
"> {new2}"
|
||||
"{new2}".format(old1=old1, old2=old2, new1=new1, new2=new2)
|
||||
)
|
||||
|
||||
def test_alter_comments(self):
|
||||
output = self.run_script(
|
||||
substfile=
|
||||
"xx yy\n"
|
||||
"*aa bb\n",
|
||||
args=("-c", "-",),
|
||||
input=
|
||||
"/* xx altered */\n"
|
||||
"int xx;\n"
|
||||
"/* aa unaltered */\n"
|
||||
"int aa;\n",
|
||||
)
|
||||
self.assertMultiLineEqual(output,
|
||||
"1\n"
|
||||
"< /* xx altered */\n"
|
||||
"> /* yy altered */\n"
|
||||
"/* yy altered */\n"
|
||||
"2\n"
|
||||
"< int xx;\n"
|
||||
"> int yy;\n"
|
||||
"int yy;\n"
|
||||
"/* aa unaltered */\n"
|
||||
"4\n"
|
||||
"< int aa;\n"
|
||||
"> int bb;\n"
|
||||
"int bb;\n"
|
||||
)
|
||||
|
||||
def test_directory(self):
|
||||
os.mkdir(support.TESTFN)
|
||||
self.addCleanup(support.rmtree, support.TESTFN)
|
||||
c_filename = os.path.join(support.TESTFN, "file.c")
|
||||
with open(c_filename, "w") as file:
|
||||
file.write("int xx;\n")
|
||||
with open(os.path.join(support.TESTFN, "file.py"), "w") as file:
|
||||
file.write("xx = 'unaltered'\n")
|
||||
script = os.path.join(scriptsdir, "fixcid.py")
|
||||
output = self.run_script(args=(support.TESTFN,))
|
||||
self.assertMultiLineEqual(output,
|
||||
"{}:\n"
|
||||
"1\n"
|
||||
'< int xx;\n'
|
||||
'> int yy;\n'.format(c_filename)
|
||||
)
|
||||
|
||||
def run_script(self, input="", *, args=("-",), substfile="xx yy\n"):
|
||||
substfilename = support.TESTFN + ".subst"
|
||||
with open(substfilename, "w") as file:
|
||||
file.write(substfile)
|
||||
self.addCleanup(support.unlink, substfilename)
|
||||
|
||||
argv = ["fixcid.py", "-s", substfilename] + list(args)
|
||||
script = os.path.join(scriptsdir, "fixcid.py")
|
||||
with support.swap_attr(sys, "argv", argv), \
|
||||
support.swap_attr(sys, "stdin", StringIO(input)), \
|
||||
support.captured_stdout() as output, \
|
||||
support.captured_stderr():
|
||||
try:
|
||||
runpy.run_path(script, run_name="__main__")
|
||||
except SystemExit as exit:
|
||||
self.assertEqual(exit.code, 0)
|
||||
return output.getvalue()
|
||||
@ -0,0 +1,35 @@
|
||||
"""Tests for the gprof2html script in the Tools directory."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from unittest import mock
|
||||
import tempfile
|
||||
|
||||
from test.test_tools import skip_if_missing, import_tool
|
||||
|
||||
skip_if_missing()
|
||||
|
||||
class Gprof2htmlTests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.gprof = import_tool('gprof2html')
|
||||
oldargv = sys.argv
|
||||
def fixup():
|
||||
sys.argv = oldargv
|
||||
self.addCleanup(fixup)
|
||||
sys.argv = []
|
||||
|
||||
def test_gprof(self):
|
||||
# Issue #14508: this used to fail with a NameError.
|
||||
with mock.patch.object(self.gprof, 'webbrowser') as wmock, \
|
||||
tempfile.TemporaryDirectory() as tmpdir:
|
||||
fn = os.path.join(tmpdir, 'abc')
|
||||
open(fn, 'w').close()
|
||||
sys.argv = ['gprof2html', fn]
|
||||
self.gprof.main()
|
||||
self.assertTrue(wmock.open.called)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@ -0,0 +1,245 @@
|
||||
"""Tests to cover the Tools/i18n package"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from textwrap import dedent
|
||||
|
||||
from test.support.script_helper import assert_python_ok
|
||||
from test.test_tools import skip_if_missing, toolsdir
|
||||
from test.support import temp_cwd, temp_dir
|
||||
|
||||
|
||||
skip_if_missing()
|
||||
|
||||
|
||||
class Test_pygettext(unittest.TestCase):
|
||||
"""Tests for the pygettext.py tool"""
|
||||
|
||||
script = os.path.join(toolsdir,'i18n', 'pygettext.py')
|
||||
|
||||
def get_header(self, data):
|
||||
""" utility: return the header of a .po file as a dictionary """
|
||||
headers = {}
|
||||
for line in data.split('\n'):
|
||||
if not line or line.startswith(('#', 'msgid','msgstr')):
|
||||
continue
|
||||
line = line.strip('"')
|
||||
key, val = line.split(':',1)
|
||||
headers[key] = val.strip()
|
||||
return headers
|
||||
|
||||
def get_msgids(self, data):
|
||||
""" utility: return all msgids in .po file as a list of strings """
|
||||
msgids = []
|
||||
reading_msgid = False
|
||||
cur_msgid = []
|
||||
for line in data.split('\n'):
|
||||
if reading_msgid:
|
||||
if line.startswith('"'):
|
||||
cur_msgid.append(line.strip('"'))
|
||||
else:
|
||||
msgids.append('\n'.join(cur_msgid))
|
||||
cur_msgid = []
|
||||
reading_msgid = False
|
||||
continue
|
||||
if line.startswith('msgid '):
|
||||
line = line[len('msgid '):]
|
||||
cur_msgid.append(line.strip('"'))
|
||||
reading_msgid = True
|
||||
else:
|
||||
if reading_msgid:
|
||||
msgids.append('\n'.join(cur_msgid))
|
||||
|
||||
return msgids
|
||||
|
||||
def extract_docstrings_from_str(self, module_content):
|
||||
""" utility: return all msgids extracted from module_content """
|
||||
filename = 'test_docstrings.py'
|
||||
with temp_cwd(None) as cwd:
|
||||
with open(filename, 'w') as fp:
|
||||
fp.write(module_content)
|
||||
assert_python_ok(self.script, '-D', filename)
|
||||
with open('messages.pot') as fp:
|
||||
data = fp.read()
|
||||
return self.get_msgids(data)
|
||||
|
||||
def test_header(self):
|
||||
"""Make sure the required fields are in the header, according to:
|
||||
http://www.gnu.org/software/gettext/manual/gettext.html#Header-Entry
|
||||
"""
|
||||
with temp_cwd(None) as cwd:
|
||||
assert_python_ok(self.script)
|
||||
with open('messages.pot') as fp:
|
||||
data = fp.read()
|
||||
header = self.get_header(data)
|
||||
|
||||
self.assertIn("Project-Id-Version", header)
|
||||
self.assertIn("POT-Creation-Date", header)
|
||||
self.assertIn("PO-Revision-Date", header)
|
||||
self.assertIn("Last-Translator", header)
|
||||
self.assertIn("Language-Team", header)
|
||||
self.assertIn("MIME-Version", header)
|
||||
self.assertIn("Content-Type", header)
|
||||
self.assertIn("Content-Transfer-Encoding", header)
|
||||
self.assertIn("Generated-By", header)
|
||||
|
||||
# not clear if these should be required in POT (template) files
|
||||
#self.assertIn("Report-Msgid-Bugs-To", header)
|
||||
#self.assertIn("Language", header)
|
||||
|
||||
#"Plural-Forms" is optional
|
||||
|
||||
@unittest.skipIf(sys.platform.startswith('aix'),
|
||||
'bpo-29972: broken test on AIX')
|
||||
def test_POT_Creation_Date(self):
|
||||
""" Match the date format from xgettext for POT-Creation-Date """
|
||||
from datetime import datetime
|
||||
with temp_cwd(None) as cwd:
|
||||
assert_python_ok(self.script)
|
||||
with open('messages.pot') as fp:
|
||||
data = fp.read()
|
||||
header = self.get_header(data)
|
||||
creationDate = header['POT-Creation-Date']
|
||||
|
||||
# peel off the escaped newline at the end of string
|
||||
if creationDate.endswith('\\n'):
|
||||
creationDate = creationDate[:-len('\\n')]
|
||||
|
||||
# This will raise if the date format does not exactly match.
|
||||
datetime.strptime(creationDate, '%Y-%m-%d %H:%M%z')
|
||||
|
||||
def test_funcdocstring(self):
|
||||
for doc in ('"""doc"""', "r'''doc'''", "R'doc'", 'u"doc"'):
|
||||
with self.subTest(doc):
|
||||
msgids = self.extract_docstrings_from_str(dedent('''\
|
||||
def foo(bar):
|
||||
%s
|
||||
''' % doc))
|
||||
self.assertIn('doc', msgids)
|
||||
|
||||
def test_funcdocstring_bytes(self):
|
||||
msgids = self.extract_docstrings_from_str(dedent('''\
|
||||
def foo(bar):
|
||||
b"""doc"""
|
||||
'''))
|
||||
self.assertFalse([msgid for msgid in msgids if 'doc' in msgid])
|
||||
|
||||
def test_funcdocstring_fstring(self):
|
||||
msgids = self.extract_docstrings_from_str(dedent('''\
|
||||
def foo(bar):
|
||||
f"""doc"""
|
||||
'''))
|
||||
self.assertFalse([msgid for msgid in msgids if 'doc' in msgid])
|
||||
|
||||
def test_classdocstring(self):
|
||||
for doc in ('"""doc"""', "r'''doc'''", "R'doc'", 'u"doc"'):
|
||||
with self.subTest(doc):
|
||||
msgids = self.extract_docstrings_from_str(dedent('''\
|
||||
class C:
|
||||
%s
|
||||
''' % doc))
|
||||
self.assertIn('doc', msgids)
|
||||
|
||||
def test_classdocstring_bytes(self):
|
||||
msgids = self.extract_docstrings_from_str(dedent('''\
|
||||
class C:
|
||||
b"""doc"""
|
||||
'''))
|
||||
self.assertFalse([msgid for msgid in msgids if 'doc' in msgid])
|
||||
|
||||
def test_classdocstring_fstring(self):
|
||||
msgids = self.extract_docstrings_from_str(dedent('''\
|
||||
class C:
|
||||
f"""doc"""
|
||||
'''))
|
||||
self.assertFalse([msgid for msgid in msgids if 'doc' in msgid])
|
||||
|
||||
def test_msgid(self):
|
||||
msgids = self.extract_docstrings_from_str(
|
||||
'''_("""doc""" r'str' u"ing")''')
|
||||
self.assertIn('docstring', msgids)
|
||||
|
||||
def test_msgid_bytes(self):
|
||||
msgids = self.extract_docstrings_from_str('_(b"""doc""")')
|
||||
self.assertFalse([msgid for msgid in msgids if 'doc' in msgid])
|
||||
|
||||
def test_msgid_fstring(self):
|
||||
msgids = self.extract_docstrings_from_str('_(f"""doc""")')
|
||||
self.assertFalse([msgid for msgid in msgids if 'doc' in msgid])
|
||||
|
||||
def test_funcdocstring_annotated_args(self):
|
||||
""" Test docstrings for functions with annotated args """
|
||||
msgids = self.extract_docstrings_from_str(dedent('''\
|
||||
def foo(bar: str):
|
||||
"""doc"""
|
||||
'''))
|
||||
self.assertIn('doc', msgids)
|
||||
|
||||
def test_funcdocstring_annotated_return(self):
|
||||
""" Test docstrings for functions with annotated return type """
|
||||
msgids = self.extract_docstrings_from_str(dedent('''\
|
||||
def foo(bar) -> str:
|
||||
"""doc"""
|
||||
'''))
|
||||
self.assertIn('doc', msgids)
|
||||
|
||||
def test_funcdocstring_defvalue_args(self):
|
||||
""" Test docstring for functions with default arg values """
|
||||
msgids = self.extract_docstrings_from_str(dedent('''\
|
||||
def foo(bar=()):
|
||||
"""doc"""
|
||||
'''))
|
||||
self.assertIn('doc', msgids)
|
||||
|
||||
def test_funcdocstring_multiple_funcs(self):
|
||||
""" Test docstring extraction for multiple functions combining
|
||||
annotated args, annotated return types and default arg values
|
||||
"""
|
||||
msgids = self.extract_docstrings_from_str(dedent('''\
|
||||
def foo1(bar: tuple=()) -> str:
|
||||
"""doc1"""
|
||||
|
||||
def foo2(bar: List[1:2]) -> (lambda x: x):
|
||||
"""doc2"""
|
||||
|
||||
def foo3(bar: 'func'=lambda x: x) -> {1: 2}:
|
||||
"""doc3"""
|
||||
'''))
|
||||
self.assertIn('doc1', msgids)
|
||||
self.assertIn('doc2', msgids)
|
||||
self.assertIn('doc3', msgids)
|
||||
|
||||
def test_classdocstring_early_colon(self):
|
||||
""" Test docstring extraction for a class with colons occurring within
|
||||
the parentheses.
|
||||
"""
|
||||
msgids = self.extract_docstrings_from_str(dedent('''\
|
||||
class D(L[1:2], F({1: 2}), metaclass=M(lambda x: x)):
|
||||
"""doc"""
|
||||
'''))
|
||||
self.assertIn('doc', msgids)
|
||||
|
||||
def test_files_list(self):
|
||||
"""Make sure the directories are inspected for source files
|
||||
bpo-31920
|
||||
"""
|
||||
text1 = 'Text to translate1'
|
||||
text2 = 'Text to translate2'
|
||||
text3 = 'Text to ignore'
|
||||
with temp_cwd(None), temp_dir(None) as sdir:
|
||||
os.mkdir(os.path.join(sdir, 'pypkg'))
|
||||
with open(os.path.join(sdir, 'pypkg', 'pymod.py'), 'w') as sfile:
|
||||
sfile.write(f'_({text1!r})')
|
||||
os.mkdir(os.path.join(sdir, 'pkg.py'))
|
||||
with open(os.path.join(sdir, 'pkg.py', 'pymod2.py'), 'w') as sfile:
|
||||
sfile.write(f'_({text2!r})')
|
||||
os.mkdir(os.path.join(sdir, 'CVS'))
|
||||
with open(os.path.join(sdir, 'CVS', 'pymod3.py'), 'w') as sfile:
|
||||
sfile.write(f'_({text3!r})')
|
||||
assert_python_ok(self.script, sdir)
|
||||
with open('messages.pot') as fp:
|
||||
data = fp.read()
|
||||
self.assertIn(f'msgid "{text1}"', data)
|
||||
self.assertIn(f'msgid "{text2}"', data)
|
||||
self.assertNotIn(text3, data)
|
||||
@ -0,0 +1,40 @@
|
||||
"""Tests for the lll script in the Tools/script directory."""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from test import support
|
||||
from test.test_tools import skip_if_missing, import_tool
|
||||
import unittest
|
||||
|
||||
skip_if_missing()
|
||||
|
||||
|
||||
class lllTests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.lll = import_tool('lll')
|
||||
|
||||
@support.skip_unless_symlink
|
||||
def test_lll_multiple_dirs(self):
|
||||
with tempfile.TemporaryDirectory() as dir1, \
|
||||
tempfile.TemporaryDirectory() as dir2:
|
||||
fn1 = os.path.join(dir1, 'foo1')
|
||||
fn2 = os.path.join(dir2, 'foo2')
|
||||
for fn, dir in (fn1, dir1), (fn2, dir2):
|
||||
open(fn, 'w').close()
|
||||
os.symlink(fn, os.path.join(dir, 'symlink'))
|
||||
|
||||
with support.captured_stdout() as output:
|
||||
self.lll.main([dir1, dir2])
|
||||
prefix = '\\\\?\\' if os.name == 'nt' else ''
|
||||
self.assertEqual(output.getvalue(),
|
||||
f'{dir1}:\n'
|
||||
f'symlink -> {prefix}{fn1}\n'
|
||||
f'\n'
|
||||
f'{dir2}:\n'
|
||||
f'symlink -> {prefix}{fn2}\n'
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@ -0,0 +1,78 @@
|
||||
"""Tests for the md5sum script in the Tools directory."""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from test import support
|
||||
from test.support import hashlib_helper
|
||||
from test.support.script_helper import assert_python_ok, assert_python_failure
|
||||
|
||||
from test.test_tools import scriptsdir, skip_if_missing
|
||||
|
||||
skip_if_missing()
|
||||
|
||||
@hashlib_helper.requires_hashdigest('md5')
|
||||
class MD5SumTests(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.script = os.path.join(scriptsdir, 'md5sum.py')
|
||||
os.mkdir(support.TESTFN)
|
||||
cls.fodder = os.path.join(support.TESTFN, 'md5sum.fodder')
|
||||
with open(cls.fodder, 'wb') as f:
|
||||
f.write(b'md5sum\r\ntest file\r\n')
|
||||
cls.fodder_md5 = b'd38dae2eb1ab346a292ef6850f9e1a0d'
|
||||
cls.fodder_textmode_md5 = b'a8b07894e2ca3f2a4c3094065fa6e0a5'
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
support.rmtree(support.TESTFN)
|
||||
|
||||
def test_noargs(self):
|
||||
rc, out, err = assert_python_ok(self.script)
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertTrue(
|
||||
out.startswith(b'd41d8cd98f00b204e9800998ecf8427e <stdin>'))
|
||||
self.assertFalse(err)
|
||||
|
||||
def test_checksum_fodder(self):
|
||||
rc, out, err = assert_python_ok(self.script, self.fodder)
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertTrue(out.startswith(self.fodder_md5))
|
||||
for part in self.fodder.split(os.path.sep):
|
||||
self.assertIn(part.encode(), out)
|
||||
self.assertFalse(err)
|
||||
|
||||
def test_dash_l(self):
|
||||
rc, out, err = assert_python_ok(self.script, '-l', self.fodder)
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertIn(self.fodder_md5, out)
|
||||
parts = self.fodder.split(os.path.sep)
|
||||
self.assertIn(parts[-1].encode(), out)
|
||||
self.assertNotIn(parts[-2].encode(), out)
|
||||
|
||||
def test_dash_t(self):
|
||||
rc, out, err = assert_python_ok(self.script, '-t', self.fodder)
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertTrue(out.startswith(self.fodder_textmode_md5))
|
||||
self.assertNotIn(self.fodder_md5, out)
|
||||
|
||||
def test_dash_s(self):
|
||||
rc, out, err = assert_python_ok(self.script, '-s', '512', self.fodder)
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertIn(self.fodder_md5, out)
|
||||
|
||||
def test_multiple_files(self):
|
||||
rc, out, err = assert_python_ok(self.script, self.fodder, self.fodder)
|
||||
self.assertEqual(rc, 0)
|
||||
lines = out.splitlines()
|
||||
self.assertEqual(len(lines), 2)
|
||||
self.assertEqual(*lines)
|
||||
|
||||
def test_usage(self):
|
||||
rc, out, err = assert_python_failure(self.script, '-h')
|
||||
self.assertEqual(rc, 2)
|
||||
self.assertEqual(out, b'')
|
||||
self.assertGreater(err, b'')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@ -0,0 +1,131 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
from test import support
|
||||
from test.test_tools import scriptsdir, skip_if_missing
|
||||
|
||||
|
||||
# need Tools/script/ directory: skip if run on Python installed on the system
|
||||
skip_if_missing()
|
||||
|
||||
|
||||
class TestPathfixFunctional(unittest.TestCase):
|
||||
script = os.path.join(scriptsdir, 'pathfix.py')
|
||||
|
||||
def setUp(self):
|
||||
self.addCleanup(support.unlink, support.TESTFN)
|
||||
|
||||
def pathfix(self, shebang, pathfix_flags, exitcode=0, stdout='', stderr='',
|
||||
directory=''):
|
||||
if directory:
|
||||
# bpo-38347: Test filename should contain lowercase, uppercase,
|
||||
# "-", "_" and digits.
|
||||
filename = os.path.join(directory, 'script-A_1.py')
|
||||
pathfix_arg = directory
|
||||
else:
|
||||
filename = support.TESTFN
|
||||
pathfix_arg = filename
|
||||
|
||||
with open(filename, 'w', encoding='utf8') as f:
|
||||
f.write(f'{shebang}\n' + 'print("Hello world")\n')
|
||||
|
||||
encoding = sys.getfilesystemencoding()
|
||||
proc = subprocess.run(
|
||||
[sys.executable, self.script,
|
||||
*pathfix_flags, '-n', pathfix_arg],
|
||||
env={**os.environ, 'PYTHONIOENCODING': encoding},
|
||||
capture_output=True)
|
||||
|
||||
if stdout == '' and proc.returncode == 0:
|
||||
stdout = f'{filename}: updating\n'
|
||||
self.assertEqual(proc.returncode, exitcode, proc)
|
||||
self.assertEqual(proc.stdout.decode(encoding), stdout.replace('\n', os.linesep), proc)
|
||||
self.assertEqual(proc.stderr.decode(encoding), stderr.replace('\n', os.linesep), proc)
|
||||
|
||||
with open(filename, 'r', encoding='utf8') as f:
|
||||
output = f.read()
|
||||
|
||||
lines = output.split('\n')
|
||||
self.assertEqual(lines[1:], ['print("Hello world")', ''])
|
||||
new_shebang = lines[0]
|
||||
|
||||
if proc.returncode != 0:
|
||||
self.assertEqual(shebang, new_shebang)
|
||||
|
||||
return new_shebang
|
||||
|
||||
def test_recursive(self):
|
||||
tmpdir = support.TESTFN + '.d'
|
||||
self.addCleanup(support.rmtree, tmpdir)
|
||||
os.mkdir(tmpdir)
|
||||
expected_stderr = f"recursedown('{os.path.basename(tmpdir)}')\n"
|
||||
self.assertEqual(
|
||||
self.pathfix(
|
||||
'#! /usr/bin/env python',
|
||||
['-i', '/usr/bin/python3'],
|
||||
directory=tmpdir,
|
||||
stderr=expected_stderr),
|
||||
'#! /usr/bin/python3')
|
||||
|
||||
def test_pathfix(self):
|
||||
self.assertEqual(
|
||||
self.pathfix(
|
||||
'#! /usr/bin/env python',
|
||||
['-i', '/usr/bin/python3']),
|
||||
'#! /usr/bin/python3')
|
||||
self.assertEqual(
|
||||
self.pathfix(
|
||||
'#! /usr/bin/env python -R',
|
||||
['-i', '/usr/bin/python3']),
|
||||
'#! /usr/bin/python3')
|
||||
|
||||
def test_pathfix_keeping_flags(self):
|
||||
self.assertEqual(
|
||||
self.pathfix(
|
||||
'#! /usr/bin/env python -R',
|
||||
['-i', '/usr/bin/python3', '-k']),
|
||||
'#! /usr/bin/python3 -R')
|
||||
self.assertEqual(
|
||||
self.pathfix(
|
||||
'#! /usr/bin/env python',
|
||||
['-i', '/usr/bin/python3', '-k']),
|
||||
'#! /usr/bin/python3')
|
||||
|
||||
def test_pathfix_adding_flag(self):
|
||||
self.assertEqual(
|
||||
self.pathfix(
|
||||
'#! /usr/bin/env python',
|
||||
['-i', '/usr/bin/python3', '-a', 's']),
|
||||
'#! /usr/bin/python3 -s')
|
||||
self.assertEqual(
|
||||
self.pathfix(
|
||||
'#! /usr/bin/env python -S',
|
||||
['-i', '/usr/bin/python3', '-a', 's']),
|
||||
'#! /usr/bin/python3 -s')
|
||||
self.assertEqual(
|
||||
self.pathfix(
|
||||
'#! /usr/bin/env python -V',
|
||||
['-i', '/usr/bin/python3', '-a', 'v', '-k']),
|
||||
'#! /usr/bin/python3 -vV')
|
||||
self.assertEqual(
|
||||
self.pathfix(
|
||||
'#! /usr/bin/env python',
|
||||
['-i', '/usr/bin/python3', '-a', 'Rs']),
|
||||
'#! /usr/bin/python3 -Rs')
|
||||
self.assertEqual(
|
||||
self.pathfix(
|
||||
'#! /usr/bin/env python -W default',
|
||||
['-i', '/usr/bin/python3', '-a', 's', '-k']),
|
||||
'#! /usr/bin/python3 -sW default')
|
||||
|
||||
def test_pathfix_adding_errors(self):
|
||||
self.pathfix(
|
||||
'#! /usr/bin/env python -E',
|
||||
['-i', '/usr/bin/python3', '-a', 'W default', '-k'],
|
||||
exitcode=2,
|
||||
stderr="-a option doesn't support whitespaces")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@ -0,0 +1,32 @@
|
||||
"""Tests for the pdeps script in the Tools directory."""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
import tempfile
|
||||
|
||||
from test.test_tools import skip_if_missing, import_tool
|
||||
|
||||
skip_if_missing()
|
||||
|
||||
|
||||
class PdepsTests(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
self.pdeps = import_tool('pdeps')
|
||||
|
||||
def test_process_errors(self):
|
||||
# Issue #14492: m_import.match(line) can be None.
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
fn = os.path.join(tmpdir, 'foo')
|
||||
with open(fn, 'w') as stream:
|
||||
stream.write("#!/this/will/fail")
|
||||
self.pdeps.process(fn, {})
|
||||
|
||||
def test_inverse_attribute_error(self):
|
||||
# Issue #14492: this used to fail with an AttributeError.
|
||||
self.pdeps.inverse({'a': []})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@ -0,0 +1,339 @@
|
||||
"""Tests for the pindent script in the Tools directory."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
import subprocess
|
||||
import textwrap
|
||||
from test import support
|
||||
from test.support.script_helper import assert_python_ok
|
||||
|
||||
from test.test_tools import scriptsdir, skip_if_missing
|
||||
|
||||
skip_if_missing()
|
||||
|
||||
|
||||
class PindentTests(unittest.TestCase):
|
||||
script = os.path.join(scriptsdir, 'pindent.py')
|
||||
|
||||
def assertFileEqual(self, fn1, fn2):
|
||||
with open(fn1) as f1, open(fn2) as f2:
|
||||
self.assertEqual(f1.readlines(), f2.readlines())
|
||||
|
||||
def pindent(self, source, *args):
|
||||
with subprocess.Popen(
|
||||
(sys.executable, self.script) + args,
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
universal_newlines=True) as proc:
|
||||
out, err = proc.communicate(source)
|
||||
self.assertIsNone(err)
|
||||
return out
|
||||
|
||||
def lstriplines(self, data):
|
||||
return '\n'.join(line.lstrip() for line in data.splitlines()) + '\n'
|
||||
|
||||
def test_selftest(self):
|
||||
self.maxDiff = None
|
||||
with support.temp_dir() as directory:
|
||||
data_path = os.path.join(directory, '_test.py')
|
||||
with open(self.script) as f:
|
||||
closed = f.read()
|
||||
with open(data_path, 'w') as f:
|
||||
f.write(closed)
|
||||
|
||||
rc, out, err = assert_python_ok(self.script, '-d', data_path)
|
||||
self.assertEqual(out, b'')
|
||||
self.assertEqual(err, b'')
|
||||
backup = data_path + '~'
|
||||
self.assertTrue(os.path.exists(backup))
|
||||
with open(backup) as f:
|
||||
self.assertEqual(f.read(), closed)
|
||||
with open(data_path) as f:
|
||||
clean = f.read()
|
||||
compile(clean, '_test.py', 'exec')
|
||||
self.assertEqual(self.pindent(clean, '-c'), closed)
|
||||
self.assertEqual(self.pindent(closed, '-d'), clean)
|
||||
|
||||
rc, out, err = assert_python_ok(self.script, '-c', data_path)
|
||||
self.assertEqual(out, b'')
|
||||
self.assertEqual(err, b'')
|
||||
with open(backup) as f:
|
||||
self.assertEqual(f.read(), clean)
|
||||
with open(data_path) as f:
|
||||
self.assertEqual(f.read(), closed)
|
||||
|
||||
broken = self.lstriplines(closed)
|
||||
with open(data_path, 'w') as f:
|
||||
f.write(broken)
|
||||
rc, out, err = assert_python_ok(self.script, '-r', data_path)
|
||||
self.assertEqual(out, b'')
|
||||
self.assertEqual(err, b'')
|
||||
with open(backup) as f:
|
||||
self.assertEqual(f.read(), broken)
|
||||
with open(data_path) as f:
|
||||
indented = f.read()
|
||||
compile(indented, '_test.py', 'exec')
|
||||
self.assertEqual(self.pindent(broken, '-r'), indented)
|
||||
|
||||
def pindent_test(self, clean, closed):
|
||||
self.assertEqual(self.pindent(clean, '-c'), closed)
|
||||
self.assertEqual(self.pindent(closed, '-d'), clean)
|
||||
broken = self.lstriplines(closed)
|
||||
self.assertEqual(self.pindent(broken, '-r', '-e', '-s', '4'), closed)
|
||||
|
||||
def test_statements(self):
|
||||
clean = textwrap.dedent("""\
|
||||
if a:
|
||||
pass
|
||||
|
||||
if a:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
if a:
|
||||
pass
|
||||
elif:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
while a:
|
||||
break
|
||||
|
||||
while a:
|
||||
break
|
||||
else:
|
||||
pass
|
||||
|
||||
for i in a:
|
||||
break
|
||||
|
||||
for i in a:
|
||||
break
|
||||
else:
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
finally:
|
||||
pass
|
||||
|
||||
with a:
|
||||
pass
|
||||
|
||||
class A:
|
||||
pass
|
||||
|
||||
def f():
|
||||
pass
|
||||
""")
|
||||
|
||||
closed = textwrap.dedent("""\
|
||||
if a:
|
||||
pass
|
||||
# end if
|
||||
|
||||
if a:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
# end if
|
||||
|
||||
if a:
|
||||
pass
|
||||
elif:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
# end if
|
||||
|
||||
while a:
|
||||
break
|
||||
# end while
|
||||
|
||||
while a:
|
||||
break
|
||||
else:
|
||||
pass
|
||||
# end while
|
||||
|
||||
for i in a:
|
||||
break
|
||||
# end for
|
||||
|
||||
for i in a:
|
||||
break
|
||||
else:
|
||||
pass
|
||||
# end for
|
||||
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
pass
|
||||
# end try
|
||||
|
||||
try:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
# end try
|
||||
|
||||
try:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
finally:
|
||||
pass
|
||||
# end try
|
||||
|
||||
with a:
|
||||
pass
|
||||
# end with
|
||||
|
||||
class A:
|
||||
pass
|
||||
# end class A
|
||||
|
||||
def f():
|
||||
pass
|
||||
# end def f
|
||||
""")
|
||||
self.pindent_test(clean, closed)
|
||||
|
||||
def test_multilevel(self):
|
||||
clean = textwrap.dedent("""\
|
||||
def foobar(a, b):
|
||||
if a == b:
|
||||
a = a+1
|
||||
elif a < b:
|
||||
b = b-1
|
||||
if b > a: a = a-1
|
||||
else:
|
||||
print 'oops!'
|
||||
""")
|
||||
closed = textwrap.dedent("""\
|
||||
def foobar(a, b):
|
||||
if a == b:
|
||||
a = a+1
|
||||
elif a < b:
|
||||
b = b-1
|
||||
if b > a: a = a-1
|
||||
# end if
|
||||
else:
|
||||
print 'oops!'
|
||||
# end if
|
||||
# end def foobar
|
||||
""")
|
||||
self.pindent_test(clean, closed)
|
||||
|
||||
def test_preserve_indents(self):
|
||||
clean = textwrap.dedent("""\
|
||||
if a:
|
||||
if b:
|
||||
pass
|
||||
""")
|
||||
closed = textwrap.dedent("""\
|
||||
if a:
|
||||
if b:
|
||||
pass
|
||||
# end if
|
||||
# end if
|
||||
""")
|
||||
self.assertEqual(self.pindent(clean, '-c'), closed)
|
||||
self.assertEqual(self.pindent(closed, '-d'), clean)
|
||||
broken = self.lstriplines(closed)
|
||||
self.assertEqual(self.pindent(broken, '-r', '-e', '-s', '9'), closed)
|
||||
clean = textwrap.dedent("""\
|
||||
if a:
|
||||
\tif b:
|
||||
\t\tpass
|
||||
""")
|
||||
closed = textwrap.dedent("""\
|
||||
if a:
|
||||
\tif b:
|
||||
\t\tpass
|
||||
\t# end if
|
||||
# end if
|
||||
""")
|
||||
self.assertEqual(self.pindent(clean, '-c'), closed)
|
||||
self.assertEqual(self.pindent(closed, '-d'), clean)
|
||||
broken = self.lstriplines(closed)
|
||||
self.assertEqual(self.pindent(broken, '-r'), closed)
|
||||
|
||||
def test_escaped_newline(self):
|
||||
clean = textwrap.dedent("""\
|
||||
class\\
|
||||
\\
|
||||
A:
|
||||
def\
|
||||
\\
|
||||
f:
|
||||
pass
|
||||
""")
|
||||
closed = textwrap.dedent("""\
|
||||
class\\
|
||||
\\
|
||||
A:
|
||||
def\
|
||||
\\
|
||||
f:
|
||||
pass
|
||||
# end def f
|
||||
# end class A
|
||||
""")
|
||||
self.assertEqual(self.pindent(clean, '-c'), closed)
|
||||
self.assertEqual(self.pindent(closed, '-d'), clean)
|
||||
|
||||
def test_empty_line(self):
|
||||
clean = textwrap.dedent("""\
|
||||
if a:
|
||||
|
||||
pass
|
||||
""")
|
||||
closed = textwrap.dedent("""\
|
||||
if a:
|
||||
|
||||
pass
|
||||
# end if
|
||||
""")
|
||||
self.pindent_test(clean, closed)
|
||||
|
||||
def test_oneline(self):
|
||||
clean = textwrap.dedent("""\
|
||||
if a: pass
|
||||
""")
|
||||
closed = textwrap.dedent("""\
|
||||
if a: pass
|
||||
# end if
|
||||
""")
|
||||
self.pindent_test(clean, closed)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@ -0,0 +1,35 @@
|
||||
"""Tests for scripts in the Tools directory.
|
||||
|
||||
This file contains regression tests for some of the scripts found in the
|
||||
Tools directory of a Python checkout or tarball, such as reindent.py.
|
||||
"""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from test.support.script_helper import assert_python_ok
|
||||
from test.support import findfile
|
||||
|
||||
from test.test_tools import scriptsdir, skip_if_missing
|
||||
|
||||
skip_if_missing()
|
||||
|
||||
class ReindentTests(unittest.TestCase):
|
||||
script = os.path.join(scriptsdir, 'reindent.py')
|
||||
|
||||
def test_noargs(self):
|
||||
assert_python_ok(self.script)
|
||||
|
||||
def test_help(self):
|
||||
rc, out, err = assert_python_ok(self.script, '-h')
|
||||
self.assertEqual(out, b'')
|
||||
self.assertGreater(err, b'')
|
||||
|
||||
def test_reindent_file_with_bad_encoding(self):
|
||||
bad_coding_path = findfile('bad_coding.py')
|
||||
rc, out, err = assert_python_ok(self.script, '-r', bad_coding_path)
|
||||
self.assertEqual(out, b'')
|
||||
self.assertNotEqual(err, b'')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@ -0,0 +1,62 @@
|
||||
"""Tests for scripts in the Tools directory.
|
||||
|
||||
This file contains extremely basic regression tests for the scripts found in
|
||||
the Tools directory of a Python checkout or tarball which don't have separate
|
||||
tests of their own.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from test import support
|
||||
|
||||
from test.test_tools import scriptsdir, import_tool, skip_if_missing
|
||||
|
||||
skip_if_missing()
|
||||
|
||||
class TestSundryScripts(unittest.TestCase):
|
||||
# At least make sure the rest don't have syntax errors. When tests are
|
||||
# added for a script it should be added to the whitelist below.
|
||||
|
||||
# scripts that have independent tests.
|
||||
whitelist = ['reindent', 'pdeps', 'gprof2html', 'md5sum']
|
||||
# scripts that can't be imported without running
|
||||
blacklist = ['make_ctype']
|
||||
# scripts that use windows-only modules
|
||||
windows_only = ['win_add2path']
|
||||
# blacklisted for other reasons
|
||||
other = ['analyze_dxp', '2to3']
|
||||
|
||||
skiplist = blacklist + whitelist + windows_only + other
|
||||
|
||||
def test_sundry(self):
|
||||
old_modules = support.modules_setup()
|
||||
try:
|
||||
for fn in os.listdir(scriptsdir):
|
||||
if not fn.endswith('.py'):
|
||||
continue
|
||||
|
||||
name = fn[:-3]
|
||||
if name in self.skiplist:
|
||||
continue
|
||||
|
||||
import_tool(name)
|
||||
finally:
|
||||
# Unload all modules loaded in this test
|
||||
support.modules_cleanup(*old_modules)
|
||||
|
||||
@unittest.skipIf(sys.platform != "win32", "Windows-only test")
|
||||
def test_sundry_windows(self):
|
||||
for name in self.windows_only:
|
||||
import_tool(name)
|
||||
|
||||
def test_analyze_dxp_import(self):
|
||||
if hasattr(sys, 'getdxp'):
|
||||
import_tool('analyze_dxp')
|
||||
else:
|
||||
with self.assertRaises(RuntimeError):
|
||||
import_tool('analyze_dxp')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user