#!/usr/bin/env python3 # -*- coding: utf-8 -*- ## pylit_test.py ## ************* ## Test pylit.py Python Module ## +++++++++++++++++++++++++++ ## ## :Date: $Date: 2007-05-17 $ ## :Version: SVN-Revision $Revision: 45 $ ## :URL: $URL: svn+ssh://svn.berlios.de/svnroot/repos/pylit/trunk/test/pylit_test.py $ ## :Copyright: 2006 Günter Milde. ## Released under the terms of the GNU General Public License ## (v. 2 or later) ## ## Changelog ## ========= ## ## .. class:: borderless ## ## ====== ========== =========================================================== ## 0.1 2007-05-17 Initial version. ## 3.0 2013-09-30 Rewrite for Python 3, replace nose ## ====== ========== =========================================================== ## ## .. contents:: ## ## A catalogue of errors ## ===================== ## ## from file:///home/milde/Texte/Doc/Programmierung/Software-Carpentry/lec/unit.html ## ## * Numbers: zero, largest, smallest magnitude, most negative ## ## * Structures: empty, exactly one element, maximum number of elements ## - Duplicate elements (e.g., letter "J" appears three times in a string) ## - Aliased elements (e.g., a list contains two references to another list) ## - Circular structures (e.g., a list that contains a reference to itself) ## ## * Searching: no match found, one match found, multiple matches found, ## everything matches ## - Code like x = find_all(structure)[0] is almost always wrong ## - Should also check aliased matches (same thing found multiple times) ## ## :: """pylit_test.py: test the "literal python" module""" from pprint import pprint import operator import unittest from functools import reduce ## Be sure that we're working with the development copy of PyLit. ## :: import sys, os sys.path.insert(0, os.path.abspath("../src")) from pylit import * ## Test DefaultDict ## ================ class Test_DefaultDict(unittest.TestCase): """Test the DefaultDict dictionary with custom default""" def setUp(self): self.defdict = DefaultDict(lambda:'#') def test_get_default(self): self.assertEqual( self.defdict['nonexisting'], '#' ) def test_set_get(self): self.defdict['mykey'] = 3 self.assertEqual( self.defdict['mykey'], 3 ) def test_change_default(self): self.defdict.default_factory = lambda:'%' self.assertEqual( self.defdict['nonexisting'], '%' ) def test_init_args(self): di = DefaultDict(lambda:'#', {'mykey': 3}) self.assertEqual( di['mykey'], 3 ) self.assertEqual( di['nonexisting'], '#' ) def test_init_args2(self): di = DefaultDict(lambda:'#', mykey = 3) self.assertEqual( di['mykey'], 3 ) self.assertEqual( di['nonexisting'], '#' ) def test_init_args3(self): di = DefaultDict(lambda:'#', [('mykey', 3)]) self.assertEqual( di['mykey'], 3 ) self.assertEqual( di['nonexisting'], '#' ) ## Text <-> Code conversion ## ======================== ## ## Test strings ## ============ ## ## Example of text, code and stripped code with typical features":: text = """.. #!/usr/bin/env python3 # -*- coding: iso-8859-1 -*- Leading text in several paragraphs followed by a literal block:: block1 = 'first block' Some more text and the next block. :: block2 = 'second block' print( block1, block2 ) Trailing text. """ # print( text ) ## The converter expects the data in separate lines (iterator or list) ## with trailing newlines. We use the `splitlines` string method with ## `keepends=True`:: textdata = text.splitlines(True) # print( textdata ) ## If a "code" source is converted with the `strip` option, only text blocks ## are extracted, which leads to:: stripped_text = """Leading text in several paragraphs followed by a literal block: Some more text and the next block. Trailing text. """ ## The code corresponding to the text test string. ## ## Using a triple-quoted string for the code (and stripped_code) can create ## problems with the conversion of this test by pylit (as the text parts ## would be converted to text). ## A workaround is using a different comment string for the text blocks and ## converting with e.g. ``pylit --comment-string='## ' pylit_test.py``. ## ## :: code = """#!/usr/bin/env python3 # -*- coding: iso-8859-1 -*- # Leading text # # in several paragraphs followed by a literal block:: block1 = 'first block' # Some more text and the next block. :: block2 = 'second block' print( block1, block2 ) # Trailing text. """ # print( code ) codedata = code.splitlines(True) ## Converting the text teststring with the `strip` option leads to:: stripped_code = """#!/usr/bin/env python3 # -*- coding: iso-8859-1 -*- block1 = 'first block' block2 = 'second block' print( block1, block2 ) """ ## pprint(textdata) ## pprint(stripped_code.splitlines(True)) ## ## Containers for special case examples: ## ## 1. Text2Code samples ## ``textsamples["what"] = (, , , , code""" ## :: def setUp(self): self.converter = Text2Code(textdata) ## test helper funs :: def test_set_state_empty(self): try: self.converter.set_state([]) raise AssertionError("should raise StopIteration") except StopIteration: pass def test_set_state_header(self): """test for "header" or "documentation" for first block""" self.converter.state = "" # normally set by the `convert` method self.converter.set_state([".. header", " block"]) self.assertEqual( self.converter.state, "header" ) self.converter.state = "" # normally set by the `convert` method self.converter.set_state(["documentation", "block"]) self.assertEqual( self.converter.state, "documentation" ) def test_set_state_code_block(self): """test for "header" or "documentation" for "code_block" """ # normally set by the `convert` method self.converter._textindent = 0 self.converter.state = "code_block" self.converter.set_state(["documentation", " block"]) self.assertEqual( self.converter.state, "documentation" ) self.converter.state = "code_block" self.converter.set_state([" documentation", "block"]) self.assertEqual( self.converter.state , "documentation" ) self.converter.state = "code_block" self.converter.set_state([" code", " block"]) print( self.converter.state ) self.assertEqual( self.converter.state , "code_block" ) def test_header_handler(self): """should strip header-string from header""" self.converter._codeindent = 0 sample = [".. header", " block"] lines = [line for line in self.converter.header_handler(sample)] print( lines ) self.assertEqual( lines , ["header", "block"] ) def test_documentation_handler(self): """should add comment string to documentation""" sample = ["doc", "block", ""] lines = [line for line in self.converter.documentation_handler(sample)] print( lines ) self.assertEqual( lines, ["# doc", "# block", "#"] ) # Changed def test_documentation_handler_set_state(self): """should add comment string to documentation""" sample = ["doc", "block::", ""] lines = [line for line in self.converter.documentation_handler(sample)] print( lines ) self.assertEqual( lines, ["# doc", "# block::", ""] ) self.assertEqual( self.converter.state , "code_block" ) def test_code_block_handler(self): """should un-indent code-blocks""" self.converter._codeindent = 0 # normally set in `convert` sample = [" code", " block", ""] lines = [line for line in self.converter.code_block_handler(sample)] print( lines ) self.assertEqual( lines , ["code", "block", ""] ) ## base tests on the "long" test data :: def test_call(self): """Calling a Text2Code instance should return the converted data as list of lines""" output = self.converter() print( repr(codedata) ) print( repr(output) ) self.assertEqual( codedata, output ) def test_call_strip(self): """strip=True should strip text parts""" self.converter.strip = True output = self.converter() print( repr(stripped_code.splitlines(True)) ) print( repr(output) ) self.assertEqual( stripped_code.splitlines(True), output ) def test_str(self): outstr = str(self.converter) print( repr(code) ) print( repr(outstr) ) self.assertEqual( code, outstr ) def test_str_strip1(self): """strip=True should strip text parts. Version 1 with `strip` given as optional argument""" outstr = str(Text2Code(textdata, strip=True)) print( "ist ", repr(outstr) ) print( "soll", repr(stripped_code) ) # pprint(outstr) self.assertEqual( stripped_code, outstr ) def test_str_strip2(self): """strip=True should strip text parts Version 2 with `strip` set after instantiation""" self.converter.strip = True outstr = str(self.converter) print( "ist ", repr(outstr) ) print( "soll", repr(stripped_code) ) # pprint(outstr) self.assertEqual( stripped_code, outstr ) def test_malindented_code_line(self): """raise error if code line is less indented than code-indent""" data1 = [".. #!/usr/bin/env python3\n", # indent == 4 * " " "\n", " print('hello world')"] # indent == 2 * " " data2 = ["..\t#!/usr/bin/env python3\n", # indent == 8 * " " "\n", " print('hello world')"] # indent == 2 * " " for data in (data1, data2): try: blocks = Text2Code(data)() self.fail( "wrong indent did not raise ValueError" ) except ValueError: pass def test_str_different_comment_string(self): """Convert only comments with the specified comment string to text """ data = [".. #!/usr/bin/env python3\n", '\n', '::\n', # leading code block as header '\n', " block1 = 'first block'\n", '\n', 'more text'] soll = "\n".join(["#!/usr/bin/env python3", "", "##::", "", "block1 = 'first block'", "", "##more text"] ) outstr = str(Text2Code(data, comment_string="##")) print( "soll:", repr(soll) ) print( "ist: ", repr(outstr) ) self.assertEqual( outstr , soll ) # Filters: test pre- and postprocessing of data def test_get_filter_preprocessor(self): """should return filter from filter_set for language""" preprocessor = self.converter.get_filter("preprocessors", "rl") print( preprocessor ) self.assertEqual( preprocessor , l2r_filter ) def test_get_filter_postprocessor(self): """should return filter from filter_set for language""" postprocessor = self.converter.get_filter("postprocessors", "x") print( postprocessor ) self.assertEqual( postprocessor , u2x_filter ) def test_get_css_postprocessor(self): """should return filter from filter_set for language""" postprocessor = self.converter.get_filter("postprocessors", "css") print( postprocessor ) self.assertEqual( postprocessor , dumb_c_postprocessor ) def test_get_filter_nonexisting_language_filter(self): """should return identity_filter if language has no filter in set""" preprocessor = self.converter.get_filter("preprocessors", "foo") print( preprocessor ) self.assertEqual( preprocessor , identity_filter ) def test_get_filter_nonexisting_filter_set(self): """should return identity_filter if filter_set does not exist""" processor = self.converter.get_filter("foo_filters", "foo") print( processor ) self.assertEqual( processor , identity_filter ) def test_preprocessor(self): """Preprocess data with registered preprocessor for language""" output = Text2Code(textdata, language="x", comment_string="# ")() soll = [line for line in u2x_filter(codedata)] print( "soll: ", repr(soll) ) print( "ist: ", repr(output) ) self.assertEqual( output, soll ) def test_postprocessor(self): """Preprocess data with registered postprocessor for language""" output = Text2Code(textdata, language="x", comment_string="# ")() soll = [line for line in u2x_filter(codedata)] print( "soll:", repr(soll) ) print( "ist: ", repr(output) ) self.assertEqual( output, soll ) ## Special Cases ## ------------- ## ## Code follows text block without blank line ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## ## End of text block detected ('::') but no paragraph separator (blank line) ## follows ## ## It is an reStructuredText syntax error, if a "literal block ## marker" is not followed by a blank line. ## ## Assuming that no double colon at end of line occurs accidentally, ## pylit could fix this and issue a warning:: # Do we need this feature? (Complicates code a lot) # # textsamples["ensure blank line after text"] = ( # """text followed by a literal block:: # block1 = 'first block' # """, # """# text followed by a literal block:: # # block1 = 'first block' # """) ## Text follows code block without blank line ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## ## End of code block detected (a line not more indented than the preceding text ## block) ## ## reStructuredText syntax demands a paragraph separator (blank line) before ## it. ## ## Assuming that the unindent is not accidental, pylit could fix this and ## issues a warning:: # Do we need this feature? (Complicates code) # textsamples["ensure blank line after code"] = ( # """:: # # block1 = 'first block' # more text # """, # """# :: # # block1 = 'first block' # # more text # """) ## Options follow code-block directive ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ textsamples["code-block directive options"] = ( """\ :: :option: argument this = 'code' """, """\ # :: # :option: argument this = 'code' """) textsamples["no code-block directive options"] = ( """\ :: text following ``::`` without blank line more documentation """, """\ # :: # text following ``::`` without blank line # # more documentation """) ## A double colon on a line on its own ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## ## As a double colon is added by the Code2Text conversion after a text block ## (if not already present), it could be removed by the Text2Code conversion ## to keep the source small and pretty. ## ## However, this would put the text and code source line numbers out of sync, ## which is bad for error reporting, failing doctests, and the JED editor ## support with the `pylit_buffer()` function in ## http://jedmodes.sf.net/mode/pylit.sl . ## ## Maybe this could be left to a post-processing filter:: # textsamples["remove single double colon"] = ( # ["text followed by a literal block\n", # "\n", # "::\n", # "\n", # " foo = 'first'\n"] # ["", # empty header # "# text followed by a literal block\n\n", # "foo = 'first'\n"] ## header samples ## ~~~~~~~~~~~~~~ ## Convert a leading reStructured text comment (variant: only if there is ## content on the first line) to a leading code block. Return an empty list, ## if there is no header. :: textsamples["simple header"] = (".. print('hello world')", "print('hello world')") textsamples["no header (start with text)"] = ( """a classical example without header:: print('hello world') """, """# a classical example without header:: print('hello world') """) textsamples["no header (start with blank line)"] = ( """ a classical example without header:: print('hello world') """, """# # a classical example without header:: print('hello world') """) textsamples["standard header, followed by text"] = ( """.. #!/usr/bin/env python3 # -*- coding: iso-8859-1 -*- a classical example with header:: print('hello world') """, """#!/usr/bin/env python3 # -*- coding: iso-8859-1 -*- # a classical example with header:: print('hello world') """) textsamples["standard header, followed by code"] = ( """.. #!/usr/bin/env python3 print('hello world') """, """#!/usr/bin/env python3 print('hello world') """) textsamples["null string"] = ("", "", "") ## Code2Text ## ========= ## ## :: class Test_Code2Text(unittest.TestCase): def setUp(self): self.converter = Code2Text(codedata) ## Code2Text.strip_literal_marker ## ## * strip `::`-line as well as preceding blank line if on a line on its own ## * strip `::` if it is preceded by whitespace. ## * convert `::` to a single colon if preceded by text ## ## :: def check_strip_code_block_marker(self, sample): """test Code2Text.strip_code_block_marker""" ist = sample[0].splitlines(True) soll = sample[1].splitlines(True) print( "before", ist ) converter = Code2Text(codedata) converter.strip_code_block_marker(ist) print( "soll:", repr(soll) ) print( "ist: ", repr(ist) ) self.assertEqual( ist , soll ) def test_strip_code_block_marker(self): samples = (("text\n\n::\n\n", "text\n\n"), ("text\n::\n\n", "text\n\n"), ("text ::\n\n", "text\n\n"), ("text::\n\n", "text:\n\n"), ("text:\n\n", "text:\n\n"), ("text\n\n", "text\n\n"), ("text\n", "text\n") ) for sample in samples: yield (self.check_strip_code_block_marker, sample) ## Code2Text.set_state ## :: def test_set_state(self): samples = (("code_block", ["code_block\n"], "code_block"), ("code_block", ["#code_block\n"], "code_block"), ("code_block", ["## code_block\n"], "code_block"), ("code_block", ["# documentation\n"], "documentation"), ("code_block", ["# documentation\n"], "documentation"), ("code_block", ["# \n"], "documentation"), ("code_block", ["#\n"], "documentation"), ("code_block", ["\n"], "documentation"), ("", ["code_block\n"], "header"), ("", ["# documentation\n"], "documentation"), ("documentation", ["code_block\n"], "code_block"), ("documentation", ["# documentation\n"], "documentation"), ) print( "comment string", repr(self.converter.comment_string) ) for (old_state, lines, soll) in samples: self.converter.state = old_state self.converter.set_state(lines) print( repr(lines), "old state", old_state ) print( "soll", repr(soll), end=' ' ) print( "result", repr(self.converter.state) ) self.assertEqual( soll , self.converter.state ) ## base tests on the "long" test strings :: def test_call(self): output = self.converter() print( repr(textdata) ) print( repr(output) ) self.assertEqual( textdata, output ) def test_call_strip(self): output = Code2Text(codedata, strip=True)() print( repr(stripped_text.splitlines(True)) ) print( repr(output) ) self.assertEqual( stripped_text.splitlines(True) , output ) def test_str(self): """Test Code2Text class converting code->text""" outstr = str(self.converter) # print( text ) print( "soll:", repr(text) ) print( "ist: ", repr(outstr) ) self.assertEqual( text, outstr ) def test_str_strip(self): """Test Code2Text class converting code->rst with strip=True Should strip code blocks """ outstr = str(Code2Text(codedata, strip=True)) print( repr(stripped_text) ) print( repr(outstr) ) self.assertEqual( stripped_text , outstr ) def test_str_different_comment_string(self): """Convert only comments with the specified comment string to text """ outstr = str(Code2Text(codedata, comment_string="##", strip=True)) print( outstr ) self.assertEqual( outstr , "" ) data = ["# ::\n", "\n", "block1 = 'first block'\n", "\n", "## more text"] soll = "\n".join(['.. # ::', # leading code block as header '', # Changed " block1 = 'first block'", '', # Changed ' more text'] # keep space (not part of comment string) ) outstr = str(Code2Text(data, comment_string="##")) print( "soll:", repr(soll) ) print( "ist: ", repr(outstr) ) self.assertEqual( outstr, soll ) def test_call_different_code_block_marker(self): """recognize specified code-block marker """ data = ["# .. code-block:: python\n", "\n", "block1 = 'first block'\n", "\n", "# more text\n"] soll = ['.. code-block:: python\n', '\n', " block1 = 'first block'\n", '\n', # Changed ' more text\n'] # keep space (not part of comment string) converter = Code2Text(data, code_block_marker='.. code-block::') output = converter() print( "soll:", repr(soll) ) print( "ist: ", repr(output) ) self.assertEqual( output, soll ) # Filters: test pre- and postprocessing of Code2Text data conversion def test_get_filter_preprocessor(self): """should return Code2Text preprocessor for language""" preprocessor = self.converter.get_filter("preprocessors", "rl") print( preprocessor ) self.assertEqual( preprocessor , r2l_filter ) def test_get_css_preprocessor(self): """should return filter from filter_set for language""" preprocessor = self.converter.get_filter("preprocessors", "css") print( preprocessor ) self.assertEqual( preprocessor , dumb_c_preprocessor ) def test_get_filter_postprocessor(self): """should return Code2Text postprocessor for language""" postprocessor = self.converter.get_filter("postprocessors", "x") print( postprocessor ) self.assertEqual( postprocessor , x2u_filter ) def test_get_filter_nonexisting_language_filter(self): """should return identity_filter if language has no filter in set""" preprocessor = self.converter.get_filter("preprocessors", "foo") print( preprocessor ) self.assertEqual( preprocessor , identity_filter ) def test_get_filter_nonexisting_filter_set(self): """should return identity_filter if filter_set does not exist""" processor = self.converter.get_filter("foo_filters", "foo") print( processor ) self.assertEqual( processor , identity_filter ) def test_preprocessor(self): """Preprocess data with registered preprocessor for language""" converter = Code2Text(codedata, language="rl", comment_string="# ") print( "preprocessor", converter.preprocessor ) print( "postprocessor", converter.postprocessor ) output = converter() soll = [line.replace("r", "l") for line in textdata] print( "ist: ", repr(output) ) print( "soll:", repr(soll) ) self.assertEqual( output, soll ) def test_postprocessor(self): """Postprocess data with registered postprocessor for language""" output = Code2Text(codedata, language="x", comment_string="# ")() soll = [line.replace("x", "u") for line in textdata] print( "soll:", repr(soll) ) print( "ist: ", repr(output) ) self.assertEqual( output, soll ) ## Special cases ## ------------- ## ## blank comment line ## ~~~~~~~~~~~~~~~~~~ ## ## Normally, whitespace in the comment string is significant, i.e. with ## ``comment_string = "# "``, a line ``"#something\n"`` will count as code. ## ## However, if a comment line is blank, trailing whitespace in the comment ## string should be ignored, i.e. ``#\n`` is recognised as a blank text line:: codesamples["ignore trailing whitespace in comment string for blank line"] = ( """# :: block1 = 'first block' # # more text """, """:: block1 = 'first block' more text """) ## No blank line after text ## ~~~~~~~~~~~~~~~~~~~~~~~~ ## ## If a matching comment precedes or follows a code line (i.e. any line ## without matching comment) without a blank line in between, it counts as code ## line. ## ## This will keep small inline comments close to the code they comment on. It ## will also keep blocks together where one commented line does not match the ## comment string (the whole block will be kept as commented code) ## :: codesamples["comment before code (without blank line)"] = ( """\ # this is text:: # this is a comment foo = 'first' """, """\ this is text:: # this is a comment foo = 'first' """, """\ this is text: """) codesamples["comment block before code (without blank line)"] = ( """\ # no text (watch the comment sign in the next line):: # # this is a comment foo = 'first' """, """\ .. # no text (watch the comment sign in the next line):: # # this is a comment foo = 'first' """, "") codesamples["comment after code (without blank line)"] = ( """\ # :: block1 = 'first block' # commented code # text again """, """\ :: block1 = 'first block' # commented code text again """, """ text again """) codesamples["comment block after code (without blank line)"] = ( """\ # :: block1 = 'first block' # commented code # # still comment """, """:: block1 = 'first block' # commented code # # still comment """, """ """) ## missing literal block marker ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## ## If text (with matching comment string) is followed by code (line(s) without ## matching comment string), but there is no double colon at the end, back ## conversion would not recognise the end of text! ## ## Therefore, pylit adds a paragraph containing only ``::`` -- the literal ## block marker in expanded form. (While it would in many cases be nicer to ## add the double colon to the last text line, this is not always valid rst ## syntax, e.g. after a section header or a list. Therefore the automatic ## insertion will use the save form, feel free to correct this by hand.):: codesamples["insert missing double colon after text block"] = ( """# text followed by code without double colon foo = 'first' """, """text followed by code without double colon :: foo = 'first' """, """text followed by code without double colon """) codesamples["ignore directive options when looking for code-block marker"] = ( """\ # :: # :option: argument # :option2: argument this = 'code' """, """\ :: :option: argument :option2: argument this = 'code' """) codesamples["code-block marker followed by text not a directive option"] = ( """\ # :: # text following ``::`` without blank line this = 'code' """, """\ :: text following ``::`` without blank line :: this = 'code' """) ## header samples ## ~~~~~~~~~~~~~~ ## ## Convert a header (leading code block) to a reStructured text comment. :: codesamples["no matching comment, just code"] = ( """print('hello world') print('ende') """, """.. print('hello world') print('ende') """) codesamples["empty header (start with matching comment)"] = ( """# a classical example without header:: print('hello world') """, """a classical example without header:: print('hello world') """, """a classical example without header: """) codesamples["standard header, followed by text"] = ( """#!/usr/bin/env python3 # -*- coding: iso-8859-1 -*- # a classical example with header:: print('hello world') """, """.. #!/usr/bin/env python3 # -*- coding: iso-8859-1 -*- a classical example with header:: print('hello world') """, """a classical example with header: """) codesamples["standard header, followed by code"] = ( """#!/usr/bin/env python3 print('hello world') """, """.. #!/usr/bin/env python3 print('hello world') """, "") ## Filter tests ## ============ ## ## :: css_code = ['/* import the default Docutils style sheet */\n', '/* --------------------------------------- */\n', '\n', '/* :: */\n', '\n', '/*comment*/\n', '@import url("html4css1.css"); /* style */\n'] ## :: css_filtered_code = ['// import the default Docutils style sheet\n', '// ---------------------------------------\n', '\n', '// ::\n', '\n', '/*comment*/\n', '@import url("html4css1.css"); /* style */\n'] ## :: def test_dumb_c_preprocessor(): """convert `C` to `C++` comments""" output = [line for line in dumb_c_preprocessor(css_code)] print("ist: {0!r}".format(output)) print("soll: {0!r}".format(css_filtered_code)) assert output == css_filtered_code Test_dumb_c_preprocessor= unittest.FunctionTestCase(test_dumb_c_preprocessor) ## :: def test_dumb_c_postprocessor(): """convert `C++` to `C` comments""" output = [line for line in dumb_c_postprocessor(css_filtered_code)] print("ist: {0!r}".format(output)) print("soll: {0!r}".format(css_code)) assert output == css_code Test_dumb_c_postprocessor= unittest.FunctionTestCase(test_dumb_c_postprocessor) ## The Main Program ## ================= ## ## The overall main parts of the test program. ## We need to build the suite from the various tests. ## We can't trust to default test discovery because of the two functions ## which build test instances. :py:func:`make_Text2Code_samples` and ## :py:func:`make_Code2Text_samples` are not simple tests; they create ## tests from source code dictionaries. ## ## We'll use the ``unittest.TextTestRunner`` to execute the tests. ## :: def suite(): s = unittest.TestSuite( [ make_Text2Code_samples(), make_Code2Text_samples(), Test_x2u_filter, Test_dumb_c_preprocessor, Test_dumb_c_postprocessor, unittest.defaultTestLoader.loadTestsFromTestCase(Test_DefaultDict), unittest.defaultTestLoader.loadTestsFromTestCase(Test_TextCodeConverter), unittest.defaultTestLoader.loadTestsFromTestCase(Test_Text2Code), unittest.defaultTestLoader.loadTestsFromTestCase(Test_Code2Text), ] ) return s if __name__ == "__main__": s= suite() unittest.TextTestRunner(verbosity=2).run(s) ## Documentation Notes ## ==================== ## ## This requires a different comment string. ## The shell command looks like this. ## ## .. parsed-literal:: ## ## python3 src/pylit.py --comment-string='## ' test/pylit_test.py docs/examples/pylit_test.py.txt ## ## The use of ## comments makes it possible to handle "::" in the code.