Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile-time Call Count in Nim

Tags:

nim-lang

The following code does not compile but illustrates what I would like to do: totalTests should hold the number of time that assertEquals() is called (assertEquals() should probably be a macro for this to be possible, but I'm not familiar with this aspect of Nim yet).

Any idea how this code should be modified to enable the following code to print [1/2] and [2/2] at the beginning of each test report line?

from strutils import format

var countTested = 0
var countFailed = 0
var countPassed = 0
let totalTests = 0 # <-- need let or other compile-time type here (?)

# using proc here... macro may be needed to be able to count calls (?)
proc assertEquals*[T](testName: string, expected: T, p: (proc(): T)) =
   countTested += 1
   totalTests += 1 # <-- compilation error (how can we increase each time it is called?)
   write(stdout, format("[$num/$total] $name: ", "num", countTested, "total", totalTests, "name", testName))
   var val = p()
   if val == expected:
     write(stdout, "passed\n")
     countPassed += 1
   else:
     write(stdout, "failed\n")
     countFailed += 1

when isMainModule:
  assertEquals("test #A", 12, proc(): int = 14-2)
  assertEquals("test #B", 12, proc(): int = 12-2)

Edit: added questions in code

like image 691
x2f Avatar asked May 12 '15 20:05

x2f


1 Answers

Here is one way to do it. You can execute code at compile time by using a macro or a static statement. Note that there's still no way to reliably count these across multiple modules.

import macros, strutils

proc beginTests()

var countTested = 0
var countFailed = 0
var countPassed = 0
var totalTests = 0
var totalTestsCT {.compiletime.} = 0

macro endTests(): stmt =
  quote do:
    proc beginTests() =
      totalTests = `totalTestsCT`

proc assertEqualsImpl*[T](testName: string, expected: T, p: (proc(): T)) =
   countTested += 1
   write(stdout, format("[$num/$total] $name: ",
         "num", countTested, "total", totalTests, "name", testName))
   var val = p()
   if val == expected:
     write(stdout, "passed\n")
     countPassed += 1
   else:
     write(stdout, "failed\n")
     countFailed += 1

macro assertEquals*[T](testName: string, expected: T, p: (proc(): T)): stmt =
  totalTestsCT += 1
  quote do:
    assertEqualsImpl(`testName`, `expected`, `p`)

when isMainModule:
  beginTests()
  assertEquals("test #A", 12, proc(): int = 14-2)
  assertEquals("test #B", 12, proc(): int = 12-2)
  endTests()

An alternative implementation would be to embed the tests in a custom block statement, e.g.

testSuite:
  assertEquals("test #A", 12, proc(): int = 14-2)
  assertEquals("test #B", 12, proc(): int = 12-2)

The testSuite macro would then count the assertions in the embedded code and initialize the variable accordingly.

Yet another solution would be to not execute the tests directly, but store them in a list and only execute them at the end.

like image 68
Reimer Behrends Avatar answered Nov 15 '22 11:11

Reimer Behrends