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.
  • [get | view] (2008-10-30 17:01:52, 20.5 KB) [[attachment:01.test-include.patch]]
  • [get | view] (2008-10-30 17:02:07, 3.1 KB) [[attachment:02.extract-setup-functions.patch]]
  • [get | view] (2008-10-30 17:02:17, 11.1 KB) [[attachment:03.extract-page-functions.patch]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.