Skip to content

Commit

Permalink
Start from PR #74
Browse files Browse the repository at this point in the history
  • Loading branch information
JokeWaumans committed Jun 10, 2024
1 parent 534c3f5 commit eb07b08
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
COVERITY_HOSTNAME='coverity.melexis.com'
COVERITY_USERNAME='reporter'
COVERITY_PASSWORD='yourpassword'
COVERITY_STREAM='stream_name'
36 changes: 36 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,42 @@ input file. When this setting is missing, the default value ``true`` is used.
.. |--xunit report.xml| replace:: ``--xunit report.xml``
.. _`--xunit report.xml`: https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#xunit-compatible-result-file

Query Coverity server for defects
---------------------------------

Coverity requires server and its Triage information to determine if reported
defect is indeed an error or a warning. Locally reported information (increase
or decrease) do not indicate if new defects appeared or they are variation of
already triaged defects. That is why local parsing of the information is not
sufficient, but warnings-plugin based on [Sphinx Coverity Plugin](https://github.com/melexis/sphinx-coverity-extension)
is now able to query the server and receive count of defects classified as
"Bug", "Pending" and "Unclassified".

Coverity checker requires connection to your local Coverity instance, but it
is able to eliminate the increase of Coverity defects of new contributions
during the development with legacy defects.

To run Coverity checker you need to copy/rename `.env.example` file to `.env` and
fill it your Coverity credentials (along with Coverity stream name). You can also
just define environment variables `COVERITY_HOSTNAME`, `COVERITY_USERNAME`,
`COVERITY_PASSWORD` and `COVERITY_STREAM`, if you are not comfortable writing
your password in a file. `.env` is added to `.gitignore` just so that you do
not commit your password to repository. You can also pass explicit .env styled
file to plugin.


.. code-block:: bash
# Coverity checker assumes logfile as configuration file (.env format)
mlx-warnings --coverityserver .env.example
# rename the .env.example
mv .env.example .env
# command line, where whatever is non-existant .env file
mlx-warnings --coverityserver whatever
# explicitly as python module
python3 -m mlx.warnings --coverityserver whatever
----------------------------------
Configuration File to Pass Options
----------------------------------
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
requires = [
'junitparser>=1.0.0,<2.0',
'ruamel.yaml>=0.17.21',
'mlx.coverity>=1.0.0,<2.0',
'python-decouple',
]

setup(
Expand Down
92 changes: 92 additions & 0 deletions src/mlx/warnings/coverity_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import sys

from decouple import config, Config, RepositoryEnv

from mlx.coverity import CoverityConfigurationService, CoverityDefectService
from urllib.error import URLError
from .warnings_checker import WarningsChecker
from .regex_checker import CoverityChecker

class CoverityServerChecker(WarningsChecker):
name = 'coverityserver'
transport = 'http'
port = '8080'
hostname = ''
username = ''
password = ''
stream = ''

def _fill_vars(self, configuration):
'''
Fill variables from Python decouple Config class
Args:
configuration (decouple.Config): Config class from python Decouple
'''
self.transport = configuration('COVERITY_TRANSPORT', default=self.transport)
self.port = configuration('COVERITY_PORT', default=self.port)
self.hostname = configuration('COVERITY_HOSTNAME', default=self.hostname)
self.username = configuration('COVERITY_USERNAME', default=self.username)
self.password = configuration('COVERITY_PASSWORD', default=self.password)
self.stream = configuration('COVERITY_STREAM', default=self.stream)

def __init__(self, verbose=False):
''' Constructor
Args:
verbose (bool): Enable/disable verbose logging
'''
self._fill_vars(config)
self.classification = "Pending,Bug,Unclassified"

super(CoverityChecker, self).__init__(verbose=verbose)

def _extract_args(self, logfile):
'''
Function for extracting arguments from logfile
Args:
logfile (file): Logfile is actually a configuration file for Coverity checker
Raises:
ValueError when all needed variables are not set to their non-default values
'''
# Add here a function that populates variables from the logfile (probably .env logfile)
# Maybe a suggestion is to simply load that env like file here
try:
self._fill_vars(Config(RepositoryEnv(str(logfile[0]))))
except FileNotFoundError:
pass
if self.hostname == '' or self.username == '' or self.password == '' or self.stream == '':
raise ValueError('Coverity checker requires COVERITY_HOSTNAME, COVERITY_USERNAME, COVERITY_PASSWORD and COVERITY_STREAM to be set in .env file or as environment variables')
return

def _connect_to_coverity(self):
'''
Login to Coverity server and retrieve project and stream information. This function
requires _extract_args to be run before as all class arguments need to be set.
'''
print("Login to Coverity Server: %s://%s:%s" % (self.transport, self.hostname, self.port))
coverity_conf_service = CoverityConfigurationService(self.transport, self.hostname, self.port)
coverity_conf_service.login(self.username, self.password)
if self.verbose:
print("Retrieving stream from Coverity Server" % (self.transport, self.hostname, self.port))
check_stream = coverity_conf_service.get_stream(self.stream)
if check_stream is None:
raise ValueError('Coverity checker failed. No such Coverity stream [%s] found on [%s]',
self.stream, coverity_conf_service.get_service_url())
self.project_name = coverity_conf_service.get_project_name(check_stream)
self.coverity_service = CoverityDefectService(coverity_conf_service)
self.coverity_service.login(self.username, self.password)

def check(self, logfile):
'''
Function for retrieving number of defects from Coverity server
Args:
content (str): some sort of configuration string
'''
self._extract_args(logfile)
self._connect_to_coverity()
print("Querying Coverity Server for defects on stream %s" % self.stream)
try:
defects = self.coverity_service.get_defects(self.project_name, self.stream, classification=self.classification)
except (URLError, AttributeError) as error:
print('Coverity checker failed with %s' % error)
return
self.count = defects.totalNumberOfRecords
3 changes: 2 additions & 1 deletion src/mlx/warnings/warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from .junit_checker import JUnitChecker
from .regex_checker import CoverityChecker, DoxyChecker, SphinxChecker, XMLRunnerChecker
from .robot_checker import RobotChecker
from .coverity_checker import CoverityServerChecker

__version__ = distribution('mlx.warnings').version

Expand Down Expand Up @@ -58,7 +59,7 @@ def __init__(self, verbose=False, config_file=None, cq_enabled=False):
self.cq_enabled = cq_enabled
self.public_checkers = [SphinxChecker(self.verbose), DoxyChecker(self.verbose), JUnitChecker(self.verbose),
XMLRunnerChecker(self.verbose), CoverityChecker(self.verbose),
RobotChecker(self.verbose)]
RobotChecker(self.verbose), CoverityServerChecker(self.verbose)]

if config_file:
with open(config_file, 'r', encoding='utf-8') as open_file:
Expand Down
4 changes: 4 additions & 0 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,7 @@ def test_cq_description_format(self, path_cwd_mock):
])
self.assertEqual(2, retval)
self.assertTrue(filecmp.cmp(out_file, ref_file), '{} differs from {}'.format(out_file, ref_file))

def test_coverity_no_credentials(self):
with self.assertRaises(ValueError):
warnings_wrapper(['--coverity', 'bla'])
3 changes: 3 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ deps =
pytest-cov
setuptools_scm
coverage
python-decouple
mlx-coverity
commands =
pytest --cov=mlx --cov-report=term-missing -vv tests/
mlx-warnings -h
Expand Down Expand Up @@ -74,6 +76,7 @@ deps =
[testenv:docs]
deps =
-r{toxinidir}/docs/requirements.txt
mlx-coverity
commands =
sphinx-build {posargs:-E} -b doctest docs docs/_build
sphinx-build {posargs:-E} -b html docs docs/_build
Expand Down

0 comments on commit eb07b08

Please sign in to comment.