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):