.. Mars Documentation
Copyright (C) 2008 Matt Giuca
3/9/2008
.. This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.. This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.. You should have received a copy of the GNU General Public License
along with this program. If not, see .
.. _testing:
.. highlight:: yaml
Testing Framework
=================
.. sectionauthor:: Matt Giuca
The Mars Test Framework (MTF) is a suite of tools for testing Mars
program code, and the Mars compiler itself.
The framework tests three things:
* Compilation (correct and incorrect code, errors and warnings),
* Compiler-inferred properties, and
* Runtime correctness (system and unit tests on Mars programs)
Test suite
----------
A test suite is simply a collection of :file:`.mar` files (Mars source code
files) and :file:`.mtc` files (Mars Test Case). MTC files are optional, and
correspond to a :file:`.mar` file of the same name.
A "test case" is a single :file:`.mar` file and possibly a :file:`.mtc` file
of the same name. Test cases are always just a single :file:`.mar` file
(though they can import other modules, eg. to test importing).
MTC file format
---------------
MTC files are `YAML `_ text files which provide instructions
about the test case, controlling the behaviour of the testing system.
At the top-level is a dict mapping section names to dicts.
Valid sections are: :data:`compile`, :data:`run` (both optional).
eg::
compile:
(compile section)
run:
(run section)
Inside each section is a dict specific to that section (see sections below).
Note that Unicode characters in MTC files are encoded with UTF-8 before being
used, unless otherwise noted.
Compilation testing
-------------------
Compilation testing involves compiling a :file:`.mar` file just to check
whether compilation succeeds or fails, and check that appropriate warnings and
errors are produced. This sort of test does not involve running the code at
all.
The most basic test case is a single :file:`.mar` file with nothing special
about it. Such a case will be compiled and will **pass** if it compiles
successfully without warnings, and **fail** if it does not compile, or there
are warnings.
If the case has a .mtc file with a :data:`compile` section, that section is
consulted for further information (which can affect the runtime and unit tests
as well). Its dictionary has the following attributes:
* :data:`outcome`: :data:`succeed` | :data:`fail` (:data:`succeed` is
default). This dictates what the outcome of compilation *should* be
(assuming a correct implementation) -- :data:`succeed` means the program
should be accepted, and :data:`fail` means the program should be rejected.
* :data:`errors`: string data -- the expected output of stderr from the
compiler. If omitted, expects stderr to be empty.
* :data:`expect`: :data:`pass` | :data:`fail` | :data:`compile_error`
(:data:`pass` is default). This dictates whether the current implementation
is correct -- :data:`pass` means it behaves correctly (compiles correctly or
fails to compile as required, depending on :data:`outcome`), :data:`fail`
means it doesn't behave correctly when the program should be rejected
(compiles when it shouldn't, or gives the wrong compiler error message), and
:data:`compile_error` means it doesn't compile when it should.
* :data:`environ`: Dict mapping environment strings to values. If supplied,
will be appended to the environment (replacing existing values), but
existing variables will not be removed. The environment will be used for all
runtime and unit tests for the given module.
* :data:`libs`: List of strings containing library names. If supplied, will be
passed to :command:`mars` with the :option:`-l` option. The libraries will
be used for all runtime and unit tests for the given module.
Hence, the default value for :data:`compile` is as follows::
compile:
outcome: succeed
errors: ""
expect: pass
environ: {}
libs: []
The test framework will only report unexpected outcomes. This means that
passing cases are not normally reported, but a passing case which is expected
to fail *will* be reported, so you know you have fixed something. Use
``expect: fail`` or ``expect: compile_error`` if you have a test case which
you know fails but intend to fix.
Runtime testing
---------------
Runtime testing is executing functions and checking their output. It implies
that compilation succeeds (if both compilation testing and runtime testing are
specified, compilation must not expect failure).
Compilation is not performed with the :option:`-m ` option, so the
program does not do anything by default.
Runtime testing requires an :file:`.mtc` file. The :data:`run` section is
consulted for information on how to do the test.
It is a list of individual cases, which are tested separately. Each case has a
dictionary.
The case's dictionary has the following attributes:
* :data:`call`: Mars expression to be evaluated after loading the module.
Required. (Typically used to call a function to be tested).
* :data:`stdin`: Input passed to stdin (may be text or binary). If omitted,
empty.
* :data:`stdout`: Expected stdout. If omitted, is not tested.
* :data:`stderr`: Expected stdout. If omitted, expect empty.
* :data:`expect`: :data:`pass` | :data:`fail`
Will only report unexpected outcomes. (If expecting fail, then "**pass**"
will be reported. Use this if you have a test case which you know fails but
intend to fix) (:data:`pass` is default).
.. note::
The test framework has no way to distinguish between when a program is
*computing* and when it is *waiting for input*. Therefore, if a test case
waits for input and none is supplied, MTF will wait forever. Be sure to
supply a line of :data:`stdin` for each line expected by the program.
Unit testing
------------
The third kind of test is a "unit test", which tests individual functions
inside Mars files. These are usually written to verify the correctness of Mars
code (eg. library code), rather than the compiler.
A module (:mod:`unittest`) is supplied in the Mars standard library, which can
be imported by test suites and called. It doesn't produce nice output -
instead, it dumps machine-readable output to stdout, which is expected to be
processed by a wrapper.
The Mars Test Framework is such a wrapper. It can be passed a Mars file and
will automatically execute any function beginning with "``test_``", and filter
the output into nice human-readable output. This does not require an
:file:`.mtc` file. Unit test functions must have type :type:`() -> io Num`
(they are also allowed to have type :type:`Num -> io Num`, but this is
deprecated).
Note that like compile and runtime testing, you are able to specify that a
case is expected to fail (but is intended to be fixed), using
:func:`~unittest.begin_case_pending`.
See :mod:`unittest` for more information.
Example MTC file
----------------
::
compile:
outcome: succeed
errors: |
Warning: Some warning message
environ: {"SOME_VAR": "some value"}
libs: ["m", "png"]
run:
- call: foo(2)
stdin: |
forty two
stdout: |
expected stdout text
perhaps across several lines
- call: foo(1)
stdin: "forty one\n"
stderr: "Incorrect\n"