Contents
Using
Git(Hub)
Testing your
changes
Writing
documentation
Writing code
CoC
So you want to contribute something to Catch2? That’s great! Whether it’s a bug fix, a new feature, support for additional compilers - or just a fix to the documentation - all contributions are very welcome and very much appreciated. Of course so are bug reports, other comments, and questions, but generally it is a better idea to ask questions in our Discord, than in the issue tracker.
This page covers some guidelines and helpful tips for contributing to the codebase itself.
Ongoing development happens in the devel
branch for
Catch2 v3, and in v2.x
for maintenance updates to the v2
versions.
Commits should be small and atomic. A commit is atomic when, after it is applied, the codebase, tests and all, still works as expected. Small commits are also preferred, as they make later operations with git history, whether it is bisecting, reverting, or something else, easier.
When submitting a pull request please do not include changes to the amalgamated distribution files. This means do not include them in your git commits!
When addressing review comments in a MR, please do not rebase/squash
the commits immediately. Doing so makes it harder to review the new
changes, slowing down the process of merging a MR. Instead, when
addressing review comments, you should append new commits to the branch
and only squash them into other commits when the MR is ready to be
merged. We recommend creating new commits with
git commit --fixup
(or --squash
) and then
later squashing them with git rebase --autosquash
to make
things easier.
Note: Running Catch2’s tests requires Python3
Catch2 has multiple layers of tests that are then run as part of our
CI. The most obvious one are the unit tests compiled into the
SelfTest
binary. These are then used in “Approval tests”,
which run (almost) all tests from SelfTest
through a
specific reporter and then compare the generated output with a known
good output (“Baseline”). By default, new tests should be placed
here.
To configure a Catch2 build with just the basic tests, use the
basic-tests
preset, like so:
# Assuming you are in Catch2's root folder
cmake -B basic-test-build -S . -DCMAKE_BUILD_TYPE=Debug --preset basic-tests
However, not all tests can be written as plain unit tests. For example, checking that Catch2 orders tests randomly when asked to, and that this random ordering is subset-invariant, is better done as an integration test using an external check script. Catch2 integration tests are written using CTest, either as a direct command invocation + pass/fail regex, or by delegating the check to a Python script.
Catch2 is slowly gaining more and more types of tests, currently Catch2 project also has buildable examples, “ExtraTests”, and CMake config tests. Examples present a small and self-contained snippets of code that use Catch2’s facilities for specific purpose. Currently they are assumed passing if they compile.
ExtraTests then are expensive tests, that we do not want to run all the time. This can be either because they take a long time to run, or because they take a long time to compile, e.g. because they test compile time configuration and require separate compilation.
Finally, CMake config tests test that you set Catch2’s compile-time configuration options through CMake, using CMake options of the same name.
These test categories can be enabled one by one, by passing
-DCATCH_BUILD_EXAMPLES=ON
,
-DCATCH_BUILD_EXTRA_TESTS=ON
, and
-DCATCH_ENABLE_CONFIGURE_TESTS=ON
when configuring the
build.
Catch2 also provides a preset that promises to enable all
test types, all-tests
.
The snippet below will build & run all tests, in
Debug
compilation mode.
# 1. Regenerate the amalgamated distribution (some tests are built against it)
./tools/scripts/generateAmalgamatedFiles.py
# 2. Configure the full test build
cmake -B debug-build -S . -DCMAKE_BUILD_TYPE=Debug --preset all-tests
# 3. Run the actual build
cmake --build debug-build
# 4. Run the tests using CTest
ctest -j 4 --output-on-failure -C Debug --test-dir debug-build
For convenience, the above commands are in the script
tools/scripts/buildAndTest.sh
, and can be run like
this:
cd Catch2
./tools/scripts/buildAndTest.sh
A Windows version of the script is available at
tools\scripts\buildAndTest.cmd
.
If you added new tests, you will likely see
ApprovalTests
failure. After you check that the output
difference is expected, you should run
tools/scripts/approve.py
to confirm them, and include these
changes in your commit.
If you have added new feature to Catch2, it needs documentation, so that other people can use it as well. This section collects some technical information that you will need for updating Catch2’s documentation, and possibly some generic advise as well.
First, the technicalities:
<a id="top"></a>
# Cool feature
> [Introduced](https://github.com/catchorg/Catch2/pull/123456) in Catch2 X.Y.Z
Text that explains how to use the cool feature.
---
[Home](Readme.md#top)
Crosslinks to different pages should target the top
anchor, like this
[link to contributing](contributing.md#top)
.
We introduced version tags to the documentation, which show users in which version a specific feature was introduced. This means that newly written documentation should be tagged with a placeholder, that will be replaced with the actual version upon release. There are 2 styles of placeholders used through the documentation, you should pick one that fits your text better (if in doubt, take a look at the existing version tags for other features).
> [Introduced](link-to-issue-or-PR) in Catch2 X.Y.Z
- this placeholder is usually used after a section heading> X (Y and Z) was [introduced](link-to-issue-or-PR) in Catch2 X.Y.Z
For pages with more than 4 subheadings, we provide a table of
contents (ToC) at the top of the page. Because GitHub markdown does not
support automatic generation of ToC, it has to be handled semi-manually.
Thus, if you’ve added a new subheading to some page, you should add it
to the ToC. This can be done either manually, or by running the
updateDocumentToC.py
script in the scripts/
folder.
Now, for some content tips:
Usage examples are good. However, having large code snippets
inline can make the documentation less readable, and so the inline
snippets should be kept reasonably short. To provide more complex
compilable examples, consider adding new .cpp file to
examples/
.
Don’t be afraid to introduce new pages. The current documentation tends towards long pages, but a lot of that is caused by legacy, and we know that some of the pages are overly big and unfocused.
When adding information to an existing page, please try to keep your formatting, style and changes consistent with the rest of the page.
Any documentation has multiple different audiences, that desire different information from the text. The 3 basic user-types to try and cover are:
If want to contribute code, this section contains some simple rules and tips on things like code formatting, code constructions to avoid, and so on.
Catch2 currently targets C++14 as the minimum supported C++ version. Features from higher language versions should be used only sparingly, when the benefits from using them outweigh the maintenance overhead.
Example of good use of polyfilling features is our use of
conjunction
, where if available we use
std::conjunction
and otherwise provide our own
implementation. The reason it is good is that the surface area for
maintenance is quite small, and std::conjunction
can
directly use compiler built-ins, thus providing significant compilation
benefits.
Example of bad use of polyfilling features would be to keep around
two sets of metaprogramming in the stringification implementation, once
using C++14 compliant TMP and once using C++17’s
if constexpr
. While the C++17 would provide significant
compilation speedups, the maintenance cost would be too high.
To make code formatting simpler for the contributors, Catch2 provides
its own config for clang-format
. However, because it is
currently impossible to replicate existing Catch2’s formatting in
clang-format, using it to reformat a whole file would cause massive
diffs. To keep the size of your diffs reasonable, you should only use
clang-format on the newly changed code.
This section is a (sadly incomplete) listing of various constructs that are problematic and are not always caught by our CI infrastructure.
If you are throwing an exception, it should be done via
CATCH_ERROR
or CATCH_RUNTIME_ERROR
in
internal/catch_enforce.hpp
. These macros will handle the
differences between compilation with or without exceptions for you.
However, some platforms (IAR) also have problems with exceptions-related
functions, such as std::current_exceptions
. We do not have
IAR in our CI, but luckily there should not be too many reasons to use
these. However, if you do, they should be kept behind a
CATCH_CONFIG_DISABLE_EXCEPTIONS
macro.
std::move
and
std::forward
std::move
and std::forward
provide nice
semantic name for a specific static_cast
. However, being
function templates they have surprisingly high cost during compilation,
and can also have a negative performance impact for low-optimization
builds.
You should be using CATCH_MOVE
and
CATCH_FORWARD
macros from
internal/catch_move_and_forward.hpp
instead. They expand
into the proper static_cast
, and avoid the overhead of
std::move
and std::forward
.
If you are using a function from C’s stdlib, please include the
header as <cfoo>
and call the function qualified. The
common knowledge that there is no difference is wrong, QNX and VxWorks
won’t compile if you include the header as <cfoo>
and
call the function unqualified.
Due to messy standardese and … not great … implementation of
-Wreserved-identifier
in Clang, avoid declaring UDLs as
operator "" _a(long double); Approx
and instead declare them as
operator ""_a(long double); Approx
Notice that the second version does not have a space between the
""
and the literal suffix.
If you are adding new source file, there is a template you should use. Specifically, every source file should start with the licence header:
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
The include guards for header files should follow the pattern
{FILENAME}_INCLUDED
. This means that for file
catch_matchers_foo.hpp
, the include guard should be
CATCH_MATCHERS_FOO_HPP_INCLUDED
, for
catch_generators_bar.hpp
, the include guard should be
CATCH_GENERATORS_BAR_HPP_INCLUDED
, and so on.
CATCH_CONFIG
optionWhen adding new CATCH_CONFIG
option, there are multiple
places to edit: * CMake/CatchConfigOptions.cmake
- this is
used to generate the configuration options in CMake, so that CMake
frontends know about them. * docs/configuration.md
- this
is where the options are documented *
src/catch2/catch_user_config.hpp.in
- this is template for
generating catch_user_config.hpp
which contains the
materialized configuration * BUILD.bazel
- Bazel does not
have configuration support like CMake, and all expansions need to be
done manually * other files as needed,
e.g. catch2/internal/catch_config_foo.hpp
for the logic
that guards the configuration
This project has a CoC. Please adhere to it while contributing to Catch2.
This documentation will always be in-progress as new information comes up, but we are trying to keep it as up to date as possible.