diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d62c57c044728f..4ac7c85d2c5dca 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,6 +8,7 @@ repos: exclude: Lib/test/coding20731.py - id: trailing-whitespace types_or: [c, python, rst] + exclude: Lib/test/test_clinic.py - repo: https://github.com/sphinx-contrib/sphinx-lint rev: v0.6.7 diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index b5744f7013d6ad..0b3e6347f24d9a 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -4,7 +4,7 @@ from test import support, test_tools from test.support import os_helper -from textwrap import dedent +from textwrap import dedent, indent from unittest import TestCase import collections import inspect @@ -15,6 +15,7 @@ test_tools.skip_if_missing('clinic') with test_tools.imports_under_tool('clinic'): import clinic + import cpp from clinic import DSLParser @@ -200,6 +201,20 @@ def test_parse_with_body_prefix(self): """).lstrip() # Note, lstrip() because of the newline self.assertEqual(out, expected) + def test_cpp_monitor_fail_override(self): + raw = """ + #if + /*[clinic input] + [clinic start generated code] + #endif + """ + msg = ( + 'Error in file "test.c" on line 1:\n' + 'Invalid format for #if line: no argument!\n' + ) + out = self.expect_failure(raw) + self.assertEqual(out, msg) + class ClinicGroupPermuterTest(TestCase): def _test(self, l, m, r, output): @@ -1985,5 +2000,246 @@ def test_suffix_all_lines(self): self.assertEqual(out, expected) +class CppMonitorTests(TestCase): + + def validate_checkpoints(self, code, checkpoints): + mon = cpp.Monitor("", verbose=False) + lines = code.strip().split("\n") + for num, line in enumerate(lines, 1): + with self.subTest(num=num, line=line): + mon.writeline(line) + if num in checkpoints: + self.assertEqual(mon.status(), checkpoints[num]) + + def validate_verbose_run(self, code, expected): + mon = cpp.Monitor("", verbose=True) + with support.captured_stdout() as stdout: + mon.write(code.strip()) + out = stdout.getvalue() + expected = dedent(expected) + expected = indent(expected.lstrip(), prefix=" ") + self.assertEqual(out, expected) + + def expect_failure(self, code, msg, *, fn=None): + mon = cpp.Monitor(fn, verbose=True) + with support.captured_stdout() as stdout: + with self.assertRaises(SystemExit): + mon.write(code.strip()) + out = stdout.getvalue() + self.assertIn(msg, out) + + def test_cpp_monitor_no_defs(self): + code = """ + // one comment, no preprocessor directives + """ + checkpoints = {0: ""} + self.validate_checkpoints(code, checkpoints) + + def test_cpp_monitor_simple_if(self): + code = """ + #if MYDEF + // code + #endif + """ + checkpoints = { + 1: " 1: (MYDEF)", + 3: " 3: ", + } + self.validate_checkpoints(code, checkpoints) + + def test_cpp_monitor_simple_if_else(self): + code = """ + #if MYDEF + // code + #else + // more code + #endif + """ + checkpoints = { + 1: " 1: (MYDEF)", + 3: " 3: !(MYDEF)", + 5: " 5: ", + } + self.validate_checkpoints(code, checkpoints) + + def test_cpp_monitor_multi_if(self): + code = """ + #if MYDEF + f(); + # if OTHER + g(); + # endif + #else + h(); + # if MORE + i(); + # else + j(); + k(); + l(); + # endif + #endif + """ + checkpoints = { + 1: " 1: (MYDEF)", + 3: " 3: (MYDEF) && (OTHER)", + 5: " 5: (MYDEF)", + 7: " 7: !(MYDEF)", + 9: " 9: !(MYDEF) && (MORE)", + 11: " 11: !(MYDEF) && !(MORE)", + 12: " 12: !(MYDEF) && !(MORE)", + 13: " 13: !(MYDEF) && !(MORE)", + 15: " 15: ", + } + self.validate_checkpoints(code, checkpoints) + + def test_cpp_monitor_expr(self): + code = """ + // comment + #if VER < 1 + a(); + #elif VER < 2 + b(); + #else + c(); + #endif + // comment + """ + checkpoints = { + 2: " 2: (VER < 1)", + 4: " 4: !(VER < 1) && (VER < 2)", + 6: " 6: !(VER < 1) && !(VER < 2)", + 8: " 8: ", + } + self.validate_checkpoints(code, checkpoints) + + def test_cpp_monitor_ifdefs(self): + code = """ + #ifdef A + a(); + # if defined(B) + b(); + # endif + #endif + """ + checkpoints = { + 1: " 1: defined(A)", + 3: " 3: defined(A) && defined(B)", + 5: " 5: defined(A)", + 6: " 6: ", + } + self.validate_checkpoints(code, checkpoints) + + def test_cpp_monitor_verbose_1(self): + code = """ + #if A || B + // code + #endif + """ + expected = """ + 1: (A || B) + 3: + """ + self.validate_verbose_run(code, expected) + + def test_cpp_monitor_verbose_2(self): + code = """ + #if defined(A) && B || C + // code + # ifndef E + // code + # else + // code + #endif + #endif + """ + expected = """ + 1: (defined(A) && B || C) + 3: (defined(A) && B || C) && !defined(E) + 5: (defined(A) && B || C) && defined(E) + 7: (defined(A) && B || C) + 8: + """ + self.validate_verbose_run(code, expected) + + def test_cpp_monitor_verbose_3(self): + code = """ + #if A + # ifdef B + # else + # endif + #endif + """ + expected = """ + 1: (A) + 2: (A) && defined(B) + 3: (A) && !defined(B) + 4: (A) + 5: + """ + self.validate_verbose_run(code, expected) + + def test_cpp_monitor_verbose_cont(self): + code = r""" + #if A \ + &&\ + C + #pragma message ("") + #endif + """ + expected = """ + 3: (A && C) + 5: + """ + self.validate_verbose_run(code, expected) + + def test_cpp_monitor_fail_nested_block_comment(self): + code = """ + /* start + /* nested + */ + */ + """ + expected = dedent(""" + Error at test.c line 2 : + Nested block comment! + """).strip() + self.expect_failure(code, expected, fn="test.c") + + def test_cpp_monitor_fail_invalid_format_noarg(self): + code = """ + #if + a() + #endif + """ + expected = dedent(""" + Error at test.c line 1 : + Invalid format for #if line: no argument! + """).strip() + self.expect_failure(code, expected, fn="test.c") + + def test_cpp_monitor_fail_invalid_format_toomanyargs(self): + code = """ + #ifdef A B + a() + #endif + """ + expected = dedent(""" + Error at test.c line 1 : + Invalid format for #ifdef line: should be exactly one argument! + """).strip() + self.expect_failure(code, expected, fn="test.c") + + def test_cpp_monitor_fail_no_matching(self): + dataset = ( + ("#else", "#else without matching #if / #ifdef / #ifndef!"), + ("#endif", "#endif without matching #if / #ifdef / #ifndef!"), + ("#elif B", "#elif without matching #if / #ifdef / #ifndef!"), + ) + for line, msg in dataset: + with self.subTest(line=line): + self.expect_failure(line, msg) + + if __name__ == "__main__": unittest.main()
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: