diff options
Diffstat (limited to 'Lib/test/test_tools')
-rw-r--r-- | Lib/test/test_tools/__init__.py | 25 | ||||
-rw-r--r-- | Lib/test/test_tools/__main__.py | 4 | ||||
-rw-r--r-- | Lib/test/test_tools/test_gprof2html.py | 36 | ||||
-rw-r--r-- | Lib/test/test_tools/test_md5sum.py | 77 | ||||
-rw-r--r-- | Lib/test/test_tools/test_pdeps.py | 34 | ||||
-rw-r--r-- | Lib/test/test_tools/test_pindent.py | 339 | ||||
-rw-r--r-- | Lib/test/test_tools/test_reindent.py | 28 | ||||
-rw-r--r-- | Lib/test/test_tools/test_sundry.py | 53 | ||||
-rw-r--r-- | Lib/test/test_tools/test_unparse.py | 282 |
9 files changed, 878 insertions, 0 deletions
diff --git a/Lib/test/test_tools/__init__.py b/Lib/test/test_tools/__init__.py new file mode 100644 index 0000000..04c8726 --- /dev/null +++ b/Lib/test/test_tools/__init__.py @@ -0,0 +1,25 @@ +"""Support functions for testing scripts in the Tools directory.""" +import os +import unittest +import importlib +from test import support +from fnmatch import fnmatch + +basepath = 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(): + if not os.path.isdir(scriptsdir): + raise unittest.SkipTest('scripts directory could not be found') + +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) diff --git a/Lib/test/test_tools/__main__.py b/Lib/test/test_tools/__main__.py new file mode 100644 index 0000000..b6f13e5 --- /dev/null +++ b/Lib/test/test_tools/__main__.py @@ -0,0 +1,4 @@ +from test.test_tools import load_tests +import unittest + +unittest.main() diff --git a/Lib/test/test_tools/test_gprof2html.py b/Lib/test/test_tools/test_gprof2html.py new file mode 100644 index 0000000..845a2a8 --- /dev/null +++ b/Lib/test/test_tools/test_gprof2html.py @@ -0,0 +1,36 @@ +"""Tests for the gprof2html script in the Tools directory.""" + +import os +import sys +import importlib +import unittest +from unittest import mock +import tempfile + +from test.test_tools import scriptsdir, 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 an 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() diff --git a/Lib/test/test_tools/test_md5sum.py b/Lib/test/test_tools/test_md5sum.py new file mode 100644 index 0000000..59ea149 --- /dev/null +++ b/Lib/test/test_tools/test_md5sum.py @@ -0,0 +1,77 @@ +"""Tests for the md5sum script in the Tools directory.""" + +import os +import sys +import unittest +from test import support +from test.script_helper import assert_python_ok, assert_python_failure + +from test.test_tools import scriptsdir, import_tool, skip_if_missing + +skip_if_missing() + +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() diff --git a/Lib/test/test_tools/test_pdeps.py b/Lib/test/test_tools/test_pdeps.py new file mode 100644 index 0000000..091fa6a --- /dev/null +++ b/Lib/test/test_tools/test_pdeps.py @@ -0,0 +1,34 @@ +"""Tests for the pdeps script in the Tools directory.""" + +import os +import sys +import unittest +import tempfile +from test import support + +from test.test_tools import scriptsdir, 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() diff --git a/Lib/test/test_tools/test_pindent.py b/Lib/test/test_tools/test_pindent.py new file mode 100644 index 0000000..14a0aa2 --- /dev/null +++ b/Lib/test/test_tools/test_pindent.py @@ -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.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() diff --git a/Lib/test/test_tools/test_reindent.py b/Lib/test/test_tools/test_reindent.py new file mode 100644 index 0000000..45cebf7 --- /dev/null +++ b/Lib/test/test_tools/test_reindent.py @@ -0,0 +1,28 @@ +"""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.script_helper import assert_python_ok + +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'') + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_tools/test_sundry.py b/Lib/test/test_tools/test_sundry.py new file mode 100644 index 0000000..39e541b --- /dev/null +++ b/Lib/test/test_tools/test_sundry.py @@ -0,0 +1,53 @@ +"""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, such as h2py.py. +""" + +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'] + + skiplist = blacklist + whitelist + windows_only + other + + def test_sundry(self): + for fn in os.listdir(scriptsdir): + name = fn[:-3] + if fn.endswith('.py') and name not in self.skiplist: + import_tool(name) + + @unittest.skipIf(sys.platform != "win32", "Windows-only test") + def test_sundry_windows(self): + for name in self.windows_only: + import_tool(name) + + @unittest.skipIf(not support.threading, "test requires _thread module") + 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() diff --git a/Lib/test/test_tools/test_unparse.py b/Lib/test/test_tools/test_unparse.py new file mode 100644 index 0000000..976a6c5 --- /dev/null +++ b/Lib/test/test_tools/test_unparse.py @@ -0,0 +1,282 @@ +"""Tests for the unparse.py script in the Tools/parser directory.""" + +import unittest +import test.support +import io +import os +import random +import tokenize +import ast + +from test.test_tools import basepath, toolsdir, skip_if_missing + +skip_if_missing() + +parser_path = os.path.join(toolsdir, "parser") + +with test.support.DirsOnSysPath(parser_path): + import unparse + +def read_pyfile(filename): + """Read and return the contents of a Python source file (as a + string), taking into account the file encoding.""" + with open(filename, "rb") as pyfile: + encoding = tokenize.detect_encoding(pyfile.readline)[0] + with open(filename, "r", encoding=encoding) as pyfile: + source = pyfile.read() + return source + +for_else = """\ +def f(): + for x in range(10): + break + else: + y = 2 + z = 3 +""" + +while_else = """\ +def g(): + while True: + break + else: + y = 2 + z = 3 +""" + +relative_import = """\ +from . import fred +from .. import barney +from .australia import shrimp as prawns +""" + +nonlocal_ex = """\ +def f(): + x = 1 + def g(): + nonlocal x + x = 2 + y = 7 + def h(): + nonlocal x, y +""" + +# also acts as test for 'except ... as ...' +raise_from = """\ +try: + 1 / 0 +except ZeroDivisionError as e: + raise ArithmeticError from e +""" + +class_decorator = """\ +@f1(arg) +@f2 +class Foo: pass +""" + +elif1 = """\ +if cond1: + suite1 +elif cond2: + suite2 +else: + suite3 +""" + +elif2 = """\ +if cond1: + suite1 +elif cond2: + suite2 +""" + +try_except_finally = """\ +try: + suite1 +except ex1: + suite2 +except ex2: + suite3 +else: + suite4 +finally: + suite5 +""" + +with_simple = """\ +with f(): + suite1 +""" + +with_as = """\ +with f() as x: + suite1 +""" + +with_two_items = """\ +with f() as x, g() as y: + suite1 +""" + +class ASTTestCase(unittest.TestCase): + def assertASTEqual(self, ast1, ast2): + self.assertEqual(ast.dump(ast1), ast.dump(ast2)) + + def check_roundtrip(self, code1, filename="internal"): + ast1 = compile(code1, filename, "exec", ast.PyCF_ONLY_AST) + unparse_buffer = io.StringIO() + unparse.Unparser(ast1, unparse_buffer) + code2 = unparse_buffer.getvalue() + ast2 = compile(code2, filename, "exec", ast.PyCF_ONLY_AST) + self.assertASTEqual(ast1, ast2) + +class UnparseTestCase(ASTTestCase): + # Tests for specific bugs found in earlier versions of unparse + + def test_del_statement(self): + self.check_roundtrip("del x, y, z") + + def test_shifts(self): + self.check_roundtrip("45 << 2") + self.check_roundtrip("13 >> 7") + + def test_for_else(self): + self.check_roundtrip(for_else) + + def test_while_else(self): + self.check_roundtrip(while_else) + + def test_unary_parens(self): + self.check_roundtrip("(-1)**7") + self.check_roundtrip("(-1.)**8") + self.check_roundtrip("(-1j)**6") + self.check_roundtrip("not True or False") + self.check_roundtrip("True or not False") + + def test_integer_parens(self): + self.check_roundtrip("3 .__abs__()") + + def test_huge_float(self): + self.check_roundtrip("1e1000") + self.check_roundtrip("-1e1000") + self.check_roundtrip("1e1000j") + self.check_roundtrip("-1e1000j") + + def test_min_int(self): + self.check_roundtrip(str(-2**31)) + self.check_roundtrip(str(-2**63)) + + def test_imaginary_literals(self): + self.check_roundtrip("7j") + self.check_roundtrip("-7j") + self.check_roundtrip("0j") + self.check_roundtrip("-0j") + + def test_lambda_parentheses(self): + self.check_roundtrip("(lambda: int)()") + + def test_chained_comparisons(self): + self.check_roundtrip("1 < 4 <= 5") + self.check_roundtrip("a is b is c is not d") + + def test_function_arguments(self): + self.check_roundtrip("def f(): pass") + self.check_roundtrip("def f(a): pass") + self.check_roundtrip("def f(b = 2): pass") + self.check_roundtrip("def f(a, b): pass") + self.check_roundtrip("def f(a, b = 2): pass") + self.check_roundtrip("def f(a = 5, b = 2): pass") + self.check_roundtrip("def f(*, a = 1, b = 2): pass") + self.check_roundtrip("def f(*, a = 1, b): pass") + self.check_roundtrip("def f(*, a, b = 2): pass") + self.check_roundtrip("def f(a, b = None, *, c, **kwds): pass") + self.check_roundtrip("def f(a=2, *args, c=5, d, **kwds): pass") + self.check_roundtrip("def f(*args, **kwargs): pass") + + def test_relative_import(self): + self.check_roundtrip(relative_import) + + def test_nonlocal(self): + self.check_roundtrip(nonlocal_ex) + + def test_raise_from(self): + self.check_roundtrip(raise_from) + + def test_bytes(self): + self.check_roundtrip("b'123'") + + def test_annotations(self): + self.check_roundtrip("def f(a : int): pass") + self.check_roundtrip("def f(a: int = 5): pass") + self.check_roundtrip("def f(*args: [int]): pass") + self.check_roundtrip("def f(**kwargs: dict): pass") + self.check_roundtrip("def f() -> None: pass") + + def test_set_literal(self): + self.check_roundtrip("{'a', 'b', 'c'}") + + def test_set_comprehension(self): + self.check_roundtrip("{x for x in range(5)}") + + def test_dict_comprehension(self): + self.check_roundtrip("{x: x*x for x in range(10)}") + + def test_class_decorators(self): + self.check_roundtrip(class_decorator) + + def test_class_definition(self): + self.check_roundtrip("class A(metaclass=type, *[], **{}): pass") + + def test_elifs(self): + self.check_roundtrip(elif1) + self.check_roundtrip(elif2) + + def test_try_except_finally(self): + self.check_roundtrip(try_except_finally) + + def test_starred_assignment(self): + self.check_roundtrip("a, *b, c = seq") + self.check_roundtrip("a, (*b, c) = seq") + self.check_roundtrip("a, *b[0], c = seq") + self.check_roundtrip("a, *(b, c) = seq") + + def test_with_simple(self): + self.check_roundtrip(with_simple) + + def test_with_as(self): + self.check_roundtrip(with_as) + + def test_with_two_items(self): + self.check_roundtrip(with_two_items) + + +class DirectoryTestCase(ASTTestCase): + """Test roundtrip behaviour on all files in Lib and Lib/test.""" + + # test directories, relative to the root of the distribution + test_directories = 'Lib', os.path.join('Lib', 'test') + + def test_files(self): + # get names of files to test + + names = [] + for d in self.test_directories: + test_dir = os.path.join(basepath, d) + for n in os.listdir(test_dir): + if n.endswith('.py') and not n.startswith('bad'): + names.append(os.path.join(test_dir, n)) + + # Test limited subset of files unless the 'cpu' resource is specified. + if not test.support.is_resource_enabled("cpu"): + names = random.sample(names, 10) + + for filename in names: + if test.support.verbose: + print('Testing %s' % filename) + source = read_pyfile(filename) + self.check_roundtrip(source) + + +if __name__ == '__main__': + unittest.main() |