display pylit logo and favicon
[pylit.git] / test / pylit_test.py
blobdd28eadb7e25d3dd8a98186c819edc4a6acd8ca3
1 #!/usr/bin/env python
2 # -*- coding: iso-8859-1 -*-
4 ## Test the pylit.py literal python module
5 ## =======================================
6 ##
7 ## :Date: $Date: 2007-05-17 $
8 ## :Version: SVN-Revision $Revision: 45 $
9 ## :URL: $URL: svn+ssh://svn.berlios.de/svnroot/repos/pylit/trunk/test/pylit_test.py $
10 ## :Copyright: 2006 Guenter Milde.
11 ## Released under the terms of the GNU General Public License
12 ## (v. 2 or later)
13 ##
14 ## .. contents::
15 ##
16 ##
17 ## A catalog of errors
18 ## ----------------------
19 ##
20 ## from file:///home/milde/Texte/Doc/Programmierung/Software-Carpentry/lec/unit.html
21 ##
22 ## * Numbers: zero, largest, smallest magnitude, most negative
23 ## * Structures: empty, exactly one element, maximum number of elements
24 ## - Duplicate elements (e.g., letter "J" appears three times in a string)
25 ## - Aliased elements (e.g., a list contains two references to another list)
26 ## - Circular structures (e.g., a list that contains a reference to itself)
27 ## * Searching: no match found, one match found, multiple matches found,
28 ## everything matches
29 ## - Code like x = find_all(structure)[0] is almost always wrong
30 ## - Should also check aliased matches (same thing found multiple times)
31 ##
32 ## ::
34 """pylit_test.py: test the "literal python" module"""
36 from pprint import pprint
37 import operator
38 from pylit import *
39 import nose
41 ## Text <-> Code conversion
42 ## ========================
43 ##
44 ## Test strings
45 ## ------------
46 ##
47 ## Example of text, code and stripped code with typical features"::
49 text = """.. #!/usr/bin/env python
50 # -*- coding: iso-8859-1 -*-
52 Leading text
54 in several paragraphs followed by a literal block::
56 block1 = 'first block'
58 Some more text and the next block. ::
60 block2 = 'second block'
61 print block1, block2
63 Trailing text.
64 """
65 # print text
67 ## The converter expects the data in separate lines (iterator or list)
68 ## with trailing newlines. We use the `splitlines` string method with
69 ## `keepends=True`::
71 textdata = text.splitlines(True)
72 # print textdata
74 ## If a "code" source is converted with the `strip` option, only text blocks
75 ## are extracted, which leads to::
77 stripped_text = """Leading text
79 in several paragraphs followed by a literal block:
81 Some more text and the next block.
83 Trailing text.
84 """
86 ## The code corresponding to the text test string.
87 ##
88 ## Using a triple-quoted string for the code (and stripped_code) can create
89 ## problems with the conversion of this test by pylit (as the text parts
90 ## would be converted to text).
91 ## A workaround is using a different comment string for the text blocks and
92 ## converting with e.g. ``pylit --comment-string='## ' pylit_test.py``.
93 ##
94 ## ::
96 code = """#!/usr/bin/env python
97 # -*- coding: iso-8859-1 -*-
99 # Leading text
101 # in several paragraphs followed by a literal block::
103 block1 = 'first block'
105 # Some more text and the next block. ::
107 block2 = 'second block'
108 print block1, block2
110 # Trailing text.
112 # print code
114 codedata = code.splitlines(True)
116 ## Converting the text teststring with the `strip` option leads to::
118 stripped_code = """#!/usr/bin/env python
119 # -*- coding: iso-8859-1 -*-
121 block1 = 'first block'
123 block2 = 'second block'
124 print block1, block2
128 ## pprint(textdata)
129 ## pprint(stripped_code.splitlines(True))
131 ## Containers for special case examples:
133 ## 1. Text2Code samples
134 ## ``textsamples["what"] = (<text data>, <output>, <output (with `strip`)``
135 ## ::
137 textsamples = {}
139 ## 2. Code2Text samples
140 ## ``codesamples["what"] = (<code data>, <output>, <output (with `strip`)``
141 ## ::
143 codesamples = {}
145 ## Auxiliary function to test the textsamples and codesamples::
147 def check_converter(key, converter, output):
148 print "E:", key
149 extract = converter()
150 print(extract)
151 outstr = "".join(extract)
152 print "soll:", repr(output)
153 print "ist: ", repr(outstr)
154 assert output == outstr
156 ## Test generator for textsample tests::
158 def test_Text2Code_samples():
159 for key, sample in textsamples.iteritems():
160 yield (check_converter, key,
161 Text2Code(sample[0].splitlines(True)), sample[1])
162 if len(sample) == 3:
163 yield (check_converter, key,
164 Text2Code(sample[0].splitlines(True), strip=True),
165 sample[2])
167 ## Test generator for codesample tests::
169 def test_Code2Text_samples():
170 for key, sample in codesamples.iteritems():
171 yield (check_converter, key,
172 Code2Text(sample[0].splitlines(True)), sample[1])
173 if len(sample) == 3:
174 yield (check_converter, key,
175 Code2Text(sample[0].splitlines(True), strip=True),
176 sample[2])
179 ## Pre and postprocessing filters (for testing the filter hooks)
181 ## ::
183 def r2l_filter(data):
184 print "applying r2l filter"
185 for line in data:
186 yield line.replace("r", "l")
188 ## ::
190 defaults.preprocessors["rl2text"] = r2l_filter
192 ## ::
194 def l2r_filter(data):
195 print "applying l2r filter"
196 for line in data:
197 yield line.replace("l", "r")
199 ## ::
201 defaults.preprocessors["text2rl"] = l2r_filter
203 ## ::
205 def x2u_filter(data):
206 print "applying x2u filter"
207 for line in data:
208 yield line.replace("x", "u")
210 ## ::
212 defaults.postprocessors["x2text"] = x2u_filter
214 ## ::
216 def u2x_filter(data):
217 print "applying u2x filter"
218 for line in data:
219 yield line.replace("u", "x")
221 ## ::
223 defaults.postprocessors["text2x"] = u2x_filter
225 ## ::
227 def test_x2u_filter():
228 soll = text.replace("x", "u")
229 result = "".join([line for line in x2u_filter(textdata)])
230 print "soll", repr(text)
231 print "ist", repr(result)
232 assert soll == result
236 ## TextCodeConverter
237 ## -----------------
239 ## ::
241 class test_TextCodeConverter(object):
242 """Test the TextCodeConverter parent class"""
244 ## ::
246 def check_marker_regexp_true(self, sample):
247 match = self.converter.marker_regexp.search(sample)
248 print 'marker: %r; sample %r' %(self.converter.code_block_marker, sample)
249 print 'match %r'%match
250 assert match is not None
252 ## ::
254 def check_marker_regexp_false(self, sample):
255 assert self.converter.marker_regexp.search(sample) is None
257 ## ::
259 def test_marker_regexp(self):
260 # Samples
261 literal = ['::',
262 ' ::',
263 't ::',
264 'text::',
265 ' indented::',
266 ' indented ::',
267 'more text :: ',
268 ' indented text :: ',
269 '. no-directive::',
270 'a .. directive:: somewhere::']
271 directive = ['.. code-block:: python',
272 ' .. code-block:: python',
273 '.. code-block:: python listings',
274 ' .. code-block:: python listings']
275 misses = ['.. comment string ::',
276 '.. ::',
277 'text:']
278 # default code_block_marker ('::')
279 self.converter = TextCodeConverter(textdata)
280 for sample in literal:
281 yield (self.check_marker_regexp_true, sample)
282 for sample in directive+misses:
283 yield (self.check_marker_regexp_false, sample)
284 # code-block directive as marker
285 self.converter = TextCodeConverter(textdata,
286 code_block_marker='.. code-block::')
287 for sample in directive:
288 yield (self.check_marker_regexp_true, sample)
289 for sample in literal+misses:
290 yield (self.check_marker_regexp_false, sample)
292 ## ::
294 def test_get_indent(self):
295 converter = TextCodeConverter(textdata)
296 assert converter.get_indent("foo") == 0
297 assert converter.get_indent(" foo") == 1
298 assert converter.get_indent(" foo") == 2
300 ## ::
302 def test_collect_blocks(self):
303 converter = TextCodeConverter(textdata)
304 textblocks = [block for block in collect_blocks(textdata)]
305 print textblocks
306 assert len(textblocks) == 7, "text sample has 7 blocks"
307 assert reduce(operator.__add__, textblocks) == textdata
309 ## Text2Code
310 ## ---------
312 ## ::
314 class test_Text2Code(object):
315 """Test the Text2Code class converting rst->code"""
317 ## ::
319 def setUp(self):
320 self.converter = Text2Code(textdata)
322 ## test helper funs ::
324 def test_set_state_empty(self):
325 try:
326 self.converter.set_state([])
327 raise AssertionError, "should raise StopIteration"
328 except StopIteration:
329 pass
331 def test_set_state_header(self):
332 """test for "header" or "documentation" for first block"""
333 self.converter.state = "" # normally set by the `convert` method
334 self.converter.set_state([".. header", " block"])
335 assert self.converter.state == "header"
336 self.converter.state = "" # normally set by the `convert` method
337 self.converter.set_state(["documentation", "block"])
338 assert self.converter.state == "documentation"
340 def test_set_state_code_block(self):
341 """test for "header" or "documentation" for "code_block" """
342 # normally set by the `convert` method
343 self.converter._textindent = 0
344 self.converter.state = "code_block"
345 self.converter.set_state(["documentation", " block"])
346 assert self.converter.state == "documentation"
348 self.converter.state = "code_block"
349 self.converter.set_state([" documentation", "block"])
350 assert self.converter.state == "documentation"
352 self.converter.state = "code_block"
353 self.converter.set_state([" code", " block"])
354 print self.converter.state
355 assert self.converter.state == "code_block"
357 def test_header_handler(self):
358 """should strip header-string from header"""
359 self.converter._codeindent = 0
360 sample = [".. header", " block"]
361 lines = [line for line in self.converter.header_handler(sample)]
362 print lines
363 assert lines == ["header", "block"]
365 def test_documentation_handler(self):
366 """should add comment string to documentation"""
367 sample = ["doc", "block", ""]
368 lines = [line for line
369 in self.converter.documentation_handler(sample)]
370 print lines
371 assert lines == ["# doc", "# block", "# "]
373 def test_documentation_handler_set_state(self):
374 """should add comment string to documentation"""
375 sample = ["doc", "block::", ""]
376 lines = [line for line
377 in self.converter.documentation_handler(sample)]
378 print lines
379 assert lines == ["# doc", "# block::", ""]
380 assert self.converter.state == "code_block"
382 def test_code_block_handler(self):
383 """should un-indent code-blocks"""
384 self.converter._codeindent = 0 # normally set in `convert`
385 sample = [" code", " block", ""]
386 lines = [line for line
387 in self.converter.code_block_handler(sample)]
388 print lines
389 assert lines == ["code", "block", ""]
392 ## base tests on the "long" test data ::
394 def test_call(self):
395 """Calling a Text2Code instance should return the converted data as list of lines"""
396 output = self.converter()
397 print repr(codedata)
398 print repr(output)
399 assert codedata == output
401 def test_call_strip(self):
402 """strip=True should strip text parts"""
403 self.converter.strip = True
404 output = self.converter()
405 print repr(stripped_code.splitlines(True))
406 print repr(output)
407 assert stripped_code.splitlines(True) == output
409 def test_str(self):
410 outstr = str(self.converter)
411 print repr(code)
412 print repr(outstr)
413 assert code == outstr
415 def test_str_strip1(self):
416 """strip=True should strip text parts.
418 Version 1 with `strip` given as optional argument"""
419 outstr = str(Text2Code(textdata, strip=True))
420 print "ist ", repr(outstr)
421 print "soll", repr(stripped_code)
422 # pprint(outstr)
423 assert stripped_code == outstr
425 def test_str_strip2(self):
426 """strip=True should strip text parts
428 Version 2 with `strip` set after instantiation"""
429 self.converter.strip = True
430 outstr = str(self.converter)
431 print "ist ", repr(outstr)
432 print "soll", repr(stripped_code)
433 # pprint(outstr)
434 assert stripped_code == outstr
436 def test_malindented_code_line(self):
437 """raise error if code line is less indented than code-indent"""
438 data1 = [".. #!/usr/bin/env python\n", # indent == 4 * " "
439 "\n",
440 " print 'hello world'"] # indent == 2 * " "
441 data2 = ["..\t#!/usr/bin/env python\n", # indent == 8 * " "
442 "\n",
443 " print 'hello world'"] # indent == 2 * " "
444 for data in (data1, data2):
445 try:
446 blocks = Text2Code(data)()
447 assert False, "wrong indent did not raise ValueError"
448 except ValueError:
449 pass
451 def test_str_different_comment_string(self):
452 """Convert only comments with the specified comment string to text
454 data = [".. #!/usr/bin/env python\n",
455 '\n',
456 '::\n', # leading code block as header
457 '\n',
458 " block1 = 'first block'\n",
459 ' \n',
460 'more text']
461 soll = "\n".join(["#!/usr/bin/env python",
463 "##::",
465 "block1 = 'first block'",
467 "##more text"]
469 outstr = str(Text2Code(data, comment_string="##"))
470 print "soll:", repr(soll)
471 print "ist: ", repr(outstr)
472 assert outstr == soll
474 # Filters: test pre- and postprocessing of data
476 def test_get_filter_preprocessor(self):
477 """should return filter from filter_set for language"""
478 preprocessor = self.converter.get_filter("preprocessors", "rl")
479 print preprocessor
480 assert preprocessor == l2r_filter
482 def test_get_filter_postprocessor(self):
483 """should return filter from filter_set for language"""
484 postprocessor = self.converter.get_filter("postprocessors", "x")
485 print postprocessor
486 assert postprocessor == u2x_filter
488 def test_get_css_postprocessor(self):
489 """should return filter from filter_set for language"""
490 postprocessor = self.converter.get_filter("postprocessors", "css")
491 print postprocessor
492 assert postprocessor == dumb_c_postprocessor
494 def test_get_filter_nonexisting_language_filter(self):
495 """should return identity_filter if language has no filter in set"""
496 preprocessor = self.converter.get_filter("preprocessors", "foo")
497 print preprocessor
498 assert preprocessor == identity_filter
500 def test_get_filter_nonexisting_filter_set(self):
501 """should return identity_filter if filter_set does not exist"""
502 processor = self.converter.get_filter("foo_filters", "foo")
503 print processor
504 assert processor == identity_filter
506 def test_preprocessor(self):
507 """Preprocess data with registered preprocessor for language"""
508 output = Text2Code(textdata, language="x", comment_string="# ")()
509 soll = [line for line in u2x_filter(codedata)]
510 print "soll: ", repr(soll)
511 print "ist: ", repr(output)
512 assert output == soll
514 def test_postprocessor(self):
515 """Preprocess data with registered postprocessor for language"""
516 output = Text2Code(textdata, language="x", comment_string="# ")()
517 soll = [line for line in u2x_filter(codedata)]
518 print "soll:", repr(soll)
519 print "ist: ", repr(output)
520 assert output == soll
522 ## Special Cases
523 ## ~~~~~~~~~~~~~
525 ## Code follows text block without blank line
526 ## ''''''''''''''''''''''''''''''''''''''''''
528 ## End of text block detected ('::') but no paragraph separator (blank line)
529 ## follows
531 ## It is an reStructuredText syntax error, if a "literal block
532 ## marker" is not followed by a blank line.
534 ## Assuming that no double colon at end of line occures accidentially,
535 ## pylit could fix this and issue a warning::
537 # Do we need this feature? (Complicates code a lot)
538 # textsamples["ensure blank line after text"] = (
539 # """text followed by a literal block::
540 # block1 = 'first block'
541 # """,
542 # """# text followed by a literal block::
544 # block1 = 'first block'
545 # """)
547 ## Text follows code block without blank line
548 ## ''''''''''''''''''''''''''''''''''''''''''
550 ## End of code block detected (a line not more indented than the preceding text
551 ## block)
553 ## reStructuredText syntax demands a paragraph separator (blank line) before
554 ## it.
556 ## Assuming that the unindent is not accidential, pylit could fix this and
557 ## issues a warning::
559 # Do we need this feature? (Complicates code)
560 # textsamples["ensure blank line after code"] = (
561 # """::
563 # block1 = 'first block'
564 # more text
565 # """,
566 # """# ::
568 # block1 = 'first block'
570 # more text
571 # """)
573 ## A double colon on a line on its own
574 ## '''''''''''''''''''''''''''''''''''
576 ## As a double colon is added by the Code2Text conversion after a text block
577 ## (if not already present), it could be removed by the Text2Code conversion
578 ## to keep the source small and pretty.
580 ## However, this would put the text and code source line numbers out of sync,
581 ## which is bad for error reporting, failing doctests, and the JED editor
582 ## support with the `pylit_buffer()` function in
583 ## http://jedmodes.sf.net/mode/pylit.sl .
585 ## Maybe this could be left to a post-processing filter::
587 # textsamples["remove single double colon"] = (
588 # ["text followed by a literal block\n",
589 # "\n",
590 # "::\n",
591 # "\n",
592 # " foo = 'first'\n"]
593 # ["", # empty header
594 # "# text followed by a literal block\n\n",
595 # "foo = 'first'\n"]
597 ## header samples
598 ## ''''''''''''''
599 ## Convert a leading reStructured text comment (variant: only if there is
600 ## content on the first line) to a leading code block. Return an empty list,
601 ## if there is no header. ::
603 textsamples["simple header"] = (".. print 'hello world'",
604 "print 'hello world'")
606 textsamples["no header (start with text)"] = (
607 """a classical example without header::
609 print 'hello world'
610 """,
611 """# a classical example without header::
613 print 'hello world'
614 """)
617 textsamples["no header (start with blank line)"] = (
619 a classical example without header::
621 print 'hello world'
622 """,
623 """#
624 # a classical example without header::
626 print 'hello world'
627 """)
630 textsamples["standard header, followed by text"] = (
631 """.. #!/usr/bin/env python
632 # -*- coding: iso-8859-1 -*-
634 a classical example with header::
636 print 'hello world'
637 """,
638 """#!/usr/bin/env python
639 # -*- coding: iso-8859-1 -*-
641 # a classical example with header::
643 print 'hello world'
644 """)
646 textsamples["standard header, followed by code"] = (
647 """.. #!/usr/bin/env python
649 print 'hello world'
650 """,
651 """#!/usr/bin/env python
653 print 'hello world'
654 """)
656 textsamples["null string"] = ("", "", "")
658 ## Code2Text
659 ## ---------
661 ## ::
663 class test_Code2Text(object):
665 def setUp(self):
666 self.converter = Code2Text(codedata)
668 ## Code2Text.strip_literal_marker
670 ## * strip `::`-line as well as preceding blank line if on a line on its own
671 ## * strip `::` if it is preceded by whitespace.
672 ## * convert `::` to a single colon if preceded by text
674 ## ::
675 def check_strip_code_block_marker(self, sample):
676 """test Code2Text.strip_code_block_marker"""
677 ist = sample[0].splitlines(True)
678 soll = sample[1].splitlines(True)
679 print "before", ist
680 converter = Code2Text(codedata)
681 converter.strip_code_block_marker(ist)
682 print "soll:", repr(soll)
683 print "ist: ", repr(ist)
684 assert ist == soll
687 def test_strip_code_block_marker(self):
688 samples = (("text\n\n::\n\n", "text\n\n"),
689 ("text\n::\n\n", "text\n\n"),
690 ("text ::\n\n", "text\n\n"),
691 ("text::\n\n", "text:\n\n"),
692 ("text:\n\n", "text:\n\n"),
693 ("text\n\n", "text\n\n"),
694 ("text\n", "text\n")
696 for sample in samples:
697 yield (self.check_strip_code_block_marker, sample)
699 ## Code2Text.set_state
700 ## ::
702 def test_set_state(self):
703 samples = (("code_block", ["code_block\n"], "code_block"),
704 ("code_block", ["#code_block\n"], "code_block"),
705 ("code_block", ["## code_block\n"], "code_block"),
706 ("code_block", ["# documentation\n"], "documentation"),
707 ("code_block", ["# documentation\n"], "documentation"),
708 ("code_block", ["# \n"], "documentation"),
709 ("code_block", ["#\n"], "documentation"),
710 ("code_block", ["\n"], "documentation"),
711 ("", ["code_block\n"], "header"),
712 ("", ["# documentation\n"], "documentation"),
713 ("documentation", ["code_block\n"], "code_block"),
714 ("documentation", ["# documentation\n"], "documentation"),
716 print "comment string", repr(self.converter.comment_string)
717 for (old_state, lines, soll) in samples:
718 self.converter.state = old_state
719 self.converter.set_state(lines)
720 print repr(lines), "old state", old_state
721 print "soll", repr(soll),
722 print "result", repr(self.converter.state)
723 assert soll == self.converter.state
725 ## base tests on the "long" test strings ::
727 def test_call(self):
728 output = self.converter()
729 print repr(textdata)
730 print repr(output)
731 assert textdata == output
733 def test_call_strip(self):
734 output = Code2Text(codedata, strip=True)()
735 print repr(stripped_text.splitlines(True))
736 print repr(output)
737 assert stripped_text.splitlines(True) == output
739 def test_str(self):
740 """Test Code2Text class converting code->text"""
741 outstr = str(self.converter)
742 # print text
743 print "soll:", repr(text)
744 print "ist: ", repr(outstr)
745 assert text == outstr
747 def test_str_strip(self):
748 """Test Code2Text class converting code->rst with strip=True
750 Should strip code blocks
752 outstr = str(Code2Text(codedata, strip=True))
753 print repr(stripped_text)
754 print repr(outstr)
755 assert stripped_text == outstr
757 def test_str_different_comment_string(self):
758 """Convert only comments with the specified comment string to text
760 outstr = str(Code2Text(codedata, comment_string="##", strip=True))
761 print outstr
762 assert outstr == ""
763 data = ["# ::\n",
764 "\n",
765 "block1 = 'first block'\n",
766 "\n",
767 "## more text"]
768 soll = "\n".join(['.. # ::', # leading code block as header
769 ' ',
770 " block1 = 'first block'",
771 ' ',
772 ' more text'] # keep space (not part of comment string)
774 outstr = str(Code2Text(data, comment_string="##"))
775 print "soll:", repr(soll)
776 print "ist: ", repr(outstr)
777 assert outstr == soll
779 def test_call_different_code_block_marker(self):
780 """recognize specified code-block marker
782 data = ["# .. code-block:: python\n",
783 "\n",
784 "block1 = 'first block'\n",
785 "\n",
786 "# more text\n"]
787 soll = ['.. code-block:: python\n',
788 '\n',
789 " block1 = 'first block'\n",
790 ' \n',
791 ' more text\n'] # keep space (not part of comment string)
793 converter = Code2Text(data, code_block_marker='.. code-block::')
794 output = converter()
795 print "soll:", repr(soll)
796 print "ist: ", repr(output)
797 assert output == soll
799 # Filters: test pre- and postprocessing of Code2Text data conversion
801 def test_get_filter_preprocessor(self):
802 """should return Code2Text preprocessor for language"""
803 preprocessor = self.converter.get_filter("preprocessors", "rl")
804 print preprocessor
805 assert preprocessor == r2l_filter
807 def test_get_css_preprocessor(self):
808 """should return filter from filter_set for language"""
809 preprocessor = self.converter.get_filter("preprocessors", "css")
810 print preprocessor
811 assert preprocessor == dumb_c_preprocessor
813 def test_get_filter_postprocessor(self):
814 """should return Code2Text postprocessor for language"""
815 postprocessor = self.converter.get_filter("postprocessors", "x")
816 print postprocessor
817 assert postprocessor == x2u_filter
819 def test_get_filter_nonexisting_language_filter(self):
820 """should return identity_filter if language has no filter in set"""
821 preprocessor = self.converter.get_filter("preprocessors", "foo")
822 print preprocessor
823 assert preprocessor == identity_filter
825 def test_get_filter_nonexisting_filter_set(self):
826 """should return identity_filter if filter_set does not exist"""
827 processor = self.converter.get_filter("foo_filters", "foo")
828 print processor
829 assert processor == identity_filter
831 def test_preprocessor(self):
832 """Preprocess data with registered preprocessor for language"""
833 converter = Code2Text(codedata, language="rl", comment_string="# ")
834 print "preprocessor", converter.preprocessor
835 print "postprocessor", converter.postprocessor
836 output = converter()
837 soll = [line.replace("r", "l") for line in textdata]
838 print "ist: ", repr(output)
839 print "soll:", repr(soll)
840 assert output == soll
842 def test_postprocessor(self):
843 """Postprocess data with registered postprocessor for language"""
844 output = Code2Text(codedata, language="x", comment_string="# ")()
845 soll = [line.replace("x", "u") for line in textdata]
846 print "soll:", repr(soll)
847 print "ist: ", repr(output)
848 assert output == soll
851 ## Special cases
852 ## ~~~~~~~~~~~~~
854 ## blank comment line
855 ## ''''''''''''''''''''
857 ## Normally, whitespace in the comment string is significant, i.e. with
858 ## ``comment_string = "# "``, a line ``"#something\n"`` will count as code.
860 ## However, if a comment line is blank, trailing whitespace in the comment
861 ## string should be ignored, i.e. ``#\n`` is recognized as a blank text line::
863 codesamples["ignore trailing whitespace in comment string for blank line"] = (
864 """# ::
866 block1 = 'first block'
869 # more text
870 """,
871 """::
873 block1 = 'first block'
876 more text
877 """)
879 ## No blank line after text
880 ## ''''''''''''''''''''''''
882 ## If a matching comment precedes oder follows a code line (i.e. any line
883 ## without matching comment) without a blank line inbetween, it counts as code
884 ## line.
886 ## This will keep small inline comments close to the code they comment on. It
887 ## will also keep blocks together where one commented line does not match the
888 ## comment string (the whole block will be kept as commented code)
889 ## ::
891 codesamples["comment before code (without blank line)"] = (
892 """# this is text::
894 # this is a comment
895 foo = 'first'
896 """,
897 """this is text::
899 # this is a comment
900 foo = 'first'
901 """,
902 """this is text:
904 """)
906 codesamples["comment block before code (without blank line)"] = (
907 """# no text (watch the comment sign in the next line)::
909 # this is a comment
910 foo = 'first'
911 """,
912 """.. # no text (watch the comment sign in the next line)::
914 # this is a comment
915 foo = 'first'
916 """,
919 codesamples["comment after code (without blank line)"] = (
920 """# ::
922 block1 = 'first block'
923 # commented code
925 # text again
926 """,
927 """::
929 block1 = 'first block'
930 # commented code
932 text again
933 """,
935 text again
936 """)
938 codesamples["comment block after code (without blank line)"] = (
939 """# ::
941 block1 = 'first block'
942 # commented code
944 # still comment
945 """,
946 """::
948 block1 = 'first block'
949 # commented code
951 # still comment
952 """,
954 """)
956 ## missing literal block marker
957 ## ''''''''''''''''''''''''''''
959 ## If text (with matching comment string) is followed by code (line(s) without
960 ## matching comment string), but there is no double colon at the end, back
961 ## conversion would not recognize the end of text!
963 ## Therefore, pylit adds a paragraph containing only ``::`` -- the literal
964 ## block marker in expanded form. (While it would in many cases be nicer to
965 ## add the double colon to the last text line, this is not always valid rst
966 ## syntax, e.g. after a section header or a list. Therefore the automatic
967 ## insertion will use the save form, feel free to correct this by hand.)::
969 codesamples["insert missing double colon after text block"] = (
970 """# text followed by code without double colon
972 foo = 'first'
973 """,
974 """text followed by code without double colon
978 foo = 'first'
979 """,
980 """text followed by code without double colon
982 """)
984 ## header samples
985 ## ''''''''''''''
987 ## Convert a header (leading code block) to a reStructured text comment. ::
989 codesamples["no matching comment, just code"] = (
990 """print 'hello world'
992 print 'ende'
993 """,
994 """.. print 'hello world'
996 print 'ende'
997 """)
999 codesamples["empty header (start with matching comment)"] = (
1000 """# a classical example without header::
1002 print 'hello world'
1003 """,
1004 """a classical example without header::
1006 print 'hello world'
1007 """,
1008 """a classical example without header:
1010 """)
1012 codesamples["standard header, followed by text"] = (
1013 """#!/usr/bin/env python
1014 # -*- coding: iso-8859-1 -*-
1016 # a classical example with header::
1018 print 'hello world'
1019 """,
1020 """.. #!/usr/bin/env python
1021 # -*- coding: iso-8859-1 -*-
1023 a classical example with header::
1025 print 'hello world'
1026 """,
1027 """a classical example with header:
1029 """)
1031 codesamples["standard header, followed by code"] = (
1032 """#!/usr/bin/env python
1034 print 'hello world'
1035 """,
1036 """.. #!/usr/bin/env python
1038 print 'hello world'
1039 """,
1043 ## Filter tests
1044 ## ------------
1046 ## ::
1048 css_code = ['/* import the default docutils style sheet */\n',
1049 '/* --------------------------------------- */\n',
1050 '\n',
1051 '/* :: */\n',
1052 '\n',
1053 '/*comment*/\n',
1054 '@import url("html4css1.css"); /* style */\n']
1056 ## ::
1058 css_filtered_code = ['// import the default docutils style sheet\n',
1059 '// ---------------------------------------\n',
1060 '\n',
1061 '// ::\n',
1062 '\n',
1063 '/*comment*/\n',
1064 '@import url("html4css1.css"); /* style */\n']
1066 ## ::
1068 def test_dumb_c_preprocessor():
1069 """convert `C` to `C++` comments"""
1070 output = [line for line in dumb_c_preprocessor(css_code)]
1071 print "ist: %r"%output
1072 print "soll: %r"%css_filtered_code
1073 assert output == css_filtered_code
1075 ## ::
1077 def test_dumb_c_postprocessor():
1078 """convert `C++` to `C` comments"""
1079 output = [line for line in dumb_c_postprocessor(css_filtered_code)]
1080 print "ist: %r"%output
1081 print "soll: %r"%css_code
1082 assert output == css_code
1086 ## ::
1088 if __name__ == "__main__":
1089 nose.runmodule() # requires nose 0.9.1
1090 sys.exit()