import os
import shelve
[docs]
class ReportGenerator(object):
"""
This class generates the HTML reports for LittleDarwin. It creates a
summary report for the entire project, as well as detailed reports for
each file that was mutated.
"""
def __init__(self, littleDarwinVersion=None):
"""
Initializes the ReportGenerator object.
:param littleDarwinVersion: The version of LittleDarwin.
:type littleDarwinVersion: str
"""
self.database = None
self.ldVersion = littleDarwinVersion
[docs]
def initiateDatabase(self, databasePath):
"""
Initiates the results database.
:param databasePath: The path to the results database.
:type databasePath: str
"""
self.database = shelve.open(databasePath, "c")
[docs]
def generateHTMLFinalReport(self, resultData, reportPath):
"""
Generates the final HTML report for the entire project.
:param resultData: A list of lists, where each inner list contains
the file path, the number of survived mutants, and
the total number of mutants for a file.
:type resultData: list
:param reportPath: The path to the report file.
:type reportPath: str
:return: The HTML report as a string.
:rtype: str
"""
reportBeginning = """<!DOCTYPE html><html><head><title>LittleDarwin Mutation Coverage Report</title>
<style type='text/css'> body { font-family: "Carlito", "Calibri", "Helvetica Neue", sans-serif; }
table { border-collapse: collapse; } th, td { text-align: left; padding: 1em; }
th { background-color: #f0f0f0; color: black; } tr:hover { background-color: #b2daf5; }
a:link, a:visited { color: black; text-decoration: none; display: inline-block; }
a:hover, a:active { font-weight: bold; } .coverage_bar { display: inline-block; border-radius: 0.3em;
height: 1.1em; width: 9em; background: #ff8482; margin: 0 5px; vertical-align: middle;
border: 1px solid #888; position: relative } .coverage_complete { display: inline-block;
border-radius: 0.3em; height: 100%; background: #acff8b; float: left }
.coverage_legend { position: absolute; height: 100%; width: 100%; left: 0; top: 0; text-align: center }
</style></head><body><h1>LittleDarwin Mutation Coverage Report</h1>
<h2>Project Summary</h2><table><thead><tr><th>Number of Files</th>
<th colspan=2>Mutation Coverage</th></tr> </thead> <tbody>"""
reportMiddle = """</tbody></table><h2>Breakdown by File</h2><table><thead><tr><th>Name</th>
<th colspan=2 >Mutation Coverage</th></tr></thead><tbody>"""
reportEnd = """</tbody></table><footer><p style="font-size: small">Report generated by LittleDarwin {} </p>
</footer></body></html>""".format(self.ldVersion)
totalMutantCount = 0
survivedMutantCount = 0
breakdownFile = list()
for mutationResult in resultData:
survivedMutantCount += mutationResult[1]
totalMutantCount += mutationResult[2]
breakdownFile.append("<tr><td><a href=\"" + os.path.relpath(
os.path.join(os.path.dirname(reportPath), mutationResult[0], "index.html"),
os.path.dirname(reportPath)) + "\" >" + os.path.relpath(
os.path.join(os.path.dirname(reportPath), mutationResult[0]),
os.path.dirname(reportPath)) + "</a></td> <td> " +
("{:3.1f}%".format(100 - (mutationResult[1] / float(mutationResult[2]) * 100)))
+ " </td> <td> <div class=\"coverage_bar\"><div class=\"coverage_complete\" style=\"width:" +
("%d" % (100 - (mutationResult[1] / float(mutationResult[2]) * 100)))
+ "%\"></div><div class=\"coverage_legend\">" + str(mutationResult[2] - mutationResult[1])
+ "/" + str(mutationResult[2]) + "</div></div></td></tr>")
killedMutantCount = totalMutantCount - survivedMutantCount
projectOverallStats = "<tr><td>" + str(len(resultData)) + " </td> <td> " + ("%3.1f" % (float(
killedMutantCount) / totalMutantCount * 100)) \
+ " </td> <td> <div class=\"coverage_bar\"><div class=\"coverage_complete\" style=\"width:"\
+ ("%d" % (float(killedMutantCount) / totalMutantCount * 100)) \
+ "%\"></div><div class=\"coverage_legend\">" + str(killedMutantCount) + "/" \
+ str(totalMutantCount) + "</div></div></td></tr>"
reportOutput = list()
reportOutput.extend([reportBeginning, projectOverallStats, reportMiddle])
reportOutput.extend(breakdownFile)
reportOutput.append(reportEnd)
return '\n'.join(reportOutput)
[docs]
def generateHTMLReportPerFile(self, filePath, reportPath, survived, killed):
"""
Generates an HTML report for a single file.
:param filePath: The path to the file.
:type filePath: str
:param reportPath: The path to the report file.
:type reportPath: str
:param survived: A list of the names of the survived mutants.
:type survived: list
:param killed: A list of the names of the killed mutants.
:type killed: list
:return: The HTML report as a string.
:rtype: str
"""
def xstr(inputVar):
"""
:param inputVar:
:type inputVar:
:return:
:rtype:
"""
if inputVar is None:
return ''
else:
return str(inputVar)
self.database[filePath] = (survived, killed)
reportBeginning = """<!DOCTYPE html><html><head><title>LittleDarwin Mutation Coverage Report</title>
<style type='text/css'> body { font-family: "Carlito", "Calibri", "Helvetica Neue", sans-serif; }
table { border-collapse: collapse; } th, td { text-align: left; padding: 1em; }
th { background-color: #f0f0f0; color: black; } tr:hover { background-color: #b2daf5; }
a:link, a:visited { color: black; text-decoration: none; display: inline-block; }
a:hover, a:active { font-weight: bold; } .coverage_bar { display: inline-block; border-radius: 0.3em;
height: 1.1em; width: 9em; background: #ff8482; margin: 0 5px; vertical-align: middle;
border: 1px solid #888; position: relative } .coverage_complete { display: inline-block;
border-radius: 0.3em; height: 100%; background: #acff8b; float: left }
.coverage_legend { position: absolute; height: 100%; width: 100%; left: 0; top: 0; text-align: center }
</style></head><body><h1>LittleDarwin Mutation Coverage Report</h1><h2>File Summary</h2><table><thead><tr>
<th>Number of Mutants</th><th>Mutation Coverage</th></tr></thead><tbody>"""
reportMiddle = """<tr><td colspan=2 style="text-align:center"><a href="aggregate.html">Aggregate Report</a></td>
</tr></tbody></table><h2>Detailed List</h2><br><table><thead><tr><th>Survived Mutant</th>
<th>Build Output</th><th>Killed Mutant</th><th>Build Output</th></tr></thead><tbody>"""
reportEnd = """</tbody></table><footer><p style="font-size: small">Report generated by LittleDarwin %s </p>
</footer></body></html>""" % self.ldVersion
output = list()
joinedList = list()
if len(survived) > len(killed):
maxIndex = len(survived)
else:
maxIndex = len(killed)
assert isinstance(survived, list)
assert isinstance(killed, list)
for i in range(0, maxIndex):
try:
survivedItem = survived[i]
except IndexError as e:
survivedItem = None
try:
killedItem = killed[i]
except IndexError as e:
killedItem = None
joinedList.append([os.path.relpath(os.path.join(os.path.dirname(reportPath), survivedItem),
os.path.dirname(reportPath)) if survivedItem is not None else None,
survivedItem, os.path.relpath(
os.path.join(os.path.dirname(reportPath), os.path.splitext(survivedItem)[0] + ".txt"),
os.path.dirname(reportPath)) if survivedItem is not None else None,
os.path.splitext(survivedItem)[0] + ".txt" if survivedItem is not None else None,
os.path.relpath(os.path.join(os.path.dirname(reportPath), killedItem),
os.path.dirname(reportPath)) if killedItem is not None else None,
killedItem, os.path.relpath(
os.path.join(os.path.dirname(reportPath), os.path.splitext(killedItem)[0] + ".txt"),
os.path.dirname(reportPath)) if killedItem is not None else None,
os.path.splitext(killedItem)[0] + ".txt" if killedItem is not None else None])
fileOverallStats = "<tr><td>" + str(len(survived) + len(killed)) + " </td> <td> " + (
"{:3.1f}%".format(float(len(killed)) / float(len(survived) + len(
killed)) * 100)) + " <div class=\"coverage_bar\"><div class=\"coverage_complete\" style=\"width:" + (
"%d" % (float(len(killed)) / float(
len(survived) + len(killed)) * 100)) + "%\"></div><div class=\"coverage_legend\">" + str(
len(killed)) + "/" + str(len(killed) + len(survived)) + "</div></div></td></tr>"
for item in joinedList:
output.append(
"<tr><td><a href=\"" + xstr(item[0]) + "\">" + xstr(item[1]) + "</a></td> <td><a href=\"" + xstr(
item[2]) + "\">" + xstr(item[3]) + "</a></td><td><a href=\"" + xstr(item[4]) + "\">" + xstr(
item[5]) + "</a></td><td><a href=\"" + xstr(item[6]) + "\">" + xstr(item[7]) + "</a></td></tr>")
reportOutput = list()
reportOutput.extend([reportBeginning, fileOverallStats, reportMiddle])
reportOutput.extend(output)
reportOutput.append(reportEnd)
return '\n'.join(reportOutput)