Merge lp:~adiroiban/pocket-lint/986239 into lp:pocket-lint

Proposed by Adi Roiban
Status: Merged
Approved by: Curtis Hovey
Approved revision: 469
Merged at revision: 468
Proposed branch: lp:~adiroiban/pocket-lint/986239
Merge into: lp:pocket-lint
Diff against target: 868 lines (+790/-2)
4 files modified
pocketlint/formatcheck.py (+245/-0)
pocketlint/tests/__init__.py (+1/-0)
pocketlint/tests/test_restructuredtext.py (+524/-0)
test.py (+20/-2)
To merge this branch: bzr merge lp:~adiroiban/pocket-lint/986239
Reviewer Review Type Date Requested Status
Curtis Hovey code Approve
Review via email: mp+102977@code.launchpad.net

Description of the change

Hi,

This is my initial rst checker.

It contains a few changes outside of the rst part.

1. For the test runner I added the option to select the test based on regex. In this was I was able to run only a subset of test modules.

2. For the general CheckerTestCase, i set self.reporter.call_count = 0 in the setUp. Not sure if this is really needed.

Please let me know if you want me to revert these changes.
----

Now for the RST part.

For the Language class, I wanted to sort the language type identifier objects, mimetypes.add_type and mime_type_language in alphabetic order... but I didn't want to add to much noise to the diff.

I know that it is hard to force a certain coding convention and the current rules are made base on the style used in the Python documentation.

It does not checks for rst compilation errors, but rather for coding conventions.

For not it only checks for sections styles and an empty line at the end of file.

Please let me know if you thinks this is on a right direction and what should be changed.

Thanks!
Adi

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

Thank you very much.

I have merged this and pushed it to Launchpad. I think there are a few methods that return True or False that could be made smaller and more precise. I do not think they need changing just yet. I think it is best that this feature be merged and used so that we know exactly where we want to improve it.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'pocketlint/formatcheck.py'
--- pocketlint/formatcheck.py 2012-04-20 16:52:00 +0000
+++ pocketlint/formatcheck.py 2012-04-21 17:17:20 +0000
@@ -178,6 +178,7 @@
178 DOCBOOK = object()178 DOCBOOK = object()
179 LOG = object()179 LOG = object()
180 SQL = object()180 SQL = object()
181 RESTRUCTUREDTEXT = object()
181182
182 XML_LIKE = (XML, XSLT, HTML, ZPT, ZCML, DOCBOOK)183 XML_LIKE = (XML, XSLT, HTML, ZPT, ZCML, DOCBOOK)
183184
@@ -186,6 +187,7 @@
186 mimetypes.add_type('text/x-python-doctest', '.doctest')187 mimetypes.add_type('text/x-python-doctest', '.doctest')
187 mimetypes.add_type('text/x-twisted-application', '.tac')188 mimetypes.add_type('text/x-twisted-application', '.tac')
188 mimetypes.add_type('text/x-log', '.log')189 mimetypes.add_type('text/x-log', '.log')
190 mimetypes.add_type('text/x-rst', '.rst')
189 mime_type_language = {191 mime_type_language = {
190 'text/x-python': PYTHON,192 'text/x-python': PYTHON,
191 'text/x-twisted-application': PYTHON,193 'text/x-twisted-application': PYTHON,
@@ -195,6 +197,7 @@
195 'text/plain': TEXT,197 'text/plain': TEXT,
196 'text/x-sql': SQL,198 'text/x-sql': SQL,
197 'text/x-log': LOG,199 'text/x-log': LOG,
200 'text/x-rst': RESTRUCTUREDTEXT,
198 'application/javascript': JAVASCRIPT,201 'application/javascript': JAVASCRIPT,
199 'application/xml': XML,202 'application/xml': XML,
200 'application/x-sh': SH,203 'application/x-sh': SH,
@@ -648,6 +651,248 @@
648 self.check_tab(line_no, line)651 self.check_tab(line_no, line)
649652
650653
654class ReStructuredTextChecker(BaseChecker, AnyTextMixin):
655 """Check reStructuredText ource code."""
656
657 # Taken from rst documentation.
658 delimiter_characters = [
659 '=', '-', '`', ':', '\'', '"', '~', '^', '_', '*', '+', '#', '<', '>',
660 ]
661
662 def __init__(self, file_path, text, reporter=None):
663 super(ReStructuredTextChecker, self).__init__(
664 file_path, text, reporter=reporter)
665 self.lines = self.text.splitlines()
666
667 def check(self):
668 """Check the syntax of the reStructuredText code."""
669 self.check_lines()
670 self.check_empty_last_line()
671
672 def check_lines(self):
673 """Call each line checker for each line in text."""
674 for line_no, line in enumerate(self.lines):
675 line_no += 1
676 self.check_length(line_no, line)
677 self.check_trailing_whitespace(line_no, line)
678 self.check_tab(line_no, line)
679 self.check_conflicts(line_no, line)
680
681 if self.isTransition(line_no - 1):
682 self.check_transition(line_no - 1)
683 elif self.isSectionDelimiter(line_no - 1):
684 self.check_section_delimiter(line_no - 1)
685 else:
686 pass
687
688 def isTransition(self, line_number):
689 '''Return True if the current line is a line transition.'''
690 line = self.lines[line_number]
691 if len(line) < 4:
692 return False
693
694 if len(self.lines) < 3:
695 return False
696
697 succesive_characters = (
698 line[0] == line[1] == line[2] == line[3] and
699 line[0] in self.delimiter_characters)
700
701 if not succesive_characters:
702 return False
703
704 emply_lines_bounded = (
705 self.lines[line_number - 1] == '' and
706 self.lines[line_number + 1] == '')
707
708 if not emply_lines_bounded:
709 return False
710
711 return True
712
713 def check_transition(self, line_number):
714 '''Transitions should be delimited by a single emtpy line.'''
715 if (self.lines[line_number - 2] == '' or
716 self.lines[line_number + 2] == ''):
717 self.message(
718 line_number + 1,
719 'Transition markers should be bounded by single empty lines.',
720 icon='info',
721 )
722
723 def isSectionDelimiter(self, line_number):
724 '''Return true if the line is a section delimiter.'''
725 if len(self.lines) < 3:
726 return False
727
728 if line_number >= len(self.lines):
729 return False
730
731 line = self.lines[line_number]
732 if len(line) < 3:
733 return False
734
735 if (line[0] == line[1] == line[2] and line[0] in
736 self.delimiter_characters):
737 if ' ' in line:
738 # We have a table header.
739 return False
740 else:
741 return True
742
743 return False
744
745 def check_section_delimiter(self, line_number):
746 """Checks for section delimiter.
747
748 These checkes are designed for sections delimited by top and bottom
749 markers.
750
751 ======= <- top marker
752 Section <- text_line
753 ======= <- bottom marker
754
755 If the section is delimted only by bottom marker, the section text
756 is considered the top marker.
757
758 Section <- top marker, text_line
759 ======= <- bottom marker
760
761 If the section has a custom anchor name:
762
763 .. _link <- top marker
764
765 =======
766 Section <- text_line
767 ======= <- bottom marker
768
769 or:
770
771 .. _link <- top marker
772
773 Section <- text_line
774 ======= <- bottom marker
775
776 If we have top and bottom markers, the check will be called twice (
777 for each marker). In this case we will skip the tests for bottom
778 marker.
779 """
780 human_line_number = line_number + 1
781 current_line = self.lines[line_number]
782
783 # Skip test if we have both top and bottom markers and we are
784 # at the bottom marker.
785 if (line_number > 1 and current_line == self.lines[line_number - 2]):
786 return
787
788 if ((line_number + 2) < len(self.lines) and
789 current_line == self.lines[line_number + 2]):
790 # We have both top and bottom markers and we are currently at
791 # the top marker.
792 top_marker = line_number
793 text_line = line_number + 1
794 bottom_marker = line_number + 2
795 else:
796 # We only have bottom marker, and are at the bottom marker.
797 top_marker = line_number - 1
798 text_line = line_number - 1
799 bottom_marker = line_number
800
801 # In case we have a custom anchor, the top_marker is replaced by
802 # the custom anchor.
803 if self._sectionHasCustomAnchor(top_marker):
804 top_marker = top_marker - 2
805
806 # Check underline length for bottom marker,
807 # since top marker can be the same as text line.
808 if len(self.lines[bottom_marker]) != len(self.lines[text_line]):
809 self.message(
810 human_line_number,
811 'Section marker has wrong length.',
812 icon='error',
813 )
814
815 if not self._haveGoodSpacingBeforeSection(top_marker):
816 self.message(
817 human_line_number,
818 'Section should be divided by 2 empty lines.',
819 icon='info',
820 )
821
822 if not self._haveGoodSpacingAfterSection(bottom_marker):
823 self.message(
824 human_line_number,
825 'Section title should be followed by 1 empty line.',
826 icon='info',
827 )
828
829 def _sectionHasCustomAnchor(self, top_marker):
830 if (top_marker - 2) < 0:
831 return False
832
833 if self.lines[top_marker - 2].startswith('.. _'):
834 return True
835
836 return False
837
838 def _haveGoodSpacingBeforeSection(self, top_marker):
839 '''Return True if we have good spacing before the section.'''
840 if top_marker > 0:
841 if self.lines[top_marker - 1] != '':
842 return False
843
844 # If we are on the second line, there is no space for 2 empty lines
845 # before.
846 if top_marker == 1:
847 return False
848
849 if top_marker > 1:
850 if self.lines[top_marker - 2] != '':
851 return False
852
853 if top_marker > 2:
854 if self.lines[top_marker - 3] == '':
855 return False
856
857 return True
858
859 def _haveGoodSpacingAfterSection(self, bottom_marker):
860 '''Return True if we have good spacing after the section.'''
861 lines_count = len(self.lines)
862
863 if bottom_marker < lines_count - 1:
864 if self.lines[bottom_marker + 1] != '':
865 return False
866
867 if bottom_marker < lines_count - 2:
868 if self.lines[bottom_marker + 2] == '':
869 # If the section is followed by 2 empty spaces and then
870 # followed by a section delimiter, the section delimiter
871 # rules will take priority
872 if self.isSectionDelimiter(bottom_marker + 3):
873 return True
874 if self.isSectionDelimiter(bottom_marker + 4):
875 return True
876 return False
877
878 return True
879
880 def check_empty_last_line(self):
881 """Chech the files ends with an emtpy line and not with double empty
882 line.
883
884 This will avoid merge conflicts.
885 """
886 if len(self.lines) < 2:
887 return
888 if self.text[-1] != '\n' or self.text[-2:] == '\n\n':
889 self.message(
890 len(self.lines),
891 'File does not ends with an empty line.',
892 icon='info',
893 )
894
895
651def get_option_parser():896def get_option_parser():
652 """Return the option parser for this program."""897 """Return the option parser for this program."""
653 usage = "usage: %prog [options] file1 file2"898 usage = "usage: %prog [options] file1 file2"
654899
=== modified file 'pocketlint/tests/__init__.py'
--- pocketlint/tests/__init__.py 2012-01-29 21:24:20 +0000
+++ pocketlint/tests/__init__.py 2012-04-21 17:17:20 +0000
@@ -11,3 +11,4 @@
1111
12 def setUp(self):12 def setUp(self):
13 self.reporter = Reporter(Reporter.COLLECTOR)13 self.reporter = Reporter(Reporter.COLLECTOR)
14 self.reporter.call_count = 0
1415
=== added file 'pocketlint/tests/test_restructuredtext.py'
--- pocketlint/tests/test_restructuredtext.py 1970-01-01 00:00:00 +0000
+++ pocketlint/tests/test_restructuredtext.py 2012-04-21 17:17:20 +0000
@@ -0,0 +1,524 @@
1'''Tests for ReStructuredTextChecker.'''
2from pocketlint.formatcheck import ReStructuredTextChecker
3from pocketlint.tests import CheckerTestCase
4
5# This is a valid rst content.
6# This comment is here so that the content starts at line 11
7# and make it easier to identify errors in tests.
8# Just add 10 to the reported line number.
9#
10valid_rst_content = '''\
11=============
12First section
13=============
14
15Text *for* first **section**.
16
17
18--------------------
19Second emtpy section
20--------------------
21
22
23Third section
24^^^^^^^^^^^^^
25
26Paragrhap for
27third section `with link<http://my.home>`_.
28
29::
30
31 Literal block1.
32
33 Literal block paragraph.
34
35
36| Line blocks are useful for addresses,
37| verse, and adornment-free lists.
38
39
40.. _section-permalink:
41
42Another Section with predefined link
43------------------------------------
44
45>>> print "This is a doctest block."
46... with a line continuation
47This is a doctest block.
48
49A grid table.
50
51+------------+------------+-----------+
52| Header 1 | Header 2 | Header 3 |
53+============+============+===========+
54| body row 1 | column 2 | column 3 |
55+------------+------------+-----------+
56| body row 2 | Cells may span columns.|
57+------------+------------+-----------+
58| body row 3 | Cells may | - Cells |
59+------------+ span rows. | - contain |
60| body row 4 | | - blocks. |
61+------------+------------+-----------+
62
63A simple table.
64
65===== ===== ======
66 Inputs Output
67------------ ------
68 A B A or B
69===== ===== ======
70False False False
71True False True
72False True True
73True True True
74===== ===== ======
75
76A transition marker is a horizontal line
77of 4 or more repeated punctuation
78characters.
79
80------------
81
82A transition should not begin or end a
83section or document, nor should two
84transitions be immediately adjacent.
85
86Footnote references, like [5]_.
87Note that footnotes may get
88rearranged, e.g., to the bottom of
89the "page".
90
91.. [5] A numerical footnote. Note
92 there's no colon after the ``]``.
93
94External hyperlinks, like Python_.
95.. _Python: http://www.python.org/
96
97For instance:
98
99.. image:: images/ball1.gif
100
101The |biohazard| symbol must be used on containers used to dispose of
102medical waste.
103
104.. |biohazard| image:: biohazard.png
105
106.. This text will not be shown
107 (but, for instance, in HTML might be
108 rendered as an HTML comment)
109'''
110# The last line from multi line string is a bit hard to visualize,
111# but it is there.
112
113
114class TestReStructuredTextChecker(CheckerTestCase):
115 """Verify reStructuredTextChecker checking."""
116
117 def test_empty_file(self):
118 self.reporter.call_count = 0
119 content = ('')
120 checker = ReStructuredTextChecker('bogus', content, self.reporter)
121 checker.check()
122 self.assertEqual([], self.reporter.messages)
123 self.assertEqual(0, self.reporter.call_count)
124
125 def test_valid_content(self):
126 self.reporter.call_count = 0
127 checker = ReStructuredTextChecker(
128 'bogus', valid_rst_content, self.reporter)
129 checker.check()
130 self.assertEqual([], self.reporter.messages)
131 self.assertEqual(0, self.reporter.call_count)
132
133 def test_no_empty_last_line(self):
134 self.reporter.call_count = 0
135 content = (
136 'Some first line\n'
137 'the second and last line witout newline'
138 )
139 checker = ReStructuredTextChecker('bogus', content, self.reporter)
140 checker.check_empty_last_line()
141 expected = [(
142 2, 'File does not ends with an empty line.')]
143 self.assertEqual(expected, self.reporter.messages)
144 self.assertEqual(1, self.reporter.call_count)
145
146 def test_multiple_empty_last_lines(self):
147 self.reporter.call_count = 0
148 content = (
149 'Some first line\n'
150 'the second and last\n'
151 '\n'
152 )
153 checker = ReStructuredTextChecker('bogus', content, self.reporter)
154 checker.check_empty_last_line()
155 expected = [(
156 3, 'File does not ends with an empty line.')]
157 self.assertEqual(expected, self.reporter.messages)
158 self.assertEqual(1, self.reporter.call_count)
159
160 def test_isTransition_good(self):
161 content = (
162 '\n'
163 '----\n'
164 '\n'
165 )
166 checker = ReStructuredTextChecker('bogus', content, self.reporter)
167 result = checker.isTransition(1)
168 self.assertTrue(result)
169
170 def test_isTransition_short_line(self):
171 content = (
172 '\n'
173 '---\n'
174 '\n'
175 )
176 checker = ReStructuredTextChecker('bogus', content, self.reporter)
177 result = checker.isTransition(1)
178 self.assertFalse(result)
179
180 def test_isTransition_short_file(self):
181 content = (
182 '\n'
183 '----\n'
184 ''
185 )
186 checker = ReStructuredTextChecker('bogus', content, self.reporter)
187 result = checker.isTransition(1)
188 self.assertFalse(result)
189
190 def test_isTransition_false(self):
191 content = (
192 '\n'
193 '----\n'
194 'some\n'
195 )
196 checker = ReStructuredTextChecker('bogus', content, self.reporter)
197 result = checker.isTransition(1)
198 self.assertFalse(result)
199
200 content = (
201 'some\n'
202 '----\n'
203 '\n'
204 )
205 checker = ReStructuredTextChecker('bogus', content, self.reporter)
206 result = checker.isTransition(1)
207 self.assertFalse(result)
208
209 def test_check_transitions_good(self):
210 content = (
211 'some text\n'
212 '\n'
213 '----\n'
214 '\n'
215 'some text\n'
216 )
217 checker = ReStructuredTextChecker('bogus', content, self.reporter)
218 checker.check_transition(2)
219 self.assertEqual([], self.reporter.messages)
220 self.assertEqual(0, self.reporter.call_count)
221
222 def test_check_transitions_bad_spacing_before(self):
223 content = (
224 'some text\n'
225 '\n'
226 '\n'
227 '----\n'
228 '\n'
229 'some text\n'
230 )
231 checker = ReStructuredTextChecker('bogus', content, self.reporter)
232 checker.check_transition(3)
233 expect = [(
234 4, 'Transition markers should be bounded by single empty lines.')]
235 self.assertEqual(expect, self.reporter.messages)
236 self.assertEqual(1, self.reporter.call_count)
237
238 def test_check_transitions_bad_spacing_after(self):
239 content = (
240 'some text\n'
241 '\n'
242 '----\n'
243 '\n'
244 '\n'
245 'some text\n'
246 )
247 checker = ReStructuredTextChecker('bogus', content, self.reporter)
248 checker.check_transition(2)
249 expect = [(
250 3, 'Transition markers should be bounded by single empty lines.')]
251 self.assertEqual(expect, self.reporter.messages)
252 self.assertEqual(1, self.reporter.call_count)
253
254 def test_check_transitions_bad_spacing_both(self):
255 content = (
256 'some text\n'
257 '\n'
258 '\n'
259 '----\n'
260 '\n'
261 '\n'
262 'some text\n'
263 )
264 checker = ReStructuredTextChecker('bogus', content, self.reporter)
265 checker.check_transition(3)
266 expect = [(
267 4, 'Transition markers should be bounded by single empty lines.')]
268 self.assertEqual(expect, self.reporter.messages)
269 self.assertEqual(1, self.reporter.call_count)
270
271 def test_isSectionDelimiter_short_file(self):
272 content = (
273 'Something'
274 '---------\n'
275 ''
276 )
277 checker = ReStructuredTextChecker('bogus', content, self.reporter)
278 result = checker.isSectionDelimiter(1)
279 self.assertFalse(result)
280
281 def test_isSectionDelimiter_short_line(self):
282 content = (
283 'Som'
284 '---\n'
285 ''
286 )
287 checker = ReStructuredTextChecker('bogus', content, self.reporter)
288 result = checker.isSectionDelimiter(1)
289 self.assertFalse(result)
290
291 def test_isSectionDelimiter_table(self):
292 content = (
293 '---- ----'
294 'Row1 Row1'
295 '---- ----\n'
296 ''
297 )
298 checker = ReStructuredTextChecker('bogus', content, self.reporter)
299 result = checker.isSectionDelimiter(0)
300 self.assertFalse(result)
301 result = checker.isSectionDelimiter(2)
302 self.assertFalse(result)
303
304 def test_isSectionDelimiter_good(self):
305 content = (
306 'Section\n'
307 '-------\n'
308 'some text'
309 )
310 checker = ReStructuredTextChecker('bogus', content, self.reporter)
311 result = checker.isSectionDelimiter(1)
312 self.assertTrue(result)
313
314 def test_isSectionDelimiter_good_bounded_start_of_file(self):
315 content = (
316 '=======\n'
317 'Section\n'
318 '=======\n'
319 )
320 checker = ReStructuredTextChecker('bogus', content, self.reporter)
321 result = checker.isSectionDelimiter(0)
322 self.assertTrue(result)
323 result = checker.isSectionDelimiter(2)
324 self.assertTrue(result)
325
326 def test_check_section_delimiter_bounded(self):
327 content = (
328 'some text\n'
329 '\n'
330 '\n'
331 '=======\n'
332 'Section\n'
333 '=======\n'
334 '\n'
335 'some text\n'
336 )
337 checker = ReStructuredTextChecker('bogus', content, self.reporter)
338 checker.check_section_delimiter(3)
339 checker.check_section_delimiter(5)
340 self.assertEqual([], self.reporter.messages)
341 self.assertEqual(0, self.reporter.call_count)
342
343 def test_check_section_delimiter_bad_marker_length(self):
344 content = (
345 'Section\n'
346 '------\n'
347 '\n'
348 'some text\n'
349 'other text\n'
350 )
351 checker = ReStructuredTextChecker('bogus', content, self.reporter)
352 checker.check_section_delimiter(1)
353 expect = [(2, 'Section marker has wrong length.')]
354 self.assertEqual(expect, self.reporter.messages)
355 self.assertEqual(1, self.reporter.call_count)
356
357 def test_check_section_delimiter_bad_length_both_markers(self):
358 content = (
359 '---------\n'
360 'Section\n'
361 '---------\n'
362 '\n'
363 'some text\n'
364 'other text\n'
365 )
366 checker = ReStructuredTextChecker('bogus', content, self.reporter)
367 checker.check_section_delimiter(0)
368 checker.check_section_delimiter(2)
369 expect = [(1, 'Section marker has wrong length.')]
370 self.assertEqual(expect, self.reporter.messages)
371 self.assertEqual(1, self.reporter.call_count)
372
373 def test_check_section_before_space_good_start_both(self):
374 content = (
375 '-------\n'
376 'Section\n'
377 '-------\n'
378 '\n'
379 )
380 checker = ReStructuredTextChecker('bogus', content, self.reporter)
381 checker.check_section_delimiter(0)
382 checker.check_section_delimiter(2)
383 self.assertEqual([], self.reporter.messages)
384 self.assertEqual(0, self.reporter.call_count)
385
386 def test_check_section_before_space_good_start_bottom(self):
387 content = (
388 'Section\n'
389 '-------\n'
390 '\n'
391 )
392 checker = ReStructuredTextChecker('bogus', content, self.reporter)
393 checker.check_section_delimiter(1)
394 self.assertEqual([], self.reporter.messages)
395 self.assertEqual(0, self.reporter.call_count)
396
397 def test_check_section_before_space_bad_only_one_line_near_start(self):
398 content = (
399 '\n'
400 'Section\n'
401 '-------\n'
402 '\n'
403 )
404 checker = ReStructuredTextChecker('bogus', content, self.reporter)
405 checker.check_section_delimiter(2)
406 expect = [(3, 'Section should be divided by 2 empty lines.')]
407 self.assertEqual(expect, self.reporter.messages)
408 self.assertEqual(1, self.reporter.call_count)
409
410 def test_check_section_before_space_bad_only_one_line(self):
411 content = (
412 'end of previous section.\n'
413 '\n'
414 'Section\n'
415 '-------\n'
416 '\n'
417 )
418 checker = ReStructuredTextChecker('bogus', content, self.reporter)
419 checker.check_section_delimiter(3)
420 expect = [(4, 'Section should be divided by 2 empty lines.')]
421 self.assertEqual(expect, self.reporter.messages)
422 self.assertEqual(1, self.reporter.call_count)
423
424 def test_check_section_before_space_multiple_empty_lines(self):
425 content = (
426 'end of previous section.\n'
427 '\n'
428 '\n'
429 '\n'
430 'Section\n'
431 '-------\n'
432 '\n'
433 )
434 checker = ReStructuredTextChecker('bogus', content, self.reporter)
435 checker.check_section_delimiter(5)
436 expect = [(6, 'Section should be divided by 2 empty lines.')]
437 self.assertEqual(expect, self.reporter.messages)
438 self.assertEqual(1, self.reporter.call_count)
439
440 def test_check_section_after_space_last_line(self):
441 content = (
442 'Section\n'
443 '-------\n'
444 )
445 checker = ReStructuredTextChecker('bogus', content, self.reporter)
446 checker.check_section_delimiter(1)
447 self.assertEqual([], self.reporter.messages)
448 self.assertEqual(0, self.reporter.call_count)
449
450 def test_check_section_after_space_bad(self):
451 content = (
452 'Section\n'
453 '-------\n'
454 'Paragraph start.\n'
455 )
456 checker = ReStructuredTextChecker('bogus', content, self.reporter)
457 checker.check_section_delimiter(1)
458 expect = [(2, 'Section title should be followed by 1 empty line.')]
459 self.assertEqual(expect, self.reporter.messages)
460 self.assertEqual(1, self.reporter.call_count)
461
462 def test_check_section_after_space_too_many_empty_lines(self):
463 content = (
464 'Section\n'
465 '-------\n'
466 '\n'
467 '\n'
468 'Paragraph start.\n'
469 )
470 checker = ReStructuredTextChecker('bogus', content, self.reporter)
471 checker.check_section_delimiter(1)
472 expect = [(2, 'Section title should be followed by 1 empty line.')]
473 self.assertEqual(expect, self.reporter.messages)
474 self.assertEqual(1, self.reporter.call_count)
475
476 def test_check_section_empty_section_next_section_only_bottom(self):
477 content = (
478 'Emtpy Section\n'
479 '=============\n'
480 '\n'
481 '\n'
482 'Another Section\n'
483 '---------------\n'
484 )
485 checker = ReStructuredTextChecker('bogus', content, self.reporter)
486 checker.check_section_delimiter(1)
487 self.assertEqual([], self.reporter.messages)
488 self.assertEqual(0, self.reporter.call_count)
489
490 def test_check_section_empty_section_next_section_both_markers(self):
491 content = (
492 'Emtpy Section\n'
493 '=============\n'
494 '\n'
495 '\n'
496 '---------------\n'
497 'Another Section\n'
498 '---------------\n'
499 )
500 checker = ReStructuredTextChecker('bogus', content, self.reporter)
501 checker.check_section_delimiter(1)
502 self.assertEqual([], self.reporter.messages)
503 self.assertEqual(0, self.reporter.call_count)
504
505 def disable_check_section_delimiter_both_markers_not_sync(self):
506 # When both top and bottom markers are used, and they don't have
507 # the same size, they are interpreted as separate markers.
508 content = (
509 '------\n'
510 'Section\n'
511 '--------\n'
512 '\n'
513 'some text\n'
514 'other text\n'
515 )
516 checker = ReStructuredTextChecker('bogus', content, self.reporter)
517 checker.check_lines()
518 expect = [
519 (1, 'Section marker has wrong length.'),
520 (1, 'Section title should be followed by 1 empty line.'),
521 (3, 'Section marker has wrong length.'),
522 ]
523 self.assertEqual(expect, self.reporter.messages)
524 self.assertEqual(3, self.reporter.call_count)
0525
=== modified file 'test.py'
--- test.py 2012-01-29 21:24:20 +0000
+++ test.py 2012-04-21 17:17:20 +0000
@@ -6,6 +6,7 @@
66
7import re7import re
8import os8import os
9import sys
9import unittest10import unittest
10try:11try:
11 from unittest.runner import _WritelnDecorator12 from unittest.runner import _WritelnDecorator
@@ -81,23 +82,40 @@
81 self.stream.write(text)82 self.stream.write(text)
8283
8384
84def find_tests(root_dir):85def find_tests(root_dir, filter=None):
85 """Generate a list of matching test modules below a directory."""86 """Generate a list of matching test modules below a directory."""
86 for path, subdirs, files in os.walk(root_dir):87 for path, subdirs, files in os.walk(root_dir):
87 subdirs[:] = [dir for dir in subdirs]88 subdirs[:] = [dir for dir in subdirs]
88 if path.endswith('tests'):89 if path.endswith('tests'):
89 for file_ in files:90 for file_ in files:
90 if file_.startswith('test_') and file_.endswith('.py'):91 if file_.startswith('test_') and file_.endswith('.py'):
92 if filter and not re.search(filter, file_):
93 continue
91 file_path = os.path.join(path, file_)94 file_path = os.path.join(path, file_)
92 test_module = file_path[2:-3].replace('/', '.')95 test_module = file_path[2:-3].replace('/', '.')
93 yield test_module96 yield test_module
9497
9598
99def show_help():
100 print '''\
101python test.py [-h|--help] [test_module_regex_filter]
102'''
103
104
96def main():105def main():
106 if len(sys.argv) > 1:
107 if (sys.argv[1] in ['-h', '--help']):
108 show_help()
109 return
110 else:
111 filter = sys.argv[1]
112 else:
113 filter = None
114
97 unittest.runner._WritelnDecorator = XTermWritelnDecorator115 unittest.runner._WritelnDecorator = XTermWritelnDecorator
98 test_loader = unittest.defaultTestLoader116 test_loader = unittest.defaultTestLoader
99 suite = unittest.TestSuite()117 suite = unittest.TestSuite()
100 for test_module in find_tests('.'):118 for test_module in find_tests('.', filter=filter):
101 suite.addTest(test_loader.loadTestsFromName(test_module))119 suite.addTest(test_loader.loadTestsFromName(test_module))
102 unittest.TextTestRunner(verbosity=2).run(suite)120 unittest.TextTestRunner(verbosity=2).run(suite)
103121

Subscribers

People subscribed via source and target branches

to all changes: