Skip to content

Commit

Permalink
Create test system using doctest
Browse files Browse the repository at this point in the history
-Tests are run by running the project with the `--runTests` arg
-Test files are only compiled into debug builds
-Added test workflow to run on PRs
-Created new helper macro to not run functions in editor
-Wrote a few simple tests for the CellSpawner class
  • Loading branch information
Alex-x90 committed Feb 15, 2024
1 parent af6234c commit 049c541
Show file tree
Hide file tree
Showing 15 changed files with 166 additions and 20 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Unit Tests

on:
pull_request:
types: [opened, edited, reopened, synchronize]

jobs:
ci:
name: Run Unit Tests
runs-on: ubuntu-latest
container:
image: barichello/godot-ci:4.2.1

steps:
- name: Check out Git repository
uses: actions/checkout@v3
with:
submodules: recursive

- name: Set up Python (for SCons)
uses: actions/setup-python@v5
with:
python-version: '3.x'

- name: Install scons
run: |
python -m pip install scons==4.0.0
- name: Build module
run: |
cd godot-cpp
scons platform=linux target=template_debug
cd ..
scons platform=linux target=template_debug disable_exceptions=no
- name: Run tests
run: |
set -e
godot --headless -T
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ bin/*.exp
bin/*.lib
.sconsign.dblite
src/*.obj
tests/*.obj
.vscode/
compile_commands.json
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
path = godot-cpp
url = https://github.com/godotengine/godot-cpp
branch = 4.1
[submodule "doctest"]
path = doctest
url = https://github.com/doctest/doctest.git
6 changes: 5 additions & 1 deletion SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ env = SConscript("godot-cpp/SConstruct")
# - LINKFLAGS are for linking flags

# tweak this if you want to use different folders, or more folders, to store your source code in.
env.Append(CPPPATH=["src/"])
env.Append(CPPPATH=["src/","tests/","doctest/doctest/"])
sources = Glob("src/*.cpp")

# If non-release build, compile the test files
if env.debug_features:
sources.extend(Glob("tests/*.cpp"))

if env["platform"] == "macos":
library = env.SharedLibrary(
"bin/libalife.{}.{}.framework/libalife.{}.{}".format(
Expand Down
6 changes: 3 additions & 3 deletions src/cell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <godot_cpp/classes/sprite2d.hpp>
#include <godot_cpp/core/class_db.hpp>

#include "helpers.hpp"

using namespace godot;

void Cell::_bind_methods() {
Expand Down Expand Up @@ -48,9 +50,7 @@ void Cell::_ready() {
}

void Cell::_process(double delta) {
// Don't run if in editor
if (Engine::get_singleton()->is_editor_hint())
return;
DONT_RUN_IN_EDITOR;

if (_cellState->getAlive()) {
// Living Cell behavior
Expand Down
41 changes: 37 additions & 4 deletions src/cell_spawner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@

#include <godot_cpp/core/class_db.hpp>

#include "helpers.hpp"

// These includes are for starting testing and should stay with the class we're using as the project entry point
#ifdef DEBUG_ENABLED
#include <godot_cpp/classes/os.hpp>
#include <godot_cpp/classes/scene_tree.hpp>

#include <string>
#include <vector>

#include "TestHeader.hpp"
#endif

using namespace godot;

void CellSpawner::_bind_methods() {
Expand Down Expand Up @@ -33,7 +46,10 @@ void CellSpawner::_bind_methods() {
}

CellSpawner::CellSpawner() { rand.instantiate(); }
CellSpawner::~CellSpawner() {}
CellSpawner::~CellSpawner() {
// Clean up spawned child nodes
queue_free();
}

void CellSpawner::setCellScene(const Ref<PackedScene> cellScene) {
_cellScene = cellScene;
Expand Down Expand Up @@ -88,7 +104,24 @@ void CellSpawner::spawnCell() {
}

void CellSpawner::_ready() {
// Don't run if in editor
if (Engine::get_singleton()->is_editor_hint())
return;
DONT_RUN_IN_EDITOR;

// If not in a release build, check for custom cmdline arg to run tests,
// forwarding additional user args into doctest as its args
#ifdef DEBUG_ENABLED
for (auto arg : OS::get_singleton()->get_cmdline_args()) {
if (arg == "--runTests" || arg == "-T") {
// Collect user command line args to pass into doctest
std::vector<const char *> userArgs;
for (godot::String arg : OS::get_singleton()->get_cmdline_user_args()) {
const char *charArg = arg.ascii().get_data();
userArgs.push_back(charArg);
}

// Run the tests, exit, and return the value doctest returned
int retVal = doctest_run(userArgs.size(), userArgs.data());
get_tree()->quit(retVal);
}
}
#endif
}
2 changes: 1 addition & 1 deletion src/cell_spawner.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#pragma once

#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/viewport.hpp>
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
#include <godot_cpp/classes/random_number_generator.hpp>
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/classes/sprite2d.hpp>
#include <godot_cpp/classes/viewport.hpp>

namespace godot {

Expand Down
6 changes: 3 additions & 3 deletions src/fps_counter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <godot_cpp/core/class_db.hpp>

#include "helpers.hpp"

using namespace godot;

void FpsCounter::_bind_methods() {}
Expand All @@ -10,9 +12,7 @@ FpsCounter::FpsCounter() {}
FpsCounter::~FpsCounter() {}

void FpsCounter::_process(double delta) {
// Don't run if in editor
if (Engine::get_singleton()->is_editor_hint())
return;
DONT_RUN_IN_EDITOR;

const String fps =
"FPS " + String::num(Engine::get_singleton()->get_frames_per_second());
Expand Down
10 changes: 10 additions & 0 deletions src/helpers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

#ifdef DEBUG_ENABLED
#define DONT_RUN_IN_EDITOR \
if (Engine::get_singleton()->is_editor_hint()) { \
return; \
}
#else
#define DONT_RUN_IN_EDITOR
#endif
8 changes: 4 additions & 4 deletions src/start_button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <godot_cpp/core/class_db.hpp>

#include "helpers.hpp"

using namespace godot;

void StartButton::_bind_methods() {
Expand All @@ -16,7 +18,7 @@ StartButton::~StartButton() {
// _pressed is used as the function for when the button is pressed
// This button is also switching the button off once it gets pressed once.
void StartButton::_pressed() {
CellSpawner *parent = (CellSpawner *)this->get_parent();
CellSpawner *parent = static_cast<CellSpawner *>(this->get_parent());
for (int i = 0; i < parent->getNumCells(); i++) {
parent->spawnCell();
}
Expand All @@ -26,9 +28,7 @@ void StartButton::_pressed() {
// Mainly used for setting diffrent values for the button but these could be done in the godot client instead
//
void StartButton::_ready() {
// Don't run if in editor
if (Engine::get_singleton()->is_editor_hint())
return;
DONT_RUN_IN_EDITOR;

this->set_text("Start");
}
2 changes: 1 addition & 1 deletion src/start_button.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pragma once

#include "cell_spawner.hpp"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/button.hpp>
#include <godot_cpp/classes/engine.hpp>

namespace godot {

Expand Down
6 changes: 3 additions & 3 deletions src/stats_counter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include <godot_cpp/core/class_db.hpp>

#include "helpers.hpp"

using namespace godot;

void StatsCounter::_bind_methods() {}
Expand All @@ -11,9 +13,7 @@ StatsCounter::StatsCounter() {}
StatsCounter::~StatsCounter() {}

void StatsCounter::_process(double delta) {
// Don't run if in editor
if (Engine::get_singleton()->is_editor_hint())
return;
DONT_RUN_IN_EDITOR;

const String stats = "COLLISIONS " + String::num(Cell::CollisionCount);
set_text(stats);
Expand Down
36 changes: 36 additions & 0 deletions tests/TestCellSpawner.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "TestHeader.hpp"

#include "cell_spawner.hpp"

#include <godot_cpp/classes/Object.hpp>

namespace godot {

TEST_CASE("Dummy") {
CHECK(1 == 1);
}

TEST_CASE("Test CellSpawner") {
CellSpawner CellSpawner{};

REQUIRE(CellSpawner.getMinForce() == 50.0);
REQUIRE(CellSpawner.getMaxForce() == 150.0);

SUBCASE("Test setting min force") {
CellSpawner.setMinForce(40.0);
CHECK(CellSpawner.getMinForce() == 40.0);

CellSpawner.setMinForce(CellSpawner.getMaxForce() + 1);
CHECK(CellSpawner.getMinForce() == CellSpawner.getMaxForce());
}

SUBCASE("Test setting max force") {
CellSpawner.setMaxForce(160.0);
CHECK(CellSpawner.getMaxForce() == 160.0);

CellSpawner.setMaxForce(CellSpawner.getMinForce() - 1);
CHECK(CellSpawner.getMaxForce() == CellSpawner.getMinForce());
}
}

} //namespace godot
5 changes: 5 additions & 0 deletions tests/TestHeader.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once

#include "doctest.h"

int doctest_run(const int, const char *const *);
15 changes: 15 additions & 0 deletions tests/TestMain.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Can only be used in one test file. Do not copy!!
#define DOCTEST_CONFIG_IMPLEMENT

#include "TestHeader.hpp"

#include <string>
#include <vector>

int doctest_run(const int argc, const char *const *argv) {
doctest::Context context;
context.applyCommandLine(argc, argv);
int res = context.run();

return res;
}

0 comments on commit 049c541

Please sign in to comment.