From e3c0a4c67507a1be43771659b0cb6aa69a2ba494 Mon Sep 17 00:00:00 2001 From: Ole Henrik Dahle Date: Wed, 27 Feb 2019 13:19:51 +0100 Subject: [PATCH 01/12] Re-styled the colors and look of the table a bit. --- webui/ccov.css | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/webui/ccov.css b/webui/ccov.css index fed91e8..3e427bc 100644 --- a/webui/ccov.css +++ b/webui/ccov.css @@ -1,5 +1,5 @@ body { - font-family: sans-serif; + font-family: arial; } h1 { @@ -15,7 +15,6 @@ div.info-panel { div.info-panel + hr { clear: both; } - table#coveredtable { margin: 0 auto; min-width: 66%; @@ -23,17 +22,17 @@ table#coveredtable { th { font-weight: bold; - background-color: #6688D4; + background-color: #676767; color: white; text-align: center; - padding: 0 1em; + padding: 0.5em 1em; font-size: 110%; } td { - background-color: #DAE7FE; + background-color: #a6a6a6; min-width: 3.5em; - padding: 0 0.5em; + padding: 0.5em 0.5em; text-align: right; } @@ -41,19 +40,19 @@ td { td:first-child { text-align: left; } -tbody td:first-child { +tbody td:not(:first-child) { font-family: monospace; } /* Coverage colored backgrounds */ .lowcov { - background-color: #FEBAC2; + background-color: #e93939; } .mediumcov { - background-color: #FEF8B1; + background-color: #e7b416; } .highcov { - background-color: #C5FEBA; + background-color: #99c140; } /* File coverage data styles */ From ecb1c736369c58a3c36219cf4d86f7ab940a8dbc Mon Sep 17 00:00:00 2001 From: Ole Henrik Dahle Date: Wed, 27 Feb 2019 13:20:45 +0100 Subject: [PATCH 02/12] Specified link to parent directory, so that it works when browsing local files as well. --- uitemplates/directory.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uitemplates/directory.html b/uitemplates/directory.html index 61260bd..f4ba8e3 100644 --- a/uitemplates/directory.html +++ b/uitemplates/directory.html @@ -15,7 +15,7 @@

Code coverage report for ${directory}

Date compiled: ${date}
Graphical Overview | Detailed Report
- +
From 8eac4022ca4d0f40cb8de75e52f9dab79e0eb9ea Mon Sep 17 00:00:00 2001 From: Ole Henrik Dahle Date: Wed, 27 Feb 2019 13:21:26 +0100 Subject: [PATCH 03/12] Added option to specify custom medium / high limits, added some more help text. --- make_ui.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/make_ui.py b/make_ui.py index 0c661ca..05831c4 100755 --- a/make_ui.py +++ b/make_ui.py @@ -9,19 +9,27 @@ def main(argv): from optparse import OptionParser - o = OptionParser() + usage = "Usage: %prog [options] inputfile(s)" + o = OptionParser(usage) o.add_option('-o', '--output', dest="outdir", help="Directory to store all HTML files", metavar="DIRECTORY") o.add_option('-s', '--source-dir', dest="basedir", help="Base directory for source code", metavar="DIRECTORY") + o.add_option('-l', '--limits', dest="limits", + help="Custom limits for medium,high coverage") (opts, args) = o.parse_args(argv) if opts.outdir is None: print "Need to pass in -o!" sys.exit(1) + if len(args) < 2: + print "Need to specify at least one input file!" + sys.exit(1) + # Add in all the data cov = CoverageData() for lcovFile in args[1:]: + print "Reading coverage data from", lcovFile cov.addFromLcovFile(open(lcovFile, 'r')) # Make the output directory @@ -29,17 +37,18 @@ def main(argv): os.makedirs(opts.outdir) print ('Building UI...') - builder = UiBuilder(cov, opts.outdir, opts.basedir) + builder = UiBuilder(cov, opts.outdir, opts.basedir, opts.limits) builder.makeStaticOutput() builder.makeDynamicOutput() class UiBuilder(object): - def __init__(self, covdata, outdir, basedir): + def __init__(self, covdata, outdir, basedir, limits): self.data = covdata self.flatdata = self.data.getFlatData() self.outdir = outdir self.uidir = os.path.dirname(__file__) self.basedir = basedir + self.limits = limits self.relsrc = None self.tests = ['all'] @@ -154,6 +163,14 @@ def _readTemplate(self, name): def _makeDirectoryIndex(self, dirname, jsondata): # Utility method for printing out rows of the table + mediumLimit = 75.0 + highLimit = 90.0 + if self.limits: + values = self.limits.split(","); + if len(values) == 2: + mediumLimit = float(values[0]) + highLimit = float(values[1]) + def summary_string(lhs, jsondata): output = '' output += '' % lhs @@ -164,8 +181,8 @@ def summary_string(lhs, jsondata): output += '' else: ratio = 100.0 * hit / count - if ratio < 75.0: clazz = "lowcov" - elif ratio < 90.0: clazz = "mediumcov" + if ratio < mediumLimit: clazz = "lowcov" + elif ratio < highLimit: clazz = "mediumcov" else: clazz = "highcov" output += '' % ( clazz, hit, count, clazz, ratio) @@ -188,7 +205,7 @@ def summary_string(lhs, jsondata): def htmlname(json): if len(json['files']) > 0: - return json['name'] + return json['name'] + "/index.html" else: return json['name'] + '.html' tablestr = '\n'.join(summary_string( @@ -214,7 +231,6 @@ def htmlname(json): self._makeFileData(dirname, child['name'], child) def _makeFileData(self, dirname, filename, jsondata): - print 'Writing %s/%s.html' % (dirname, filename) htmltmp = self._readTemplate('file.html') parameters = {} From 43b7581a07cf0fe094d91d106b871df8f0eb6b3b Mon Sep 17 00:00:00 2001 From: Ole Henrik Dahle Date: Fri, 7 Jun 2019 14:03:39 +0200 Subject: [PATCH 04/12] Opening textual instead of graphical view for single files. --- uitemplates/coverage.html | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/uitemplates/coverage.html b/uitemplates/coverage.html index bff3d7a..df4f084 100644 --- a/uitemplates/coverage.html +++ b/uitemplates/coverage.html @@ -171,6 +171,7 @@ } function get_source_file(data) { + if(!data) return ''; if ("_path" in data) return data._path; if ('parent' in data) { @@ -210,7 +211,22 @@ while(d && d.parent != root) d = d.parent; } if (d) - reroot(d); + { + if (d.files.length > 0) + { + reroot(d); + } else + { + // Single file, open textual view + var url = window.location.href; + if (url.indexOf('?')) + { + url = url.substr(0,url.indexOf('?') -1); + } + var directory = url.substr(0,url.lastIndexOf("/")); + window.location.href = directory + "/" + d._path + ".html" + } + } }).transition().delay(2000).style("opacity", 1); nodes.order(); From e79e076794a0f86af1f1a85ec7d09fcf54b85b6d Mon Sep 17 00:00:00 2001 From: Ole Henrik Dahle Date: Wed, 16 Oct 2019 14:16:18 +0200 Subject: [PATCH 05/12] On behalf of Ann-Karin Kihle: Added support for generating reports for single classes. --- uitemplates/root_directory.html | 30 ++++++++++++++++++++++++++++++ uitemplates/single_test_file.html | 26 ++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 uitemplates/root_directory.html create mode 100644 uitemplates/single_test_file.html diff --git a/uitemplates/root_directory.html b/uitemplates/root_directory.html new file mode 100644 index 0000000..d289164 --- /dev/null +++ b/uitemplates/root_directory.html @@ -0,0 +1,30 @@ + + + +Code coverage for ${directory} + + + + + + +

Code coverage report for ${reponame}

+
+
Test: + +
+
Date compiled: ${date}
+
Graphical Overview | Detailed Report
+
+
%s0 / 0-%d / %d%.1f%%
+ + + + + ${tbody} + + + ${tfoot} +
FilenameLineFunctionBranch
+ + diff --git a/uitemplates/single_test_file.html b/uitemplates/single_test_file.html new file mode 100644 index 0000000..23c752f --- /dev/null +++ b/uitemplates/single_test_file.html @@ -0,0 +1,26 @@ + + + +Code coverage + + + + + +

Code coverage report for ${file}

+
+
Test: + +
+
Date compiled: ${date}
+
+ + + + + + ${tbody} + + + + From 5191c89327bf060497acaa4b1d09c1ad709924f2 Mon Sep 17 00:00:00 2001 From: Ole Henrik Dahle Date: Mon, 21 Oct 2019 13:21:00 +0200 Subject: [PATCH 06/12] On behalf of Ann-Karin Kihle: Added support for generating reports for single classes. --- make_ui.py | 29 ++++++++++++++++++++++------- uitemplates/coverage.html | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/make_ui.py b/make_ui.py index 05831c4..9a9d630 100755 --- a/make_ui.py +++ b/make_ui.py @@ -50,7 +50,7 @@ def __init__(self, covdata, outdir, basedir, limits): self.basedir = basedir self.limits = limits self.relsrc = None - self.tests = ['all'] + self.tests = [] def _loadGlobalData(self): json_data = self.buildJSONData(self.flatdata) @@ -135,7 +135,8 @@ def makeStaticOutput(self): def makeDynamicOutput(self): # Dump out JSON files json_data = self._loadGlobalData() - json.dump(json_data, open(os.path.join(self.outdir, 'all.json'), 'w')) + if 'all' in self.tests : + json.dump(json_data, open(os.path.join(self.outdir, 'all.json'), 'w')) for test in self.data.getTests(): small_data = self.data.getTestData(test) if len(small_data) == 0: @@ -187,8 +188,10 @@ def summary_string(lhs, jsondata): output += '' % ( clazz, hit, count, clazz, ratio) return output + '' - htmltmp = self._readTemplate('directory.html') - + if dirname: + htmltmp = self._readTemplate('directory.html') + else: + htmltmp = self._readTemplate('root_directory.html') jsondata['files'].sort(lambda x, y: cmp(x['name'], y['name'])) # Parameters for output @@ -202,6 +205,8 @@ def summary_string(lhs, jsondata): ('' % test) for test in self.tests) from datetime import date parameters['date'] = date.today().isoformat() + if not dirname: + parameters['reponame'] = os.getcwd()[os.getcwd().rfind('/')+1:len(os.getcwd())] def htmlname(json): if len(json['files']) > 0: @@ -231,12 +236,22 @@ def htmlname(json): self._makeFileData(dirname, child['name'], child) def _makeFileData(self, dirname, filename, jsondata): - htmltmp = self._readTemplate('file.html') - + if dirname: + if dirname == 'inc': + htmltmp = self._readTemplate('single_test_file.html') + else: + htmltmp = self._readTemplate('file.html') + else: + htmltmp = self._readTemplate('single_test_file.html') parameters = {} parameters['file'] = os.path.join(dirname, filename) parameters['directory'] = dirname - parameters['depth'] = '/'.join('..' for x in dirname.split('/')) + + if dirname: + parameters['depth'] = '/'.join('..' for x in dirname.split('/')) + else: + parameters['depth'] = '.' + parameters['testoptions'] = '\n'.join( '' % s for s in self.tests) from datetime import date diff --git a/uitemplates/coverage.html b/uitemplates/coverage.html index df4f084..2eebc18 100644 --- a/uitemplates/coverage.html +++ b/uitemplates/coverage.html @@ -59,7 +59,7 @@ .style("position", "relative") .style("width", width + "px") .style("height", height + "px"); - d3.json("all.json", loadJsonData); + d3.json(d3.select("#testsuite").node().value + ".json", loadJsonData); // Bind the coverage scale d3.select("#scale").selectAll("rect") From 7025c209bfc1b3d18b17ecfe1d4856729500ef21 Mon Sep 17 00:00:00 2001 From: Ole Henrik Dahle Date: Tue, 26 Oct 2021 11:14:59 +0200 Subject: [PATCH 07/12] Create README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..db6245a --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +Various code coverage scripts, originally used for the Mozilla (browser) project by Joshua Cranmer. +Forked from https://github.com/jcranmer/mozilla-coverage, since that project is no longer maintained. +The Python scripts rely on lcov and d3.js to produce colored tables and treemaps to illustrate code coverage. From 0efba0f16412cb48c40ef6c34feb4d1abbf4bb5e Mon Sep 17 00:00:00 2001 From: Ole Henrik Dahle Date: Tue, 26 Oct 2021 11:24:00 +0200 Subject: [PATCH 08/12] Added LICENSE file Chose ISC license since D3 library is included and must be attributed. --- LICENSE | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ea295f7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,16 @@ +ISC License + +Copyright (c) 2012-2015 Jason Cranmer, 2019-2021 Ole Henrik Dahle and Ann-Karin Kihle +Included D3 library is also under the ISC license, Copyright 2010-2021 Mike Bostock + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. From 8d08ae22e9085fdd2f9c84f44a2dbdee9a709982 Mon Sep 17 00:00:00 2001 From: Ole Henrik Dahle Date: Thu, 28 Oct 2021 08:16:22 +0200 Subject: [PATCH 09/12] Fixed Python 3 compatibility. --- ccov.py | 31 ++++++++++++++++++++++--------- make_ui.py | 26 ++++++++++++++++++-------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/ccov.py b/ccov.py index 4a43c90..6e2ea5b 100755 --- a/ccov.py +++ b/ccov.py @@ -6,6 +6,7 @@ import shutil import subprocess import tempfile +import sys def format_set_difference(a, b): if a == b: @@ -35,7 +36,7 @@ def add_line_hit(self, line, hitcount): def lines(self): '''Returns an iterator over (line #, hit count) for this file.''' - for i in xrange(len(self._lines)): + for i in range(len(self._lines)): count = self._lines[i] if count != -1: yield (i, count) @@ -53,8 +54,13 @@ def add_function_hit(self, name, hitcount, lineno=None): def functions(self): '''Returns an iterator over (function name, line #, hit count) for this file.''' - for func, fndata in self._funcs.iteritems(): - yield (func, fndata[0], fndata[1]) + if sys.version_info[0] == 2: + # Python 2 has iteritems, Python 3 has items + for func, fndata in self._funcs.iteritems(): + yield (func, fndata[0], fndata[1]) + else: + for func, fndata in self._funcs.items(): + yield (func, fndata[0], fndata[1]) def add_branch_hit(self, lineno, brno, targetid, count): '''Note that the brno'th branch on the line number going to the targetid @@ -65,10 +71,17 @@ def add_branch_hit(self, lineno, brno, targetid, count): def branches(self): '''Returns an iterator over (line #, branch #, [ids], [counts]) for this file.''' - for tup in self._branches.iteritems(): - items = tup[1].items() - items.sort() - yield (tup[0][0], tup[0][1], [x[0] for x in items], [x[1] for x in items]) + if sys.version_info[0] == 2: + # Python 2 has iteritems, Python 3 has items + for tup in self._branches.iteritems(): + items = tup[1].items() + items.sort() + yield (tup[0][0], tup[0][1], [x[0] for x in items], [x[1] for x in items]) + else: + for tup in self._branches.items(): + items = tup[1].items() + items.sort() + yield (tup[0][0], tup[0][1], [x[0] for x in items], [x[1] for x in items]) def write_lcov_output(self, fd): '''Writes the record for this file to the file descriptor in the LCOV @@ -201,7 +214,7 @@ def loadGcdaTree(self, testname, gcdaDir): gcnodata.add_to_coverage(self, testname, dirpath) return for dirpath, dirnames, filenames in os.walk(gcdaDir): - print 'Processing %s' % dirpath + print('Processing %s' % dirpath) gcda_files = filter(lambda f: f.endswith('.gcda'), filenames) gcno_files = [f[:-2] + 'no' for f in gcda_files] filepairs = [(da, no) for (da, no) in zip(gcda_files, gcno_files) @@ -301,7 +314,7 @@ def __init__(self, basedir, gcovtool='gcov', table={}): self.table = table def loadDirectory(self, directory, gcda_files): - print 'Processing %s' % directory + print('Processing %s' % directory) gcda_files = map(lambda f: os.path.join(directory, f), gcda_files) gcovdir = tempfile.mktemp("gcovdir") os.mkdir(gcovdir) diff --git a/make_ui.py b/make_ui.py index 9a9d630..66d5004 100755 --- a/make_ui.py +++ b/make_ui.py @@ -1,6 +1,5 @@ #!/usr/bin/python -import cgi import json import os import shutil @@ -19,17 +18,17 @@ def main(argv): help="Custom limits for medium,high coverage") (opts, args) = o.parse_args(argv) if opts.outdir is None: - print "Need to pass in -o!" + print("Need to pass in -o!") sys.exit(1) if len(args) < 2: - print "Need to specify at least one input file!" + print("Need to specify at least one input file!") sys.exit(1) # Add in all the data cov = CoverageData() for lcovFile in args[1:]: - print "Reading coverage data from", lcovFile + print("Reading coverage data from", lcovFile) cov.addFromLcovFile(open(lcovFile, 'r')) # Make the output directory @@ -192,7 +191,7 @@ def summary_string(lhs, jsondata): htmltmp = self._readTemplate('directory.html') else: htmltmp = self._readTemplate('root_directory.html') - jsondata['files'].sort(lambda x, y: cmp(x['name'], y['name'])) + jsondata['files'].sort(key=lambda x: x['name']) # Parameters for output parameters = {} @@ -236,6 +235,12 @@ def htmlname(json): self._makeFileData(dirname, child['name'], child) def _makeFileData(self, dirname, filename, jsondata): + # Python 2 / 3 compatibility fix + try: + import html + except ImportError: + import cgi as html + if dirname: if dirname == 'inc': htmltmp = self._readTemplate('single_test_file.html') @@ -265,8 +270,13 @@ def _makeFileData(self, dirname, filename, jsondata): '') parameters['data'] = '' else: - with open(srcfile, 'r') as fd: - srclines = fd.readlines() + if sys.version_info[0] == 2:' + # Python 2 version of open + with open(srcfile, mode="r") as fd: + srclines = fd.readlines() + else: + with open(srcfile, mode="r", encoding="utf-8", errors="ignore") as fd: + srclines = fd.readlines() flatdata = self.flatdata[filekey] del self.flatdata[filekey] # Scavenge memory we don't need anymore. @@ -309,7 +319,7 @@ def _makeFileData(self, dirname, filename, jsondata): outlines.append((' ' + '\n' ) % (covstatus, lineno, brcount, linecount, - cgi.escape(line.rstrip()))) + html.escape(line.rstrip()))) lineno += 1 parameters['tbody'] = ''.join(outlines) From 945f1da0b523818ec1152962505a086cdf2c731c Mon Sep 17 00:00:00 2001 From: Ole Henrik Dahle Date: Thu, 12 Jan 2023 09:29:04 +0100 Subject: [PATCH 10/12] Python 3 bugfixes. --- ccov.py | 12 ++++++------ make_ui.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ccov.py b/ccov.py index 6e2ea5b..e16831a 100755 --- a/ccov.py +++ b/ccov.py @@ -60,7 +60,7 @@ def functions(self): yield (func, fndata[0], fndata[1]) else: for func, fndata in self._funcs.items(): - yield (func, fndata[0], fndata[1]) + yield (func, fndata[0], fndata[1]) def add_branch_hit(self, lineno, brno, targetid, count): '''Note that the brno'th branch on the line number going to the targetid @@ -81,7 +81,7 @@ def branches(self): for tup in self._branches.items(): items = tup[1].items() items.sort() - yield (tup[0][0], tup[0][1], [x[0] for x in items], [x[1] for x in items]) + yield (tup[0][0], tup[0][1], [x[0] for x in items], [x[1] for x in items]) def write_lcov_output(self, fd): '''Writes the record for this file to the file descriptor in the LCOV @@ -214,7 +214,7 @@ def loadGcdaTree(self, testname, gcdaDir): gcnodata.add_to_coverage(self, testname, dirpath) return for dirpath, dirnames, filenames in os.walk(gcdaDir): - print('Processing %s' % dirpath) + print("Processing ", dirpath) gcda_files = filter(lambda f: f.endswith('.gcda'), filenames) gcno_files = [f[:-2] + 'no' for f in gcda_files] filepairs = [(da, no) for (da, no) in zip(gcda_files, gcno_files) @@ -314,7 +314,7 @@ def __init__(self, basedir, gcovtool='gcov', table={}): self.table = table def loadDirectory(self, directory, gcda_files): - print('Processing %s' % directory) + print("Processing ", directory) gcda_files = map(lambda f: os.path.join(directory, f), gcda_files) gcovdir = tempfile.mktemp("gcovdir") os.mkdir(gcovdir) @@ -402,7 +402,7 @@ def main(argv): coverage = CoverageData() if opts.more_files == None: opts.more_files = [] for lcovFile in opts.more_files: - print >> sys.stderr, "Reading file %s" % lcovFile + sys.stderr.write("Reading file ", lcovFile) fd = open(lcovFile, 'r') coverage.addFromLcovFile(fd) @@ -417,7 +417,7 @@ def main(argv): coverage.filterFilesByGlob(opts.extract_glob) # Store it to output if opts.outfile != None: - print >> sys.stderr, "Writing to file %s" % opts.outfile + sys.stderr.write("Writing to file", opts.outfile) outfd = open(opts.outfile, 'w') else: outfd = sys.stdout diff --git a/make_ui.py b/make_ui.py index 66d5004..6616f7d 100755 --- a/make_ui.py +++ b/make_ui.py @@ -270,7 +270,7 @@ def _makeFileData(self, dirname, filename, jsondata): '') parameters['data'] = '' else: - if sys.version_info[0] == 2:' + if sys.version_info[0] == 2: # Python 2 version of open with open(srcfile, mode="r") as fd: srclines = fd.readlines() From c44b056ee4174bc6a21075a8d2e63e49c9b17412 Mon Sep 17 00:00:00 2001 From: Ole Henrik Dahle Date: Thu, 12 Jan 2023 09:33:28 +0100 Subject: [PATCH 11/12] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index ea295f7..9cbe9de 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ ISC License -Copyright (c) 2012-2015 Jason Cranmer, 2019-2021 Ole Henrik Dahle and Ann-Karin Kihle +Copyright (c) 2012-2015 Jason Cranmer, 2019-2023 Ole Henrik Dahle and Ann-Karin Kihle Included D3 library is also under the ISC license, Copyright 2010-2021 Mike Bostock Permission to use, copy, modify, and/or distribute this software for any From b9844f7440aef192ea32112bfb180d0bffa670e3 Mon Sep 17 00:00:00 2001 From: Ole Henrik Dahle Date: Tue, 2 Sep 2025 11:35:50 +0200 Subject: [PATCH 12/12] Better web design: Switched from HR to DIV for the info container, modernized CSS a bit. Updated copyright statement. --- LICENSE | 2 +- uitemplates/directory.html | 4 +- uitemplates/root_directory.html | 4 +- webui/ccov.css | 77 +++++++++++++++++++++++++-------- 4 files changed, 65 insertions(+), 22 deletions(-) diff --git a/LICENSE b/LICENSE index 9cbe9de..e67ab81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ ISC License -Copyright (c) 2012-2015 Jason Cranmer, 2019-2023 Ole Henrik Dahle and Ann-Karin Kihle +Copyright (c) 2012-2015 Jason Cranmer, 2019-2025 Ole Henrik Dahle and Ann-Karin Kihle Included D3 library is also under the ISC license, Copyright 2010-2021 Mike Bostock Permission to use, copy, modify, and/or distribute this software for any diff --git a/uitemplates/directory.html b/uitemplates/directory.html index f4ba8e3..adbc727 100644 --- a/uitemplates/directory.html +++ b/uitemplates/directory.html @@ -9,14 +9,14 @@

Code coverage report for ${directory}

-
+
Test:
Date compiled: ${date}
Graphical Overview | Detailed Report
-
+
Branch dataLine dataSource code
%d / %d%.1f%%
File could not be found
%d%s%s%s
File could not be found
diff --git a/uitemplates/root_directory.html b/uitemplates/root_directory.html index d289164..29db278 100644 --- a/uitemplates/root_directory.html +++ b/uitemplates/root_directory.html @@ -9,13 +9,13 @@

Code coverage report for ${reponame}

-
+
>
Test:
Date compiled: ${date}
Graphical Overview | Detailed Report
-
+
FilenameLineFunctionBranch
diff --git a/webui/ccov.css b/webui/ccov.css index 3e427bc..0e5955e 100644 --- a/webui/ccov.css +++ b/webui/ccov.css @@ -1,38 +1,65 @@ body { - font-family: arial; + font-family: Arial, sans-serif; } h1 { text-align: center; } -/* Styles for the region between the two hrs */ -div.info-panel { - float: left; - margin-bottom: 0.5em; - margin-right: 2em; +a { + color: #3498db; /* Lighter blue for links */ + text-decoration: none; +} + +/* Styles for the info panel on top of each page */ +div.info-container { + display: flex; + justify-content: center; + align-items: center; + background-color: #4a4a4a; /* Dark gray background */ + margin-bottom: 1em; + width: 66%; /* Align width with coveredtable */ + margin: 0 auto; /* Center the container */ + border-radius: 8px; } -div.info-panel + hr { - clear: both; + +div.info-panel { + flex: 1; + margin: 0 1em; + padding: 1em; + background-color: #4a4a4a; /* Dark gray background */ + border-radius: 8px; + text-align: center; + color: white; /* Ensure text is visible on dark background */ } + + table#coveredtable { margin: 0 auto; min-width: 66%; + border-collapse: collapse; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + border-radius: 8px; + overflow: hidden; +} + +th, td { + border: 1px solid #ccc; /* Added border to cells */ } th { font-weight: bold; - background-color: #676767; + background-color: #4a4a4a; color: white; text-align: center; - padding: 0.5em 1em; + padding: 0.75em 1em; font-size: 110%; } td { - background-color: #a6a6a6; + background-color: #d9d9d9; min-width: 3.5em; - padding: 0.5em 0.5em; + padding: 0.75em 0.75em; text-align: right; } @@ -40,46 +67,62 @@ td { td:first-child { text-align: left; } + tbody td:not(:first-child) { font-family: monospace; } /* Coverage colored backgrounds */ .lowcov { - background-color: #e93939; + background-color: #ff6b6b; } + .mediumcov { - background-color: #e7b416; + background-color: #f7b731; } + .highcov { - background-color: #99c140; + background-color: #20bf6b; } /* File coverage data styles */ table#filetable { font-family: monospace; border-spacing: 0; + width: 100%; + border-collapse: collapse; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + border-radius: 8px; + overflow: hidden; } + table#filetable td, table#filetable th { background-color: inherit; color: black; - padding: 0 0.5em; + padding: 0.75em; white-space: pre; vertical-align: top; + border: 1px solid #ccc; /* Added border to cells */ } + table#filetable td:first-child { - background-color: #EFE383; + background-color: #f9e79f; text-align: right; } + table#filetable td:not(:first-child):not(:last-child) { text-align: right; border-right: solid black 1px; min-width: 5em; } + table#filetable th { font-size: 90%; text-align: center; + background-color: #4a4a4a; + color: white; } + table#filetable tr > *:last-child { text-align: left; }
FilenameLineFunctionBranch