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

svn_grass at osgeo.org svn_grass at osgeo.org
Mon Jul 28 18:01:45 PDT 2014


Author: wenzeslaus
Date: 2014-07-28 18:01:45 -0700 (Mon, 28 Jul 2014)
New Revision: 61438

Modified:
   grass/trunk/lib/python/gunittest/invoker.py
   grass/trunk/lib/python/gunittest/reporters.py
   grass/trunk/lib/python/gunittest/runner.py
Log:
gunittest: generate keyvalue file for the whole report

Modified: grass/trunk/lib/python/gunittest/invoker.py
===================================================================
--- grass/trunk/lib/python/gunittest/invoker.py	2014-07-28 22:20:49 UTC (rev 61437)
+++ grass/trunk/lib/python/gunittest/invoker.py	2014-07-29 01:01:45 UTC (rev 61438)
@@ -25,36 +25,16 @@
 from .loader import GrassTestLoader, discover_modules
 from .reporters import (GrassTestFilesMultiReporter,
                         GrassTestFilesTextReporter, GrassTestFilesHtmlReporter,
-                        TestsuiteDirReporter, get_svn_path_authors,
-                        NoopFileAnonymizer)
+                        TestsuiteDirReporter, GrassTestFilesKeyValueReporter,
+                        get_svn_path_authors,
+                        NoopFileAnonymizer, keyvalue_to_text)
 from .utils import silent_rmtree, ensure_dir
 
 import grass.script.setup as gsetup
 
 import collections
-import types
 
 
-# TODO: change text_to_keyvalue to same sep as here
-def keyvalue_to_text(keyvalue, sep='=', vsep='\n', isep=',',
-                     last_vertical=None):
-    if not last_vertical:
-        last_vertical = vsep == '\n'
-    items = []
-    for key, value in keyvalue.iteritems():
-        # TODO: use isep for iterables other than strings
-        if (not isinstance(value, types.StringTypes)
-                and isinstance(value, collections.Iterable)):
-            # TODO: this does not work for list of non-strings
-            value = isep.join(value)
-        items.append('{key}{sep}{value}'.format(
-            key=key, sep=sep, value=value))
-    text = vsep.join(items)
-    if last_vertical:
-        text = text + vsep
-    return text
-
-
 # TODO: this might be more extend then update
 def update_keyval_file(filename, module, returncode):
     if os.path.exists(filename):
@@ -64,10 +44,12 @@
         keyval = {}
 
     # this is for one file
-    # TODO: testing authors are more appropriate stat for testsuite dir index
     test_file_authors = get_svn_path_authors(module.abs_file_path)
+    # in case that SVN is not available use empty authors
+    if test_file_authors is None:
+        test_file_authors = ''
 
-    # always owerwrite name and ok
+    # always owerwrite name and status
     keyval['name'] = module.name
     keyval['tested_dir'] = module.tested_dir
     if 'status' not in keyval.keys():
@@ -143,7 +125,9 @@
         cwd = os.path.join(results_dir, module.tested_dir, module.name)
         data_dir = os.path.join(module.file_dir, 'data')
         if os.path.exists(data_dir):
-            # TODO: link dir intead of copy tree
+            # TODO: link dir instead of copy tree and remove link afterwads
+            # (removing is good because of testsuite dir in samplecode)
+            # TODO: use different dir name in samplecode and test if it works
             shutil.copytree(data_dir, os.path.join(cwd, 'data'),
                             ignore=shutil.ignore_patterns('*.svn*'))
         ensure_dir(os.path.abspath(cwd))
@@ -195,6 +179,7 @@
                 GrassTestFilesTextReporter(stream=sys.stderr),
                 GrassTestFilesHtmlReporter(
                     file_anonymizer=self._file_anonymizer),
+                GrassTestFilesKeyValueReporter()
             ])
         self.testsuite_dirs = collections.defaultdict(list)  # reset list of dirs each time
         # TODO: move constants out of loader class or even module

Modified: grass/trunk/lib/python/gunittest/reporters.py
===================================================================
--- grass/trunk/lib/python/gunittest/reporters.py	2014-07-28 22:20:49 UTC (rev 61437)
+++ grass/trunk/lib/python/gunittest/reporters.py	2014-07-29 01:01:45 UTC (rev 61438)
@@ -19,6 +19,7 @@
 import subprocess
 import StringIO
 import collections
+import types
 import re
 
 import grass.script as gscript
@@ -27,6 +28,27 @@
 from .checkers import text_to_keyvalue
 
 
+# TODO: change text_to_keyvalue to same sep as here
+# TODO: create keyvalue file and move it there together with things from checkers
+def keyvalue_to_text(keyvalue, sep='=', vsep='\n', isep=',',
+                     last_vertical=None):
+    if not last_vertical:
+        last_vertical = vsep == '\n'
+    items = []
+    for key, value in keyvalue.iteritems():
+        # TODO: use isep for iterables other than strings
+        if (not isinstance(value, types.StringTypes)
+                and isinstance(value, collections.Iterable)):
+            # TODO: this does not work for list of non-strings
+            value = isep.join(value)
+        items.append('{key}{sep}{value}'.format(
+            key=key, sep=sep, value=value))
+    text = vsep.join(items)
+    if last_vertical:
+        text = text + vsep
+    return text
+
+
 def replace_in_file(file_path, pattern, repl):
     """
 
@@ -230,9 +252,21 @@
     # so test code authors list also contains authors of tests only
     # TODO: don't do this for the top level directories?
     tests_authors = set(tests_authors)
+    no_svn_text = ('<span style="font-size: 60%">'
+                   'Test file authors were not obtained.'
+                   '</span>')
+    if (not tests_authors
+            or (len(tests_authors) == 1 and list(tests_authors)[0] == '')):
+        return '<h3>Code and test authors</h3>' + no_svn_text
     from_date = years_ago(datetime.date.today(), years=1)
     tested_dir_authors = get_svn_path_authors(directory, from_date)
-    not_testing_authors = tested_dir_authors - tests_authors
+    if tested_dir_authors is not None:
+        not_testing_authors = tested_dir_authors - tests_authors
+    else:
+        no_svn_text = ('<span style="font-size: 60%">'
+                       'Authors cannot be obtained using SVN.'
+                       '</span>')
+        not_testing_authors = tested_dir_authors = [no_svn_text]
     if not not_testing_authors:
         not_testing_authors = ['all recent authors contributed tests']
 
@@ -484,7 +518,7 @@
         svn_info = get_svn_info()
         if not svn_info:
             svn_text = ('<span style="font-size: 60%">'
-                        'SVN revision cannot be be obtained'
+                        'SVN revision cannot be obtained'
                         '</span>')
         else:
             url = get_source_url(path=svn_info['relative-url'],
@@ -693,6 +727,139 @@
             # a stream can be added and if not none, we could write
 
 
+class GrassTestFilesKeyValueReporter(GrassTestFilesCountingReporter):
+
+    def __init__(self):
+        super(GrassTestFilesKeyValueReporter, self).__init__()
+        self.result_dir = None
+
+    def start(self, results_dir):
+        super(GrassTestFilesKeyValueReporter, self).start(results_dir)
+        # having all variables public although not really part of API
+        self.result_dir = results_dir
+
+        # TODO: this can be moved to the counter class
+        self.failures = 0
+        self.errors = 0
+        self.skipped = 0
+        self.successes = 0
+        self.expected_failures = 0
+        self.unexpected_success = 0
+        self.total = 0
+
+        # TODO: document: tested_dirs is a list and it should fit with names
+        self.names = []
+        self.tested_dirs = []
+        self.files_returncodes = []
+
+        # sets (no size specified)
+        self.modules = set()
+        self.test_files_authors = set()
+
+    def finish(self):
+        super(GrassTestFilesKeyValueReporter, self).finish()
+
+        # this shoul be moved to some additional meta passed in constructor
+        svn_info = get_svn_info()
+        if not svn_info:
+            svn_revision = ''
+        else:
+            svn_revision = svn_info['revision']
+
+        summary = {}
+        summary['files_total'] = self.test_files
+        summary['files_successes'] = self.files_pass
+        summary['files_failures'] = self.files_fail
+
+        summary['names'] = self.names
+        summary['tested_dirs'] = self.tested_dirs
+        # TODO: we don't have a general mechanism for storing any type in text
+        summary['files_returncodes'] = [str(item)
+                                        for item in self.files_returncodes]
+
+        # let's use seconds as a universal time delta format
+        # (there is no standard way how to store time delta as string)
+        summary['time'] = self.main_time.total_seconds()
+
+        status = 'failed' if self.files_fail else 'succeeded'
+        summary['status'] = status
+
+        summary['total'] = self.total
+        summary['successes'] = self.successes
+        summary['failures'] = self.failures
+        summary['errors'] = self.errors
+        summary['skipped'] = self.skipped
+        summary['expected_failures'] = self.expected_failures
+        summary['unexpected_successes'] = self.unexpected_success
+
+        summary['test_files_authors'] = self.test_files_authors
+        summary['tested_modules'] = self.modules
+        summary['svn_revision'] = svn_revision
+        # ignoring issues with time zones
+        summary['timestamp'] = self.main_start_time.strftime('%Y-%m-%d %H:%M:%S')
+        # TODO: add some general metadata here (passed in constructor)
+
+        summary_filename = os.path.join(self.result_dir,
+                                        'test_keyvalue_result.txt')
+        with open(summary_filename, 'w') as summary_file:
+            text = keyvalue_to_text(summary, sep='=', vsep='\n', isep=',')
+            summary_file.write(text)
+
+    def end_file_test(self, module, cwd, returncode, stdout, stderr,
+                      test_summary):
+        super(GrassTestFilesKeyValueReporter, self).end_file_test(
+            module=module, cwd=cwd, returncode=returncode,
+            stdout=stdout, stderr=stderr)
+        # TODO: considering others accoring to total, OK?
+        # here we are using 0 for total but HTML reporter is using None
+        total = test_summary.get('total', 0)
+        failures = test_summary.get('failures', 0)
+        errors = test_summary.get('errors', 0)
+        # Python unittest TestResult class is reporting success for no
+        # errors or failures, so skipped, expected failures and unexpected
+        # success are ignored
+        # but successful tests are only total - the others
+        skipped = test_summary.get('skipped', 0)
+        expected_failures = test_summary.get('expected_failures', 0)
+        unexpected_successes = test_summary.get('unexpected_successes', 0)
+        successes = test_summary.get('successes', 0)
+
+        # TODO: move this to counter class and perhaps use aggregation
+        # rather then inheritance
+        self.failures += failures
+        self.errors += errors
+        self.skipped += skipped
+        self.expected_failures += expected_failures
+        self.unexpected_success += unexpected_successes
+
+        # TODO: should we test for zero?
+        if total is not None:
+            # success are only the clear ones
+            # percentage is influenced by all
+            # but putting only failures to table
+            self.successes += successes
+            self.total += total
+
+        self.files_returncodes.append(returncode)
+
+        self.tested_dirs.append(module.tested_dir)
+        self.names.append(module.name)
+
+        modules = test_summary.get('tested_modules', None)
+        if modules:
+            # TODO: replace by better handling of potential lists when parsing
+            # TODO: create link to module if running in grass or in addons
+            # alternatively a link to module test summary
+            if type(modules) not in [list, set]:
+                modules = [modules]
+            self.modules.update(modules)
+
+        test_file_authors = test_summary['test_file_authors']
+        if type(test_file_authors) not in [list, set]:
+            test_file_authors = [test_file_authors]
+        self.test_files_authors.update(test_file_authors)
+
+
 class GrassTestFilesTextReporter(GrassTestFilesCountingReporter):
 
     def __init__(self, stream):
@@ -843,7 +1010,7 @@
             test_file_authors = summary['test_file_authors']
             if type(test_file_authors) is not list:
                 test_file_authors = [test_file_authors]
-            test_files_authors += test_file_authors
+            test_files_authors.extend(test_file_authors)
 
             file_total += 1
             file_successes += 0 if summary['returncode'] else 1

Modified: grass/trunk/lib/python/gunittest/runner.py
===================================================================
--- grass/trunk/lib/python/gunittest/runner.py	2014-07-28 22:20:49 UTC (rev 61437)
+++ grass/trunk/lib/python/gunittest/runner.py	2014-07-29 01:01:45 UTC (rev 61438)
@@ -280,7 +280,7 @@
         infos.append("unexpected_successes=%d" % unexpectedSuccesses)
 
         # TODO: include each module just once? list good and bad modules?
-        infos.append("modules=%s" % ','.join(self._grass_modules))
+        infos.append("tested_modules=%s" % ','.join(self._grass_modules))
         infos.append("supplementary_files=%s" % ','.join(self._supplementary_files))
 
         # module, modules?, c, c++?, python



More information about the grass-commit mailing list