Adding a rule
Following the steps in the given order ensures everything is covered.
Create documentation
Create test
Create rule
Note
If a similar rule already exists, copy that rules elements for each of the following steps.
Create Documentation
A documentation first approach clarifies what a rule will address.
The documentation is located in the docs directory.
All rules for a rule group are kept in a file with the following pattern: <rule_group>_rules.rst
.
The rules are in alphabetical order within the documentation.
Rule documentation contains the following items:
Rule ID
Icons
Summary
Link to configuration options
Violation example
Fixed example
architecture_001
################
|phase_4| |error| |indent|
This rule checks for blank spaces before the **architecture** keyword.
**Violation**
.. code-block:: vhdl
architecture rtl of fifo is
begin
**Fix**
.. code-block:: vhdl
architecture rtl of fifo is
begin
The Rule ID identifies the rule and is unique through all versions of VSG. A Rule ID will never be re-used.
Icons provide information about the rule at a quick glance.
These icons indicate the phase in which the rule is ran, whether it is disabled by default, etc…
Links to the icons are stored in a file named icons.rst
and is included into each file using an include at the top of every file.
The summary is a very brief description of what issue the rule is attempting to resolve.
If the rule has configuration options, a link to the configuration information will be given.
The links are sored in a file named links.rst
and in included into each file using an include at the top of every file.
The Violation Example provides a visual indicating what the issue is.
The Fixed example provides a visual indicating what the end state should be.
Create Test
The next step is to create a test for the new rule.
Test directory structure
The directory structure of the tests closely matches the rules directory. Every rule group has it’s own directory.
tests
├── after
├── alias_declaration
├── architecture
...
└── whitespace
Each rule will typically consist of at least three files:
test file of the form
test_rule_[0-9][0-9][0-9].py
input vhdl file with violations in the form of
rule_[0-9][0-9][0-9]_test_input.vhd
fixed vhdl file without violations in the form of
rule_[0-9][0-9][0-9]_test_input.fixed.vhd
tests
└── architecture
├── rule_001_test_input.fixed.vhd
├── rule_001_test_input.vhd
└── test_rule_001.py
Test File Structure
The test file contains a class with the name of test_rule
.
The minimum number of tests will be one for those rules for which a fix is not available.
For rules in which a fix is available, a minimum of two tests will be required: one for detecting violations and another for verifying the violations can be fixed.
If the rule has configurable options, then additional tests are required based on the number of configurable items.
class test_rule(unittest.TestCase):
def test_rule_001(self):
oRule = architecture.rule_001()
def test_fix_rule_001(self):
oRule = architecture.rule_001()
The test_rule_001
method operates on the test input file and returns a list of lines where a violation was detected.
The line numbers are then validated.
The test_fix_rule_001
method operations on the test input file and attempts to fix the violations.
The resulting fix is compared against the rule_001_test_input.fixed.vhd
file.
Any discrepancies are flagged.
Test Input File
The test input file provides examples of code passing and violating the particular rule. It provides the conditions where the rule is checked. Depending on the rule, this file can range from very simple to quite complex. If configuration options are available for the rule, then this file should provide conditions for each configurable item.
architecture RTL of FIFO is begin end architecture RTL;
-- This should fail
architecture RTL of FIFO is
begin
end architecture RTL;
Fixed Input File
This file provides the output product of running the rule in isolation. Additional rules are not applied. If configuration options are available for the rule, then additional files are required for each configuration.
architecture RTL of FIFO is begin end architecture RTL;
-- This should fail
architecture RTL of FIFO is
begin
end architecture RTL;
Run failing test
VSG uses pytest and individual tests can be executed:
$ pytest tests/architecture/test_rule_001.py
======================================== test session starts =========================================
platform linux -- Python 3.10.13, pytest-8.1.1, pluggy-1.4.0
rootdir: /home/jcleary/projects/vsg-master
configfile: pyproject.toml
plugins: html-4.1.1, html-reporter-0.2.9, metadata-3.1.1
collected 2 items
tests/architecture/test_rule_001.py FF [100%]
============================================== FAILURES ==============================================
______________________________ test_architecture_rule.test_fix_rule_001 ______________________________
self = <tests.architecture.test_rule_001.test_architecture_rule testMethod=test_fix_rule_001>
def test_fix_rule_001(self):
> oRule = architecture.rule_001()
E AttributeError: module 'vsg.rules.architecture' has no attribute 'rule_001'. Did you mean: 'rule_002'?
tests/architecture/test_rule_001.py:39: AttributeError
________________________________ test_architecture_rule.test_rule_001 ________________________________
self = <tests.architecture.test_rule_001.test_architecture_rule testMethod=test_rule_001>
def test_rule_001(self):
> oRule = architecture.rule_001()
E AttributeError: module 'vsg.rules.architecture' has no attribute 'rule_001'. Did you mean: 'rule_002'?
tests/architecture/test_rule_001.py:28: AttributeError
====================================== short test summary info =======================================
FAILED tests/architecture/test_rule_001.py::test_architecture_rule::test_fix_rule_001 - AttributeError: module 'vsg.rules.architecture' has no attribute 'rule_001'. Did you mean: 'rule_...
FAILED tests/architecture/test_rule_001.py::test_architecture_rule::test_rule_001 - AttributeError: module 'vsg.rules.architecture' has no attribute 'rule_001'. Did you mean: 'rule_...
========================================= 2 failed in 0.43s ==========================================
The test failed because the rule does not yet exist.
Create Rule
Updating __init__.py file
In order for a rule to be used, it must be added to the __init__.py
file in the rule group directory.
# -*- coding: utf-8 -*-
from .rule_001 import rule_001
from .rule_002 import rule_002
Rule file structure
class name
docstring
rule implementation
The class name of the rule must follow this pattern: rule_[0-9][0-9][0-9]
.
The docstring must match the documentation but does not include the header or the icons.
The rule implementation could be unique or it could call a base rule.
# -*- coding: utf-8 -*-
from vsg.rules import token_indent
from vsg.token import architecture_body as token
class rule_001(token_indent):
"""
This rule checks for blank spaces before the **architecture** keyword.
**Violation**
.. code-block:: vhdl
architecture rtl of fifo is
begin
**Fix**
.. code-block:: vhdl
architecture rtl of fifo is
begin
"""
def __init__(self):
super().__init__([token.architecture_keyword])
In this case the token_indent
base rule is used to check the indent of the architecture keyword.
Run passing test
Re-run the test and make any changes until the test passes.
======================================== test session starts =========================================
platform linux -- Python 3.10.13, pytest-8.1.1, pluggy-1.4.0
rootdir: /home/jcleary/projects/vsg-master
configfile: pyproject.toml
plugins: html-4.1.1, html-reporter-0.2.9, metadata-3.1.1
collected 2 items
tests/architecture/test_rule_001.py .. [100%]
========================================= 2 passed in 0.34s ==========================================
Run regression tests
Now that the single test runs, the entire suite of tests must be ran to ensure there no side effects.
$ tox -e test-py38
======================================== test session starts =========================================
platform linux -- Python 3.8.10, pytest-8.1.1, pluggy-1.4.0
cachedir: .tox/test-py38/.pytest_cache
rootdir: /home/jcleary/projects/vsg-master
configfile: pyproject.toml
plugins: cov-5.0.0, html-4.1.1, xdist-3.5.0, html-reporter-0.2.9, metadata-3.1.1
8 workers [3005 items]
.............................................................................................. [ 3%]
.............................................................................................. [ 6%]
.............................................................................................. [ 97%]
....................................................................................... [100%]
---------- coverage: platform linux, python 3.8.10-final-0 -----------
Coverage HTML written to dir build.out/test-py38/coverage
Coverage XML written to file build.out/test-py38/coverage.xml
- Generated html report: file:///home/jcleary/projects/vsg-master/build.out/test-py38/test/pytest.html -
================================== 3005 passed in 72.67s (0:01:12) ===================================
test-py38: OK (81.82=setup[8.52]+cmd[73.30] seconds)
congratulations :) (81.87 seconds)