summary refs log tree commit diff stats
path: root/doc/testament.md
blob: 0ff3591ac9856afc151eb2be46b568538222fa23 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
49
50
51
52
53
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
===========
 Testament
===========

.. default-role:: code
.. include:: rstcommon.rst
.. contents::

Testament is an advanced automatic unittests runner for Nim tests, is used for the development of Nim itself,
offers process isolation for your tests, it can generate statistics about test cases,
supports multiple targets (C, C++, ObjectiveC, JavaScript, etc.),
simulated [Dry-Runs](https://en.wikipedia.org/wiki/Dry_run_(testing)),
has logging, can generate HTML reports, skip tests from a file, and more,
so can be useful to run your tests, even the most complex ones.


Test files location
===================

By default, Testament looks for test files on ``"./tests/category/*.nim"``.
You can overwrite this pattern glob using `pattern <glob>`:option:.
The default working directory path can be changed using
`--directory:"folder/subfolder/"`:option:.

Testament uses the `nim`:cmd: compiler on `PATH`.
You can change that using `--nim:"folder/subfolder/nim"`:option:.
Running JavaScript tests with `--targets:"js"`:option: requires
a working NodeJS on `PATH`.

Commands
========

p|pat|pattern <glob>        run all the tests matching the given pattern
all                         run all tests inside of category folders
c|cat|category <category>   run all the tests of a certain category
r|run <test>                run single test file
html                        generate testresults.html from the database


Options
=======

--print                   print results to the console
--verbose                 print commands (compiling and running tests)
--simulate                see what tests would be run but don't run them (for debugging)
--failing                 only show failing/ignored tests
--targets:"c cpp js objc" run tests for specified targets (default: c)
--nim:path                use a particular nim executable (default: $PATH/nim)
--directory:dir           Change to directory dir before reading the tests or doing anything else.
--colors:on|off           Turn messages coloring on|off.
--backendLogging:on|off   Disable or enable backend logging. By default turned on.
--megatest:on|off         Enable or disable megatest. Default is on.
--valgrind:on|off         Enable or disable valgrind support. Default is on.
--skipFrom:file           Read tests to skip from `file` - one test per line, # comments ignored


Running a single test
=====================

This is a minimal example to understand the basics,
not very useful for production, but easy to understand:

  ```console
  $ mkdir -p tests/category
  $ echo "assert 42 == 42" > tests/category/test0.nim
  $ testament run tests/category/test0.nim
  PASS: tests/category/test0.nim c                           ( 0.2 sec)
  $ testament r tests/category/test0
  PASS: tests/category/test0.nim C                           ( 0.2 sec)
  ```


Running all tests from a directory
==================================

 This will run all tests in the top level tests/ directory. NOTE: these
 tests are skipped by `testament all`.
 
  ```console
  $ testament pattern "tests/*.nim"
  ```

To search for tests deeper in a directory, use

  ```console
  $ testament pattern "tests/**/*.nim"    # one level deeper
  $ testament pattern "tests/**/**/*.nim" # two levels deeper
  ```

HTML Reports
============

Generate HTML Reports ``testresults.html`` from unittests,
you have to run at least 1 test *before* generating a report:

  ```console
  $ testament html
  ```


Writing Unit tests
==================

Example "template" **to edit** and write a Testament unittest:

  ```nim
  discard """

    # What actions to expect completion on.
    # Options:
    #   "compile": expect successful compilation
    #   "run": expect successful compilation and execution
    #   "reject": expect failed compilation. The "reject" action can catch
    #             {.error.} pragmas but not {.fatal.} pragmas because
    #             {.error.} calls are expected to originate from the test-file, 
    #             and can explicitly be specified using the "file", "line" and
    #             "column" options.
    #             {.fatal.} pragmas guarantee that compilation will be aborted.
    action: "run"
    
    # For testing failed compilations you can specify the expected origin of the 
    # compilation error.
    # With the "file", "line" and "column" options you can define the file, 
    # line and column that a compilation-error should have originated from.
    # Use only with action: "reject" as it expects a failed compilation.
    # Requires errormsg or msg to be defined.
    # file: ""
    # line: ""
    # column: ""

    # The exit code that the test is expected to return. Typically, the default
    # value of 0 is fine. Note that if the test will be run by valgrind, then
    # the test will exit with either a code of 0 on success or 1 on failure.
    exitcode: 0

    # Provide an `output` string to assert that the test prints to standard out
    # exactly the expected string. Provide an `outputsub` string to assert that
    # the string given here is a substring of the standard out output of the
    # test (the output includes both the compiler and test execution output).
    output: ""
    outputsub: ""

    # Whether to sort the compiler output lines before comparing them to the 
    # expected output.
    sortoutput: true

    # Provide a `nimout` string to assert that the compiler during compilation
    # prints the defined lines to the standard out.
    # The lines must match in order, but there may be more lines that appear 
    # before, after, or in between them. 
    nimout: '''
  a very long,
  multi-line
  string'''

    # This is the Standard Input the test should take, if any.
    input: ""

    # Error message the test should print, if any.
    errormsg: ""

    # Can be run in batch mode, or not.
    batchable: true

    # Can be run Joined with other tests to run all together, or not.
    joinable: true

    # On Linux 64-bit machines, whether to use Valgrind to check for bad memory
    # accesses or memory leaks. On other architectures, the test will be run
    # as-is, without Valgrind.
    # Options:
    #   true: run the test with Valgrind
    #   false: run the without Valgrind
    #   "leaks": run the test with Valgrind, but do not check for memory leaks
    valgrind: false   # Can use Valgrind to check for memory leaks, or not (Linux 64Bit only).

    # Checks that the specified piece of C-code is within the generated C-code.
    ccodecheck: "'Assert error message'"

    # Command the test should use to run. If left out or an empty string is
    # provided, the command is taken to be:
    # "nim $target --hints:on -d:testing --nimblePath:build/deps/pkgs $options $file"
    # Subject to variable interpolation.
    cmd: "nim c -r $file"

    # Maximum generated temporary intermediate code file size for the test.
    maxcodesize: 666

    # Timeout seconds to run the test. Fractional values are supported.
    timeout: 1.5

    # Targets to run the test into (c, cpp, objc, js). Defaults to c.
    targets: "c js"

    # flags with which to run the test, delimited by `;`
    matrix: "; -d:release; -d:caseFoo -d:release"

    # Conditions that will skip this test. Use of multiple "disabled" clauses
    # is permitted.
    disabled: "bsd"   # Can disable OSes...
    disabled: "win"
    disabled: "32bit" # ...or architectures
    disabled: "i386"
    disabled: "azure" # ...or pipeline runners
    disabled: true    # ...or can disable the test entirely

  """
  assert true
  assert 42 == 42, "Assert error message"
  ```


* As you can see the "Spec" is just a `discard """ """`.
* Spec has sane defaults, so you don't need to provide them all, any simple assert will work just fine.
* This is not the full spec of Testament, check [the Testament Spec on GitHub,
  see parseSpec()](https://github.com/nim-lang/Nim/blob/devel/testament/specs.nim#L317).
* Nim itself uses Testament, so [there are plenty of test examples](
  https://github.com/nim-lang/Nim/tree/devel/tests).
* Has some built-in CI compatibility, like Azure Pipelines, etc.


Inline hints, warnings and errors (notes)
-----------------------------------------

Testing the line, column, kind and message of hints, warnings and errors can
be written inline like so:
  ```nim
  {.warning: "warning!!"} #[tt.Warning
           ^ warning!! [User] ]#
  ```

The opening `#[tt.` marks the message line.
The `^` marks the message column.

Inline messages can be combined with `nimout` when `nimoutFull` is false (default).
This allows testing for expected messages from other modules:

  ```nim
  discard """
    nimout: "config.nims(1, 1) Hint: some hint message [User]"
  """
  {.warning: "warning!!"} #[tt.Warning
           ^ warning!! [User] ]#
  ```

Multiple messages for a line can be checked by delimiting messages with ';':

  ```nim
  discard """
    matrix: "--errorMax:0 --styleCheck:error"
  """

  proc generic_proc*[T](a_a: int) = #[tt.Error
       ^ 'generic_proc' should be: 'genericProc'; tt.Error
                        ^ 'a_a' should be: 'aA' ]#
    discard
  ```

Use `--errorMax:0` in `matrix`, or `cmd: "nim check $file"` when testing
for multiple 'Error' messages.

Output message variable interpolation
-------------------------------------

`errormsg`, `nimout`, and inline messages are subject to these variable interpolations:

* `${/}` - platform's directory separator
* `$file` - the filename (without directory) of the test

All other `$` characters need escaped as `$$`.

Cmd variable interpolation
--------------------------

The `cmd` option is subject to these variable interpolations:

* `$target` - the compilation target, e.g. `c`.
* `$options` - the options for the compiler.
* `$file` - the file path of the test.
* `$filedir` - the directory of the test file.

.. code-block:: nim

  discard """
    cmd: "nim $target --nimblePath:./nimbleDir/simplePkgs $options $file"
  """

All other `$` characters need escaped as `$$`.


Unit test Examples
==================

Expected to fail:

  ```nim
  discard """
    errormsg: "undeclared identifier: 'not_defined'"
  """
  assert not_defined == "not_defined", "not_defined is not defined"
  ```

Expected to fail with error thrown from another file:
```nim
# test.nim
discard """
  action: "reject"
  errorMsg: "I break"
  file: "breakPragma.nim"
"""
import ./breakPragma

proc x() {.justDo.} = discard

# breakPragma.nim
import std/macros

macro justDo*(procDef: typed): untyped =
  error("I break")
  return procDef
```

Expecting generated C to contain a given piece of code:

  ```nim
  discard """
    # Checks that the string "Assert error message" is in the generated 
    # C code.
    ccodecheck: "'Assert error message'"
  """
  assert 42 == 42, "Assert error message"
  ```


Non-Zero exit code:

  ```nim
  discard """
    exitcode: 1
  """
  quit "Non-Zero exit code", 1
  ```

Standard output checking:

  ```nim
  discard """

    output: '''
  0
  1
  2
  3
  4
  5
  '''

  """
  for i in 0..5: echo i
  ```

JavaScript tests:

  ```nim
  discard """
    targets: "js"
  """
  when defined(js):
    import std/jsconsole
    console.log("My Frontend Project")
  ```

Compile-time tests:

  ```nim
  discard """
    action: "compile"
  """
  static: assert 9 == 9, "Compile time assert"
  ```

Tests without Spec:

  ```nim
  assert 1 == 1
  ```


See also:
* [Unittest](unittest.html)