Hi all --
a week or two ago, I made vague noises on this list about refactoring
the Include macro and adding tests for it. Well, I have made good
progress on the tests today: in fact, the attached patch adds a test
suite for Include that tests every feature *except* 'editlink'. (I ran
out of time. Will try to test that tomorrow.)
Tomorrow, I'll be a good citizen and publish a repository with this
patch, create a patch page in the MoinMoin wiki, etc. For now, please
take a look at the attached tests and let me know what you think!
Thanks --
Greg
P.S. this is relative to 1.8 and not quite ready to commit yet, but it's
a very good start.
changeset: 4182:bf9bca1e748e
tag: qbase
tag: tip
tag: test-include
tag: qtip
parent: 4178:0b9869467641
user: [EMAIL PROTECTED]
date: Tue Oct 28 17:25:18 2008 -0400
summary: Add tests for Include macro.
diff -r 0b9869467641 -r bf9bca1e748e MoinMoin/macro/_tests/test_Include.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/macro/_tests/test_Include.py Tue Oct 28 17:25:18 2008 -0400
@@ -0,0 +1,293 @@
+# -*- coding: utf-8 -*-
+"""
+ MoinMoin - MoinMoin.macro.Include Tests
+
+ @copyright: 2008 MoinMoin:GregWard
+ @license: GNU GPL, see COPYING for details.
+"""
+
+import re
+from MoinMoin._tests import become_trusted, create_page, make_macro, nuke_page
+
+class BaseTest:
+ def _execute(self, args):
+ """Execute macro Include in self.includer_page with args, returning
the result."""
+ macro = make_macro(self.request, self.includer_page)
+ result = macro.execute('Include', args)
+ print "macro result: %r" % result
+ return result
+
+ def _assert_regex(self, expect_re, actual_result, pos):
+ print "expected result to match %s from pos %d" % (expect_re.pattern,
pos)
+ match = expect_re.search(actual_result, pos=pos)
+ assert match is not None
+ return match
+
+ def _assert_included(self, expect_content, actual_result, pos=0):
+ # Hmmm: the space after the included content seems strange, but
+ # it is how Include behaves.
+ content_re = re.compile(r'<p class="\w+">%s<span' %
re.escape(expect_content + " "))
+ return self._assert_regex(content_re, actual_result, pos=pos)
+
+ def _assert_sysmsg(self, expect_class, expect_message, actual_result):
+ msg_re = re.compile(r'<p><strong class="%s">%s</strong></p>'
+ % (re.escape(expect_class),
re.escape(expect_message)))
+ return self._assert_regex(msg_re, actual_result, pos=0)
+
+ def _assert_heading(self, expect_level, expect_content, actual_result,
pos=0):
+ heading_re = re.compile(r'<h%d id=\"[\w\.\-]+\">%s</h%d>'
+ % (expect_level, re.escape(expect_content),
expect_level))
+ return self._assert_regex(heading_re, actual_result, pos=pos)
+
+ def _assert_link(self, expect_href, expect_text, actual_result, pos=0):
+ link_re = re.compile(r'<a href=\"%s\">%s</a>'
+ % (expect_href, re.escape(expect_text)))
+ return self._assert_regex(link_re, actual_result, pos=pos)
+
+class TestInclude(BaseTest):
+ """tests for macro Include"""
+ included_pagename = u'IncludedPage'
+ included_content = u'Here is included content!'
+ includer_pagename = u'IncluderPage'
+
+ def setup_method(self, method):
+ assert isinstance(self, TestInclude)
+
+ request = self.request
+ become_trusted(request)
+ self._create_included_page()
+ self.includer_page = create_page(
+ request, self.includer_pagename, u'Argh')
+
+ def teardown_method(self, method):
+ nuke_page(self.request, self.included_pagename)
+ nuke_page(self.request, self.includer_pagename)
+
+ def _create_included_page(self, pagename=included_pagename,
content=included_content):
+ return create_page(
+ self.request, pagename, content)
+
+ def testIncludeSimple(self):
+ result = self._execute(u'IncludedPage')
+ self._assert_included(self.included_content, result)
+
+ def testIncludeNoArgs(self):
+ # with no args, Include() expands to an error message
+ result = self._execute(u'')
+ assert 'Invalid include arguments' in result
+
+ def testIncludeMarkup(self):
+ # If the included page includes wiki markup, it is rendered to
+ # HTML by the macro.
+ # XXX this makes assumptions about the formatter: is that evil?
+ self._create_included_page(content=u'This is \'\'\'bold\'\'\' text')
+ result = self._execute(u'IncludedPage')
+ self._assert_included('This is <strong>bold</strong> text', result)
+
+ def testIncludeHeading(self):
+ # You can add a heading to the included content by passing 'heading'
+ # arg to Include.
+ heading = u'Flubby Flobby'
+ result = self._execute(u'IncludedPage, "%s"' % heading)
+
+ # XXX again, this makes assumptions about the formatter: evil?
+ heading_match = re.search(r'<h1 id="FlubbyFlobby">.*%s.*</h1>' %
heading, result)
+ assert heading_match
+
+ content_idx = result.find(self.included_content)
+ assert content_idx > heading_match.end()
+
+ def testIncludeHeadingLevel(self):
+ # Add a heading with a specific level.
+ heading = u'Weeble Wobble'
+ result = self._execute(u'IncludedPage, "%s", 3' % heading)
+
+ # XXX again, this makes assumptions about the formatter: evil?
+ heading_match = re.search(r'<h3 id="WeebleWobble">.*%s.*</h3>' %
heading, result)
+ assert heading_match
+
+ def testIncludeFrom(self):
+ # Start including at the point right after text that matches the
'from' regex.
+ included_content = 'Here is some --delimited-- content'
+ self._create_included_page(content=included_content)
+ result = self._execute(u'IncludedPage,,, from="-+"')
+
+ self._assert_included('delimited-- content', result)
+ assert "Here is some" not in result
+
+ def testIncludeFromBadRegex(self):
+ # If the user passes a bad "from" regex -- say, r"foo[" -- then
+ # Include assumes the user meant to escape the metacharacters
+ # and does it for them, changing the regex to r"foo\[".
+ included_content = u'start including after foo[that string].'
+ self._create_included_page(content=included_content)
+
+ # First: do it with the correct regex syntax, so Include does
+ # not have to catch re.error.
+ result1 = self._execute(u'IncludedPage,,, from="foo\\["')
+ self._assert_included(u'that string].', result1)
+
+ # Now trip the exception and see that Include escapes the "[".
+ result2 = self._execute(u'IncludedPage,,, from="foo["')
+ self._assert_included(u'that string].', result2)
+
+ def testIncludeFromNoMatch(self):
+ # If the 'from' regex does not match the included page, Include
+ # inserts a warning message and then completely includes the
+ # other page.
+ result = self._execute(u'IncludedPage,,, from="foo"')
+ self._assert_sysmsg('warning', 'Include: Nothing found for "foo"!',
result)
+ self._assert_included(self.included_content, result)
+
+ # Sigh. The tests for 'to' are very similar to the tests for
+ # 'from'. But the code being tested is also quite repetitive, so
+ # it's important to test both 'from' and 'to' thoroughly before
+ # refactoring!
+
+ def testIncludeTo(self):
+ # Stop including at the point before after text that matches the 'to'
regex.
+ included_content = 'Here is some --delimited-- content'
+ self._create_included_page(content=included_content)
+ result = self._execute(u'IncludedPage,,, to="-+"')
+
+ self._assert_included('Here is some ', result)
+ assert "--delimited-- content" not in result
+
+ def testIncludeToBadRegex(self):
+ # As with 'from', a bad 'to' regex gets quoted and tried again.
+ included_content = u'include up to foo[that string'
+ self._create_included_page(content=included_content)
+
+ # First with correct regex syntax.
+ result1 = self._execute(u'IncludedPage,,, to="foo\\["')
+ self._assert_included(u'include up to ', result1)
+
+ # Now trip the exception and make sure we get the same thing.
+ result2 = self._execute(u'IncludedPage,,, to="foo["')
+ self._assert_included(u'include up to ', result2)
+
+ def testIncludeToNoMatch(self):
+ # As with 'from', a bad 'to' regex results in a warning.
+ result = self._execute(u'IncludedPage,,, to="foo"')
+ self._assert_sysmsg('warning', 'Include: Nothing found for "foo"!',
result)
+ self._assert_included(self.included_content, result)
+
+class TestIncludeMany(BaseTest):
+ includer_pagename = u'IncluderPage'
+
+ included_pagename_1 = u'1_Intro'
+ included_content_1 = u'''\
+= Intro =
+
+This is an introduction.
+
+== Ooga ==
+
+Ooga-booga. Ding-dong.
+
+== Argle ==
+
+Argle bargle snarky weeb.
+'''
+
+ included_pagename_2 = u'2_BlahBlah'
+ included_content_2 = u'''\
+= Blah Blah =
+
+more blah blah blah
+'''
+ included_pagename_3 = u'3_Conclusions'
+ included_content_3 = u'''\
+= Conclusions =
+
+Clearly, the snarky weeb rocks.
+'''
+
+ def setup_class(cls):
+ become_trusted(cls.request)
+ cls.includer_page = create_page(cls.request, cls.includer_pagename,
u'Foo!')
+
+ create_page(cls.request, cls.included_pagename_1,
cls.included_content_1)
+ create_page(cls.request, cls.included_pagename_2,
cls.included_content_2)
+ create_page(cls.request, cls.included_pagename_3,
cls.included_content_3)
+
+ def teardown_class(cls):
+ nuke_page(cls.request, cls.includer_pagename)
+ nuke_page(cls.request, cls.included_pagename_1)
+ nuke_page(cls.request, cls.included_pagename_2)
+ nuke_page(cls.request, cls.included_pagename_3)
+
+ def testIncludeMany(self):
+ # Include with a regex pulls in multiple pages.
+ result = self._execute(u'^\d_')
+ match = self._assert_heading(1, 'Intro', result)
+ match = self._assert_included('This is an introduction.', result,
pos=match.end())
+ match = self._assert_heading(2, 'Ooga', result, pos=match.end())
+ match = self._assert_included('Ooga-booga. Ding-dong.', result,
pos=match.end())
+ match = self._assert_heading(2, 'Argle', result, pos=match.end())
+ match = self._assert_included('Argle bargle snarky weeb.', result,
pos=match.end())
+ match = self._assert_heading(1, 'Blah Blah', result, pos=match.end())
+ match = self._assert_included('more blah blah blah', result,
pos=match.end())
+ match = self._assert_heading(1, 'Conclusions', result, pos=match.end())
+ match = self._assert_included('Clearly, the snarky weeb rocks.',
result, pos=match.end())
+
+ def testIncludeManySortDescending(self):
+ # sort=descending includes pages in reverse order
+ result = self._execute(u'^\d_,,, sort=descending')
+ match = self._assert_heading(1, 'Conclusions', result)
+ match = self._assert_included('Clearly, the snarky weeb rocks.',
result, pos=match.end())
+ match = self._assert_heading(1, 'Blah Blah', result, pos=match.end())
+ match = self._assert_included('more blah blah blah', result,
pos=match.end())
+ match = self._assert_heading(1, 'Intro', result, pos=match.end())
+ match = self._assert_included('This is an introduction.', result,
pos=match.end())
+ match = self._assert_heading(2, 'Ooga', result, pos=match.end())
+ match = self._assert_included('Ooga-booga. Ding-dong.', result,
pos=match.end())
+ match = self._assert_heading(2, 'Argle', result, pos=match.end())
+ match = self._assert_included('Argle bargle snarky weeb.', result,
pos=match.end())
+
+ def testIncludeManyMaxItems(self):
+ # items=N limits the include to N pages
+ result = self._execute(u'^\d_,,, items=2')
+ match = self._assert_heading(1, 'Intro', result)
+ match = self._assert_included('This is an introduction.', result,
pos=match.end())
+ match = self._assert_heading(2, 'Ooga', result, pos=match.end())
+ match = self._assert_included('Ooga-booga. Ding-dong.', result,
pos=match.end())
+ match = self._assert_heading(2, 'Argle', result, pos=match.end())
+ match = self._assert_included('Argle bargle snarky weeb.', result,
pos=match.end())
+ match = self._assert_heading(1, 'Blah Blah', result, pos=match.end())
+ match = self._assert_included('more blah blah blah', result,
pos=match.end())
+ assert 'Conclusions' not in result
+ assert 'Clearly, the snarky weeb rocks.' not in result
+
+ def testIncludeManySkipItems(self):
+ # skipitems=N skips the first N matching pages
+ result = self._execute(u'^\d_,,, skipitems=2')
+ match = self._assert_heading(1, 'Conclusions', result)
+ match = self._assert_included('Clearly, the snarky weeb rocks.',
result, pos=match.end())
+ assert 'Intro' not in result
+ assert 'Ooga-booga. Ding-dong.' not in result
+ assert 'Blah Blah' not in result
+ assert 'more blah blah blah' not in result
+
+ def testIncludeManyTitlesOnly(self):
+ # titlesonly suppresses content, and just renders headers from the
+ # included pages
+ result = self._execute(u'^\d_,,, titlesonly')
+
+ # Hmmm: currently, Include links to the whole page for subheadings.
+ # Since this might be a bug, I pass href patterns that allow Include to
+ # change so it links to an anchor in the page. If that is in fact the
+ # desired behaviour, those href patterns should make the anchor
+ # mandatory!
+
+ match = self._assert_link("./1_Intro", 'Intro', result)
+ match = self._assert_link("./1_Intro(#\S+)?", 'Ooga', result,
pos=match.end())
+ match = self._assert_link("./1_Intro(#\S+)?", 'Argle', result,
pos=match.end())
+ match = self._assert_link("./2_BlahBlah", 'Blah Blah', result,
pos=match.end())
+ match = self._assert_link("./3_Conclusions", 'Conclusions', result,
pos=match.end())
+
+ assert 'This is an introduction.' not in result
+ assert 'Ooga-booga. Ding-dong.' not in result
+ assert 'Argle bargle snarky weeb.' not in result
+ assert 'more blah blah blah' not in result
+ assert 'Clearly, the snarky weeb rocks.' not in result
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Moin-user mailing list
Moin-user@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/moin-user