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 &amp; 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 = []

Reply via email to