Attachment '01.test-include.patch'
Download 1 # HG changeset patch
2 # User gerg.ward+moin@gmail.com
3 # Date 1225379530 14400
4 # Node ID abcab597f0bf4bf291c8df9903dc09d7e92d8349
5 # Parent 70a5344a1c5645cb9b02646c2d7a31f3773a5084
6 Add tests for Include macro.
7
8 diff -r 70a5344a1c56 -r abcab597f0bf MoinMoin/macro/_tests/test_Include.py
9 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10 +++ b/MoinMoin/macro/_tests/test_Include.py Thu Oct 30 11:12:10 2008 -0400
11 @@ -0,0 +1,437 @@
12 +# -*- coding: utf-8 -*-
13 +"""
14 + MoinMoin - MoinMoin.macro.Include Tests
15 +
16 + @copyright: 2008 MoinMoin:GregWard
17 + @license: GNU GPL, see COPYING for details.
18 +"""
19 +
20 +import re
21 +from MoinMoin._tests import become_trusted, create_page, make_macro, nuke_page
22 +
23 +class BaseTest:
24 + includer_pagename = u'IncluderPage'
25 +
26 + def _execute(self, args):
27 + """Execute macro Include in self.includer_page with args, returning the result."""
28 + macro = make_macro(self.request, self.includer_page)
29 + result = macro.execute('Include', args)
30 + print "macro result: %r" % result
31 + return result
32 +
33 + def _assert_regex(self, expect_re, actual_result, pos):
34 + print "expected result to match %s from pos %d" % (expect_re.pattern, pos)
35 + match = expect_re.search(actual_result, pos=pos)
36 + assert match is not None
37 + return match
38 +
39 + def _assert_included(self, expect_content, actual_result, pos=0):
40 + # Hmmm: the space after the included content seems strange, but
41 + # it is how Include behaves.
42 + content_re = re.compile(r'<p class="\w+">%s<span' % re.escape(expect_content + " "))
43 + return self._assert_regex(content_re, actual_result, pos=pos)
44 +
45 + def _assert_sysmsg(self, expect_class, expect_message, actual_result, pos=0):
46 + msg_re = re.compile(r'<p><strong class="%s">%s</strong></p>'
47 + % (re.escape(expect_class), re.escape(expect_message)))
48 + return self._assert_regex(msg_re, actual_result, pos=pos)
49 +
50 + _open_heading_pattern = r'<h%d( id=\"[\w\.\-]+\")?>'
51 + _close_heading_pattern = r'</h%d>'
52 +
53 + _open_link_pattern = r'<a( class=\"[^\"]*\")? href=\"%s\">'
54 + _close_link_pattern = r'</a>'
55 +
56 + def _assert_heading(self, expect_level, expect_content, actual_result, pos=0):
57 + heading_re = re.compile((self._open_heading_pattern % expect_level) +
58 + re.escape(expect_content) +
59 + (self._close_heading_pattern % expect_level))
60 + return self._assert_regex(heading_re, actual_result, pos=pos)
61 +
62 + def _assert_heading_link(self, expect_level, expect_content, expect_href_pattern,
63 + actual_result, pos=0):
64 + heading_re = re.compile((self._open_heading_pattern % expect_level) +
65 + (self._open_link_pattern % expect_href_pattern) +
66 + re.escape(expect_content) +
67 + (self._close_link_pattern) +
68 + (self._close_heading_pattern % expect_level))
69 + return self._assert_regex(heading_re, actual_result, pos=pos)
70 +
71 + def _assert_link(self, expect_href_pattern, expect_text, actual_result, pos=0):
72 + link_re = re.compile(r'<a (class=\"[^\"]*\")?\s*href=\"%s\">%s</a>'
73 + % (expect_href_pattern, re.escape(expect_text)))
74 + return self._assert_regex(link_re, actual_result, pos=pos)
75 +
76 +class TestInclude(BaseTest):
77 + """tests for macro Include"""
78 + included_pagename = u'IncludedPage'
79 + included_content = u'Here is included content!'
80 +
81 + def setup_method(self, method):
82 + assert isinstance(self, TestInclude)
83 +
84 + request = self.request
85 + become_trusted(request)
86 + self._create_included_page()
87 + self.includer_page = create_page(
88 + request, self.includer_pagename, u'Argh')
89 +
90 + def teardown_method(self, method):
91 + nuke_page(self.request, self.included_pagename)
92 + nuke_page(self.request, self.includer_pagename)
93 + self.request.action = "show" # in case of tinkering
94 +
95 + def _create_included_page(self, pagename=included_pagename, content=included_content):
96 + return create_page(self.request, pagename, content)
97 +
98 + def testIncludeSimple(self):
99 + result = self._execute(u'IncludedPage')
100 + self._assert_included(self.included_content, result)
101 +
102 + def testIncludeNoArgs(self):
103 + # with no args, Include() expands to an error message
104 + result = self._execute(u'')
105 + assert 'Invalid include arguments' in result
106 +
107 + def testIncludeBadPage(self):
108 + # with a bad page name, Include returns the empty string (no error message!)
109 + result = self._execute(u'BogusPage')
110 + assert result == ""
111 +
112 + def testIncludeRecursive(self):
113 + # a page cannot include itself -- doing so returns an error message
114 + self._create_included_page(content=u'Hello <<Include(IncludedPage)>> recursion!')
115 + result = self._execute(u'IncludedPage')
116 + self._assert_sysmsg('error', 'Recursive include of "IncludedPage" forbidden', result)
117 +
118 + def testIncludeRecursiveIndirect(self):
119 + # indirect recursion -- A includes B, B includes A -- is also detected
120 + self._create_included_page(pagename=u'Included1',
121 + content=u'Outer: <<Include(Included2)>> blah')
122 + self._create_included_page(pagename=u'Included2',
123 + content=u'Inner: <<Include(Included1)>> argh')
124 + try:
125 + result = self._execute(u'Included1')
126 + self._assert_sysmsg('error', 'Recursive include of "Included1" forbidden', result)
127 + finally:
128 + nuke_page(self.request, u'Included1')
129 + nuke_page(self.request, u'Included2')
130 +
131 + def testIncludePermissions(self):
132 + # including a page that the current user does not have rights to
133 + # returns an empty result
134 + permissions = self.request.user.may
135 + saved = permissions.read
136 + try:
137 + # Blecch! This is rather evil. But it's easier than trying to figure
138 + # out how to correctly revoke the current user's permission to read
139 + # IncludedPage. ;-)
140 + permissions.read = lambda pagename: False
141 + result = self._execute(u'IncludedPage')
142 + assert result == ""
143 + finally:
144 + permissions.read = saved
145 +
146 + def testIncludeMarkup(self):
147 + # If the included page includes wiki markup, it is rendered to
148 + # HTML by the macro.
149 + # XXX this makes assumptions about the formatter: is that evil?
150 + self._create_included_page(content=u'This is \'\'\'bold\'\'\' text')
151 + result = self._execute(u'IncludedPage')
152 + self._assert_included('This is <strong>bold</strong> text', result)
153 +
154 + def testIncludeHeading(self):
155 + # You can add a heading to the included content by passing 'heading'
156 + # arg to Include. The heading is actually a link to the included page,
157 + # which we test by using _assert_heading_link().
158 + heading = u'Flubby Flobby'
159 + result = self._execute(u'IncludedPage, "%s"' % heading)
160 +
161 + match = self._assert_heading_link(1, heading, r'\./IncludedPage', result)
162 + match = self._assert_included(self.included_content, result, pos=match.end())
163 +
164 + def testIncludeHeadingLevel(self):
165 + # Add a heading with a specific level.
166 + heading = u'Weeble Wobble'
167 + result = self._execute(u'IncludedPage, "%s", 3' % heading)
168 +
169 + match = self._assert_heading_link(3, heading, r'\./IncludedPage', result)
170 + match = self._assert_included(self.included_content, result, pos=match.end())
171 +
172 + def testIncludeFrom(self):
173 + # Start including at the point right after text that matches the 'from' regex.
174 + included_content = 'Here is some --delimited-- content'
175 + self._create_included_page(content=included_content)
176 + result = self._execute(u'IncludedPage,,, from="-+"')
177 +
178 + self._assert_included('delimited-- content', result)
179 + assert "Here is some" not in result
180 +
181 + def testIncludeFromBadRegex(self):
182 + # If the user passes a bad "from" regex -- say, r"foo[" -- then
183 + # Include assumes the user meant to escape the metacharacters
184 + # and does it for them, changing the regex to r"foo\[".
185 + included_content = u'start including after foo[that string].'
186 + self._create_included_page(content=included_content)
187 +
188 + # First: do it with the correct regex syntax, so Include does
189 + # not have to catch re.error.
190 + result1 = self._execute(u'IncludedPage,,, from="foo\\["')
191 + self._assert_included(u'that string].', result1)
192 +
193 + # Now trip the exception and see that Include escapes the "[".
194 + result2 = self._execute(u'IncludedPage,,, from="foo["')
195 + self._assert_included(u'that string].', result2)
196 +
197 + def testIncludeFromNoMatch(self):
198 + # If the 'from' regex does not match the included page, Include
199 + # inserts a warning message and then completely includes the
200 + # other page.
201 + result = self._execute(u'IncludedPage,,, from="foo"')
202 + self._assert_sysmsg('warning', 'Include: Nothing found for "foo"!', result)
203 + self._assert_included(self.included_content, result)
204 +
205 + # Sigh. The tests for 'to' are very similar to the tests for
206 + # 'from'. But the code being tested is also quite repetitive, so
207 + # it's important to test both 'from' and 'to' thoroughly before
208 + # refactoring!
209 +
210 + def testIncludeTo(self):
211 + # Stop including at the point before after text that matches the 'to' regex.
212 + included_content = 'Here is some --delimited-- content'
213 + self._create_included_page(content=included_content)
214 + result = self._execute(u'IncludedPage,,, to="-+"')
215 +
216 + self._assert_included('Here is some ', result)
217 + assert "--delimited-- content" not in result
218 +
219 + def testIncludeToBadRegex(self):
220 + # As with 'from', a bad 'to' regex gets quoted and tried again.
221 + included_content = u'include up to foo[that string'
222 + self._create_included_page(content=included_content)
223 +
224 + # First with correct regex syntax.
225 + result1 = self._execute(u'IncludedPage,,, to="foo\\["')
226 + self._assert_included(u'include up to ', result1)
227 +
228 + # Now trip the exception and make sure we get the same thing.
229 + result2 = self._execute(u'IncludedPage,,, to="foo["')
230 + self._assert_included(u'include up to ', result2)
231 +
232 + def testIncludeToNoMatch(self):
233 + # As with 'from', a bad 'to' regex results in a warning.
234 + result = self._execute(u'IncludedPage,,, to="foo"')
235 + self._assert_sysmsg('warning', 'Include: Nothing found for "foo"!', result)
236 + self._assert_included(self.included_content, result)
237 +
238 + def testIncludeEditLinks(self):
239 + result = self._execute(u'IncludedPage,,, editlink')
240 + match = self._assert_included(self.included_content, result)
241 + match = self._assert_link(
242 + ur'\./IncludedPage', u'[IncludedPage]', result, pos=match.end())
243 + match = self._assert_link(
244 + ur'\./IncludedPage\?action=edit.*?', u'[edit]', result, pos=match.end())
245 +
246 + def testIncludeHeadingDisablesEditLink(self):
247 + # Weird: supplying a 'heading' for the included page disables the
248 + # editlink. Not sure if this is actually desirable behaviour, but these
249 + # tests are initially to ensure that my refactoring does not change
250 + # existing behaviour.
251 + heading = u'included stuff:'
252 + result = self._execute(u'IncludedPage, "%s",, editlink' % heading)
253 +
254 + match = self._assert_heading_link(1, heading, r'\./IncludedPage', result)
255 + match = self._assert_included(self.included_content, result, pos=match.end())
256 + assert u'[IncludedPage]' not in result
257 + assert u'[edit]' not in result
258 +
259 + # Just for fun, repeat with an explicitly supplied 'level'.
260 + result = self._execute(u'IncludedPage, "%s", 4, editlink' % heading)
261 +
262 + match = self._assert_heading_link(4, heading, r'\./IncludedPage', result)
263 + match = self._assert_included(self.included_content, result, pos=match.end())
264 + assert u'[IncludedPage]' not in result
265 + assert u'[edit]' not in result
266 +
267 +
268 +# Certain values of request.action imply "print mode", which changes the
269 +# behaviour of Include.
270 +class TestIncludePrintMode(BaseTest):
271 +
272 + included_pagename = u'IncludedPage'
273 + included_content = u'Yargghh!!'
274 +
275 + def setup_class(cls):
276 + become_trusted(cls.request)
277 + cls.includer_page = create_page(cls.request, cls.includer_pagename, u'Foo!')
278 + create_page(cls.request, cls.included_pagename, cls.included_content)
279 +
280 + assert cls.request.action == "show"
281 + cls.request.action = "print"
282 +
283 + def teardown_class(cls):
284 + cls.request.action = "show"
285 + nuke_page(cls.request, cls.includer_pagename)
286 + nuke_page(cls.request, cls.included_pagename)
287 +
288 + def testIncludePrintModeSimple(self):
289 + # A basic Include is unaffected by print mode: this test is basically a
290 + # repeat of testIncludeSimple().
291 + assert self.request.action == "print"
292 + result = self._execute(u'IncludedPage')
293 + self._assert_included(self.included_content, result)
294 +
295 + def testIncludePrintModeHeading(self):
296 + # In normal mode, 'heading' inserts a heading that is also a link to the
297 + # included page -- hence the use of _assert_heading_link() in
298 + # testIncludeHeading() above. In print mode, it's just a heading -- no
299 + # link.
300 + heading = u'Look Out!'
301 + result = self._execute(u'IncludedPage, "%s", 2' % heading)
302 + match = self._assert_heading(2, heading, result)
303 + self._assert_included(self.included_content, result, pos=match.end())
304 +
305 + def testIncludePrintModeEditLink(self):
306 + # In print mode, 'editlink' has no effect.
307 + result = self._execute(u'IncludedPage,,, editlink')
308 + match = self._assert_included(self.included_content, result)
309 + assert u'[IncludedPage]' not in result
310 + assert u'[edit]' not in result
311 +
312 +
313 +class TestIncludeMany(BaseTest):
314 +
315 + included_pagename_1 = u'1_Intro'
316 + included_content_1 = u'''\
317 += Intro =
318 +
319 +This is an introduction.
320 +
321 +== Ooga ==
322 +
323 +Ooga-booga. Ding-dong.
324 +
325 +== Argle ==
326 +
327 +Argle bargle snarky weeb.
328 +'''
329 +
330 + included_pagename_2 = u'2_BlahBlah'
331 + included_content_2 = u'''\
332 += Blah Blah =
333 +
334 +more blah blah blah
335 +'''
336 + included_pagename_3 = u'3_Conclusions'
337 + included_content_3 = u'''\
338 += Conclusions =
339 +
340 +Clearly, the snarky weeb rocks.
341 +'''
342 +
343 + def setup_class(cls):
344 + become_trusted(cls.request)
345 + cls.includer_page = create_page(cls.request, cls.includer_pagename, u'Foo!')
346 +
347 + create_page(cls.request, cls.included_pagename_1, cls.included_content_1)
348 + create_page(cls.request, cls.included_pagename_2, cls.included_content_2)
349 + create_page(cls.request, cls.included_pagename_3, cls.included_content_3)
350 +
351 + def teardown_class(cls):
352 + nuke_page(cls.request, cls.includer_pagename)
353 + nuke_page(cls.request, cls.included_pagename_1)
354 + nuke_page(cls.request, cls.included_pagename_2)
355 + nuke_page(cls.request, cls.included_pagename_3)
356 +
357 + def testIncludeMany(self):
358 + # Include with a regex pulls in multiple pages.
359 + result = self._execute(u'^\d_')
360 + match = self._assert_heading(1, 'Intro', result)
361 + match = self._assert_included('This is an introduction.', result, pos=match.end())
362 + match = self._assert_heading(2, 'Ooga', result, pos=match.end())
363 + match = self._assert_included('Ooga-booga. Ding-dong.', result, pos=match.end())
364 + match = self._assert_heading(2, 'Argle', result, pos=match.end())
365 + match = self._assert_included('Argle bargle snarky weeb.', result, pos=match.end())
366 + match = self._assert_heading(1, 'Blah Blah', result, pos=match.end())
367 + match = self._assert_included('more blah blah blah', result, pos=match.end())
368 + match = self._assert_heading(1, 'Conclusions', result, pos=match.end())
369 + match = self._assert_included('Clearly, the snarky weeb rocks.', result, pos=match.end())
370 +
371 + def testIncludeManySortDescending(self):
372 + # sort=descending includes pages in reverse order
373 + result = self._execute(u'^\d_,,, sort=descending')
374 + match = self._assert_heading(1, 'Conclusions', result)
375 + match = self._assert_included('Clearly, the snarky weeb rocks.', result, pos=match.end())
376 + match = self._assert_heading(1, 'Blah Blah', result, pos=match.end())
377 + match = self._assert_included('more blah blah blah', result, pos=match.end())
378 + match = self._assert_heading(1, 'Intro', result, pos=match.end())
379 + match = self._assert_included('This is an introduction.', result, pos=match.end())
380 + match = self._assert_heading(2, 'Ooga', result, pos=match.end())
381 + match = self._assert_included('Ooga-booga. Ding-dong.', result, pos=match.end())
382 + match = self._assert_heading(2, 'Argle', result, pos=match.end())
383 + match = self._assert_included('Argle bargle snarky weeb.', result, pos=match.end())
384 +
385 + def testIncludeManyMaxItems(self):
386 + # items=N limits the include to N pages
387 + result = self._execute(u'^\d_,,, items=2')
388 + match = self._assert_heading(1, 'Intro', result)
389 + match = self._assert_included('This is an introduction.', result, pos=match.end())
390 + match = self._assert_heading(2, 'Ooga', result, pos=match.end())
391 + match = self._assert_included('Ooga-booga. Ding-dong.', result, pos=match.end())
392 + match = self._assert_heading(2, 'Argle', result, pos=match.end())
393 + match = self._assert_included('Argle bargle snarky weeb.', result, pos=match.end())
394 + match = self._assert_heading(1, 'Blah Blah', result, pos=match.end())
395 + match = self._assert_included('more blah blah blah', result, pos=match.end())
396 + assert 'Conclusions' not in result
397 + assert 'Clearly, the snarky weeb rocks.' not in result
398 +
399 + def testIncludeManySkipItems(self):
400 + # skipitems=N skips the first N matching pages
401 + result = self._execute(u'^\d_,,, skipitems=2')
402 + match = self._assert_heading(1, 'Conclusions', result)
403 + match = self._assert_included('Clearly, the snarky weeb rocks.', result, pos=match.end())
404 + assert 'Intro' not in result
405 + assert 'Ooga-booga. Ding-dong.' not in result
406 + assert 'Blah Blah' not in result
407 + assert 'more blah blah blah' not in result
408 +
409 + def testIncludeManyMaxSkip(self):
410 + # Combine 'items' and 'skipitems'. Their combined behaviour is (IMHO) a
411 + # bit weird: 'items' really means "the number of included pages to
412 + # consider before skipping 'skipitems'". Thus, "items=1, skipitems=1"
413 + # will return an empty result: it considers only 1 page, and then skips
414 + # past that page.
415 + result = self._execute(u'^\d_,,, items=2, skipitems=1')
416 + match = self._assert_heading(1, 'Blah Blah', result, pos=0)
417 + match = self._assert_included('more blah blah blah', result, pos=match.end())
418 + assert 'Intro' not in result
419 + assert 'Conclusions' not in result
420 +
421 + result = self._execute(u'^\d_,,, items=1, skipitems=1')
422 + assert result == ''
423 +
424 + def testIncludeManyTitlesOnly(self):
425 + # titlesonly suppresses content, and just renders headers from the
426 + # included pages
427 + result = self._execute(u'^\d_,,, titlesonly')
428 +
429 + # Hmmm: currently, Include links to the whole page for subheadings.
430 + # Since this might be a bug, I pass href patterns that allow Include to
431 + # change so it links to an anchor in the page. If that is in fact the
432 + # desired behaviour, those href patterns should make the anchor
433 + # mandatory!
434 +
435 + match = self._assert_link("./1_Intro", 'Intro', result)
436 + match = self._assert_link("./1_Intro(#\S+)?", 'Ooga', result, pos=match.end())
437 + match = self._assert_link("./1_Intro(#\S+)?", 'Argle', result, pos=match.end())
438 + match = self._assert_link("./2_BlahBlah", 'Blah Blah', result, pos=match.end())
439 + match = self._assert_link("./3_Conclusions", 'Conclusions', result, pos=match.end())
440 +
441 + assert 'This is an introduction.' not in result
442 + assert 'Ooga-booga. Ding-dong.' not in result
443 + assert 'Argle bargle snarky weeb.' not in result
444 + assert 'more blah blah blah' not in result
445 + assert 'Clearly, the snarky weeb rocks.' not in result
446 +
447 +
448 +coverage_modules = ['MoinMoin.macro.Include']
449
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.