├── .gitignore ├── .travis.yml ├── LICENSE ├── Readme.md ├── attachments ├── 43c4b9c5.png ├── a74b8ce4.png └── b5193c3c.png ├── coco.html ├── coco.nim ├── coco.nimble ├── coverage ├── amber.png ├── emerald.png ├── gcov.css ├── glass.png ├── index-sort-f.html ├── index-sort-l.html ├── index.html ├── nim-coverage │ ├── coco.nim.func-sort-c.html │ ├── coco.nim.func.html │ ├── coco.nim.gcov.html │ ├── index-sort-f.html │ ├── index-sort-l.html │ └── index.html ├── ruby.png ├── snow.png └── updown.png ├── lcov.info └── tests ├── compilation.nim ├── foo.nim └── report ├── cleanup_past_report.nim └── generate_coverage.nim /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/macos 3 | 4 | ### macOS ### 5 | # General 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | # Thumbnails 14 | ._* 15 | 16 | # Files that might appear in the root of a volume 17 | .DocumentRevisions-V100 18 | .fseventsd 19 | .Spotlight-V100 20 | .TemporaryItems 21 | .Trashes 22 | .VolumeIcon.icns 23 | .com.apple.timemachine.donotpresent 24 | 25 | # Directories potentially created on remote AFP share 26 | .AppleDB 27 | .AppleDesktop 28 | Network Trash Folder 29 | Temporary Items 30 | .apdisk 31 | 32 | ### Nim ### 33 | nimcache/ 34 | 35 | # Binary 36 | coco 37 | 38 | # End of https://www.gitignore.io/api/macos -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | before_install: 5 | - docker pull nimlang/nim 6 | script: 7 | - docker run nimlang/nim nim --version 8 | - docker run -v "$(pwd):/project" -w /project nimlang/nim sh -c "nimble install -y && nimble test" 9 | - docker run -v "$(pwd):/project" -w /project nimlang/nim sh -c "nimble coverage" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Samuel Roy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ![43c4b9c5.png](attachments/43c4b9c5.png) 2 | 3 | > Code coverage for Nim using LCOV. 4 | ___ 5 | 6 | ## Features 7 | 8 | * Line & Branch coverage support 9 | * Forwards nim compiler options 10 | * Works out-of-the box with Nim unittests 11 | * API + CLI 12 | * HTML report 13 | 14 | 15 | ## Installation & usage 16 | 17 | Install using [Nimble](https://github.com/nim-lang/nimble): 18 | 19 | ```shell 20 | nimble install coco 21 | ``` 22 | 23 | ### CLI 24 | 25 | Generate a code coverage report: 26 | 27 | ```shell 28 | coco --target "tests/**/*.nim" --cov '!tests' --compiler="--hints:off" 29 | ``` 30 | 31 | The flag `--target` tells Coco to run and compile nim files in the `tests/` directory (and subdirectories if any). 32 | 33 | The flag `--cov` is helpful for extracting or removing data from the file `*.info`. Here we don't want code coverage for our tests, only for our main library `coco`. 34 | 35 | The flag `--compiler` forwards its value directly to the Nim compiler. 36 | 37 | **To get the full list of available flags, type in your shell `coco -h`** 38 | ___ 39 | By default, Coco will generate a `lcov.info` file on your project root folder and create a report under `coverage/`. 40 | 41 | To check your report, open the file `coverage/index.html`in your browser: 42 | 43 | ![a74b8ce4.png](attachments/a74b8ce4.png) 44 | 45 | ## VScode visualization 46 | 47 | Install [Coverage Gutters](https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters). 48 | 49 | This nice plugin will map your coverage to your source code right into VScode: 50 | 51 | ![b5193c3c.png](attachments/b5193c3c.png) 52 | 53 | ### Library 54 | 55 | import and use: 56 | 57 | ```nim 58 | import coco 59 | 60 | discard coverage(target = "tests/foo.nim", branch = true) 61 | 62 | ``` 63 | 64 | You can find the full documentation here: [API Coco](https://samuelroy.github.io/coco/) 65 | 66 | 67 | ## Tests 68 | 69 | This task has only been tested under MacOSX. Feel free to open a pull request to share your experiments to code coverage Nim projects. 70 | 71 | ## TODO 72 | 73 | - [x] Support for subdirectories in tests/ 74 | - [ ] Coveralls.io integration 75 | 76 | ## Dependencies 77 | 78 | [Linux Test Project - Coverage » lcov](http://ltp.sourceforge.net/coverage/lcov.php) 79 | -------------------------------------------------------------------------------- /attachments/43c4b9c5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binhonglee/coco/0d7b08c9acfd492db19538aceb71901da9e47def/attachments/43c4b9c5.png -------------------------------------------------------------------------------- /attachments/a74b8ce4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binhonglee/coco/0d7b08c9acfd492db19538aceb71901da9e47def/attachments/a74b8ce4.png -------------------------------------------------------------------------------- /attachments/b5193c3c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binhonglee/coco/0d7b08c9acfd492db19538aceb71901da9e47def/attachments/b5193c3c.png -------------------------------------------------------------------------------- /coco.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Module coco 20 | 1201 | 1202 | 1203 | 1204 | 1217 | 1218 | 1219 | 1220 |
1221 |
1222 |

Module coco

1223 |
1224 |
1225 | 1229 |
1230 | Search: 1232 |
1233 |
1234 | Group by: 1235 | 1239 |
1240 | 1265 | 1266 |
1267 |
1268 |
1269 |

This module is the API behind the binary Coco.

Coco makes line or branch code coverage for Nim a breeze.
1270 |
  • Depends on LCOV
  • 1271 |
  • Generates a nice looking HTML report
  • 1272 |
  • Works with visualization tools like Coverage Gutters on VSCode.
  • 1273 |
1274 |
1275 |
1276 |

1277 |
1278 |

Procs

1279 |
1280 |
proc get_cache_folder(filename, nimcache: string; increment = 0): string {...}{.raises: [],
1281 |     tags: [].}
1282 |
1283 | 1284 | 1285 |
1286 |
proc compile(target = "tests/**/*.nim"; nimcache = "nimcache"; verbose = false;
1287 |             options = "") {...}{.raises: [OSError, OSError, Exception, RegexError,
1288 |                                  GlobSyntaxError, OSError, Exception, RegexError,
1289 |                                  GlobSyntaxError],
1290 |                          tags: [ReadDirEffect, RootEffect, ExecIOEffect].}
1291 |
1292 |
Compiles Nim files in coverage mode
1293 |
  • target should be a Glob with a .nim extension
  • 1294 |
1295 |
1296 |
1297 | 1298 | 1299 |
1300 |
proc reset_coverage(source = "lcov.info"; path = "coverage"; nimcache = "nimcache") {...}{.
1301 |     raises: [], tags: [ExecIOEffect].}
1302 |
1303 |
Removes everything generated by a past code coverage generation:
1304 |
  • Nimcache folder
  • 1305 |
  • .info
  • 1306 |
  • Code coverage report folder
  • 1307 |
1308 |
1309 |
1310 | 1311 | 1312 |
1313 |
proc trace(target: string) {...}{.raises: [OSError, OSError, Exception, RegexError,
1314 |                                   GlobSyntaxError, OSError, Exception, RegexError,
1315 |                                   GlobSyntaxError],
1316 |                           tags: [ReadDirEffect, RootEffect, ExecIOEffect].}
1317 |
1318 |
Runs the compiled Nim files to produce coverage informations.
1319 |
  • target should be a Glob with a .nim extension.
  • 1320 |
1321 |
1322 |
1323 | 1324 | 1325 |
1326 |
proc cleanup_report(fileinfo = "lcov.info"; cov: string; verbose = false; branch = false) {...}{.raises: [
1327 |     ValueError, OSError, OSError, Exception, RegexError, GlobSyntaxError, OSError,
1328 |     Exception, RegexError, GlobSyntaxError, OSError, Exception, OSError, Exception,
1329 |     RegexError, GlobSyntaxError], tags: [ExecIOEffect, ReadDirEffect, RootEffect].}
1330 |
1331 |
Keeps only relevant coverage informations
1332 |
Without any cleanup, your code coverage report will include standard libraries, tests and so on.
1333 |
1334 | 1335 | 1336 |
1337 |
proc genhtml(source = "lcov.info"; path = "coverage"; verbose = false; branch = false) {...}{.
1338 |     raises: [], tags: [ExecIOEffect].}
1339 |
1340 | Generates the HTML code coverage report from a .info file generated by LCOV 1341 | 1342 |
1343 |
proc coverage(target = "tests/**/*.nim"; cov = "!tests"; verbose = false; branch = false;
1344 |              nimcache = "nimcache"; report_source = "lcov.info";
1345 |              report_path = "coverage"; compiler = ""): int {...}{.
1346 |     raises: [OSError, Exception, RegexError, GlobSyntaxError, ValueError],
1347 |     tags: [ExecIOEffect, ReadDirEffect, RootEffect].}
1348 |
1349 |
1350 |
Code coverage for Nim:
1351 |
  1. Clean up past reports
  2. 1352 |
  3. Compile nim files in coverage mode
  4. 1353 |
  5. Run the the executables
  6. 1354 |
  7. Capture, produce and cleanup LCOV .info file
  8. 1355 |
  9. Generate the HTML report
  10. 1356 |
1357 |
1358 |
1359 | 1360 | 1361 |
1362 | 1363 |
1364 | 1365 |
1366 |
1367 | 1368 |
1369 | 1374 |
1375 |
1376 |
1377 | 1378 | 1379 | 1380 | -------------------------------------------------------------------------------- /coco.nim: -------------------------------------------------------------------------------- 1 | ## This module is the API behind the binary Coco. 2 | ## 3 | ## Coco makes line or branch code coverage for Nim a breeze. 4 | ## - Depends on LCOV 5 | ## - Generates a nice looking HTML report 6 | ## - Works with visualization tools like Coverage Gutters on VSCode. 7 | 8 | import glob, strutils, os, strformat, sequtils 9 | 10 | const nimcache_default = "nimcache" 11 | const fileinfo = "lcov.info" 12 | const report_path = "coverage" 13 | 14 | # Workaround for #12376 (https://github.com/nim-lang/Nim/issues/12376) 15 | const generated_not_to_break_here = "generated_not_to_break_here" 16 | 17 | proc exec(command: string) = 18 | ## Wrapper around execShellCmd that exits if the command fail 19 | doAssert execShellCmd(command) == 0, "command failed: " & command 20 | 21 | proc get_cache_folder*(filename, nimcache: string, increment=0): string = 22 | fmt"{nimcache}/{filename}_{increment}_cov" 23 | 24 | proc compile*(target="tests/**/*.nim", nimcache=nimcache_default, verbose=false, options= "") = 25 | ## Compiles Nim files in coverage mode 26 | ## - target should be a Glob with a .nim extension 27 | var nim_args = "--hints:off" 28 | if verbose: 29 | nim_args = "" 30 | if options.len > 0: 31 | nim_args &= fmt" {options}" 32 | 33 | var i = 0 # used to avoid name folder conflicts 34 | for nimfile in walkGlob(target): 35 | var cache_folder = 36 | nimfile 37 | .extractFilename() 38 | .get_cache_folder(nimcache, i) 39 | 40 | exec(fmt"nim {nim_args} --nimcache={cache_folder} --debugger:native --passC:--coverage --passL:--coverage c " & nimfile) 41 | i.inc() 42 | 43 | proc reset_coverage*(source=fileinfo, path=report_path, nimcache=nimcache_default) = 44 | ## Removes everything generated by a past code coverage generation: 45 | ## - Nimcache folder 46 | ## - .info 47 | ## - Code coverage report folder 48 | try: 49 | removeDir(nimcache) 50 | removeDir(path) 51 | removeFile(source) 52 | except OSError: 53 | echo "No past code coverage to clean up." 54 | 55 | 56 | proc trace*(target: string) = 57 | ## Runs the compiled Nim files to produce coverage informations. 58 | ## - target should be a Glob with a .nim extension. 59 | 60 | for nimfile in walkGlob(target): 61 | var bin = nimfile 62 | removeSuffix(bin, ".nim") 63 | exec(fmt"./{bin}") 64 | # cleanup after execution 65 | removeFile(bin) 66 | 67 | proc build_lcov_args(verbose=false, branch=false): string = 68 | ## Simple LCOV arguments wrapper 69 | var lcov_args = "--quiet" 70 | if verbose: 71 | lcov_args = "" 72 | if branch: 73 | lcov_args &= " --rc lcov_branch_coverage=1" 74 | lcov_args 75 | 76 | proc cleanup_report*(fileinfo = fileinfo, cov: string, verbose=false, branch=false) = 77 | ## Keeps only relevant coverage informations 78 | ## Without any cleanup, your code coverage report will include standard libraries, tests and so on. 79 | 80 | let lcov_args = build_lcov_args(verbose, branch) 81 | const options: GlobOptions = {GlobOption.Directories, GlobOption.Absolute} 82 | 83 | # Remove standard lib and nimble pkgs informations 84 | let currentFolder = absolutePath("") 85 | exec(fmt"""lcov {lcov_args} --extract {fileinfo} "{currentFolder}*" -o {fileinfo}""") 86 | exec(fmt"""lcov {lcov_args} --remove {fileinfo} "{currentFolder}/{generated_not_to_break_here}" -o {fileinfo}""") 87 | 88 | for pattern in cov.split(","): 89 | if pattern.startsWith("!"): 90 | var pattern_noprefix = pattern 91 | removePrefix(pattern_noprefix, "!") 92 | if existsFile(pattern_noprefix): 93 | exec(fmt"""lcov {lcov_args} --remove {fileinfo} "{currentFolder}/{pattern_noprefix}" -o {fileinfo}""") 94 | for path in walkGlob(pattern_noprefix, "", options): 95 | exec(fmt"""lcov {lcov_args} --remove {fileinfo} "{path}*" -o {fileinfo}""") 96 | else: 97 | for path in walkGlob(pattern, "", options): 98 | exec(fmt"""lcov {lcov_args} --extract {fileinfo} "{path}*" -o {fileinfo}""") 99 | 100 | proc genhtml*(source=fileinfo, path=report_path, verbose=false, branch=false) = 101 | ## Generates the HTML code coverage report from a .info file generated by LCOV 102 | var lcov_args = "--quiet" 103 | if verbose: 104 | lcov_args = "" 105 | if branch: 106 | lcov_args &= " --branch-coverage" 107 | ## Generate LCOV Html report 108 | exec(fmt"genhtml {lcov_args} --legend -o {path} {source}") 109 | 110 | proc coverage*(target="tests/**/*.nim", cov="!tests", verbose=false, branch=false, nimcache=nimcache_default, report_source=fileinfo, report_path=report_path, compiler="", gen_html=true): int = 111 | ## ____ 112 | ## 113 | ## Code coverage for Nim: 114 | ## 1. Clean up past reports 115 | ## 2. Compile nim files in coverage mode 116 | ## 3. Run the the executables 117 | ## 4. Capture, produce and cleanup LCOV .info file 118 | ## 5. Generate the HTML report 119 | ## 120 | reset_coverage(report_source, report_path, nimcache) 121 | 122 | let targets = target.split(",") 123 | for target in targets: 124 | compile(target, nimcache, verbose, compiler) 125 | 126 | let lcov_args = build_lcov_args(verbose, branch) 127 | writeFile(generated_not_to_break_here, "") 128 | 129 | exec(fmt"lcov {lcov_args} --base-directory . --directory {nimcache} --zerocounters -q") 130 | 131 | echo "Running tests..." 132 | for target in targets: 133 | trace(target) 134 | 135 | echo "Preparing coverage report..." 136 | exec(fmt"lcov {lcov_args} --base-directory . --directory {nimcache} -c -o {report_source}") 137 | removeFile(generated_not_to_break_here) 138 | 139 | echo "Cleaning up report..." 140 | cleanup_report(report_source, cov, verbose, branch) 141 | 142 | if gen_html: 143 | echo "Generating html..." 144 | genhtml(report_source, report_path, verbose, branch) 145 | 146 | result = 0 147 | 148 | 149 | when isMainModule: 150 | import cligen 151 | dispatch(coverage, 152 | help = { 153 | "target": "Nim files to compile and run in coverage mode. Direct path or glob patterns.", 154 | "cov": "Path to folders and files you want in your code coverage report. Default takes your current directory and excludes tests/ folder. Support glob patterns. ", 155 | "verbose": "Displays all traces coming from LCOV and Nim", 156 | "branch": "Enables LCOV branch code coverage mode", 157 | "nimcache": "Nimcache path used by the Nim compiler", 158 | "report_source": "Path used by LCOV to generate the file .info", 159 | "report_path": "Folder path where the HTML code coverage report will be created", 160 | "compiler": "Forward your parameter(s) to the Nim compiler", 161 | "gen_html": "Create a html readable version of the code coverage" 162 | }, 163 | short = { 164 | "report_source": 's', 165 | "report_path": 'p' 166 | } 167 | ) -------------------------------------------------------------------------------- /coco.nimble: -------------------------------------------------------------------------------- 1 | import strutils 2 | 3 | # Package 4 | 5 | version = "0.0.3" 6 | author = "Samuel Roy" 7 | description = "Code coverage with line and branch support for Nim" 8 | license = "MIT" 9 | 10 | installFiles= @["coco.nim"] 11 | bin = @["coco"] 12 | 13 | # Deps 14 | 15 | requires "nim >= 0.19.0" 16 | requires "cligen" 17 | requires "glob" 18 | 19 | task coverage, "Generate code coverage report": 20 | echo "Generate code coverage report" 21 | exec "./coco" 22 | 23 | -------------------------------------------------------------------------------- /coverage/amber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binhonglee/coco/0d7b08c9acfd492db19538aceb71901da9e47def/coverage/amber.png -------------------------------------------------------------------------------- /coverage/emerald.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binhonglee/coco/0d7b08c9acfd492db19538aceb71901da9e47def/coverage/emerald.png -------------------------------------------------------------------------------- /coverage/gcov.css: -------------------------------------------------------------------------------- 1 | /* All views: initial background and text color */ 2 | body 3 | { 4 | color: #000000; 5 | background-color: #FFFFFF; 6 | } 7 | 8 | /* All views: standard link format*/ 9 | a:link 10 | { 11 | color: #284FA8; 12 | text-decoration: underline; 13 | } 14 | 15 | /* All views: standard link - visited format */ 16 | a:visited 17 | { 18 | color: #00CB40; 19 | text-decoration: underline; 20 | } 21 | 22 | /* All views: standard link - activated format */ 23 | a:active 24 | { 25 | color: #FF0040; 26 | text-decoration: underline; 27 | } 28 | 29 | /* All views: main title format */ 30 | td.title 31 | { 32 | text-align: center; 33 | padding-bottom: 10px; 34 | font-family: sans-serif; 35 | font-size: 20pt; 36 | font-style: italic; 37 | font-weight: bold; 38 | } 39 | 40 | /* All views: header item format */ 41 | td.headerItem 42 | { 43 | text-align: right; 44 | padding-right: 6px; 45 | font-family: sans-serif; 46 | font-weight: bold; 47 | vertical-align: top; 48 | white-space: nowrap; 49 | } 50 | 51 | /* All views: header item value format */ 52 | td.headerValue 53 | { 54 | text-align: left; 55 | color: #284FA8; 56 | font-family: sans-serif; 57 | font-weight: bold; 58 | white-space: nowrap; 59 | } 60 | 61 | /* All views: header item coverage table heading */ 62 | td.headerCovTableHead 63 | { 64 | text-align: center; 65 | padding-right: 6px; 66 | padding-left: 6px; 67 | padding-bottom: 0px; 68 | font-family: sans-serif; 69 | font-size: 80%; 70 | white-space: nowrap; 71 | } 72 | 73 | /* All views: header item coverage table entry */ 74 | td.headerCovTableEntry 75 | { 76 | text-align: right; 77 | color: #284FA8; 78 | font-family: sans-serif; 79 | font-weight: bold; 80 | white-space: nowrap; 81 | padding-left: 12px; 82 | padding-right: 4px; 83 | background-color: #DAE7FE; 84 | } 85 | 86 | /* All views: header item coverage table entry for high coverage rate */ 87 | td.headerCovTableEntryHi 88 | { 89 | text-align: right; 90 | color: #000000; 91 | font-family: sans-serif; 92 | font-weight: bold; 93 | white-space: nowrap; 94 | padding-left: 12px; 95 | padding-right: 4px; 96 | background-color: #A7FC9D; 97 | } 98 | 99 | /* All views: header item coverage table entry for medium coverage rate */ 100 | td.headerCovTableEntryMed 101 | { 102 | text-align: right; 103 | color: #000000; 104 | font-family: sans-serif; 105 | font-weight: bold; 106 | white-space: nowrap; 107 | padding-left: 12px; 108 | padding-right: 4px; 109 | background-color: #FFEA20; 110 | } 111 | 112 | /* All views: header item coverage table entry for ow coverage rate */ 113 | td.headerCovTableEntryLo 114 | { 115 | text-align: right; 116 | color: #000000; 117 | font-family: sans-serif; 118 | font-weight: bold; 119 | white-space: nowrap; 120 | padding-left: 12px; 121 | padding-right: 4px; 122 | background-color: #FF0000; 123 | } 124 | 125 | /* All views: header legend value for legend entry */ 126 | td.headerValueLeg 127 | { 128 | text-align: left; 129 | color: #000000; 130 | font-family: sans-serif; 131 | font-size: 80%; 132 | white-space: nowrap; 133 | padding-top: 4px; 134 | } 135 | 136 | /* All views: color of horizontal ruler */ 137 | td.ruler 138 | { 139 | background-color: #6688D4; 140 | } 141 | 142 | /* All views: version string format */ 143 | td.versionInfo 144 | { 145 | text-align: center; 146 | padding-top: 2px; 147 | font-family: sans-serif; 148 | font-style: italic; 149 | } 150 | 151 | /* Directory view/File view (all)/Test case descriptions: 152 | table headline format */ 153 | td.tableHead 154 | { 155 | text-align: center; 156 | color: #FFFFFF; 157 | background-color: #6688D4; 158 | font-family: sans-serif; 159 | font-size: 120%; 160 | font-weight: bold; 161 | white-space: nowrap; 162 | padding-left: 4px; 163 | padding-right: 4px; 164 | } 165 | 166 | span.tableHeadSort 167 | { 168 | padding-right: 4px; 169 | } 170 | 171 | /* Directory view/File view (all): filename entry format */ 172 | td.coverFile 173 | { 174 | text-align: left; 175 | padding-left: 10px; 176 | padding-right: 20px; 177 | color: #284FA8; 178 | background-color: #DAE7FE; 179 | font-family: monospace; 180 | } 181 | 182 | /* Directory view/File view (all): bar-graph entry format*/ 183 | td.coverBar 184 | { 185 | padding-left: 10px; 186 | padding-right: 10px; 187 | background-color: #DAE7FE; 188 | } 189 | 190 | /* Directory view/File view (all): bar-graph outline color */ 191 | td.coverBarOutline 192 | { 193 | background-color: #000000; 194 | } 195 | 196 | /* Directory view/File view (all): percentage entry for files with 197 | high coverage rate */ 198 | td.coverPerHi 199 | { 200 | text-align: right; 201 | padding-left: 10px; 202 | padding-right: 10px; 203 | background-color: #A7FC9D; 204 | font-weight: bold; 205 | font-family: sans-serif; 206 | } 207 | 208 | /* Directory view/File view (all): line count entry for files with 209 | high coverage rate */ 210 | td.coverNumHi 211 | { 212 | text-align: right; 213 | padding-left: 10px; 214 | padding-right: 10px; 215 | background-color: #A7FC9D; 216 | white-space: nowrap; 217 | font-family: sans-serif; 218 | } 219 | 220 | /* Directory view/File view (all): percentage entry for files with 221 | medium coverage rate */ 222 | td.coverPerMed 223 | { 224 | text-align: right; 225 | padding-left: 10px; 226 | padding-right: 10px; 227 | background-color: #FFEA20; 228 | font-weight: bold; 229 | font-family: sans-serif; 230 | } 231 | 232 | /* Directory view/File view (all): line count entry for files with 233 | medium coverage rate */ 234 | td.coverNumMed 235 | { 236 | text-align: right; 237 | padding-left: 10px; 238 | padding-right: 10px; 239 | background-color: #FFEA20; 240 | white-space: nowrap; 241 | font-family: sans-serif; 242 | } 243 | 244 | /* Directory view/File view (all): percentage entry for files with 245 | low coverage rate */ 246 | td.coverPerLo 247 | { 248 | text-align: right; 249 | padding-left: 10px; 250 | padding-right: 10px; 251 | background-color: #FF0000; 252 | font-weight: bold; 253 | font-family: sans-serif; 254 | } 255 | 256 | /* Directory view/File view (all): line count entry for files with 257 | low coverage rate */ 258 | td.coverNumLo 259 | { 260 | text-align: right; 261 | padding-left: 10px; 262 | padding-right: 10px; 263 | background-color: #FF0000; 264 | white-space: nowrap; 265 | font-family: sans-serif; 266 | } 267 | 268 | /* File view (all): "show/hide details" link format */ 269 | a.detail:link 270 | { 271 | color: #B8D0FF; 272 | font-size:80%; 273 | } 274 | 275 | /* File view (all): "show/hide details" link - visited format */ 276 | a.detail:visited 277 | { 278 | color: #B8D0FF; 279 | font-size:80%; 280 | } 281 | 282 | /* File view (all): "show/hide details" link - activated format */ 283 | a.detail:active 284 | { 285 | color: #FFFFFF; 286 | font-size:80%; 287 | } 288 | 289 | /* File view (detail): test name entry */ 290 | td.testName 291 | { 292 | text-align: right; 293 | padding-right: 10px; 294 | background-color: #DAE7FE; 295 | font-family: sans-serif; 296 | } 297 | 298 | /* File view (detail): test percentage entry */ 299 | td.testPer 300 | { 301 | text-align: right; 302 | padding-left: 10px; 303 | padding-right: 10px; 304 | background-color: #DAE7FE; 305 | font-family: sans-serif; 306 | } 307 | 308 | /* File view (detail): test lines count entry */ 309 | td.testNum 310 | { 311 | text-align: right; 312 | padding-left: 10px; 313 | padding-right: 10px; 314 | background-color: #DAE7FE; 315 | font-family: sans-serif; 316 | } 317 | 318 | /* Test case descriptions: test name format*/ 319 | dt 320 | { 321 | font-family: sans-serif; 322 | font-weight: bold; 323 | } 324 | 325 | /* Test case descriptions: description table body */ 326 | td.testDescription 327 | { 328 | padding-top: 10px; 329 | padding-left: 30px; 330 | padding-bottom: 10px; 331 | padding-right: 30px; 332 | background-color: #DAE7FE; 333 | } 334 | 335 | /* Source code view: function entry */ 336 | td.coverFn 337 | { 338 | text-align: left; 339 | padding-left: 10px; 340 | padding-right: 20px; 341 | color: #284FA8; 342 | background-color: #DAE7FE; 343 | font-family: monospace; 344 | } 345 | 346 | /* Source code view: function entry zero count*/ 347 | td.coverFnLo 348 | { 349 | text-align: right; 350 | padding-left: 10px; 351 | padding-right: 10px; 352 | background-color: #FF0000; 353 | font-weight: bold; 354 | font-family: sans-serif; 355 | } 356 | 357 | /* Source code view: function entry nonzero count*/ 358 | td.coverFnHi 359 | { 360 | text-align: right; 361 | padding-left: 10px; 362 | padding-right: 10px; 363 | background-color: #DAE7FE; 364 | font-weight: bold; 365 | font-family: sans-serif; 366 | } 367 | 368 | /* Source code view: source code format */ 369 | pre.source 370 | { 371 | font-family: monospace; 372 | white-space: pre; 373 | margin-top: 2px; 374 | } 375 | 376 | /* Source code view: line number format */ 377 | span.lineNum 378 | { 379 | background-color: #EFE383; 380 | } 381 | 382 | /* Source code view: format for lines which were executed */ 383 | td.lineCov, 384 | span.lineCov 385 | { 386 | background-color: #CAD7FE; 387 | } 388 | 389 | /* Source code view: format for Cov legend */ 390 | span.coverLegendCov 391 | { 392 | padding-left: 10px; 393 | padding-right: 10px; 394 | padding-bottom: 2px; 395 | background-color: #CAD7FE; 396 | } 397 | 398 | /* Source code view: format for lines which were not executed */ 399 | td.lineNoCov, 400 | span.lineNoCov 401 | { 402 | background-color: #FF6230; 403 | } 404 | 405 | /* Source code view: format for NoCov legend */ 406 | span.coverLegendNoCov 407 | { 408 | padding-left: 10px; 409 | padding-right: 10px; 410 | padding-bottom: 2px; 411 | background-color: #FF6230; 412 | } 413 | 414 | /* Source code view (function table): standard link - visited format */ 415 | td.lineNoCov > a:visited, 416 | td.lineCov > a:visited 417 | { 418 | color: black; 419 | text-decoration: underline; 420 | } 421 | 422 | /* Source code view: format for lines which were executed only in a 423 | previous version */ 424 | span.lineDiffCov 425 | { 426 | background-color: #B5F7AF; 427 | } 428 | 429 | /* Source code view: format for branches which were executed 430 | * and taken */ 431 | span.branchCov 432 | { 433 | background-color: #CAD7FE; 434 | } 435 | 436 | /* Source code view: format for branches which were executed 437 | * but not taken */ 438 | span.branchNoCov 439 | { 440 | background-color: #FF6230; 441 | } 442 | 443 | /* Source code view: format for branches which were not executed */ 444 | span.branchNoExec 445 | { 446 | background-color: #FF6230; 447 | } 448 | 449 | /* Source code view: format for the source code heading line */ 450 | pre.sourceHeading 451 | { 452 | white-space: pre; 453 | font-family: monospace; 454 | font-weight: bold; 455 | margin: 0px; 456 | } 457 | 458 | /* All views: header legend value for low rate */ 459 | td.headerValueLegL 460 | { 461 | font-family: sans-serif; 462 | text-align: center; 463 | white-space: nowrap; 464 | padding-left: 4px; 465 | padding-right: 2px; 466 | background-color: #FF0000; 467 | font-size: 80%; 468 | } 469 | 470 | /* All views: header legend value for med rate */ 471 | td.headerValueLegM 472 | { 473 | font-family: sans-serif; 474 | text-align: center; 475 | white-space: nowrap; 476 | padding-left: 2px; 477 | padding-right: 2px; 478 | background-color: #FFEA20; 479 | font-size: 80%; 480 | } 481 | 482 | /* All views: header legend value for hi rate */ 483 | td.headerValueLegH 484 | { 485 | font-family: sans-serif; 486 | text-align: center; 487 | white-space: nowrap; 488 | padding-left: 2px; 489 | padding-right: 4px; 490 | background-color: #A7FC9D; 491 | font-size: 80%; 492 | } 493 | 494 | /* All views except source code view: legend format for low coverage */ 495 | span.coverLegendCovLo 496 | { 497 | padding-left: 10px; 498 | padding-right: 10px; 499 | padding-top: 2px; 500 | background-color: #FF0000; 501 | } 502 | 503 | /* All views except source code view: legend format for med coverage */ 504 | span.coverLegendCovMed 505 | { 506 | padding-left: 10px; 507 | padding-right: 10px; 508 | padding-top: 2px; 509 | background-color: #FFEA20; 510 | } 511 | 512 | /* All views except source code view: legend format for hi coverage */ 513 | span.coverLegendCovHi 514 | { 515 | padding-left: 10px; 516 | padding-right: 10px; 517 | padding-top: 2px; 518 | background-color: #A7FC9D; 519 | } 520 | -------------------------------------------------------------------------------- /coverage/glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binhonglee/coco/0d7b08c9acfd492db19538aceb71901da9e47def/coverage/glass.png -------------------------------------------------------------------------------- /coverage/index-sort-f.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LCOV - lcov.info 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 50 | 51 | 52 | 53 |
LCOV - code coverage report
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Current view:top levelHitTotalCoverage
Test:lcov.infoLines:19121987.2 %
Date:2018-10-21 17:27:55Functions:1313100.0 %
49 |
54 | 55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 77 | 78 | 79 | 80 | 81 | 82 |

Directory Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverage
nim-coverage 75 |
87.2%87.2%
76 |
87.2 %191 / 219100.0 %13 / 13
83 |
84 |
85 | 86 | 87 | 88 | 89 |
Generated by: LCOV version 1.13
90 |
91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /coverage/index-sort-l.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LCOV - lcov.info 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 50 | 51 | 52 | 53 |
LCOV - code coverage report
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Current view:top levelHitTotalCoverage
Test:lcov.infoLines:19121987.2 %
Date:2018-10-21 17:27:55Functions:1313100.0 %
49 |
54 | 55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 77 | 78 | 79 | 80 | 81 | 82 |

Directory Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverage
nim-coverage 75 |
87.2%87.2%
76 |
87.2 %191 / 219100.0 %13 / 13
83 |
84 |
85 | 86 | 87 | 88 | 89 |
Generated by: LCOV version 1.13
90 |
91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /coverage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LCOV - lcov.info 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 50 | 51 | 52 | 53 |
LCOV - code coverage report
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Current view:top levelHitTotalCoverage
Test:lcov.infoLines:19121987.2 %
Date:2018-10-21 17:27:55Functions:1313100.0 %
49 |
54 | 55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 77 | 78 | 79 | 80 | 81 | 82 |

Directory Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverage
nim-coverage 75 |
87.2%87.2%
76 |
87.2 %191 / 219100.0 %13 / 13
83 |
84 |
85 | 86 | 87 | 88 | 89 |
Generated by: LCOV version 1.13
90 |
91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /coverage/nim-coverage/coco.nim.func-sort-c.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LCOV - lcov.info - nim-coverage/coco.nim - functions 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 50 | 51 | 52 | 53 |
LCOV - code coverage report
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Current view:top level - nim-coverage - coco.nim (source / functions)HitTotalCoverage
Test:lcov.infoLines:19121987.2 %
Date:2018-10-21 17:27:55Functions:1313100.0 %
49 |
54 | 55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |

Function Name Sort by function nameHit count Sort by hit count
cleanup_report_dPv2AN9bb2Ex4MxJpG9b0P8Q2
coco_cocoDatInit0002
coco_cocoInit0002
coverage_gQdfdL0GrUcpenV29bZUV8Q2
genhtml_Bc8FKraVTPiHJzPEQ8AvrQ2
reset_coverage_ixTztPHyuvrGNLphHbspAQ3
trace_nfmMBZX3RDcJhvDmKVGUTg3
build_lcov_args_blqGq2ikIWuqvj9ci9aCpL7Q4
compile_tD2HZguUY4w09b7iPzBY6yQ4
get_cache_folder_2hKxOP9bo6u9buKgLZs3GK2Q13
exec_GHZmtcyzKL5XmpLeZFIZmQ22
exit_on_fail_BYdsb5VgzWcR6SWKTHzp9aA22
is_successful_nl6RaRS8BA9at9a9bFgvk1Bag22
115 |
116 |
117 | 118 | 119 | 120 |
Generated by: LCOV version 1.13
121 |
122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /coverage/nim-coverage/coco.nim.func.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LCOV - lcov.info - nim-coverage/coco.nim - functions 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 50 | 51 | 52 | 53 |
LCOV - code coverage report
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Current view:top level - nim-coverage - coco.nim (source / functions)HitTotalCoverage
Test:lcov.infoLines:19121987.2 %
Date:2018-10-21 17:27:55Functions:1313100.0 %
49 |
54 | 55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |

Function Name Sort by function nameHit count Sort by hit count
build_lcov_args_blqGq2ikIWuqvj9ci9aCpL7Q4
cleanup_report_dPv2AN9bb2Ex4MxJpG9b0P8Q2
coco_cocoDatInit0002
coco_cocoInit0002
compile_tD2HZguUY4w09b7iPzBY6yQ4
coverage_gQdfdL0GrUcpenV29bZUV8Q2
exec_GHZmtcyzKL5XmpLeZFIZmQ22
exit_on_fail_BYdsb5VgzWcR6SWKTHzp9aA22
genhtml_Bc8FKraVTPiHJzPEQ8AvrQ2
get_cache_folder_2hKxOP9bo6u9buKgLZs3GK2Q13
is_successful_nl6RaRS8BA9at9a9bFgvk1Bag22
reset_coverage_ixTztPHyuvrGNLphHbspAQ3
trace_nfmMBZX3RDcJhvDmKVGUTg3
115 |
116 |
117 | 118 | 119 | 120 |
Generated by: LCOV version 1.13
121 |
122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /coverage/nim-coverage/coco.nim.gcov.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LCOV - lcov.info - nim-coverage/coco.nim 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 50 | 51 | 52 | 53 |
LCOV - code coverage report
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Current view:top level - nim-coverage - coco.nim (source / functions)HitTotalCoverage
Test:lcov.infoLines:19121987.2 %
Date:2018-10-21 17:27:55Functions:1313100.0 %
49 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 221 | 222 |

61 |
          Line data    Source code
62 |
 63 |        1             : ## This module is the API behind the binary Coco.
 64 |        2             : ##
 65 |        3             : ## Coco makes line or branch code coverage for Nim a breeze. 
 66 |        4             : ##      - Depends on LCOV
 67 |        5             : ##      - Generates a nice looking HTML report
 68 |        6             : ##      - Works with visualization tools like Coverage Gutters on VSCode.
 69 |        7             : 
 70 |        8             : import glob, strutils, os, strformat, sequtils
 71 |        9             : 
 72 |       10             : proc is_successful(ret: int): bool =
 73 |       11          22 :     if ret == 0:
 74 |       12          22 :         true
 75 |       13          22 :     else:
 76 |       14             :         false
 77 |       15           0 : 
 78 |       16             : proc exit_on_fail(success: bool): void {.discardable.} =
 79 |       17          66 :     if not success:
 80 |       18           0 :         quit("Command line failed. Coco exited.")
 81 |       19             : proc exec(command: string): void {.discardable.} =
 82 |       20          22 :     ## Wrapper around execShellCmd that exits if the command fail
 83 |       21          44 :     execShellCmd(command)
 84 |       22          44 :     .is_successful()
 85 |       23          22 :     .exit_on_fail()
 86 |       24             : 
 87 |       25             : proc get_cache_folder*(filename, nimcache: string, increment=0): string =
 88 |       26          52 :     &"{nimcache}/{filename}_{increment}_cov"
 89 |       27             : 
 90 |       28             : proc compile*(target="tests/**/*.nim", nimcache="nimcache", verbose=false, options= "") =
 91 |       29           6 :     ## Compiles Nim files in coverage mode
 92 |       30             :     ##      - target should be a Glob with a .nim extension
 93 |       31           5 :     var nim_args = "--hints:off"
 94 |       32           7 :     if verbose:
 95 |       33           1 :         nim_args = ""
 96 |       34          16 :     if options.len > 0:
 97 |       35           2 :         nim_args &= &" {options}"
 98 |       36             : 
 99 |       37           4 :     var i = 0 # used to avoid name folder conflicts
100 |       38             :     for nimfile in walkGlob(target):
101 |       39           4 :         var cache_folder = 
102 |       40             :             nimfile
103 |       41           4 :             .extractFilename()
104 |       42             :             .get_cache_folder(nimcache, i)
105 |       43             : 
106 |       44          28 :         exec(&"nim {nim_args} --nimcache={cache_folder} --debugger:native --passC:--coverage --passL:--coverage c " & nimfile)
107 |       45           4 :         i.inc()
108 |       46             : 
109 |       47           4 : proc reset_coverage*(source="lcov.info", path="coverage", nimcache="nimcache") =
110 |       48           3 :     ## Removes everything generated by a past code coverage generation:
111 |       49             :     ##      - Nimcache folder
112 |       50           4 :     ##      - .info
113 |       51             :     ##      - Code coverage report folder
114 |       52          17 :     exec(&"rm -rf {source} {path} {nimcache}")
115 |       53             : 
116 |       54           4 : proc trace*(target: string) = 
117 |       55           3 :     ## Runs the compiled Nim files to produce coverage informations.
118 |       56           0 :     ##      - target should be a Glob with a .nim extension.
119 |       57             :     
120 |       58             :     for nimfile in walkGlob(target):
121 |       59           3 :         var bin = nimfile
122 |       60           3 :         removeSuffix(bin, ".nim")
123 |       61           9 :         exec("./" & bin)
124 |       62           3 : 
125 |       63             : proc build_lcov_args(verbose=false, branch=false): string =
126 |       64           7 :     ## Simple LCOV arguments wrapper
127 |       65           4 :     var lcov_args = "--quiet"
128 |       66           6 :     if verbose:
129 |       67           5 :         lcov_args = ""
130 |       68           6 :     if branch:
131 |       69           8 :         lcov_args &= " --rc lcov_branch_coverage=1"
132 |       70             :     lcov_args
133 |       71           7 : 
134 |       72           0 : proc cleanup_report*(fileinfo = "lcov.info", cov: string, verbose=false, branch=false) =
135 |       73           2 :     ## Keeps only relevant coverage informations
136 |       74             :     ##      Without any cleanup, your code coverage report will include standard libraries, tests and so on.
137 |       75             :     
138 |       76           2 :     var lcov_args = build_lcov_args(verbose, branch)
139 |       77           2 :     var options: GlobOptions = {GlobOption.Directories, GlobOption.Absolute}
140 |       78             :     
141 |       79             :     # Remove standard lib and nimble pkgs informations
142 |       80           2 :     var currentFolder = absolutePath("")
143 |       81           8 :     exec(&"""lcov {lcov_args} --extract {fileinfo} "{currentFolder}*" -o {fileinfo}""")
144 |       82             : 
145 |       83             :     for pattern in cov.split(","):
146 |       84           2 :         if pattern.startsWith("!"):
147 |       85           2 :             var pattern_noprefix = pattern
148 |       86           2 :             removePrefix(pattern_noprefix, "!")
149 |       87           2 :             for path in walkGlob(pattern_noprefix, "", options):
150 |       88          16 :                 exec(&"""lcov {lcov_args} --remove {fileinfo} "{path}*" -o {fileinfo}""")
151 |       89             :         else:
152 |       90             :             for path in walkGlob(pattern, "", options):
153 |       91           0 :                 exec(&"""lcov {lcov_args} --extract {fileinfo} "{path}*" -o {fileinfo}""")
154 |       92             : 
155 |       93             : proc genhtml*(source="lcov.info", path="coverage", verbose=false, branch=false) =
156 |       94           2 :     ## Generates the HTML code coverage report from a .info file generated by LCOV
157 |       95           2 :     var lcov_args = "--quiet"
158 |       96           3 :     if verbose:
159 |       97           1 :         lcov_args = ""
160 |       98           3 :     if branch:
161 |       99           1 :         lcov_args &= " --branch-coverage"
162 |      100             :     ## Generate LCOV Html report
163 |      101           6 :     exec(&"genhtml {lcov_args} -o {path} {source}")
164 |      102             : 
165 |      103             : proc coverage*(target="tests/**/*.nim", cov="!tests", verbose=false, branch=false, nimcache="nimcache", report_source="lcov.info", report_path="coverage", compiler=""): int =
166 |      104           2 :     ## ____
167 |      105             :     ## 
168 |      106             :     ## Code coverage for Nim:
169 |      107             :     ##      1. Clean up past reports
170 |      108             :     ##      2. Compile nim files in coverage mode
171 |      109             :     ##      3. Run the the executables
172 |      110             :     ##      4. Capture, produce and cleanup LCOV .info file
173 |      111             :     ##      5. Generate the HTML report
174 |      112             :     ##
175 |      113           2 :     reset_coverage(report_source, report_path, nimcache)
176 |      114             : 
177 |      115           2 :     var targets = target.split(",")
178 |      116             :     for target in targets:
179 |      117           2 :         compile(target, nimcache, verbose, compiler)
180 |      118             :     
181 |      119           2 :     var lcov_args = build_lcov_args(verbose, branch)
182 |      120             : 
183 |      121           6 :     exec(&"lcov {lcov_args} --base-directory . --directory {nimcache} --zerocounters -q")
184 |      122           2 :     for target in targets:
185 |      123           2 :         trace(target)
186 |      124             :    
187 |      125           6 :     exec(&"lcov {lcov_args}  --base-directory . --directory {nimcache} -c -o {report_source}")
188 |      126             : 
189 |      127           2 :     cleanup_report(report_source, cov, verbose, branch)
190 |      128           2 :     genhtml(report_source, report_path, verbose, branch)
191 |      129             : 
192 |      130           2 :     result = 0
193 |      131             : 
194 |      132             : 
195 |      133           2 : when isMainModule:
196 |      134             :     import cligen
197 |      135           1 :     var helpOptions = {
198 |      136             :         "target": "Nim files to compile and run in coverage mode. Support glob patterns.",
199 |      137             :         "cov": "Path to folders and files you want in your code coverage report. Default takes your current directory and excludes your tests/ folder.",
200 |      138           2 :         "verbose": "Displays every traces from LCOV and Nim"
201 |      139             :     }
202 |      140             :     dispatch(coverage, 
203 |      141             :         help = {
204 |      142             :         "target": "Nim files to compile and run in coverage mode. Direct path or glob patterns.",
205 |      143             :         "cov": "Path to folders and files you want in your code coverage report. Default takes your current directory and excludes tests/ folder. Support glob patterns. ",
206 |      144             :         "verbose": "Displays all traces coming from LCOV and Nim",
207 |      145             :         "branch": "Enables LCOV branch code coverage mode",
208 |      146             :         "nimcache": "Nimcache path used by the Nim compiler",
209 |      147             :         "report_source": "Path used by LCOV to generate the file .info",
210 |      148             :         "report_path": "Folder path where the HTML code coverage report will be created",
211 |      149             :         "compiler": "Forward your parameter(s) to the Nim compiler"
212 |      150             :         },
213 |      151             :         short = {
214 |      152             :         "report_source": 's',
215 |      153             :         "report_path": 'p',
216 |      154             :         "compiler": 'o'
217 |      155             :         }
218 |      156             :     ) 
219 | 
220 |
223 |
224 | 225 | 226 | 227 | 228 |
Generated by: LCOV version 1.13
229 |
230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /coverage/nim-coverage/index-sort-f.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LCOV - lcov.info - nim-coverage 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 50 | 51 | 52 | 53 |
LCOV - code coverage report
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Current view:top level - nim-coverageHitTotalCoverage
Test:lcov.infoLines:19121987.2 %
Date:2018-10-21 17:27:55Functions:1313100.0 %
49 |
54 | 55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 77 | 78 | 79 | 80 | 81 | 82 |

Filename Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverage
coco.nim 75 |
87.2%87.2%
76 |
87.2 %191 / 219100.0 %13 / 13
83 |
84 |
85 | 86 | 87 | 88 | 89 |
Generated by: LCOV version 1.13
90 |
91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /coverage/nim-coverage/index-sort-l.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LCOV - lcov.info - nim-coverage 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 50 | 51 | 52 | 53 |
LCOV - code coverage report
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Current view:top level - nim-coverageHitTotalCoverage
Test:lcov.infoLines:19121987.2 %
Date:2018-10-21 17:27:55Functions:1313100.0 %
49 |
54 | 55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 77 | 78 | 79 | 80 | 81 | 82 |

Filename Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverage
coco.nim 75 |
87.2%87.2%
76 |
87.2 %191 / 219100.0 %13 / 13
83 |
84 |
85 | 86 | 87 | 88 | 89 |
Generated by: LCOV version 1.13
90 |
91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /coverage/nim-coverage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LCOV - lcov.info - nim-coverage 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 50 | 51 | 52 | 53 |
LCOV - code coverage report
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Current view:top level - nim-coverageHitTotalCoverage
Test:lcov.infoLines:19121987.2 %
Date:2018-10-21 17:27:55Functions:1313100.0 %
49 |
54 | 55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 77 | 78 | 79 | 80 | 81 | 82 |

Filename Sort by nameLine Coverage Sort by line coverageFunctions Sort by function coverage
coco.nim 75 |
87.2%87.2%
76 |
87.2 %191 / 219100.0 %13 / 13
83 |
84 |
85 | 86 | 87 | 88 | 89 |
Generated by: LCOV version 1.13
90 |
91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /coverage/ruby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binhonglee/coco/0d7b08c9acfd492db19538aceb71901da9e47def/coverage/ruby.png -------------------------------------------------------------------------------- /coverage/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binhonglee/coco/0d7b08c9acfd492db19538aceb71901da9e47def/coverage/snow.png -------------------------------------------------------------------------------- /coverage/updown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binhonglee/coco/0d7b08c9acfd492db19538aceb71901da9e47def/coverage/updown.png -------------------------------------------------------------------------------- /lcov.info: -------------------------------------------------------------------------------- 1 | TN: 2 | SF:/Users/sam/Code/nim-coverage/coco.nim 3 | FN:10,is_successful_nl6RaRS8BA9at9a9bFgvk1Bag 4 | FN:16,exit_on_fail_BYdsb5VgzWcR6SWKTHzp9aA 5 | FN:19,exec_GHZmtcyzKL5XmpLeZFIZmQ 6 | FN:25,get_cache_folder_2hKxOP9bo6u9buKgLZs3GK2Q 7 | FN:28,compile_tD2HZguUY4w09b7iPzBY6yQ 8 | FN:47,reset_coverage_ixTztPHyuvrGNLphHbspAQ 9 | FN:54,trace_nfmMBZX3RDcJhvDmKVGUTg 10 | FN:63,build_lcov_args_blqGq2ikIWuqvj9ci9aCpL7Q 11 | FN:72,cleanup_report_dPv2AN9bb2Ex4MxJpG9b0P8Q 12 | FN:93,genhtml_Bc8FKraVTPiHJzPEQ8AvrQ 13 | FN:103,coverage_gQdfdL0GrUcpenV29bZUV8Q 14 | FN:131,coco_cocoInit000 15 | FN:137,coco_cocoDatInit000 16 | FNDA:22,is_successful_nl6RaRS8BA9at9a9bFgvk1Bag 17 | FNDA:2,cleanup_report_dPv2AN9bb2Ex4MxJpG9b0P8Q 18 | FNDA:2,genhtml_Bc8FKraVTPiHJzPEQ8AvrQ 19 | FNDA:22,exit_on_fail_BYdsb5VgzWcR6SWKTHzp9aA 20 | FNDA:2,coco_cocoDatInit000 21 | FNDA:13,get_cache_folder_2hKxOP9bo6u9buKgLZs3GK2Q 22 | FNDA:22,exec_GHZmtcyzKL5XmpLeZFIZmQ 23 | FNDA:4,build_lcov_args_blqGq2ikIWuqvj9ci9aCpL7Q 24 | FNDA:4,compile_tD2HZguUY4w09b7iPzBY6yQ 25 | FNDA:2,coverage_gQdfdL0GrUcpenV29bZUV8Q 26 | FNDA:2,coco_cocoInit000 27 | FNDA:3,reset_coverage_ixTztPHyuvrGNLphHbspAQ 28 | FNDA:3,trace_nfmMBZX3RDcJhvDmKVGUTg 29 | FNF:13 30 | FNH:13 31 | DA:11,22 32 | DA:12,22 33 | DA:13,22 34 | DA:15,0 35 | DA:17,66 36 | DA:18,0 37 | DA:20,22 38 | DA:21,44 39 | DA:22,44 40 | DA:23,22 41 | DA:26,52 42 | DA:29,6 43 | DA:31,5 44 | DA:32,7 45 | DA:33,1 46 | DA:34,16 47 | DA:35,2 48 | DA:37,4 49 | DA:39,4 50 | DA:41,4 51 | DA:44,28 52 | DA:45,4 53 | DA:47,4 54 | DA:48,3 55 | DA:50,4 56 | DA:52,17 57 | DA:54,4 58 | DA:55,3 59 | DA:56,0 60 | DA:59,3 61 | DA:60,3 62 | DA:61,9 63 | DA:62,3 64 | DA:64,7 65 | DA:65,4 66 | DA:66,6 67 | DA:67,5 68 | DA:68,6 69 | DA:69,8 70 | DA:71,7 71 | DA:72,0 72 | DA:73,2 73 | DA:76,2 74 | DA:77,2 75 | DA:80,2 76 | DA:81,8 77 | DA:84,2 78 | DA:85,2 79 | DA:86,2 80 | DA:87,2 81 | DA:88,16 82 | DA:91,0 83 | DA:94,2 84 | DA:95,2 85 | DA:96,3 86 | DA:97,1 87 | DA:98,3 88 | DA:99,1 89 | DA:101,6 90 | DA:104,2 91 | DA:113,2 92 | DA:115,2 93 | DA:117,2 94 | DA:119,2 95 | DA:121,6 96 | DA:122,2 97 | DA:123,2 98 | DA:125,6 99 | DA:127,2 100 | DA:128,2 101 | DA:130,2 102 | DA:133,2 103 | DA:135,1 104 | DA:138,2 105 | DA:260,259 106 | DA:261,11 107 | DA:263,4 108 | DA:266,16 109 | DA:268,35 110 | DA:270,10 111 | DA:271,4 112 | DA:272,0 113 | DA:299,2 114 | DA:316,22 115 | DA:317,22 116 | DA:319,11 117 | DA:329,11 118 | DA:386,2 119 | DA:387,2 120 | DA:389,14 121 | DA:390,2 122 | DA:391,70 123 | DA:392,28 124 | DA:393,4 125 | DA:394,2 126 | DA:395,4 127 | DA:396,2 128 | DA:397,4 129 | DA:398,2 130 | DA:414,74 131 | DA:415,34 132 | DA:416,18 133 | DA:468,9 134 | DA:469,9 135 | DA:470,9 136 | DA:471,0 137 | DA:472,9 138 | DA:473,9 139 | DA:476,22 140 | DA:477,11 141 | DA:478,11 142 | DA:482,9 143 | DA:483,9 144 | DA:484,9 145 | DA:485,9 146 | DA:487,18 147 | DA:488,18 148 | DA:489,18 149 | DA:491,6 150 | DA:492,6 151 | DA:493,4 152 | DA:494,2 153 | DA:495,2 154 | DA:497,14 155 | DA:498,2 156 | DA:499,0 157 | DA:501,16 158 | DA:502,2 159 | DA:507,2 160 | DA:508,2 161 | DA:509,2 162 | DA:511,2 163 | DA:512,2 164 | DA:514,4 165 | DA:515,2 166 | DA:516,26 167 | DA:517,4 168 | DA:518,4 169 | DA:521,36 170 | DA:522,36 171 | DA:523,36 172 | DA:524,36 173 | DA:526,36 174 | DA:528,36 175 | DA:530,0 176 | DA:531,0 177 | DA:532,2 178 | DA:533,0 179 | DA:534,4 180 | DA:536,2 181 | DA:538,0 182 | DA:539,0 183 | DA:541,4 184 | DA:546,0 185 | DA:548,2 186 | DA:549,6 187 | DA:550,2 188 | DA:551,2 189 | DA:553,2 190 | DA:558,4 191 | DA:560,0 192 | DA:561,2 193 | DA:562,0 194 | DA:563,32 195 | DA:564,0 196 | DA:565,32 197 | DA:591,11 198 | DA:762,11 199 | DA:763,11 200 | DA:764,11 201 | DA:765,11 202 | DA:766,22 203 | DA:767,11 204 | DA:768,22 205 | DA:769,22 206 | DA:770,22 207 | DA:771,11 208 | DA:772,11 209 | DA:866,4 210 | DA:867,4 211 | DA:868,8 212 | DA:869,30 213 | DA:870,38 214 | DA:871,60 215 | DA:873,26 216 | DA:874,2 217 | DA:876,48 218 | DA:878,25 219 | DA:879,20 220 | DA:880,18 221 | DA:881,0 222 | DA:884,18 223 | DA:885,36 224 | DA:886,36 225 | DA:887,0 226 | DA:888,0 227 | DA:889,18 228 | DA:890,18 229 | DA:892,0 230 | DA:893,0 231 | DA:894,0 232 | DA:895,0 233 | DA:896,0 234 | DA:897,0 235 | DA:1194,22 236 | DA:2019,11 237 | DA:2020,55 238 | DA:2021,11 239 | DA:2022,11 240 | DA:2024,11 241 | DA:2026,11 242 | DA:2027,0 243 | DA:3822,4 244 | DA:3823,12 245 | DA:3824,20 246 | DA:3825,16 247 | DA:3826,4 248 | DA:3827,16 249 | DA:3828,0 250 | LF:219 251 | LH:191 252 | end_of_record 253 | -------------------------------------------------------------------------------- /tests/compilation.nim: -------------------------------------------------------------------------------- 1 | import unittest, glob, os, strformat, ../coco 2 | 3 | suite "Compiles Nim files in coverage mode": 4 | 5 | setup: 6 | const nimcache = "tests_nimcache" 7 | const basename = "foo" 8 | const filename = fmt"{basename}.nim" 9 | const default_cache_folder = get_cache_folder(filename, nimcache, 0) 10 | const base_filename = fmt"{default_cache_folder}/@m{filename}.c" 11 | teardown: 12 | removeDir(nimcache) 13 | 14 | test "Get cache folder from filename": 15 | ## Cache folder path pattern is /__cov 16 | check: 17 | get_cache_folder(filename, nimcache, 0) == fmt"{nimcache}/{filename}_0_cov" 18 | 19 | test "Each compiled file should have its own folder in nimcache": 20 | 21 | compile(fmt"tests/{filename}", nimcache) 22 | 23 | check: 24 | existsDir(default_cache_folder) 25 | existsFile(base_filename) 26 | existsFile(fmt"{base_filename}.gcno") 27 | 28 | test "One should be able to pass parameters to the compiler": 29 | 30 | compile(fmt"tests/{filename}", nimcache, options = "--hints:off") 31 | 32 | check: 33 | existsDir(default_cache_folder) 34 | existsFile(base_filename) 35 | existsFile(fmt"{base_filename}.gcno") 36 | 37 | test "Running a compiled file in coverage mode should generate .gdca files": 38 | trace(fmt"tests/{filename}") 39 | 40 | check: 41 | existsFile(fmt"{base_filename}.gcda") -------------------------------------------------------------------------------- /tests/foo.nim: -------------------------------------------------------------------------------- 1 | ## Minimal nim file used in tests for checking compilation and coverage 2 | import ../coco 3 | 4 | echo get_cache_folder("foo.nim", "bar", 42) -------------------------------------------------------------------------------- /tests/report/cleanup_past_report.nim: -------------------------------------------------------------------------------- 1 | import unittest, os, system, strformat, ../../coco 2 | 3 | suite "Removes past code coverage reports and data": 4 | 5 | setup: 6 | const fileinfo = "test.info" 7 | const nimcache = "tests_nimcache" 8 | const coverage = "tests_coverage" 9 | 10 | const basename = "foo" 11 | const filename = fmt"{basename}.nim" 12 | const default_cache_folder = get_cache_folder(filename, nimcache, 0) 13 | const base_filename = fmt"{default_cache_folder}/coco_{basename}.c" 14 | 15 | teardown: 16 | removeDir(coverage) 17 | removeDir(nimcache) 18 | removeFile(fileinfo) 19 | 20 | test "Cleanup custom locations:": 21 | # create custom locations 22 | createDir(coverage) 23 | createDir(nimcache) 24 | writeFile(fileinfo, "") 25 | 26 | reset_coverage(fileinfo, coverage, nimcache) 27 | check: 28 | existsDir(coverage) == false 29 | existsDir(nimcache) == false 30 | existsFile(fileinfo) == false -------------------------------------------------------------------------------- /tests/report/generate_coverage.nim: -------------------------------------------------------------------------------- 1 | import unittest, glob, os, strformat, ../../coco 2 | 3 | suite "Generate code coverage report": 4 | 5 | setup: 6 | const fileinfo = "report.info" 7 | const coverage = "tests_coverage" 8 | const nimcache = "tests_nimcache_report" 9 | const basename = "foo" 10 | const filename = fmt"{basename}.nim" 11 | const default_cache_folder = get_cache_folder(filename, nimcache, 0) 12 | const base_filename = fmt"{default_cache_folder}/coco_{basename}.c" 13 | teardown: 14 | removeDir(coverage) 15 | removeDir(nimcache) 16 | removeFile(fileinfo) 17 | 18 | test "Generate line code coverage only from test file foo.nim": 19 | 20 | discard coverage(target = fmt"tests/{filename}", nimcache = nimcache, report_source = fileinfo, report_path = coverage) 21 | 22 | check: 23 | existsDir(coverage) == true 24 | existsFile(fmt"{coverage}/index-sort-b.html") == false # should not generate branch coverage 25 | 26 | test "Generate code coverage in branch mode only from test file foo.nim": 27 | 28 | discard coverage(target = fmt"tests/{filename}", nimcache = nimcache, report_source = fileinfo, report_path = coverage, branch = true, verbose = true, compiler = "--hints:off") 29 | 30 | check: 31 | existsFile(fmt"{coverage}/index-sort-b.html") == true 32 | --------------------------------------------------------------------------------