first commit

This commit is contained in:
2025-08-07 13:15:31 +01:00
commit d903893b4c
21854 changed files with 4461308 additions and 0 deletions

View File

@ -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)

View File

@ -0,0 +1,4 @@
from test.test_tools import load_tests
import unittest
unittest.main()

View File

@ -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)

View File

@ -0,0 +1,5 @@
from . import load_tests
import unittest
unittest.main()

View File

@ -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)

View File

@ -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',)),
# ])

View File

@ -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.

View File

@ -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, [])

View File

@ -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)

View File

@ -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),
])

View File

@ -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

View File

@ -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')),
])

View File

@ -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)

View File

@ -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>',)),
])

View File

@ -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)

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -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.

View File

@ -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',)),
])

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()