Add content from docs repository.

Re-structure according to actual content.
WIP, some references are still broken.
This commit is contained in:
Lukas Tenbrink
2025-07-29 21:22:56 +02:00
parent 0ab0aff7a7
commit cc29190189
25 changed files with 3234 additions and 46 deletions

393
engine/code_style.rst Normal file
View File

@@ -0,0 +1,393 @@
.. _doc_code_style_guidelines:
Code style guidelines
=====================
.. highlight:: shell
When contributing to Godot's source code, you will be expected to follow the
style guidelines outlined below. Some of them are checked via the Continuous
Integration process and reviewers will ask you to fix potential issues, so
best setup your system as outlined below to ensure all your commits follow the
guidelines.
C++ and Objective-C
-------------------
There are no written guidelines, but the code style agreed upon by the
developers is enforced via the `clang-format <https://clang.llvm.org/docs/ClangFormat.html>`__
code beautifier, which takes care for you of all our conventions.
To name a few:
- Indentation and alignment are both tab based (respectively one and two tabs)
- One space around math and assignments operators as well as after commas
- Pointer and reference operators are affixed to the variable identifier, not
to the type name
- See further down regarding header includes
The rules used by clang-format are outlined in the
`.clang-format <https://github.com/godotengine/godot/blob/master/.clang-format>`__
file of the Godot repository.
As long as you ensure that your style matches the surrounding code and that you're
not introducing trailing whitespace or space-based indentation, you should be
fine. If you plan to contribute regularly, however, we strongly advise that you
set up clang-format locally to check and automatically fix all your commits.
.. warning:: Godot's code style should *not* be applied to third-party code,
i.e. code that is included in Godot's source tree, but was not written
specifically for our project. Such code usually comes from
different upstream projects with their own style guides (or lack
thereof), and don't want to introduce differences that would make
syncing with upstream repositories harder.
Third-party code is usually included in the ``thirdparty/`` folder
and can thus easily be excluded from formatting scripts. For the
rare cases where a third-party code snippet needs to be included
directly within a Godot file, you can use
``/* clang-format off */`` and ``/* clang-format on */`` to tell
clang-format to ignore a chunk of code.
.. seealso::
These guidelines only cover code formatting. See :ref:`doc_cpp_usage_guidelines`
for a list of language features that are permitted in pull requests.
Using clang-format locally
~~~~~~~~~~~~~~~~~~~~~~~~~~
You need to use **clang-format 17** to be compatible with Godot's format. Later versions might
be suitable, but previous versions may not support all used options, or format
some things differently, leading to style issues in pull requests.
.. _doc_code_style_guidelines_pre_commit_hook:
Pre-commit hook
^^^^^^^^^^^^^^^
For ease of use, we provide hooks for Git with the `pre-commit <https://pre-commit.com/>`__
Python framework that will run clang-format automatically on all your commits with the
correct version of clang-format.
To set up:
::
pip install pre-commit
pre-commit install
You can also run the hook manually with ``pre-commit run``.
.. note::
Previously, we supplied a hook in the folder ``misc/hooks``. If you copied the
script manually, these hooks should still work, but symlinks will be broken.
If you are using the new system, run ``rm .git/hooks/*`` to remove the old hooks
that are no longer needed.
Installation
^^^^^^^^^^^^
Here's how to install clang-format:
- Linux: It will usually be available out-of-the-box with the clang toolchain
packaged by your distribution. If your distro version is not the required one,
you can download a pre-compiled version from the
`LLVM website <https://releases.llvm.org/download.html>`__, or if you are on
a Debian derivative, use the `upstream repos <https://apt.llvm.org/>`__.
- macOS and Windows: You can download precompiled binaries from the
`LLVM website <https://releases.llvm.org/download.html>`__. You may need to add
the path to the binary's folder to your system's ``PATH`` environment
variable to be able to call clang-format out of the box.
You then have different possibilities to apply clang-format to your changes:
Manual usage
^^^^^^^^^^^^
You can apply clang-format manually for one or more files with the following
command:
::
clang-format -i <path/to/file(s)>
- ``-i`` means that the changes should be written directly to the file (by
default clang-format would only output the fixed version to the terminal).
- The path can point to several files, either one after the other or using
wildcards like in a typical Unix shell. Be careful when globbing so that
you don't run clang-format on compiled objects (.o and .a files) that are
in Godot's tree. So better use ``core/*.{cpp,h}`` than ``core/*``.
IDE plugin
^^^^^^^^^^
Most IDEs or code editors have beautifier plugins that can be configured to run
clang-format automatically, for example, each time you save a file.
Here is a non-exhaustive list of beautifier plugins for some IDEs:
- Qt Creator: `Beautifier plugin <https://doc.qt.io/qtcreator/creator-beautifier.html>`__
- Visual Studio Code: `Clang-Format <https://marketplace.visualstudio.com/items?itemName=xaver.clang-format>`__
- Visual Studio: `Clang Power Tools 2022 <https://marketplace.visualstudio.com/items?itemName=caphyon.ClangPowerTools2022>`__
- vim: `vim-clang-format <https://github.com/rhysd/vim-clang-format>`__
- CLion: Starting from version ``2019.1``, no plugin is required. Instead, enable
`ClangFormat <https://www.jetbrains.com/help/clion/clangformat-as-alternative-formatter.html#clion-support>`__
(Pull requests are welcome to extend this list with tested plugins.)
.. _doc_code_style_guidelines_header_includes:
Header includes
~~~~~~~~~~~~~~~
When adding new C++ or Objective-C files or including new headers in existing
ones, the following rules should be followed:
- The first lines in the file should be Godot's copyright header and MIT
license, copy-pasted from another file. Make sure to adjust the filename.
- In a ``.h`` header, include guards should be used with the form
``FILENAME_H``.
- In a ``.cpp`` file (e.g. ``filename.cpp``), the first include should be the
one where the class is declared (e.g. ``#include "filename.h"``), followed by
an empty line for separation.
- Then come headers from Godot's own code base, included in alphabetical order
(enforced by ``clang-format``) with paths relative to the root folder. Those
includes should be done with quotes, e.g. ``#include "core/object.h"``. The
block of Godot header includes should then be followed by an empty line for
separation.
- Finally, third-party headers (either from ``thirdparty`` or from the system's
include paths) come next and should be included with the < and > symbols, e.g.
``#include <png.h>``. The block of third-party headers should also be followed
by an empty line for separation.
- Godot and third-party headers should be included in the file that requires
them, i.e. in the `.h` header if used in the declarative code or in the `.cpp`
if used only in the imperative code.
Example:
.. code-block:: cpp
:caption: my_new_file.h
/**************************************************************************/
/* my_new_file.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/hash_map.h"
#include "core/list.h"
#include "scene/gui/control.h"
#include <png.h>
.. code-block:: cpp
:caption: my_new_file.cpp
/**************************************************************************/
/* my_new_file.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "my_new_file.h"
#include "core/math/math_funcs.h"
#include "scene/gui/line_edit.h"
#include <zlib.h>
#include <zstd.h>
Java
----
Godot's Java code (mostly in ``platform/android``) is also enforced via
``clang-format``, so see the instructions above to set it up. Keep in mind that
this style guide only applies to code written and maintained by Godot, not
third-party code such as the ``java/src/com/google`` subfolder.
Python
------
Godot's SCons buildsystem is written in Python, and various scripts included
in the source tree are also using Python.
For those, we use the `Ruff linter and code formatter <https://docs.astral.sh/ruff/>`__.
Using ruff locally
~~~~~~~~~~~~~~~~~~~
First of all, you will need to install Ruff. Ruff requires Python 3.7+ to run.
Installation
^^^^^^^^^^^^
Here's how to install ruff:
::
pip3 install ruff --user
You then have different possibilities to apply ruff to your changes:
Manual usage
^^^^^^^^^^^^
You can apply ``ruff`` manually to one or more files with the following
command:
::
ruff -l 120 <path/to/file(s)>
- ``-l 120`` means that the allowed number of characters per line is 120.
This number was agreed upon by the developers.
- The path can point to several files, either one after the other or using
wildcards like in a typical Unix shell.
Pre-commit hook
~~~~~~~~~~~~~~~
For ease of use, we provide hooks for Git with the `pre-commit <https://pre-commit.com/>`__
Python framework that will run ``ruff`` automatically on all your commits with the
correct version of ``ruff``.
To set up:
::
pip install pre-commit
pre-commit install
You can also run the hook manually with ``pre-commit run``.
.. note::
Previously, we supplied a hook in the folder ``misc/hooks``. If you copied the
script manually, these hooks should still work, but symlinks will be broken.
If you are using the new system, run ``rm .git/hooks/*`` to remove the old hooks
that are no longer needed.
Editor integration
^^^^^^^^^^^^^^^^^^
Many IDEs or code editors have beautifier plugins that can be configured to run
ruff automatically, for example, each time you save a file. For details, you can
check `Ruff Editor Integrations <https://docs.astral.sh/ruff/editors/>`__.
Comment style guide
-------------------
This comment style guide applies to all programming languages used within
Godot's codebase.
- Begin comments with a space character to distinguish them from disabled code.
- Use sentence case for comments. Begin comments with an uppercase character and
always end them with a period.
- Reference variable/function names and values using backticks.
- Wrap comments to ~100 characters.
- You can use ``TODO:``, ``FIXME:``, ``NOTE:``, ``WARNING:``, or ``HACK:`` as admonitions
when needed.
**Example:**
.. code-block:: cpp
// Compute the first 10,000 decimals of Pi.
// FIXME: Don't crash when computing the 1,337th decimal due to `increment`
// being negative.
Don't repeat what the code says in a comment. Explain the *why* rather than *how*.
**Bad:**
.. code-block:: cpp
// Draw loading screen.
draw_load_screen();
You can use Javadoc-style comments above function or macro definitions. It's
recommended to use Javadoc-style comments *only* for methods which are not
exposed to scripting. This is because exposed methods should be documented in
the :ref:`class reference XML <doc_updating_the_class_reference>`
instead.
**Example:**
.. code-block:: cpp
/**
* Returns the number of nodes in the universe.
* This can potentially be a very large number, hence the 64-bit return type.
*/
uint64_t Universe::get_node_count() {
// ...
}
For member variables, don't use Javadoc-style comments, but use single-line comments instead:
.. code-block:: cpp
class Universe {
// The cached number of nodes in the universe.
// This value may not always be up-to-date with the current number of nodes
// in the universe.
uint64_t node_count_cached = 0;
};

View File

@@ -0,0 +1,144 @@
.. _doc_handling_compatibility_breakages:
Handling compatibility breakages
================================
.. TODO: Elaborate on types of compatibility and procedure.
So you've added a new parameter to a method, changed the return type,
changed the type of a parameter, or changed its default value,
and now the automated testing is complaining about compatibility breakages?
Breaking compatibility should be avoided, but when necessary there are systems in place
to handle this in a way that makes the transition as smooth as possible.
A practical example
-------------------
.. TODO: Add example that showcases more details like original default arguments etc.
These changes are taken from `pull request #88047 <https://github.com/godotengine/godot/pull/88047>`_, which added
new pathing options to ``AStarGrid2D`` and other AStar classes.
Among other changes, these methods were modified in ``core/math/a_star_grid_2d.h``:
.. code-block:: cpp
Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to);
TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to);
To:
.. code-block:: cpp
Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to, bool p_allow_partial_path = false);
TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to, bool p_allow_partial_path = false);
This meant adding new compatibility method bindings to the file, which should be in the ``protected`` section of
the code, usually placed next to ``_bind_methods()``:
.. code-block:: cpp
#ifndef DISABLE_DEPRECATED
TypedArray<Vector2i> _get_id_path_bind_compat_88047(const Vector2i &p_from, const Vector2i &p_to);
Vector<Vector2> _get_point_path_bind_compat_88047(const Vector2i &p_from, const Vector2i &p_to);
static void _bind_compatibility_methods();
#endif
They should start with an ``_`` to indicate that they are internal, and end with ``_bind_compat_`` followed by the PR number
that introduced the change (``88047`` in this example). These compatibility methods need to be implemented in a dedicated file,
like ``core/math/a_star_grid_2d.compat.inc`` in this case:
.. code-block:: cpp
:caption: core/math/a_star_grid_2d.compat.inc
/**************************************************************************/
/* a_star_grid_2d.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DISABLE_DEPRECATED
#include "core/variant/typed_array.h"
TypedArray<Vector2i> AStarGrid2D::_get_id_path_bind_compat_88047(const Vector2i &p_from_id, const Vector2i &p_to_id) {
return get_id_path(p_from_id, p_to_id, false);
}
Vector<Vector2> AStarGrid2D::_get_point_path_bind_compat_88047(const Vector2i &p_from_id, const Vector2i &p_to_id) {
return get_point_path(p_from_id, p_to_id, false);
}
void AStarGrid2D::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStarGrid2D::_get_id_path_bind_compat_88047);
ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStarGrid2D::_get_point_path_bind_compat_88047);
}
#endif // DISABLE_DEPRECATED
Unless the change in compatibility is complex, the compatibility method should call the modified method directly,
instead of duplicating that method. Make sure to match the default arguments for that method (in the example above this would be ``false``).
This file should always be placed next to the original file, and have ``.compat.inc`` at the end instead of ``.cpp`` or ``.h``.
Next, this should be included in the ``.cpp`` file we're adding compatibility methods to, so ``core/math/a_star_grid_2d.cpp``:
.. code-block:: cpp
:caption: core/math/a_star_grid_2d.cpp
#include "a_star_grid_2d.h"
#include "a_star_grid_2d.compat.inc"
#include "core/variant/typed_array.h"
And finally, the changes reported by the API validation step should be added to the relevant validation file. Because this was
done during the development of 4.3, this would be ``misc/extension_api_validation/4.2-stable.expected`` (including changes not shown in
this example):
.. code-block:: text
:caption: misc/extension_api_validation/4.2-stable.expected
GH-88047
--------
Validate extension JSON: Error: Field 'classes/AStar2D/methods/get_id_path/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/AStar2D/methods/get_point_path/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/AStar3D/methods/get_id_path/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/AStar3D/methods/get_point_path/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/AStarGrid2D/methods/get_id_path/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/AStarGrid2D/methods/get_point_path/arguments': size changed value in new API, from 2 to 3.
Added optional "allow_partial_path" argument to get_id_path and get_point_path methods in AStar classes.
Compatibility methods registered.
The instructions for how to add to that file are at the top of the file itself.
If you get a "Hash changed" error for a method, it means that the compatibility binding is missing or incorrect.
Such lines shouldn't be added to the ``.expected`` file, but fixed by binding the proper compatibility method.
And that's it! You might run into a bit more complicated cases, like rearranging arguments,
changing return types, etc., but this covers the basic on how to use this system.
For more information, see `pull request #76446 <https://github.com/godotengine/godot/pull/76446>`_.

View File

@@ -0,0 +1,130 @@
.. _doc_cpp_usage_guidelines:
C++ usage guidelines
====================
Rationale
---------
Since Godot 4.0, the C++ standard used throughout the codebase is a subset of
**C++17**. While modern C++ brings a lot of opportunities to write faster, more
readable code, we chose to restrict our usage of C++ to a subset for a few
reasons:
- It makes it easier to review code in online editors. This is because engine
contributors don't always have access to a full-featured IDE while reviewing
code.
- It makes the code easier to grasp for beginner contributors (who may not be
professional C++ programmers). Godot's codebase is known to be easy to learn
from, and we'd like to keep it that way.
To get your pull request merged, it needs to follow the C++ usage guidelines
outlined here. Of course, you can use features not allowed here in your own C++
modules or GDExtensions.
.. note::
Prior to Godot 4.0, the C++ standard used throughout the codebase was C++03,
with a handful of C++14 extensions. If you are contributing a pull request
to the `3.x` branch rather than `master`, your code can't use C++17 features.
Instead, your code must be able to be built with a C++14 compiler.
The guidelines below don't apply to third-party dependencies, although we
generally favor small libraries instead of larger solutions. See also
:ref:`doc_best_practices_for_engine_contributors`.
.. seealso::
See :ref:`doc_code_style_guidelines` for formatting guidelines.
Disallowed features
-------------------
**Any feature not listed below is allowed.** Using features like ``constexpr``
variables and ``nullptr`` is encouraged when possible. Still, try to keep your
use of modern C++ features conservative. Their use needs to serve a real
purpose, such as improving code readability or performance.
.. _doc_cpp_godot_types:
Standard Template Library
~~~~~~~~~~~~~~~~~~~~~~~~~
We don't allow using the `STL <https://en.wikipedia.org/wiki/Standard_Template_Library>`__
as Godot provides its own data types (among other things).
See :ref:`doc_faq_why_not_stl` for more information.
This means that pull requests should **not** use ``std::string``,
``std::vector`` and the like. Instead, use Godot's datatypes as described in
the :ref:`doc_core_types` documentation.
A 📜 icon denotes the type is part of :ref:`Variant <doc_variant_class>`. This
means it can be used as a parameter or return value of a method exposed to the
scripting API.
``auto`` keyword
~~~~~~~~~~~~~~~~
Please don't use the ``auto`` keyword for type inference. While it can avoid
repetition, it can also lead to confusing code:
.. code-block:: cpp
// Not so confusing...
auto button = memnew(Button);
// ...but what about this?
auto result = EditorNode::get_singleton()->get_complex_result();
Keep in mind hover documentation often isn't readily available for pull request
reviewers. Most of the time, reviewers will use GitHub's online viewer to review
pull requests.
The ``auto`` keyword can be used in some special cases, like C++ lambda or Objective-C block
definitions and C++ templates. Please ask before using templates with ``auto`` in a pull request.
.. code-block:: cpp
// Full type definitions.
void (*mult64to128)(uint64_t, uint64_t, uint64_t &, uint64_t &) = [](uint64_t u, uint64_t v, uint64_t &h, uint64_t &l) { ... }
void (^JOYSTICK_LEFT)(GCControllerDirectionPad *__strong, float, float) = ^(GCControllerDirectionPad *dpad, float xValue, float yValue) { ... }
// Less clutter with auto.
auto mult64to128 = [](uint64_t u, uint64_t v, uint64_t &h, uint64_t &l) { ... }
auto JOYSTICK_LEFT = ^(GCControllerDirectionPad *dpad, float xValue, float yValue) { ... }
// Compare function for different types.
template <typename T1, typename T2>
constexpr auto MIN(const T1 m_a, const T2 m_b) {
return m_a < m_b ? m_a : m_b;
}
We chose to forbid ``auto`` in all other cases. Thank you for your understanding.
Lambdas
~~~~~~~
Lambdas should be used conservatively when they make code effectively faster or
simpler, and do not impede readability. Please ask before using lambdas in a
pull request.
``#ifdef``-based include guards
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Starting with 4.5, all files now use the ``#pragma once`` directive, as they
improve readability and declutter macros. Use of ``#ifdef``-based include
guards are now actively discouraged.
``try``-``catch`` blocks
~~~~~~~~~~~~~~~~~~~~~~~~
C++ style exception handling using ``try`` and ``catch`` blocks is forbidden.
This restriction is in place for several reasons, including performance, binary
size and code complexity.
Use :ref:`doc_common_engine_methods_and_macros_error_macros` instead.
.. seealso::
See :ref:`doc_code_style_guidelines_header_includes` for guidelines on sorting
includes in C++ and Objective-C files.

View File

@@ -0,0 +1,579 @@
.. _doc_pr_workflow:
Creating pull requests
======================
.. highlight:: shell
The so-called "PR workflow" used by Godot is common to many projects using
Git, and should be familiar to veteran free software contributors. The idea
is that only a small number (if any) commit directly to the *master* branch.
Instead, contributors *fork* the project (i.e. create a copy of it, which
they can modify as they wish), and then use the GitHub interface to request
a *pull* from one of their fork's branches to one branch of the original
(often named *upstream*) repository.
The resulting *pull request* (PR) can then be reviewed by other contributors,
which might approve it, reject it, or most often request that modifications
be done. Once approved, the PR can then be merged by one of the core
developers, and its commit(s) will become part of the target branch (usually
the *master* branch).
We will go together through an example to show the typical workflow and
associated Git commands. But first, let's have a quick look at the
organization of Godot's Git repository.
Git source repository
---------------------
The `repository on GitHub <https://github.com/godotengine/godot>`_ is a
`Git <https://git-scm.com>`_ code repository together with an embedded
issue tracker and PR system.
.. note:: If you are contributing to the documentation, its repository can
be found `here <https://github.com/godotengine/godot-docs>`_.
The Git version control system is the tool used to keep track of successive
edits to the source code - to contribute efficiently to Godot, learning the
basics of the Git command line is *highly* recommended. There exist some
graphical interfaces for Git, but they usually encourage users to take bad
habits regarding the Git and PR workflow, and we therefore recommend not to
use them. In particular, we advise not to use GitHub's online editor for code
contributions (although it's tolerated for small fixes or documentation changes)
as it enforces one commit per file and per modification,
which quickly leads to PRs with an unreadable Git history (especially after peer review).
.. seealso:: The first sections of Git's "Book" are a good introduction to
the tool's philosophy and the various commands you need to
master in your daily workflow. You can read them online on the
`Git SCM <https://git-scm.com/book/en/v2>`_ website.
You can also try out `GitHub's interactive guide <https://try.github.io/>`__.
The branches on the Git repository are organized as follows:
- The ``master`` branch is where the development of the next major version
occurs. As a development branch, it can be unstable
and is not meant for use in production. This is where PRs should be done
in priority.
- The stable branches are named after their version, e.g. ``3.1`` and ``2.1``.
They are used to backport bugfixes and enhancements from the ``master``
branch to the currently maintained stable release (e.g. 3.1.2 or 2.1.6).
As a rule of thumb, the last stable branch is maintained until the next
minor version (e.g. the ``3.0`` branch was maintained until the release of
Godot 3.1).
If you want to make PRs against a maintained stable branch, please check
first if your changes are also relevant for the ``master`` branch, and if so
make the PR for the ``master`` branch in priority. Release managers can then
cherry-pick the fix to a stable branch if relevant.
- There might occasionally be feature branches, usually meant to be merged into
the ``master`` branch at some time.
Forking and cloning
-------------------
The first step is to *fork* the `godotengine/godot <https://github.com/godotengine/godot>`_
repository on GitHub. To do so, you will need to have a GitHub account and to
be logged in. In the top right corner of the repository's GitHub page, you
should see the "Fork" button as shown below:
.. image:: img/github_fork_button.png
Click it, and after a while you should be redirected to your own fork of the
Godot repo, with your GitHub username as namespace:
.. image:: img/github_fork_url.png
You can then *clone* your fork, i.e. create a local copy of the online
repository (in Git speak, the *origin remote*). If you haven't already,
download Git from `its website <https://git-scm.com>`_ if you're using Windows or
macOS, or install it through your package manager if you're using Linux.
.. note:: If you are on Windows, open Git Bash to type commands. macOS and Linux users
can use their respective terminals.
To clone your fork from GitHub, use the following command:
::
git clone https://github.com/USERNAME/godot
After a little while, you should have a ``godot`` directory in your current
working directory. Move into it using the ``cd`` command:
::
cd godot
We will start by setting up a reference to the original repository that we forked:
::
git remote add upstream https://github.com/godotengine/godot
git fetch upstream
This will create a reference named ``upstream`` pointing to the original
``godotengine/godot`` repository. This will be useful when you want to pull new
commits from its ``master`` branch to update your fork. You have another
remote reference named ``origin``, which points to your fork (``USERNAME/godot``).
You only need to do the above steps once, as long as you keep that local
``godot`` folder (which you can move around if you want, the relevant
metadata is hidden in its ``.git`` subfolder).
.. note:: *Branch it, pull it, code it, stage it, commit, push it, rebase
it... technologic.*
This bad take on Daft Punk's *Technologic* shows the general
conception Git beginners have of its workflow: lots of strange
commands to learn by copy and paste, hoping they will work as
expected. And that's actually not a bad way to learn, as long as
you're curious and don't hesitate to question your search engine
when lost, so we will give you the basic commands to know when
working in Git.
In the following, we will assume as an example that you want to implement a feature in
Godot's Project Manager, which is coded in the ``editor/project_manager.cpp``
file.
Branching
---------
By default, the ``git clone`` should have put you on the ``master`` branch of
your fork (``origin``). To start your own feature development, we will create
a feature branch:
::
# Create the branch based on the current branch (master)
git branch better-project-manager
# Change the current branch to the new one
git checkout better-project-manager
This command is equivalent:
::
# Change the current branch to a new named one, based on the current branch
git checkout -b better-project-manager
If you want to go back to the ``master`` branch, you'd use:
::
git checkout master
You can see which branch you are currently on with the ``git branch``
command:
::
git branch
2.1
* better-project-manager
master
Be sure to always go back to the ``master`` branch before creating a new branch,
as your current branch will be used as the base for the new one. Alternatively,
you can specify a custom base branch after the new branch's name:
::
git checkout -b my-new-feature master
Updating your branch
--------------------
This would not be needed the first time (just after you forked the upstream
repository). However, the next time you want to work on something, you will
notice that your fork's ``master`` is several commits behind the upstream
``master`` branch: pull requests from other contributors would have been merged
in the meantime.
To ensure there won't be conflicts between the feature you develop and the
current upstream ``master`` branch, you will have to update your branch by
*pulling* the upstream branch.
::
git pull --rebase upstream master
The ``--rebase`` argument will ensure that any local changes that you committed
will be re-applied *on top* of the pulled branch, which is usually what we want
in our PR workflow. This way, when you open a pull request, your own commits will
be the only difference with the upstream ``master`` branch.
While rebasing, conflicts may arise if your commits modified code that has been
changed in the upstream branch in the meantime. If that happens, Git will stop at
the conflicting commit and will ask you to resolve the conflicts. You can do so
with any text editor, then stage the changes (more on that later), and proceed with
``git rebase --continue``. Repeat the operation if later commits have conflicts too,
until the rebase operation completes.
If you're unsure about what is going on during a rebase and you panic (no worry,
we all do the first few times), you can abort the rebase with ``git rebase --abort``.
You will then be back to the original state of your branch before calling
``git pull --rebase``.
.. note:: If you omit the ``--rebase`` argument, you will instead create a merge
commit which tells Git what to make of the two distinct branches. If any
conflicts arise, they would be resolved all at once via this merge commit.
While this is a valid workflow and the default behavior of ``git pull``,
merge commits within PRs are frowned upon in our PR workflow. We only use
them when merging PRs into the upstream branch.
The philosophy is that a PR should represent the final stage of the changes
made to the codebase, and we are not interested in mistakes and fixes that
would have been done in intermediate stages before merging.
Git gives us great tools to "rewrite the history" and make it as if we got
things right the first time, and we're happy to use it to ensure that
changes are easy to review and understand long after they have been merged.
If you have already created a merge commit without using ``rebase``, or
have made any other changes that have resulted in undesired history, the best option
is to use an *interactive rebase* on the upstream branch. See the :ref:`dedicated
section <doc_pr_workflow_rebase>` for instructions.
.. tip:: If at any time you want to *reset* a local branch to a given commit or branch,
you can do so with ``git reset --hard <commit ID>`` or
``git reset --hard <remote>/<branch>`` (e.g. ``git reset --hard upstream/master``).
Be warned that this will remove any changes that you might have committed in
this branch. If you ever lose commits by mistake, use the ``git reflog`` command
to find the commit ID of the previous state that you would like to restore, and
use it as argument of ``git reset --hard`` to go back to that state.
Making changes
--------------
You would then do your changes to our example's
``editor/project_manager.cpp`` file with your usual development environment
(text editor, IDE, etc.).
By default, those changes are *unstaged*. The staging area is a layer between
your working directory (where you make your modifications) and the local Git
repository (the commits and all the metadata in the ``.git`` folder). To
bring changes from the working directory to the Git repository, you need to
*stage* them with the ``git add`` command, and then to commit them with the
``git commit`` command.
There are various commands you should know to review your current work,
before staging it, while it is staged, and after it has been committed.
- ``git diff`` will show you the current unstaged changes, i.e. the
differences between your working directory and the staging area.
- ``git checkout -- <files>`` will undo the unstaged changes to the given
files.
- ``git add <files>`` will *stage* the changes on the listed files.
- ``git diff --staged`` will show the current staged changes, i.e. the
differences between the staging area and the last commit.
- ``git reset HEAD <files>`` will *unstage* changes to the listed files.
- ``git status`` will show you what are the currently staged and unstaged
modifications.
- ``git commit`` will commit the staged files. It will open a text editor
(you can define the one you want to use with the ``GIT_EDITOR`` environment
variable or the ``core.editor`` setting in your Git configuration) to let you
write a commit log. You can use ``git commit -m "Cool commit log"`` to
write the log directly.
- ``git commit --amend`` lets you amend the last commit with your currently
staged changes (added with ``git add``). This is the best option if you
want to fix a mistake in the last commit (bug, typo, style issue, etc.).
- ``git log`` will show you the last commits of your current branch. If you
did local commits, they should be shown at the top.
- ``git show`` will show you the changes of the last commit. You can also
specify a commit hash to see the changes for that commit.
That's a lot to memorize! Don't worry, just check this cheat sheet when you
need to make changes, and learn by doing.
Here's how the shell history could look like on our example:
::
# It's nice to know where you're starting from
git log
# Do changes to the Project Manager with the nano text editor
nano editor/project_manager.cpp
# Find an unrelated bug in Control and fix it
nano scene/gui/control.cpp
# Review changes
git status
git diff
# We'll do two commits for our unrelated changes,
# starting by the Control changes necessary for the PM enhancements
git add scene/gui/control.cpp
git commit -m "Fix handling of margins in Control"
# Check we did good
git log
git show
git status
# Make our second commit
git add editor/project_manager.cpp
git commit -m "Add a pretty banner to the Project Manager"
git log
With this, we should have two new commits in our ``better-project-manager``
branch which were not in the ``master`` branch. They are still only local
though, the remote fork does not know about them, nor does the upstream repo.
Pushing changes to a remote
---------------------------
That's where ``git push`` will come into play. In Git, a commit is always
done in the local repository (unlike Subversion where a commit will modify
the remote repository directly). You need to *push* the new commits to a
remote branch to share them with the world. The syntax for this is:
::
git push <remote> <local branch>[:<remote branch>]
The part about the remote branch can be omitted if you want it to have the
same name as the local branch, which is our case in this example, so we will
do:
::
git push origin better-project-manager
Git will ask you for your username and password. For your password, enter your
GitHub Personal Access Token (PAT). If you do not have a GitHub Personal Access
Token, or do not have one with the correct permissions for your newly forked
repository, you will need to create one. Follow this link to create your Personal
Access Token: `Creating a personal access token
<https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token>`_.
After you have successfully verified your account using your PAT, the changes
will be sent to your remote repository. If you check the fork's page on GitHub,
you should see a new branch with your added commits.
Issuing a pull request
----------------------
When you load your fork's branch on GitHub, you should see a line saying
*"This branch is 2 commits ahead of godotengine:master."* (and potentially some
commits behind, if your ``master`` branch was out of sync with the upstream
``master`` branch).
.. image:: img/github_fork_make_pr.png
On that line, there is a "Pull request" link. Clicking it will open a form
that will let you issue a pull request on the ``godotengine/godot`` upstream
repository. It should show you your two commits, and state "Able to merge".
If not (e.g. it has way more commits, or says there are merge conflicts),
don't create the PR yet, something went wrong. Go to our
`Godot Contributors Chat <https://chat.godotengine.org/>`_ and ask for support :)
Use an explicit title for the PR and put the necessary details in the comment
area. You can drag and drop screenshots, GIFs or zipped projects if relevant,
to showcase what your work implements. Click "Create a pull request", and
tadaa!
Modifying a pull request
------------------------
While it is reviewed by other contributors, you will often need to make
changes to your yet-unmerged PR, either because contributors requested them,
or because you found issues yourself while testing.
The good news is that you can modify a pull request simply by acting on the
branch you made the pull request from. You can e.g. make a new commit on that
branch, push it to your fork, and the PR will be updated automatically:
::
# Check out your branch again if you had changed in the meantime
git checkout better-project-manager
# Fix a mistake
nano editor/project_manager.cpp
git add editor/project_manager.cpp
git commit -m "Fix a typo in the banner's title"
git push origin better-project-manager
However, be aware that in our PR workflow, we favor commits that bring the
codebase from one functional state to another functional state, without having
intermediate commits fixing up bugs in your own code or style issues. Most of
the time, we will prefer a single commit in a given PR (unless there's a good
reason to keep the changes separate). Instead of authoring a new commit,
consider using ``git commit --amend`` to amend the previous commit with your
fixes. The above example would then become:
::
# Check out your branch again if you had changed in the meantime
git checkout better-project-manager
# Fix a mistake
nano editor/project_manager.cpp
git add editor/project_manager.cpp
# --amend will change the previous commit, so you will have the opportunity
# to edit its commit message if relevant.
git commit --amend
# As we modified the last commit, it no longer matches the one from your
# remote branch, so we need to force push to overwrite that branch.
git push --force origin better-project-manager
.. Kept for compatibility with the previous title, linked in many PRs.
.. _mastering-the-pr-workflow-the-rebase:
.. _doc_pr_workflow_rebase:
The interactive rebase
----------------------
If you didn't follow the above steps closely to *amend* changes into a commit
instead of creating fixup commits, or if you authored your changes without being
aware of our workflow and Git usage tips, reviewers might request you to
*rebase* your branch to *squash* some or all of the commits into one.
Indeed, if some commits have been made following reviews to fix bugs, typos, etc.
in the original commit, they are not relevant to a future changelog reader who
would want to know what happened in the Godot codebase, or when and how a given
file was last modified.
To squash those extraneous commits into the main one, we will have to *rewrite
history*. Right, we have that power. You may read that it's a bad practice, and
it's true when it comes to branches of the upstream repo. But in your fork, you
can do whatever you want, and everything is allowed to get neat PRs :)
We will use the *interactive rebase* ``git rebase -i`` to do this. This
command takes a commit ID or a branch name as argument, and will let you modify
all commits between that commit/branch and the last one in your working branch,
the so-called ``HEAD``.
While you can give any commit ID to ``git rebase -i`` and review everything in
between, the most common and convenient workflow involves rebasing on the
upstream ``master`` branch, which you can do with:
::
git rebase -i upstream/master
.. note:: Referencing branches in Git is a bit tricky due to the distinction
between remote and local branches. Here, ``upstream/master`` (with a
`/`) is a local branch which has been pulled from the ``upstream``
remote's ``master`` branch.
Interactive rebases can only be done on local branches, so the `/`
is important here. As the upstream remote changes frequently, your
local ``upstream/master`` branch may become outdated, so you can
update it with ``git fetch upstream master``. Contrarily to
``git pull --rebase upstream master`` which would update your
currently checked out branch, ``fetch`` will only update the
``upstream/master`` reference (which is distinct from your local
``master`` branch... yes it's confusing, but you'll become familiar
with this little by little).
This will open a text editor (``vi`` by default, see
`Git docs <https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#_core_editor>`_
to configure your favorite one) with something which may look like this:
.. code-block:: text
pick 1b4aad7 Add a pretty banner to the Project Manager
pick e07077e Fix a typo in the banner's title
The editor will also show instructions regarding how you can act on those
commits. In particular, it should tell you that "pick" means to use that
commit (do nothing), and that "squash" and "fixup" can be used to *meld* the
commit in its parent commit. The difference between "squash" and "fixup" is
that "fixup" will discard the commit log from the squashed commit. In our
example, we are not interested in keeping the log of the "Fix a typo" commit,
so we use:
.. code-block:: text
pick 1b4aad7 Add a pretty banner to the Project Manager
fixup e07077e Fix a typo in the banner's title
Upon saving and quitting the editor, the rebase will occur. The second commit
will be melded into the first one, and ``git log`` and ``git show`` should
now confirm that you have only one commit with the changes from both previous
commits.
But! You rewrote the history, and now your local and remote branches have
diverged. Indeed, commit 1b4aad7 in the above example will have changed, and
therefore got a new commit hash. If you try to push to your remote branch, it
will raise an error:
::
git push origin better-project-manager
To https://github.com/akien-mga/godot
! [rejected] better-project-manager -> better-project-manager (non-fast-forward)
error: failed to push some refs to 'https://akien-mga@github.com/akien-mga/godot'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.
This is reasonable behavior, Git will not let you push changes that would
override remote content. But that's actually what we want to do here, so we
will have to *force* it:
::
git push --force origin better-project-manager
And tadaa! Git will happily *replace* your remote branch with what you had
locally (so make sure that's what you wanted, using ``git log``). This will
also update the PR accordingly.
Rebasing onto another branch
----------------------------
If you have accidentally opened your PR on the wrong branch, or need to target another branch
for some reason, you might need to filter out a lot of commits that differ between the old branch
(for example ``4.2``) and the new branch (for example ``master``). This can make rebasing difficult
and tedious. Fortunately ``git`` has a command just for this situation, ``git rebase --onto``.
If your PR was created from the ``4.2`` branch and you want to update it to instead start at ``master``
the following steps *should* fix this in one step:
.. code-block:: text
git rebase -i --onto master 4.2
This will take all the commits on your branch *after* the ``4.2`` branch, and then splice them on top of ``master``,
ignoring any commits from the ``4.2`` branch not on the ``master`` branch. You may still need to do some fixing, but
this command should save you a lot of tedious work removing commits.
Just like above for the interactive rebase you need to force push your branch to handle the different changes:
::
git push --force origin better-project-manager
Deleting a Git branch
---------------------
After your pull request gets merged, there's one last thing you should do: delete your
Git branch for the PR. There won't be issues if you don't delete your branch, but it's
good practice to do so. You'll need to do this twice, once for the local branch and another
for the remote branch on GitHub.
To delete our better Project Manager branch locally, use this command:
::
git branch -d better-project-manager
Alternatively, if the branch hadn't been merged yet and we wanted to delete it anyway, instead
of ``-d`` you would use ``-D``.
Next, to delete the remote branch on GitHub use this command:
::
git push origin -d better-project-manager
You can also delete the remote branch from the GitHub PR itself, a button should appear once
it has been merged or closed.

60
engine/first_steps.rst Normal file
View File

@@ -0,0 +1,60 @@
First Steps
===========
The possibility to study, use, modify and redistribute modifications of the
engine's source code are the fundamental rights that
Godot's `MIT <https://tldrlegal.com/license/mit-license>`_ license grants you,
making it `free and open source software <https://en.wikipedia.org/wiki/Free_and_open-source_software>`_.
As such, everyone is entitled to modify
`Godot's source code <https://github.com/godotengine/godot>`_, and send those
modifications back to the upstream project in the form of a patch (a text file
describing the changes in a ready-to-apply manner) or - in the modern workflow
that we use - via a so-called "pull request" (PR), i.e. a proposal to directly
merge one or more Git commits (patches) into the main development branch.
Contributing code changes upstream has two big advantages:
- Your own code will be reviewed and improved by other developers, and will be
further maintained directly in the upstream project, so you won't have to
reapply your own changes every time you move to a newer version. On the
other hand it comes with a responsibility, as your changes have to be
generic enough to be beneficial to all users, and not just your project; so
in some cases it might still be relevant to keep your changes only for your
own project, if they are too specific.
- The whole community will benefit from your work, and other contributors will
behave the same way, contributing code that will be beneficial to you. At
the time of this writing, over 2,000 developers have contributed code
changes to the engine!
To ensure good collaboration and overall quality, the Godot developers
enforce some rules for code contributions, for example regarding the style to
use in the C++ code (indentation, brackets, etc.) or the Git and PR workflow.
A good place to start is by searching for issues tagged as
`good first issue <https://github.com/godotengine/godot/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22>`_
on GitHub.
.. seealso:: Technical details about the PR workflow are outlined in a
specific section, :ref:`doc_pr_workflow`.
Details about the code style guidelines and the ``clang-format``
tool used to enforce them are outlined in
:ref:`doc_code_style_guidelines`.
All pull requests must go through a review process before being accepted.
Depending on the scope of the changes, it may take some time for a maintainer
responsible for the modified part of the engine to provide their review.
We value all of our contributors and ask them to be patient in the meantime,
as it is expected that in an open source project like Godot, there is going to be
way more contributions than people validating them.
To make sure that your time and efforts aren't wasted, it is recommended to vet the idea
first before implementing it and putting it for a review as a PR. To that end, Godot
has a `proposal system <https://github.com/godotengine/godot-proposals>`_. Its
usage is encouraged to plan changes and discuss them with the community. Implementation
details can also be discussed with other contributors on the `Godot Contributors Chat <https://chat.godotengine.org/>`_.
.. note:: Proposals are only required when working on an enhancement or a new feature.
Bug reports are sufficient for fixing issues.

455
engine/unit_tests.rst Normal file
View File

@@ -0,0 +1,455 @@
.. _doc_unit_testing:
Unit testing
============
Godot Engine allows to write unit tests directly in C++. The engine integrates
the `doctest <https://github.com/doctest/doctest>`_ unit testing framework which
gives ability to write test suites and test cases next to production code, but
since the tests in Godot go through a different ``main`` entry point, the tests
reside in a dedicated ``tests/`` directory instead, which is located at the root
of the engine source code.
Platform and target support
---------------------------
C++ unit tests can be run on Linux, macOS, and Windows operating systems.
Tests can only be run with editor ``tools`` enabled, which means that export
templates cannot be tested currently.
Running tests
-------------
Before tests can be actually run, the engine must be compiled with the ``tests``
build option enabled (and any other build option you typically use), as the
tests are not compiled as part of the engine by default:
.. code-block:: shell
scons tests=yes
Once the build is done, run the tests with a ``--test`` command-line option:
.. code-block:: shell
./bin/<godot_binary> --test
The test run can be configured with the various doctest-specific command-line
options. To retrieve the full list of supported options, run the ``--test``
command with the ``--help`` option:
.. code-block:: shell
./bin/<godot_binary> --test --help
Any other options and arguments after the ``--test`` command are treated as
arguments for doctest.
.. note::
Tests are compiled automatically if you use the ``dev_mode=yes`` SCons option.
``dev_mode=yes`` is recommended if you plan on contributing to the engine
development as it will automatically treat compilation warnings as errors.
The continuous integration system will fail if any compilation warnings are
detected, so you should strive to fix all warnings before opening a pull
request.
Filtering tests
~~~~~~~~~~~~~~~
By default, all tests are run if you don't supply any extra arguments after the
``--test`` command. But if you're writing new tests or would like to see the
successful assertions output coming from those tests for debugging purposes, you
can run the tests of interest with the various filtering options provided by
doctest.
The wildcard syntax ``*`` is supported for matching any number of characters in
test suites, test cases, and source file names:
+--------------------+---------------+------------------------+
| **Filter options** | **Shorthand** | **Examples** |
+--------------------+---------------+------------------------+
| ``--test-suite`` | ``-ts`` | ``-ts="*[GDScript]*"`` |
+--------------------+---------------+------------------------+
| ``--test-case`` | ``-tc`` | ``-tc="*[String]*"`` |
+--------------------+---------------+------------------------+
| ``--source-file`` | ``-sf`` | ``-sf="*test_color*"`` |
+--------------------+---------------+------------------------+
For instance, to run only the ``String`` unit tests, run:
.. code-block:: shell
./bin/<godot_binary> --test --test-case="*[String]*"
Successful assertions output can be enabled with the ``--success`` (``-s``)
option, and can be combined with any combination of filtering options above,
for instance:
.. code-block:: shell
./bin/<godot_binary> --test --source-file="*test_color*" --success
Specific tests can be skipped with corresponding ``-exclude`` options. As of
now, some tests include random stress tests which take a while to execute. In
order to skip those kind of tests, run the following command:
.. code-block:: shell
./bin/<godot_binary> --test --test-case-exclude="*[Stress]*"
Writing tests
-------------
Test suites represent C++ header files which must be included as part of the
main test entry point in ``tests/test_main.cpp``. Most test suites are located
directly under ``tests/`` directory.
All header files are prefixed with ``test_``, and this is a naming convention
which the Godot build system relies on to detect tests throughout the engine.
Here's a minimal working test suite with a single test case written:
.. code-block:: cpp
#pragma once
#include "tests/test_macros.h"
namespace TestString {
TEST_CASE("[String] Hello World!") {
String hello = "Hello World!";
CHECK(hello == "Hello World!");
}
} // namespace TestString
.. note::
You can quickly generate new tests using the ``create_test.py`` script found in the ``tests/`` directory.
This script automatically creates a new test file with the required boilerplate code in the appropriate location.
It's also able to automatically include the new header in ``tests/test_main.cpp`` using invasive mode (``-i`` flag).
To view usage instructions, run the script with the ``-h`` flag.
The ``tests/test_macros.h`` header encapsulates everything which is needed for
writing C++ unit tests in Godot. It includes doctest assertion and logging
macros such as ``CHECK`` as seen above, and of course the definitions for
writing test cases themselves.
.. seealso::
`tests/test_macros.h <https://github.com/godotengine/godot/blob/master/tests/test_macros.h>`_
source code for currently implemented macros and aliases for them.
Test cases are created using ``TEST_CASE`` function-like macro. Each test case
must have a brief description written in parentheses, optionally including
custom tags which allow to filter the tests at runtime, such as ``[String]``,
``[Stress]`` etc.
Test cases are written in a dedicated namespace. This is not required, but
allows to prevent naming collisions for when other static helper functions are
written to accommodate the repeating testing procedures such as populating
common test data for each test, or writing parameterized tests.
Godot supports writing tests per C++ module. For instructions on how to write
module tests, refer to :ref:`doc_custom_module_unit_tests`.
Subcases
~~~~~~~~
In situations where you have a common setup for several test cases with only slight variations, subcases can be very helpful. Here's an example:
.. code-block:: cpp
TEST_CASE("[SceneTree][Node] Testing node operations with a very simple scene tree") {
// ... common setup (e.g. creating a scene tree with a few nodes)
SUBCASE("Move node to specific index") {
// ... setup and checks for moving a node
}
SUBCASE("Remove node at specific index") {
// ... setup and checks for removing a node
}
}
Each ``SUBCASE`` causes the ``TEST_CASE`` to be executed from the beginning.
Subcases can be nested to an arbitrary depth, but it is advised to limit nesting to no more than one level deep.
Assertions
~~~~~~~~~~
A list of all commonly used assertions used throughout the Godot tests, sorted
by severity.
+-------------------+----------------------------------------------------------------------------------------------------------------------------------+
| **Assertion** | **Description** |
+-------------------+----------------------------------------------------------------------------------------------------------------------------------+
| ``REQUIRE`` | Test if condition holds true. Fails the entire test immediately if the condition does not hold true. |
+-------------------+----------------------------------------------------------------------------------------------------------------------------------+
| ``REQUIRE_FALSE`` | Test if condition does not hold true. Fails the entire test immediately if the condition holds true. |
+-------------------+----------------------------------------------------------------------------------------------------------------------------------+
| ``CHECK`` | Test if condition holds true. Marks the test run as failing, but allow to run other assertions. |
+-------------------+----------------------------------------------------------------------------------------------------------------------------------+
| ``CHECK_FALSE`` | Test if condition does not hold true. Marks the test run as failing, but allow to run other assertions. |
+-------------------+----------------------------------------------------------------------------------------------------------------------------------+
| ``WARN`` | Test if condition holds true. Does not fail the test under any circumstance, but logs a warning if something does not hold true. |
+-------------------+----------------------------------------------------------------------------------------------------------------------------------+
| ``WARN_FALSE`` | Test if condition does not hold true. Does not fail the test under any circumstance, but logs a warning if something holds true. |
+-------------------+----------------------------------------------------------------------------------------------------------------------------------+
All of the above assertions have corresponding ``*_MESSAGE`` macros, which allow
to print optional message with rationale of what should happen.
Prefer to use ``CHECK`` for self-explanatory assertions and ``CHECK_MESSAGE``
for more complex ones if you think that it deserves a better explanation.
.. seealso::
`doctest: Assertion macros <https://github.com/doctest/doctest/blob/master/doc/markdown/assertions.md>`_.
Logging
~~~~~~~
The test output is handled by doctest itself, and does not rely on Godot
printing or logging functionality at all, so it's recommended to use dedicated
macros which allow to log test output in a format written by doctest.
+----------------+-----------------------------------------------------------------------------------------------------------+
| **Macro** | **Description** |
+----------------+-----------------------------------------------------------------------------------------------------------+
| ``MESSAGE`` | Prints a message. |
+----------------+-----------------------------------------------------------------------------------------------------------+
| ``FAIL_CHECK`` | Marks the test as failing, but continue the execution. Can be wrapped in conditionals for complex checks. |
+----------------+-----------------------------------------------------------------------------------------------------------+
| ``FAIL`` | Fails the test immediately. Can be wrapped in conditionals for complex checks. |
+----------------+-----------------------------------------------------------------------------------------------------------+
Different reporters can be chosen at runtime. For instance, here's how the
output can be redirected to an XML file:
.. code-block:: shell
./bin/<godot_binary> --test --source-file="*test_validate*" --success --reporters=xml --out=doctest.txt
.. seealso::
`doctest: Logging macros <https://github.com/doctest/doctest/blob/master/doc/markdown/logging.md>`_.
Testing failure paths
~~~~~~~~~~~~~~~~~~~~~
Sometimes, it's not always feasible to test for an *expected* result. With the
Godot development philosophy of that the engine should not crash and should
gracefully recover whenever a non-fatal error occurs, it's important to check
that those failure paths are indeed safe to execute without crashing the engine.
*Unexpected* behavior can be tested in the same way as anything else. The only
problem this creates is that the error printing shall unnecessarily pollute the
test output with errors coming from the engine itself (even if the end result is
successful).
To alleviate this problem, use ``ERR_PRINT_OFF`` and ``ERR_PRINT_ON`` macros
directly within test cases to temporarily disable the error output coming from
the engine, for instance:
.. code-block:: cpp
TEST_CASE("[Color] Constructor methods") {
ERR_PRINT_OFF;
Color html_invalid = Color::html("invalid");
ERR_PRINT_ON; // Don't forget to re-enable!
CHECK_MESSAGE(html_invalid.is_equal_approx(Color()),
"Invalid HTML notation should result in a Color with the default values.");
}
Special tags in test case names
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These tags can be added to the test case name to modify or extend the test environment:
+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **Tag** | **Description** |
+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``[SceneTree]`` | Required for test cases that rely on a scene tree with MessageQueue to be available. It also enables a mock rendering server and :ref:`ThemeDB<class_ThemeDB>`. |
+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``[Editor]`` | Like ``[SceneTree]``, but with additional editor-related infrastructure available, such as :ref:`EditorSettings<class_EditorSettings>`. |
+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``[Audio]`` | Initializes the :ref:`AudioServer<class_AudioServer>` using a mock audio driver. |
+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``[Navigation2D]`` | Creates the default 2D navigation server and makes it available for testing. |
+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``[Navigation3D]`` | Creates the default 3D navigation server and makes it available for testing. |
+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
You can use them together to combine multiple test environment extensions.
Testing signals
~~~~~~~~~~~~~~~
The following macros can be use to test signals:
.. list-table::
:header-rows: 1
:widths: auto
* - Macro
- Description
* - ``SIGNAL_WATCH(object, "signal_name")``
- Starts watching the specified signal on the given object.
* - ``SIGNAL_UNWATCH(object, "signal_name")``
- Stops watching the specified signal on the given object.
* - ``SIGNAL_CHECK("signal_name", Vector<Vector<Variant>>)``
- Checks the arguments of all fired signals. The outer vector contains each fired signal, while the inner vector contains the list of arguments for that signal. The order of signals is significant.
* - ``SIGNAL_CHECK_FALSE("signal_name")``
- Checks if the specified signal was not fired.
* - ``SIGNAL_DISCARD("signal_name")``
- Discards all records of the specified signal.
Below is an example demonstrating the use of these macros:
.. code-block:: cpp
//...
SUBCASE("[Timer] Timer process timeout signal must be emitted") {
SIGNAL_WATCH(test_timer, SNAME("timeout"));
test_timer->start(0.1);
SceneTree::get_singleton()->process(0.2);
Array signal_args;
signal_args.push_back(Array());
SIGNAL_CHECK(SNAME("timeout"), signal_args);
SIGNAL_UNWATCH(test_timer, SNAME("timeout"));
}
//...
Test tools
----------
Test tools are advanced methods which allow you to run arbitrary procedures to
facilitate the process of manual testing and debugging the engine internals.
These tools can be run by supplying the name of a tool after the ``--test``
command-line option. For instance, the GDScript module implements and registers
several tools to help the debugging of the tokenizer, parser, and compiler:
.. code-block:: shell
./bin/<godot_binary> --test gdscript-tokenizer test.gd
./bin/<godot_binary> --test gdscript-parser test.gd
./bin/<godot_binary> --test gdscript-compiler test.gd
If any such tool is detected, then the rest of the unit tests are skipped.
Test tools can be registered anywhere throughout the engine as the registering
mechanism closely resembles of what doctest provides while registering test
cases using dynamic initialization technique, but usually these can be
registered at corresponding ``register_types.cpp`` sources (per module or core).
Here's an example of how GDScript registers test tools in
``modules/gdscript/register_types.cpp``:
.. code-block:: cpp
#ifdef TESTS_ENABLED
void test_tokenizer() {
TestGDScript::test(TestGDScript::TestType::TEST_TOKENIZER);
}
void test_parser() {
TestGDScript::test(TestGDScript::TestType::TEST_PARSER);
}
void test_compiler() {
TestGDScript::test(TestGDScript::TestType::TEST_COMPILER);
}
REGISTER_TEST_COMMAND("gdscript-tokenizer", &test_tokenizer);
REGISTER_TEST_COMMAND("gdscript-parser", &test_parser);
REGISTER_TEST_COMMAND("gdscript-compiler", &test_compiler);
#endif
The custom command-line parsing can be performed by a test tool itself with the
help of OS :ref:`get_cmdline_args<class_OS_method_get_cmdline_args>` method.
Integration tests for GDScript
------------------------------
Godot uses doctest to prevent regressions in GDScript during development. There
are several types of test scripts which can be written:
- tests for expected errors;
- tests for warnings;
- tests for features.
Therefore, the process of writing integration tests for GDScript is the following:
1. Pick a type of a test script you'd like to write, and create a new GDScript
file under the ``modules/gdscript/tests/scripts`` directory within
corresponding sub-directory.
2. Write GDScript code. The test script must have a function called ``test()``
which takes no arguments. Such function will be called by the test runner.
The test should not have any dependency unless it's part of the test too.
Global classes (using ``class_name``) are registered before the runner
starts, so those should work if needed.
Here's an example test script:
::
func test():
if true # Missing colon here.
print("true")
3. Change directory to the Godot source repository root.
.. code-block:: shell
cd godot
4. Generate ``*.out`` files to update the expected results from the output:
.. code-block:: shell
bin/<godot_binary> --gdscript-generate-tests modules/gdscript/tests/scripts
You may add the ``--print-filenames`` option to see filenames as their test
outputs are generated. If you are working on a new feature that is causing
hard crashes, you can use this option to quickly find which test file causes
the crash and debug from there.
5. Run GDScript tests with:
.. code-block:: shell
./bin/<godot_binary> --test --test-suite="*GDScript*"
This also accepts the ``--print-filenames`` option (see above).
If no errors are printed and everything goes well, you're done!
.. warning::
Make sure the output does have the expected values before submitting a pull
request. If ``--gdscript-generate-tests`` produces ``*.out`` files which are
unrelated to newly added tests, you should revert those files back and
only commit ``*.out`` files for new tests.
.. note::
The GDScript test runner is meant for testing the GDScript implementation,
not for testing user scripts nor testing the engine using scripts. We
recommend writing new tests for already resolved
`issues related to GDScript at GitHub <https://github.com/godotengine/godot/issues?q=is%3Aissue+label%3Atopic%3Agdscript+is%3Aclosed>`_,
or writing tests for currently working features.
.. note::
If your test case requires that there is no ``test()``
function present inside the script file,
you can disable the runtime section of the test by naming the script file so that it matches the pattern ``*.notest.gd``.
For example, "test_empty_file.notest.gd".