Revision: 8e88ebe1e31e
Author:   Mikko Korpela <[email protected]>
Date:     Fri Jun  3 01:44:04 2011
Log:      new Reporter for report and log generation
http://code.google.com/p/robotframework/source/detail?r=8e88ebe1e31e

Added:
 /utest/resources/golden_suite/output2.xml
 /utest/serializing/test_reporting.py
Modified:
 /src/robot/__init__.py
 /src/robot/serializing/jsparser.py
 /src/robot/serializing/testoutput.py
 /utest/resources/__init__.py
 /utest/serializing/test_parser.py

=======================================
--- /dev/null
+++ /utest/resources/golden_suite/output2.xml   Fri Jun  3 01:44:04 2011
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<robot generated="20110603 11:15:42.552" generator="Robot trunk 20110527 (Python 2.6.5 on linux2)"> +<suite source="/home/mkorpela/workspace/robot/utest/resources/golden_suite" name="Golden Suite">
+<doc>root docs
+with new line, several spaces " " and a &lt;b&gt;bold tag&lt;/b&gt;.</doc>
+<metadata>
+<item name="root">rocks</item>
+</metadata>
+<kw type="setup" name="BuiltIn.Log" timeout="">
+<doc>Logs the given message with the given level.</doc>
+<arguments>
+<arg>Rock on</arg>
+</arguments>
+<msg timestamp="20110603 11:15:42.603" level="INFO">Rock on</msg>
+<status status="PASS" endtime="20110603 11:15:42.603" starttime="20110603 11:15:42.603"></status>
+</kw>
+<suite source="/home/mkorpela/workspace/robot/utest/resources/golden_suite/all_settings.txt" name="All Settings">
+<doc>Suite docs
+with new line, several spaces " " and a &lt;b&gt;bold tag&lt;/b&gt;.</doc>
+<metadata>
+<item name="meta">rulez with &lt;b&gt;escaped&lt;/b&gt;</item>
+<item name="version">alpha</item>
+</metadata>
+<kw type="setup" name="BuiltIn.Log" timeout="">
+<doc>Logs the given message with the given level.</doc>
+<arguments>
+<arg>suite msg</arg>
+</arguments>
+<msg timestamp="20110603 11:15:42.606" level="INFO">suite msg</msg>
+<status status="PASS" endtime="20110603 11:15:42.606" starttime="20110603 11:15:42.605"></status>
+</kw>
+<test name="My test" timeout="1 minute">
+<doc>Test docs
+with new line, several spaces " " and a &lt;b&gt;bold tag&lt;/b&gt;.</doc>
+<kw type="setup" name="BuiltIn.Log" timeout="">
+<doc>Logs the given message with the given level.</doc>
+<arguments>
+<arg>Test setup msg</arg>
+</arguments>
+<msg timestamp="20110603 11:15:42.607" level="INFO">Test setup msg</msg>
+<status status="PASS" endtime="20110603 11:15:42.608" starttime="20110603 11:15:42.607"></status>
+</kw>
+<kw type="kw" name="My kw" timeout="">
+<doc>Kw docs</doc>
+<arguments>
+<arg>This is my _non html_ message\nwith new line, several spaces " \ \ \ " and a &lt;b&gt;bold tag&lt;/b&gt;.</arg>
+</arguments>
+<kw type="kw" name="BuiltIn.Log" timeout="">
+<doc>Logs the given message with the given level.</doc>
+<arguments>
+<arg>${arg}</arg>
+<arg>${level}</arg>
+</arguments>
+<msg timestamp="20110603 11:15:42.614" level="WARN">This is my _non html_ message +with new line, several spaces " " and a &lt;b&gt;bold tag&lt;/b&gt;.</msg> +<status status="PASS" endtime="20110603 11:15:42.616" starttime="20110603 11:15:42.612"></status>
+</kw>
+<kw type="teardown" name="BuiltIn.Log" timeout="">
+<doc>Logs the given message with the given level.</doc>
+<arguments>
+<arg>keyword teardown</arg>
+</arguments>
+<msg timestamp="20110603 11:15:42.617" level="INFO">keyword teardown</msg>
+<status status="PASS" endtime="20110603 11:15:42.618" starttime="20110603 11:15:42.616"></status>
+</kw>
+<status status="PASS" endtime="20110603 11:15:42.618" starttime="20110603 11:15:42.609"></status>
+</kw>
+<kw type="kw" name="My kw" timeout="">
+<doc>Kw docs</doc>
+<arguments>
+<arg>This is my &lt;blink&gt;HTML&lt;/blink&gt; message\nwith new line, several spaces " \ \ \ " and a &lt;b&gt;bold tag&lt;/b&gt;.</arg>
+<arg>HTML</arg>
+</arguments>
+<kw type="kw" name="BuiltIn.Log" timeout="">
+<doc>Logs the given message with the given level.</doc>
+<arguments>
+<arg>${arg}</arg>
+<arg>${level}</arg>
+</arguments>
+<msg timestamp="20110603 11:15:42.621" html="yes" level="INFO">This is my &lt;blink&gt;HTML&lt;/blink&gt; message +with new line, several spaces " " and a &lt;b&gt;bold tag&lt;/b&gt;.</msg> +<status status="PASS" endtime="20110603 11:15:42.622" starttime="20110603 11:15:42.620"></status>
+</kw>
+<kw type="teardown" name="BuiltIn.Log" timeout="">
+<doc>Logs the given message with the given level.</doc>
+<arguments>
+<arg>keyword teardown</arg>
+</arguments>
+<msg timestamp="20110603 11:15:42.623" level="INFO">keyword teardown</msg>
+<status status="PASS" endtime="20110603 11:15:42.623" starttime="20110603 11:15:42.622"></status>
+</kw>
+<status status="PASS" endtime="20110603 11:15:42.624" starttime="20110603 11:15:42.619"></status>
+</kw>
+<kw type="teardown" name="BuiltIn.Log" timeout="">
+<doc>Logs the given message with the given level.</doc>
+<arguments>
+<arg>Test teardown msg</arg>
+</arguments>
+<msg timestamp="20110603 11:15:42.624" level="INFO">Test teardown msg</msg>
+<status status="PASS" endtime="20110603 11:15:42.624" starttime="20110603 11:15:42.624"></status>
+</kw>
+<tags>
+<tag>someothertag</tag>
+<tag>sometag</tag>
+</tags>
+<status status="PASS" endtime="20110603 11:15:42.625" critical="yes" starttime="20110603 11:15:42.606"></status>
+</test>
+<kw type="teardown" name="BuiltIn.Log" timeout="">
+<doc>Logs the given message with the given level.</doc>
+<arguments>
+<arg>suite teardown msg</arg>
+</arguments>
+<msg timestamp="20110603 11:15:42.625" level="INFO">suite teardown msg</msg> +<status status="PASS" endtime="20110603 11:15:42.626" starttime="20110603 11:15:42.625"></status>
+</kw>
+<status status="PASS" endtime="20110603 11:15:42.626" starttime="20110603 11:15:42.603"></status>
+</suite>
+<suite source="/home/mkorpela/workspace/robot/utest/resources/golden_suite/failing_suite.txt" name="Failing Suite">
+<doc></doc>
+<metadata>
+</metadata>
+<test name="This fails at test" timeout="">
+<doc></doc>
+<kw type="kw" name="BuiltIn.Fail" timeout="">
+<doc>Fails the test immediately with the given (optional) message.</doc>
+<arguments>
+<arg>Failure msg</arg>
+</arguments>
+<msg timestamp="20110603 11:15:42.633" level="FAIL">Failure msg</msg>
+<status status="FAIL" endtime="20110603 11:15:42.634" starttime="20110603 11:15:42.631"></status>
+</kw>
+<tags>
+</tags>
+<status status="FAIL" endtime="20110603 11:15:42.634" critical="yes" starttime="20110603 11:15:42.630">Failure msg</status>
+</test>
+<test name="This fails at kw" timeout="">
+<doc></doc>
+<kw type="kw" name="Lets fail at keyword" timeout="">
+<doc></doc>
+<arguments>
+</arguments>
+<kw type="kw" name="BuiltIn.Fail" timeout="">
+<doc>Fails the test immediately with the given (optional) message.</doc>
+<arguments>
+<arg>Failure msg</arg>
+</arguments>
+<msg timestamp="20110603 11:15:42.636" level="FAIL">Failure msg</msg>
+<status status="FAIL" endtime="20110603 11:15:42.636" starttime="20110603 11:15:42.635"></status>
+</kw>
+<status status="FAIL" endtime="20110603 11:15:42.636" starttime="20110603 11:15:42.635"></status>
+</kw>
+<tags>
+</tags>
+<status status="FAIL" endtime="20110603 11:15:42.636" critical="yes" starttime="20110603 11:15:42.634">Failure msg</status>
+</test>
+<test name="This Errors" timeout="">
+<doc></doc>
+<kw type="kw" name="This does not exist" timeout="">
+<doc></doc>
+<arguments>
+</arguments>
+<msg timestamp="20110603 11:15:42.638" level="FAIL">No keyword with name 'This does not exist' found.</msg> +<status status="FAIL" endtime="20110603 11:15:42.638" starttime="20110603 11:15:42.637"></status>
+</kw>
+<tags>
+</tags>
+<status status="FAIL" endtime="20110603 11:15:42.638" critical="yes" starttime="20110603 11:15:42.637">No keyword with name 'This does not exist' found.</status>
+</test>
+<status status="FAIL" endtime="20110603 11:15:42.639" starttime="20110603 11:15:42.626"></status>
+</suite>
+<kw type="teardown" name="BuiltIn.Fail" timeout="">
+<doc>Fails the test immediately with the given (optional) message.</doc>
+<arguments>
+</arguments>
+<msg timestamp="20110603 11:15:42.640" level="FAIL">AssertionError</msg>
+<status status="FAIL" endtime="20110603 11:15:42.640" starttime="20110603 11:15:42.639"></status>
+</kw>
+<status status="FAIL" endtime="20110603 11:15:42.640" starttime="20110603 11:15:42.564">Suite teardown failed:
+AssertionError</status>
+</suite>
+<statistics>
+<total>
+<stat fail="4" doc="" pass="0">Critical Tests</stat>
+<stat fail="4" doc="" pass="0">All Tests</stat>
+</total>
+<tag>
+<stat info="" fail="1" pass="0" links="" doc="">someothertag</stat>
+<stat info="" fail="1" pass="0" links="" doc="">sometag</stat>
+</tag>
+<suite>
+<stat fail="4" doc="Golden Suite" pass="0">Golden Suite</stat>
+<stat fail="1" doc="Golden Suite.All Settings" pass="0">Golden Suite.All Settings</stat> +<stat fail="3" doc="Golden Suite.Failing Suite" pass="0">Golden Suite.Failing Suite</stat>
+</suite>
+</statistics>
+<errors>
+<msg linkable="yes" timestamp="20110603 11:15:42.614" level="WARN">This is my _non html_ message +with new line, several spaces " " and a &lt;b&gt;bold tag&lt;/b&gt;.</msg> +<msg timestamp="20110603 11:15:42.629" level="ERROR">Invalid syntax in file '/home/mkorpela/workspace/robot/utest/resources/golden_suite/failing_suite.txt' in table 'Settings': Importing test library 'Idontexist' failed: ImportError: No module named Idontexist +PYTHONPATH: ['/usr/local/lib/python2.6/dist-packages/robot/libraries', '/usr/local/lib/python2.6/dist-packages/docutils-0.7-py2.6.egg', '/usr/local/lib/python2.6/dist-packages/decorator-3.2.0-py2.6.egg', '/usr/local/lib/python2.6/dist-packages/robotframework_seleniumlibrary-2.4-py2.6.egg', '/usr/local/lib/python2.6/dist-packages/mock-0.7.0b4-py2.6.egg', '/usr/local/lib/python2.6/dist-packages/nose-0.11.3-py2.6.egg', '/usr/local/lib/python2.6/dist-packages/guppy-0.1.5-py2.6-linux-x86_64.egg', '/usr/local/lib/python2.6/dist-packages/Pygments-1.4-py2.6.egg', '/usr/lib/python2.6', '/usr/lib/python2.6/plat-linux2', '/usr/lib/python2.6/lib-tk', '/usr/lib/python2.6/lib-old', '/usr/lib/python2.6/lib-dynload', '/usr/lib/python2.6/dist-packages', '/usr/lib/python2.6/dist-packages/PIL', '/usr/lib/python2.6/dist-packages/gst-0.10', '/usr/lib/pymodules/python2.6', '/usr/lib/python2.6/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.6/gtk-2.0', '/usr/lib/python2.6/dist-packages/wx-2.8-gtk2-unicode', '/usr/local/lib/python2.6/dist-packages', '.']
+Traceback (most recent call last):
+ File "/usr/local/lib/python2.6/dist-packages/robot/utils/importing.py", line 85, in _non_dotted_import
+    module = __import__(name)</msg>
+<msg timestamp="20110603 11:15:42.629" level="ERROR">Invalid syntax in file '/home/mkorpela/workspace/robot/utest/resources/golden_suite/failing_suite.txt' in table 'Settings': Resource file 'And I'm not here' does not exist.</msg>
+</errors>
+</robot>
=======================================
--- /dev/null
+++ /utest/serializing/test_reporting.py        Fri Jun  3 01:44:04 2011
@@ -0,0 +1,126 @@
+import unittest
+from robot.output.readers import ExecutionErrors
+import resources
+from robot.common.model import BaseTestSuite
+from robot.serializing.testoutput import Reporter
+import robot.serializing.testoutput
+
+def set_serialize_log_mock():
+    results = {'log_path':None}
+    def serialize_log(test_output_datamodel, log_path, title=None):
+        results['log_path'] = log_path
+        results['title'] = title
+    robot.serializing.testoutput.serialize_log = serialize_log
+    return results
+
+def set_serialize_report_mock():
+    results = {'report_path':None}
+ def serialize_report(test_output_datamodel, report_path, title=None, background=None, logpath=None):
+        results['report_path'] = report_path
+        results['title'] = title
+        results['background'] = background
+        results['logpath'] = logpath
+    robot.serializing.testoutput.serialize_report = serialize_report
+    return results
+
+def set_process_outputs_mock():
+    results = {'paths':None}
+    def process_outputs(paths, settings):
+        results['paths'] = paths
+        results['settings'] = settings
+        suite = BaseTestSuite('Suite')
+        suite.starttime = 7
+        suite.endtime = 42
+        return suite, ExecutionErrors(None)
+    robot.serializing.testoutput.process_outputs = process_outputs
+    return results
+
+class TestReporting(unittest.TestCase):
+
+    def setUp(self):
+        self._reporter = Reporter()
+        self._settings = {
+            'Report': 'NONE',
+            'Log': 'NONE',
+            'LogTitle': None,
+            'ReportTitle': None,
+            'ReportBackground': None,
+            'SuiteStatLevel': None,
+            'TagStatInclude': None,
+            'TagStatExclude': None,
+            'TagStatCombine': None,
+            'TagDoc': None,
+            'TagStatLink': None,
+            'SetTag': None,
+            'SuiteNames': None,
+            'TestNames': None,
+            'Include': None,
+            'Exclude': None,
+            'StartTime': 0,
+            'Name': None,
+            'Doc': None,
+            'Metadata': {},
+            'Critical': None,
+            'NonCritical': None,
+            'NoStatusRC': None,
+            'EndTime': 0,
+            'LogLevel': 'INFO'
+        }
+        self._log_results = set_serialize_log_mock()
+        self._report_results = set_serialize_report_mock()
+        #self._process_outputs_results = set_process_outputs_mock()
+
+    def test_generate_report_and_log(self):
+        self._settings['Log'] = 'log.html'
+        self._settings['Report'] = 'report.html'
+        self._reporter.execute(self._settings, resources.GOLDEN_OUTPUT)
+        self._assert_expected_log('log.html')
+        self._assert_expected_report('report.html')
+        self._assert_log_link_in_report('log.html')
+
+    def test_no_generation(self):
+        self._reporter.execute(self._settings, resources.GOLDEN_OUTPUT)
+        self._assert_no_log()
+        self._assert_no_report()
+
+    def test_only_log(self):
+        self._settings['Log'] = 'only-log.html'
+        self._reporter.execute(self._settings, resources.GOLDEN_OUTPUT)
+        self._assert_expected_log('only-log.html')
+        self._assert_no_report()
+
+    def test_only_report(self):
+        self._settings['Report'] = 'reports-only.html'
+        self._reporter.execute(self._settings, resources.GOLDEN_OUTPUT)
+        self._assert_no_log()
+        self._assert_expected_report('reports-only.html')
+        self._assert_no_log_links_in_report()
+
+    def test_multiple_outputs(self):
+        self._settings['Log'] = 'log.html'
+        self._settings['Report'] = 'report.html'
+ self._reporter.execute(self._settings, *[resources.GOLDEN_OUTPUT, resources.GOLDEN_OUTPUT2])
+        self._assert_expected_log('log.html')
+        self._assert_expected_report('report.html')
+
+    def _assert_expected_log(self, expected_file_name):
+ self.assertEquals(self._log_results['log_path'], expected_file_name)
+
+    def _assert_expected_report(self, expected_file_name):
+ self.assertEquals(self._report_results['report_path'], expected_file_name)
+
+    def _assert_log_link_in_report(self, expected_log_link):
+ self.assertEquals(self._report_results['logpath'], expected_log_link)
+
+    def _assert_no_log_links_in_report(self):
+        self._assert_log_link_in_report(None)
+
+    def _assert_no_log(self):
+        self._assert_expected_log(None)
+
+    def _assert_no_report(self):
+        self._assert_expected_report(None)
+
+
+if __name__ == '__main__':
+    unittest.main()
=======================================
--- /src/robot/__init__.py      Mon May 30 04:58:01 2011
+++ /src/robot/__init__.py      Fri Jun  3 01:44:04 2011
@@ -14,6 +14,7 @@

 import sys
 import os
+from robot.serializing.testoutput import Reporter

 if __name__ == '__main__':
     sys.stderr.write("Use 'runner' or 'rebot' for executing.\n")
@@ -128,13 +129,10 @@
     output.close(suite)
     output_src = settings['Output']
     if settings.is_rebot_needed():
- datasources, settings = settings.get_rebot_datasources_and_settings()
+        _, settings = settings.get_rebot_datasources_and_settings()
         if settings['SplitOutputs'] > 0:
             raise Exception('Splitting? No way!')
- testoutput = SplitIndexTestOutput(suite, datasources[0], settings)
-        else:
-            testoutput = RebotTestOutput(datasources, settings)
-        testoutput.serialize(settings, output=output_src)
+        Reporter().execute(settings, output_src)
     LOGGER.close()
     return suite

@@ -157,8 +155,7 @@
     settings = RebotSettings(options)
     LOGGER.register_console_logger(colors=settings['MonitorColors'])
     LOGGER.disable_message_cache()
-    testoutput = RebotTestOutput(datasources, settings)
-    testoutput.serialize(settings, generator='Rebot')
+    Reporter().execute(settings, *datasources)
     LOGGER.close()
     return testoutput.suite

=======================================
--- /src/robot/serializing/jsparser.py  Thu Jun  2 21:39:47 2011
+++ /src/robot/serializing/jsparser.py  Fri Jun  3 01:44:04 2011
@@ -38,6 +38,8 @@
         return self._texts.dump()

     def timestamp(self, time):
+        if time == 'N/A':
+            return -1
         dt = datetime.strptime(time+"000", "%Y%m%d %H:%M:%S.%f")
         millis = int(mktime(dt.timetuple())*1000+dt.microsecond/1000)
         if self.basemillis is None:
@@ -360,7 +362,7 @@
     def __init__(self, context, attrs):
         _Handler.__init__(self, context, attrs)
         self._name = attrs.getValue('name')
-        self._source = attrs.getValue('source')
+        self._source = attrs.get('source') or ''
         self.context.start_suite(self._name)
         self.context.collect_stats()

=======================================
--- /src/robot/serializing/testoutput.py        Mon May 30 04:58:01 2011
+++ /src/robot/serializing/testoutput.py        Fri Jun  3 01:44:04 2011
@@ -32,6 +32,28 @@
 from robot.serializing import jsparser


+class Reporter(object):
+
+    def execute(self, settings, *data_sources):
+        if len(data_sources) > 1:
+            suite, exec_errors = process_outputs(data_sources, settings)
+            suite.set_options(settings)
+ RobotTestOutput(suite, exec_errors, settings).serialize_output('foo.xml')
+            data_sources = ['foo.xml']
+        data_model = jsparser.create_datamodel_from(data_sources[0])
+        report_path = self._parse_file(settings['Report'])
+        log_path = self._parse_file(settings['Log'])
+        if report_path:
+ serialize_report(data_model, report_path, settings['ReportTitle'], settings['ReportBackground'], log_path)
+            LOGGER.output_file('Report', report_path)
+        if log_path:
+            serialize_log(data_model, log_path, settings['LogTitle'])
+            LOGGER.output_file('Log', log_path)
+
+    def _parse_file(self, string):
+        return string if string != 'NONE' else None
+
+
 class RobotTestOutput:

     def __init__(self, suite, exec_errors, settings=None):
=======================================
--- /utest/resources/__init__.py        Tue May 31 06:04:41 2011
+++ /utest/resources/__init__.py        Fri Jun  3 01:44:04 2011
@@ -2,5 +2,6 @@

 THIS_PATH = os.path.dirname(__file__)
 GOLDEN_OUTPUT = os.path.join(THIS_PATH, 'golden_suite', 'output.xml')
+GOLDEN_OUTPUT2 = os.path.join(THIS_PATH, 'golden_suite', 'output2.xml')
 GOLDEN_JS = os.path.join(THIS_PATH, 'golden_suite', 'expected.js')

=======================================
--- /utest/serializing/test_parser.py   Sun May 29 23:54:31 2011
+++ /utest/serializing/test_parser.py   Fri Jun  3 01:44:04 2011
@@ -1,5 +1,13 @@
 import robot.serializing.jsparser as jsparser

+def test_timestamp():
+    context = jsparser.Context()
+    time = context.timestamp('20110603 12:00:00.000')
+    assert time == 0
+    time = context.timestamp('N/A')
+    assert time == -1
+    time = context.timestamp('20110603 12:00:01.000')
+    assert time == 1000

 def test_stats_when_failing_suite_teardown():
     context = jsparser.Context()

Reply via email to