Skip to content

Commit

Permalink
Auto-test the Readme.md code. (#47)
Browse files Browse the repository at this point in the history
* Added a shortcut to set the ID field in VCF Variants.

* Added a minimal example to read TSV files.

* Changed the `ID=` proc to use the htslib bcf_update_id.

* Added an error message if the ID set fails.

* Added a script to auto-test the Readme samples.

The code snippets in the Readme has to start with "```nim" and
end with "```" lines. All the inner lines will be saved in a
temporary file, compiled with -d:release flag, and executed.

In case of failure, the test files won't be removed for you to
check.

* Refactored the testing code to have a clearer output.

It should also remove the remaining files even in failure events.

* Import the current hts-lib package, not the installed one.

* Polished the snippet code in the Readme.

Refactored the test code to be clearer.

* Made the tests Windows friendly.
  • Loading branch information
xbello authored and brentp committed Aug 28, 2019
1 parent bbf7f6e commit 987dfcd
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 8 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import hts
# open a bam/cram and look for the index.
var b:Bam
open(b, "tests/HG02002.cramam", index=true, fai="/data/human/g1k_v37_decoy.fa")
open(b, "tests/HG02002.bam", index=true, fai="/data/human/g1k_v37_decoy.fa")
for record in b:
if record.mapping_quality > 10u:
Expand Down Expand Up @@ -75,14 +75,14 @@ for rec in v:
var info = rec.info
# accessing stuff from the INFO field is meant to be as fast as possible, allowing
# the user to re-use memory as needed.
info.get("CSQ", csq) # string
info.get("AC", acs) # ints
info.get("AF", afs) # floats
doAssert info.get("CSQ", csq) == Status.OK # string
doAssert info.get("AC", acs) == Status.OK # ints
doAssert info.get("AF", afs) == Status.OK # floats
echo acs, afs, csq, info.has_flag("IN_EXAC")
# accessing format fields is similar
var dps = new_seq[int32](len(v.samples))
doAssert rec.format.ints("DP", dps) == Status.OK
doAssert rec.format.get("DP", dps) == Status.OK
# open a VCF for writing
var wtr:VCF
Expand All @@ -95,8 +95,8 @@ for rec in v.query("1:15600-18250"):
echo rec.CHROM, ":", $rec.POS
# adjust some values in the INFO
var val = 22.3
check rec.info.set("VQSLOD", val) == Status.OK
doAssert(wtr.write_variant(rec))
doAssert rec.info.set("VQSLOD", val) == Status.OK
doAssert wtr.write_variant(rec)
```

Expand Down
2 changes: 1 addition & 1 deletion tests/all.nim
Original file line number Diff line number Diff line change
@@ -1 +1 @@
import flagtest, cigartest, htstest, bgzftest, faitest, auxtest, vcftest, bamtest, statstests, vcfiso, test_files
import flagtest, cigartest, htstest, bgzftest, faitest, auxtest, vcftest, bamtest, statstests, vcfiso, test_files, test_readme
81 changes: 81 additions & 0 deletions tests/test_readme.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import os, osproc
import random
import strformat
import strutils
import terminal
import unittest


randomize()

proc getRandomNimname(length: int = 8): string =
result = "test_"
for _ in ..length:
result.add(sample(IdentChars))
result.add(".nim")

proc saveNimCode(codeLines: seq[string]): string =
# Write codeLines to a temporary file and return the filename
result = getRandomNimname()
var output = open(result, fmWrite)
for line in codeLines:
if line == "import hts":
# Use the current hts package, not the installed one
output.writeLine(line.replace("hts", "src/hts"))
else:
output.writeLine(line)
output.close()

proc compileRun(filename: string): bool =
## Compile and Run
result = true
let commands = @[
("Compile", &"nim c -d:release {filename}"),
("Run", &"{CurDir}{DirSep}{filename.changeFileExt(ExeExt)}")]

for command in commands:
var (outp, errC) = execCmdEx(command[1])
if errC > 0:
stdout.styledWrite(fgRed, styleBright, " [FAILED] ")
echo &"{command[0]} Readme sample"
echo outp
result = false


suite "Ensure the samples included in Readme.md works as intended":
test "Code extraction and running.":
var readme: File = open("README.md")
defer: readme.close()

var codeSample: seq[string]
var inSnippet: bool = false
var readmeLines: int

for line in readme.lines:
readmeLines.inc

if line.startsWith("```nim"): # Starting a snippet code
inSnippet = true
continue

if inSnippet:
if not line.startsWith("```"): # Save every line of the snippet
codeSample.add(line)
else: # The end of the snippet in reached
inSnippet = false

var testFilename = saveNimCode(codeSample)
try:
doAssert compileRun(testFilename)
echo " Success: Readme.md sample code between ",
&"lines {readmeLines - codeSample.len} and {readmeLines} ",
"seems OK."
except AssertionError:
echo " Error: Readme.md sample code raised the above error between ",
&"lines {readmeLines - codeSample.len} and {readmeLines}."
raise newException(AssertionError, getCurrentExceptionMsg())
finally:
# Clear code sample and remove temp files
codeSample = @[]
removeFile(testFilename)
removeFile(testFilename.changeFileExt(ExeExt))

0 comments on commit 987dfcd

Please sign in to comment.