Commit 2615d5a8 authored by louiz’'s avatar louiz’

Use Catch for the unit tests

Rewrite most tests, and update the CMakeLists.txt
parent bf67ecf6
......@@ -50,8 +50,6 @@ if(BUILD_CLIENT)
find_package(SFML 2 REQUIRED system window graphics audio)
endif()
find_package(GTest)
##########################
# Includes and Linkage
##########################
......@@ -63,9 +61,6 @@ include_directories(${OPENSSL_INCLUDE_DIR})
include_directories(${BOTAN_INCLUDE_DIRS})
include_directories(${PROTOBUF_INCLUDE_DIRS})
include_directories(${WEBSOCKETPP_INCLUDE_DIRS})
if(GTEST_FOUND)
include_directories(${GTEST_INCLUDE_DIRS})
endif()
link_directories(${Boost_LIBRARY_DIRS})
......@@ -375,30 +370,37 @@ target_link_libraries(game_server
# Tests
####################
if(GTEST_FOUND)
enable_testing()
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -V)
file(GLOB tests_srcs
tests/*test.cpp
)
foreach(test_src ${tests_srcs})
get_filename_component(lib ${test_src} NAME_WE)
string(REGEX REPLACE "(.*)_test$" "\\1" lib ${lib})
if(TARGET ${lib})
add_executable(${lib}_test EXCLUDE_FROM_ALL ${test_src})
target_link_libraries(${lib}_test
${lib}
${GTEST_BOTH_LIBRARIES}
)
add_test(${lib}_test ${lib}_test)
add_dependencies(check ${lib}_test)
else()
message("Library ${lib} does not exist, ${lib}_test.cpp will not be enabled.")
endif()
endforeach(test_src)
endif()
find_package(CATCH REQUIRED)
if(CATCH_FOUND)
file(GLOB source_tests
tests/*.cpp)
add_executable(test_suite EXCLUDE_FROM_ALL
${source_tests})
target_link_libraries(test_suite
config
network
fixmath
world
master_server
game)
target_include_directories(test_suite
PUBLIC ${CATCH_INCLUDE_DIRS})
target_include_directories(test_suite
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/tests)
add_custom_target(check COMMAND "test_suite"
DEPENDS test_suite)
#
## Code coverage
#
if(CMAKE_BUILD_TYPE MATCHES Debug)
include(CodeCoverage)
setup_target_for_coverage(coverage
test_suite
coverage)
endif()
endif(CATCH_FOUND)
# - Find catch
# Find the catch cryptographic library
#
# This module defines the following variables:
# CATCH_FOUND - True if library and include directory are found
# If set to TRUE, the following are also defined:
# CATCH_INCLUDE_DIRS - The directory where to find the header file
# CATCH_LIBRARIES - Where to find the library file
#
# For conveniance, these variables are also set. They have the same values
# than the variables above. The user can thus choose his/her prefered way
# to write them.
# CATCH_LIBRARY
# CATCH_INCLUDE_DIR
#
# This file is in the public domain
find_path(CATCH_INCLUDE_DIRS NAMES catch.hpp
PATH_SUFFIXES catch-1.11
DOC "The catch include directory")
# Use some standard module to handle the QUIETLY and REQUIRED arguments, and
# set CATCH_FOUND to TRUE if these two variables are set.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CATCH REQUIRED_VARS CATCH_INCLUDE_DIRS)
if(CATCH_FOUND)
set(CATCH_INCLUDE_DIR ${CATCH_INCLUDE_DIRS})
endif()
mark_as_advanced(CATCH_INCLUDE_DIRS)
#include <approx.hpp>
/**
* The epsilon is quite big, because Fix16 computations are not precise.
*/
Approx fix_approx(const Fix16& value)
{
return fix_approx(value, 0.014);
}
Approx fix_approx(const Fix16& value, const double epsilon)
{
Approx res(value.to_double());
res.epsilon(epsilon);
return res;
}
bool operator==(const Fix16& lhs, const Approx& rhs)
{
return lhs.to_double() == rhs;
}
TEST_CASE("Fix16 Approx", "[approx]")
{
Fix16 value(2.01);
CHECK(2.f == fix_approx(value));
CHECK(18.f != fix_approx(value));
CHECK(18.f == fix_approx(value, 20.0));
CHECK(2_fix == fix_approx(value));
}
#ifndef BATAJELO_APPROX_HPP
#define BATAJELO_APPROX_HPP
/**
* Define our own approx for our own types.
*/
#include <fixmath/fix16.hpp>
#include "catch.hpp"
Approx fix_approx(const Fix16& value);
Approx fix_approx(const Fix16& value, const double epsilon);
bool operator==(const Fix16& lhs, const Approx& rhs);
#endif //BATAJELO_APPROX_HPP
#include <config/config.hpp>
#include <catch.hpp>
TEST_CASE("Config basic")
{
Config::filename = "test.cfg";
Config::file_must_exist = false;
Config::set("coucou", "bonjour", true);
Config::close();
bool error = false;
try
{
Config::file_must_exist = true;
CHECK(Config::get("coucou", "") == "bonjour");
CHECK(Config::get("does not exist", "default") == "default");
Config::close();
}
catch (const std::ios::failure& e)
{
error = true;
}
CHECK_FALSE(error);
}
TEST_CASE("Config callbacks")
{
bool switched = false;
Config::connect([&switched]()
{
switched = !switched;
});
CHECK_FALSE(switched);
Config::set("un", "deux", true);
CHECK(switched);
Config::set("un", "trois", true);
CHECK_FALSE(switched);
Config::set("un", "trois", false);
CHECK_FALSE(switched);
}
TEST_CASE("Config get_int")
{
auto res = Config::get_int("number", 0);
CHECK(res == 0);
Config::set("number", "88");
res = Config::get_int("number", 0);
CHECK(res == 88);
Config::set("number", "pouet");
res = Config::get_int("number", -1);
CHECK(res == 0);
}
#include <config/config.hpp>
#include <iostream>
#include <fstream>
#include <gtest/gtest.h>
class ConfigTest: public testing::Test
{
protected:
void SetUp()
{
std::ofstream os("tests/test.conf");
os << " coucou=machin" << std::endl;
os << "number= 2" << std::endl;
os << "empty =" << std::endl;
os.close();
a = 0;
}
int a;
};
TEST_F(ConfigTest, basic)
{
EXPECT_EQ(Config::read_conf("tests/test.conf"), true);
Config::connect([&]() {++a;});
EXPECT_EQ(Config::get("coucou", ""), "machin");
EXPECT_EQ(Config::get("does not exist", "zizi"), "zizi");
EXPECT_EQ(Config::get_int("number", 0), 2);
EXPECT_EQ(Config::get_int("does not exist", 1), 1);
Config::set("coucou", "coucou");
EXPECT_EQ(Config::get("coucou", ""), "coucou");
Config::set("new", "hello");
EXPECT_EQ(Config::get("new", ""), "hello");
Config::set_int("number", 12, true);
EXPECT_EQ(Config::get_int("number", 0), 12);
EXPECT_EQ(a, 1);
Config::close(true);
EXPECT_EQ(Config::read_conf("tests/test.conf"), true);
EXPECT_EQ(Config::get_int("number", 0), 12);
Config::close(true);
}
#include <database/db_object.hpp>
#include <database/database.hpp>
#include <database/user.hpp>
#include <database/friend_request.hpp>
#include <config/config.hpp>
#include <logging/logging.hpp>
#include <sha.h>
#include <base64.h>
#define BOOST_TEST_MODULE database
#include <boost/test/included/unit_test.hpp>
/* HOW TO USE THE TESTS
Before launching the tests, you need to execute the data/test.sql script
It will insert some required values for testing in the database
Then you are ready to launch the tests !
*/
BOOST_AUTO_TEST_SUITE(database_suite1)
BOOST_AUTO_TEST_CASE(empty_user)
{
BOOST_TEST_MESSAGE("Testing 0 row result");
DbObject* user1 = Database::inst()->get_object("*", "User", "id=1");
BOOST_REQUIRE(user1 == nullptr);
delete user1;
}
BOOST_AUTO_TEST_CASE(update_user)
{
std::string digest;
CryptoPP::SHA256 hash;
CryptoPP::StringSource("nightmare", true,
new CryptoPP::HashFilter(hash,
new CryptoPP::Base64Encoder (
new CryptoPP::StringSink(digest))));
User* user = new User();
user->set("login", "testing");
user->set("password", digest);
BOOST_REQUIRE(Database::inst()->update(user, "User") == true);
delete user;
User* user1 = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing'"));
BOOST_REQUIRE(user1 != 0);
BOOST_REQUIRE(user1->get("password") == digest);
digest.clear();
CryptoPP::StringSource("nightmare2", true,
new CryptoPP::HashFilter(hash,
new CryptoPP::Base64Encoder (
new CryptoPP::StringSink(digest))));
user1->set("password", digest);
BOOST_REQUIRE(Database::inst()->update(user1, "User") == true);
User* user2 = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing'"));
BOOST_REQUIRE(user2 != 0);
BOOST_REQUIRE(user2->get("password") == digest);
}
BOOST_AUTO_TEST_CASE(add_friend)
{
std::string digest;
CryptoPP::SHA256 hash;
CryptoPP::StringSource("lol", true,
new CryptoPP::HashFilter(hash,
new CryptoPP::Base64Encoder (
new CryptoPP::StringSink(digest))));
User* user2 = new User();
user2->set("login", "testing2");
user2->set("password", digest);
BOOST_REQUIRE(Database::inst()->update(user2, "User") == true);
user2 = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing2'"));
BOOST_REQUIRE(user2 != 0);
User* user = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing'"));
BOOST_REQUIRE(user != 0);
std::string where = "sender_id=" + user->get("id");
where += " AND receiver_id = " + user2->get("id");
where += " AND request_id =";
where += Config::get("request_friendship", "1").data();
FriendRequest request;
request.create(user, user2);
DbObject* friendship_pending = Database::inst()->get_object("*", "User_Request", where);
BOOST_REQUIRE(friendship_pending != 0);
request.accept(user2, user);
where = "user_id=" + user->get("id");
where += " AND friend_id=" + user2->get("id");
DbObject* friendship = Database::inst()->get_object("*", "User_Friend", where);
BOOST_REQUIRE(friendship != 0);
BOOST_REQUIRE(friendship->get("user_id") == user->get("id"));
BOOST_REQUIRE(friendship->get("friend_id") == user2->get("id"));
}
BOOST_AUTO_TEST_CASE(add_user_achievement)
{
User* user = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing'"));
BOOST_REQUIRE(user != 0);
user->add_achievement("1");
std::vector<DbObject*> user_achievements = user->get_achievements();
BOOST_REQUIRE(user_achievements.size() == 1);
}
BOOST_AUTO_TEST_CASE(add_and_get_replays)
{
User* user = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing'"));
User* user2 = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing2'"));
BOOST_REQUIRE(user != 0);
BOOST_REQUIRE(user2 != 0);
DbObject* replay = Database::inst()->get_object("id", "Replay", "name='test'");
BOOST_REQUIRE(replay != 0);
user->add_replay(replay->get("id"));
DbObject* user_replay = Database::inst()->get_object("user_id, replay_id", "user_replay", "replay_id=" + replay->get("id") + " AND user_id=" + user->get("id"));
BOOST_REQUIRE(user_replay != 0);
user2->add_replay(replay->get("id"));
DbObject* user_replay2 = Database::inst()->get_object("user_id, replay_id", "user_replay", "replay_id=" + replay->get("id") + " AND user_id=" + user2->get("id"));
BOOST_REQUIRE(user_replay2 != 0);
std::vector<DbObject*> replays = user->get_replays();
BOOST_REQUIRE(replays.size() == 1);
std::vector<DbObject*> replays2 = user2->get_replays();
BOOST_REQUIRE(replays.size() == 1);
}
BOOST_AUTO_TEST_CASE(add_update_remove_ban)
{
User* user = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing'"));
BOOST_REQUIRE(user);
user->add_ban(24);
DbObject* user_infos = Database::inst()->get_object("*", "user", "id=" + user->get("id"));
BOOST_REQUIRE(user_infos->get_int("is_banned") == 1);
user = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing'"));
BOOST_REQUIRE(user);
DbObject* user_ban = Database::inst()->get_object("*", "user_ban", "user_id=" + user->get("id"));
boost::posix_time::ptime last_unban_time(boost::posix_time::time_from_string(user_ban->get("ban_end")));
BOOST_REQUIRE(user_ban != 0);
user->update_ban(24);
user_ban = Database::inst()->get_object("*", "user_ban", "user_id=" + user->get("id"));
boost::posix_time::ptime unban_time(boost::posix_time::time_from_string(user_ban->get("ban_end")));
BOOST_REQUIRE(last_unban_time + boost::posix_time::hours(24) == unban_time);
user->remove_ban();
user_infos = Database::inst()->get_object("*", "user", "id=" + user->get("id"));
BOOST_REQUIRE(user_infos->get_int("is_banned") == 0);
}
BOOST_AUTO_TEST_CASE(delete_users)
{
User* user = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing'"));
User* user2 = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing2'"));
BOOST_REQUIRE(user);
BOOST_REQUIRE(user2);
BOOST_REQUIRE(Database::inst()->remove(user, "User") == true);
BOOST_REQUIRE(Database::inst()->remove(user2, "User") == true);
user = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing'"));
BOOST_REQUIRE(user == 0);
user2 = dynamic_cast<User*>(Database::inst()->get_object("*", "User", "login='testing'"));
BOOST_REQUIRE(user2 == 0);
}
BOOST_AUTO_TEST_SUITE_END()
#ifndef BATAJELO_ENABLE_LOGGING_HPP
#define BATAJELO_ENABLE_LOGGING_HPP
#include <config/config.hpp>
#include <logging/logging.hpp>
#include <utils/scopeguard.hpp>
/**
* Calling this macro in a scope will enable logging,
* for the duration of the scope.
* Use it to debug a test, but never commit it.
*/
#define enable_logging() \
Logger::instance().reset(); \
Config::set("log_level", "0");\
utils::ScopeGuard g([](){ Logger::instance().reset();Config::set("log_level", "5"); });
#endif //BATAJELO_ENABLE_LOGGING_HPP
#include "catch.hpp"
#include <world/entity_factory.hpp>
#include <world/health.hpp>
#include <world/location.hpp>
#include <world/mobility.hpp>
#include <world/abilities.hpp>
#include <world/abilities/blink.hpp>
#include <world/abilities/emp.hpp>
#include <world/abilities/attack.hpp>
SCENARIO("Create entities and check their behaviour", "[entities]")
{
GIVEN("a basic entity")
{
Entity entity(0);
WHEN("we add basic components")
{
entity.add_component(std::make_unique<Health>(10));
entity.add_component(std::make_unique<Location>(10, true));
THEN("We can get these components")
{
auto location = entity.get<Location>();
CHECK(location);
auto health = entity.get<Health>();
CHECK(health);
}
AND_THEN("We can see that it does not have the other components")
{
auto mobility = entity.get<Mobility>();
CHECK(!mobility);
}
}
WHEN("we add an empty abilities component")
{
entity.add_component(std::make_unique<Abilities>(2));
THEN("it has an ability component")
{
auto abilities = entity.get<Abilities>();
CHECK(abilities);
}
}
}
}
TEST_CASE("Create and fill Abilities", "[entities]")
{
GIVEN("An empty entity")
{
Entity entity(0);
WHEN("we add components and abilities in one line")
{
entity.add_component<Location>(10, true).
add_and_get_component<Abilities>().
add<Attack>(300ms, 500ms, 450_fix).
add<Emp>();
THEN("the entity has all the given attributes")
{
auto abilities = entity.get<Abilities>();
CHECK(abilities);
auto attack = abilities->find<Attack>(AbilityType::Attack);
CHECK(attack);
CHECK(attack->get_range() == 450_fix);
auto blink = abilities->find<Blink>(AbilityType::Blink);
CHECK(!blink);
}
}
AND_WHEN("we create an other entity")
{
Entity entity2(0);
THEN("the new ID is different")
{
CHECK(entity2.get_id() != entity.get_id());
}
}
}
}
TEST_CASE("Entity factory", "[entities]")
{
EntityFactory factory;
Entity entity(0);
auto entity_ptr = factory.make_entity(0);
CHECK(entity_ptr->get_type() == entity.get_type());
}
#include <fixmath/fix16.hpp>
#include "catch.hpp"
TEST_CASE("basic", "[fixmath]")
{
Fix16 a(-2);
CHECK(-a == 2);
Fix16 b(8787);
CHECK(-b == -8787);
}
TEST_CASE("ComparisonWithPositiveInt", "[fixmath]")
{
Fix16 a(8);
CHECK(a == 8);
CHECK(a == 8u);
CHECK(a == 8.f);
CHECK(a == 8.);
CHECK(8 == a);
CHECK(8u == a);
CHECK(8.f == a);
CHECK(8. == a);
Fix16 b(9);
CHECK(a != b);
CHECK(a != 9);
CHECK(a != 9u);
CHECK(a != 9.);
CHECK(a != 9.f);
CHECK(a < b);
CHECK(a < 9);
CHECK(a < 9u);
CHECK(a < 9.);
CHECK(a < 9.f);
CHECK(b > a);
CHECK(9 > a);
CHECK(9u > a);
CHECK(9. > a);
CHECK(9.f > a);
CHECK(b >= a);
CHECK(9 >= a);
CHECK(9u >= a);
CHECK(9. >= a);
CHECK(9.f >= a);
}
TEST_CASE("ComparisonWithNegativeInt", "[fixmath]")
{
Fix16 a(-2839);
CHECK(a == -2839);
CHECK(a < -8);
CHECK(a < 2811);
Fix16 b(-1);
CHECK(a < b);
CHECK(a < 8888);
CHECK(a == -2839.f);
CHECK(a >= -2839.f);
CHECK(a <= -2839.f);
}
TEST_CASE("ComparisonWithDouble", "[fixmath]")
{
Fix16 a(7878.8);
CHECK(a > 7878);
CHECK(a < 7879u);
CHECK(a == 7878.80);
}
TEST_CASE("ToDouble", "[fixmath]")
{
auto a = 12.001_fix;
CHECK(a.to_double() > 12.0);
CHECK(a.to_double() < 12.1);