4 new revisions:

Revision: cb464bb79ba2
Author:   Pekka Klärck
Date:     Sat Jun 30 08:09:49 2012
Log: XML library: Never include XML declaration in return value from `Eleme...
http://code.google.com/p/robotframework/source/detail?r=cb464bb79ba2

Revision: 062877363512
Author:   Pekka Klärck
Date:     Sat Jun 30 08:12:38 2012
Log: XML library: Changed 'Log Element' to also return the logged content. ...
http://code.google.com/p/robotframework/source/detail?r=062877363512

Revision: 8ad874c5e685
Author:   Pekka Klärck
Date:     Sat Jun 30 08:36:27 2012
Log:      XML library: Clean-up
http://code.google.com/p/robotframework/source/detail?r=8ad874c5e685

Revision: 865fd92cbc21
Author:   Pekka Klärck
Date:     Sat Jun 30 09:15:44 2012
Log: XML library: implemented normalize_whitespace support for 'Elements Sh...
http://code.google.com/p/robotframework/source/detail?r=865fd92cbc21

==============================================================================
Revision: cb464bb79ba2
Author:   Pekka Klärck
Date:     Sat Jun 30 08:09:49 2012
Log: XML library: Never include XML declaration in return value from `Element To String`. The main motivation for this change was that ET.ElementTree.write() in Python 2.6 does not support xml_declaration option. Interestingly that is not mentioned in ET docs.
http://code.google.com/p/robotframework/source/detail?r=cb464bb79ba2

Modified:
 /atest/testdata/standard_libraries/xml/to_string.txt
 /src/robot/libraries/XML.py

=======================================
--- /atest/testdata/standard_libraries/xml/to_string.txt Fri Jun 29 02:01:34 2012 +++ /atest/testdata/standard_libraries/xml/to_string.txt Sat Jun 30 08:09:49 2012
@@ -2,16 +2,17 @@
 Library      OperatingSystem
 Resource     resource.txt

-*** Test cases ***
+*** Variables ***
+${XML}    <root>\n\t<tag attr="1">content</tag>\n</root>
+
+*** Test Cases ***
 Log element
     Log Element    <simple>content</simple>
     Log Element    <root><tag a="1" c="3">päivää</tag></root>    DEBUG
     Log Element    ${TEST}

 Element to string
-    ${text}=    Element To String    <simple>content</simple>
-    Should Be Equal    ${text}    <simple>content</simple>
-    ${text}=    Element To String    ${TEST}    xml_declaration=${True}
-    ${expected}=    Get File    ${TEST}
-    # TODO: Fails because attributes are not ordered
-    Should Be Equal    ${text.strip()}    ${expected.strip()}
+    ${string}=    Element To String    ${XML}
+    Should Be Equal    ${string}    ${XML}
+    ${string}=    Element To String    ${TEST}
+    Elements Should Be Equal    ${string}    ${TEST}
=======================================
--- /src/robot/libraries/XML.py Fri Jun 29 02:40:48 2012
+++ /src/robot/libraries/XML.py Sat Jun 30 08:09:49 2012
@@ -16,7 +16,6 @@

 import re
 from functools import partial
-from StringIO import StringIO

 from robot.libraries.BuiltIn import BuiltIn
 from robot.api import logger
@@ -33,6 +32,7 @@
     _should_be_equal = BuiltIn().should_be_equal
     _should_match = BuiltIn().should_match
     _normalize_whitespace = partial(re.compile('\s+').sub, ' ')
+    _xml_declaration = re.compile('^<\?xml .*\?>\n')

     def parse_xml(self, source):
         with ETSource(source) as source:
@@ -117,12 +117,9 @@
     def log_element(self, source, level='INFO'):
         logger.write(self.element_to_string(source), level)

-    def element_to_string(self, source, xml_declaration=False):
-        tree = ET.ElementTree(self.get_element(source))
-        output = StringIO()
- tree.write(output, encoding='UTF-8', xml_declaration=xml_declaration)
-        output.seek(0)
-        return output.read().decode('UTF-8')
+    def element_to_string(self, source):
+        string = ET.tostring(self.get_element(source), encoding='UTF-8')
+        return self._xml_declaration.sub('', string.decode('UTF-8'))


 class ElementComparator(object):

==============================================================================
Revision: 062877363512
Author:   Pekka Klärck
Date:     Sat Jun 30 08:12:38 2012
Log: XML library: Changed 'Log Element' to also return the logged content. Also moved 'Log Element' after 'Element To String'.
http://code.google.com/p/robotframework/source/detail?r=062877363512

Modified:
 /atest/testdata/standard_libraries/xml/to_string.txt
 /src/robot/libraries/XML.py

=======================================
--- /atest/testdata/standard_libraries/xml/to_string.txt Sat Jun 30 08:09:49 2012 +++ /atest/testdata/standard_libraries/xml/to_string.txt Sat Jun 30 08:12:38 2012
@@ -6,13 +6,15 @@
 ${XML}    <root>\n\t<tag attr="1">content</tag>\n</root>

 *** Test Cases ***
-Log element
-    Log Element    <simple>content</simple>
-    Log Element    <root><tag a="1" c="3">päivää</tag></root>    DEBUG
-    Log Element    ${TEST}
-
 Element to string
     ${string}=    Element To String    ${XML}
     Should Be Equal    ${string}    ${XML}
     ${string}=    Element To String    ${TEST}
     Elements Should Be Equal    ${string}    ${TEST}
+
+Log element
+    ${string}=    Log Element    ${XML}
+    Should Be Equal    ${string}    ${XML}
+    Log Element    <root><tag a="1" c="3">päivää</tag></root>    DEBUG
+    Log Element    ${TEST}
+
=======================================
--- /src/robot/libraries/XML.py Sat Jun 30 08:09:49 2012
+++ /src/robot/libraries/XML.py Sat Jun 30 08:12:38 2012
@@ -114,13 +114,15 @@
ElementComparator(self._should_match).compare(self.get_element(source),
                 self.get_element(expected), normalize_whitespace)

-    def log_element(self, source, level='INFO'):
-        logger.write(self.element_to_string(source), level)
-
     def element_to_string(self, source):
         string = ET.tostring(self.get_element(source), encoding='UTF-8')
         return self._xml_declaration.sub('', string.decode('UTF-8'))

+    def log_element(self, source, level='INFO'):
+        string = self.element_to_string(source)
+        logger.write(string, level)
+        return string
+

 class ElementComparator(object):
     _should_be_equal = BuiltIn().should_be_equal

==============================================================================
Revision: 8ad874c5e685
Author:   Pekka Klärck
Date:     Sat Jun 30 08:36:27 2012
Log:      XML library: Clean-up
http://code.google.com/p/robotframework/source/detail?r=8ad874c5e685

Modified:
 /atest/testdata/standard_libraries/xml/elements_should_be_equal.txt
 /atest/testdata/standard_libraries/xml/elements_should_match.txt
 /src/robot/libraries/XML.py

=======================================
--- /atest/testdata/standard_libraries/xml/elements_should_be_equal.txt Fri Jun 29 02:29:41 2012 +++ /atest/testdata/standard_libraries/xml/elements_should_be_equal.txt Sat Jun 30 08:36:27 2012
@@ -14,7 +14,7 @@
     <tag/>   <täg/>    Different tag name: tag != täg

 Different attribute values
-    <tag a="1"/>   <tag a="2"/>    Different value for attribute a: 1 != 2
+ <tag a="1"/> <tag a="2"/> Different value for attribute 'a': 1 != 2
     <tag a="1" c="3"/>   <tag b="1"/>
     ...   Different attribute names: ['a', 'c'] != ['b']

=======================================
--- /atest/testdata/standard_libraries/xml/elements_should_match.txt Fri Jun 29 02:29:41 2012 +++ /atest/testdata/standard_libraries/xml/elements_should_match.txt Sat Jun 30 08:36:27 2012
@@ -13,7 +13,7 @@
     <tag/>   <täg/>    Different tag name: tag != täg

 Different attribute values
- <tag a="12"/> <tag a="?"/> Different value for attribute a: '12' does not match '?' + <tag a="12"/> <tag a="?"/> Different value for attribute 'a': '12' does not match '?'
     <tag a="1" c="3"/>   <tag a="?" b="?"/>
     ...   Different attribute names: ['a', 'c'] != ['a', 'b']

=======================================
--- /src/robot/libraries/XML.py Sat Jun 30 08:12:38 2012
+++ /src/robot/libraries/XML.py Sat Jun 30 08:36:27 2012
@@ -15,36 +15,31 @@
 from __future__ import with_statement

 import re
-from functools import partial
-
-from robot.libraries.BuiltIn import BuiltIn
+
 from robot.api import logger
+from robot.libraries.BuiltIn import BuiltIn
 from robot.utils import ET, ETSource


+should_be_equal = BuiltIn().should_be_equal
+should_match = BuiltIn().should_match
+
+
 class XML(object):
     """
Supported xpath is documented here: http://effbot.org/zone/element-xpath.htm
-    Notice that support for predicates (e.g. tag[@id="1"]) is supported
-    only in 1.3 i.e in Python 2.7!
+    Notice that predicates (e.g. tag[@id="1"]) are supported only in ET 1.3
+    i.e in Python 2.7!
     """
-
-    _should_be_equal = BuiltIn().should_be_equal
-    _should_match = BuiltIn().should_match
-    _normalize_whitespace = partial(re.compile('\s+').sub, ' ')
+    _whitespace = re.compile('\s+')
     _xml_declaration = re.compile('^<\?xml .*\?>\n')

     def parse_xml(self, source):
         with ETSource(source) as source:
             return ET.parse(source).getroot()

-    def _get_parent(self, source):
-        if isinstance(source, basestring):
-            return self.parse_xml(source)
-        return source
-
     def get_element(self, source, xpath='.'):
- if xpath == '.': # TODO: Is this good workaround for ET 1.2 not supporting '.'? + if xpath == '.': # ET included in Python < 2.7 does not support '.'.
             return self._get_parent(source)
         elements = self.get_elements(source, xpath)
         if not elements:
@@ -54,6 +49,11 @@
                                % (len(elements), xpath))
         return elements[0]

+    def _get_parent(self, source):
+        if isinstance(source, basestring):
+            return self.parse_xml(source)
+        return source
+
     def get_elements(self, source, xpath):
         return self._get_parent(source).findall(xpath)

@@ -73,6 +73,9 @@
         if element.tail and not top:
             yield element.tail

+    def _normalize_whitespace(self, text):
+        return self._whitespace.sub(' ', text.strip())
+
def get_elements_texts(self, source, xpath, normalize_whitespace=False): return [self.get_element_text(elem, normalize_whitespace=normalize_whitespace)
                 for elem in self.get_elements(source, xpath)]
@@ -80,12 +83,12 @@
     def element_text_should_be(self, source, expected, xpath='.',
                                normalize_whitespace=False, message=None):
         text = self.get_element_text(source, xpath, normalize_whitespace)
-        self._should_be_equal(text, expected, message, values=False)
+        should_be_equal(text, expected, message, values=False)

     def element_text_should_match(self, source, pattern, xpath='.',
normalize_whitespace=False, message=None):
         text = self.get_element_text(source, xpath, normalize_whitespace)
-        self._should_match(text, pattern, message, values=False)
+        should_match(text, pattern, message, values=False)

     def get_element_attribute(self, source, name, xpath='.', default=None):
         return self.get_element(source, xpath).get(name, default)
@@ -96,23 +99,23 @@
def element_attribute_should_be(self, source, name, expected, xpath='.',
                                     message=None):
         attr = self.get_element_attribute(source, name, xpath)
-        self._should_be_equal(attr, expected, message, values=False)
+        should_be_equal(attr, expected, message, values=False)

def element_attribute_should_match(self, source, name, pattern, xpath='.',
                                        message=None):
         attr = self.get_element_attribute(source, name, xpath)
         if attr is None:
             raise AssertionError("Attribute '%s' does not exist." % name)
-        self._should_match(attr, pattern, message, values=False)
+        should_match(attr, pattern, message, values=False)

     def elements_should_be_equal(self, source, expected,
                                  normalize_whitespace=False):
- ElementComparator(self._should_be_equal).compare(self.get_element(source),
-            self.get_element(expected), normalize_whitespace)
+ comparator = ElementComparator(should_be_equal, normalize_whitespace) + comparator.compare(self.get_element(source), self.get_element(expected))

def elements_should_match(self, source, expected, normalize_whitespace=False): - ElementComparator(self._should_match).compare(self.get_element(source),
-                self.get_element(expected), normalize_whitespace)
+        comparator = ElementComparator(should_match, normalize_whitespace)
+ comparator.compare(self.get_element(source), self.get_element(expected))

     def element_to_string(self, source):
         string = ET.tostring(self.get_element(source), encoding='UTF-8')
@@ -125,39 +128,38 @@


 class ElementComparator(object):
-    _should_be_equal = BuiltIn().should_be_equal
-
-    def __init__(self, comparator):
+
+    def __init__(self, comparator, normalize_whitespace):
         self._comparator = comparator
-
-    def compare(self, actual, expected, normalize_whitespace):
-        self._compare_tag_name(actual, expected)
-        self._compare_texts(actual, expected)
+        self._normalize_whitespace = normalize_whitespace
+
+    def compare(self, actual, expected):
+        self._compare_tags(actual, expected)
         self._compare_attributes(actual, expected)
+        self._compare_texts(actual, expected)
         self._compare_tails(actual, expected)
-        self._compare_children(actual, expected, normalize_whitespace)
-
-    def _compare_tag_name(self, actual, expected):
- self._should_be_equal(actual.tag, expected.tag, 'Different tag name')
+        self._compare_children(actual, expected)
+
+    def _compare_tags(self, actual, expected):
+        should_be_equal(actual.tag, expected.tag, 'Different tag name')

     def _compare_texts(self, actual, expected):
         self._comparator(actual.text or '', expected.text or '',
                          'Different text')

     def _compare_attributes(self, actual, expected):
-        act_names = sorted(actual.attrib.keys())
-        exp_names = sorted(expected.attrib.keys())
- self._should_be_equal(act_names, exp_names, 'Different attribute names')
+        should_be_equal(sorted(actual.attrib), sorted(expected.attrib),
+                        'Different attribute names')
         for key in actual.attrib:
-            act, exp = actual.attrib[key], expected.attrib[key]
- self._comparator(act, exp, 'Different value for attribute %s' % key)
+            self._comparator(actual.attrib[key], expected.attrib[key],
+                             "Different value for attribute '%s'" % key)

     def _compare_tails(self, actual, expected):
         self._comparator(actual.tail or '', expected.tail or '',
                          'Different tail text')

-    def _compare_children(self, actual, expected, normalize_whitespace):
-        self._should_be_equal(len(actual), len(expected),
-                              'Different number of child elements')
-        for a, e in zip(actual, expected):
-            self.compare(a, e, normalize_whitespace)
+    def _compare_children(self, actual, expected):
+        should_be_equal(len(actual), len(expected),
+                        'Different number of child elements')
+        for act, exp in zip(actual, expected):
+            self.compare(act, exp)

==============================================================================
Revision: 865fd92cbc21
Author:   Pekka Klärck
Date:     Sat Jun 30 09:15:44 2012
Log: XML library: implemented normalize_whitespace support for 'Elements Should *' keywords
http://code.google.com/p/robotframework/source/detail?r=865fd92cbc21

Modified:
 /atest/testdata/standard_libraries/xml/elements_should_be_equal.txt
 /atest/testdata/standard_libraries/xml/elements_should_match.txt
 /src/robot/libraries/XML.py

=======================================
--- /atest/testdata/standard_libraries/xml/elements_should_be_equal.txt Sat Jun 30 08:36:27 2012 +++ /atest/testdata/standard_libraries/xml/elements_should_be_equal.txt Sat Jun 30 09:15:44 2012
@@ -1,8 +1,8 @@
 *** Settings ***
-Resource     resource.txt
-Test Template     Elements are different
-
-*** Test cases ***
+Resource          resource.txt
+Test Template     Elements should not be equal
+
+*** Test Cases ***
 Elements should be equal
     [Template]    Element should be equal to itself
     <tag/>
@@ -33,6 +33,13 @@
<root><tag><child/></tag></root> <root><different></different>tail</root>
     ...    Different tag name: tag != different

+Normalize whitespace
+    [Template]    NONE
+ Elements should be equal <p>\n\tThis \ \ \ text\n<i>spaces \ has</i> also \ in\ttail!\n</p>
+    ...   <p>This text <i>spaces has</i> also in tail!</p>    normalize
+ Elements should not be equal <tag>\ntext \ here\n</tag> <tag>\twrong \ here\t</tag>
+    ...   Different text: text here != wrong here    normalize
+
 *** Keywords ***
 Element should be equal to itself
     [Arguments]    ${source}
@@ -40,7 +47,7 @@
     Elements Should Be Equal    ${source}    ${xml}
     Elements Should Be Equal    ${xml}    ${source}

-Elements are different
-    [Arguments]   ${source}    ${expected}    ${error}
+Elements should not be equal
+ [Arguments] ${source} ${expected} ${error} ${normalize}=${FALSE}
     Run Keyword and Expect Error    ${error}
-    ...    Elements Should Be Equal   ${source}    ${expected}
+ ... Elements Should Be Equal ${source} ${expected} ${normalize}
=======================================
--- /atest/testdata/standard_libraries/xml/elements_should_match.txt Sat Jun 30 08:36:27 2012 +++ /atest/testdata/standard_libraries/xml/elements_should_match.txt Sat Jun 30 09:15:44 2012
@@ -1,6 +1,6 @@
 *** Settings ***
-Resource     resource.txt
-Test Template     Elements do not match
+Resource          resource.txt
+Test Template     Elements should not match

 *** Test cases ***
 Elements should match
@@ -29,6 +29,13 @@
     <root><tag>content</tag></root>    <root><tag>invalid*</tag></root>
     ...    Different text: 'content' does not match 'invalid*'

+Normalize whitespace
+    [Template]    NONE
+ Elements should match <p>\n\tThis \ \ \ text\n<i>spaces \ has</i> also \ in\ttail!\n</p>
+    ...   <p>This *<i>spaces ???</i>*!</p>    normalize_whitespace=yes
+ Elements should not match <tag>\ntext\n</tag> <tag>\t*wrong*\t</tag>
+    ...   Different text: 'text' does not match '*wrong*'    normalize
+
 *** Keywords ***
 Match Elements
     [Arguments]    ${source}    ${match}
@@ -36,7 +43,7 @@
     ${source}=    Parse XML    ${source}
     Elements Should Match    ${source}    ${match}

-Elements do not match
-    [Arguments]   ${source}    ${expected}    ${error}
+Elements should not match
+ [Arguments] ${source} ${expected} ${error} ${normalize}=${FALSE}
     Run Keyword and Expect Error    ${error}
-    ...    Elements Should Match   ${source}    ${expected}
+    ...    Elements Should Match   ${source}    ${expected}    ${normalize}
=======================================
--- /src/robot/libraries/XML.py Sat Jun 30 08:36:27 2012
+++ /src/robot/libraries/XML.py Sat Jun 30 09:15:44 2012
@@ -61,7 +61,7 @@
         element = self.get_element(source, xpath)
         text = ''.join(self._yield_texts(element))
         if normalize_whitespace:
-            text = self._normalize_whitespace(text).strip()
+            text = self._normalize_whitespace(text)
         return text

     def _yield_texts(self, element, top=True):
@@ -108,13 +108,15 @@
             raise AssertionError("Attribute '%s' does not exist." % name)
         should_match(attr, pattern, message, values=False)

-    def elements_should_be_equal(self, source, expected,
-                                 normalize_whitespace=False):
- comparator = ElementComparator(should_be_equal, normalize_whitespace) - comparator.compare(self.get_element(source), self.get_element(expected)) + def elements_should_be_equal(self, source, expected, normalize_whitespace=False): + self._compare_elements(source, expected, should_be_equal, normalize_whitespace)

def elements_should_match(self, source, expected, normalize_whitespace=False):
-        comparator = ElementComparator(should_match, normalize_whitespace)
+ self._compare_elements(source, expected, should_match, normalize_whitespace)
+
+ def _compare_elements(self, source, expected, comparator, normalize_whitespace): + normalizer = self._normalize_whitespace if normalize_whitespace else None
+        comparator = ElementComparator(comparator, normalizer)
comparator.compare(self.get_element(source), self.get_element(expected))

     def element_to_string(self, source):
@@ -129,9 +131,9 @@

 class ElementComparator(object):

-    def __init__(self, comparator, normalize_whitespace):
+    def __init__(self, comparator, normalizer=None):
         self._comparator = comparator
-        self._normalize_whitespace = normalize_whitespace
+        self._normalizer = normalizer

     def compare(self, actual, expected):
         self._compare_tags(actual, expected)
@@ -144,9 +146,16 @@
         should_be_equal(actual.tag, expected.tag, 'Different tag name')

     def _compare_texts(self, actual, expected):
-        self._comparator(actual.text or '', expected.text or '',
+ self._comparator(self._text(actual.text), self._text(expected.text),
                          'Different text')

+    def _text(self, text):
+        if not text:
+            return ''
+        if not self._normalizer:
+            return text
+        return self._normalizer(text)
+
     def _compare_attributes(self, actual, expected):
         should_be_equal(sorted(actual.attrib), sorted(expected.attrib),
                         'Different attribute names')
@@ -155,7 +164,7 @@
                              "Different value for attribute '%s'" % key)

     def _compare_tails(self, actual, expected):
-        self._comparator(actual.tail or '', expected.tail or '',
+ self._comparator(self._text(actual.tail), self._text(expected.tail),
                          'Different tail text')

     def _compare_children(self, actual, expected):

Reply via email to