Skip to content

Commit

Permalink
Add community rule: not-allowed-keyword (#1082)
Browse files Browse the repository at this point in the history
* Add community rule: not-allowed-keyword

* Update tests for RF3 and RF4
  • Loading branch information
bhirsz committed May 10, 2024
1 parent 31840e3 commit efc242f
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 1 deletion.
23 changes: 23 additions & 0 deletions docs/releasenotes/unreleased/rules.1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Community rule: not-allowed-keyword (#1067)
-------------------------------------------

New community (optional) rule W10002 ``not-allowed-keyword``. You can use this rule to find keywords that should not
be used in your project.

For example with following configuration::

> > robocop -i not-allowed-keyword -c not-allowed-keyword:keywords:click_using_javascript,click_with_sleep

It will find and raise issues in the following code::

*** Test Cases ***
Test with obsolete keyword
[Setup] Click With Sleep 1 min # Robocop will report not allowed keyword
Test Step


*** Keywords ***
Keyword With Obsolete Implementation
[Arguments] ${locator}
Click Using Javascript ${locator} # Robocop will report not allowed keyword

91 changes: 90 additions & 1 deletion robocop/checkers/community_rules/keywords.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
from typing import Set

from robot.api import Token
from robot.utils.robottime import timestr_to_secs

from robocop.checkers import VisitorChecker
from robocop.rules import Rule, RuleParam, RuleSeverity
from robocop.utils.misc import normalize_robot_name
from robocop.utils.run_keywords import iterate_keyword_names

RULE_CATEGORY_ID = "00"


def comma_separated_list(value: str) -> Set[str]:
if value is None:
return set()
return {normalize_robot_name(kw) for kw in value.split(",")}


rules = {
"10001": Rule(
RuleParam(
Expand Down Expand Up @@ -43,7 +53,41 @@
robocop -c sleep-keyword-used:max_time:1min .
""",
)
),
"10002": Rule(
RuleParam(
name="keywords",
default=None,
converter=comma_separated_list,
desc="Comma separated list of not allowed keywords",
),
rule_id="10002",
name="not-allowed-keyword",
msg="Keyword '{{ keyword }}' is not allowed",
severity=RuleSeverity.WARNING,
added_in_version="5.1.0",
enabled=False,
docs="""
Reports usage of not allowed keywords.
Configure which keywords should be reported by using ``keywords`` parameter.
Keyword names are normalized to match Robot Framework search behaviour (lower case, removed whitespace and
underscores).
For example::
> robocop -i not-allowed-keyword -c not-allowed-keyword:keywords:click_using_javascript
*** Keywords ***
Keyword With Obsolete Implementation
[Arguments] ${locator}
Click Using Javascript ${locator} # Robocop will report not allowed keyword
If keyword call contains possible library name (ie. Library.Keyword Name), Robocop checks if it matches
the not allowed keywords and if not, it will remove library part and check again.
""",
),
}


Expand Down Expand Up @@ -89,3 +133,48 @@ def visit_KeywordCall(self, node): # noqa
col=name_token.col_offset + 1,
end_col=name_token.end_col_offset + 1,
)


class NotAllowedKeyword(VisitorChecker):
reports = ("not-allowed-keyword",)

def check_keyword_naming_with_subkeywords(self, node, name_token_type):
for keyword in iterate_keyword_names(node, name_token_type):
self.check_keyword_naming(keyword.value, keyword)

def check_keyword_naming(self, name: str, keyword):
if not name:
return
not_allowed = self.param("not-allowed-keyword", "keywords")
normalized_name = normalize_robot_name(name)
if normalized_name not in not_allowed:
if "." not in normalized_name:
return
# handle possible library names (builtin.log)
normalized_name = normalized_name.split(".")[-1]
if normalized_name not in not_allowed:
return
self.report(
"not-allowed-keyword",
keyword=name,
node=keyword,
col=keyword.col_offset + 1,
end_col=keyword.end_col_offset + 1,
)

def visit_Setup(self, node): # noqa
self.check_keyword_naming_with_subkeywords(node, Token.NAME)

visit_TestTeardown = visit_SuiteTeardown = visit_Teardown = visit_TestSetup = visit_SuiteSetup = visit_Setup

def visit_Template(self, node): # noqa
# allow / disallow param
if node.value:
name_token = node.get_token(Token.NAME)
self.check_keyword_naming(node.value, name_token)
self.generic_visit(node)

visit_TestTemplate = visit_Template

def visit_KeywordCall(self, node): # noqa
self.check_keyword_naming_with_subkeywords(node, Token.KEYWORD)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
test.robot:2:16:2:27 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:4:15:4:26 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:5:18:5:39 [W] 10002 Keyword 'Not Allowed With Args' is not allowed
test.robot:15:5:15:16 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:16:25:16:35 [W] 10002 Keyword 'Notallowed' is not allowed
test.robot:18:9:18:30 [W] 10002 Keyword 'Not_allowed With Args' is not allowed
test.robot:23:9:23:20 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:28:12:28:23 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:30:12:30:23 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:31:19:31:40 [W] 10002 Keyword 'Not Allowed With Args' is not allowed
test.robot:34:19:34:30 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:38:5:38:33 [W] 10002 Keyword 'Library.Not Allowed With Lib' is not allowed
test.robot:39:5:39:32 [W] 10002 Keyword 'Library2.Nested.Not Allowed' is not allowed
test.robot:44:5:44:16 [W] 10002 Keyword 'Not Allowed' is not allowed
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
test.robot:2:16:2:27 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:4:15:4:26 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:5:18:5:39 [W] 10002 Keyword 'Not Allowed With Args' is not allowed
test.robot:15:5:15:16 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:18:9:18:30 [W] 10002 Keyword 'Not_allowed With Args' is not allowed
test.robot:23:9:23:20 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:28:12:28:23 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:30:12:30:23 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:31:19:31:40 [W] 10002 Keyword 'Not Allowed With Args' is not allowed
test.robot:34:19:34:30 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:38:5:38:33 [W] 10002 Keyword 'Library.Not Allowed With Lib' is not allowed
test.robot:39:5:39:32 [W] 10002 Keyword 'Library2.Nested.Not Allowed' is not allowed
test.robot:44:5:44:16 [W] 10002 Keyword 'Not Allowed' is not allowed
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
test.robot:2:16:2:27 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:4:15:4:26 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:5:18:5:39 [W] 10002 Keyword 'Not Allowed With Args' is not allowed
test.robot:15:5:15:16 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:18:9:18:30 [W] 10002 Keyword 'Not_allowed With Args' is not allowed
test.robot:23:9:23:20 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:28:12:28:23 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:30:12:30:23 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:31:19:31:40 [W] 10002 Keyword 'Not Allowed With Args' is not allowed
test.robot:34:19:34:30 [W] 10002 Keyword 'Not Allowed' is not allowed
test.robot:38:5:38:33 [W] 10002 Keyword 'Library.Not Allowed With Lib' is not allowed
test.robot:39:5:39:32 [W] 10002 Keyword 'Library2.Nested.Not Allowed' is not allowed
test.robot:44:5:44:16 [W] 10002 Keyword 'Not Allowed' is not allowed
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
*** Settings ***
Suite Setup Not Allowed
Suite Teardown
Test Setup Not Allowed
Test Teardown Not Allowed With Args
... ${arg}


*** Test Cases ***
Test with allowed keywords
Allowed
Allowed With Args ${arg}

Test with not allowed keywords
Not Allowed
IF $condition Notallowed
FOR ${var} IN RANGE 10
Not_allowed With Args ${arg}
... ${arg}
END
IF True
Log statement
Not Allowed
END

Test with settings
[Setup] Run Keywords
... Not Allowed
... AND
... Not Allowed
[Teardown] Not Allowed With Args ${arg}

Test with template
[Template] Not Allowed

Test with library
Not Allowed With Lib # should be ignored
Library.Not Allowed With Lib # should be reported
Library2.Nested.Not Allowed # should be reported


*** Keywords ***
Keyword with not allowed
Not Allowed

# while and stuff?
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from tests.atest.utils import RuleAcceptance


class TestRuleAcceptance(RuleAcceptance):
def test_rule(self):
self.check_rule(
src_files=["test.robot"],
expected_file="expected_output.txt",
config="-c not-allowed-keyword:keywords:NotAllowed,Not_AllowedWithArgs,library.not_allowed_with_lib",
issue_format="end_col",
target_version=">=5",
)

def test_rule_rf3(self):
self.check_rule(
src_files=["test.robot"],
expected_file="expected_output_rf3.txt",
config="-c not-allowed-keyword:keywords:NotAllowed,Not_AllowedWithArgs,library.not_allowed_with_lib",
issue_format="end_col",
target_version="==3.*",
)

def test_rule_rf4(self):
self.check_rule(
src_files=["test.robot"],
expected_file="expected_output_rf4.txt",
config="-c not-allowed-keyword:keywords:NotAllowed,Not_AllowedWithArgs,library.not_allowed_with_lib",
issue_format="end_col",
target_version="==4.*",
)

0 comments on commit efc242f

Please sign in to comment.