4

I wrote most of my unit test with the help of the unittest module, but I’m not sure how to use it for code that the compiler should reject at compile time. For example, if I want to write the following code and make sure the compiler always errors during compilation (the type and template would be in a separate module), how do I write a test case for this?

import macros
type
  T[n:static[int]] = object
template foo3(n: int): expr =
  static:
    if n > 3: error "n > 3"
  type T3 = T[n]
  T3
var
  bar: foo3(4)
jxy
  • 784
  • 7
  • 16

3 Answers3

3

You can do something similar with the compiles magic, provided by the system module.

Here is an example from the compiler test suite:
https://github.com/nim-lang/Nim/blob/devel/tests/metatype/tbindtypedesc.nim#L19

Notice how at the top of the file, we define accept and reject as simple static assertions using the compiles magic and we use them throughout the file to test valid and invalid overloaded calls.

Personally, I think failing at compile-time is better, but you can assign the result of compiles to a run-time value or use it in a check statement. The only advantage of this would be that failures will be reported in the standard way for the unittest library.

zah
  • 5,314
  • 1
  • 34
  • 31
  • The compilation still aborts when hitting the first `error` call, even within `compiles`. So there is no way to automate the test. – jxy Feb 10 '16 at 20:28
  • I also couldn’t find a way to save the result of `compiles` to a runtime value for writing with `unittest`. – jxy Feb 10 '16 at 20:29
1

Just to add an example of how to combine check with compiles:

template notCompiles*(e: untyped): untyped =
  not compiles(e)

# usage in unit tests:
check:
  notCompiles: 
    does not compile
  notCompiles:
    let x = 1    # would fail

I'm using a template, because combining the not with the block directly is not possible, and I don't want to use parentheses.

bluenote10
  • 23,414
  • 14
  • 122
  • 178
0

In https://github.com/shaunc/cucumber_nim/blob/b1601a795dbf8ea0d0b5d96bf5b6a1cd90271327/tests/steps/dynmodule.nim

I have a wrapper that compiles and loads a nim source module. Of course, you won't want to run it, but the technique might work for you:

  sourceFN = "foo.nim"
  ... (write source) ...
  libFN = "foo.dll"
  let output = execProcess(
    "nim c --verbosity:0 --app:lib $1" % sourceFN,
    options = {poStdErrToStdOut, poUsePath, poEvalCommand})
  if not fileExists(libFN):
    echo "COULDN'T COMPILE"
shaunc
  • 5,317
  • 4
  • 43
  • 58