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