debian-suds-jurko/tests/test_compare_sax.py

246 lines
11 KiB
Python

# -*- coding: utf-8 -*-
# This program is free software; you can redistribute it and/or modify it under
# the terms of the (LGPL) GNU Lesser General Public License as published by the
# Free Software Foundation; either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Library Lesser General Public License
# for more details at ( http://www.gnu.org/licenses/lgpl.html ).
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# written by: Jurko Gospodnetić ( jurko.gospodnetic@pke.hr )
"""
CompareSAX testing utility unit tests.
"""
if __name__ == "__main__":
import testutils
testutils.run_using_pytest(globals())
import suds
import suds.sax.document
import suds.sax.parser
from testutils.assertion import assert_no_output
from testutils.compare_sax import CompareSAX
import pytest
from six import text_type, u
import xml.sax
# CompareSAX class uses Python assertions to report failed comparison results
# so we need to skip the tests in this module if Python assertions have been
# disabled in the CompareSAX implementation module.
skip_test_if_CompareSAX_assertions_disabled = pytest.mark.skipif(
not CompareSAX.assertions_enabled(),
reason="CompareSAX assertions disabled")
@skip_test_if_CompareSAX_assertions_disabled
@pytest.mark.parametrize("data", (
"",
"<bad1/><bad2/>",
'<bad a="1" a="1"/>',
"<bad><bad>xml</document></bad>"))
def test_failed_parsing(data, capsys):
pytest.raises(xml.sax.SAXParseException, CompareSAX.data2data, data, data)
assert_no_output(capsys)
class TestMatched:
"""Successful CompareSAX matching tests."""
@skip_test_if_CompareSAX_assertions_disabled
def test_empty_document(self, capsys):
a = suds.sax.document.Document()
b = suds.sax.document.Document()
CompareSAX.document2document(a, b)
assert_no_output(capsys)
@skip_test_if_CompareSAX_assertions_disabled
@pytest.mark.parametrize(("data1", "data2"), (
# Simple matches.
('<a><ns:b xmlns:ns="x"/></a>', '<a><ns:b xmlns:ns="x"/></a>'),
('<a><b xmlns="x"/></a>', '<a><b xmlns="x"/></a>'),
('<a xmlns="x"><b/></a>', '<a xmlns="x"><b/></a>'),
# Extra namespace declarations.
('<ns1:b xmlns:ns1="two"/>', '<ns2:b xmlns="one" xmlns:ns2="two"/>'),
('<ns1:b xmlns:ns1="2"/>', '<ns2:b xmlns:ns3="1" xmlns:ns2="2"/>'),
('<b xmlns="1"/>', '<ns1:b xmlns="0" xmlns:ns1="1" xmlns:ns2="2"/>'),
# Mismatched namespace prefixes.
('<a xmlns="one"/>', '<ns:a xmlns:ns="one"/>'),
('<ns1:b xmlns:ns1="two"/>', '<ns2:b xmlns:ns2="two"/>'),
# Numeric unicode character references.
(u("<a>\u2606</a>"), "<a>&#%d;</a>" % (0x2606,))))
def test_data2data(self, data1, data2, capsys):
CompareSAX.data2data(data1, data2)
assert_no_output(capsys)
@skip_test_if_CompareSAX_assertions_disabled
@pytest.mark.parametrize("type1", (suds.byte_str, text_type))
@pytest.mark.parametrize("type2", (suds.byte_str, text_type))
def test_string_input_types(self, type1, type2, capsys):
xml = "<a/>"
CompareSAX.data2data(type1(xml), type2(xml))
assert_no_output(capsys)
@skip_test_if_CompareSAX_assertions_disabled
def test_xml_encoding(self, capsys):
"""Test that the encoding listed in the XML declaration is honored."""
xml_format = u('<?xml version="1.0" encoding="%s"?><a>\u00D8</a>')
data1 = (xml_format % ("UTF-8",)).encode('utf-8')
data2 = (xml_format % ("latin1",)).encode('latin1')
CompareSAX.data2data(data1, data2)
assert_no_output(capsys)
class TestMismatched:
"""Failed CompareSAX matching tests."""
@skip_test_if_CompareSAX_assertions_disabled
@pytest.mark.parametrize(("data1", "data2", "expected_context"), (
# Different element namespaces.
("<a/>", '<a xmlns="x"/>', "data2data.<a>.namespace"),
('<a xmlns="1"/>', '<a xmlns="2"/>', "data2data.<a>.namespace"),
('<r><a xmlns="1"/></r>', '<r><a xmlns="2"/></r>',
"data2data.<r>.<a>.namespace"),
('<r><tag><a xmlns="1"/></tag><y/></r>',
'<r><tag><a xmlns="2"/></tag><y/></r>',
"data2data.<r>.<tag(1/2)>.<a>.namespace"),
# Different textual content in text only nodes.
("<a>one</a>", "<a>two</a>", "data2data.<a>.text"),
("<a>x</a>", "<a>x </a>", "data2data.<a>.text"),
("<a>x</a>", "<a>x </a>", "data2data.<a>.text"),
("<a>x </a>", "<a>x </a>", "data2data.<a>.text"),
("<a> x</a>", "<a>x</a>", "data2data.<a>.text"),
("<a> x</a>", "<a>x</a>", "data2data.<a>.text"),
("<a> x</a>", "<a> x</a>", "data2data.<a>.text"),
("<a><b><c>x</c><c2/></b></a>", "<a><b><c>X</c><c2/></b></a>",
"data2data.<a>.<b>.<c(1/2)>.text"),
("<a><b><c>x</c><d>y</d></b></a>", "<a><b><c>x</c><d>Y</d></b></a>",
"data2data.<a>.<b>.<d(2/2)>.text"),
# Different textual content in mixed content nodes with children.
("<a>42<b/><b/>42</a>", "<a>42<b/> <b/>42</a>", "data2data.<a>.text"),
# Differently named elements.
("<a/>", "<b/>", "data2data.<a/b>"),
("<a><b/></a>", "<a><c/></a>", "data2data.<a>.<b/c>"),
("<a><b/><x/></a>", "<a><c/><x/></a>", "data2data.<a>.<b/c(1/2)>"),
("<a><x/><b/></a>", "<a><x/><c/></a>", "data2data.<a>.<b/c(2/2)>"),
("<a><b><c/></b></a>", "<a><b><d/></b></a>",
"data2data.<a>.<b>.<c/d>"),
("<a><b><y1/><y2/><c/></b><x/></a>",
"<a><b><y1/><y2/><d/></b><x/></a>",
"data2data.<a>.<b(1/2)>.<c/d(3/3)>"),
# Extra/missing non-root element.
("<a><b/></a>", "<a/>", "data2data.<a>"),
("<a/>", "<a><b/></a>", "data2data.<a>"),
("<a><x/><b/></a>", "<a><b/></a>", "data2data.<a>"),
("<a><b/><x/></a>", "<a><b/></a>", "data2data.<a>"),
("<a><b/></a>", "<a><x/><b/></a>", "data2data.<a>"),
("<a><b/></a>", "<a><b/><x/></a>", "data2data.<a>"),
# Multiple differences.
("<a><b/></a>", "<c><d/></c>", "data2data.<a/c>"),
("<a><b/></a>", '<a xmlns="o"><c/></a>', "data2data.<a>.namespace"),
("<r><a><b/></a></r>", "<r><c><d/></c></r>", "data2data.<r>.<a/c>"),
("<r><a><b/></a></r>", '<r><a xmlns="o"><c/></a></r>',
"data2data.<r>.<a>.namespace")))
def test_data2data(self, data1, data2, expected_context, capsys):
pytest.raises(AssertionError, CompareSAX.data2data, data1, data2)
_assert_context_output(capsys, expected_context)
@skip_test_if_CompareSAX_assertions_disabled
def test_document2document_context(self, capsys):
a = suds.sax.document.Document()
b = suds.sax.parser.Parser().parse(string=suds.byte_str("<a/>"))
pytest.raises(AssertionError, CompareSAX.document2document, a, b)
_assert_context_output(capsys, "document2document")
@skip_test_if_CompareSAX_assertions_disabled
def test_document2element_context(self, capsys):
a = suds.sax.parser.Parser().parse(string=suds.byte_str("<xx>1</xx>"))
b = suds.sax.parser.Parser().parse(string=suds.byte_str("<xx>2</xx>"))
pytest.raises(AssertionError, CompareSAX.document2element, a, b.root())
_assert_context_output(capsys, "document2element.<xx>.text")
@skip_test_if_CompareSAX_assertions_disabled
def test_element2element_context(self, capsys):
Parser = suds.sax.parser.Parser
e1 = Parser().parse(string=suds.byte_str("<x/>")).root()
e2 = Parser().parse(string=suds.byte_str("<y/>")).root()
pytest.raises(AssertionError, CompareSAX.element2element, e1, e2)
_assert_context_output(capsys, "element2element.<x/y>")
@skip_test_if_CompareSAX_assertions_disabled
def test_element2element_context_invalid_name__left(self, capsys):
Parser = suds.sax.parser.Parser
e = Parser().parse(string=suds.byte_str("<x/>")).root()
e_invalid = object()
pytest.raises(AssertionError, CompareSAX.element2element, e_invalid, e)
_assert_context_output(capsys, "element2element.<???/x>")
@skip_test_if_CompareSAX_assertions_disabled
def test_element2element_context_invalid_name__right(self, capsys):
Parser = suds.sax.parser.Parser
e = Parser().parse(string=suds.byte_str("<y/>")).root()
e_invalid = object()
pytest.raises(AssertionError, CompareSAX.element2element, e, e_invalid)
_assert_context_output(capsys, "element2element.<y/???>")
@skip_test_if_CompareSAX_assertions_disabled
def test_empty_vs_non_empty_document(self, capsys):
document = suds.sax.document.Document()
data = "<a/>"
pytest.raises(AssertionError, CompareSAX.document2data, document, data)
_assert_context_output(capsys, "document2data")
#TODO: TestSAXModelFeatures tests should be removed once their respective SAX
# document model features get tested by SAX document model specific unit tests.
#TODO: Additional missing suds SAX document model unit tests:
# * SAX parser fails on documents with multiple root elements.
# * SAX document may contain at most one element, accessible as root().
# * SAX document append() overwrites the root element silently.
class TestSAXModelFeatures:
"""SAX document model feature testing using the CompareSAX interface."""
@skip_test_if_CompareSAX_assertions_disabled
@pytest.mark.parametrize(("data1", "data2"), (
# Differently placed default namespace declaration.
('<ns:a xmlns:ns="1" xmlns="2"><b/></ns:a>',
'<ns:a xmlns:ns="1"><b xmlns="2"/></ns:a>'),
# Differently placed namespace prefix declaration.
('<a xmlns:ns="1"><ns:b/></a>', '<a><ns:b xmlns:ns="1"/></a>'),
# Element's textual content merged.
("<a>111<b/>222</a>", "<a>111222<b/></a>"),
("<a>111<b/>222</a>", "<a><b/>111222</a>"),
("<a>111<b/>222</a>", "<a>11<b/>1222</a>"),
# Explicit "" namespace == no prefix or default namespace.
('<a xmlns=""/>', "<a/>"),
('<ns:a xmlns:ns=""/>', "<a/>"),
# Extra leading/trailing textual whitespace trimmed in mixed content
# elements with more than one child element.
("<a> \n\n <b/> \t\t\n\n</a>", "<a><b/></a>"),
("<a> \nxxx\n <b/> \t\t\n\n</a>", "<a>xxx<b/></a>")))
def test_data2data(self, data1, data2, capsys):
CompareSAX.data2data(data1, data2)
assert_no_output(capsys)
def _assert_context_output(capsys, context):
"""
Test utility asserting an expected captured stderr context output and no
captured stdout output.
"""
out, err = capsys.readouterr()
assert not out
assert err == "Failed SAX XML comparison context:\n %s\n" % (context,)