diff --git a/src/Selenium2Library/keywords/keywordgroup.py b/src/Selenium2Library/keywords/keywordgroup.py index ff4f319ca..672603afd 100644 --- a/src/Selenium2Library/keywords/keywordgroup.py +++ b/src/Selenium2Library/keywords/keywordgroup.py @@ -8,13 +8,22 @@ decorator = None # decorator module doesn't work with IronPython 2.6 def _run_on_failure_decorator(method, *args, **kwargs): + self = args[0] + already_in_keyword = getattr(self, "_already_in_keyword", False) # If False, we are in the outermost keyword (or in `run_keyword`, if it's a dynamic library) + self._already_in_keyword = True # Set a flag on the instance so that as we call keywords inside this call and this gets run again, we know we're at least one level in. try: return method(*args, **kwargs) except Exception, err: - self = args[0] - if hasattr(self, '_run_on_failure'): + if hasattr(self, '_run_on_failure') and not self._has_run_on_failure: + # If we're in an inner keyword, track the fact that we've already run on failure once + self._has_run_on_failure = True self._run_on_failure() raise + finally: + if not already_in_keyword: + # If we are in the outer call, reset the flags. + self._already_in_keyword = False + self._has_run_on_failure = False class KeywordGroupMetaClass(type): def __new__(cls, clsname, bases, dict): diff --git a/test/acceptance/keywords/run_on_failure.txt b/test/acceptance/keywords/run_on_failure.txt old mode 100644 new mode 100755 index d5a482cab..6d9e45927 --- a/test/acceptance/keywords/run_on_failure.txt +++ b/test/acceptance/keywords/run_on_failure.txt @@ -1,14 +1,37 @@ *** Settings *** Suite Setup Run Keywords Go To Front Page Set Info Loglevel Suite Teardown Set Debug Loglevel -Test Teardown Register Keyword to Run on Failure Nothing Resource ../resource.txt *** Variables *** ${PAGE TITLE} (root)/index.html ${FAILURE MESSAGE} Page should not have contained text 'needle' +${old order} + +*** Keywords *** +On Fail + ${count}= Evaluate ${ON FAIL COUNT} + 1 + Set Test Variable ${ON FAIL COUNT} ${count} + +Prefer Custom Keywords + Import Library CustomSeleniumKeywords + ${old order}= Set Library Search Order CustomSeleniumKeywords + +Restore Old Search Order + Set Library Search Order ${old order} + +Open Browser To Front Page + Open Browser ${FRONT PAGE} *** Test Cases *** +Run On Failure Keyword Only Called Once + [Setup] Prefer Custom Keywords + Set Test Variable ${ON FAIL COUNT} ${0} + Register Keyword To Run On Failure On Fail + Run Keyword And Ignore Error Custom Selenium Keyword + Should Be Equal ${ON FAIL COUNT} ${1} On Failure Keyword called ${ON FAIL COUNT} times. + [Teardown] Register Keyword to Run On Failure Nothing + Log Title On Failure [Documentation] LOG 1 Log Title will be run on failure. LOG 2:2 NONE LOG 3.1.1:1 ${PAGE TITLE} LOG 3.1:3 NONE Register Keyword to Run on Failure Log Title diff --git a/test/acceptance/resource.txt b/test/acceptance/resource.txt old mode 100644 new mode 100755 diff --git a/test/env.py b/test/env.py old mode 100644 new mode 100755 index ed636cde9..efc6c4148 --- a/test/env.py +++ b/test/env.py @@ -6,6 +6,7 @@ ACCEPTANCE_TEST_DIR = os.path.join(ROOT_DIR, "acceptance") LIB_DIR = os.path.join(ROOT_DIR, "lib") RESOURCES_DIR = os.path.join(ROOT_DIR, "resources") +TEST_LIBS_DIR = os.path.join(RESOURCES_DIR, "testlibs") RESULTS_DIR = os.path.join(ROOT_DIR, "results") HTTP_SERVER_FILE = os.path.join(RESOURCES_DIR, 'testserver', 'testserver.py') SRC_DIR = os.path.join(ROOT_DIR, "..", "src") diff --git a/test/resources/testlibs/CustomSeleniumKeywords.py b/test/resources/testlibs/CustomSeleniumKeywords.py new file mode 100755 index 000000000..8c6a79c6f --- /dev/null +++ b/test/resources/testlibs/CustomSeleniumKeywords.py @@ -0,0 +1,18 @@ +from Selenium2Library import Selenium2Library +from robot.utils import asserts +from robot.libraries.BuiltIn import BuiltIn + +class CustomSeleniumKeywords(Selenium2Library): + + def __init__(self, *args, **kwargs): + """Share `Selenium2Library`'s cache of browsers, so that + we don't have to open a separate browser instance for the + `Run On Failure Keyword Only Called Once` test.""" + ret = super(CustomSeleniumKeywords, self).__init__(*args, **kwargs) + self._cache = BuiltIn().get_library_instance("Selenium2Library")._cache + + def custom_selenium_keyword(self): + self.custom_selenium_keyword_inner() + + def custom_selenium_keyword_inner(self): + asserts.assert_false(True) \ No newline at end of file diff --git a/test/run_tests.py b/test/run_tests.py index e8e009744..ed0d4bdd4 100755 --- a/test/run_tests.py +++ b/test/run_tests.py @@ -26,7 +26,7 @@ '--critical', 'regression', '--noncritical', 'inprogress', ] -ARG_VALUES = {'outdir': env.RESULTS_DIR, 'pythonpath': env.SRC_DIR} +ARG_VALUES = {'outdir': env.RESULTS_DIR, 'pythonpath': ':'.join((env.SRC_DIR, env.TEST_LIBS_DIR))} def acceptance_tests(interpreter, browser, args): ARG_VALUES['browser'] = browser.replace('*', '')