[GRASS-SVN] r61334 - grass/trunk/lib/python/gunittest

svn_grass at osgeo.org svn_grass at osgeo.org
Tue Jul 22 13:01:02 PDT 2014


Author: wenzeslaus
Date: 2014-07-22 13:01:02 -0700 (Tue, 22 Jul 2014)
New Revision: 61334

Modified:
   grass/trunk/lib/python/gunittest/invoker.py
   grass/trunk/lib/python/gunittest/reporters.py
Log:
gunittest: introduce possibility of multiple reports for test file invoker (now HTML report and simple text report)

Modified: grass/trunk/lib/python/gunittest/invoker.py
===================================================================
--- grass/trunk/lib/python/gunittest/invoker.py	2014-07-22 19:57:51 UTC (rev 61333)
+++ grass/trunk/lib/python/gunittest/invoker.py	2014-07-22 20:01:02 UTC (rev 61334)
@@ -21,7 +21,9 @@
 TestProgram.USAGE = USAGE_AS_MAIN
 
 from .loader import GrassTestLoader, discover_modules
-from .reporters import GrassTestFilesReporter
+from .reporters import (GrassTestFilesMultiReporter,
+                        GrassTestFilesTextReporter,
+                        GrassTestFilesHtmlReporter)
 from .utils import silent_rmtree, ensure_dir
 
 import grass.script.setup as gsetup
@@ -125,7 +127,11 @@
         if os.path.abspath(results_dir) == os.path.abspath(self.start_dir):
             raise RuntimeError("Results root directory should not be the same"
                                " as discovery start directory")
-        self.reporter = GrassTestFilesReporter(results_dir=results_dir)
+        self.reporter = GrassTestFilesMultiReporter(
+            reporters=[
+                GrassTestFilesTextReporter(stream=sys.stderr),
+                GrassTestFilesHtmlReporter(),
+            ])
 
         # TODO: move constants out of loader class or even module
         modules = discover_modules(start_dir=self.start_dir,
@@ -137,6 +143,7 @@
                                    universal_location_value=GrassTestLoader.universal_tests_value,
                                    import_modules=False)
 
+        self.reporter.start(results_dir)
         for module in modules:
             self._run_test_module(module=module, results_dir=results_dir,
                                   gisdbase=gisdbase, location=location)

Modified: grass/trunk/lib/python/gunittest/reporters.py
===================================================================
--- grass/trunk/lib/python/gunittest/reporters.py	2014-07-22 19:57:51 UTC (rev 61333)
+++ grass/trunk/lib/python/gunittest/reporters.py	2014-07-22 20:01:02 UTC (rev 61334)
@@ -13,7 +13,6 @@
 
 
 import os
-import sys
 import datetime
 import xml.sax.saxutils as saxutils
 import xml.etree.ElementTree as et
@@ -67,7 +66,8 @@
 def get_svn_revision():
     """Get SVN revision number
 
-    :returns: SVN revision number as string or None if it is not possible to get
+    :returns: SVN revision number as string or None if it is
+        not possible to get
     """
     # TODO: here should be starting directory
     # but now we are using current as starting
@@ -131,16 +131,118 @@
     return None
 
 
-class GrassTestFilesReporter(object):
+class GrassTestFilesMultiReporter(object):
 
-    def __init__(self, results_dir):
+    def __init__(self, reporters, forgiving=False):
+        self.reporters = reporters
+        self.forgiving = forgiving
+
+    def start(self, results_dir):
         # TODO: no directory cleaning (self.clean_before)? now cleaned by caller
+        # TODO: perhaps only those whoe need it should do it (even multiple times)
+        # and there is also the delet problem
         ensure_dir(os.path.abspath(results_dir))
+        for reporter in self.reporters:
+            try:
+                reporter.start(results_dir)
+            except AttributeError:
+                if self.forgiving:
+                    pass
+                else:
+                    raise
 
+    def finish(self):
+        for reporter in self.reporters:
+            try:
+                reporter.finish()
+            except AttributeError:
+                if self.forgiving:
+                    pass
+                else:
+                    raise
+
+    def start_file_test(self, module):
+        for reporter in self.reporters:
+            try:
+                reporter.start_file_test(module)
+            except AttributeError:
+                if self.forgiving:
+                    pass
+                else:
+                    raise
+
+    def end_file_test(self, module, cwd, returncode, stdout, stderr):
+        for reporter in self.reporters:
+            try:
+                reporter.end_file_test(module, cwd, returncode, stdout, stderr)
+            except AttributeError:
+                if self.forgiving:
+                    pass
+                else:
+                    raise
+
+
+class GrassTestFilesCountingReporter(object):
+    def __init__(self):
+        self.test_files = None
+        self.files_fail = None
+        self.files_pass = None
+
+        self.file_pass_per = None
+        self.file_fail_per = None
+
+        self.main_start_time = None
+        self.main_end_time = None
+        self.main_time = None
+
+        self.file_start_time = None
+        self.file_end_time = None
+        self.file_time = None
+        self._start_file_test_called = False
+
+    def start(self, results_dir):
+        self.test_files = 0
+        self.files_fail = 0
+        self.files_pass = 0
+
+        # this might be moved to some report start method
+        self.main_start_time = datetime.datetime.now()
+
+    def finish(self):
+        self.main_end_time = datetime.datetime.now()
+        self.main_time = self.main_end_time - self.main_start_time
+
+        assert self.test_files == self.files_fail + self.files_pass
+        self.file_pass_per = 100 * float(self.files_pass) / self.test_files
+        self.file_fail_per = 100 * float(self.files_fail) / self.test_files
+
+    def start_file_test(self, module):
+        self.file_start_time = datetime.datetime.now()
+        self._start_file_test_called = True
+        self.test_files += 1
+
+    def end_file_test(self, module, cwd, returncode, stdout, stderr):
+        assert self._start_file_test_called
+        self.file_end_time = datetime.datetime.now()
+        self.file_time = self.file_end_time - self.file_start_time
+        if returncode:
+            self.files_fail += 1
+        else:
+            self.files_pass += 1
+        self._start_file_test_called = False
+
+
+class GrassTestFilesHtmlReporter(GrassTestFilesCountingReporter):
+
+    def __init__(self):
+        super(GrassTestFilesHtmlReporter, self).__init__()
+        self.main_index = None
+
+    def start(self, results_dir):
+        super(GrassTestFilesHtmlReporter, self).start(results_dir)
         # having all variables public although not really part of API
         self.main_index = open(os.path.join(results_dir, 'index.html'), 'w')
-        # this might be moved to some report start method
-        self.main_start_time = datetime.datetime.now()
+
         svn_info = get_svn_info()
         if not svn_info:
             svn_text = ('<span style="font-size: 60%">'
@@ -165,19 +267,10 @@
                               '</tr></thead><tbody>'.format(
                                   time=self.main_start_time,
                                   svn=svn_text))
-        self.file_start_time = None
-        self._start_file_test_called = False
-        self.test_files = 0
-        self.files_failed = 0
-        self.files_succeeded = 0
 
     def finish(self):
-        main_end_time = datetime.datetime.now()
-        main_time = main_end_time - self.main_start_time
-        assert self.test_files == self.files_failed + self.files_succeeded
+        super(GrassTestFilesHtmlReporter, self).finish()
 
-        file_success_per = 100 * float(self.files_succeeded) / self.test_files
-        file_fail_per = 100 * float(self.files_failed) / self.test_files
         tfoot = ('<tfoot>'
                  '<tr>'
                  '<td>Summary</td>'
@@ -185,7 +278,7 @@
                  '<td>{nsper:.2f}% successful</td>'
                  '</tr>'
                  '</tfoot>'.format(nfiles=self.test_files,
-                                   nsper=file_success_per))
+                                   nsper=self.file_pass_per))
 
         summary_sentence = ('Executed {nfiles} test files in {time:}.'
                             ' From them'
@@ -193,11 +286,11 @@
                             ' and {nffiles} files ({nfper:.2f}%) failed.'
                             .format(
                                 nfiles=self.test_files,
-                                time=main_time,
-                                nsfiles=self.files_succeeded,
-                                nffiles=self.files_failed,
-                                nsper=file_success_per,
-                                nfper=file_fail_per))
+                                time=self.main_time,
+                                nsfiles=self.files_pass,
+                                nffiles=self.files_fail,
+                                nsper=self.file_pass_per,
+                                nfper=self.file_fail_per))
 
         self.main_index.write('<tbody>{tfoot}</table>'
                               '<p>{summary}</p>'
@@ -208,10 +301,8 @@
         self.main_index.close()
 
     def start_file_test(self, module):
-        self.file_start_time = datetime.datetime.now()
-        self._start_file_test_called = True
-        self.main_index.flush()  # to get previous ones to the report
-        self.test_files += 1
+        super(GrassTestFilesHtmlReporter, self).start_file_test(module)
+        self.main_index.flush()  # to get previous lines to the report
 
     def wrap_stdstream_to_html(self, infile, outfile, module, stream):
         before = '<html><body><h1>%s</h1><pre>' % (module.name + ' ' + stream)
@@ -232,13 +323,16 @@
 
     def returncode_to_html_sentence(self, returncode):
         if returncode:
-            return '<span style="color: red">&#x274c;</span> Test failed (return code %d)' % (returncode)
+            return ('<span style="color: red">&#x274c;</span>'
+                    ' Test failed (return code %d)' % (returncode))
         else:
-            return '<span style="color: green">&#x2713;</span> Test succeeded (return code %d)' % (returncode)
+            return ('<span style="color: green">&#x2713;</span>'
+                    ' Test succeeded (return code %d)' % (returncode))
 
     def end_file_test(self, module, cwd, returncode, stdout, stderr):
-        assert self._start_file_test_called
-        file_time = datetime.datetime.now() - self.file_start_time
+        super(GrassTestFilesHtmlReporter, self).end_file_test(
+            module=module, cwd=cwd, returncode=returncode,
+            stdout=stdout, stderr=stderr)
         self.main_index.write(
             '<tr><td>{d}</td>'
             '<td><a href="{d}/{m}/index.html">{m}</a></td><td>{sf}</td>'
@@ -253,27 +347,69 @@
                                     module=module, stream='stderr')
         file_index_path = os.path.join(cwd, 'index.html')
         file_index = open(file_index_path, 'w')
-        file_index.write('<html><body>'
-                         '<h1>{m.name}</h1>'
-                         '<h2>{m.tested_dir} – {m.name}</h2>'
-                         '<p>{status}'
-                         '<p>Test duration: {dur}'
-                         '<ul>'
-                         '<li><a href="stdout.html">standard output (stdout)</a>'
-                         '<li><a href="stderr.html">standard error output (stderr)</a>'
-                         '<li><a href="testcodecoverage/index.html">code coverage</a>'
-                         '</ul>'
-                         '</body></html>'.format(
-                             dur=file_time, m=module,
-                             status=self.returncode_to_html_sentence(returncode)))
+        file_index.write(
+            '<html><body>'
+            '<h1>{m.name}</h1>'
+            '<h2>{m.tested_dir} – {m.name}</h2>'
+            '<p>{status}'
+            '<p>Test duration: {dur}'
+            '<ul>'
+            '<li><a href="stdout.html">standard output (stdout)</a>'
+            '<li><a href="stderr.html">standard error output (stderr)</a>'
+            '<li><a href="testcodecoverage/index.html">code coverage</a>'
+            '</ul>'
+            '</body></html>'
+            .format(
+                dur=self.file_time, m=module,
+                status=self.returncode_to_html_sentence(returncode)))
         file_index.close()
 
         if returncode:
-            sys.stderr.write('{d}/{m} failed (see {f})\n'.format(d=module.tested_dir,
-                                                                 m=module.name,
-                                                                 f=file_index_path))
-            self.files_failed += 1
-        else:
-            self.files_succeeded += 1
+            pass
+            # TODO: here we don't have oportunity to write error file
+            # to stream (stdout/stderr)
+            # a stream can be added and if not none, we could write
 
-        self._start_file_test_called = False
+
+class GrassTestFilesTextReporter(GrassTestFilesCountingReporter):
+
+    def __init__(self, stream):
+        super(GrassTestFilesTextReporter, self).__init__()
+        self._stream = stream
+
+    def start(self, results_dir):
+        super(GrassTestFilesTextReporter, self).start(results_dir)
+
+    def finish(self):
+        super(GrassTestFilesTextReporter, self).finish()
+
+        summary_sentence = ('\nExecuted {nfiles} test files in {time:}.'
+                            '\nFrom them'
+                            ' {nsfiles} files ({nsper:.2f}%) were successful'
+                            ' and {nffiles} files ({nfper:.2f}%) failed.\n'
+                            .format(
+                                nfiles=self.test_files,
+                                time=self.main_time,
+                                nsfiles=self.files_pass,
+                                nffiles=self.files_fail,
+                                nsper=self.file_pass_per,
+                                nfper=self.file_fail_per))
+        self._stream.write(summary_sentence)
+
+    def start_file_test(self, module):
+        super(GrassTestFilesTextReporter, self).start_file_test(module)
+        self._stream.flush()  # to get previous lines to the report
+
+    def end_file_test(self, module, cwd, returncode, stdout, stderr):
+        super(GrassTestFilesTextReporter, self).end_file_test(
+            module=module, cwd=cwd, returncode=returncode,
+            stdout=stdout, stderr=stderr)
+
+        if returncode:
+            self._stream.write(
+                '{m} from {d} failed\n'
+                .format(
+                    d=module.tested_dir,
+                    m=module.name))
+            # TODO: here we lost the possibility to include also file name
+            # of the appropriate report



More information about the grass-commit mailing list