diff --git a/mwparserfromhell/nodes/html_entity.py b/mwparserfromhell/nodes/html_entity.py index 5b7607c..b51bd92 100644 --- a/mwparserfromhell/nodes/html_entity.py +++ b/mwparserfromhell/nodes/html_entity.py @@ -23,7 +23,7 @@ from __future__ import unicode_literals from . import Node -from ..compat import htmlentities, str +from ..compat import htmlentities, py3k, str __all__ = ["HTMLEntity"] @@ -63,28 +63,31 @@ class HTMLEntity(Node): return self.normalize() return self - def _unichr(self, value): - """Implement the builtin unichr() with support for non-BMP code points. + if not py3k: + @staticmethod + def _unichr(value): + """Implement builtin unichr() with support for non-BMP code points. - On wide Python builds, this functions like the normal unichr(). On - narrow builds, this returns the value's corresponding surrogate pair. - """ - try: - return unichr(value) - except ValueError: - # Test whether we're on the wide or narrow Python build. Check the - # length of a non-BMP code point (U+1F64A, SPEAK-NO-EVIL MONKEY): - if len("\U0001F64A") == 2: - # Ensure this is within the range we can encode: - if value > 0x10FFFF: - raise ValueError("unichr() arg not in range(0x110000)") - code = value - 0x10000 - if value < 0: # Invalid code point - raise - lead = 0xD800 + (code >> 10) - trail = 0xDC00 + (code % (1 << 10)) - return unichr(lead) + unichr(trail) - raise + On wide Python builds, this functions like the normal unichr(). On + narrow builds, this returns the value's encoded surrogate pair. + """ + try: + return unichr(value) + except ValueError: + # Test whether we're on the wide or narrow Python build. Check + # the length of a non-BMP code point + # (U+1F64A, SPEAK-NO-EVIL MONKEY): + if len("\U0001F64A") == 2: + # Ensure this is within the range we can encode: + if value > 0x10FFFF: + raise ValueError("unichr() arg not in range(0x110000)") + code = value - 0x10000 + if value < 0: # Invalid code point + raise + lead = 0xD800 + (code >> 10) + trail = 0xDC00 + (code % (1 << 10)) + return unichr(lead) + unichr(trail) + raise @property def value(self): @@ -119,19 +122,47 @@ class HTMLEntity(Node): @value.setter def value(self, newval): newval = str(newval) - if newval not in htmlentities.entitydefs: - test = int(self.value, 16) - if test < 0 or (test > 0x10FFFF and int(self.value) > 0x10FFFF): - raise ValueError(newval) + try: + int(newval) + except ValueError: + try: + int(newval, 16) + except ValueError: + if newval not in htmlentities.entitydefs: + raise ValueError("entity value is not a valid name") + self._named = True + self._hexadecimal = False + else: + if int(newval, 16) < 0 or int(newval, 16) > 0x10FFFF: + raise ValueError("entity value is not in range(0x110000)") + self._named = False + self._hexadecimal = True + else: + test = int(newval, 16 if self.hexadecimal else 10) + if test < 0 or test > 0x10FFFF: + raise ValueError("entity value is not in range(0x110000)") + self._named = False self._value = newval @named.setter def named(self, newval): - self._named = bool(newval) + newval = bool(newval) + if newval and self.value not in htmlentities.entitydefs: + raise ValueError("entity value is not a valid name") + if not newval: + try: + int(self.value, 16) + except ValueError: + err = "current entity value is not a valid Unicode codepoint" + raise ValueError(err) + self._named = newval @hexadecimal.setter def hexadecimal(self, newval): - self._hexadecimal = bool(newval) + newval = bool(newval) + if newval and self.named: + raise ValueError("a named entity cannot be hexadecimal") + self._hexadecimal = newval @hex_char.setter def hex_char(self, newval): @@ -142,8 +173,9 @@ class HTMLEntity(Node): def normalize(self): """Return the unicode character represented by the HTML entity.""" + chrfunc = chr if py3k else HTMLEntity._unichr if self.named: - return unichr(htmlentities.name2codepoint[self.value]) + return chrfunc(htmlentities.name2codepoint[self.value]) if self.hexadecimal: - return self._unichr(int(self.value, 16)) - return self._unichr(int(self.value)) + return chrfunc(int(self.value, 16)) + return chrfunc(int(self.value)) diff --git a/mwparserfromhell/nodes/template.py b/mwparserfromhell/nodes/template.py index e34ba7a..3834d41 100644 --- a/mwparserfromhell/nodes/template.py +++ b/mwparserfromhell/nodes/template.py @@ -81,7 +81,7 @@ class Template(Node): in parameter names or values so they are not mistaken for new parameters. """ - replacement = HTMLEntity(value=ord(char)) + replacement = str(HTMLEntity(value=ord(char))) for node in code.filter_text(recursive=False): if char in node: code.replace(node, node.replace(char, replacement)) @@ -107,7 +107,7 @@ class Template(Node): values = tuple(theories.values()) best = max(values) confidence = float(best) / sum(values) - if confidence > 0.75: + if confidence >= 0.75: return tuple(theories.keys())[values.index(best)] def _get_spacing_conventions(self, use_names): @@ -142,9 +142,9 @@ class Template(Node): return False return True - def _remove_without_field(self, param, i, force_no_field): + def _remove_without_field(self, param, i): """Return False if a parameter name should be kept, otherwise True.""" - if not param.showkey and not force_no_field: + if not param.showkey: dependents = [not after.showkey for after in self.params[i+1:]] if any(dependents): return False @@ -183,11 +183,10 @@ class Template(Node): def get(self, name): """Get the parameter whose name is *name*. - The returned object is a - :py:class:`~.Parameter` instance. Raises :py:exc:`ValueError` if no - parameter has this name. Since multiple parameters can have the same - name, we'll return the last match, since the last parameter is the only - one read by the MediaWiki parser. + The returned object is a :py:class:`~.Parameter` instance. Raises + :py:exc:`ValueError` if no parameter has this name. Since multiple + parameters can have the same name, we'll return the last match, since + the last parameter is the only one read by the MediaWiki parser. """ name = name.strip() if isinstance(name, basestring) else str(name) for param in reversed(self.params): @@ -195,20 +194,34 @@ class Template(Node): return param raise ValueError(name) - def add(self, name, value, showkey=None, force_nonconformity=False): + def add(self, name, value, showkey=None, before=None, + preserve_spacing=True): """Add a parameter to the template with a given *name* and *value*. *name* and *value* can be anything parasable by - :py:func:`.utils.parse_anything`; pipes (and equal signs, if - appropriate) are automatically escaped from *value* where applicable. + :py:func:`.utils.parse_anything`; pipes and equal signs are + automatically escaped from *value* when appropriate. + If *showkey* is given, this will determine whether or not to show the parameter's name (e.g., ``{{foo|bar}}``'s parameter has a name of ``"1"`` but it is hidden); otherwise, we'll make a safe and intelligent - guess. If *name* is already a parameter, we'll replace its value while - keeping the same spacing rules unless *force_nonconformity* is - ``True``. We will also try to guess the dominant spacing convention - when adding a new parameter using :py:meth:`_get_spacing_conventions` - unless *force_nonconformity* is ``True``. + guess. + + If *name* is already a parameter in the template, we'll replace its + value while keeping the same whitespace around it. We will also try to + guess the dominant spacing convention when adding a new parameter using + :py:meth:`_get_spacing_conventions`. + + If *before* is given (either a :py:class:`~.Parameter` object or a + name), then we will place the parameter immediately before this one. + Otherwise, it will be added at the end. If *before* is a name and + exists multiple times in the template, we will place it before the last + occurance. If *before* is not in the template, :py:exc:`ValueError` is + raised. The argument is ignored if the new parameter already exists. + + If *preserve_spacing* is ``False``, we will avoid preserving spacing + conventions when changing the value of an existing parameter or when + adding a new one. """ name, value = parse_anything(name), parse_anything(value) self._surface_escape(value, "|") @@ -217,14 +230,17 @@ class Template(Node): self.remove(name, keep_field=True) existing = self.get(name) if showkey is not None: - if not showkey: - self._surface_escape(value, "=") existing.showkey = showkey + if not existing.showkey: + self._surface_escape(value, "=") nodes = existing.value.nodes - if force_nonconformity: - existing.value = value - else: + if preserve_spacing: + for i in range(2): # Ignore empty text nodes + if not nodes[i]: + nodes[i] = None existing.value = parse_anything([nodes[0], value, nodes[1]]) + else: + existing.value = value return existing if showkey is None: @@ -246,43 +262,38 @@ class Template(Node): if not showkey: self._surface_escape(value, "=") - if not force_nonconformity: + if preserve_spacing: before_n, after_n = self._get_spacing_conventions(use_names=True) - if before_n and after_n: - name = parse_anything([before_n, name, after_n]) - elif before_n: - name = parse_anything([before_n, name]) - elif after_n: - name = parse_anything([name, after_n]) - before_v, after_v = self._get_spacing_conventions(use_names=False) - if before_v and after_v: - value = parse_anything([before_v, value, after_v]) - elif before_v: - value = parse_anything([before_v, value]) - elif after_v: - value = parse_anything([value, after_v]) + name = parse_anything([before_n, name, after_n]) + value = parse_anything([before_v, value, after_v]) param = Parameter(name, value, showkey) - self.params.append(param) + if before: + if not isinstance(before, Parameter): + before = self.get(before) + self.params.insert(self.params.index(before), param) + else: + self.params.append(param) return param - def remove(self, name, keep_field=False, force_no_field=False): + def remove(self, name, keep_field=False): """Remove a parameter from the template whose name is *name*. If *keep_field* is ``True``, we will keep the parameter's name, but blank its value. Otherwise, we will remove the parameter completely *unless* other parameters are dependent on it (e.g. removing ``bar`` from ``{{foo|bar|baz}}`` is unsafe because ``{{foo|baz}}`` is not what - we expected, so ``{{foo||baz}}`` will be produced instead), unless - *force_no_field* is also ``True``. If the parameter shows up multiple - times in the template, we will remove all instances of it (and keep - one if *keep_field* is ``True`` - that being the first instance if - none of the instances have dependents, otherwise that instance will be - kept). + we expected, so ``{{foo||baz}}`` will be produced instead). + + If the parameter shows up multiple times in the template, we will + remove all instances of it (and keep one if *keep_field* is ``True`` - + the first instance if none have dependents, otherwise the one with + dependents will be kept). """ name = name.strip() if isinstance(name, basestring) else str(name) removed = False + to_remove =[] for i, param in enumerate(self.params): if param.name.strip() == name: if keep_field: @@ -290,13 +301,15 @@ class Template(Node): self._blank_param_value(param.value) keep_field = False else: - self.params.remove(param) + to_remove.append(param) else: - if self._remove_without_field(param, i, force_no_field): - self.params.remove(param) + if self._remove_without_field(param, i): + to_remove.append(param) else: self._blank_param_value(param.value) if not removed: removed = True if not removed: raise ValueError(name) + for param in to_remove: + self.params.remove(param) diff --git a/mwparserfromhell/nodes/wikilink.py b/mwparserfromhell/nodes/wikilink.py index 6fea468..527e9bb 100644 --- a/mwparserfromhell/nodes/wikilink.py +++ b/mwparserfromhell/nodes/wikilink.py @@ -79,4 +79,7 @@ class Wikilink(Node): @text.setter def text(self, value): - self._text = parse_anything(value) + if value is None: + self._text = None + else: + self._text = parse_anything(value) diff --git a/mwparserfromhell/wikicode.py b/mwparserfromhell/wikicode.py index 8d8ebe2..f2d9c89 100644 --- a/mwparserfromhell/wikicode.py +++ b/mwparserfromhell/wikicode.py @@ -88,13 +88,7 @@ class Wikicode(StringMixIn): If *obj* is a ``Node``, the function will test whether they are the same object, otherwise it will compare them with ``==``. """ - if isinstance(obj, Node): - if node is obj: - return True - else: - if node == obj: - return True - return False + return (node is obj) if isinstance(obj, Node) else (node == obj) def _contains(self, nodes, obj): """Return ``True`` if *obj* is inside of *nodes*, else ``False``. diff --git a/tests/_test_tree_equality.py b/tests/_test_tree_equality.py index 758a72e..52130ed 100644 --- a/tests/_test_tree_equality.py +++ b/tests/_test_tree_equality.py @@ -26,8 +26,21 @@ from unittest import TestCase from mwparserfromhell.nodes import (Argument, Comment, Heading, HTMLEntity, Tag, Template, Text, Wikilink) from mwparserfromhell.nodes.extras import Attribute, Parameter +from mwparserfromhell.smart_list import SmartList from mwparserfromhell.wikicode import Wikicode +wrap = lambda L: Wikicode(SmartList(L)) +wraptext = lambda *args: wrap([Text(t) for t in args]) + +def getnodes(code): + """Iterate over all child nodes of a given parent node. + + Imitates Wikicode._get_all_nodes(). + """ + for node in code.nodes: + for context, child in node.__iternodes__(getnodes): + yield child + class TreeEqualityTestCase(TestCase): """A base test case with support for comparing the equality of node trees. diff --git a/tests/test_argument.py b/tests/test_argument.py new file mode 100644 index 0000000..8191804 --- /dev/null +++ b/tests/test_argument.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import unicode_literals +import unittest + +from mwparserfromhell.compat import str +from mwparserfromhell.nodes import Argument, Text + +from ._test_tree_equality import TreeEqualityTestCase, getnodes, wrap, wraptext + +class TestArgument(TreeEqualityTestCase): + """Test cases for the Argument node.""" + + def test_unicode(self): + """test Argument.__unicode__()""" + node = Argument(wraptext("foobar")) + self.assertEqual("{{{foobar}}}", str(node)) + node2 = Argument(wraptext("foo"), wraptext("bar")) + self.assertEqual("{{{foo|bar}}}", str(node2)) + + def test_iternodes(self): + """test Argument.__iternodes__()""" + node1n1 = Text("foobar") + node2n1, node2n2, node2n3 = Text("foo"), Text("bar"), Text("baz") + node1 = Argument(wrap([node1n1])) + node2 = Argument(wrap([node2n1]), wrap([node2n2, node2n3])) + gen1 = node1.__iternodes__(getnodes) + gen2 = node2.__iternodes__(getnodes) + self.assertEqual((None, node1), next(gen1)) + self.assertEqual((None, node2), next(gen2)) + self.assertEqual((node1.name, node1n1), next(gen1)) + self.assertEqual((node2.name, node2n1), next(gen2)) + self.assertEqual((node2.default, node2n2), next(gen2)) + self.assertEqual((node2.default, node2n3), next(gen2)) + self.assertRaises(StopIteration, next, gen1) + self.assertRaises(StopIteration, next, gen2) + + def test_strip(self): + """test Argument.__strip__()""" + node = Argument(wraptext("foobar")) + node2 = Argument(wraptext("foo"), wraptext("bar")) + for a in (True, False): + for b in (True, False): + self.assertIs(None, node.__strip__(a, b)) + self.assertEqual("bar", node2.__strip__(a, b)) + + def test_showtree(self): + """test Argument.__showtree__()""" + output = [] + getter, marker = object(), object() + get = lambda code: output.append((getter, code)) + mark = lambda: output.append(marker) + node1 = Argument(wraptext("foobar")) + node2 = Argument(wraptext("foo"), wraptext("bar")) + node1.__showtree__(output.append, get, mark) + node2.__showtree__(output.append, get, mark) + valid = [ + "{{{", (getter, node1.name), "}}}", "{{{", (getter, node2.name), + " | ", marker, (getter, node2.default), "}}}"] + self.assertEqual(valid, output) + + def test_name(self): + """test getter/setter for the name attribute""" + name = wraptext("foobar") + node1 = Argument(name) + node2 = Argument(name, wraptext("baz")) + self.assertIs(name, node1.name) + self.assertIs(name, node2.name) + node1.name = "héhehé" + node2.name = "héhehé" + self.assertWikicodeEqual(wraptext("héhehé"), node1.name) + self.assertWikicodeEqual(wraptext("héhehé"), node2.name) + + def test_default(self): + """test getter/setter for the default attribute""" + default = wraptext("baz") + node1 = Argument(wraptext("foobar")) + node2 = Argument(wraptext("foobar"), default) + self.assertIs(None, node1.default) + self.assertIs(default, node2.default) + node1.default = "buzz" + node2.default = None + self.assertWikicodeEqual(wraptext("buzz"), node1.default) + self.assertIs(None, node2.default) + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tests/test_builder.py b/tests/test_builder.py index 1e578ed..903d144 100644 --- a/tests/test_builder.py +++ b/tests/test_builder.py @@ -28,12 +28,8 @@ from mwparserfromhell.nodes import (Argument, Comment, Heading, HTMLEntity, from mwparserfromhell.nodes.extras import Attribute, Parameter from mwparserfromhell.parser import tokens from mwparserfromhell.parser.builder import Builder -from mwparserfromhell.smart_list import SmartList -from mwparserfromhell.wikicode import Wikicode -from ._test_tree_equality import TreeEqualityTestCase - -wrap = lambda L: Wikicode(SmartList(L)) +from ._test_tree_equality import TreeEqualityTestCase, wrap, wraptext class TestBuilder(TreeEqualityTestCase): """Tests for the builder, which turns tokens into Wikicode objects.""" @@ -44,10 +40,10 @@ class TestBuilder(TreeEqualityTestCase): def test_text(self): """tests for building Text nodes""" tests = [ - ([tokens.Text(text="foobar")], wrap([Text("foobar")])), - ([tokens.Text(text="fóóbar")], wrap([Text("fóóbar")])), + ([tokens.Text(text="foobar")], wraptext("foobar")), + ([tokens.Text(text="fóóbar")], wraptext("fóóbar")), ([tokens.Text(text="spam"), tokens.Text(text="eggs")], - wrap([Text("spam"), Text("eggs")])), + wraptext("spam", "eggs")), ] for test, valid in tests: self.assertWikicodeEqual(valid, self.builder.build(test)) @@ -57,25 +53,24 @@ class TestBuilder(TreeEqualityTestCase): tests = [ ([tokens.TemplateOpen(), tokens.Text(text="foobar"), tokens.TemplateClose()], - wrap([Template(wrap([Text("foobar")]))])), + wrap([Template(wraptext("foobar"))])), ([tokens.TemplateOpen(), tokens.Text(text="spam"), tokens.Text(text="eggs"), tokens.TemplateClose()], - wrap([Template(wrap([Text("spam"), Text("eggs")]))])), + wrap([Template(wraptext("spam", "eggs"))])), ([tokens.TemplateOpen(), tokens.Text(text="foo"), tokens.TemplateParamSeparator(), tokens.Text(text="bar"), tokens.TemplateClose()], - wrap([Template(wrap([Text("foo")]), params=[ - Parameter(wrap([Text("1")]), wrap([Text("bar")]), - showkey=False)])])), + wrap([Template(wraptext("foo"), params=[ + Parameter(wraptext("1"), wraptext("bar"), showkey=False)])])), ([tokens.TemplateOpen(), tokens.Text(text="foo"), tokens.TemplateParamSeparator(), tokens.Text(text="bar"), tokens.TemplateParamEquals(), tokens.Text(text="baz"), tokens.TemplateClose()], - wrap([Template(wrap([Text("foo")]), params=[ - Parameter(wrap([Text("bar")]), wrap([Text("baz")]))])])), + wrap([Template(wraptext("foo"), params=[ + Parameter(wraptext("bar"), wraptext("baz"))])])), ([tokens.TemplateOpen(), tokens.Text(text="foo"), tokens.TemplateParamSeparator(), tokens.Text(text="bar"), @@ -86,14 +81,12 @@ class TestBuilder(TreeEqualityTestCase): tokens.TemplateParamEquals(), tokens.Text(text="buff"), tokens.TemplateParamSeparator(), tokens.Text(text="baff"), tokens.TemplateClose()], - wrap([Template(wrap([Text("foo")]), params=[ - Parameter(wrap([Text("bar")]), wrap([Text("baz")])), - Parameter(wrap([Text("1")]), wrap([Text("biz")]), - showkey=False), - Parameter(wrap([Text("2")]), wrap([Text("buzz")]), - showkey=False), - Parameter(wrap([Text("3")]), wrap([Text("buff")])), - Parameter(wrap([Text("3")]), wrap([Text("baff")]), + wrap([Template(wraptext("foo"), params=[ + Parameter(wraptext("bar"), wraptext("baz")), + Parameter(wraptext("1"), wraptext("biz"), showkey=False), + Parameter(wraptext("2"), wraptext("buzz"), showkey=False), + Parameter(wraptext("3"), wraptext("buff")), + Parameter(wraptext("3"), wraptext("baff"), showkey=False)])])), ] for test, valid in tests: @@ -104,23 +97,22 @@ class TestBuilder(TreeEqualityTestCase): tests = [ ([tokens.ArgumentOpen(), tokens.Text(text="foobar"), tokens.ArgumentClose()], - wrap([Argument(wrap([Text("foobar")]))])), + wrap([Argument(wraptext("foobar"))])), ([tokens.ArgumentOpen(), tokens.Text(text="spam"), tokens.Text(text="eggs"), tokens.ArgumentClose()], - wrap([Argument(wrap([Text("spam"), Text("eggs")]))])), + wrap([Argument(wraptext("spam", "eggs"))])), ([tokens.ArgumentOpen(), tokens.Text(text="foo"), tokens.ArgumentSeparator(), tokens.Text(text="bar"), tokens.ArgumentClose()], - wrap([Argument(wrap([Text("foo")]), wrap([Text("bar")]))])), + wrap([Argument(wraptext("foo"), wraptext("bar"))])), ([tokens.ArgumentOpen(), tokens.Text(text="foo"), tokens.Text(text="bar"), tokens.ArgumentSeparator(), tokens.Text(text="baz"), tokens.Text(text="biz"), tokens.ArgumentClose()], - wrap([Argument(wrap([Text("foo"), Text("bar")]), - wrap([Text("baz"), Text("biz")]))])), + wrap([Argument(wraptext("foo", "bar"), wraptext("baz", "biz"))])), ] for test, valid in tests: self.assertWikicodeEqual(valid, self.builder.build(test)) @@ -130,23 +122,22 @@ class TestBuilder(TreeEqualityTestCase): tests = [ ([tokens.WikilinkOpen(), tokens.Text(text="foobar"), tokens.WikilinkClose()], - wrap([Wikilink(wrap([Text("foobar")]))])), + wrap([Wikilink(wraptext("foobar"))])), ([tokens.WikilinkOpen(), tokens.Text(text="spam"), tokens.Text(text="eggs"), tokens.WikilinkClose()], - wrap([Wikilink(wrap([Text("spam"), Text("eggs")]))])), + wrap([Wikilink(wraptext("spam", "eggs"))])), ([tokens.WikilinkOpen(), tokens.Text(text="foo"), tokens.WikilinkSeparator(), tokens.Text(text="bar"), tokens.WikilinkClose()], - wrap([Wikilink(wrap([Text("foo")]), wrap([Text("bar")]))])), + wrap([Wikilink(wraptext("foo"), wraptext("bar"))])), ([tokens.WikilinkOpen(), tokens.Text(text="foo"), tokens.Text(text="bar"), tokens.WikilinkSeparator(), tokens.Text(text="baz"), tokens.Text(text="biz"), tokens.WikilinkClose()], - wrap([Wikilink(wrap([Text("foo"), Text("bar")]), - wrap([Text("baz"), Text("biz")]))])), + wrap([Wikilink(wraptext("foo", "bar"), wraptext("baz", "biz"))])), ] for test, valid in tests: self.assertWikicodeEqual(valid, self.builder.build(test)) @@ -176,11 +167,11 @@ class TestBuilder(TreeEqualityTestCase): tests = [ ([tokens.HeadingStart(level=2), tokens.Text(text="foobar"), tokens.HeadingEnd()], - wrap([Heading(wrap([Text("foobar")]), 2)])), + wrap([Heading(wraptext("foobar"), 2)])), ([tokens.HeadingStart(level=4), tokens.Text(text="spam"), tokens.Text(text="eggs"), tokens.HeadingEnd()], - wrap([Heading(wrap([Text("spam"), Text("eggs")]), 4)])), + wrap([Heading(wraptext("spam", "eggs"), 4)])), ] for test, valid in tests: self.assertWikicodeEqual(valid, self.builder.build(test)) @@ -190,11 +181,11 @@ class TestBuilder(TreeEqualityTestCase): tests = [ ([tokens.CommentStart(), tokens.Text(text="foobar"), tokens.CommentEnd()], - wrap([Comment(wrap([Text("foobar")]))])), + wrap([Comment(wraptext("foobar"))])), ([tokens.CommentStart(), tokens.Text(text="spam"), tokens.Text(text="eggs"), tokens.CommentEnd()], - wrap([Comment(wrap([Text("spam"), Text("eggs")]))])), + wrap([Comment(wraptext("spam", "eggs"))])), ] for test, valid in tests: self.assertWikicodeEqual(valid, self.builder.build(test)) @@ -218,10 +209,10 @@ class TestBuilder(TreeEqualityTestCase): tokens.TemplateOpen(), tokens.Text(text="bin"), tokens.TemplateClose(), tokens.TemplateClose()] valid = wrap( - [Template(wrap([Template(wrap([Template(wrap([Template(wrap([Text( - "foo")])), Text("bar")]), params=[Parameter(wrap([Text("baz")]), - wrap([Text("biz")]))]), Text("buzz")])), Text("usr")]), params=[ - Parameter(wrap([Text("1")]), wrap([Template(wrap([Text("bin")]))]), + [Template(wrap([Template(wrap([Template(wrap([Template(wraptext( + "foo")), Text("bar")]), params=[Parameter(wraptext("baz"), + wraptext("biz"))]), Text("buzz")])), Text("usr")]), params=[ + Parameter(wraptext("1"), wrap([Template(wraptext("bin"))]), showkey=False)])]) self.assertWikicodeEqual(valid, self.builder.build(test)) @@ -247,14 +238,14 @@ class TestBuilder(TreeEqualityTestCase): tokens.Text(text="nbsp"), tokens.HTMLEntityEnd(), tokens.TemplateClose()] valid = wrap( - [Template(wrap([Text("a")]), params=[Parameter(wrap([Text("1")]), - wrap([Text("b")]), showkey=False), Parameter(wrap([Text("2")]), - wrap([Template(wrap([Text("c")]), params=[Parameter(wrap([Text("1") - ]), wrap([Wikilink(wrap([Text("d")])), Argument(wrap([Text("e")]))] - ), showkey=False)])]), showkey=False)]), Wikilink(wrap([Text("f")] - ), wrap([Argument(wrap([Text("g")])), Comment(wrap([Text("h")]))]) - ), Template(wrap([Text("i")]), params=[Parameter(wrap([Text("j")]), - wrap([HTMLEntity("nbsp", named=True)]))])]) + [Template(wraptext("a"), params=[Parameter(wraptext("1"), wraptext( + "b"), showkey=False), Parameter(wraptext("2"), wrap([Template( + wraptext("c"), params=[Parameter(wraptext("1"), wrap([Wikilink( + wraptext("d")), Argument(wraptext("e"))]), showkey=False)])]), + showkey=False)]), Wikilink(wraptext("f"), wrap([Argument(wraptext( + "g")), Comment(wraptext("h"))])), Template(wraptext("i"), params=[ + Parameter(wraptext("j"), wrap([HTMLEntity("nbsp", + named=True)]))])]) self.assertWikicodeEqual(valid, self.builder.build(test)) if __name__ == "__main__": diff --git a/tests/test_comment.py b/tests/test_comment.py new file mode 100644 index 0000000..44225a2 --- /dev/null +++ b/tests/test_comment.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import unicode_literals +import unittest + +from mwparserfromhell.compat import str +from mwparserfromhell.nodes import Comment + +from ._test_tree_equality import TreeEqualityTestCase + +class TestComment(TreeEqualityTestCase): + """Test cases for the Comment node.""" + + def test_unicode(self): + """test Comment.__unicode__()""" + node = Comment("foobar") + self.assertEqual("", str(node)) + + def test_iternodes(self): + """test Comment.__iternodes__()""" + node = Comment("foobar") + gen = node.__iternodes__(None) + self.assertEqual((None, node), next(gen)) + self.assertRaises(StopIteration, next, gen) + + def test_strip(self): + """test Comment.__strip__()""" + node = Comment("foobar") + for a in (True, False): + for b in (True, False): + self.assertIs(None, node.__strip__(a, b)) + + def test_showtree(self): + """test Comment.__showtree__()""" + output = [] + node = Comment("foobar") + node.__showtree__(output.append, None, None) + self.assertEqual([""], output) + + def test_contents(self): + """test getter/setter for the contents attribute""" + node = Comment("foobar") + self.assertEqual("foobar", node.contents) + node.contents = "barfoo" + self.assertEqual("barfoo", node.contents) + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tests/test_heading.py b/tests/test_heading.py new file mode 100644 index 0000000..7a65872 --- /dev/null +++ b/tests/test_heading.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import unicode_literals +import unittest + +from mwparserfromhell.compat import str +from mwparserfromhell.nodes import Heading, Text + +from ._test_tree_equality import TreeEqualityTestCase, getnodes, wrap, wraptext + +class TestHeading(TreeEqualityTestCase): + """Test cases for the Heading node.""" + + def test_unicode(self): + """test Heading.__unicode__()""" + node = Heading(wraptext("foobar"), 2) + self.assertEqual("==foobar==", str(node)) + node2 = Heading(wraptext(" zzz "), 5) + self.assertEqual("===== zzz =====", str(node2)) + + def test_iternodes(self): + """test Heading.__iternodes__()""" + text1, text2 = Text("foo"), Text("bar") + node = Heading(wrap([text1, text2]), 3) + gen = node.__iternodes__(getnodes) + self.assertEqual((None, node), next(gen)) + self.assertEqual((node.title, text1), next(gen)) + self.assertEqual((node.title, text2), next(gen)) + self.assertRaises(StopIteration, next, gen) + + def test_strip(self): + """test Heading.__strip__()""" + node = Heading(wraptext("foobar"), 3) + for a in (True, False): + for b in (True, False): + self.assertEqual("foobar", node.__strip__(a, b)) + + def test_showtree(self): + """test Heading.__showtree__()""" + output = [] + getter = object() + get = lambda code: output.append((getter, code)) + node1 = Heading(wraptext("foobar"), 3) + node2 = Heading(wraptext(" baz "), 4) + node1.__showtree__(output.append, get, None) + node2.__showtree__(output.append, get, None) + valid = ["===", (getter, node1.title), "===", + "====", (getter, node2.title), "===="] + self.assertEqual(valid, output) + + def test_title(self): + """test getter/setter for the title attribute""" + title = wraptext("foobar") + node = Heading(title, 3) + self.assertIs(title, node.title) + node.title = "héhehé" + self.assertWikicodeEqual(wraptext("héhehé"), node.title) + + def test_level(self): + """test getter/setter for the level attribute""" + node = Heading(wraptext("foobar"), 3) + self.assertEqual(3, node.level) + node.level = 5 + self.assertEqual(5, node.level) + self.assertRaises(ValueError, setattr, node, "level", 0) + self.assertRaises(ValueError, setattr, node, "level", 7) + self.assertRaises(ValueError, setattr, node, "level", "abc") + self.assertRaises(ValueError, setattr, node, "level", False) + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tests/test_html_entity.py b/tests/test_html_entity.py new file mode 100644 index 0000000..d38e5ec --- /dev/null +++ b/tests/test_html_entity.py @@ -0,0 +1,169 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import unicode_literals +import unittest + +from mwparserfromhell.compat import str +from mwparserfromhell.nodes import HTMLEntity + +from ._test_tree_equality import TreeEqualityTestCase, wrap + +class TestHTMLEntity(TreeEqualityTestCase): + """Test cases for the HTMLEntity node.""" + + def test_unicode(self): + """test HTMLEntity.__unicode__()""" + node1 = HTMLEntity("nbsp", named=True, hexadecimal=False) + node2 = HTMLEntity("107", named=False, hexadecimal=False) + node3 = HTMLEntity("6b", named=False, hexadecimal=True) + node4 = HTMLEntity("6C", named=False, hexadecimal=True, hex_char="X") + self.assertEqual(" ", str(node1)) + self.assertEqual("k", str(node2)) + self.assertEqual("k", str(node3)) + self.assertEqual("l", str(node4)) + + def test_iternodes(self): + """test HTMLEntity.__iternodes__()""" + node = HTMLEntity("nbsp", named=True, hexadecimal=False) + gen = node.__iternodes__(None) + self.assertEqual((None, node), next(gen)) + self.assertRaises(StopIteration, next, gen) + + def test_strip(self): + """test HTMLEntity.__strip__()""" + node1 = HTMLEntity("nbsp", named=True, hexadecimal=False) + node2 = HTMLEntity("107", named=False, hexadecimal=False) + node3 = HTMLEntity("e9", named=False, hexadecimal=True) + for a in (True, False): + self.assertEqual("\xa0", node1.__strip__(True, a)) + self.assertEqual(" ", node1.__strip__(False, a)) + self.assertEqual("k", node2.__strip__(True, a)) + self.assertEqual("k", node2.__strip__(False, a)) + self.assertEqual("é", node3.__strip__(True, a)) + self.assertEqual("é", node3.__strip__(False, a)) + + def test_showtree(self): + """test HTMLEntity.__showtree__()""" + output = [] + node1 = HTMLEntity("nbsp", named=True, hexadecimal=False) + node2 = HTMLEntity("107", named=False, hexadecimal=False) + node3 = HTMLEntity("e9", named=False, hexadecimal=True) + node1.__showtree__(output.append, None, None) + node2.__showtree__(output.append, None, None) + node3.__showtree__(output.append, None, None) + res = [" ", "k", "é"] + self.assertEqual(res, output) + + def test_value(self): + """test getter/setter for the value attribute""" + node1 = HTMLEntity("nbsp") + node2 = HTMLEntity("107") + node3 = HTMLEntity("e9") + self.assertEqual("nbsp", node1.value) + self.assertEqual("107", node2.value) + self.assertEqual("e9", node3.value) + + node1.value = "ffa4" + node2.value = 72 + node3.value = "Sigma" + self.assertEqual("ffa4", node1.value) + self.assertFalse(node1.named) + self.assertTrue(node1.hexadecimal) + self.assertEqual("72", node2.value) + self.assertFalse(node2.named) + self.assertFalse(node2.hexadecimal) + self.assertEqual("Sigma", node3.value) + self.assertTrue(node3.named) + self.assertFalse(node3.hexadecimal) + + node1.value = "10FFFF" + node2.value = 110000 + node2.value = 1114111 + self.assertRaises(ValueError, setattr, node3, "value", "") + self.assertRaises(ValueError, setattr, node3, "value", "foobar") + self.assertRaises(ValueError, setattr, node3, "value", True) + self.assertRaises(ValueError, setattr, node3, "value", -1) + self.assertRaises(ValueError, setattr, node1, "value", 110000) + self.assertRaises(ValueError, setattr, node1, "value", "1114112") + + def test_named(self): + """test getter/setter for the named attribute""" + node1 = HTMLEntity("nbsp") + node2 = HTMLEntity("107") + node3 = HTMLEntity("e9") + self.assertTrue(node1.named) + self.assertFalse(node2.named) + self.assertFalse(node3.named) + node1.named = 1 + node2.named = 0 + node3.named = 0 + self.assertTrue(node1.named) + self.assertFalse(node2.named) + self.assertFalse(node3.named) + self.assertRaises(ValueError, setattr, node1, "named", False) + self.assertRaises(ValueError, setattr, node2, "named", True) + self.assertRaises(ValueError, setattr, node3, "named", True) + + def test_hexadecimal(self): + """test getter/setter for the hexadecimal attribute""" + node1 = HTMLEntity("nbsp") + node2 = HTMLEntity("107") + node3 = HTMLEntity("e9") + self.assertFalse(node1.hexadecimal) + self.assertFalse(node2.hexadecimal) + self.assertTrue(node3.hexadecimal) + node1.hexadecimal = False + node2.hexadecimal = True + node3.hexadecimal = False + self.assertFalse(node1.hexadecimal) + self.assertTrue(node2.hexadecimal) + self.assertFalse(node3.hexadecimal) + self.assertRaises(ValueError, setattr, node1, "hexadecimal", True) + + def test_hex_char(self): + """test getter/setter for the hex_char attribute""" + node1 = HTMLEntity("e9") + node2 = HTMLEntity("e9", hex_char="X") + self.assertEqual("x", node1.hex_char) + self.assertEqual("X", node2.hex_char) + node1.hex_char = "X" + node2.hex_char = "x" + self.assertEqual("X", node1.hex_char) + self.assertEqual("x", node2.hex_char) + self.assertRaises(ValueError, setattr, node1, "hex_char", 123) + self.assertRaises(ValueError, setattr, node1, "hex_char", "foobar") + self.assertRaises(ValueError, setattr, node1, "hex_char", True) + + def test_normalize(self): + """test getter/setter for the normalize attribute""" + node1 = HTMLEntity("nbsp") + node2 = HTMLEntity("107") + node3 = HTMLEntity("e9") + node4 = HTMLEntity("1f648") + self.assertEqual("\xa0", node1.normalize()) + self.assertEqual("k", node2.normalize()) + self.assertEqual("é", node3.normalize()) + self.assertEqual("\U0001F648", node4.normalize()) + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tests/test_parameter.py b/tests/test_parameter.py new file mode 100644 index 0000000..4786e12 --- /dev/null +++ b/tests/test_parameter.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import unicode_literals +import unittest + +from mwparserfromhell.compat import str +from mwparserfromhell.nodes import Text +from mwparserfromhell.nodes.extras import Parameter + +from ._test_tree_equality import TreeEqualityTestCase, wrap, wraptext + +class TestParameter(TreeEqualityTestCase): + """Test cases for the Parameter node extra.""" + + def test_unicode(self): + """test Parameter.__unicode__()""" + node = Parameter(wraptext("1"), wraptext("foo"), showkey=False) + self.assertEqual("foo", str(node)) + node2 = Parameter(wraptext("foo"), wraptext("bar")) + self.assertEqual("foo=bar", str(node2)) + + def test_name(self): + """test getter/setter for the name attribute""" + name1 = wraptext("1") + name2 = wraptext("foobar") + node1 = Parameter(name1, wraptext("foobar"), showkey=False) + node2 = Parameter(name2, wraptext("baz")) + self.assertIs(name1, node1.name) + self.assertIs(name2, node2.name) + node1.name = "héhehé" + node2.name = "héhehé" + self.assertWikicodeEqual(wraptext("héhehé"), node1.name) + self.assertWikicodeEqual(wraptext("héhehé"), node2.name) + + def test_value(self): + """test getter/setter for the value attribute""" + value = wraptext("bar") + node = Parameter(wraptext("foo"), value) + self.assertIs(value, node.value) + node.value = "héhehé" + self.assertWikicodeEqual(wraptext("héhehé"), node.value) + + def test_showkey(self): + """test getter/setter for the showkey attribute""" + node1 = Parameter(wraptext("1"), wraptext("foo"), showkey=False) + node2 = Parameter(wraptext("foo"), wraptext("bar")) + self.assertFalse(node1.showkey) + self.assertTrue(node2.showkey) + node1.showkey = True + node2.showkey = "" + self.assertTrue(node1.showkey) + self.assertFalse(node2.showkey) + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tests/test_parser.py b/tests/test_parser.py index 9d2c969..ec5f065 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -26,10 +26,8 @@ import unittest from mwparserfromhell import parser from mwparserfromhell.nodes import Template, Text, Wikilink from mwparserfromhell.nodes.extras import Parameter -from mwparserfromhell.smart_list import SmartList -from mwparserfromhell.wikicode import Wikicode -from ._test_tree_equality import TreeEqualityTestCase +from ._test_tree_equality import TreeEqualityTestCase, wrap, wraptext from .compat import range class TestParser(TreeEqualityTestCase): @@ -45,18 +43,17 @@ class TestParser(TreeEqualityTestCase): def test_parsing(self): """integration test for parsing overall""" text = "this is text; {{this|is=a|template={{with|[[links]]|in}}it}}" - wrap = lambda L: Wikicode(SmartList(L)) expected = wrap([ Text("this is text; "), - Template(wrap([Text("this")]), [ - Parameter(wrap([Text("is")]), wrap([Text("a")])), - Parameter(wrap([Text("template")]), wrap([ - Template(wrap([Text("with")]), [ - Parameter(wrap([Text("1")]), - wrap([Wikilink(wrap([Text("links")]))]), + Template(wraptext("this"), [ + Parameter(wraptext("is"), wraptext("a")), + Parameter(wraptext("template"), wrap([ + Template(wraptext("with"), [ + Parameter(wraptext("1"), + wrap([Wikilink(wraptext("links"))]), showkey=False), - Parameter(wrap([Text("2")]), - wrap([Text("in")]), showkey=False) + Parameter(wraptext("2"), + wraptext("in"), showkey=False) ]), Text("it") ])) diff --git a/tests/test_template.py b/tests/test_template.py new file mode 100644 index 0000000..28592df --- /dev/null +++ b/tests/test_template.py @@ -0,0 +1,364 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import unicode_literals +import unittest + +from mwparserfromhell.compat import str +from mwparserfromhell.nodes import HTMLEntity, Template, Text +from mwparserfromhell.nodes.extras import Parameter +from ._test_tree_equality import TreeEqualityTestCase, getnodes, wrap, wraptext + +pgens = lambda k, v: Parameter(wraptext(k), wraptext(v), showkey=True) +pgenh = lambda k, v: Parameter(wraptext(k), wraptext(v), showkey=False) + +class TestTemplate(TreeEqualityTestCase): + """Test cases for the Template node.""" + + def test_unicode(self): + """test Template.__unicode__()""" + node = Template(wraptext("foobar")) + self.assertEqual("{{foobar}}", str(node)) + node2 = Template(wraptext("foo"), + [pgenh("1", "bar"), pgens("abc", "def")]) + self.assertEqual("{{foo|bar|abc=def}}", str(node2)) + + def test_iternodes(self): + """test Template.__iternodes__()""" + node1n1 = Text("foobar") + node2n1, node2n2, node2n3 = Text("foo"), Text("bar"), Text("abc") + node2n4, node2n5 = Text("def"), Text("ghi") + node2p1 = Parameter(wraptext("1"), wrap([node2n2]), showkey=False) + node2p2 = Parameter(wrap([node2n3]), wrap([node2n4, node2n5]), + showkey=True) + node1 = Template(wrap([node1n1])) + node2 = Template(wrap([node2n1]), [node2p1, node2p2]) + + gen1 = node1.__iternodes__(getnodes) + gen2 = node2.__iternodes__(getnodes) + self.assertEqual((None, node1), next(gen1)) + self.assertEqual((None, node2), next(gen2)) + self.assertEqual((node1.name, node1n1), next(gen1)) + self.assertEqual((node2.name, node2n1), next(gen2)) + self.assertEqual((node2.params[0].value, node2n2), next(gen2)) + self.assertEqual((node2.params[1].name, node2n3), next(gen2)) + self.assertEqual((node2.params[1].value, node2n4), next(gen2)) + self.assertEqual((node2.params[1].value, node2n5), next(gen2)) + self.assertRaises(StopIteration, next, gen1) + self.assertRaises(StopIteration, next, gen2) + + def test_strip(self): + """test Template.__strip__()""" + node1 = Template(wraptext("foobar")) + node2 = Template(wraptext("foo"), + [pgenh("1", "bar"), pgens("abc", "def")]) + for a in (True, False): + for b in (True, False): + self.assertEqual(None, node1.__strip__(a, b)) + self.assertEqual(None, node2.__strip__(a, b)) + + def test_showtree(self): + """test Template.__showtree__()""" + output = [] + getter, marker = object(), object() + get = lambda code: output.append((getter, code)) + mark = lambda: output.append(marker) + node1 = Template(wraptext("foobar")) + node2 = Template(wraptext("foo"), + [pgenh("1", "bar"), pgens("abc", "def")]) + node1.__showtree__(output.append, get, mark) + node2.__showtree__(output.append, get, mark) + valid = [ + "{{", (getter, node1.name), "}}", "{{", (getter, node2.name), + " | ", marker, (getter, node2.params[0].name), " = ", marker, + (getter, node2.params[0].value), " | ", marker, + (getter, node2.params[1].name), " = ", marker, + (getter, node2.params[1].value), "}}"] + self.assertEqual(valid, output) + + def test_name(self): + """test getter/setter for the name attribute""" + name = wraptext("foobar") + node1 = Template(name) + node2 = Template(name, [pgenh("1", "bar")]) + self.assertIs(name, node1.name) + self.assertIs(name, node2.name) + node1.name = "asdf" + node2.name = "téstïng" + self.assertWikicodeEqual(wraptext("asdf"), node1.name) + self.assertWikicodeEqual(wraptext("téstïng"), node2.name) + + def test_params(self): + """test getter for the params attribute""" + node1 = Template(wraptext("foobar")) + plist = [pgenh("1", "bar"), pgens("abc", "def")] + node2 = Template(wraptext("foo"), plist) + self.assertEqual([], node1.params) + self.assertIs(plist, node2.params) + + def test_has_param(self): + """test Template.has_param()""" + node1 = Template(wraptext("foobar")) + node2 = Template(wraptext("foo"), + [pgenh("1", "bar"), pgens("\nabc ", "def")]) + node3 = Template(wraptext("foo"), + [pgenh("1", "a"), pgens("b", "c"), pgens("1", "d")]) + node4 = Template(wraptext("foo"), [pgenh("1", "a"), pgens("b", " ")]) + self.assertFalse(node1.has_param("foobar")) + self.assertTrue(node2.has_param(1)) + self.assertTrue(node2.has_param("abc")) + self.assertFalse(node2.has_param("def")) + self.assertTrue(node3.has_param("1")) + self.assertTrue(node3.has_param(" b ")) + self.assertFalse(node4.has_param("b")) + self.assertTrue(node3.has_param("b", False)) + self.assertTrue(node4.has_param("b", False)) + + def test_get(self): + """test Template.get()""" + node1 = Template(wraptext("foobar")) + node2p1 = pgenh("1", "bar") + node2p2 = pgens("abc", "def") + node2 = Template(wraptext("foo"), [node2p1, node2p2]) + node3p1 = pgens("b", "c") + node3p2 = pgens("1", "d") + node3 = Template(wraptext("foo"), [pgenh("1", "a"), node3p1, node3p2]) + node4p1 = pgens(" b", " ") + node4 = Template(wraptext("foo"), [pgenh("1", "a"), node4p1]) + self.assertRaises(ValueError, node1.get, "foobar") + self.assertIs(node2p1, node2.get(1)) + self.assertIs(node2p2, node2.get("abc")) + self.assertRaises(ValueError, node2.get, "def") + self.assertIs(node3p1, node3.get("b")) + self.assertIs(node3p2, node3.get("1")) + self.assertIs(node4p1, node4.get("b ")) + + def test_add(self): + """test Template.add()""" + node1 = Template(wraptext("a"), [pgens("b", "c"), pgenh("1", "d")]) + node2 = Template(wraptext("a"), [pgens("b", "c"), pgenh("1", "d")]) + node3 = Template(wraptext("a"), [pgens("b", "c"), pgenh("1", "d")]) + node4 = Template(wraptext("a"), [pgens("b", "c"), pgenh("1", "d")]) + node5 = Template(wraptext("a"), [pgens("b", "c"), + pgens(" d ", "e")]) + node6 = Template(wraptext("a"), [pgens("b", "c"), pgens("b", "d"), + pgens("b", "e")]) + node7 = Template(wraptext("a"), [pgens("b", "c"), pgenh("1", "d")]) + node8p = pgenh("1", "d") + node8 = Template(wraptext("a"), [pgens("b", "c"), node8p]) + node9 = Template(wraptext("a"), [pgens("b", "c"), pgenh("1", "d")]) + node10 = Template(wraptext("a"), [pgens("b", "c"), pgenh("1", "e")]) + node11 = Template(wraptext("a"), [pgens("b", "c")]) + node12 = Template(wraptext("a"), [pgens("b", "c")]) + node13 = Template(wraptext("a"), [ + pgens("\nb ", " c"), pgens("\nd ", " e"), pgens("\nf ", " g")]) + node14 = Template(wraptext("a\n"), [ + pgens("b ", "c\n"), pgens("d ", " e"), pgens("f ", "g\n"), + pgens("h ", " i\n")]) + node15 = Template(wraptext("a"), [ + pgens("b ", " c\n"), pgens("\nd ", " e"), pgens("\nf ", "g ")]) + node16 = Template(wraptext("a"), [ + pgens("\nb ", " c"), pgens("\nd ", " e"), pgens("\nf ", " g")]) + node17 = Template(wraptext("a"), [ + pgens("\nb ", " c"), pgens("\nd ", " e"), pgens("\nf ", " g")]) + node18 = Template(wraptext("a\n"), [ + pgens("b ", "c\n"), pgens("d ", " e"), pgens("f ", "g\n"), + pgens("h ", " i\n")]) + node19 = Template(wraptext("a"), [ + pgens("b ", " c\n"), pgens("\nd ", " e"), pgens("\nf ", "g ")]) + node20 = Template(wraptext("a"), [ + pgens("\nb ", " c"), pgens("\nd ", " e"), pgens("\nf ", " g")]) + node21 = Template(wraptext("a"), [pgenh("1", "b")]) + node22 = Template(wraptext("a"), [pgenh("1", "b")]) + node23 = Template(wraptext("a"), [pgenh("1", "b")]) + node24 = Template(wraptext("a"), [pgenh("1", "b"), pgenh("2", "c"), + pgenh("3", "d"), pgenh("4", "e")]) + node25 = Template(wraptext("a"), [pgenh("1", "b"), pgenh("2", "c"), + pgens("4", "d"), pgens("5", "e")]) + node26 = Template(wraptext("a"), [pgenh("1", "b"), pgenh("2", "c"), + pgens("4", "d"), pgens("5", "e")]) + node27 = Template(wraptext("a"), [pgenh("1", "b")]) + node28 = Template(wraptext("a"), [pgenh("1", "b")]) + node29 = Template(wraptext("a"), [pgens("b", "c")]) + node30 = Template(wraptext("a"), [pgenh("1", "b")]) + node31 = Template(wraptext("a"), [pgenh("1", "b")]) + node32 = Template(wraptext("a"), [pgens("1", "b")]) + node33 = Template(wraptext("a"), [ + pgens("\nb ", " c"), pgens("\nd ", " e"), pgens("\nf ", " g")]) + node34 = Template(wraptext("a\n"), [ + pgens("b ", "c\n"), pgens("d ", " e"), pgens("f ", "g\n"), + pgens("h ", " i\n")]) + node35 = Template(wraptext("a"), [ + pgens("b ", " c\n"), pgens("\nd ", " e"), pgens("\nf ", "g ")]) + node36 = Template(wraptext("a"), [ + pgens("\nb ", " c "), pgens("\nd ", " e "), pgens("\nf ", " g ")]) + node37 = Template(wraptext("a"), [pgens("b", "c"), pgens("d", "e"), + pgens("b", "f"), pgens("b", "h"), + pgens("i", "j")]) + node37 = Template(wraptext("a"), [pgens("b", "c"), pgens("d", "e"), + pgens("b", "f"), pgens("b", "h"), + pgens("i", "j")]) + node38 = Template(wraptext("a"), [pgens("1", "b"), pgens("x", "y"), + pgens("1", "c"), pgens("2", "d")]) + node39 = Template(wraptext("a"), [pgens("1", "b"), pgens("x", "y"), + pgenh("1", "c"), pgenh("2", "d")]) + node40 = Template(wraptext("a"), [pgens("b", "c"), pgens("d", "e"), + pgens("f", "g")]) + + node1.add("e", "f", showkey=True) + node2.add(2, "g", showkey=False) + node3.add("e", "foo|bar", showkey=True) + node4.add("e", "f", showkey=True, before="b") + node5.add("f", "g", showkey=True, before=" d ") + node6.add("f", "g", showkey=True, before="b") + self.assertRaises(ValueError, node7.add, "e", "f", showkey=True, + before="q") + node8.add("e", "f", showkey=True, before=node8p) + node9.add("e", "f", showkey=True, before=pgenh("1", "d")) + self.assertRaises(ValueError, node10.add, "e", "f", showkey=True, + before=pgenh("1", "d")) + node11.add("d", "foo=bar", showkey=True) + node12.add("1", "foo=bar", showkey=False) + node13.add("h", "i", showkey=True) + node14.add("j", "k", showkey=True) + node15.add("h", "i", showkey=True) + node16.add("h", "i", showkey=True, preserve_spacing=False) + node17.add("h", "i", showkey=False) + node18.add("j", "k", showkey=False) + node19.add("h", "i", showkey=False) + node20.add("h", "i", showkey=False, preserve_spacing=False) + node21.add("2", "c") + node22.add("3", "c") + node23.add("c", "d") + node24.add("5", "f") + node25.add("3", "f") + node26.add("6", "f") + node27.add("c", "foo=bar") + node28.add("2", "foo=bar") + node29.add("b", "d") + node30.add("1", "foo=bar") + node31.add("1", "foo=bar", showkey=True) + node32.add("1", "foo=bar", showkey=False) + node33.add("d", "foo") + node34.add("f", "foo") + node35.add("f", "foo") + node36.add("d", "foo", preserve_spacing=False) + node37.add("b", "k") + node38.add("1", "e") + node39.add("1", "e") + node40.add("d", "h", before="b") + + self.assertEqual("{{a|b=c|d|e=f}}", node1) + self.assertEqual("{{a|b=c|d|g}}", node2) + self.assertEqual("{{a|b=c|d|e=foo|bar}}", node3) + self.assertIsInstance(node3.params[2].value.get(1), HTMLEntity) + self.assertEqual("{{a|e=f|b=c|d}}", node4) + self.assertEqual("{{a|b=c|f=g| d =e}}", node5) + self.assertEqual("{{a|b=c|b=d|f=g|b=e}}", node6) + self.assertEqual("{{a|b=c|d}}", node7) + self.assertEqual("{{a|b=c|e=f|d}}", node8) + self.assertEqual("{{a|b=c|e=f|d}}", node9) + self.assertEqual("{{a|b=c|e}}", node10) + self.assertEqual("{{a|b=c|d=foo=bar}}", node11) + self.assertEqual("{{a|b=c|foo=bar}}", node12) + self.assertIsInstance(node12.params[1].value.get(1), HTMLEntity) + self.assertEqual("{{a|\nb = c|\nd = e|\nf = g|\nh = i}}", node13) + self.assertEqual("{{a\n|b =c\n|d = e|f =g\n|h = i\n|j =k\n}}", node14) + self.assertEqual("{{a|b = c\n|\nd = e|\nf =g |h =i}}", node15) + self.assertEqual("{{a|\nb = c|\nd = e|\nf = g|h=i}}", node16) + self.assertEqual("{{a|\nb = c|\nd = e|\nf = g| i}}", node17) + self.assertEqual("{{a\n|b =c\n|d = e|f =g\n|h = i\n|k\n}}", node18) + self.assertEqual("{{a|b = c\n|\nd = e|\nf =g |i}}", node19) + self.assertEqual("{{a|\nb = c|\nd = e|\nf = g|i}}", node20) + self.assertEqual("{{a|b|c}}", node21) + self.assertEqual("{{a|b|3=c}}", node22) + self.assertEqual("{{a|b|c=d}}", node23) + self.assertEqual("{{a|b|c|d|e|f}}", node24) + self.assertEqual("{{a|b|c|4=d|5=e|f}}", node25) + self.assertEqual("{{a|b|c|4=d|5=e|6=f}}", node26) + self.assertEqual("{{a|b|c=foo=bar}}", node27) + self.assertEqual("{{a|b|foo=bar}}", node28) + self.assertIsInstance(node28.params[1].value.get(1), HTMLEntity) + self.assertEqual("{{a|b=d}}", node29) + self.assertEqual("{{a|foo=bar}}", node30) + self.assertIsInstance(node30.params[0].value.get(1), HTMLEntity) + self.assertEqual("{{a|1=foo=bar}}", node31) + self.assertEqual("{{a|foo=bar}}", node32) + self.assertIsInstance(node32.params[0].value.get(1), HTMLEntity) + self.assertEqual("{{a|\nb = c|\nd = foo|\nf = g}}", node33) + self.assertEqual("{{a\n|b =c\n|d = e|f =foo\n|h = i\n}}", node34) + self.assertEqual("{{a|b = c\n|\nd = e|\nf =foo }}", node35) + self.assertEqual("{{a|\nb = c |\nd =foo|\nf = g }}", node36) + self.assertEqual("{{a|b=k|d=e|i=j}}", node37) + self.assertEqual("{{a|1=e|x=y|2=d}}", node38) + self.assertEqual("{{a|x=y|e|d}}", node39) + self.assertEqual("{{a|b=c|d=h|f=g}}", node40) + + def test_remove(self): + """test Template.remove()""" + node1 = Template(wraptext("foobar")) + node2 = Template(wraptext("foo"), [pgenh("1", "bar"), + pgens("abc", "def")]) + node3 = Template(wraptext("foo"), [pgenh("1", "bar"), + pgens("abc", "def")]) + node4 = Template(wraptext("foo"), [pgenh("1", "bar"), + pgenh("2", "baz")]) + node5 = Template(wraptext("foo"), [ + pgens(" a", "b"), pgens("b", "c"), pgens("a ", "d")]) + node6 = Template(wraptext("foo"), [ + pgens(" a", "b"), pgens("b", "c"), pgens("a ", "d")]) + node7 = Template(wraptext("foo"), [ + pgens("1 ", "a"), pgens(" 1", "b"), pgens("2", "c")]) + node8 = Template(wraptext("foo"), [ + pgens("1 ", "a"), pgens(" 1", "b"), pgens("2", "c")]) + node9 = Template(wraptext("foo"), [ + pgens("1 ", "a"), pgenh("1", "b"), pgenh("2", "c")]) + node10 = Template(wraptext("foo"), [ + pgens("1 ", "a"), pgenh("1", "b"), pgenh("2", "c")]) + + node2.remove("1") + node2.remove("abc") + node3.remove(1, keep_field=True) + node3.remove("abc", keep_field=True) + node4.remove("1", keep_field=False) + node5.remove("a", keep_field=False) + node6.remove("a", keep_field=True) + node7.remove(1, keep_field=True) + node8.remove(1, keep_field=False) + node9.remove(1, keep_field=True) + node10.remove(1, keep_field=False) + + self.assertRaises(ValueError, node1.remove, 1) + self.assertRaises(ValueError, node1.remove, "a") + self.assertRaises(ValueError, node2.remove, "1") + self.assertEqual("{{foo}}", node2) + self.assertEqual("{{foo||abc=}}", node3) + self.assertEqual("{{foo||baz}}", node4) + self.assertEqual("{{foo|b=c}}", node5) + self.assertEqual("{{foo| a=|b=c}}", node6) + self.assertEqual("{{foo|1 =|2=c}}", node7) + self.assertEqual("{{foo|2=c}}", node8) + self.assertEqual("{{foo||c}}", node9) + self.assertEqual("{{foo||c}}", node10) + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tests/test_text.py b/tests/test_text.py new file mode 100644 index 0000000..35ac340 --- /dev/null +++ b/tests/test_text.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import unicode_literals +import unittest + +from mwparserfromhell.compat import str +from mwparserfromhell.nodes import Text + +class TestText(unittest.TestCase): + """Test cases for the Text node.""" + + def test_unicode(self): + """test Text.__unicode__()""" + node = Text("foobar") + self.assertEqual("foobar", str(node)) + node2 = Text("fóóbar") + self.assertEqual("fóóbar", str(node2)) + + def test_iternodes(self): + """test Text.__iternodes__()""" + node = Text("foobar") + gen = node.__iternodes__(None) + self.assertEqual((None, node), next(gen)) + self.assertRaises(StopIteration, next, gen) + + def test_strip(self): + """test Text.__strip__()""" + node = Text("foobar") + for a in (True, False): + for b in (True, False): + self.assertIs(node, node.__strip__(a, b)) + + def test_showtree(self): + """test Text.__showtree__()""" + output = [] + node1 = Text("foobar") + node2 = Text("fóóbar") + node3 = Text("𐌲𐌿𐍄") + node1.__showtree__(output.append, None, None) + node2.__showtree__(output.append, None, None) + node3.__showtree__(output.append, None, None) + res = ["foobar", r"f\xf3\xf3bar", "\\U00010332\\U0001033f\\U00010344"] + self.assertEqual(res, output) + + def test_value(self): + """test getter/setter for the value attribute""" + node = Text("foobar") + self.assertEqual("foobar", node.value) + self.assertIsInstance(node.value, str) + node.value = "héhéhé" + self.assertEqual("héhéhé", node.value) + self.assertIsInstance(node.value, str) + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tests/test_utils.py b/tests/test_utils.py index c088530..80a0e5e 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -24,33 +24,28 @@ from __future__ import unicode_literals import unittest from mwparserfromhell.nodes import Template, Text -from mwparserfromhell.smart_list import SmartList from mwparserfromhell.utils import parse_anything -from mwparserfromhell.wikicode import Wikicode -from ._test_tree_equality import TreeEqualityTestCase +from ._test_tree_equality import TreeEqualityTestCase, wrap, wraptext class TestUtils(TreeEqualityTestCase): """Tests for the utils module, which provides parse_anything().""" def test_parse_anything_valid(self): """tests for valid input to utils.parse_anything()""" - wrap = lambda L: Wikicode(SmartList(L)) - textify = lambda L: wrap([Text(item) for item in L]) tests = [ - (wrap([Text("foobar")]), textify(["foobar"])), - (Template(wrap([Text("spam")])), - wrap([Template(textify(["spam"]))])), - ("fóóbar", textify(["fóóbar"])), - (b"foob\xc3\xa1r", textify(["foobár"])), - (123, textify(["123"])), - (True, textify(["True"])), + (wraptext("foobar"), wraptext("foobar")), + (Template(wraptext("spam")), wrap([Template(wraptext("spam"))])), + ("fóóbar", wraptext("fóóbar")), + (b"foob\xc3\xa1r", wraptext("foobár")), + (123, wraptext("123")), + (True, wraptext("True")), (None, wrap([])), ([Text("foo"), Text("bar"), Text("baz")], - textify(["foo", "bar", "baz"])), - ([wrap([Text("foo")]), Text("bar"), "baz", 123, 456], - textify(["foo", "bar", "baz", "123", "456"])), - ([[[([[((("foo",),),)], "bar"],)]]], textify(["foo", "bar"])) + wraptext("foo", "bar", "baz")), + ([wraptext("foo"), Text("bar"), "baz", 123, 456], + wraptext("foo", "bar", "baz", "123", "456")), + ([[[([[((("foo",),),)], "bar"],)]]], wraptext("foo", "bar")) ] for test, valid in tests: self.assertWikicodeEqual(valid, parse_anything(test)) diff --git a/tests/test_wikilink.py b/tests/test_wikilink.py new file mode 100644 index 0000000..7851032 --- /dev/null +++ b/tests/test_wikilink.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Ben Kurtovic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import unicode_literals +import unittest + +from mwparserfromhell.compat import str +from mwparserfromhell.nodes import Text, Wikilink + +from ._test_tree_equality import TreeEqualityTestCase, getnodes, wrap, wraptext + +class TestWikilink(TreeEqualityTestCase): + """Test cases for the Wikilink node.""" + + def test_unicode(self): + """test Wikilink.__unicode__()""" + node = Wikilink(wraptext("foobar")) + self.assertEqual("[[foobar]]", str(node)) + node2 = Wikilink(wraptext("foo"), wraptext("bar")) + self.assertEqual("[[foo|bar]]", str(node2)) + + def test_iternodes(self): + """test Wikilink.__iternodes__()""" + node1n1 = Text("foobar") + node2n1, node2n2, node2n3 = Text("foo"), Text("bar"), Text("baz") + node1 = Wikilink(wrap([node1n1])) + node2 = Wikilink(wrap([node2n1]), wrap([node2n2, node2n3])) + gen1 = node1.__iternodes__(getnodes) + gen2 = node2.__iternodes__(getnodes) + self.assertEqual((None, node1), next(gen1)) + self.assertEqual((None, node2), next(gen2)) + self.assertEqual((node1.title, node1n1), next(gen1)) + self.assertEqual((node2.title, node2n1), next(gen2)) + self.assertEqual((node2.text, node2n2), next(gen2)) + self.assertEqual((node2.text, node2n3), next(gen2)) + self.assertRaises(StopIteration, next, gen1) + self.assertRaises(StopIteration, next, gen2) + + def test_strip(self): + """test Wikilink.__strip__()""" + node = Wikilink(wraptext("foobar")) + node2 = Wikilink(wraptext("foo"), wraptext("bar")) + for a in (True, False): + for b in (True, False): + self.assertEqual("foobar", node.__strip__(a, b)) + self.assertEqual("bar", node2.__strip__(a, b)) + + def test_showtree(self): + """test Wikilink.__showtree__()""" + output = [] + getter, marker = object(), object() + get = lambda code: output.append((getter, code)) + mark = lambda: output.append(marker) + node1 = Wikilink(wraptext("foobar")) + node2 = Wikilink(wraptext("foo"), wraptext("bar")) + node1.__showtree__(output.append, get, mark) + node2.__showtree__(output.append, get, mark) + valid = [ + "[[", (getter, node1.title), "]]", "[[", (getter, node2.title), + " | ", marker, (getter, node2.text), "]]"] + self.assertEqual(valid, output) + + def test_title(self): + """test getter/setter for the title attribute""" + title = wraptext("foobar") + node1 = Wikilink(title) + node2 = Wikilink(title, wraptext("baz")) + self.assertIs(title, node1.title) + self.assertIs(title, node2.title) + node1.title = "héhehé" + node2.title = "héhehé" + self.assertWikicodeEqual(wraptext("héhehé"), node1.title) + self.assertWikicodeEqual(wraptext("héhehé"), node2.title) + + def test_text(self): + """test getter/setter for the text attribute""" + text = wraptext("baz") + node1 = Wikilink(wraptext("foobar")) + node2 = Wikilink(wraptext("foobar"), text) + self.assertIs(None, node1.text) + self.assertIs(text, node2.text) + node1.text = "buzz" + node2.text = None + self.assertWikicodeEqual(wraptext("buzz"), node1.text) + self.assertIs(None, node2.text) + +if __name__ == "__main__": + unittest.main(verbosity=2)