#!/usr/bin/env python3
# Copyright(c) 2007,2013 Progress Software Corporation (PSC). All rights
# Copyright (c) 2013,2015-2016, 2018-2021 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors.
# Use, reproduction, transfer, publication or disclosure is prohibited except as specifically provided for in your License Agreement with Software AG
""" Contains support for EPL code coverage reporting.
"""
import sys, os, logging
import io
import pysys
from pysys.constants import *
from pysys.writer.testoutput import CollectTestOutputWriter
from pysys.utils.fileutils import deletedir, mkdir, pathexists
log = logging.getLogger('pysys.apama.coverage')
[docs]class EPLCoverageWriter(CollectTestOutputWriter):
"""Writer that collects Apama EPL code coverage files during test execution, and writes an HTML report showing
line coverage for each ``.mon`` file after testing has completed.
To configure this writer, add it to your ``pysysproject.xml`` and set the required ``destDir`` property
(a typical value would be ``__coverage_epl.${outDirName}``), and optionally other configuration properties.
To enable it at runtime, run pysys with ``-XcodeCoverage`` (or alternatively, ``-XeplCoverage``).
If coverage is generated, the directory containing all coverage files is published as an artifact named
"EPLCoverageDir". Optionally an archive of this directory can be generated by setting the
``destArchive`` property (see `pysys.writer.testoutput.CollectTestOutputWriter`), and published as
"EPLCoverageArchive".
.. versionadded:: 10.7.0
The following property can be set in the project configuration for this writer (and see also
`pysys.writer.testoutput.CollectTestOutputWriter` for inherited properties such as ``destDir`` (required) and ``destArchive``):
"""
srcIncludes = []
"""
List of EPL source file patterns to include. Default is ``**``.
"""
srcExcludes = []
"""
List of EPL source file patterns to exclude, e.g. ``**/foo/Bar*.mon, **/baz/*.mon``.
In addition to the specified exclusions, files under APAMA_HOME, the test root dir, any test input dirs (``Input/*.mon``) and
any temporary (``*.tmp.mon``) files are excluded.
"""
srcSearchDirs = []
"""
List of directories that will be searched to locate source files that were injected without an absolute path.
"""
args = []
"""
Additional command line arguments to pass to ``epl_coverage``.
"""
title = ''
"""
Title for the HTML coverage report.
"""
# override CollectTestOutputWriter property values
fileIncludesRegex = u'.*[.]eplcoverage$'
outputPattern = u'@TESTID@_@FILENAME@.@UNIQUE@.eplcoverage'
publishArtifactDirCategory = u'EPLCoverageDir'
publishArtifactArchiveCategory = u'EPLCoverageArchive'
@staticmethod
def _isEPLCoverageEnabled(runner):
return (
runner.getBoolProperty('eplCoverage', default=runner.getBoolProperty('codeCoverage'))
or runner.getBoolProperty('eplcoverage')) # old pre-10.7 name
def isEnabled(self, record=False, **kwargs):
"""
:meta private: Does not need to be displayed in the API docs.
"""
return self.destDir and self._isEPLCoverageEnabled(self.runner)
def cleanup(self, **kwargs):
"""
:meta private: Does not need to be displayed in the API docs.
"""
if not pathexists(self.destDir):
log.info('No tests generated any EPL code coverage files (hint: check your correlators were shutdown cleanly).')
return
log.info('Preparing Apama EPL coverage report in: %s', os.path.normpath(self.destDir))
covfiles = os.listdir(self.destDir)
with io.open(self.destDir+'/coverage_files.txt', 'w', encoding='utf-8') as f:
# Writes Generated EPL Coverage file names to coverage_file.txt - always utf-8
for cov in covfiles:
f.write(cov)
f.write(u'\n')
del cov
arguments=[
'--output', self.destDir,
'--exclude', '**/Input/**.mon', # test files aren't interesting
'--exclude', '**/*.tmp.mon', # test files aren't interesting
# exclude all files from Apama monitor dir.
'--exclude', self.runner.project.APAMA_HOME+'/monitors/**',
# not useful
'--exclude', '**/*.qry.mon',
]
if getattr(self, 'excludeTestRootDir', 'true').lower()=='true':
# excludeTestRootDir is undocumented/unsupported, but kept as a back door in case this causes unexpected problems
arguments.extend(['--exclude', self.runner.project.testRootDir+'/**'])
for s in self.srcIncludes:
arguments.extend(['--include', s])
for s in self.srcExcludes:
arguments.extend(['--exclude', s])
for s in self.srcSearchDirs:
arguments.extend(['--source', s])
arguments.extend(self.args)
if self.title:
arguments.extend(['--title', self.title])
arguments.append('@%s/coverage_files.txt'%self.destDir)
proc = self.runner.startProcess(self.runner.project.APAMA_HOME+'/bin/epl_coverage',
arguments=arguments,
state=FOREGROUND,
environs=dict(os.environ), # need this so we have APAMA_JRE/JAVA_HOME etc available (on Windows, or from Unix core edition)
displayName='epl_coverage (with %d coverage files)'%(len(covfiles)),
stdouterr=self.destDir+'/epl_coverage',
workingDir=self.destDir,
abortOnError=True,
)
# if it succeeded the individual cov files can be deleted, but in case we excluded everything, leave them around
if os.path.getsize(self.destDir+os.sep+'merged.eplcoverage') > 12:
for p in covfiles: os.remove(os.path.join(self.destDir, p))
# logging goes to stdout, so stderr never contains anything interesting and not worth keeping given the above succeeded
os.remove(proc.stderr)
self.archiveAndPublish()