Revision: 3133
Author: janne.t.harkonen
Date: Tue May  4 01:05:02 2010
Log: Cleaner implementation for handling multiple failures
http://code.google.com/p/robotframework/source/detail?r=3133

Modified:
 /trunk/src/robot/errors.py
 /trunk/src/robot/running/keywords.py
 /trunk/src/robot/running/model.py
 /trunk/src/robot/running/runerrors.py
 /trunk/src/robot/running/userkeyword.py

=======================================
--- /trunk/src/robot/errors.py  Mon May  3 05:28:16 2010
+++ /trunk/src/robot/errors.py  Tue May  4 01:05:02 2010
@@ -46,15 +46,37 @@

 class ExecutionFailed(RobotError):
     """Used for communicating failures in test execution"""
+    msg = property(lambda self: self._msg)
+
     def __init__(self, message, timeout=False, exit=False, cont=False,
                  syntax=False):
         RobotError.__init__(self, message)
-        self.msg = message
+        self._msg = message
         self.timeout = timeout
         self.exit = exit
         self.cont = cont
         self.syntax = syntax

+    def get_errors(self):
+        return [self.msg]
+
+
+class MultipleErrors(ExecutionFailed):
+    msg = property(lambda self: ''.join(self._errors))
+
+    def __init__(self, errors):
+        exit = any(err for err in errors if err.exit)
+        cont = any(err for err in errors if err.cont)
+        timeout = any(err for err in errors if err.timeout)
+        syntax = any(err for err in errors if err.syntax)
+        self._errors= []
+        for e in errors:
+            self._errors.extend(e.get_errors())
+        ExecutionFailed.__init__(self, '', timeout, exit, cont, syntax)
+
+    def get_errors(self):
+        return self._errors
+

 class TimeoutError(RobotError):
     """Used when test execution is timed out"""
=======================================
--- /trunk/src/robot/running/keywords.py        Fri Apr 30 04:09:21 2010
+++ /trunk/src/robot/running/keywords.py        Tue May  4 01:05:02 2010
@@ -13,12 +13,77 @@
 #  limitations under the License.

 from robot import utils
-from robot.errors import FrameworkError, ExecutionFailed, DataError
+from robot.errors import FrameworkError, ExecutionFailed, DataError, MultipleErrors
 from robot.common import BaseKeyword
 from robot.variables import is_list_var


-def KeywordFactory(kwdata):
+class _Keywords(object):
+
+    def __init__(self, kwdata):
+        self._keywords = [ _KeywordFactory(kw) for kw in kwdata ]
+
+    def __iter__(self):
+        return iter(self._keywords)
+
+    def _run(self, output, namespace):
+        errors = []
+        for kw in self._keywords:
+            error = self._run_with_error_handling(kw, output, namespace)
+            if error:
+                errors.append(error)
+                if not error.cont:
+                    return self._report_errors(errors)
+        return self._report_errors(errors)
+
+    def _run_with_error_handling(self, kw, output, namespace):
+        try:
+            kw.run(output, namespace)
+            return None
+        except ExecutionFailed, err:
+            self._keyword_failed(err)
+            return err
+
+    def _keyword_failed(self, err):
+        pass
+
+
+class TestCaseKeywords(_Keywords):
+
+    def run(self, output, namespace, test_timeout, suite_run_errors):
+        self.test_timeout = test_timeout
+        self.suite_run_errors = suite_run_errors
+        return self._run(output, namespace)
+
+    def _keyword_failed(self, err):
+        self.test_timeout.set_keyword_timeout(err.timeout)
+        self.suite_run_errors.test_failed(exit=err.exit)
+
+    def _report_errors(self, errors):
+        all_errors = []
+        for err in errors:
+            all_errors.extend(err.get_errors())
+        return all_errors
+
+
+class UserKeywordKeywords(_Keywords):
+
+    def run(self, output, namespace):
+        self._run(output, namespace)
+
+    def _run_with_error_handling(self, kw, output, namespace):
+        try:
+            kw.run(output, namespace)
+            return None
+        except ExecutionFailed, err:
+            return err
+
+    def _report_errors(self, errors):
+        if errors:
+            raise MultipleErrors(errors)
+
+
+def _KeywordFactory(kwdata):
     if kwdata.type == 'kw':
         return Keyword(kwdata.name, kwdata.args)
     try:
@@ -102,7 +167,11 @@
             raise ExecutionFailed(msg)

     def _run_and_set_variables(self, handler, output, namespace):
-        return_value = Keyword._run(self, handler, output, namespace)
+        try:
+            return_value = Keyword._run(self, handler, output, namespace)
+        except ExecutionFailed:
+            self._set_variables(namespace, output, None)
+            raise
         self._set_variables(namespace, output, return_value)
         return return_value

@@ -186,7 +255,7 @@
         self.items = kwdata.items
         self.range = kwdata.range
         BaseKeyword.__init__(self, kwdata.name, type='for')
-        self.keywords = [ KeywordFactory(kw) for kw in kwdata.keywords ]
+        self.keywords = [ _KeywordFactory(kw) for kw in kwdata.keywords ]

     def run(self, output, namespace):
         self.starttime = utils.get_timestamp()
=======================================
--- /trunk/src/robot/running/model.py   Mon May  3 09:13:44 2010
+++ /trunk/src/robot/running/model.py   Tue May  4 01:05:02 2010
@@ -21,7 +21,7 @@

 from fixture import Setup, Teardown
 from timeouts import TestTimeout
-from keywords import KeywordFactory
+from keywords import TestCaseKeywords
 from namespace import Namespace
 from runerrors import SuiteRunErrors, TestRunErrors
 from userkeyword import UserLibrary
@@ -163,7 +163,7 @@
         self.tags = defaults.force_tags \
                     + utils.get_not_none(data.tags, defaults.default_tags)
self.timeout = utils.get_not_none(data.timeout, defaults.test_timeout)
-        self.keywords = [ KeywordFactory(kw) for kw in data.keywords ]
+        self.keywords = TestCaseKeywords(data.keywords)
         self.exit_on_failure = False

     def run(self, output, namespace, suite_errors):
@@ -203,7 +203,9 @@
         self.timeout.start()
         self._run_setup(output, namespace)
         if not self._run_errors.setup_failed():
-            self._run_keywords(output, namespace)
+            errors = self.keywords.run(output, namespace, self.timeout,
+                                       self._suite_errors)
+            self._run_errors.kw_err(errors)
         self._report_status(namespace)
         self._run_teardown(output, namespace)
         self._report_status_after_teardown()
@@ -217,14 +219,6 @@
         if error:
             self._run_errors.setup_err(error.msg)

-    def _run_keywords(self, output, namespace):
-        for kw in self.keywords:
-            error = self._run_with_error_handling(kw, output, namespace)
-            if error:
-                self._run_errors.kw_err(error.msg)
-                if not error.cont:
-                    return
-
     def _report_status(self, namespace):
         message = self._run_errors.get_message()
         if message:
=======================================
--- /trunk/src/robot/running/runerrors.py       Mon May  3 05:28:24 2010
+++ /trunk/src/robot/running/runerrors.py       Tue May  4 01:05:02 2010
@@ -129,11 +129,8 @@
     def setup_failed(self):
         return bool(self._setup_err)

-    def kw_err(self, err):
-        if isinstance(err, basestring):
-            self._kw_errs.append(err)
-        else:
-            self._kw_errs.extend(err)
+    def kw_err(self, errors):
+        self._kw_errs = errors

     def teardown_err(self, err):
         if isinstance(err, basestring):
@@ -156,7 +153,7 @@

     def parent_or_init_error(self):
         return self._parent_err or self._init_err
-
+
     def _form_error_message(self, errors):
"""Returns list of errors formatted as a string (empty string is returned if list is empty)""" return '\n\n'.join(errors if len(errors) == 1 else [ 'Error %d: %s' % (i+1, err)
=======================================
--- /trunk/src/robot/running/userkeyword.py     Mon May  3 05:28:24 2010
+++ /trunk/src/robot/running/userkeyword.py     Tue May  4 01:05:02 2010
@@ -16,11 +16,11 @@
 import re

 from robot.common import BaseLibrary, UserErrorHandler
-from robot.errors import DataError, ExecutionFailed
+from robot.errors import DataError
 from robot.variables import is_list_var, VariableSplitter
 from robot import utils

-from keywords import KeywordFactory
+from keywords import UserKeywordKeywords
 from timeouts import KeywordTimeout
 from arguments import UserKeywordArguments

@@ -109,7 +109,7 @@
         self.name = utils.printable_name(handlerdata.name)
         self._libname = libname
         self._set_variable_dependent_metadata(handlerdata.metadata)
- self.keywords = [ KeywordFactory(kw) for kw in handlerdata.keywords ]
+        self.keywords = UserKeywordKeywords(handlerdata.keywords)
         self.arguments = UserKeywordArguments(handlerdata.args,
                                               handlerdata.defaults,
                                               handlerdata.varargs,
@@ -143,11 +143,7 @@
                                      output)
         self._verify_keyword_is_valid()
         self.timeout.start()
-        errs = []
-        for kw in self.keywords:
-            self._run_with_error_handling(kw, output, namespace, errs)
-        if errs:
-            raise ExecutionFailed(errs, cont=True)
+        self.keywords.run(output, namespace)
         return self._get_return_value(namespace.variables)

     def _verify_keyword_is_valid(self):
@@ -158,18 +154,6 @@
             raise DataError("User keyword '%s' contains no keywords"
                             % self.name)

-    def _run_with_error_handling(self, kw, output, namespace, errs):
-        try:
-            kw.run(output, namespace)
-        except ExecutionFailed, err:
-            if isinstance(err.msg, basestring):
-                errs.append(err.msg)
-            else:
-                errs.extend(err.msg)
-            if not err.cont:
-                raise ExecutionFailed(errs, exit=err.exit,
- syntax=err.syntax,timeout=err.timeout)
-
     def _get_return_value(self, variables):
         if not self.return_value:
             return None

Reply via email to