5 new revisions:
Revision: fe4fba0ecf8a
Author: Pekka Klärck
Date: Mon Jun 20 04:33:57 2011
Log: fixed status class in test details, use 'Pattern' label instead
of 'Co...
http://code.google.com/p/robotframework/source/detail?r=fe4fba0ecf8a
Revision: 9c21a134ba8a
Author: Pekka Klärck
Date: Mon Jun 20 06:51:01 2011
Log: atest resources: support for getting tag stat nodes as DomWrapper
obje...
http://code.google.com/p/robotframework/source/detail?r=9c21a134ba8a
Revision: ef3495733489
Author: Pekka Klärck
Date: Mon Jun 20 06:55:41 2011
Log: Tests for setting doc and links for combined tags....
http://code.google.com/p/robotframework/source/detail?r=ef3495733489
Revision: 84ac88ac2c31
Author: Pekka Klärck
Date: Mon Jun 20 07:28:03 2011
Log: cleanup: split metadata, tagsdoc, and tagstatcombine into parts
alread...
http://code.google.com/p/robotframework/source/detail?r=84ac88ac2c31
Revision: 855470439a8c
Author: Pekka Klärck
Date: Mon Jun 20 07:37:15 2011
Log: Cleanup and several enhancements to tag statistics:...
http://code.google.com/p/robotframework/source/detail?r=855470439a8c
==============================================================================
Revision: fe4fba0ecf8a
Author: Pekka Klärck
Date: Mon Jun 20 04:33:57 2011
Log: fixed status class in test details, use 'Pattern' label instead
of 'Combined' when showing the combined pattern
http://code.google.com/p/robotframework/source/detail?r=fe4fba0ecf8a
Modified:
/src/robot/webcontent/report.html
=======================================
--- /src/robot/webcontent/report.html Mon Jun 20 02:56:16 2011
+++ /src/robot/webcontent/report.html Mon Jun 20 04:33:57 2011
@@ -390,7 +390,7 @@
{{/if}}
{{if combined}}
<tr>
- <th>Combined:</th>
+ <th>Pattern:</th>
<td>${combined}</td>
</tr>
{{/if}}
@@ -470,7 +470,7 @@
<td class="col_doc">{{html documentation}}</td>
<td class="col_tags">${tags.join(', ')}</td>
<td class="col_crit">{{if isCritical}}yes{{else}}no{{/if}}</td>
- <td class="col_status ${status}">${status.toUpperCase()}</td>
+ <td class="col_status ${status.toLowerCase()}">${status}</td>
<td class="col_msg">${message}</td>
<td class="col_times">${times.startTime(true)}<br
/>${times.elapsedTime(true)}</td>
</tr>
==============================================================================
Revision: 9c21a134ba8a
Author: Pekka Klärck
Date: Mon Jun 20 06:51:01 2011
Log: atest resources: support for getting tag stat nodes as DomWrapper
objects directly from output xml - this allows testing tag stats from
outputs easily
http://code.google.com/p/robotframework/source/detail?r=9c21a134ba8a
Modified:
/atest/resources/TestCheckerLibrary.py
/atest/resources/atest_resource.txt
=======================================
--- /atest/resources/TestCheckerLibrary.py Mon May 3 09:13:44 2010
+++ /atest/resources/TestCheckerLibrary.py Mon Jun 20 06:51:01 2011
@@ -8,7 +8,7 @@
class TestCheckerLibrary:
-
+
def process_output(self, path):
path = path.replace('/', os.sep)
try:
@@ -31,9 +31,9 @@
else:
err = "More than one test '%s' found from suite '%s'"
raise RuntimeError(err % (name, suite.name))
-
+
def get_tests_from_suite(self, suite, name=None):
- tests = [ test for test in suite.tests
+ tests = [ test for test in suite.tests
if name is None or utils.eq(test.name, name) ]
for subsuite in suite.suites:
tests.extend(self.get_tests_from_suite(subsuite, name))
@@ -57,14 +57,14 @@
def check_test_status(self, test, status=None, message=None):
"""Verifies that test's status and message are as expected.
-
- Expected status and message can be given as parameters. If expected
- status is not given, expected status and message are read from
test's
- documentation. If documentation doesn't contain any of PASS, FAIL
or
- ERROR, test's status is expected to be PASS. If status is given
that is
- used. Expected message is documentation after given status.
Expected
- message can also be regular expression. In that case expected match
- starts with REGEXP: , which is ignored in the regexp match.
+
+ Expected status and message can be given as parameters. If expected
+ status is not given, expected status and message are read from
test's
+ documentation. If documentation doesn't contain any of PASS, FAIL
or
+ ERROR, test's status is expected to be PASS. If status is given
that is
+ used. Expected message is documentation after given status.
Expected
+ message can also be regular expression. In that case expected match
+ starts with REGEXP: , which is ignored in the regexp match.
"""
if status is not None:
test.exp_status = status
@@ -101,7 +101,7 @@
def check_suite_contains_tests(self, suite, *expected_names):
actual_tests = [ test for test in self.get_tests_from_suite(suite)
]
tests_msg = """
-Expected tests : %s
+Expected tests : %s
Actual tests : %s""" % (str(list(expected_names)), str(actual_tests))
expected_names = [ utils.normalize(name) for name in
expected_names ]
if len(actual_tests) != len(expected_names):
@@ -116,14 +116,13 @@
% (test.name, tests_msg))
if len(expected_names) != 0:
raise Exception("Bug in test library")
-
-
- def get_node(self, path, node=None):
- dom = utils.DomWrapper(path)
- if node is None:
- return dom
- return dom.get_node(node)
-
+
+ def get_node(self, file_path, node_path=None):
+ dom = utils.DomWrapper(file_path)
+ return dom.get_node(node_path) if node_path else dom
+
+ def get_nodes(self, file_path, node_path):
+ return utils.DomWrapper(file_path).get_nodes(node_path)
def process_suite(suite):
@@ -142,14 +141,14 @@
test.exp_message = test.doc.split('FAIL', 1)[1].lstrip()
else:
test.exp_status = 'PASS'
- test.exp_message = ''
+ test.exp_message = ''
test.kws = test.keywords
test.keyword_count = test.kw_count = len(test.keywords)
for kw in test.keywords:
process_keyword(kw)
process_keyword(test.setup)
process_keyword(test.teardown)
-
+
def process_keyword(kw):
if kw is None:
return
=======================================
--- /atest/resources/atest_resource.txt Tue May 24 05:36:52 2011
+++ /atest/resources/atest_resource.txt Mon Jun 20 06:51:01 2011
@@ -272,3 +272,7 @@
Should be equal ${PREV TEST NAME} ${name}
Should be equal ${PREV TEST STATUS} PASS
+Get Tag Stat Nodes
+ ${nodes} = Get Nodes ${OUTFILE} statistics/tag/stat
+ [Return] ${nodes}
+
==============================================================================
Revision: ef3495733489
Author: Pekka Klärck
Date: Mon Jun 20 06:55:41 2011
Log: Tests for setting doc and links for combined tags.
Update issue 883
Status: Started
Here come the tests for this bug.
http://code.google.com/p/robotframework/source/detail?r=ef3495733489
Modified:
/atest/robot/tags/tag_doc.txt
/atest/robot/tags/tag_stat_link.txt
=======================================
--- /atest/robot/tags/tag_doc.txt Mon Jun 13 23:52:45 2011
+++ /atest/robot/tags/tag_doc.txt Mon Jun 20 06:55:41 2011
@@ -62,11 +62,14 @@
Format In Report Details Should Be Correct d2 Doc for many tags More
doc
Tag Doc With Multiple Matches In Output Statistics
- Format In Output Statistics Should Be Correct d2 Doc for many tags
More doc
+ Format In Output Statistics Should Be Correct d2 Doc for many tags
& More doc
+
+Tag Doc For Combined Statistics
+ Format In Output Statistics Should Be Correct DX Doc for many tags
*** Keywords ***
Run Tests And Read Outputs
- Run Tests --log log.html --report report.html --tagdoc
f1:Some_documentation --tagdoc t1:http://some.url_*bold* --tagdoc
d?:Doc_for_many_tags --tagdoc D2:More_doc misc/normal.html
+ Run Tests --log log.html --report report.html --tagdoc
f1:Some_documentation --tagdoc t1:http://some.url_*bold* --tagdoc
d?:Doc_for_many_tags --tagdoc D2:More_doc --tagstatcombine d*:DX
misc/normal.html
${LOG} = Get File ${OUT_DIR}${/}log.html
${REPORT} = Get File ${OUT_DIR}${/}report.html
${OUTPUT} = Get File ${OUT_FILE}
=======================================
--- /atest/robot/tags/tag_stat_link.txt Mon Jun 13 23:52:45 2011
+++ /atest/robot/tags/tag_stat_link.txt Mon Jun 20 06:55:41 2011
@@ -40,6 +40,11 @@
\ ${expected} = Set ${expected}\n${exp_link}
${expected} = Set ${expected}\n</div>\n</td>
Contains ${report} ${expected}
+
+Link For Combined Tag
+ Run Tests --tagstatcombine d*:DX --tagstatlink d?:http://%1:Title_%1
misc/suites
+ ${stats} = Get Tag Stat Nodes
+ Should Be Equal ${stats[0].attrs['links']} Title X:http://X
*** Keywords ***
Report Should Contain Taglink
==============================================================================
Revision: 84ac88ac2c31
Author: Pekka Klärck
Date: Mon Jun 20 07:28:03 2011
Log: cleanup: split metadata, tagsdoc, and tagstatcombine into parts
already when processing cli options to ease further processing
http://code.google.com/p/robotframework/source/detail?r=84ac88ac2c31
Modified:
/src/robot/common/model.py
/src/robot/conf/settings.py
/src/robot/output/readers.py
=======================================
--- /src/robot/common/model.py Thu Jun 16 07:30:30 2011
+++ /src/robot/common/model.py Mon Jun 20 07:28:03 2011
@@ -99,18 +99,9 @@
self.doc = doc
def set_metadata(self, metalist):
- for metastr in metalist:
- metastr = self._escape_metadata(metastr)
- try:
- name, value = metastr.split(':', 1)
- except ValueError:
- name, value = metastr, ''
+ for name, value in metalist:
self.metadata[name] = value
- def _escape_metadata(self, metastr):
- # Overridden by output.readers.TestSuite
- return metastr.replace('\\', '\\\\')
-
def get_metadata(self, html=False):
names = sorted(self.metadata.keys())
values = [ self.metadata[n] for n in names ]
=======================================
--- /src/robot/conf/settings.py Fri Jun 17 06:59:50 2011
+++ /src/robot/conf/settings.py Mon Jun 20 07:28:03 2011
@@ -75,14 +75,14 @@
self._opts[name] = value
def _process_value(self, name, value, log):
- if value is None:
+ if value in [None, []]:
return value
if name in
['Name', 'Doc', 'LogTitle', 'ReportTitle', 'SummaryTitle']:
return value.replace('_', ' ')
if name in ['Metadata', 'TagDoc']:
- return [v.replace('_', ' ') for v in value]
- if name in ['Include', 'Exclude', 'TagStatCombine']:
- return [item.replace('AND', '&').replace('_', ' ') for item in
value]
+ return [self._process_metadata_or_tagdoc(v) for v in value]
+ if name in ['Include', 'Exclude']:
+ return [v.replace('AND', '&').replace('_', ' ') for v in value]
if name in self._output_opts and utils.eq(value, 'NONE'):
return 'NONE'
if name == 'OutputDir':
@@ -91,8 +91,10 @@
return self._convert_to_positive_integer(name, value)
if name in ['Listeners', 'VariableFiles']:
return [self._split_args_from_name(item) for item in value]
+ if name == 'TagStatCombine':
+ return [self._process_tag_stat_combine(v) for v in value]
if name == 'TagStatLink':
- return self._process_tag_stat_link(value)
+ return [v for v in [self._process_tag_stat_link(v) for v in
value] if v]
if name == 'RemoveKeywords':
return value.upper()
if name == 'SplitOutputs' and value != -1:
@@ -150,16 +152,27 @@
raise DataError("Can't create %s file's parent
directory '%s': %s"
% (type_.lower(), path,
utils.get_error_message()))
+ def _process_metadata_or_tagdoc(self, value):
+ value = value.replace('_', ' ')
+ if ':' in value:
+ return value.split(':', 1)
+ return value, ''
+
+ def _process_tag_stat_combine(self, value):
+ for replwhat, replwith in [('_', ' '), ('AND', ' & '),
+ ('&', ' & '), ('NOT', ' NOT ')]:
+ value = value.replace(replwhat, replwith)
+ if ':' in value:
+ return value.rsplit(':', 1)
+ return value, value
+
def _process_tag_stat_link(self, value):
- ret = []
- for item in value:
- tokens = item.split(':')
- if len(tokens) >= 3:
- ret.append((tokens[0], ':'.join(tokens[1:-1]), tokens[-1]))
- else:
- LOGGER.error("Invalid format for option '--tagstatlink'. "
- "Expected 'tag:link:title' but got '%s'." %
item)
- return ret
+ tokens = value.split(':')
+ if len(tokens) >= 3:
+ return tokens[0], ':'.join(tokens[1:-1]), tokens[-1]
+ LOGGER.error("Invalid format for option '--tagstatlink'. "
+ "Expected 'tag:link:title' but got '%s'." % value)
+ return None
def _convert_to_positive_integer(self, name, value):
value = self._convert_to_integer(name, value)
=======================================
--- /src/robot/output/readers.py Wed Jun 15 04:30:11 2011
+++ /src/robot/output/readers.py Mon Jun 20 07:28:03 2011
@@ -212,9 +212,6 @@
self.set_status()
return ret
- def _escape_metadata(self, metastr):
- return metastr
-
def remove_keywords(self, how):
should_remove = ShouldRemoveCallable(how)
if not should_remove:
==============================================================================
Revision: 855470439a8c
Author: Pekka Klärck
Date: Mon Jun 20 07:37:15 2011
Log: Cleanup and several enhancements to tag statistics:
1) Combined tags can now get doc and links (issue 883)
2) Also tag stat link title can contain groups (issue 884)
3) Combine multiple tag docs with ' & ' instead of ' '
http://code.google.com/p/robotframework/source/detail?r=855470439a8c
Modified:
/src/robot/common/statistics.py
=======================================
--- /src/robot/common/statistics.py Fri Jun 17 06:59:50 2011
+++ /src/robot/common/statistics.py Mon Jun 20 07:37:15 2011
@@ -78,15 +78,15 @@
class TagStat(Stat):
type = 'tag'
- def __init__(self, name, critical=False, non_critical=False, info=None,
- combined=''):
+ def __init__(self, name, doc='', links=[], critical=False,
+ non_critical=False, combined=''):
Stat.__init__(self, name)
- self.doc = info.get_doc(name) if info else ''
+ self.doc = doc
+ self.links = links
self.critical = critical
self.non_critical = non_critical
- self.tests = []
- self.links = info.get_links(name) if info else []
self.combined = combined
+ self.tests = []
def add_test(self, test):
Stat.add_test(self, test)
@@ -155,49 +155,42 @@
class TagStatistics:
- def __init__(self, include=None, exclude=None, tag_stat_combine=None,
- docs=None, links=None):
+ def __init__(self, include=None, exclude=None, combine=None, docs=None,
+ links=None):
self.stats = utils.NormalizedDict()
self._include = include or []
self._exclude = exclude or []
- self._patterns_and_names =
self._get_patterns_and_names(tag_stat_combine)
- self._taginfo = TagStatInfo(docs or [], links or [])
-
- def _get_patterns_and_names(self, tag_stat_combine_options):
- if not tag_stat_combine_options:
- return []
- return [ self._parse_name_and_pattern_from(option) \
- for option in tag_stat_combine_options ]
-
- def _parse_name_and_pattern_from(self, option):
- pattern = option.replace('&', ' & ').replace('NOT', ' NOT ')
- if ':' in pattern:
- return pattern.rsplit(':', 1)
- return pattern, pattern
+ self._combine = combine or []
+ info = TagStatInfo(docs or [], links or [])
+ self._get_doc = info.get_doc
+ self._get_links = info.get_links
def add_test(self, test, critical):
self._add_tags_statistics(test, critical)
- self._add_tagstatcombine_statistics(test)
+ self._add_combined_statistics(test)
def _add_tags_statistics(self, test, critical):
for tag in test.tags:
if not self._is_included(tag):
continue
if tag not in self.stats:
- self.stats[tag] = TagStat(tag, critical.is_critical(tag),
- critical.is_non_critical(tag),
- self._taginfo)
+ self.stats[tag] = TagStat(tag, self._get_doc(tag),
+ self._get_links(tag),
+ critical.is_critical(tag),
+ critical.is_non_critical(tag))
self.stats[tag].add_test(test)
def _is_included(self, tag):
- if self._include != [] and not utils.matches_any(tag,
self._include):
+ if self._include and not utils.matches_any(tag, self._include):
return False
return not utils.matches_any(tag, self._exclude)
- def _add_tagstatcombine_statistics(self, test):
- for pattern, name in self._patterns_and_names:
+ def _add_combined_statistics(self, test):
+ for pattern, name in self._combine:
if name not in self.stats:
- self.stats[name] = TagStat(name, combined=pattern)
+ self.stats[name] = TagStat(name, self._get_doc(name),
+ self._get_links(name),
+ combined=pattern)
if test.is_included([pattern], []):
self.stats[name].add_test(test)
@@ -228,26 +221,24 @@
class TagStatInfo:
def __init__(self, docs, links):
- self._docs = [ self._parse_doc(doc) for doc in docs ]
- self._links = [ TagStatLink(*link) for link in links ]
-
- def _parse_doc(self, cli_item):
- try:
- tag, doc = cli_item.split(':', 1)
- except ValueError:
- tag, doc = cli_item, ''
- return tag, doc
+ self._docs = [TagStatDoc(*doc) for doc in docs]
+ self._links = [TagStatLink(*link) for link in links]
def get_doc(self, tag):
- docs = []
- for pattern, doc in self._docs:
- if utils.matches(tag, pattern):
- docs.append(doc)
- return ' '.join(docs) if docs else ''
+ return ' & '.join(doc.text for doc in self._docs if
doc.matches(tag))
def get_links(self, tag):
- links = [ link.get_link(tag) for link in self._links ]
- return [ link for link in links if link is not None ]
+ return [link.get_link(tag) for link in self._links if
link.matches(tag)]
+
+
+class TagStatDoc:
+
+ def __init__(self, pattern, doc):
+ self.text = doc
+ self._pattern = pattern
+
+ def matches(self, tag):
+ return utils.matches(tag, self._pattern)
class TagStatLink:
@@ -258,18 +249,22 @@
self._link = link
self._title = title.replace('_', ' ')
+ def matches(self, tag):
+ return self._regexp.match(tag) is not None
+
def get_link(self, tag):
match = self._regexp.match(tag)
- if match is not None:
- link = self._replace_matches(self._link, match)
- return link, self._title
- return None
-
- def _replace_matches(self, url, match):
- groups = match.groups()
- for i, group in enumerate(groups):
- url = url.replace('%%%d' % (i+1), group)
- return url
+ if not match:
+ return None
+ link, title = self._replace_groups(self._link, self._title, match)
+ return link, title
+
+ def _replace_groups(self, link, title, match):
+ for index, group in enumerate(match.groups()):
+ placefolder = '%' + str(index+1)
+ link = link.replace(placefolder, group)
+ title = title.replace(placefolder, group)
+ return link, title
def _get_match_regexp(self, pattern):
regexp = []