# Copyright 2008-2015 Nokia Networks
# Copyright 2016- Robot Framework Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from robot.errors import DataError
from robot.model import SuiteVisitor, TagPattern
from robot.utils import Matcher, plural_or_not
[docs]def KeywordRemover(how):
upper = how.upper()
if upper.startswith('NAME:'):
return ByNameKeywordRemover(pattern=how[5:])
if upper.startswith('TAG:'):
return ByTagKeywordRemover(pattern=how[4:])
try:
return {'ALL': AllKeywordsRemover,
'PASSED': PassedKeywordRemover,
'FOR': ForLoopItemsRemover,
'WUKS': WaitUntilKeywordSucceedsRemover}[upper]()
except KeyError:
raise DataError("Expected 'ALL', 'PASSED', 'NAME:<pattern>', "
"'TAG:<pattern>', 'FOR', or 'WUKS' but got '%s'." % how)
class _KeywordRemover(SuiteVisitor):
_message = 'Keyword data removed using --RemoveKeywords option.'
def __init__(self):
self._removal_message = RemovalMessage(self._message)
def _clear_content(self, kw):
kw.keywords = []
kw.messages = []
self._removal_message.set(kw)
def _failed_or_warning_or_error(self, item):
return not item.passed or self._warning_or_error(item)
def _warning_or_error(self, item):
finder = WarningAndErrorFinder()
item.visit(finder)
return finder.found
[docs]class AllKeywordsRemover(_KeywordRemover):
[docs] def visit_keyword(self, keyword):
self._clear_content(keyword)
[docs]class PassedKeywordRemover(_KeywordRemover):
[docs] def start_suite(self, suite):
if not suite.statistics.all.failed:
for keyword in suite.keywords:
if not self._warning_or_error(keyword):
self._clear_content(keyword)
[docs] def visit_test(self, test):
if not self._failed_or_warning_or_error(test):
for keyword in test.keywords:
self._clear_content(keyword)
[docs] def visit_keyword(self, keyword):
pass
[docs]class ByNameKeywordRemover(_KeywordRemover):
def __init__(self, pattern):
_KeywordRemover.__init__(self)
self._matcher = Matcher(pattern, ignore='_')
[docs] def start_keyword(self, kw):
if self._matcher.match(kw.name) and not self._warning_or_error(kw):
self._clear_content(kw)
[docs]class ByTagKeywordRemover(_KeywordRemover):
def __init__(self, pattern):
_KeywordRemover.__init__(self)
self._pattern = TagPattern(pattern)
[docs] def start_keyword(self, kw):
if self._pattern.match(kw.tags) and not self._warning_or_error(kw):
self._clear_content(kw)
[docs]class ForLoopItemsRemover(_KeywordRemover):
_message = '%d passing step%s removed using --RemoveKeywords option.'
[docs] def start_keyword(self, kw):
if kw.type == kw.FOR_LOOP_TYPE:
before = len(kw.keywords)
kw.keywords = self._remove_keywords(kw.keywords)
self._removal_message.set_if_removed(kw, before)
def _remove_keywords(self, keywords):
return [kw for kw in keywords
if self._failed_or_warning_or_error(kw) or kw is keywords[-1]]
[docs]class WaitUntilKeywordSucceedsRemover(_KeywordRemover):
_message = '%d failing step%s removed using --RemoveKeywords option.'
[docs] def start_keyword(self, kw):
if kw.name == 'BuiltIn.Wait Until Keyword Succeeds' and kw.keywords:
before = len(kw.keywords)
kw.keywords = self._remove_keywords(list(kw.keywords))
self._removal_message.set_if_removed(kw, before)
def _remove_keywords(self, keywords):
include_from_end = 2 if keywords[-1].passed else 1
return self._kws_with_warnings(keywords[:-include_from_end]) \
+ keywords[-include_from_end:]
def _kws_with_warnings(self, keywords):
return [kw for kw in keywords if self._warning_or_error(kw)]
[docs]class WarningAndErrorFinder(SuiteVisitor):
def __init__(self):
self.found = False
[docs] def start_suite(self, suite):
return not self.found
[docs] def start_test(self, test):
return not self.found
[docs] def start_keyword(self, keyword):
return not self.found
[docs] def visit_message(self, msg):
if msg.level in ('WARN', 'ERROR'):
self.found = True
[docs]class RemovalMessage(object):
def __init__(self, message):
self._message = message
[docs] def set_if_removed(self, kw, len_before):
removed = len_before - len(kw.keywords)
if removed:
self.set(kw, self._message % (removed, plural_or_not(removed)))
[docs] def set(self, kw, message=None):
kw.doc = ('%s\n\n_%s_' % (kw.doc, message or self._message)).strip()