|
|
# Testing KiCad #
[TOC]
# Unit tests {#unit-tests}
KiCad has a limited number of unit tests, which can be used tocheck that certain functionality works.
Tests are registered using [CTest][], part of CMake. CTest gathers all thedisparate test programs and runs them. Most C++ unittests are written using the [Boost Unit Test framework][], but this is notrequired to add a test to the testing suite.
The test CMake targets generally start with `qa_`, the names of the testswithin CTest are the same but without the `qa_` prefix.
## Running tests {#running-tests}
You can run all tests after building with `make test` or `ctest`. The latteroption allows many CTest options which can be useful, especially in automatedor CI environments.
### Running specific tests {#running-specific-tests}
To run a specific test executable, you can just run with `ctest` or runthe executable directly. Running directly is often the simplest way whenworking on a specific test and you want access to the test executable'sarguments. For example:
# run the libcommon tests cd /path/to/kicad/build qa/common/qa_common [parameters]
For Boost unit tests, you can see the options for the test with `<test> --help`.Common useful patterns:
* `<test> -t "Utf8/*"` runs all tests in the `Utf8` test suite.* `<test> -t "Utf8/UniIterNull"` runs only a single test in a specific suite.* `<test> -l all` adds more verbose debugging to the output.* `<test> --list_content` lists the test suites and test cases within the test program. You can use these for arguments to `-t`.
You can rebuild just a specific test with CMake to avoid rebuildingeverything when working on a small area, e.g. `make qa_common`.
### Automated testing {#automated-testing}
The unit tests can be run on automated Continuous Integration (CI) systems.
By default, tests output human-readable results, which is useful whendeveloping or debugging, but not so useful for automated test reporting.Systems that can parse XML test results can enable these by setting the`KICAD_TEST_XML_OUTPUT` option to `ON`. The test results are then outputas files ending in `.xml` in the `qa` subdirectory.
Test results are written to the build directory as follows:
* Boost units tests: one XML file per test with the extension `.boost-results.xml`* Python unit tests: one directory per test with the extension `.xunit-results.xml`. These directories contain one `.xml` file per Python test case file.
## Writing Boost tests {#writing-boost-tests}
Boost unit tests are straightforward to write. Individual test cases can beregistered with:
BOOST_AUTO_TEST_CASE( SomeTest ) { BOOST_CHECK_EQUAL( 1, 1 ); }
There is a range of functions like `BOOST_CHECK`, which are documented[here][boost-test-functions]. Using the most specific function is preferred, as thatallows Boost to provide more detailed failures: `BOOST_CHECK( foo == bar )` onlyreports a mismatch, `BOOST_CHECK_EQUAL( foo, bar )` will show the values ofeach.
To output debug messages, you can use `BOOST_TEST_MESSAGE` in the unit tests,which will be visible only if you set the `-l` parameter to `message` or higher.This colours the text differently to make it stand out from other testingmessages and standard output.
You can also use `std::cout`, `printf`, `wxLogDebug` and so on for debugmessages inside tested functions (i.e. where you don't have access to the Boostunit test headers). These will always be printed, so take careto remove them before committing, or they'll show up when KiCad runs normally!
### Expected failures {#expected-failures}
Sometimes, it is helpful to check in tests that do not pass. However, it is badpractise to intentionally check in commits that break builds (which is whathappens if you cause `make test` to fail).
Boost provides a method of declaring that some specific tests are allowed to fail.This syntax is not consistently available in all supported Boost versions, so youshould use the following construct:
```#include <unit_test_utils/unit_test_utils.h>
// On platforms with older boosts, the test will be excluded entirely#ifdef HAVE_EXPECTED_FAILURES
// Declare a test case with 1 "allowed" failure (out of 2, in this case)BOOST_AUTO_TEST_CASE( SomeTest, *boost::unit_test::expected_failures( 1 ) ){ BOOST_CHECK_EQUAL( 1, 1 );
// This check fails, but does not cause a test suite failure BOOST_CHECK_EQUAL( 1, 2 );
// Further failures *would* be a test suit failure}
#endif
```
When run, this produces output somewhat like this:
```qa/common/test_mytest.cpp(123): error: in "MyTests/SomeTest": check 1 == 2 has failed [1 != 2*** No errors detected```
And the unit test executable returns `0` (success).
Checking in a failing test is a strictly temporary situation, used to illustratethe triggering of a bug prior to fixing it. This is advantageous, not only froma "project history" perspective, but also to ensure that the test you write tocatch the bug in question does, in fact, catch the bug in the first place.
### Assertions {#test-assertions}
It is possible to check for assertions in unit tests. When running the unittests, `wxASSERT` calls are caught and re-thrown as exceptions. You can then usethe `CHECK_WX_ASSERT` macro to check this is called in Debug builds. In Releasebuilds, the check is not run, as `wxASSERT` is disabled in these builds.
You can use this to ensure that code rejects invalid input correctly.
## Python modules {#python-tests}
The Pcbnew Python modules have some test programs in the `qa` directory.You must have the `KICAD_SCRIPTING_MODULES` option on in CMake tobuild the modules and enable this target.
The main test script is `qa/test.py` and the test units are in`qa/testcases`. All the test units can by run using `ctest python`, whichruns `test.py`.
You can also run an individual case manually, by making sure themodules are built, adding them to `PYTHONPATH` and running the testfrom the source tree:
make pcbnew_python_module export PYTHONPATH=/path/to/kicad/build/pcbnew cd /path/to/kicad/source/qa python2 testcase/test_001_pcb_load.py
### Diagnosing segfaults {#python-segfaults}
Although the module is Python, it links against a C++ library(the same one used by KiCad Pcbnew), so it can segfault if the libraryhas a defect.
You can run the tests in GDB to trace this:
$ gdb
(gdb) file python2 (gdb) run testcases/test_001_pcb_load.py
If the test segfaults, you will get a familiar backtrace, just likeif you were running pcbnew under GDB.
# Utility programs {#utility-programs}
KiCad includes some utility programs that can be used for debugging, profiling,analysing or developing certain parts of the code without having to invoke the fullGUI program.
Generally, they are part of the `qa_*_tools` QA executables, each one containingthe relevant tools for that library. To list the tools in a given program, passthe `-l` parameter. Most tools provide help with the `-h` argument.To invoke a program:
qa_<lib>_tools <tool name> [-h] [tool arguments]
Below is a brief outline of some available tools. For full information and command-lineparameters, refer to the tools' usage test (`-h`).
* `common_tools` (the common library and core functions): * `coroutine`: A simple coroutine example * `io_benchmark`: Show relative speeds of reading files using various IO techniques.* `qa_pcbnew_tools` (pcbnew-related functions): * `drc`: Run and benchmark certain DRC functions on a user-provided `.kicad_pcb` files * `pcb_parser`: Parse user-provided `.kicad_pcb` files * `polygon_generator`: Dump polygons found on a PCB to the console * `polygon_triangulation`: Perform triangulation of zone polygons on PCBs
# Fuzz testing {#fuzz-testing}
It is possible to run fuzz testing on some parts of KiCad. To do this for ageneric function, you need to be able to pass some kind of input from the fuzztesting tool to the function under test.
For example, to use the [AFL fuzzing tool][], you will need:
* A test executable that can: * Receive input from `stdin` to be run by `afl-fuzz`. * Optional: process input from a filename to allow `afl-tmin` to minimise the input files.* To compile this executable with an AFL compiler, to enable the instrumentation that allows the fuzzer to detect the fuzzing state.
For example, the `qa_pcbnew_tools` executable (which contains `pcb_parser`,a fuzz testing tool for `.kicad_pcb` file parsing) can be compiled like this:
mkdir build cd build cmake -DCMAKE_CXX_COMPILER=/usr/bin/afl-clang-fast++ -DCMAKE_C_COMPILER=/usr/bin/afl-clang-fast ../kicad_src make qa_pcbnew_tools
You may need to disable core dumps and CPU frequency scaling on your system (AFLwill warn you if you should do this). For example, as root:
# echo core >/proc/sys/kernel/core_pattern # echo performance | tee cpu*/cpufreq/scaling_governor
To fuzz, run the executable via `afl-fuzz`:
afl-fuzz -i fuzzin -o fuzzout -m500 qa/pcbnew_tools/qa_pcbnew_tools pcb_parser
where:
* `-i` is a directory of files to use as fuzz input "seeds"* `-o` is a directory to write the results (including inputs that provoke crashes or hangs)* `-t` is the maximum time that a run is allowed to take before being declared a "hang"* `-m` is the memory allowed to use (this often needs to be bumped, as KiCad code tends to use a lot of memory to initialise)
The AFL TUI will then display the fuzzing progress, and you can use the hang- orcrash-provoking inputs to debug code as needed.
# Run-time debugging {#run-time}
KiCad can be debugged at run-time, either under a full debuggersuch as GDB, or using simple methods like logging debug to theconsole.
## Printing debug {#print-debug}
If you are compiling KiCad yourself, you can simply add debugging statements torelevant places in the code, for example:
wxLogDebug( "Value of variable: %d", my_int );
This produces debug output that can only be seen when compilingin Debug mode.
You can also use `std::cout` and `printf`.
Ensure you do not leave this kind of debugging in place whensubmitting code.
## Printing trace {#trace-debug}
Some parts of the code have "trace" that can be enabled selectively according toa "mask", for example:
wxLogTrace( "TRACEMASK", "My trace, value: %d", my_int );
This will not be printed by default. To show it, set the `WXTRACE` environmentvariable when you run KiCad to include the masks you wish to enable:
$ WXTRACE="TRACEMASK,OTHERMASK" kicad
When printed, the debug will be prefixed with a timestamp and the trace mask:
11:22:33: Trace: (TRACEMASK) My trace, value: 42
If you add a trace mask, define and document the mask as a variable in`include/trace_helpers.h`. This will add it to the [trace mask documentation][].
Some available masks:
* Core KiCad functions: * `KICAD_KEY_EVENTS` * `KicadScrollSettings` * `KICAD_FIND_ITEM` * `KICAD_FIND_REPLACE` * `KICAD_NGSPICE` * `KICAD_PLUGINLOADER` * `GAL_PROFILE` * `GAL_CACHED_CONTAINER` * `PNS` * `CN` * `SCROLL_ZOOM` - for the scroll-wheel zooming logic in GAL* Plugin-specific (including "standard" KiCad formats): * `3D_CACHE` * `3D_SG` * `3D_RESOLVER` * `3D_PLUGIN_MANAGER` * `KI_TRACE_CCAMERA` * `PLUGIN_IDF` * `PLUGIN_VRML` * `KICAD_SCH_LEGACY_PLUGIN` * `KICAD_GEDA_PLUGIN` * `KICAD_PCB_PLUGIN`
# Advanced configuration {#advanced-configuration}
There are some advance configuration options, which are mostly used fordevelopment or testing purposes.
To set these options, you can create the file `kicad_advanced` and set the keysas desired (the [advanced config documentation][] for a current list. You shouldnever need to set these keys for normal usage - if you do, that's a bug.
Any features enabled though the advanced configuration system areconsidered experimental and therefore unsuitable for production use. Thesefeatures are explicitly not supported or considered fully tested.Issues are still welcome for defects discovered.
[CTest]: https://cmake.org/cmake/help/latest/module/CTest.html[Boost Unit Test framework]: https://www.boost.org/doc/libs/1_68_0/libs/test/doc/html/index.html[boost-test-functions]: https://www.boost.org/doc/libs/1_68_0/libs/test/doc/html/boost_test/utf_reference/testing_tool_ref.html[AFL fuzzing tool]: http://lcamtuf.coredump.cx/afl/[trace mask documentation]: http://docs.kicad-pcb.org/doxygen/group__trace__env__vars.html[trace mask documentation]: http://docs.kicad-pcb.org/doxygen/group__trace__env__vars.html[advanced config documentation]: http://docs.kicad-pcb.org/doxygen/namespaceAC__KEYS.html
|