[diffoscope] 01/05: Add new XML comparator. (Closes: #866120)

Chris Lamb chris at chris-lamb.co.uk
Fri Jul 21 11:43:55 CEST 2017


This is an automated email from the git hooks/post-receive script.

lamby pushed a commit to branch master
in repository diffoscope.

commit 7734b8f0b430dbe6d30125369374fbccdcbd54ee
Author: Juliana Rodrigues <juliana.orod at gmail.com>
Date:   Thu Jul 20 23:56:03 2017 -0300

    Add new XML comparator. (Closes: #866120)
    
    Signed-off-by: Juliana Rodrigues <juliana.orod at gmail.com>
    Signed-off-by: Chris Lamb <lamby at debian.org>
---
 diffoscope/comparators/__init__.py |   1 +
 diffoscope/comparators/xml.py      | 101 +++++++++++++++++++++++++++++++++++++
 tests/comparators/test_xml.py      |  49 ++++++++++++++++++
 tests/data/test1.xml               |   9 ++++
 tests/data/test2.xml               |   9 ++++
 tests/data/test_invalid.xml        |   8 +++
 tests/data/test_xml_expected_diff  |  14 +++++
 7 files changed, 191 insertions(+)

diff --git a/diffoscope/comparators/__init__.py b/diffoscope/comparators/__init__.py
index d22aa79..7653741 100644
--- a/diffoscope/comparators/__init__.py
+++ b/diffoscope/comparators/__init__.py
@@ -40,6 +40,7 @@ class ComparatorManager(object):
         ('ps.PsFile',),
         ('javascript.JavaScriptFile',),
         ('json.JSONFile',),
+        ('xml.XMLFile',),
         ('text.TextFile',),
         ('bzip2.Bzip2File',),
         ('cpio.CpioFile',),
diff --git a/diffoscope/comparators/xml.py b/diffoscope/comparators/xml.py
new file mode 100644
index 0000000..d46f1bc
--- /dev/null
+++ b/diffoscope/comparators/xml.py
@@ -0,0 +1,101 @@
+import re
+
+from xml.dom import minidom
+from diffoscope.difference import Difference
+from diffoscope.comparators.utils.file import File
+from xml.parsers.expat import ExpatError
+
+def _format(node):
+  """
+  Removes *inplace* spaces from minidom.Document
+
+  Args:
+    node -- A xml.dom.minidom.Document object
+
+  Returns:
+    void
+  """
+  for n in node.childNodes:
+    if n.nodeType == minidom.Node.TEXT_NODE:
+      if n.nodeValue: n.nodeValue = n.nodeValue.strip()
+    elif n.nodeType == minidom.Node.ELEMENT_NODE:
+      _format(n)
+
+def _parse(file):
+  """
+  Formats a minidom.Document file and returns XML as string.
+
+  Args:
+    file -- An io.TextIOWrapper object
+
+  Returns:
+    str: formated string object
+  """
+  xml = minidom.parse(file)
+  _format(xml)
+  xml.normalize()
+  return xml.toprettyxml(indent=2*' ')
+
+
+class XMLFile(File):
+  """
+  XML Files Comparison class
+
+  Attributes:
+    RE_FILE_EXTENSION (SRE_Pattern): xml file extension pattern
+  """
+  RE_FILE_EXTENSION = re.compile(r'\.xml$')
+
+  @staticmethod
+  def recognizes(file):
+    """
+    Identifies if a given file has XML extension
+
+    Args:
+      file - a diffoscope.comparators.utils.file.File object
+
+    Returns:
+      False if file is not a XML File, True otherwise
+    """
+    if XMLFile.RE_FILE_EXTENSION.search(file.name) is None:
+      return False
+
+    with open(file.path) as f:
+      try:
+        file.parsed = _parse(f)
+      except ExpatError:
+        return False
+
+    return True
+
+  def compare_details(self, other, source=None):
+    """
+    Compares self.object with another, returning a Difference object
+
+    Args:
+      other  -- A XMLFile object
+      source
+
+    Returns:
+      A diffoscope.difference.Difference object
+    """
+    return [ Difference.from_text(self.dumps(self), self.dumps(other),
+      self.path, other.path)]
+
+  def dumps(self, file):
+    """
+    Opens a XMLFile and returns its parsed content
+
+    Args:
+      file -- XMLFile object
+
+    Returns:
+      str -- Formatted XML content from file
+    """
+    if file.parsed:
+      return file.parsed
+
+    with open(file.path) as f:
+      return _parse(f)
+
+
diff --git a/tests/comparators/test_xml.py b/tests/comparators/test_xml.py
new file mode 100644
index 0000000..a1da7d1
--- /dev/null
+++ b/tests/comparators/test_xml.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+#
+# diffoscope: in-depth comparison of files, archives, and directories
+#
+# Copyright � 2016 Chris Lamb <lamby at debian.org>
+#
+# diffoscope is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# diffoscope 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with diffoscope.  If not, see <https://www.gnu.org/licenses/>.
+
+import pytest
+
+from diffoscope.comparators.xml import XMLFile
+
+from ..utils.data import load_fixture, get_data
+from ..utils.nonexisting import assert_non_existing
+
+
+xml_a = load_fixture('test1.xml')
+xml_b = load_fixture('test2.xml')
+invalid_xml = load_fixture('test_invalid.xml')
+
+def test_identification(xml_a):
+    assert isinstance(xml_a, XMLFile)
+
+def test_invalid(invalid_xml):
+    assert not isinstance(invalid_xml, XMLFile)
+
+def test_no_differences(xml_a):
+    assert xml_a.compare(xml_a) is None
+
+ at pytest.fixture
+def differences(xml_a, xml_b):
+    return xml_a.compare(xml_b).details
+
+def test_diff(differences):
+    expected_diff = get_data('test_xml_expected_diff')
+    assert differences[0].unified_diff == expected_diff
+
+
diff --git a/tests/data/test1.xml b/tests/data/test1.xml
new file mode 100644
index 0000000..b02bf09
--- /dev/null
+++ b/tests/data/test1.xml
@@ -0,0 +1,9 @@
+<note>
+        <style type="text/css" id="night-mode-pro-style" />
+        <link type="text/css" rel="stylesheet"
+        id="night-mode-pro-link" />
+        <to>Tove</to>
+        <from>Jani</from>
+        <heading>Reminder</heading>
+        <body>Don't forget me this weekend!</body>
+</note>
diff --git a/tests/data/test2.xml b/tests/data/test2.xml
new file mode 100644
index 0000000..7e892f8
--- /dev/null
+++ b/tests/data/test2.xml
@@ -0,0 +1,9 @@
+<note>
+        <style type="text/css" id="night-mode-pro-style" />
+        <link type="text/css" rel="stylesheet"
+        id="night-mode-pro-link" />
+        <to>Jani</to>
+        <from>Toni</from>
+        <heading>Re: Reminder</heading>
+        <body>Pick me up on 5!</body>
+</note>
diff --git a/tests/data/test_invalid.xml b/tests/data/test_invalid.xml
new file mode 100644
index 0000000..2a4cd51
--- /dev/null
+++ b/tests/data/test_invalid.xml
@@ -0,0 +1,8 @@
+<note>
+        style type="text/css" id="night-mode-pro-style" />
+        <link type="text/css" rel="stylesheet"
+        id="night-mode-pro-link" />
+        <to>Tove</to>
+        from>Jani</from>
+        <heading>Reminder</heading>
+        <body>Don't forget me this weekend!</body>
diff --git a/tests/data/test_xml_expected_diff b/tests/data/test_xml_expected_diff
new file mode 100644
index 0000000..0b450cb
--- /dev/null
+++ b/tests/data/test_xml_expected_diff
@@ -0,0 +1,14 @@
+@@ -1,9 +1,9 @@
+ <?xml version="1.0" ?>
+ <note>
+   <style id="night-mode-pro-style" type="text/css"/>
+   <link id="night-mode-pro-link" rel="stylesheet" type="text/css"/>
+-  <to>Tove</to>
+-  <from>Jani</from>
+-  <heading>Reminder</heading>
+-  <body>Don't forget me this weekend!</body>
++  <to>Jani</to>
++  <from>Toni</from>
++  <heading>Re: Reminder</heading>
++  <body>Pick me up on 5!</body>
+ </note>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/reproducible/diffoscope.git


More information about the diffoscope mailing list