Squashed 'external/entt/entt/' content from commit fef92113

git-subtree-dir: external/entt/entt
git-subtree-split: fef921132cae7588213d0f9bcd2fb9c8ffd8b7fc
This commit is contained in:
2023-07-25 11:29:51 +02:00
commit 5c7231b7a3
242 changed files with 146004 additions and 0 deletions

273
test/CMakeLists.txt Normal file
View File

@ -0,0 +1,273 @@
#
# Tests configuration
#
include(FetchContent)
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("
#include<version>
int main() { return 0; }
" ENTT_HAS_HEADER_VERSION)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
if(ENTT_FIND_GTEST_PACKAGE)
find_package(GTest REQUIRED)
else()
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG main
GIT_SHALLOW 1
)
FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
FetchContent_Populate(googletest)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
endif()
add_library(GTest::Main ALIAS gtest_main)
target_compile_features(gtest PUBLIC cxx_std_17)
target_compile_features(gtest_main PUBLIC cxx_std_17)
target_compile_features(gmock PUBLIC cxx_std_17)
target_compile_features(gmock_main PUBLIC cxx_std_17)
endif()
include_directories($<TARGET_PROPERTY:EnTT,INTERFACE_INCLUDE_DIRECTORIES>)
add_compile_options($<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_OPTIONS>)
function(SETUP_TARGET TARGET_NAME)
set_target_properties(${TARGET_NAME} PROPERTIES CXX_EXTENSIONS OFF)
target_compile_features(${TARGET_NAME} PRIVATE ${ENTT_CXX_STD})
target_link_libraries(${TARGET_NAME} PRIVATE EnTT)
if(MSVC)
target_compile_options(
${TARGET_NAME}
PRIVATE
/EHsc /W1 /wd4996 /w14800
$<$<CONFIG:Debug>:/Od>
$<$<CONFIG:Release>:/O2>
)
else()
target_compile_options(
${TARGET_NAME}
PRIVATE
-pedantic -fvisibility=hidden -Wall -Wshadow -Wno-deprecated-declarations
$<$<CONFIG:Debug>:-O0 -g>
$<$<CONFIG:Release>:-O2>
)
endif()
target_compile_definitions(
${TARGET_NAME}
PRIVATE
ENTT_ID_TYPE=${ENTT_ID_TYPE}
_ENABLE_EXTENDED_ALIGNED_STORAGE
NOMINMAX
${ARGN}
)
if(ENTT_HAS_HEADER_VERSION)
target_compile_definitions(
${TARGET_NAME}
PRIVATE
ENTT_HAS_HEADER_VERSION
)
endif()
endfunction()
add_library(odr OBJECT odr.cpp)
set_target_properties(odr PROPERTIES POSITION_INDEPENDENT_CODE ON)
SETUP_TARGET(odr)
function(SETUP_BASIC_TEST TEST_NAME TEST_SOURCES)
add_executable(${TEST_NAME} $<TARGET_OBJECTS:odr> ${TEST_SOURCES})
target_link_libraries(${TEST_NAME} PRIVATE GTest::Main Threads::Threads)
SETUP_TARGET(${TEST_NAME} ${ARGN})
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
endfunction()
function(SETUP_LIB_TEST TEST_NAME)
add_library(_${TEST_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/lib.cpp)
SETUP_TARGET(_${TEST_NAME} ENTT_API_EXPORT)
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp ENTT_API_IMPORT)
target_link_libraries(lib_${TEST_NAME} PRIVATE _${TEST_NAME})
endfunction()
function(SETUP_PLUGIN_TEST TEST_NAME)
add_library(_${TEST_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/plugin.cpp)
SETUP_TARGET(_${TEST_NAME} ${ARGVN})
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp PLUGIN="$<TARGET_FILE:_${TEST_NAME}>" ${ARGVN})
target_include_directories(_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_include_directories(lib_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_link_libraries(lib_${TEST_NAME} PRIVATE ${CMAKE_DL_LIBS})
add_dependencies(lib_${TEST_NAME} _${TEST_NAME})
endfunction()
# Test benchmark
if(ENTT_BUILD_BENCHMARK)
SETUP_BASIC_TEST(benchmark benchmark/benchmark.cpp)
endif()
# Test example
if(ENTT_BUILD_EXAMPLE)
SETUP_BASIC_TEST(custom_identifier example/custom_identifier.cpp)
SETUP_BASIC_TEST(entity_copy example/entity_copy.cpp)
SETUP_BASIC_TEST(signal_less example/signal_less.cpp)
endif()
# Test lib
if(ENTT_BUILD_LIB)
FetchContent_Declare(
cr
GIT_REPOSITORY https://github.com/fungos/cr.git
GIT_TAG master
GIT_SHALLOW 1
)
FetchContent_GetProperties(cr)
if(NOT cr_POPULATED)
FetchContent_Populate(cr)
set(cr_INCLUDE_DIR ${cr_SOURCE_DIR})
endif()
SETUP_LIB_TEST(dispatcher)
SETUP_LIB_TEST(emitter)
SETUP_LIB_TEST(locator)
SETUP_LIB_TEST(meta)
SETUP_LIB_TEST(registry)
SETUP_PLUGIN_TEST(dispatcher_plugin)
SETUP_PLUGIN_TEST(emitter_plugin)
SETUP_PLUGIN_TEST(locator_plugin)
SETUP_PLUGIN_TEST(meta_plugin)
SETUP_PLUGIN_TEST(registry_plugin)
SETUP_PLUGIN_TEST(meta_plugin_std ENTT_STANDARD_CPP)
endif()
# Test snapshot
if(ENTT_BUILD_SNAPSHOT)
FetchContent_Declare(
cereal
GIT_REPOSITORY https://github.com/USCiLab/cereal.git
GIT_TAG v1.2.2
GIT_SHALLOW 1
)
FetchContent_GetProperties(cereal)
if(NOT cereal_POPULATED)
FetchContent_Populate(cereal)
set(cereal_INCLUDE_DIR ${cereal_SOURCE_DIR}/include)
endif()
SETUP_BASIC_TEST(cereal snapshot/snapshot.cpp)
target_include_directories(cereal PRIVATE ${cereal_INCLUDE_DIR})
endif()
# Test config
SETUP_BASIC_TEST(version entt/config/version.cpp)
# Test container
SETUP_BASIC_TEST(dense_map entt/container/dense_map.cpp)
SETUP_BASIC_TEST(dense_set entt/container/dense_set.cpp)
# Test core
SETUP_BASIC_TEST(algorithm entt/core/algorithm.cpp)
SETUP_BASIC_TEST(any entt/core/any.cpp)
SETUP_BASIC_TEST(compressed_pair entt/core/compressed_pair.cpp)
SETUP_BASIC_TEST(enum entt/core/enum.cpp)
SETUP_BASIC_TEST(family entt/core/family.cpp)
SETUP_BASIC_TEST(hashed_string entt/core/hashed_string.cpp)
SETUP_BASIC_TEST(ident entt/core/ident.cpp)
SETUP_BASIC_TEST(iterator entt/core/iterator.cpp)
SETUP_BASIC_TEST(memory entt/core/memory.cpp)
SETUP_BASIC_TEST(monostate entt/core/monostate.cpp)
SETUP_BASIC_TEST(tuple entt/core/tuple.cpp)
SETUP_BASIC_TEST(type_info entt/core/type_info.cpp)
SETUP_BASIC_TEST(type_traits entt/core/type_traits.cpp)
SETUP_BASIC_TEST(utility entt/core/utility.cpp)
# Test entity
SETUP_BASIC_TEST(component entt/entity/component.cpp)
SETUP_BASIC_TEST(entity entt/entity/entity.cpp)
SETUP_BASIC_TEST(group entt/entity/group.cpp)
SETUP_BASIC_TEST(handle entt/entity/handle.cpp)
SETUP_BASIC_TEST(helper entt/entity/helper.cpp)
SETUP_BASIC_TEST(observer entt/entity/observer.cpp)
SETUP_BASIC_TEST(organizer entt/entity/organizer.cpp)
SETUP_BASIC_TEST(registry entt/entity/registry.cpp)
SETUP_BASIC_TEST(runtime_view entt/entity/runtime_view.cpp)
SETUP_BASIC_TEST(snapshot entt/entity/snapshot.cpp)
SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp)
SETUP_BASIC_TEST(storage entt/entity/storage.cpp)
SETUP_BASIC_TEST(storage_mixin entt/entity/storage_mixin.cpp)
SETUP_BASIC_TEST(view entt/entity/view.cpp)
# Test graph
SETUP_BASIC_TEST(adjacency_matrix entt/graph/adjacency_matrix.cpp)
SETUP_BASIC_TEST(dot entt/graph/dot.cpp)
SETUP_BASIC_TEST(flow entt/graph/flow.cpp)
# Test locator
SETUP_BASIC_TEST(locator entt/locator/locator.cpp)
# Test meta
SETUP_BASIC_TEST(meta_any entt/meta/meta_any.cpp)
SETUP_BASIC_TEST(meta_base entt/meta/meta_base.cpp)
SETUP_BASIC_TEST(meta_container entt/meta/meta_container.cpp)
SETUP_BASIC_TEST(meta_context entt/meta/meta_context.cpp)
SETUP_BASIC_TEST(meta_conv entt/meta/meta_conv.cpp)
SETUP_BASIC_TEST(meta_ctor entt/meta/meta_ctor.cpp)
SETUP_BASIC_TEST(meta_data entt/meta/meta_data.cpp)
SETUP_BASIC_TEST(meta_dtor entt/meta/meta_dtor.cpp)
SETUP_BASIC_TEST(meta_func entt/meta/meta_func.cpp)
SETUP_BASIC_TEST(meta_handle entt/meta/meta_handle.cpp)
SETUP_BASIC_TEST(meta_pointer entt/meta/meta_pointer.cpp)
SETUP_BASIC_TEST(meta_prop entt/meta/meta_prop.cpp)
SETUP_BASIC_TEST(meta_range entt/meta/meta_range.cpp)
SETUP_BASIC_TEST(meta_template entt/meta/meta_template.cpp)
SETUP_BASIC_TEST(meta_type entt/meta/meta_type.cpp)
SETUP_BASIC_TEST(meta_utility entt/meta/meta_utility.cpp)
# Test poly
SETUP_BASIC_TEST(poly entt/poly/poly.cpp)
# Test process
SETUP_BASIC_TEST(process entt/process/process.cpp)
SETUP_BASIC_TEST(scheduler entt/process/scheduler.cpp)
# Test resource
SETUP_BASIC_TEST(resource entt/resource/resource.cpp)
SETUP_BASIC_TEST(resource_cache entt/resource/resource_cache.cpp)
SETUP_BASIC_TEST(resource_loader entt/resource/resource_loader.cpp)
# Test signal
SETUP_BASIC_TEST(delegate entt/signal/delegate.cpp)
SETUP_BASIC_TEST(dispatcher entt/signal/dispatcher.cpp)
SETUP_BASIC_TEST(emitter entt/signal/emitter.cpp)
SETUP_BASIC_TEST(sigh entt/signal/sigh.cpp)

1094
test/benchmark/benchmark.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
#ifndef ENTT_COMMON_BASIC_TEST_ALLOCATOR_HPP
#define ENTT_COMMON_BASIC_TEST_ALLOCATOR_HPP
#include <memory>
#include <type_traits>
namespace test {
template<typename Type, typename Pocs = std::true_type>
struct basic_test_allocator: std::allocator<Type> {
// basic pocca/pocma/pocs allocator
using base = std::allocator<Type>;
using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_swap = Pocs;
using std::allocator<Type>::allocator;
basic_test_allocator &operator=(const basic_test_allocator &other) {
// necessary to avoid call suppression
base::operator=(other);
return *this;
}
bool operator==(const basic_test_allocator &other) {
return (this == &other);
}
};
} // namespace test
#endif

18
test/entt/common/config.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef ENTT_COMMON_CONFIG_H
#define ENTT_COMMON_CONFIG_H
namespace test {
#ifdef NDEBUG
# define ENTT_DEBUG_TEST(Case, Test) TEST(Case, DISABLED_##Test)
# define ENTT_DEBUG_TEST_F(Case, Test) TEST_F(Case, DISABLED_##Test)
# define ENTT_DEBUG_TYPED_TEST(Case, Test) TYPED_TEST(Case, DISABLED_##Test)
#else
# define ENTT_DEBUG_TEST(Case, Test) TEST(Case, Test)
# define ENTT_DEBUG_TEST_F(Case, Test) TEST_F(Case, Test)
# define ENTT_DEBUG_TYPED_TEST(Case, Test) TYPED_TEST(Case, Test)
#endif
} // namespace test
#endif

View File

@ -0,0 +1,69 @@
#ifndef ENTT_COMMON_THROWING_ALLOCATOR_HPP
#define ENTT_COMMON_THROWING_ALLOCATOR_HPP
#include <cstddef>
#include <memory>
#include <type_traits>
namespace test {
template<typename Type>
class throwing_allocator: std::allocator<Type> {
template<typename Other>
friend class throwing_allocator;
using base = std::allocator<Type>;
struct test_exception {};
public:
using value_type = Type;
using pointer = value_type *;
using const_pointer = const value_type *;
using void_pointer = void *;
using const_void_pointer = const void *;
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type;
using exception_type = test_exception;
template<class Other>
struct rebind {
using other = throwing_allocator<Other>;
};
throwing_allocator() = default;
template<class Other>
throwing_allocator(const throwing_allocator<Other> &other)
: base{other} {}
pointer allocate(std::size_t length) {
if(trigger_on_allocate) {
trigger_on_allocate = false;
throw test_exception{};
}
trigger_on_allocate = trigger_after_allocate;
trigger_after_allocate = false;
return base::allocate(length);
}
void deallocate(pointer mem, std::size_t length) {
base::deallocate(mem, length);
}
bool operator==(const throwing_allocator<Type> &) const {
return true;
}
bool operator!=(const throwing_allocator<Type> &other) const {
return !(*this == other);
}
static inline bool trigger_on_allocate{};
static inline bool trigger_after_allocate{};
};
} // namespace test
#endif

View File

@ -0,0 +1,46 @@
#ifndef ENTT_COMMON_THROWING_TYPE_HPP
#define ENTT_COMMON_THROWING_TYPE_HPP
namespace test {
class throwing_type {
struct test_exception {};
public:
using exception_type = test_exception;
static constexpr auto moved_from_value = -1;
throwing_type(int value)
: data{value} {}
throwing_type(const throwing_type &other)
: data{other.data} {
if(data == trigger_on_value) {
data = moved_from_value;
throw exception_type{};
}
}
throwing_type &operator=(const throwing_type &other) {
if(other.data == trigger_on_value) {
data = moved_from_value;
throw exception_type{};
}
data = other.data;
return *this;
}
operator int() const {
return data;
}
static inline int trigger_on_value{};
private:
int data{};
};
} // namespace test
#endif

View File

@ -0,0 +1,64 @@
#ifndef ENTT_COMMON_TRACKED_MEMORY_RESOURCE_HPP
#define ENTT_COMMON_TRACKED_MEMORY_RESOURCE_HPP
#ifdef ENTT_HAS_HEADER_VERSION
# include <version>
#
# if defined(__cpp_lib_memory_resource) && __cpp_lib_memory_resource >= 201603L
# define ENTT_HAS_TRACKED_MEMORY_RESOURCE
#
# include <cstddef>
# include <memory_resource>
# include <string>
namespace test {
class tracked_memory_resource: public std::pmr::memory_resource {
void *do_allocate(std::size_t bytes, std::size_t alignment) override {
++alloc_counter;
return std::pmr::get_default_resource()->allocate(bytes, alignment);
}
void do_deallocate(void *value, std::size_t bytes, std::size_t alignment) override {
++dealloc_counter;
std::pmr::get_default_resource()->deallocate(value, bytes, alignment);
}
bool do_is_equal(const std::pmr::memory_resource &other) const noexcept override {
return (this == &other);
}
public:
using string_type = std::pmr::string;
using size_type = std::size_t;
static constexpr const char *default_value = "a string long enough to force an allocation (hopefully)";
tracked_memory_resource()
: alloc_counter{},
dealloc_counter{} {}
size_type do_allocate_counter() const noexcept {
return alloc_counter;
}
size_type do_deallocate_counter() const noexcept {
return dealloc_counter;
}
void reset() noexcept {
alloc_counter = 0u;
dealloc_counter = 0u;
}
private:
size_type alloc_counter;
size_type dealloc_counter;
};
} // namespace test
# endif
#endif
#endif

View File

@ -0,0 +1,8 @@
#include <regex>
#include <gtest/gtest.h>
#include <entt/config/version.h>
TEST(Version, All) {
ASSERT_STREQ(ENTT_VERSION, ENTT_XSTR(ENTT_VERSION_MAJOR) "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH));
ASSERT_TRUE(std::regex_match(ENTT_VERSION, std::regex{"^[0-9]+\\.[0-9]+\\.[0-9]+$"}));
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,946 @@
#include <cmath>
#include <cstddef>
#include <functional>
#include <iterator>
#include <memory>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/container/dense_set.hpp>
#include <entt/core/memory.hpp>
#include <entt/core/utility.hpp>
#include "../common/throwing_allocator.hpp"
#include "../common/tracked_memory_resource.hpp"
struct transparent_equal_to {
using is_transparent = void;
template<typename Type, typename Other>
constexpr std::enable_if_t<std::is_convertible_v<Other, Type>, bool>
operator()(const Type &lhs, const Other &rhs) const {
return lhs == static_cast<Type>(rhs);
}
};
TEST(DenseSet, Functionalities) {
entt::dense_set<int, entt::identity, transparent_equal_to> set;
const auto &cset = set;
ASSERT_NO_FATAL_FAILURE([[maybe_unused]] auto alloc = set.get_allocator());
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.load_factor(), 0.f);
ASSERT_EQ(set.max_load_factor(), .875f);
ASSERT_EQ(set.max_size(), (std::vector<std::pair<std::size_t, int>>{}.max_size()));
set.max_load_factor(.9f);
ASSERT_EQ(set.max_load_factor(), .9f);
ASSERT_EQ(set.begin(), set.end());
ASSERT_EQ(cset.begin(), cset.end());
ASSERT_EQ(set.cbegin(), set.cend());
ASSERT_NE(set.max_bucket_count(), 0u);
ASSERT_EQ(set.bucket_count(), 8u);
ASSERT_EQ(set.bucket_size(3u), 0u);
ASSERT_EQ(set.bucket(0), 0u);
ASSERT_EQ(set.bucket(3), 3u);
ASSERT_EQ(set.bucket(8), 0u);
ASSERT_EQ(set.bucket(10), 2u);
ASSERT_EQ(set.begin(1u), set.end(1u));
ASSERT_EQ(cset.begin(1u), cset.end(1u));
ASSERT_EQ(set.cbegin(1u), set.cend(1u));
ASSERT_FALSE(set.contains(42));
ASSERT_FALSE(set.contains(4.2));
ASSERT_EQ(set.find(42), set.end());
ASSERT_EQ(set.find(4.2), set.end());
ASSERT_EQ(cset.find(42), set.cend());
ASSERT_EQ(cset.find(4.2), set.cend());
ASSERT_EQ(set.hash_function()(42), 42);
ASSERT_TRUE(set.key_eq()(42, 42));
set.emplace(0);
ASSERT_EQ(set.count(0), 1u);
ASSERT_EQ(set.count(4.2), 0u);
ASSERT_EQ(cset.count(0.0), 1u);
ASSERT_EQ(cset.count(42), 0u);
ASSERT_FALSE(set.empty());
ASSERT_EQ(set.size(), 1u);
ASSERT_NE(set.begin(), set.end());
ASSERT_NE(cset.begin(), cset.end());
ASSERT_NE(set.cbegin(), set.cend());
ASSERT_TRUE(set.contains(0));
ASSERT_EQ(set.bucket(0), 0u);
set.clear();
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.begin(), set.end());
ASSERT_EQ(cset.begin(), cset.end());
ASSERT_EQ(set.cbegin(), set.cend());
ASSERT_FALSE(set.contains(0));
}
TEST(DenseSet, Constructors) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<int> set;
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
set = entt::dense_set<int>{std::allocator<int>{}};
set = entt::dense_set<int>{2u * minimum_bucket_count, std::allocator<float>{}};
set = entt::dense_set<int>{4u * minimum_bucket_count, std::hash<int>(), std::allocator<double>{}};
set.emplace(3);
entt::dense_set<int> temp{set, set.get_allocator()};
entt::dense_set<int> other{std::move(temp), set.get_allocator()};
ASSERT_EQ(set.size(), 1u);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(set.bucket_count(), 4u * minimum_bucket_count);
ASSERT_EQ(other.bucket_count(), 4u * minimum_bucket_count);
}
TEST(DenseSet, Copy) {
entt::dense_set<std::size_t, entt::identity> set;
set.max_load_factor(set.max_load_factor() - .05f);
set.emplace(3u);
entt::dense_set<std::size_t, entt::identity> other{set};
ASSERT_TRUE(set.contains(3u));
ASSERT_TRUE(other.contains(3u));
ASSERT_EQ(set.max_load_factor(), other.max_load_factor());
set.emplace(1u);
set.emplace(11u);
other.emplace(0u);
other = set;
ASSERT_TRUE(other.contains(3u));
ASSERT_TRUE(other.contains(1u));
ASSERT_TRUE(other.contains(11u));
ASSERT_FALSE(other.contains(0u));
ASSERT_EQ(other.bucket(3u), set.bucket(11u));
ASSERT_EQ(other.bucket(3u), other.bucket(11u));
ASSERT_EQ(*other.begin(3u), *set.begin(3u));
ASSERT_EQ(*other.begin(3u), 11u);
ASSERT_EQ((*++other.begin(3u)), 3u);
}
TEST(DenseSet, Move) {
entt::dense_set<std::size_t, entt::identity> set;
set.max_load_factor(set.max_load_factor() - .05f);
set.emplace(3u);
entt::dense_set<std::size_t, entt::identity> other{std::move(set)};
ASSERT_EQ(set.size(), 0u);
ASSERT_TRUE(other.contains(3u));
ASSERT_EQ(set.max_load_factor(), other.max_load_factor());
set = other;
set.emplace(1u);
set.emplace(11u);
other.emplace(0u);
other = std::move(set);
ASSERT_EQ(set.size(), 0u);
ASSERT_TRUE(other.contains(3u));
ASSERT_TRUE(other.contains(1u));
ASSERT_TRUE(other.contains(11u));
ASSERT_FALSE(other.contains(0u));
ASSERT_EQ(other.bucket(3u), other.bucket(11u));
ASSERT_EQ(*other.begin(3u), 11u);
ASSERT_EQ(*++other.begin(3u), 3u);
}
TEST(DenseSet, Iterator) {
using iterator = typename entt::dense_set<int>::iterator;
static_assert(std::is_same_v<iterator::value_type, int>);
static_assert(std::is_same_v<iterator::pointer, const int *>);
static_assert(std::is_same_v<iterator::reference, const int &>);
entt::dense_set<int> set;
set.emplace(3);
iterator end{set.begin()};
iterator begin{};
begin = set.end();
std::swap(begin, end);
ASSERT_EQ(begin, set.begin());
ASSERT_EQ(end, set.end());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, set.begin());
ASSERT_EQ(begin--, set.end());
ASSERT_EQ(begin + 1, set.end());
ASSERT_EQ(end - 1, set.begin());
ASSERT_EQ(++begin, set.end());
ASSERT_EQ(--begin, set.begin());
ASSERT_EQ(begin += 1, set.end());
ASSERT_EQ(begin -= 1, set.begin());
ASSERT_EQ(begin + (end - begin), set.end());
ASSERT_EQ(begin - (begin - end), set.end());
ASSERT_EQ(end - (end - begin), set.begin());
ASSERT_EQ(end + (begin - end), set.begin());
ASSERT_EQ(begin[0u], *set.begin().operator->());
ASSERT_EQ(begin[0u], *set.begin());
ASSERT_LT(begin, end);
ASSERT_LE(begin, set.begin());
ASSERT_GT(end, begin);
ASSERT_GE(end, set.end());
set.emplace(42);
begin = set.begin();
ASSERT_EQ(begin[0u], 3);
ASSERT_EQ(begin[1u], 42);
}
TEST(DenseSet, ConstIterator) {
using iterator = typename entt::dense_set<int>::const_iterator;
static_assert(std::is_same_v<iterator::value_type, int>);
static_assert(std::is_same_v<iterator::pointer, const int *>);
static_assert(std::is_same_v<iterator::reference, const int &>);
entt::dense_set<int> set;
set.emplace(3);
iterator cend{set.cbegin()};
iterator cbegin{};
cbegin = set.cend();
std::swap(cbegin, cend);
ASSERT_EQ(cbegin, set.cbegin());
ASSERT_EQ(cend, set.cend());
ASSERT_NE(cbegin, cend);
ASSERT_EQ(cbegin++, set.cbegin());
ASSERT_EQ(cbegin--, set.cend());
ASSERT_EQ(cbegin + 1, set.cend());
ASSERT_EQ(cend - 1, set.cbegin());
ASSERT_EQ(++cbegin, set.cend());
ASSERT_EQ(--cbegin, set.cbegin());
ASSERT_EQ(cbegin += 1, set.cend());
ASSERT_EQ(cbegin -= 1, set.cbegin());
ASSERT_EQ(cbegin + (cend - cbegin), set.cend());
ASSERT_EQ(cbegin - (cbegin - cend), set.cend());
ASSERT_EQ(cend - (cend - cbegin), set.cbegin());
ASSERT_EQ(cend + (cbegin - cend), set.cbegin());
ASSERT_EQ(cbegin[0u], *set.cbegin().operator->());
ASSERT_EQ(cbegin[0u], *set.cbegin());
ASSERT_LT(cbegin, cend);
ASSERT_LE(cbegin, set.cbegin());
ASSERT_GT(cend, cbegin);
ASSERT_GE(cend, set.cend());
set.emplace(42);
cbegin = set.cbegin();
ASSERT_EQ(cbegin[0u], 3);
ASSERT_EQ(cbegin[1u], 42);
}
TEST(DenseSet, IteratorConversion) {
entt::dense_set<int> set;
set.emplace(3);
typename entt::dense_set<int, int>::iterator it = set.begin();
typename entt::dense_set<int, int>::const_iterator cit = it;
static_assert(std::is_same_v<decltype(*it), const int &>);
static_assert(std::is_same_v<decltype(*cit), const int &>);
ASSERT_EQ(*it, 3);
ASSERT_EQ(*it.operator->(), 3);
ASSERT_EQ(it.operator->(), cit.operator->());
ASSERT_EQ(*it, *cit);
ASSERT_EQ(it - cit, 0);
ASSERT_EQ(cit - it, 0);
ASSERT_LE(it, cit);
ASSERT_LE(cit, it);
ASSERT_GE(it, cit);
ASSERT_GE(cit, it);
ASSERT_EQ(it, cit);
ASSERT_NE(++cit, it);
}
TEST(DenseSet, Insert) {
entt::dense_set<int> set;
typename entt::dense_set<int>::iterator it;
bool result;
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.find(0), set.end());
ASSERT_FALSE(set.contains(0));
int value{1};
std::tie(it, result) = set.insert(std::as_const(value));
ASSERT_TRUE(result);
ASSERT_EQ(set.size(), 1u);
ASSERT_EQ(it, --set.end());
ASSERT_TRUE(set.contains(1));
ASSERT_NE(set.find(1), set.end());
ASSERT_EQ(*it, 1);
std::tie(it, result) = set.insert(value);
ASSERT_FALSE(result);
ASSERT_EQ(set.size(), 1u);
ASSERT_EQ(it, --set.end());
ASSERT_EQ(*it, 1);
std::tie(it, result) = set.insert(3);
ASSERT_TRUE(result);
ASSERT_EQ(set.size(), 2u);
ASSERT_EQ(it, --set.end());
ASSERT_TRUE(set.contains(3));
ASSERT_NE(set.find(3), set.end());
ASSERT_EQ(*it, 3);
std::tie(it, result) = set.insert(3);
ASSERT_FALSE(result);
ASSERT_EQ(set.size(), 2u);
ASSERT_EQ(it, --set.end());
ASSERT_EQ(*it, 3);
int range[2u]{7, 9};
set.insert(std::begin(range), std::end(range));
ASSERT_EQ(set.size(), 4u);
ASSERT_TRUE(set.contains(7));
ASSERT_NE(set.find(9), set.end());
}
TEST(DenseSet, InsertRehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
ASSERT_TRUE(set.insert(next).second);
}
ASSERT_EQ(set.size(), minimum_bucket_count);
ASSERT_GT(set.bucket_count(), minimum_bucket_count);
ASSERT_TRUE(set.contains(minimum_bucket_count / 2u));
ASSERT_EQ(set.bucket(minimum_bucket_count / 2u), minimum_bucket_count / 2u);
ASSERT_FALSE(set.contains(minimum_bucket_count));
ASSERT_TRUE(set.insert(minimum_bucket_count).second);
ASSERT_EQ(set.size(), minimum_bucket_count + 1u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count * 2u);
ASSERT_TRUE(set.contains(minimum_bucket_count / 2u));
ASSERT_EQ(set.bucket(minimum_bucket_count / 2u), minimum_bucket_count / 2u);
ASSERT_TRUE(set.contains(minimum_bucket_count));
for(std::size_t next{}; next <= minimum_bucket_count; ++next) {
ASSERT_TRUE(set.contains(next));
ASSERT_EQ(set.bucket(next), next);
}
}
TEST(DenseSet, InsertSameBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
ASSERT_EQ(set.cbegin(next), set.cend(next));
}
ASSERT_TRUE(set.insert(1u).second);
ASSERT_TRUE(set.insert(9u).second);
ASSERT_EQ(set.size(), 2u);
ASSERT_TRUE(set.contains(1u));
ASSERT_NE(set.find(9u), set.end());
ASSERT_EQ(set.bucket(1u), 1u);
ASSERT_EQ(set.bucket(9u), 1u);
ASSERT_EQ(set.bucket_size(1u), 2u);
ASSERT_EQ(set.cbegin(6u), set.cend(6u));
}
TEST(DenseSet, Emplace) {
entt::dense_set<int> set;
typename entt::dense_set<int>::iterator it;
bool result;
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.find(0), set.end());
ASSERT_FALSE(set.contains(0));
std::tie(it, result) = set.emplace();
ASSERT_TRUE(result);
ASSERT_EQ(set.size(), 1u);
ASSERT_EQ(it, --set.end());
ASSERT_TRUE(set.contains(0));
ASSERT_NE(set.find(0), set.end());
ASSERT_EQ(*it, 0);
std::tie(it, result) = set.emplace();
ASSERT_FALSE(result);
ASSERT_EQ(set.size(), 1u);
ASSERT_EQ(it, --set.end());
ASSERT_EQ(*it, 0);
std::tie(it, result) = set.emplace(1);
ASSERT_TRUE(result);
ASSERT_EQ(set.size(), 2u);
ASSERT_EQ(it, --set.end());
ASSERT_TRUE(set.contains(1));
ASSERT_NE(set.find(1), set.end());
ASSERT_EQ(*it, 1);
std::tie(it, result) = set.emplace(1);
ASSERT_FALSE(result);
ASSERT_EQ(set.size(), 2u);
ASSERT_EQ(it, --set.end());
ASSERT_EQ(*it, 1);
}
TEST(DenseSet, EmplaceRehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
ASSERT_TRUE(set.emplace(next).second);
ASSERT_LE(set.load_factor(), set.max_load_factor());
}
ASSERT_EQ(set.size(), minimum_bucket_count);
ASSERT_GT(set.bucket_count(), minimum_bucket_count);
ASSERT_TRUE(set.contains(minimum_bucket_count / 2u));
ASSERT_EQ(set.bucket(minimum_bucket_count / 2u), minimum_bucket_count / 2u);
ASSERT_FALSE(set.contains(minimum_bucket_count));
ASSERT_TRUE(set.emplace(minimum_bucket_count).second);
ASSERT_EQ(set.size(), minimum_bucket_count + 1u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count * 2u);
ASSERT_TRUE(set.contains(minimum_bucket_count / 2u));
ASSERT_EQ(set.bucket(minimum_bucket_count / 2u), minimum_bucket_count / 2u);
ASSERT_TRUE(set.contains(minimum_bucket_count));
for(std::size_t next{}; next <= minimum_bucket_count; ++next) {
ASSERT_TRUE(set.contains(next));
ASSERT_EQ(set.bucket(next), next);
}
}
TEST(DenseSet, EmplaceSameBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
ASSERT_EQ(set.cbegin(next), set.cend(next));
}
ASSERT_TRUE(set.emplace(1u).second);
ASSERT_TRUE(set.emplace(9u).second);
ASSERT_EQ(set.size(), 2u);
ASSERT_TRUE(set.contains(1u));
ASSERT_NE(set.find(9u), set.end());
ASSERT_EQ(set.bucket(1u), 1u);
ASSERT_EQ(set.bucket(9u), 1u);
ASSERT_EQ(set.bucket_size(1u), 2u);
ASSERT_EQ(set.cbegin(6u), set.cend(6u));
}
TEST(DenseSet, Erase) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
for(std::size_t next{}, last = minimum_bucket_count + 1u; next < last; ++next) {
set.emplace(next);
}
ASSERT_EQ(set.bucket_count(), 2 * minimum_bucket_count);
ASSERT_EQ(set.size(), minimum_bucket_count + 1u);
for(std::size_t next{}, last = minimum_bucket_count + 1u; next < last; ++next) {
ASSERT_TRUE(set.contains(next));
}
auto it = set.erase(++set.begin());
it = set.erase(it, it + 1);
ASSERT_EQ(*--set.end(), 6u);
ASSERT_EQ(set.erase(6u), 1u);
ASSERT_EQ(set.erase(6u), 0u);
ASSERT_EQ(set.bucket_count(), 2 * minimum_bucket_count);
ASSERT_EQ(set.size(), minimum_bucket_count + 1u - 3u);
ASSERT_EQ(it, ++set.begin());
ASSERT_EQ(*it, 7u);
ASSERT_EQ(*--set.end(), 5u);
for(std::size_t next{}, last = minimum_bucket_count + 1u; next < last; ++next) {
if(next == 1u || next == 8u || next == 6u) {
ASSERT_FALSE(set.contains(next));
ASSERT_EQ(set.bucket_size(next), 0u);
} else {
ASSERT_TRUE(set.contains(next));
ASSERT_EQ(set.bucket(next), next);
ASSERT_EQ(set.bucket_size(next), 1u);
}
}
set.erase(set.begin(), set.end());
for(std::size_t next{}, last = minimum_bucket_count + 1u; next < last; ++next) {
ASSERT_FALSE(set.contains(next));
}
ASSERT_EQ(set.bucket_count(), 2 * minimum_bucket_count);
ASSERT_EQ(set.size(), 0u);
}
TEST(DenseSet, EraseWithMovableKeyValue) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::string> set;
set.emplace("0");
set.emplace("1");
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
ASSERT_EQ(set.size(), 2u);
auto it = set.erase(set.find("0"));
ASSERT_EQ(*it, "1");
ASSERT_EQ(set.size(), 1u);
ASSERT_FALSE(set.contains("0"));
}
TEST(DenseSet, EraseFromBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
ASSERT_EQ(set.size(), 0u);
for(std::size_t next{}; next < 4u; ++next) {
ASSERT_TRUE(set.emplace(2u * minimum_bucket_count * next).second);
ASSERT_TRUE(set.emplace(2u * minimum_bucket_count * next + 2u).second);
ASSERT_TRUE(set.emplace(2u * minimum_bucket_count * (next + 1u) - 1u).second);
}
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_EQ(set.size(), 12u);
ASSERT_EQ(set.bucket_size(0u), 4u);
ASSERT_EQ(set.bucket_size(2u), 4u);
ASSERT_EQ(set.bucket_size(15u), 4u);
set.erase(set.end() - 3, set.end());
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_EQ(set.size(), 9u);
ASSERT_EQ(set.bucket_size(0u), 3u);
ASSERT_EQ(set.bucket_size(2u), 3u);
ASSERT_EQ(set.bucket_size(15u), 3u);
for(std::size_t next{}; next < 3u; ++next) {
ASSERT_TRUE(set.contains(2u * minimum_bucket_count * next));
ASSERT_EQ(set.bucket(2u * minimum_bucket_count * next), 0u);
ASSERT_TRUE(set.contains(2u * minimum_bucket_count * next + 2u));
ASSERT_EQ(set.bucket(2u * minimum_bucket_count * next + 2u), 2u);
ASSERT_TRUE(set.contains(2u * minimum_bucket_count * (next + 1u) - 1u));
ASSERT_EQ(set.bucket(2u * minimum_bucket_count * (next + 1u) - 1u), 15u);
}
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 3u));
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 3u + 2u));
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * (3u + 1u) - 1u));
set.erase(*++set.begin(0u));
set.erase(*++set.begin(2u));
set.erase(*++set.begin(15u));
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_EQ(set.size(), 6u);
ASSERT_EQ(set.bucket_size(0u), 2u);
ASSERT_EQ(set.bucket_size(2u), 2u);
ASSERT_EQ(set.bucket_size(15u), 2u);
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 1u));
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 1u + 2u));
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * (1u + 1u) - 1u));
while(set.begin(15) != set.end(15u)) {
set.erase(*set.begin(15));
}
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_EQ(set.size(), 4u);
ASSERT_EQ(set.bucket_size(0u), 2u);
ASSERT_EQ(set.bucket_size(2u), 2u);
ASSERT_EQ(set.bucket_size(15u), 0u);
ASSERT_TRUE(set.contains(0u * minimum_bucket_count));
ASSERT_TRUE(set.contains(0u * minimum_bucket_count + 2u));
ASSERT_TRUE(set.contains(4u * minimum_bucket_count));
ASSERT_TRUE(set.contains(4u * minimum_bucket_count + 2u));
set.erase(4u * minimum_bucket_count + 2u);
set.erase(0u * minimum_bucket_count);
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_EQ(set.size(), 2u);
ASSERT_EQ(set.bucket_size(0u), 1u);
ASSERT_EQ(set.bucket_size(2u), 1u);
ASSERT_EQ(set.bucket_size(15u), 0u);
ASSERT_FALSE(set.contains(0u * minimum_bucket_count));
ASSERT_TRUE(set.contains(0u * minimum_bucket_count + 2u));
ASSERT_TRUE(set.contains(4u * minimum_bucket_count));
ASSERT_FALSE(set.contains(4u * minimum_bucket_count + 2u));
}
TEST(DenseSet, Swap) {
entt::dense_set<int> set;
entt::dense_set<int> other;
set.emplace(0);
ASSERT_FALSE(set.empty());
ASSERT_TRUE(other.empty());
ASSERT_TRUE(set.contains(0));
ASSERT_FALSE(other.contains(0));
set.swap(other);
ASSERT_TRUE(set.empty());
ASSERT_FALSE(other.empty());
ASSERT_FALSE(set.contains(0));
ASSERT_TRUE(other.contains(0));
}
TEST(DenseSet, EqualRange) {
entt::dense_set<int, entt::identity, transparent_equal_to> set;
const auto &cset = set;
set.emplace(42);
ASSERT_EQ(set.equal_range(0).first, set.end());
ASSERT_EQ(set.equal_range(0).second, set.end());
ASSERT_EQ(cset.equal_range(0).first, cset.cend());
ASSERT_EQ(cset.equal_range(0).second, cset.cend());
ASSERT_EQ(set.equal_range(0.0).first, set.end());
ASSERT_EQ(set.equal_range(0.0).second, set.end());
ASSERT_EQ(cset.equal_range(0.0).first, cset.cend());
ASSERT_EQ(cset.equal_range(0.0).second, cset.cend());
ASSERT_NE(set.equal_range(42).first, set.end());
ASSERT_EQ(*set.equal_range(42).first, 42);
ASSERT_EQ(set.equal_range(42).second, set.end());
ASSERT_NE(cset.equal_range(42).first, cset.cend());
ASSERT_EQ(*cset.equal_range(42).first, 42);
ASSERT_EQ(cset.equal_range(42).second, cset.cend());
ASSERT_NE(set.equal_range(42.0).first, set.end());
ASSERT_EQ(*set.equal_range(42.0).first, 42);
ASSERT_EQ(set.equal_range(42.0).second, set.end());
ASSERT_NE(cset.equal_range(42.0).first, cset.cend());
ASSERT_EQ(*cset.equal_range(42.0).first, 42);
ASSERT_EQ(cset.equal_range(42.0).second, cset.cend());
}
TEST(DenseSet, LocalIterator) {
using iterator = typename entt::dense_set<std::size_t, entt::identity>::local_iterator;
static_assert(std::is_same_v<iterator::value_type, std::size_t>);
static_assert(std::is_same_v<iterator::pointer, const std::size_t *>);
static_assert(std::is_same_v<iterator::reference, const std::size_t &>);
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
set.emplace(3u);
set.emplace(3u + minimum_bucket_count);
iterator end{set.begin(3u)};
iterator begin{};
begin = set.end(3u);
std::swap(begin, end);
ASSERT_EQ(begin, set.begin(3u));
ASSERT_EQ(end, set.end(3u));
ASSERT_NE(begin, end);
ASSERT_EQ(*begin.operator->(), 3u + minimum_bucket_count);
ASSERT_EQ(*begin, 3u + minimum_bucket_count);
ASSERT_EQ(begin++, set.begin(3u));
ASSERT_EQ(++begin, set.end(3u));
}
TEST(DenseSet, ConstLocalIterator) {
using iterator = typename entt::dense_set<std::size_t, entt::identity>::const_local_iterator;
static_assert(std::is_same_v<iterator::value_type, std::size_t>);
static_assert(std::is_same_v<iterator::pointer, const std::size_t *>);
static_assert(std::is_same_v<iterator::reference, const std::size_t &>);
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
set.emplace(3u);
set.emplace(3u + minimum_bucket_count);
iterator cend{set.begin(3u)};
iterator cbegin{};
cbegin = set.end(3u);
std::swap(cbegin, cend);
ASSERT_EQ(cbegin, set.begin(3u));
ASSERT_EQ(cend, set.end(3u));
ASSERT_NE(cbegin, cend);
ASSERT_EQ(*cbegin.operator->(), 3u + minimum_bucket_count);
ASSERT_EQ(*cbegin, 3u + minimum_bucket_count);
ASSERT_EQ(cbegin++, set.begin(3u));
ASSERT_EQ(++cbegin, set.end(3u));
}
TEST(DenseSet, LocalIteratorConversion) {
entt::dense_set<int> set;
set.emplace(3);
typename entt::dense_set<int>::local_iterator it = set.begin(set.bucket(3));
typename entt::dense_set<int>::const_local_iterator cit = it;
static_assert(std::is_same_v<decltype(*it), const int &>);
static_assert(std::is_same_v<decltype(*cit), const int &>);
ASSERT_EQ(*it, 3);
ASSERT_EQ(*it.operator->(), 3);
ASSERT_EQ(it.operator->(), cit.operator->());
ASSERT_EQ(*it, *cit);
ASSERT_EQ(it, cit);
ASSERT_NE(++cit, it);
}
TEST(DenseSet, Rehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
set.emplace(32u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
ASSERT_EQ(set.bucket(32u), 0u);
set.rehash(12u);
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
ASSERT_EQ(set.bucket(32u), 0u);
set.rehash(44u);
ASSERT_EQ(set.bucket_count(), 8u * minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
ASSERT_EQ(set.bucket(32u), 32u);
set.rehash(0u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
ASSERT_EQ(set.bucket(32u), 0u);
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
set.emplace(next);
}
ASSERT_EQ(set.size(), minimum_bucket_count + 1u);
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
set.rehash(0u);
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
set.rehash(55u);
ASSERT_EQ(set.bucket_count(), 8u * minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
set.rehash(2u);
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
ASSERT_TRUE(set.contains(32u));
ASSERT_EQ(set.bucket(32u), 0u);
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
ASSERT_TRUE(set.contains(next));
ASSERT_EQ(set.bucket(next), next);
}
ASSERT_EQ(set.bucket_size(0u), 2u);
ASSERT_EQ(set.bucket_size(3u), 1u);
ASSERT_EQ(*set.begin(0u), 0u);
ASSERT_EQ(*++set.begin(0u), 32u);
set.clear();
set.rehash(2u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
ASSERT_FALSE(set.contains(32u));
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
ASSERT_FALSE(set.contains(next));
}
ASSERT_EQ(set.bucket_size(0u), 0u);
ASSERT_EQ(set.bucket_size(3u), 0u);
}
TEST(DenseSet, Reserve) {
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<int> set;
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
set.reserve(0u);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
set.reserve(minimum_bucket_count);
ASSERT_EQ(set.bucket_count(), 2 * minimum_bucket_count);
ASSERT_EQ(set.bucket_count(), entt::next_power_of_two(std::ceil(minimum_bucket_count / set.max_load_factor())));
}
TEST(DenseSet, ThrowingAllocator) {
using allocator = test::throwing_allocator<std::size_t>;
using packed_allocator = test::throwing_allocator<std::pair<std::size_t, std::size_t>>;
using packed_exception = typename packed_allocator::exception_type;
static constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, std::hash<std::size_t>, std::equal_to<std::size_t>, allocator> set{};
packed_allocator::trigger_on_allocate = true;
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
ASSERT_THROW(set.reserve(2u * set.bucket_count()), packed_exception);
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
packed_allocator::trigger_on_allocate = true;
ASSERT_THROW(set.emplace(), packed_exception);
ASSERT_FALSE(set.contains(0u));
packed_allocator::trigger_on_allocate = true;
ASSERT_THROW(set.emplace(std::size_t{}), packed_exception);
ASSERT_FALSE(set.contains(0u));
packed_allocator::trigger_on_allocate = true;
ASSERT_THROW(set.insert(0u), packed_exception);
ASSERT_FALSE(set.contains(0u));
}
#if defined(ENTT_HAS_TRACKED_MEMORY_RESOURCE)
TEST(DenseSet, NoUsesAllocatorConstruction) {
using allocator = std::pmr::polymorphic_allocator<int>;
test::tracked_memory_resource memory_resource{};
entt::dense_set<int, std::hash<int>, std::equal_to<int>, allocator> set{&memory_resource};
set.reserve(1u);
memory_resource.reset();
set.emplace(0);
ASSERT_TRUE(set.get_allocator().resource()->is_equal(memory_resource));
ASSERT_EQ(memory_resource.do_allocate_counter(), 0u);
ASSERT_EQ(memory_resource.do_deallocate_counter(), 0u);
}
TEST(DenseSet, UsesAllocatorConstruction) {
using string_type = typename test::tracked_memory_resource::string_type;
using allocator = std::pmr::polymorphic_allocator<string_type>;
test::tracked_memory_resource memory_resource{};
entt::dense_set<string_type, std::hash<string_type>, std::equal_to<string_type>, allocator> set{&memory_resource};
set.reserve(1u);
memory_resource.reset();
set.emplace(test::tracked_memory_resource::default_value);
ASSERT_TRUE(set.get_allocator().resource()->is_equal(memory_resource));
ASSERT_GT(memory_resource.do_allocate_counter(), 0u);
ASSERT_EQ(memory_resource.do_deallocate_counter(), 0u);
}
#endif

View File

@ -0,0 +1,98 @@
#include <array>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/algorithm.hpp>
struct boxed_int {
int value;
};
TEST(Algorithm, StdSort) {
// well, I'm pretty sure it works, it's std::sort!!
std::array<int, 5> arr{{4, 1, 3, 2, 0}};
entt::std_sort sort;
sort(arr.begin(), arr.end());
for(auto i = 0u; i < (arr.size() - 1u); ++i) {
ASSERT_LT(arr[i], arr[i + 1u]);
}
}
TEST(Algorithm, StdSortBoxedInt) {
// well, I'm pretty sure it works, it's std::sort!!
std::array<boxed_int, 6> arr{{{4}, {1}, {3}, {2}, {0}, {6}}};
entt::std_sort sort;
sort(arr.begin(), arr.end(), [](const auto &lhs, const auto &rhs) {
return lhs.value > rhs.value;
});
for(auto i = 0u; i < (arr.size() - 1u); ++i) {
ASSERT_GT(arr[i].value, arr[i + 1u].value);
}
}
TEST(Algorithm, InsertionSort) {
std::array<int, 5> arr{{4, 1, 3, 2, 0}};
entt::insertion_sort sort;
sort(arr.begin(), arr.end());
for(auto i = 0u; i < (arr.size() - 1u); ++i) {
ASSERT_LT(arr[i], arr[i + 1u]);
}
}
TEST(Algorithm, InsertionSortBoxedInt) {
std::array<boxed_int, 6> arr{{{4}, {1}, {3}, {2}, {0}, {6}}};
entt::insertion_sort sort;
sort(arr.begin(), arr.end(), [](const auto &lhs, const auto &rhs) {
return lhs.value > rhs.value;
});
for(auto i = 0u; i < (arr.size() - 1u); ++i) {
ASSERT_GT(arr[i].value, arr[i + 1u].value);
}
}
TEST(Algorithm, InsertionSortEmptyContainer) {
std::vector<int> vec{};
entt::insertion_sort sort;
// this should crash with asan enabled if we break the constraint
sort(vec.begin(), vec.end());
}
TEST(Algorithm, RadixSort) {
std::array<uint32_t, 5> arr{{4, 1, 3, 2, 0}};
entt::radix_sort<8, 32> sort;
sort(arr.begin(), arr.end(), [](const auto &value) {
return value;
});
for(auto i = 0u; i < (arr.size() - 1u); ++i) {
ASSERT_LT(arr[i], arr[i + 1u]);
}
}
TEST(Algorithm, RadixSortBoxedInt) {
std::array<boxed_int, 6> arr{{{4}, {1}, {3}, {2}, {0}, {6}}};
entt::radix_sort<2, 6> sort;
sort(arr.rbegin(), arr.rend(), [](const auto &instance) {
return instance.value;
});
for(auto i = 0u; i < (arr.size() - 1u); ++i) {
ASSERT_GT(arr[i].value, arr[i + 1u].value);
}
}
TEST(Algorithm, RadixSortEmptyContainer) {
std::vector<int> vec{};
entt::radix_sort<8, 32> sort;
// this should crash with asan enabled if we break the constraint
sort(vec.begin(), vec.end());
}

1466
test/entt/core/any.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/compressed_pair.hpp>
struct empty_type {};
struct move_only_type {
move_only_type()
: value{new int{99}} {}
move_only_type(int v)
: value{new int{v}} {}
~move_only_type() {
delete value;
}
move_only_type(const move_only_type &) = delete;
move_only_type &operator=(const move_only_type &) = delete;
move_only_type(move_only_type &&other) noexcept
: value{std::exchange(other.value, nullptr)} {}
move_only_type &operator=(move_only_type &&other) noexcept {
delete value;
value = std::exchange(other.value, nullptr);
return *this;
}
int *value;
};
struct non_default_constructible {
non_default_constructible(int v)
: value{v} {}
int value;
};
TEST(CompressedPair, Size) {
struct local {
int value;
empty_type empty;
};
static_assert(sizeof(entt::compressed_pair<int, int>) == sizeof(int[2u]));
static_assert(sizeof(entt::compressed_pair<empty_type, int>) == sizeof(int));
static_assert(sizeof(entt::compressed_pair<int, empty_type>) == sizeof(int));
static_assert(sizeof(entt::compressed_pair<int, empty_type>) < sizeof(local));
static_assert(sizeof(entt::compressed_pair<int, empty_type>) < sizeof(std::pair<int, empty_type>));
}
TEST(CompressedPair, ConstructCopyMove) {
static_assert(!std::is_default_constructible_v<entt::compressed_pair<non_default_constructible, empty_type>>);
static_assert(std::is_default_constructible_v<entt::compressed_pair<move_only_type, empty_type>>);
static_assert(std::is_copy_constructible_v<entt::compressed_pair<non_default_constructible, empty_type>>);
static_assert(!std::is_copy_constructible_v<entt::compressed_pair<move_only_type, empty_type>>);
static_assert(std::is_copy_assignable_v<entt::compressed_pair<non_default_constructible, empty_type>>);
static_assert(!std::is_copy_assignable_v<entt::compressed_pair<move_only_type, empty_type>>);
static_assert(std::is_move_constructible_v<entt::compressed_pair<move_only_type, empty_type>>);
static_assert(std::is_move_assignable_v<entt::compressed_pair<move_only_type, empty_type>>);
entt::compressed_pair copyable{non_default_constructible{42}, empty_type{}};
auto by_copy{copyable};
ASSERT_EQ(by_copy.first().value, 42);
by_copy.first().value = 3;
copyable = by_copy;
ASSERT_EQ(copyable.first().value, 3);
entt::compressed_pair<empty_type, move_only_type> movable{};
auto by_move{std::move(movable)};
ASSERT_EQ(*by_move.second().value, 99);
ASSERT_EQ(movable.second().value, nullptr);
*by_move.second().value = 3;
movable = std::move(by_move);
ASSERT_EQ(*movable.second().value, 3);
ASSERT_EQ(by_move.second().value, nullptr);
}
TEST(CompressedPair, PiecewiseConstruct) {
std::vector<int> vec{42};
entt::compressed_pair<empty_type, empty_type> empty{std::piecewise_construct, std::make_tuple(), std::make_tuple()};
entt::compressed_pair<std::vector<int>, std::size_t> pair{std::piecewise_construct, std::forward_as_tuple(std::move(vec)), std::make_tuple(sizeof(empty))};
ASSERT_EQ(pair.first().size(), 1u);
ASSERT_EQ(pair.second(), sizeof(empty));
ASSERT_EQ(vec.size(), 0u);
}
TEST(CompressedPair, DeductionGuide) {
int value = 42;
empty_type empty{};
entt::compressed_pair pair{value, 3};
static_assert(std::is_same_v<decltype(entt::compressed_pair{empty_type{}, empty}), entt::compressed_pair<empty_type, empty_type>>);
ASSERT_TRUE((std::is_same_v<decltype(pair), entt::compressed_pair<int, int>>));
ASSERT_EQ(pair.first(), 42);
ASSERT_EQ(pair.second(), 3);
}
TEST(CompressedPair, Getters) {
entt::compressed_pair pair{3, empty_type{}};
const auto &cpair = pair;
static_assert(std::is_same_v<decltype(pair.first()), int &>);
static_assert(std::is_same_v<decltype(pair.second()), empty_type &>);
static_assert(std::is_same_v<decltype(cpair.first()), const int &>);
static_assert(std::is_same_v<decltype(cpair.second()), const empty_type &>);
ASSERT_EQ(pair.first(), cpair.first());
ASSERT_EQ(&pair.second(), &cpair.second());
}
TEST(CompressedPair, Swap) {
entt::compressed_pair pair{1, 2};
entt::compressed_pair other{3, 4};
swap(pair, other);
ASSERT_EQ(pair.first(), 3);
ASSERT_EQ(pair.second(), 4);
ASSERT_EQ(other.first(), 1);
ASSERT_EQ(other.second(), 2);
pair.swap(other);
ASSERT_EQ(pair.first(), 1);
ASSERT_EQ(pair.second(), 2);
ASSERT_EQ(other.first(), 3);
ASSERT_EQ(other.second(), 4);
}
TEST(CompressedPair, Get) {
entt::compressed_pair pair{1, 2};
ASSERT_EQ(pair.get<0>(), 1);
ASSERT_EQ(pair.get<1>(), 2);
ASSERT_EQ(&pair.get<0>(), &pair.first());
ASSERT_EQ(&pair.get<1>(), &pair.second());
auto &&[first, second] = pair;
ASSERT_EQ(first, 1);
ASSERT_EQ(second, 2);
first = 3;
second = 4;
ASSERT_EQ(pair.first(), 3);
ASSERT_EQ(pair.second(), 4);
auto &[cfirst, csecond] = std::as_const(pair);
ASSERT_EQ(cfirst, 3);
ASSERT_EQ(csecond, 4);
static_assert(std::is_same_v<decltype(cfirst), const int>);
static_assert(std::is_same_v<decltype(csecond), const int>);
auto [tfirst, tsecond] = entt::compressed_pair{9, 99};
ASSERT_EQ(tfirst, 9);
ASSERT_EQ(tsecond, 99);
static_assert(std::is_same_v<decltype(cfirst), const int>);
static_assert(std::is_same_v<decltype(csecond), const int>);
}

72
test/entt/core/enum.cpp Normal file
View File

@ -0,0 +1,72 @@
#include <cstdint>
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/core/enum.hpp>
enum class detected {
foo = 0x01,
bar = 0x02,
quux = 0x04,
_entt_enum_as_bitmask
};
// small type on purpose
enum class registered : std::uint8_t {
foo = 0x01,
bar = 0x02,
quux = 0x04
};
template<>
struct entt::enum_as_bitmask<registered>
: std::true_type {};
template<typename Type>
struct Enum: testing::Test {
using type = Type;
};
using EnumTypes = ::testing::Types<detected, registered>;
TYPED_TEST_SUITE(Enum, EnumTypes, );
TYPED_TEST(Enum, Functionalities) {
using enum_type = typename TestFixture::type;
ASSERT_TRUE(!!((enum_type::foo | enum_type::bar) & enum_type::foo));
ASSERT_TRUE(!!((enum_type::foo | enum_type::bar) & enum_type::bar));
ASSERT_TRUE(!((enum_type::foo | enum_type::bar) & enum_type::quux));
ASSERT_TRUE(!!((enum_type::foo ^ enum_type::bar) & enum_type::foo));
ASSERT_TRUE(!((enum_type::foo ^ enum_type::foo) & enum_type::foo));
ASSERT_TRUE(!(~enum_type::foo & enum_type::foo));
ASSERT_TRUE(!!(~enum_type::foo & enum_type::bar));
ASSERT_TRUE(enum_type::foo == enum_type::foo);
ASSERT_TRUE(enum_type::foo != enum_type::bar);
enum_type value = enum_type::foo;
ASSERT_TRUE(!!(value & enum_type::foo));
ASSERT_TRUE(!(value & enum_type::bar));
ASSERT_TRUE(!(value & enum_type::quux));
value |= (enum_type::bar | enum_type::quux);
ASSERT_TRUE(!!(value & enum_type::foo));
ASSERT_TRUE(!!(value & enum_type::bar));
ASSERT_TRUE(!!(value & enum_type::quux));
value &= (enum_type::bar | enum_type::quux);
ASSERT_TRUE(!(value & enum_type::foo));
ASSERT_TRUE(!!(value & enum_type::bar));
ASSERT_TRUE(!!(value & enum_type::quux));
value ^= enum_type::bar;
ASSERT_TRUE(!(value & enum_type::foo));
ASSERT_TRUE(!(value & enum_type::bar));
ASSERT_TRUE(!!(value & enum_type::quux));
}

22
test/entt/core/family.cpp Normal file
View File

@ -0,0 +1,22 @@
#include <gtest/gtest.h>
#include <entt/core/family.hpp>
using a_family = entt::family<struct a_family_type>;
using another_family = entt::family<struct another_family_type>;
TEST(Family, Functionalities) {
auto t1 = a_family::value<int>;
auto t2 = a_family::value<int>;
auto t3 = a_family::value<char>;
auto t4 = another_family::value<double>;
ASSERT_EQ(t1, t2);
ASSERT_NE(t1, t3);
ASSERT_EQ(t1, t4);
}
TEST(Family, Uniqueness) {
ASSERT_NE(a_family::value<int>, a_family::value<int &>);
ASSERT_NE(a_family::value<int>, a_family::value<int &&>);
ASSERT_NE(a_family::value<int>, a_family::value<const int &>);
}

View File

@ -0,0 +1,224 @@
#include <cstdint>
#include <string>
#include <string_view>
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
template<typename>
struct foobar_t;
template<>
struct foobar_t<std::uint32_t> {
static constexpr auto value = 0xbf9cf968;
};
template<>
struct foobar_t<std::uint64_t> {
static constexpr auto value = 0x85944171f73967e8;
};
inline constexpr auto foobar_v = foobar_t<entt::id_type>::value;
TEST(BasicHashedString, DeductionGuide) {
static_assert(std::is_same_v<decltype(entt::basic_hashed_string{"foo"}), entt::hashed_string>);
static_assert(std::is_same_v<decltype(entt::basic_hashed_string{L"foo"}), entt::hashed_wstring>);
}
TEST(HashedString, Functionalities) {
using namespace entt::literals;
using hash_type = entt::hashed_string::hash_type;
const char *bar = "bar";
auto foo_hs = entt::hashed_string{"foo"};
auto bar_hs = entt::hashed_string{bar};
ASSERT_NE(static_cast<hash_type>(foo_hs), static_cast<hash_type>(bar_hs));
ASSERT_STREQ(static_cast<const char *>(foo_hs), "foo");
ASSERT_STREQ(static_cast<const char *>(bar_hs), bar);
ASSERT_STREQ(foo_hs.data(), "foo");
ASSERT_STREQ(bar_hs.data(), bar);
ASSERT_EQ(foo_hs.size(), 3u);
ASSERT_EQ(bar_hs.size(), 3u);
ASSERT_EQ(foo_hs, foo_hs);
ASSERT_NE(foo_hs, bar_hs);
entt::hashed_string hs{"foobar"};
ASSERT_EQ(static_cast<hash_type>(hs), foobar_v);
ASSERT_EQ(hs.value(), foobar_v);
ASSERT_EQ(foo_hs, "foo"_hs);
ASSERT_NE(bar_hs, "foo"_hs);
entt::hashed_string empty_hs{};
ASSERT_EQ(empty_hs, entt::hashed_string{});
ASSERT_NE(empty_hs, foo_hs);
empty_hs = foo_hs;
ASSERT_NE(empty_hs, entt::hashed_string{});
ASSERT_EQ(empty_hs, foo_hs);
}
TEST(HashedString, Empty) {
using hash_type = entt::hashed_string::hash_type;
entt::hashed_string hs{};
ASSERT_EQ(hs.size(), 0u);
ASSERT_EQ(static_cast<hash_type>(hs), hash_type{});
ASSERT_EQ(static_cast<const char *>(hs), nullptr);
}
TEST(HashedString, Correctness) {
const char *foobar = "foobar";
std::string_view view{"foobar__", 6};
ASSERT_EQ(entt::hashed_string{foobar}, foobar_v);
ASSERT_EQ((entt::hashed_string{view.data(), view.size()}), foobar_v);
ASSERT_EQ(entt::hashed_string{"foobar"}, foobar_v);
ASSERT_EQ(entt::hashed_string::value(foobar), foobar_v);
ASSERT_EQ(entt::hashed_string::value(view.data(), view.size()), foobar_v);
ASSERT_EQ(entt::hashed_string::value("foobar"), foobar_v);
ASSERT_EQ(entt::hashed_string{foobar}.size(), 6u);
ASSERT_EQ((entt::hashed_string{view.data(), view.size()}).size(), 6u);
ASSERT_EQ(entt::hashed_string{"foobar"}.size(), 6u);
}
TEST(HashedString, Order) {
using namespace entt::literals;
const entt::hashed_string lhs = "foo"_hs;
const entt::hashed_string rhs = "bar"_hs;
ASSERT_FALSE(lhs < lhs);
ASSERT_FALSE(rhs < rhs);
ASSERT_LT(rhs, lhs);
ASSERT_LE(rhs, lhs);
ASSERT_GT(lhs, rhs);
ASSERT_GE(lhs, rhs);
}
TEST(HashedString, Constexprness) {
using namespace entt::literals;
constexpr std::string_view view{"foobar__", 6};
static_assert(entt::hashed_string{"quux"} == "quux"_hs);
static_assert(entt::hashed_string{"foobar"} == foobar_v);
static_assert(entt::hashed_string::value("quux") == "quux"_hs);
static_assert(entt::hashed_string::value("foobar") == foobar_v);
static_assert(entt::hashed_string{"quux", 4} == "quux"_hs);
static_assert(entt::hashed_string{view.data(), view.size()} == foobar_v);
static_assert(entt::hashed_string::value("quux", 4) == "quux"_hs);
static_assert(entt::hashed_string::value(view.data(), view.size()) == foobar_v);
static_assert(entt::hashed_string{"bar"} < "foo"_hs);
static_assert(entt::hashed_string{"bar"} <= "bar"_hs);
static_assert(entt::hashed_string{"foo"} > "bar"_hs);
static_assert(entt::hashed_string{"foo"} >= "foo"_hs);
}
TEST(HashedWString, Functionalities) {
using namespace entt::literals;
using hash_type = entt::hashed_wstring::hash_type;
const wchar_t *bar = L"bar";
auto foo_hws = entt::hashed_wstring{L"foo"};
auto bar_hws = entt::hashed_wstring{bar};
ASSERT_NE(static_cast<hash_type>(foo_hws), static_cast<hash_type>(bar_hws));
ASSERT_STREQ(static_cast<const wchar_t *>(foo_hws), L"foo");
ASSERT_STREQ(static_cast<const wchar_t *>(bar_hws), bar);
ASSERT_STREQ(foo_hws.data(), L"foo");
ASSERT_STREQ(bar_hws.data(), bar);
ASSERT_EQ(foo_hws.size(), 3u);
ASSERT_EQ(bar_hws.size(), 3u);
ASSERT_EQ(foo_hws, foo_hws);
ASSERT_NE(foo_hws, bar_hws);
entt::hashed_wstring hws{L"foobar"};
ASSERT_EQ(static_cast<hash_type>(hws), foobar_v);
ASSERT_EQ(hws.value(), foobar_v);
ASSERT_EQ(foo_hws, L"foo"_hws);
ASSERT_NE(bar_hws, L"foo"_hws);
}
TEST(HashedWString, Empty) {
using hash_type = entt::hashed_wstring::hash_type;
entt::hashed_wstring hws{};
ASSERT_EQ(hws.size(), 0u);
ASSERT_EQ(static_cast<hash_type>(hws), hash_type{});
ASSERT_EQ(static_cast<const wchar_t *>(hws), nullptr);
}
TEST(HashedWString, Correctness) {
const wchar_t *foobar = L"foobar";
std::wstring_view view{L"foobar__", 6};
ASSERT_EQ(entt::hashed_wstring{foobar}, foobar_v);
ASSERT_EQ((entt::hashed_wstring{view.data(), view.size()}), foobar_v);
ASSERT_EQ(entt::hashed_wstring{L"foobar"}, foobar_v);
ASSERT_EQ(entt::hashed_wstring::value(foobar), foobar_v);
ASSERT_EQ(entt::hashed_wstring::value(view.data(), view.size()), foobar_v);
ASSERT_EQ(entt::hashed_wstring::value(L"foobar"), foobar_v);
ASSERT_EQ(entt::hashed_wstring{foobar}.size(), 6u);
ASSERT_EQ((entt::hashed_wstring{view.data(), view.size()}).size(), 6u);
ASSERT_EQ(entt::hashed_wstring{L"foobar"}.size(), 6u);
}
TEST(HashedWString, Order) {
using namespace entt::literals;
const entt::hashed_wstring lhs = L"foo"_hws;
const entt::hashed_wstring rhs = L"bar"_hws;
ASSERT_FALSE(lhs < lhs);
ASSERT_FALSE(rhs < rhs);
ASSERT_LT(rhs, lhs);
ASSERT_LE(rhs, lhs);
ASSERT_GT(lhs, rhs);
ASSERT_GE(lhs, rhs);
}
TEST(HashedWString, Constexprness) {
using namespace entt::literals;
constexpr std::wstring_view view{L"foobar__", 6};
static_assert(entt::hashed_wstring{L"quux"} == L"quux"_hws);
static_assert(entt::hashed_wstring{L"foobar"} == foobar_v);
static_assert(entt::hashed_wstring::value(L"quux") == L"quux"_hws);
static_assert(entt::hashed_wstring::value(L"foobar") == foobar_v);
static_assert(entt::hashed_wstring{L"quux", 4} == L"quux"_hws);
static_assert(entt::hashed_wstring{view.data(), view.size()} == foobar_v);
static_assert(entt::hashed_wstring::value(L"quux", 4) == L"quux"_hws);
static_assert(entt::hashed_wstring::value(view.data(), view.size()) == foobar_v);
static_assert(entt::hashed_wstring{L"bar"} < L"foo"_hws);
static_assert(entt::hashed_wstring{L"bar"} <= L"bar"_hws);
static_assert(entt::hashed_wstring{L"foo"} > L"bar"_hws);
static_assert(entt::hashed_wstring{L"foo"} >= L"foo"_hws);
}

31
test/entt/core/ident.cpp Normal file
View File

@ -0,0 +1,31 @@
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/core/ident.hpp>
struct a_type {};
struct another_type {};
TEST(Ident, Uniqueness) {
using id = entt::ident<a_type, another_type>;
constexpr a_type an_instance;
constexpr another_type another_instance;
ASSERT_NE(id::value<a_type>, id::value<another_type>);
ASSERT_EQ(id::value<a_type>, id::value<decltype(an_instance)>);
ASSERT_NE(id::value<a_type>, id::value<decltype(another_instance)>);
ASSERT_EQ(id::value<a_type>, id::value<a_type>);
ASSERT_EQ(id::value<another_type>, id::value<another_type>);
// test uses in constant expressions
switch(id::value<another_type>) {
case id::value<a_type>:
FAIL();
case id::value<another_type>:
SUCCEED();
}
}
TEST(Identifier, SingleType) {
using id = entt::ident<a_type>;
[[maybe_unused]] std::integral_constant<id::value_type, id::value<a_type>> ic;
}

View File

@ -0,0 +1,55 @@
#include <cstddef>
#include <type_traits>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/iterator.hpp>
struct clazz {
int value{0};
};
TEST(InputIteratorPointer, Functionalities) {
clazz instance{};
entt::input_iterator_pointer ptr{std::move(instance)};
ptr->value = 42;
ASSERT_EQ(instance.value, 0);
ASSERT_EQ(ptr->value, 42);
ASSERT_EQ(ptr->value, (*ptr).value);
ASSERT_EQ(ptr.operator->(), &ptr.operator*());
}
TEST(IotaIterator, Functionalities) {
entt::iota_iterator<std::size_t> first{};
const entt::iota_iterator<std::size_t> last{2u};
ASSERT_NE(first, last);
ASSERT_FALSE(first == last);
ASSERT_TRUE(first != last);
ASSERT_EQ(*first++, 0u);
ASSERT_EQ(*first, 1u);
ASSERT_EQ(*++first, *last);
ASSERT_EQ(*first, 2u);
}
TEST(IterableAdaptor, Functionalities) {
std::vector<int> vec{1, 2};
entt::iterable_adaptor iterable{vec.begin(), vec.end()};
decltype(iterable) other{};
ASSERT_NO_FATAL_FAILURE(other = iterable);
ASSERT_NO_FATAL_FAILURE(std::swap(other, iterable));
ASSERT_EQ(iterable.begin(), vec.begin());
ASSERT_EQ(iterable.end(), vec.end());
ASSERT_EQ(*iterable.cbegin(), 1);
ASSERT_EQ(*++iterable.cbegin(), 2);
ASSERT_EQ(++iterable.cbegin(), --iterable.end());
for(auto value: entt::iterable_adaptor<const int *, const void *>{vec.data(), vec.data() + 1u}) {
ASSERT_EQ(value, 1);
}
}

241
test/entt/core/memory.cpp Normal file
View File

@ -0,0 +1,241 @@
#include <cmath>
#include <cstddef>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/memory.hpp>
#include "../common/basic_test_allocator.hpp"
#include "../common/config.h"
#include "../common/throwing_allocator.hpp"
#include "../common/throwing_type.hpp"
#include "../common/tracked_memory_resource.hpp"
TEST(ToAddress, Functionalities) {
std::shared_ptr<int> shared = std::make_shared<int>();
auto *plain = std::addressof(*shared);
ASSERT_EQ(entt::to_address(shared), plain);
ASSERT_EQ(entt::to_address(plain), plain);
}
TEST(PoccaPocmaAndPocs, Functionalities) {
test::basic_test_allocator<int> lhs, rhs;
// honestly, I don't even know how one is supposed to test such a thing :)
entt::propagate_on_container_copy_assignment(lhs, rhs);
entt::propagate_on_container_move_assignment(lhs, rhs);
entt::propagate_on_container_swap(lhs, rhs);
}
ENTT_DEBUG_TEST(PoccaPocmaAndPocsDeathTest, Functionalities) {
using pocs = std::false_type;
test::basic_test_allocator<int, pocs> lhs, rhs;
ASSERT_DEATH(entt::propagate_on_container_swap(lhs, rhs), "");
}
TEST(IsPowerOfTwo, Functionalities) {
// constexpr-ness guaranteed
constexpr auto zero_is_power_of_two = entt::is_power_of_two(0u);
ASSERT_FALSE(zero_is_power_of_two);
ASSERT_TRUE(entt::is_power_of_two(1u));
ASSERT_TRUE(entt::is_power_of_two(2u));
ASSERT_TRUE(entt::is_power_of_two(4u));
ASSERT_FALSE(entt::is_power_of_two(7u));
ASSERT_TRUE(entt::is_power_of_two(128u));
ASSERT_FALSE(entt::is_power_of_two(200u));
}
TEST(NextPowerOfTwo, Functionalities) {
// constexpr-ness guaranteed
constexpr auto next_power_of_two_of_zero = entt::next_power_of_two(0u);
ASSERT_EQ(next_power_of_two_of_zero, 1u);
ASSERT_EQ(entt::next_power_of_two(1u), 1u);
ASSERT_EQ(entt::next_power_of_two(2u), 2u);
ASSERT_EQ(entt::next_power_of_two(3u), 4u);
ASSERT_EQ(entt::next_power_of_two(17u), 32u);
ASSERT_EQ(entt::next_power_of_two(32u), 32u);
ASSERT_EQ(entt::next_power_of_two(33u), 64u);
ASSERT_EQ(entt::next_power_of_two(std::pow(2, 16)), std::pow(2, 16));
ASSERT_EQ(entt::next_power_of_two(std::pow(2, 16) + 1u), std::pow(2, 17));
}
ENTT_DEBUG_TEST(NextPowerOfTwoDeathTest, Functionalities) {
ASSERT_DEATH(static_cast<void>(entt::next_power_of_two((std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)) + 1)), "");
}
TEST(FastMod, Functionalities) {
// constexpr-ness guaranteed
constexpr auto fast_mod_of_zero = entt::fast_mod(0u, 8u);
ASSERT_EQ(fast_mod_of_zero, 0u);
ASSERT_EQ(entt::fast_mod(7u, 8u), 7u);
ASSERT_EQ(entt::fast_mod(8u, 8u), 0u);
}
TEST(AllocateUnique, Functionalities) {
test::throwing_allocator<test::throwing_type> allocator{};
test::throwing_allocator<test::throwing_type>::trigger_on_allocate = true;
test::throwing_type::trigger_on_value = 0;
ASSERT_THROW((entt::allocate_unique<test::throwing_type>(allocator, 0)), test::throwing_allocator<test::throwing_type>::exception_type);
ASSERT_THROW((entt::allocate_unique<test::throwing_type>(allocator, test::throwing_type{0})), test::throwing_type::exception_type);
std::unique_ptr<test::throwing_type, entt::allocation_deleter<test::throwing_allocator<test::throwing_type>>> ptr = entt::allocate_unique<test::throwing_type>(allocator, 42);
ASSERT_TRUE(ptr);
ASSERT_EQ(*ptr, 42);
ptr.reset();
ASSERT_FALSE(ptr);
}
#if defined(ENTT_HAS_TRACKED_MEMORY_RESOURCE)
TEST(AllocateUnique, NoUsesAllocatorConstruction) {
test::tracked_memory_resource memory_resource{};
std::pmr::polymorphic_allocator<int> allocator{&memory_resource};
using type = std::unique_ptr<int, entt::allocation_deleter<std::pmr::polymorphic_allocator<int>>>;
type ptr = entt::allocate_unique<int>(allocator, 0);
ASSERT_EQ(memory_resource.do_allocate_counter(), 1u);
ASSERT_EQ(memory_resource.do_deallocate_counter(), 0u);
}
TEST(AllocateUnique, UsesAllocatorConstruction) {
using string_type = typename test::tracked_memory_resource::string_type;
test::tracked_memory_resource memory_resource{};
std::pmr::polymorphic_allocator<string_type> allocator{&memory_resource};
using type = std::unique_ptr<string_type, entt::allocation_deleter<std::pmr::polymorphic_allocator<string_type>>>;
type ptr = entt::allocate_unique<string_type>(allocator, test::tracked_memory_resource::default_value);
ASSERT_GT(memory_resource.do_allocate_counter(), 1u);
ASSERT_EQ(memory_resource.do_deallocate_counter(), 0u);
}
#endif
TEST(UsesAllocatorConstructionArgs, NoUsesAllocatorConstruction) {
const auto value = 42;
const auto args = entt::uses_allocator_construction_args<int>(std::allocator<int>{}, value);
static_assert(std::tuple_size_v<decltype(args)> == 1u);
static_assert(std::is_same_v<decltype(args), const std::tuple<const int &>>);
ASSERT_EQ(std::get<0>(args), value);
}
TEST(UsesAllocatorConstructionArgs, LeadingAllocatorConvention) {
const auto value = 42;
const auto args = entt::uses_allocator_construction_args<std::tuple<int, char>>(std::allocator<int>{}, value, 'c');
static_assert(std::tuple_size_v<decltype(args)> == 4u);
static_assert(std::is_same_v<decltype(args), const std::tuple<std::allocator_arg_t, const std::allocator<int> &, const int &, char &&>>);
ASSERT_EQ(std::get<2>(args), value);
}
TEST(UsesAllocatorConstructionArgs, TrailingAllocatorConvention) {
const auto size = 42u;
const auto args = entt::uses_allocator_construction_args<std::vector<int>>(std::allocator<int>{}, size);
static_assert(std::tuple_size_v<decltype(args)> == 2u);
static_assert(std::is_same_v<decltype(args), const std::tuple<const unsigned int &, const std::allocator<int> &>>);
ASSERT_EQ(std::get<0>(args), size);
}
TEST(UsesAllocatorConstructionArgs, PairPiecewiseConstruct) {
const auto size = 42u;
const auto tup = std::make_tuple(size);
const auto args = entt::uses_allocator_construction_args<std::pair<int, std::vector<int>>>(std::allocator<int>{}, std::piecewise_construct, std::make_tuple(3), tup);
static_assert(std::tuple_size_v<decltype(args)> == 3u);
static_assert(std::is_same_v<decltype(args), const std::tuple<std::piecewise_construct_t, std::tuple<int &&>, std::tuple<const unsigned int &, const std::allocator<int> &>>>);
ASSERT_EQ(std::get<0>(std::get<2>(args)), size);
}
TEST(UsesAllocatorConstructionArgs, PairNoArgs) {
[[maybe_unused]] const auto args = entt::uses_allocator_construction_args<std::pair<int, std::vector<int>>>(std::allocator<int>{});
static_assert(std::tuple_size_v<decltype(args)> == 3u);
static_assert(std::is_same_v<decltype(args), const std::tuple<std::piecewise_construct_t, std::tuple<>, std::tuple<const std::allocator<int> &>>>);
}
TEST(UsesAllocatorConstructionArgs, PairValues) {
const auto size = 42u;
const auto args = entt::uses_allocator_construction_args<std::pair<int, std::vector<int>>>(std::allocator<int>{}, 3, size);
static_assert(std::tuple_size_v<decltype(args)> == 3u);
static_assert(std::is_same_v<decltype(args), const std::tuple<std::piecewise_construct_t, std::tuple<int &&>, std::tuple<const unsigned int &, const std::allocator<int> &>>>);
ASSERT_EQ(std::get<0>(std::get<2>(args)), size);
}
TEST(UsesAllocatorConstructionArgs, PairConstLValueReference) {
const auto value = std::make_pair(3, 42u);
const auto args = entt::uses_allocator_construction_args<std::pair<int, std::vector<int>>>(std::allocator<int>{}, value);
static_assert(std::tuple_size_v<decltype(args)> == 3u);
static_assert(std::is_same_v<decltype(args), const std::tuple<std::piecewise_construct_t, std::tuple<const int &>, std::tuple<const unsigned int &, const std::allocator<int> &>>>);
ASSERT_EQ(std::get<0>(std::get<1>(args)), 3);
ASSERT_EQ(std::get<0>(std::get<2>(args)), 42u);
}
TEST(UsesAllocatorConstructionArgs, PairRValueReference) {
[[maybe_unused]] const auto args = entt::uses_allocator_construction_args<std::pair<int, std::vector<int>>>(std::allocator<int>{}, std::make_pair(3, 42u));
static_assert(std::tuple_size_v<decltype(args)> == 3u);
static_assert(std::is_same_v<decltype(args), const std::tuple<std::piecewise_construct_t, std::tuple<int &&>, std::tuple<unsigned int &&, const std::allocator<int> &>>>);
}
TEST(MakeObjUsingAllocator, Functionalities) {
const auto size = 42u;
test::throwing_allocator<int>::trigger_on_allocate = true;
ASSERT_THROW((entt::make_obj_using_allocator<std::vector<int, test::throwing_allocator<int>>>(test::throwing_allocator<int>{}, size)), test::throwing_allocator<int>::exception_type);
const auto vec = entt::make_obj_using_allocator<std::vector<int>>(std::allocator<int>{}, size);
ASSERT_FALSE(vec.empty());
ASSERT_EQ(vec.size(), size);
}
TEST(UninitializedConstructUsingAllocator, NoUsesAllocatorConstruction) {
alignas(int) std::byte storage[sizeof(int)];
std::allocator<int> allocator{};
int *value = entt::uninitialized_construct_using_allocator(reinterpret_cast<int *>(&storage), allocator, 42);
ASSERT_EQ(*value, 42);
}
#if defined(ENTT_HAS_TRACKED_MEMORY_RESOURCE)
TEST(UninitializedConstructUsingAllocator, UsesAllocatorConstruction) {
using string_type = typename test::tracked_memory_resource::string_type;
test::tracked_memory_resource memory_resource{};
std::pmr::polymorphic_allocator<string_type> allocator{&memory_resource};
alignas(string_type) std::byte storage[sizeof(string_type)];
string_type *value = entt::uninitialized_construct_using_allocator(reinterpret_cast<string_type *>(&storage), allocator, test::tracked_memory_resource::default_value);
ASSERT_GT(memory_resource.do_allocate_counter(), 0u);
ASSERT_EQ(memory_resource.do_deallocate_counter(), 0u);
ASSERT_EQ(*value, test::tracked_memory_resource::default_value);
value->~string_type();
}
#endif

View File

@ -0,0 +1,22 @@
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/monostate.hpp>
TEST(Monostate, Functionalities) {
using namespace entt::literals;
const bool b_pre = entt::monostate<entt::hashed_string{"foobar"}>{};
const int i_pre = entt::monostate<"foobar"_hs>{};
ASSERT_FALSE(b_pre);
ASSERT_EQ(i_pre, int{});
entt::monostate<"foobar"_hs>{} = true;
entt::monostate_v<"foobar"_hs> = 42;
const bool &b_post = entt::monostate<"foobar"_hs>{};
const int &i_post = entt::monostate_v<entt::hashed_string{"foobar"}>;
ASSERT_TRUE(b_post);
ASSERT_EQ(i_post, 42);
}

38
test/entt/core/tuple.cpp Normal file
View File

@ -0,0 +1,38 @@
#include <tuple>
#include <gtest/gtest.h>
#include <entt/core/tuple.hpp>
TEST(Tuple, IsTuple) {
static_assert(!entt::is_tuple_v<int>);
static_assert(entt::is_tuple_v<std::tuple<>>);
static_assert(entt::is_tuple_v<std::tuple<int>>);
static_assert(entt::is_tuple_v<std::tuple<int, char>>);
}
TEST(Tuple, UnwrapTuple) {
auto single = std::make_tuple(42);
auto multi = std::make_tuple(42, 'c');
auto ref = std::forward_as_tuple(std::get<0>(single));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(single)), int &>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(multi)), std::tuple<int, char> &>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(ref)), int &>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(std::move(single))), int &&>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(std::move(multi))), std::tuple<int, char> &&>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(std::move(ref))), int &>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(std::as_const(single))), const int &>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(std::as_const(multi))), const std::tuple<int, char> &>));
ASSERT_TRUE((std::is_same_v<decltype(entt::unwrap_tuple(std::as_const(ref))), int &>));
ASSERT_EQ(entt::unwrap_tuple(single), 42);
ASSERT_EQ(entt::unwrap_tuple(multi), multi);
ASSERT_EQ(entt::unwrap_tuple(std::move(ref)), 42);
}
TEST(Tuple, ForwardApply) {
ASSERT_EQ(entt::forward_apply{[](auto &&...args) { return sizeof...(args); }}(std::make_tuple()), 0u);
ASSERT_EQ(entt::forward_apply{[](int i) { return i; }}(std::make_tuple(42)), 42);
ASSERT_EQ(entt::forward_apply{[](auto... args) { return (args + ...); }}(std::make_tuple('a', 1u)), 'b');
}

View File

@ -0,0 +1,116 @@
#include <string_view>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/core/type_traits.hpp>
template<>
struct entt::type_name<float> final {
[[nodiscard]] static constexpr std::string_view value() noexcept {
return std::string_view{""};
}
};
TEST(TypeIndex, Functionalities) {
ASSERT_EQ(entt::type_index<int>::value(), entt::type_index<int>::value());
ASSERT_NE(entt::type_index<int>::value(), entt::type_index<char>::value());
ASSERT_NE(entt::type_index<int>::value(), entt::type_index<int &&>::value());
ASSERT_NE(entt::type_index<int &>::value(), entt::type_index<const int &>::value());
ASSERT_EQ(static_cast<entt::id_type>(entt::type_index<int>{}), entt::type_index<int>::value());
}
TEST(TypeHash, Functionalities) {
ASSERT_NE(entt::type_hash<int>::value(), entt::type_hash<const int>::value());
ASSERT_NE(entt::type_hash<int>::value(), entt::type_hash<char>::value());
ASSERT_EQ(entt::type_hash<int>::value(), entt::type_hash<int>::value());
ASSERT_EQ(static_cast<entt::id_type>(entt::type_hash<int>{}), entt::type_hash<int>::value());
}
TEST(TypeName, Functionalities) {
ASSERT_EQ(entt::type_name<int>::value(), std::string_view{"int"});
ASSERT_EQ(entt::type_name<float>{}.value(), std::string_view{""});
ASSERT_TRUE((entt::type_name<entt::integral_constant<3>>::value() == std::string_view{"std::integral_constant<int, 3>"})
|| (entt::type_name<entt::integral_constant<3>>::value() == std::string_view{"std::__1::integral_constant<int, 3>"})
|| (entt::type_name<entt::integral_constant<3>>::value() == std::string_view{"struct std::integral_constant<int,3>"}));
ASSERT_TRUE(((entt::type_name<entt::type_list<entt::type_list<int, char>, double>>::value()) == std::string_view{"entt::type_list<entt::type_list<int, char>, double>"})
|| ((entt::type_name<entt::type_list<entt::type_list<int, char>, double>>::value()) == std::string_view{"struct entt::type_list<struct entt::type_list<int,char>,double>"}));
ASSERT_EQ(static_cast<std::string_view>(entt::type_name<int>{}), entt::type_name<int>::value());
}
TEST(TypeInfo, Functionalities) {
static_assert(std::is_copy_constructible_v<entt::type_info>);
static_assert(std::is_move_constructible_v<entt::type_info>);
static_assert(std::is_copy_assignable_v<entt::type_info>);
static_assert(std::is_move_assignable_v<entt::type_info>);
entt::type_info info{std::in_place_type<int>};
entt::type_info other{std::in_place_type<void>};
ASSERT_EQ(info, entt::type_info{std::in_place_type<int &>});
ASSERT_EQ(info, entt::type_info{std::in_place_type<int &&>});
ASSERT_EQ(info, entt::type_info{std::in_place_type<const int &>});
ASSERT_NE(info, other);
ASSERT_TRUE(info == info);
ASSERT_FALSE(info != info);
ASSERT_EQ(info.index(), entt::type_index<int>::value());
ASSERT_EQ(info.hash(), entt::type_hash<int>::value());
ASSERT_EQ(info.name(), entt::type_name<int>::value());
other = info;
ASSERT_EQ(other.index(), entt::type_index<int>::value());
ASSERT_EQ(other.hash(), entt::type_hash<int>::value());
ASSERT_EQ(other.name(), entt::type_name<int>::value());
ASSERT_EQ(other.index(), info.index());
ASSERT_EQ(other.hash(), info.hash());
ASSERT_EQ(other.name(), info.name());
other = std::move(info);
ASSERT_EQ(other.index(), entt::type_index<int>::value());
ASSERT_EQ(other.hash(), entt::type_hash<int>::value());
ASSERT_EQ(other.name(), entt::type_name<int>::value());
ASSERT_EQ(other.index(), info.index());
ASSERT_EQ(other.hash(), info.hash());
ASSERT_EQ(other.name(), info.name());
}
TEST(TypeInfo, Order) {
entt::type_info rhs = entt::type_id<int>();
entt::type_info lhs = entt::type_id<char>();
// let's adjust the two objects since values are generated at runtime
rhs < lhs ? void() : std::swap(lhs, rhs);
ASSERT_FALSE(lhs < lhs);
ASSERT_FALSE(rhs < rhs);
ASSERT_LT(rhs, lhs);
ASSERT_LE(rhs, lhs);
ASSERT_GT(lhs, rhs);
ASSERT_GE(lhs, rhs);
}
TEST(TypeId, Functionalities) {
const int value = 42;
ASSERT_EQ(entt::type_id(value), entt::type_id<int>());
ASSERT_EQ(entt::type_id(42), entt::type_id<int>());
ASSERT_EQ(entt::type_id<int>(), entt::type_id<int>());
ASSERT_EQ(entt::type_id<int &>(), entt::type_id<int &&>());
ASSERT_EQ(entt::type_id<int &>(), entt::type_id<int>());
ASSERT_NE(entt::type_id<int>(), entt::type_id<char>());
ASSERT_EQ(&entt::type_id<int>(), &entt::type_id<int>());
ASSERT_NE(&entt::type_id<int>(), &entt::type_id<void>());
}

View File

@ -0,0 +1,224 @@
#include <functional>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/type_traits.hpp>
struct not_comparable {
bool operator==(const not_comparable &) const = delete;
};
struct nlohmann_json_like final {
using value_type = nlohmann_json_like;
bool operator==(const nlohmann_json_like &) const {
return true;
}
};
struct clazz {
char foo(int) {
return {};
}
int bar(double, float) const {
return {};
}
bool quux;
};
int free_function(int, const double &) {
return 42;
}
template<typename, typename Type = void>
struct multi_argument_operation {
using type = Type;
};
TEST(SizeOf, Functionalities) {
static_assert(entt::size_of_v<void> == 0u);
static_assert(entt::size_of_v<char> == sizeof(char));
static_assert(entt::size_of_v<int[]> == 0u);
static_assert(entt::size_of_v<int[3]> == sizeof(int[3]));
}
TEST(UnpackAsType, Functionalities) {
auto test = [](auto &&...args) {
return [](entt::unpack_as_type<int, decltype(args)>... value) {
return (value + ... + 0);
};
};
ASSERT_EQ(test('c', 42., true)(1, 2, 3), 6);
}
TEST(UnpackAsValue, Functionalities) {
auto test = [](auto &&...args) {
return (entt::unpack_as_value<2, decltype(args)> + ... + 0);
};
ASSERT_EQ(test('c', 42., true), 6);
}
TEST(IntegralConstant, Functionalities) {
entt::integral_constant<3> constant{};
static_assert(std::is_same_v<typename entt::integral_constant<3>::value_type, int>);
static_assert(constant.value == 3);
}
TEST(Choice, Functionalities) {
static_assert(std::is_base_of_v<entt::choice_t<0>, entt::choice_t<1>>);
static_assert(!std::is_base_of_v<entt::choice_t<1>, entt::choice_t<0>>);
}
TEST(TypeList, Functionalities) {
using type = entt::type_list<int, char>;
using other = entt::type_list<double>;
static_assert(type::size == 2u);
static_assert(other::size == 1u);
static_assert(std::is_same_v<decltype(type{} + other{}), entt::type_list<int, char, double>>);
static_assert(std::is_same_v<entt::type_list_cat_t<type, other, type, other>, entt::type_list<int, char, double, int, char, double>>);
static_assert(std::is_same_v<entt::type_list_cat_t<type, other>, entt::type_list<int, char, double>>);
static_assert(std::is_same_v<entt::type_list_cat_t<type, type>, entt::type_list<int, char, int, char>>);
static_assert(std::is_same_v<entt::type_list_unique_t<entt::type_list_cat_t<type, type>>, entt::type_list<int, char>>);
static_assert(entt::type_list_contains_v<type, int>);
static_assert(entt::type_list_contains_v<type, char>);
static_assert(!entt::type_list_contains_v<type, double>);
static_assert(std::is_same_v<entt::type_list_element_t<0u, type>, int>);
static_assert(std::is_same_v<entt::type_list_element_t<1u, type>, char>);
static_assert(std::is_same_v<entt::type_list_element_t<0u, other>, double>);
static_assert(entt::type_list_index_v<int, type> == 0u);
static_assert(entt::type_list_index_v<char, type> == 1u);
static_assert(entt::type_list_index_v<double, other> == 0u);
static_assert(std::is_same_v<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<float, bool>>, entt::type_list<int, char, double>>);
static_assert(std::is_same_v<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<int, char, double>>, entt::type_list<>>);
static_assert(std::is_same_v<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<int, char>>, entt::type_list<double>>);
static_assert(std::is_same_v<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<char, double>>, entt::type_list<int>>);
static_assert(std::is_same_v<entt::type_list_diff_t<entt::type_list<int, char, double>, entt::type_list<char>>, entt::type_list<int, double>>);
static_assert(std::is_same_v<entt::type_list_transform_t<entt::type_list<int, char>, entt::type_identity>, entt::type_list<int, char>>);
static_assert(std::is_same_v<entt::type_list_transform_t<entt::type_list<int, char>, std::add_const>, entt::type_list<const int, const char>>);
static_assert(std::is_same_v<entt::type_list_transform_t<entt::type_list<int, char>, multi_argument_operation>, entt::type_list<void, void>>);
}
TEST(ValueList, Functionalities) {
using value = entt::value_list<0, 2>;
using other = entt::value_list<1>;
static_assert(value::size == 2u);
static_assert(other::size == 1u);
static_assert(std::is_same_v<decltype(value{} + other{}), entt::value_list<0, 2, 1>>);
static_assert(std::is_same_v<entt::value_list_cat_t<value, other, value, other>, entt::value_list<0, 2, 1, 0, 2, 1>>);
static_assert(std::is_same_v<entt::value_list_cat_t<value, other>, entt::value_list<0, 2, 1>>);
static_assert(std::is_same_v<entt::value_list_cat_t<value, value>, entt::value_list<0, 2, 0, 2>>);
static_assert(entt::value_list_element_v<0u, value> == 0);
static_assert(entt::value_list_element_v<1u, value> == 2);
static_assert(entt::value_list_element_v<0u, other> == 1);
}
TEST(IsApplicable, Functionalities) {
static_assert(entt::is_applicable_v<void(int, char), std::tuple<double, char>>);
static_assert(!entt::is_applicable_v<void(int, char), std::tuple<int>>);
static_assert(entt::is_applicable_r_v<float, int(int, char), std::tuple<double, char>>);
static_assert(!entt::is_applicable_r_v<float, void(int, char), std::tuple<double, char>>);
static_assert(!entt::is_applicable_r_v<int, int(int, char), std::tuple<void>>);
}
TEST(IsComplete, Functionalities) {
static_assert(!entt::is_complete_v<void>);
static_assert(entt::is_complete_v<int>);
}
TEST(IsIterator, Functionalities) {
static_assert(!entt::is_iterator_v<void>);
static_assert(!entt::is_iterator_v<int>);
static_assert(!entt::is_iterator_v<void *>);
static_assert(entt::is_iterator_v<int *>);
static_assert(entt::is_iterator_v<std::vector<int>::iterator>);
static_assert(entt::is_iterator_v<std::vector<int>::const_iterator>);
static_assert(entt::is_iterator_v<std::vector<int>::reverse_iterator>);
}
TEST(IsEBCOEligible, Functionalities) {
static_assert(entt::is_ebco_eligible_v<not_comparable>);
static_assert(!entt::is_ebco_eligible_v<nlohmann_json_like>);
static_assert(!entt::is_ebco_eligible_v<double>);
static_assert(!entt::is_ebco_eligible_v<void>);
}
TEST(IsTransparent, Functionalities) {
static_assert(!entt::is_transparent_v<std::less<int>>);
static_assert(entt::is_transparent_v<std::less<void>>);
static_assert(!entt::is_transparent_v<std::logical_not<double>>);
static_assert(entt::is_transparent_v<std::logical_not<void>>);
}
TEST(IsEqualityComparable, Functionalities) {
static_assert(entt::is_equality_comparable_v<int>);
static_assert(entt::is_equality_comparable_v<const int>);
static_assert(entt::is_equality_comparable_v<std::vector<int>>);
static_assert(entt::is_equality_comparable_v<std::vector<std::vector<int>>>);
static_assert(entt::is_equality_comparable_v<std::unordered_map<int, int>>);
static_assert(entt::is_equality_comparable_v<std::unordered_map<int, std::unordered_map<int, char>>>);
static_assert(entt::is_equality_comparable_v<std::pair<const int, int>>);
static_assert(entt::is_equality_comparable_v<std::pair<const int, std::unordered_map<int, char>>>);
static_assert(entt::is_equality_comparable_v<std::vector<not_comparable>::iterator>);
static_assert(entt::is_equality_comparable_v<nlohmann_json_like>);
static_assert(!entt::is_equality_comparable_v<not_comparable>);
static_assert(!entt::is_equality_comparable_v<const not_comparable>);
static_assert(!entt::is_equality_comparable_v<std::vector<not_comparable>>);
static_assert(!entt::is_equality_comparable_v<std::vector<std::vector<not_comparable>>>);
static_assert(!entt::is_equality_comparable_v<std::unordered_map<int, not_comparable>>);
static_assert(!entt::is_equality_comparable_v<std::unordered_map<int, std::unordered_map<int, not_comparable>>>);
static_assert(!entt::is_equality_comparable_v<std::pair<const int, not_comparable>>);
static_assert(!entt::is_equality_comparable_v<std::pair<const int, std::unordered_map<int, not_comparable>>>);
static_assert(!entt::is_equality_comparable_v<void>);
}
TEST(ConstnessAs, Functionalities) {
static_assert(std::is_same_v<entt::constness_as_t<int, char>, int>);
static_assert(std::is_same_v<entt::constness_as_t<const int, char>, int>);
static_assert(std::is_same_v<entt::constness_as_t<int, const char>, const int>);
static_assert(std::is_same_v<entt::constness_as_t<const int, const char>, const int>);
}
TEST(MemberClass, Functionalities) {
static_assert(std::is_same_v<clazz, entt::member_class_t<decltype(&clazz::foo)>>);
static_assert(std::is_same_v<clazz, entt::member_class_t<decltype(&clazz::bar)>>);
static_assert(std::is_same_v<clazz, entt::member_class_t<decltype(&clazz::quux)>>);
}
TEST(NthArgument, Functionalities) {
static_assert(std::is_same_v<entt::nth_argument_t<0u, &free_function>, int>);
static_assert(std::is_same_v<entt::nth_argument_t<1u, &free_function>, const double &>);
static_assert(std::is_same_v<entt::nth_argument_t<0u, &clazz::bar>, double>);
static_assert(std::is_same_v<entt::nth_argument_t<1u, &clazz::bar>, float>);
static_assert(std::is_same_v<entt::nth_argument_t<0u, &clazz::quux>, bool>);
ASSERT_EQ(free_function(entt::nth_argument_t<0u, &free_function>{}, entt::nth_argument_t<1u, &free_function>{}), 42);
}
TEST(Tag, Functionalities) {
using namespace entt::literals;
static_assert(entt::tag<"foobar"_hs>::value == entt::hashed_string::value("foobar"));
static_assert(std::is_same_v<typename entt::tag<"foobar"_hs>::value_type, entt::id_type>);
}

View File

@ -0,0 +1,61 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_traits.hpp>
#include <entt/core/utility.hpp>
struct functions {
static void foo(int) {}
static void foo() {}
void bar(int) {}
void bar() {}
};
TEST(Identity, Functionalities) {
entt::identity identity;
int value = 42;
ASSERT_TRUE(entt::is_transparent_v<entt::identity>);
ASSERT_EQ(identity(value), value);
ASSERT_EQ(&identity(value), &value);
}
TEST(Overload, Functionalities) {
ASSERT_EQ(entt::overload<void(int)>(&functions::foo), static_cast<void (*)(int)>(&functions::foo));
ASSERT_EQ(entt::overload<void()>(&functions::foo), static_cast<void (*)()>(&functions::foo));
ASSERT_EQ(entt::overload<void(int)>(&functions::bar), static_cast<void (functions::*)(int)>(&functions::bar));
ASSERT_EQ(entt::overload<void()>(&functions::bar), static_cast<void (functions::*)()>(&functions::bar));
functions instance;
ASSERT_NO_FATAL_FAILURE(entt::overload<void(int)>(&functions::foo)(0));
ASSERT_NO_FATAL_FAILURE(entt::overload<void()>(&functions::foo)());
ASSERT_NO_FATAL_FAILURE((instance.*entt::overload<void(int)>(&functions::bar))(0));
ASSERT_NO_FATAL_FAILURE((instance.*entt::overload<void()>(&functions::bar))());
}
TEST(Overloaded, Functionalities) {
int iv = 0;
char cv = '\0';
entt::overloaded func{
[&iv](int value) { iv = value; },
[&cv](char value) { cv = value; }};
func(42);
func('c');
ASSERT_EQ(iv, 42);
ASSERT_EQ(cv, 'c');
}
TEST(YCombinator, Functionalities) {
entt::y_combinator gauss([](const auto &self, auto value) -> unsigned int {
return value ? (value + self(value - 1u)) : 0;
});
ASSERT_EQ(gauss(3u), 3u * 4u / 2u);
ASSERT_EQ(std::as_const(gauss)(7u), 7u * 8u / 2u);
}

View File

@ -0,0 +1,79 @@
#include <gtest/gtest.h>
#include <entt/config/config.h>
#include <entt/entity/component.hpp>
struct empty {};
struct non_empty {
int value;
};
struct non_movable {
non_movable() = default;
non_movable(const non_movable &) = delete;
non_movable &operator=(const non_movable &) = delete;
int value;
};
struct self_contained {
static constexpr auto in_place_delete = true;
static constexpr auto page_size = 4u;
};
struct traits_based {};
template<>
struct entt::component_traits<traits_based> {
using type = traits_based;
static constexpr auto in_place_delete = false;
static constexpr auto page_size = 8u;
};
TEST(Component, VoidType) {
using traits = entt::component_traits<void>;
static_assert(traits::in_place_delete);
static_assert(entt::ignore_as_empty_v<typename traits::type>);
// we don't really care about this thanks to ignore_as_empty_v
static_assert(traits::page_size != 0u);
}
TEST(Component, Empty) {
using traits = entt::component_traits<empty>;
static_assert(!traits::in_place_delete);
static_assert(entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == 0u);
}
TEST(Component, NonEmpty) {
using traits = entt::component_traits<non_empty>;
static_assert(!traits::in_place_delete);
static_assert(!entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == ENTT_PACKED_PAGE);
}
TEST(Component, NonMovable) {
using traits = entt::component_traits<non_movable>;
static_assert(traits::in_place_delete);
static_assert(!entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == ENTT_PACKED_PAGE);
}
TEST(Component, SelfContained) {
using traits = entt::component_traits<self_contained>;
static_assert(traits::in_place_delete);
static_assert(!entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == 4u);
}
TEST(Component, TraitsBased) {
using traits = entt::component_traits<traits_based>;
static_assert(!traits::in_place_delete);
static_assert(!entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == 8u);
}

View File

@ -0,0 +1,92 @@
#include <gtest/gtest.h>
#include <entt/entity/entity.hpp>
#include <entt/entity/registry.hpp>
TEST(Entity, Traits) {
using traits_type = entt::entt_traits<entt::entity>;
constexpr entt::entity tombstone = entt::tombstone;
constexpr entt::entity null = entt::null;
entt::registry registry{};
registry.destroy(registry.create());
const auto entity = registry.create();
const auto other = registry.create();
ASSERT_EQ(entt::to_integral(entity), entt::to_integral(entity));
ASSERT_NE(entt::to_integral(entity), entt::to_integral<entt::entity>(entt::null));
ASSERT_NE(entt::to_integral(entity), entt::to_integral(entt::entity{}));
ASSERT_EQ(entt::to_entity(entity), 0u);
ASSERT_EQ(entt::to_version(entity), 1u);
ASSERT_EQ(entt::to_entity(other), 1u);
ASSERT_EQ(entt::to_version(other), 0u);
ASSERT_EQ(traits_type::construct(entt::to_entity(entity), entt::to_version(entity)), entity);
ASSERT_EQ(traits_type::construct(entt::to_entity(other), entt::to_version(other)), other);
ASSERT_NE(traits_type::construct(entt::to_entity(entity), {}), entity);
ASSERT_EQ(traits_type::construct(entt::to_entity(other), entt::to_version(entity)), traits_type::combine(entt::to_integral(other), entt::to_integral(entity)));
ASSERT_EQ(traits_type::combine(entt::tombstone, entt::null), tombstone);
ASSERT_EQ(traits_type::combine(entt::null, entt::tombstone), null);
}
TEST(Entity, Null) {
using traits_type = entt::entt_traits<entt::entity>;
constexpr entt::entity null = entt::null;
ASSERT_FALSE(entt::entity{} == entt::null);
ASSERT_TRUE(entt::null == entt::null);
ASSERT_FALSE(entt::null != entt::null);
entt::registry registry{};
const auto entity = registry.create();
ASSERT_EQ(traits_type::combine(entt::null, entt::to_integral(entity)), (traits_type::construct(entt::to_entity(null), entt::to_version(entity))));
ASSERT_EQ(traits_type::combine(entt::null, entt::to_integral(null)), null);
ASSERT_EQ(traits_type::combine(entt::null, entt::tombstone), null);
registry.emplace<int>(entity, 42);
ASSERT_FALSE(entity == entt::null);
ASSERT_FALSE(entt::null == entity);
ASSERT_TRUE(entity != entt::null);
ASSERT_TRUE(entt::null != entity);
const entt::entity other = entt::null;
ASSERT_FALSE(registry.valid(other));
ASSERT_NE(registry.create(other), other);
}
TEST(Entity, Tombstone) {
using traits_type = entt::entt_traits<entt::entity>;
constexpr entt::entity tombstone = entt::tombstone;
ASSERT_FALSE(entt::entity{} == entt::tombstone);
ASSERT_TRUE(entt::tombstone == entt::tombstone);
ASSERT_FALSE(entt::tombstone != entt::tombstone);
entt::registry registry{};
const auto entity = registry.create();
ASSERT_EQ(traits_type::combine(entt::to_integral(entity), entt::tombstone), (traits_type::construct(entt::to_entity(entity), entt::to_version(tombstone))));
ASSERT_EQ(traits_type::combine(entt::tombstone, entt::to_integral(tombstone)), tombstone);
ASSERT_EQ(traits_type::combine(entt::tombstone, entt::null), tombstone);
registry.emplace<int>(entity, 42);
ASSERT_FALSE(entity == entt::tombstone);
ASSERT_FALSE(entt::tombstone == entity);
ASSERT_TRUE(entity != entt::tombstone);
ASSERT_TRUE(entt::tombstone != entity);
constexpr auto vers = entt::to_version(tombstone);
const auto other = traits_type::construct(entt::to_entity(entity), vers);
ASSERT_FALSE(registry.valid(entt::tombstone));
ASSERT_NE(registry.destroy(entity, vers), vers);
ASSERT_NE(registry.create(other), other);
}

1468
test/entt/entity/group.cpp Normal file

File diff suppressed because it is too large Load Diff

295
test/entt/entity/handle.cpp Normal file
View File

@ -0,0 +1,295 @@
#include <tuple>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/entity/entity.hpp>
#include <entt/entity/handle.hpp>
#include <entt/entity/registry.hpp>
TEST(BasicHandle, Assumptions) {
static_assert(std::is_trivially_copyable_v<entt::handle>);
static_assert(std::is_trivially_assignable_v<entt::handle, entt::handle>);
static_assert(std::is_trivially_destructible_v<entt::handle>);
static_assert(std::is_trivially_copyable_v<entt::const_handle>);
static_assert(std::is_trivially_assignable_v<entt::const_handle, entt::const_handle>);
static_assert(std::is_trivially_destructible_v<entt::const_handle>);
}
TEST(BasicHandle, DeductionGuide) {
static_assert(std::is_same_v<decltype(entt::basic_handle{std::declval<entt::registry &>(), {}}), entt::basic_handle<entt::registry>>);
static_assert(std::is_same_v<decltype(entt::basic_handle{std::declval<const entt::registry &>(), {}}), entt::basic_handle<const entt::registry>>);
}
TEST(BasicHandle, Construction) {
entt::registry registry;
const auto entity = registry.create();
entt::handle handle{registry, entity};
entt::const_handle chandle{std::as_const(registry), entity};
ASSERT_FALSE(entt::null == handle.entity());
ASSERT_EQ(entity, handle);
ASSERT_TRUE(handle);
ASSERT_FALSE(entt::null == chandle.entity());
ASSERT_EQ(entity, chandle);
ASSERT_TRUE(chandle);
ASSERT_EQ(handle, chandle);
static_assert(std::is_same_v<entt::registry *, decltype(handle.registry())>);
static_assert(std::is_same_v<const entt::registry *, decltype(chandle.registry())>);
}
TEST(BasicHandle, Invalidation) {
entt::handle handle;
ASSERT_FALSE(handle);
ASSERT_EQ(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entt::entity{entt::null});
entt::registry registry;
const auto entity = registry.create();
handle = {registry, entity};
ASSERT_TRUE(handle);
ASSERT_NE(handle.registry(), nullptr);
ASSERT_NE(handle.entity(), entt::entity{entt::null});
handle = {};
ASSERT_FALSE(handle);
ASSERT_EQ(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entt::entity{entt::null});
}
TEST(BasicHandle, Destruction) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
const auto entity = registry.create();
entt::handle handle{registry, entity};
ASSERT_TRUE(handle);
ASSERT_TRUE(handle.valid());
ASSERT_NE(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entity);
handle.destroy(traits_type::to_version(entity));
ASSERT_FALSE(handle);
ASSERT_FALSE(handle.valid());
ASSERT_NE(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entity);
ASSERT_EQ(registry.current(entity), typename entt::registry::version_type{});
handle = entt::handle{registry, registry.create()};
ASSERT_TRUE(handle);
ASSERT_TRUE(handle.valid());
ASSERT_NE(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entity);
handle.destroy();
ASSERT_FALSE(handle);
ASSERT_FALSE(handle.valid());
ASSERT_NE(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entity);
ASSERT_NE(registry.current(entity), typename entt::registry::version_type{});
}
TEST(BasicHandle, Comparison) {
entt::registry registry;
const auto entity = registry.create();
entt::handle handle{registry, entity};
entt::const_handle chandle = handle;
ASSERT_NE(handle, entt::handle{});
ASSERT_FALSE(handle == entt::handle{});
ASSERT_TRUE(handle != entt::handle{});
ASSERT_NE(chandle, entt::const_handle{});
ASSERT_FALSE(chandle == entt::const_handle{});
ASSERT_TRUE(chandle != entt::const_handle{});
ASSERT_EQ(handle, chandle);
ASSERT_TRUE(handle == chandle);
ASSERT_FALSE(handle != chandle);
ASSERT_EQ(entt::handle{}, entt::const_handle{});
ASSERT_TRUE(entt::handle{} == entt::const_handle{});
ASSERT_FALSE(entt::handle{} != entt::const_handle{});
handle = {};
chandle = {};
ASSERT_EQ(handle, entt::handle{});
ASSERT_TRUE(handle == entt::handle{});
ASSERT_FALSE(handle != entt::handle{});
ASSERT_EQ(chandle, entt::const_handle{});
ASSERT_TRUE(chandle == entt::const_handle{});
ASSERT_FALSE(chandle != entt::const_handle{});
entt::registry other;
const auto entt = other.create();
handle = {registry, entity};
chandle = {other, entt};
ASSERT_NE(handle, chandle);
ASSERT_FALSE(chandle == handle);
ASSERT_TRUE(chandle != handle);
ASSERT_EQ(handle.entity(), chandle.entity());
ASSERT_NE(handle.registry(), chandle.registry());
}
TEST(BasicHandle, Component) {
entt::registry registry;
const auto entity = registry.create();
entt::handle_view<int, char, double> handle{registry, entity};
ASSERT_EQ(3, handle.emplace<int>(3));
ASSERT_EQ('c', handle.emplace_or_replace<char>('c'));
ASSERT_EQ(.3, handle.emplace_or_replace<double>(.3));
const auto &patched = handle.patch<int>([](auto &comp) { comp = 42; });
ASSERT_EQ(42, patched);
ASSERT_EQ('a', handle.replace<char>('a'));
ASSERT_TRUE((handle.all_of<int, char, double>()));
ASSERT_EQ((std::make_tuple(42, 'a', .3)), (handle.get<int, char, double>()));
handle.erase<char, double>();
ASSERT_TRUE(registry.storage<char>().empty());
ASSERT_TRUE(registry.storage<double>().empty());
ASSERT_EQ(0u, (handle.remove<char, double>()));
for(auto [id, pool]: handle.storage()) {
ASSERT_EQ(id, entt::type_id<int>().hash());
ASSERT_TRUE(pool.contains(handle.entity()));
}
ASSERT_TRUE((handle.any_of<int, char, double>()));
ASSERT_FALSE((handle.all_of<int, char, double>()));
ASSERT_FALSE(handle.orphan());
ASSERT_EQ(1u, (handle.remove<int>()));
ASSERT_TRUE(registry.storage<int>().empty());
ASSERT_TRUE(handle.orphan());
ASSERT_EQ(42, handle.get_or_emplace<int>(42));
ASSERT_EQ(42, handle.get_or_emplace<int>(1));
ASSERT_EQ(42, handle.get<int>());
ASSERT_EQ(42, *handle.try_get<int>());
ASSERT_EQ(nullptr, handle.try_get<char>());
ASSERT_EQ(nullptr, std::get<1>(handle.try_get<int, char, double>()));
}
TEST(BasicHandle, FromEntity) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity, 42);
registry.emplace<char>(entity, 'c');
entt::handle handle{registry, entity};
ASSERT_TRUE(handle);
ASSERT_EQ(entity, handle.entity());
ASSERT_TRUE((handle.all_of<int, char>()));
ASSERT_EQ(handle.get<int>(), 42);
ASSERT_EQ(handle.get<char>(), 'c');
}
TEST(BasicHandle, Lifetime) {
entt::registry registry;
const auto entity = registry.create();
auto *handle = new entt::handle{registry, entity};
handle->emplace<int>();
ASSERT_FALSE(registry.storage<int>().empty());
ASSERT_FALSE(registry.empty());
registry.each([handle](const auto e) {
ASSERT_EQ(handle->entity(), e);
});
delete handle;
ASSERT_FALSE(registry.storage<int>().empty());
ASSERT_FALSE(registry.empty());
}
TEST(BasicHandle, ImplicitConversions) {
entt::registry registry;
const entt::handle handle{registry, registry.create()};
const entt::const_handle chandle = handle;
const entt::handle_view<int, char> vhandle = handle;
const entt::const_handle_view<int> cvhandle = vhandle;
handle.emplace<int>(42);
ASSERT_EQ(handle.get<int>(), chandle.get<int>());
ASSERT_EQ(chandle.get<int>(), vhandle.get<int>());
ASSERT_EQ(vhandle.get<int>(), cvhandle.get<int>());
ASSERT_EQ(cvhandle.get<int>(), 42);
}
TEST(BasicHandle, Storage) {
entt::registry registry;
const auto entity = registry.create();
entt::handle handle{registry, entity};
entt::const_handle chandle{std::as_const(registry), entity};
static_assert(std::is_same_v<decltype(*handle.storage().begin()), std::pair<entt::id_type, entt::sparse_set &>>);
static_assert(std::is_same_v<decltype(*chandle.storage().begin()), std::pair<entt::id_type, const entt::sparse_set &>>);
ASSERT_EQ(handle.storage().begin(), handle.storage().end());
ASSERT_EQ(chandle.storage().begin(), chandle.storage().end());
registry.storage<double>();
registry.emplace<int>(entity);
ASSERT_NE(handle.storage().begin(), handle.storage().end());
ASSERT_NE(chandle.storage().begin(), chandle.storage().end());
ASSERT_EQ(++handle.storage().begin(), handle.storage().end());
ASSERT_EQ(++chandle.storage().begin(), chandle.storage().end());
ASSERT_EQ(handle.storage().begin()->second.type(), entt::type_id<int>());
ASSERT_EQ(chandle.storage().begin()->second.type(), entt::type_id<int>());
}
TEST(BasicHandle, HandleStorageIterator) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<double>(entity);
auto test = [](auto iterable) {
auto end{iterable.begin()};
decltype(end) begin{};
begin = iterable.end();
std::swap(begin, end);
ASSERT_EQ(begin, iterable.cbegin());
ASSERT_EQ(end, iterable.cend());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(++begin, iterable.end());
};
test(entt::handle{registry, entity}.storage());
test(entt::const_handle{std::as_const(registry), entity}.storage());
}

126
test/entt/entity/helper.cpp Normal file
View File

@ -0,0 +1,126 @@
#include <gtest/gtest.h>
#include <entt/entity/entity.hpp>
#include <entt/entity/helper.hpp>
#include <entt/entity/registry.hpp>
struct clazz {
void func(entt::registry &, entt::entity curr) {
entt = curr;
}
entt::entity entt{entt::null};
};
struct stable_type {
static constexpr auto in_place_delete = true;
int value;
};
TEST(Helper, AsView) {
entt::registry registry;
const entt::registry cregistry;
([](entt::view<entt::get_t<int>>) {})(entt::as_view{registry});
([](entt::view<entt::get_t<char, double>, entt::exclude_t<int>>) {})(entt::as_view{registry});
([](entt::view<entt::get_t<const char, double>, entt::exclude_t<const int>>) {})(entt::as_view{registry});
([](entt::view<entt::get_t<const char, const double>, entt::exclude_t<const int>>) {})(entt::as_view{cregistry});
}
TEST(Helper, AsGroup) {
entt::registry registry;
const entt::registry cregistry;
([](entt::group<entt::owned_t<double>, entt::get_t<char>, entt::exclude_t<int>>) {})(entt::as_group{registry});
([](entt::group<entt::owned_t<double>, entt::get_t<const char>, entt::exclude_t<const int>>) {})(entt::as_group{registry});
([](entt::group<entt::owned_t<const double>, entt::get_t<const char>, entt::exclude_t<const int>>) {})(entt::as_group{cregistry});
}
TEST(Helper, Invoke) {
entt::registry registry;
const auto entity = registry.create();
registry.on_construct<clazz>().connect<entt::invoke<&clazz::func>>();
registry.emplace<clazz>(entity);
ASSERT_EQ(entity, registry.get<clazz>(entity).entt);
}
TEST(Helper, ToEntity) {
entt::registry registry;
const entt::entity null = entt::null;
constexpr auto page_size = entt::component_traits<int>::page_size;
const int value = 42;
ASSERT_EQ(entt::to_entity(registry, 42), null);
ASSERT_EQ(entt::to_entity(registry, value), null);
const auto entity = registry.create();
auto &&storage = registry.storage<int>();
storage.emplace(entity);
while(storage.size() < (page_size - 1u)) {
storage.emplace(registry.create(), value);
}
const auto other = registry.create();
const auto next = registry.create();
registry.emplace<int>(other);
registry.emplace<int>(next);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(other)), other);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(next)), next);
ASSERT_EQ(&registry.get<int>(entity) + page_size - 1u, &registry.get<int>(other));
registry.destroy(other);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<int>(next)), next);
ASSERT_EQ(&registry.get<int>(entity) + page_size - 1u, &registry.get<int>(next));
ASSERT_EQ(entt::to_entity(registry, 42), null);
ASSERT_EQ(entt::to_entity(registry, value), null);
}
TEST(Helper, ToEntityStableType) {
entt::registry registry;
const entt::entity null = entt::null;
constexpr auto page_size = entt::component_traits<stable_type>::page_size;
const stable_type value{42};
ASSERT_EQ(entt::to_entity(registry, stable_type{42}), null);
ASSERT_EQ(entt::to_entity(registry, value), null);
const auto entity = registry.create();
auto &&storage = registry.storage<stable_type>();
registry.emplace<stable_type>(entity);
while(storage.size() < (page_size - 2u)) {
storage.emplace(registry.create(), value);
}
const auto other = registry.create();
const auto next = registry.create();
registry.emplace<stable_type>(other);
registry.emplace<stable_type>(next);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(other)), other);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(next)), next);
ASSERT_EQ(&registry.get<stable_type>(entity) + page_size - 2u, &registry.get<stable_type>(other));
registry.destroy(other);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(entity)), entity);
ASSERT_EQ(entt::to_entity(registry, registry.get<stable_type>(next)), next);
ASSERT_EQ(&registry.get<stable_type>(entity) + page_size - 1u, &registry.get<stable_type>(next));
ASSERT_EQ(entt::to_entity(registry, stable_type{42}), null);
ASSERT_EQ(entt::to_entity(registry, value), null);
}

View File

@ -0,0 +1,371 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/entity/observer.hpp>
#include <entt/entity/registry.hpp>
TEST(Observer, Functionalities) {
entt::registry registry;
entt::observer observer{registry, entt::collector.group<int>()};
ASSERT_EQ(observer.size(), 0u);
ASSERT_TRUE(observer.empty());
ASSERT_EQ(observer.begin(), observer.end());
const auto entity = registry.create();
registry.emplace<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
ASSERT_NE(observer.begin(), observer.end());
ASSERT_EQ(++observer.begin(), observer.end());
ASSERT_EQ(*observer.begin(), entity);
observer.clear();
ASSERT_EQ(observer.size(), 0u);
ASSERT_TRUE(observer.empty());
observer.disconnect();
registry.erase<int>(entity);
registry.emplace<int>(entity);
ASSERT_EQ(observer.size(), 0u);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, AllOf) {
constexpr auto collector =
entt::collector
.group<int, char>(entt::exclude<float>)
.group<int, double>();
entt::registry registry;
entt::observer observer{registry, collector};
const auto entity = registry.create();
ASSERT_TRUE(observer.empty());
registry.emplace<int>(entity);
registry.emplace<char>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
registry.emplace<double>(entity);
ASSERT_FALSE(observer.empty());
registry.erase<int>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace<float>(entity);
registry.emplace<int>(entity);
ASSERT_FALSE(observer.empty());
registry.erase<double>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace<double>(entity);
observer.clear();
ASSERT_TRUE(observer.empty());
observer.disconnect();
registry.emplace_or_replace<int>(entity);
registry.emplace_or_replace<char>(entity);
registry.erase<float>(entity);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, AllOfFiltered) {
constexpr auto collector =
entt::collector
.group<int>()
.where<char>(entt::exclude<double>);
entt::registry registry;
entt::observer observer{registry, collector};
const auto entity = registry.create();
ASSERT_TRUE(observer.empty());
registry.emplace<int>(entity);
ASSERT_EQ(observer.size(), 0u);
ASSERT_TRUE(observer.empty());
registry.erase<int>(entity);
registry.emplace<char>(entity);
registry.emplace<double>(entity);
registry.emplace<int>(entity);
ASSERT_TRUE(observer.empty());
registry.erase<int>(entity);
registry.erase<double>(entity);
registry.emplace<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
registry.emplace<double>(entity);
ASSERT_TRUE(observer.empty());
registry.erase<double>(entity);
ASSERT_TRUE(observer.empty());
observer.disconnect();
registry.erase<int>(entity);
registry.emplace<int>(entity);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, Observe) {
entt::registry registry;
entt::observer observer{registry, entt::collector.update<int>().update<char>()};
const auto entity = registry.create();
ASSERT_TRUE(observer.empty());
registry.emplace<int>(entity);
registry.emplace<char>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace_or_replace<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
observer.clear();
registry.replace<char>(entity);
ASSERT_FALSE(observer.empty());
observer.clear();
ASSERT_TRUE(observer.empty());
observer.disconnect();
registry.emplace_or_replace<int>(entity);
registry.emplace_or_replace<char>(entity);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, ObserveFiltered) {
constexpr auto collector =
entt::collector
.update<int>()
.where<char>(entt::exclude<double>);
entt::registry registry;
entt::observer observer{registry, collector};
const auto entity = registry.create();
ASSERT_TRUE(observer.empty());
registry.emplace<int>(entity);
registry.replace<int>(entity);
ASSERT_EQ(observer.size(), 0u);
ASSERT_TRUE(observer.empty());
registry.emplace<char>(entity);
registry.emplace<double>(entity);
registry.replace<int>(entity);
ASSERT_TRUE(observer.empty());
registry.erase<double>(entity);
registry.replace<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
registry.emplace<double>(entity);
ASSERT_TRUE(observer.empty());
registry.erase<double>(entity);
ASSERT_TRUE(observer.empty());
observer.disconnect();
registry.replace<int>(entity);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, AllOfObserve) {
entt::registry registry;
entt::observer observer{};
const auto entity = registry.create();
observer.connect(registry, entt::collector.group<int>().update<char>());
ASSERT_TRUE(observer.empty());
registry.emplace<int>(entity);
registry.emplace<char>(entity);
registry.replace<char>(entity);
registry.erase<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
registry.erase<char>(entity);
registry.emplace<char>(entity);
ASSERT_TRUE(observer.empty());
registry.replace<char>(entity);
observer.clear();
ASSERT_TRUE(observer.empty());
observer.disconnect();
registry.emplace_or_replace<int>(entity);
registry.emplace_or_replace<char>(entity);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, CrossRulesCornerCase) {
entt::registry registry;
entt::observer observer{registry, entt::collector.group<int>().group<char>()};
const auto entity = registry.create();
registry.emplace<int>(entity);
observer.clear();
ASSERT_TRUE(observer.empty());
registry.emplace<char>(entity);
registry.erase<int>(entity);
ASSERT_FALSE(observer.empty());
}
TEST(Observer, Each) {
entt::registry registry;
entt::observer observer{registry, entt::collector.group<int>()};
const auto entity = registry.create();
registry.emplace<int>(entity);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(observer.size(), 1u);
std::as_const(observer).each([entity](const auto entt) {
ASSERT_EQ(entity, entt);
});
ASSERT_FALSE(observer.empty());
ASSERT_EQ(observer.size(), 1u);
observer.each([entity](const auto entt) {
ASSERT_EQ(entity, entt);
});
ASSERT_TRUE(observer.empty());
ASSERT_EQ(observer.size(), 0u);
}
TEST(Observer, MultipleFilters) {
constexpr auto collector =
entt::collector
.update<int>()
.where<char>()
.update<double>()
.where<float>();
entt::registry registry;
entt::observer observer{registry, collector};
const auto entity = registry.create();
ASSERT_TRUE(observer.empty());
registry.emplace_or_replace<int>(entity);
registry.emplace<char>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace_or_replace<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
observer.clear();
registry.emplace<double>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace_or_replace<double>(entity);
registry.emplace<float>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace_or_replace<double>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
registry.erase<float>(entity);
ASSERT_TRUE(observer.empty());
registry.emplace_or_replace<int>(entity);
ASSERT_EQ(observer.size(), 1u);
ASSERT_FALSE(observer.empty());
ASSERT_EQ(*observer.data(), entity);
observer.clear();
observer.disconnect();
registry.emplace_or_replace<int>(entity);
ASSERT_TRUE(observer.empty());
}
TEST(Observer, GroupCornerCase) {
constexpr auto add_collector = entt::collector.group<int>(entt::exclude<char>);
constexpr auto remove_collector = entt::collector.group<int, char>();
entt::registry registry;
entt::observer add_observer{registry, add_collector};
entt::observer remove_observer{registry, remove_collector};
const auto entity = registry.create();
registry.emplace<int>(entity);
ASSERT_FALSE(add_observer.empty());
ASSERT_TRUE(remove_observer.empty());
add_observer.clear();
registry.emplace<char>(entity);
ASSERT_TRUE(add_observer.empty());
ASSERT_FALSE(remove_observer.empty());
remove_observer.clear();
registry.erase<char>(entity);
ASSERT_FALSE(add_observer.empty());
ASSERT_TRUE(remove_observer.empty());
}

View File

@ -0,0 +1,432 @@
#include <cstddef>
#include <gtest/gtest.h>
#include <entt/entity/organizer.hpp>
#include <entt/entity/registry.hpp>
void ro_int_rw_char_double(entt::view<entt::get_t<const int, char>>, double &) {}
void ro_char_rw_int(entt::view<entt::get_t<int, const char>>) {}
void ro_char_rw_double(entt::view<entt::get_t<const char>>, double &) {}
void ro_int_double(entt::view<entt::get_t<const int>>, const double &) {}
void sync_point(entt::registry &, entt::view<entt::get_t<const int>>) {}
struct clazz {
void ro_int_char_double(entt::view<entt::get_t<const int, const char>>, const double &) {}
void rw_int(entt::view<entt::get_t<int>>) {}
void rw_int_char(entt::view<entt::get_t<int, char>>) {}
void rw_int_char_double(entt::view<entt::get_t<int, char>>, double &) {}
static void ro_int_with_payload(const clazz &, entt::view<entt::get_t<const int>>) {}
static void ro_char_with_payload(const clazz &, entt::view<entt::get_t<const char>>) {}
static void ro_int_char_with_payload(clazz &, entt::view<entt::get_t<const int, const char>>) {}
};
void to_args_integrity(entt::view<entt::get_t<int>> view, std::size_t &value, entt::registry &registry) {
value = view.size();
}
TEST(Organizer, EmplaceFreeFunction) {
entt::organizer organizer;
entt::registry registry;
organizer.emplace<&ro_int_rw_char_double>("t1");
organizer.emplace<&ro_char_rw_int>("t2");
organizer.emplace<&ro_char_rw_double>("t3");
organizer.emplace<&ro_int_double>("t4");
const auto graph = organizer.graph();
ASSERT_EQ(graph.size(), 4u);
ASSERT_STREQ(graph[0u].name(), "t1");
ASSERT_STREQ(graph[1u].name(), "t2");
ASSERT_STREQ(graph[2u].name(), "t3");
ASSERT_STREQ(graph[3u].name(), "t4");
ASSERT_EQ(graph[0u].ro_count(), 1u);
ASSERT_EQ(graph[1u].ro_count(), 1u);
ASSERT_EQ(graph[2u].ro_count(), 1u);
ASSERT_EQ(graph[3u].ro_count(), 2u);
ASSERT_EQ(graph[0u].rw_count(), 2u);
ASSERT_EQ(graph[1u].rw_count(), 1u);
ASSERT_EQ(graph[2u].rw_count(), 1u);
ASSERT_EQ(graph[3u].rw_count(), 0u);
ASSERT_NE(graph[0u].info(), graph[1u].info());
ASSERT_NE(graph[1u].info(), graph[2u].info());
ASSERT_NE(graph[2u].info(), graph[3u].info());
ASSERT_TRUE(graph[0u].top_level());
ASSERT_FALSE(graph[1u].top_level());
ASSERT_FALSE(graph[2u].top_level());
ASSERT_FALSE(graph[3u].top_level());
ASSERT_EQ(graph[0u].children().size(), 2u);
ASSERT_EQ(graph[1u].children().size(), 1u);
ASSERT_EQ(graph[2u].children().size(), 1u);
ASSERT_EQ(graph[3u].children().size(), 0u);
ASSERT_EQ(graph[0u].children()[0u], 1u);
ASSERT_EQ(graph[0u].children()[1u], 2u);
ASSERT_EQ(graph[1u].children()[0u], 3u);
ASSERT_EQ(graph[2u].children()[0u], 3u);
for(auto &&vertex: graph) {
ASSERT_NO_FATAL_FAILURE(vertex.callback()(vertex.data(), registry));
}
organizer.clear();
ASSERT_EQ(organizer.graph().size(), 0u);
}
TEST(Organizer, EmplaceMemberFunction) {
entt::organizer organizer;
entt::registry registry;
clazz instance;
organizer.emplace<&clazz::ro_int_char_double>(instance, "t1");
organizer.emplace<&clazz::rw_int>(instance, "t2");
organizer.emplace<&clazz::rw_int_char>(instance, "t3");
organizer.emplace<&clazz::rw_int_char_double>(instance, "t4");
const auto graph = organizer.graph();
ASSERT_EQ(graph.size(), 4u);
ASSERT_STREQ(graph[0u].name(), "t1");
ASSERT_STREQ(graph[1u].name(), "t2");
ASSERT_STREQ(graph[2u].name(), "t3");
ASSERT_STREQ(graph[3u].name(), "t4");
ASSERT_EQ(graph[0u].ro_count(), 3u);
ASSERT_EQ(graph[1u].ro_count(), 0u);
ASSERT_EQ(graph[2u].ro_count(), 0u);
ASSERT_EQ(graph[3u].ro_count(), 0u);
ASSERT_EQ(graph[0u].rw_count(), 0u);
ASSERT_EQ(graph[1u].rw_count(), 1u);
ASSERT_EQ(graph[2u].rw_count(), 2u);
ASSERT_EQ(graph[3u].rw_count(), 3u);
ASSERT_NE(graph[0u].info(), graph[1u].info());
ASSERT_NE(graph[1u].info(), graph[2u].info());
ASSERT_NE(graph[2u].info(), graph[3u].info());
ASSERT_TRUE(graph[0u].top_level());
ASSERT_FALSE(graph[1u].top_level());
ASSERT_FALSE(graph[2u].top_level());
ASSERT_FALSE(graph[3u].top_level());
ASSERT_EQ(graph[0u].children().size(), 1u);
ASSERT_EQ(graph[1u].children().size(), 1u);
ASSERT_EQ(graph[2u].children().size(), 1u);
ASSERT_EQ(graph[3u].children().size(), 0u);
ASSERT_EQ(graph[0u].children()[0u], 1u);
ASSERT_EQ(graph[1u].children()[0u], 2u);
ASSERT_EQ(graph[2u].children()[0u], 3u);
for(auto &&vertex: graph) {
ASSERT_NO_FATAL_FAILURE(vertex.callback()(vertex.data(), registry));
}
organizer.clear();
ASSERT_EQ(organizer.graph().size(), 0u);
}
TEST(Organizer, EmplaceFreeFunctionWithPayload) {
entt::organizer organizer;
entt::registry registry;
clazz instance;
organizer.emplace<&clazz::ro_int_char_double>(instance, "t1");
organizer.emplace<&clazz::ro_int_with_payload>(instance, "t2");
organizer.emplace<&clazz::ro_char_with_payload, const clazz>(instance, "t3");
organizer.emplace<&clazz::ro_int_char_with_payload, clazz>(instance, "t4");
organizer.emplace<&clazz::rw_int_char>(instance, "t5");
const auto graph = organizer.graph();
ASSERT_EQ(graph.size(), 5u);
ASSERT_STREQ(graph[0u].name(), "t1");
ASSERT_STREQ(graph[1u].name(), "t2");
ASSERT_STREQ(graph[2u].name(), "t3");
ASSERT_STREQ(graph[3u].name(), "t4");
ASSERT_STREQ(graph[4u].name(), "t5");
ASSERT_EQ(graph[0u].ro_count(), 3u);
ASSERT_EQ(graph[1u].ro_count(), 1u);
ASSERT_EQ(graph[2u].ro_count(), 2u);
ASSERT_EQ(graph[3u].ro_count(), 2u);
ASSERT_EQ(graph[4u].ro_count(), 0u);
ASSERT_EQ(graph[0u].rw_count(), 0u);
ASSERT_EQ(graph[1u].rw_count(), 0u);
ASSERT_EQ(graph[2u].rw_count(), 0u);
ASSERT_EQ(graph[3u].rw_count(), 1u);
ASSERT_EQ(graph[4u].rw_count(), 2u);
ASSERT_NE(graph[0u].info(), graph[1u].info());
ASSERT_NE(graph[1u].info(), graph[2u].info());
ASSERT_NE(graph[2u].info(), graph[3u].info());
ASSERT_NE(graph[3u].info(), graph[4u].info());
ASSERT_TRUE(graph[0u].top_level());
ASSERT_TRUE(graph[1u].top_level());
ASSERT_TRUE(graph[2u].top_level());
ASSERT_FALSE(graph[3u].top_level());
ASSERT_FALSE(graph[4u].top_level());
ASSERT_EQ(graph[0u].children().size(), 1u);
ASSERT_EQ(graph[1u].children().size(), 1u);
ASSERT_EQ(graph[2u].children().size(), 1u);
ASSERT_EQ(graph[3u].children().size(), 1u);
ASSERT_EQ(graph[4u].children().size(), 0u);
ASSERT_EQ(graph[0u].children()[0u], 4u);
ASSERT_EQ(graph[1u].children()[0u], 4u);
ASSERT_EQ(graph[2u].children()[0u], 3u);
ASSERT_EQ(graph[3u].children()[0u], 4u);
for(auto &&vertex: graph) {
ASSERT_NO_FATAL_FAILURE(vertex.callback()(vertex.data(), registry));
}
organizer.clear();
ASSERT_EQ(organizer.graph().size(), 0u);
}
TEST(Organizer, EmplaceDirectFunction) {
entt::organizer organizer;
entt::registry registry;
clazz instance;
// no aggressive comdat
auto t1 = +[](const void *, entt::registry &reg) { reg.clear<int>(); };
auto t2 = +[](const void *, entt::registry &reg) { reg.clear<char>(); };
auto t3 = +[](const void *, entt::registry &reg) { reg.clear<double>(); };
auto t4 = +[](const void *, entt::registry &reg) { reg.clear(); };
organizer.emplace<int>(t1, nullptr, "t1");
organizer.emplace<const int>(t2, &instance, "t2");
organizer.emplace<const int, char>(t3, nullptr, "t3");
organizer.emplace<int, char, double>(t4, &instance, "t4");
const auto graph = organizer.graph();
ASSERT_EQ(graph.size(), 4u);
ASSERT_STREQ(graph[0u].name(), "t1");
ASSERT_STREQ(graph[1u].name(), "t2");
ASSERT_STREQ(graph[2u].name(), "t3");
ASSERT_STREQ(graph[3u].name(), "t4");
ASSERT_EQ(graph[0u].ro_count(), 0u);
ASSERT_EQ(graph[1u].ro_count(), 1u);
ASSERT_EQ(graph[2u].ro_count(), 1u);
ASSERT_EQ(graph[3u].ro_count(), 0u);
ASSERT_EQ(graph[0u].rw_count(), 1u);
ASSERT_EQ(graph[1u].rw_count(), 0u);
ASSERT_EQ(graph[2u].rw_count(), 1u);
ASSERT_EQ(graph[3u].rw_count(), 3u);
ASSERT_TRUE(graph[0u].callback() == t1);
ASSERT_TRUE(graph[1u].callback() == t2);
ASSERT_TRUE(graph[2u].callback() == t3);
ASSERT_TRUE(graph[3u].callback() == t4);
ASSERT_EQ(graph[0u].data(), nullptr);
ASSERT_EQ(graph[1u].data(), &instance);
ASSERT_EQ(graph[2u].data(), nullptr);
ASSERT_EQ(graph[3u].data(), &instance);
ASSERT_EQ(graph[0u].info(), entt::type_id<void>());
ASSERT_EQ(graph[1u].info(), entt::type_id<void>());
ASSERT_EQ(graph[2u].info(), entt::type_id<void>());
ASSERT_EQ(graph[3u].info(), entt::type_id<void>());
ASSERT_TRUE(graph[0u].top_level());
ASSERT_FALSE(graph[1u].top_level());
ASSERT_FALSE(graph[2u].top_level());
ASSERT_FALSE(graph[3u].top_level());
ASSERT_EQ(graph[0u].children().size(), 1u);
ASSERT_EQ(graph[1u].children().size(), 1u);
ASSERT_EQ(graph[2u].children().size(), 1u);
ASSERT_EQ(graph[3u].children().size(), 0u);
ASSERT_EQ(graph[0u].children()[0u], 1u);
ASSERT_EQ(graph[1u].children()[0u], 2u);
ASSERT_EQ(graph[2u].children()[0u], 3u);
for(auto &&vertex: graph) {
ASSERT_NO_FATAL_FAILURE(vertex.callback()(vertex.data(), registry));
}
organizer.clear();
ASSERT_EQ(organizer.graph().size(), 0u);
}
TEST(Organizer, SyncPoint) {
entt::organizer organizer;
entt::registry registry;
clazz instance;
organizer.emplace<&ro_int_double>("before");
organizer.emplace<&sync_point>("sync_1");
organizer.emplace<&clazz::ro_int_char_double>(instance, "mid_1");
organizer.emplace<&ro_int_double>("mid_2");
organizer.emplace<&sync_point>("sync_2");
organizer.emplace<&ro_int_double>("after");
const auto graph = organizer.graph();
ASSERT_EQ(graph.size(), 6u);
ASSERT_STREQ(graph[0u].name(), "before");
ASSERT_STREQ(graph[1u].name(), "sync_1");
ASSERT_STREQ(graph[2u].name(), "mid_1");
ASSERT_STREQ(graph[3u].name(), "mid_2");
ASSERT_STREQ(graph[4u].name(), "sync_2");
ASSERT_STREQ(graph[5u].name(), "after");
ASSERT_TRUE(graph[0u].top_level());
ASSERT_FALSE(graph[1u].top_level());
ASSERT_FALSE(graph[2u].top_level());
ASSERT_FALSE(graph[3u].top_level());
ASSERT_FALSE(graph[4u].top_level());
ASSERT_FALSE(graph[5u].top_level());
ASSERT_EQ(graph[0u].children().size(), 1u);
ASSERT_EQ(graph[1u].children().size(), 2u);
ASSERT_EQ(graph[2u].children().size(), 1u);
ASSERT_EQ(graph[3u].children().size(), 1u);
ASSERT_EQ(graph[4u].children().size(), 1u);
ASSERT_EQ(graph[5u].children().size(), 0u);
ASSERT_EQ(graph[0u].children()[0u], 1u);
ASSERT_EQ(graph[1u].children()[0u], 2u);
ASSERT_EQ(graph[1u].children()[1u], 3u);
ASSERT_EQ(graph[2u].children()[0u], 4u);
ASSERT_EQ(graph[3u].children()[0u], 4u);
ASSERT_EQ(graph[4u].children()[0u], 5u);
for(auto &&vertex: graph) {
ASSERT_NO_FATAL_FAILURE(vertex.callback()(vertex.data(), registry));
}
}
TEST(Organizer, Override) {
entt::organizer organizer;
organizer.emplace<&ro_int_rw_char_double, const char, const double>("t1");
organizer.emplace<&ro_char_rw_double, const double>("t2");
organizer.emplace<&ro_int_double, double>("t3");
const auto graph = organizer.graph();
ASSERT_EQ(graph.size(), 3u);
ASSERT_STREQ(graph[0u].name(), "t1");
ASSERT_STREQ(graph[1u].name(), "t2");
ASSERT_STREQ(graph[2u].name(), "t3");
ASSERT_TRUE(graph[0u].top_level());
ASSERT_TRUE(graph[1u].top_level());
ASSERT_FALSE(graph[2u].top_level());
ASSERT_EQ(graph[0u].children().size(), 1u);
ASSERT_EQ(graph[1u].children().size(), 1u);
ASSERT_EQ(graph[2u].children().size(), 0u);
ASSERT_EQ(graph[0u].children()[0u], 2u);
ASSERT_EQ(graph[1u].children()[0u], 2u);
}
TEST(Organizer, Prepare) {
entt::organizer organizer;
entt::registry registry;
clazz instance;
organizer.emplace<&ro_int_double>();
organizer.emplace<&clazz::rw_int_char>(instance);
const auto graph = organizer.graph();
ASSERT_FALSE(registry.ctx().contains<int>());
ASSERT_FALSE(registry.ctx().contains<char>());
ASSERT_FALSE(registry.ctx().contains<double>());
for(auto &&vertex: graph) {
vertex.prepare(registry);
}
ASSERT_FALSE(registry.ctx().contains<int>());
ASSERT_FALSE(registry.ctx().contains<char>());
ASSERT_TRUE(registry.ctx().contains<double>());
}
TEST(Organizer, Dependencies) {
entt::organizer organizer;
clazz instance;
organizer.emplace<&ro_int_double>();
organizer.emplace<&clazz::rw_int_char>(instance);
organizer.emplace<char, const double>(+[](const void *, entt::registry &) {});
const auto graph = organizer.graph();
const entt::type_info *buffer[5u]{};
ASSERT_EQ(graph.size(), 3u);
ASSERT_EQ(graph[0u].ro_count(), 2u);
ASSERT_EQ(graph[0u].rw_count(), 0u);
ASSERT_EQ(graph[0u].ro_dependency(buffer, 0u), 0u);
ASSERT_EQ(graph[0u].rw_dependency(buffer, 2u), 0u);
ASSERT_EQ(graph[0u].ro_dependency(buffer, 5u), 2u);
ASSERT_EQ(*buffer[0u], entt::type_id<int>());
ASSERT_EQ(*buffer[1u], entt::type_id<double>());
ASSERT_EQ(graph[1u].ro_count(), 0u);
ASSERT_EQ(graph[1u].rw_count(), 2u);
ASSERT_EQ(graph[1u].ro_dependency(buffer, 2u), 0u);
ASSERT_EQ(graph[1u].rw_dependency(buffer, 0u), 0u);
ASSERT_EQ(graph[1u].rw_dependency(buffer, 5u), 2u);
ASSERT_EQ(*buffer[0u], entt::type_id<int>());
ASSERT_EQ(*buffer[1u], entt::type_id<char>());
ASSERT_EQ(graph[2u].ro_count(), 1u);
ASSERT_EQ(graph[2u].rw_count(), 1u);
ASSERT_EQ(graph[2u].ro_dependency(buffer, 2u), 1u);
ASSERT_EQ(graph[2u].rw_dependency(buffer, 0u), 0u);
ASSERT_EQ(graph[2u].ro_dependency(buffer, 5u), 1u);
ASSERT_EQ(*buffer[0u], entt::type_id<double>());
ASSERT_EQ(graph[2u].rw_dependency(buffer, 5u), 1u);
ASSERT_EQ(*buffer[0u], entt::type_id<char>());
}
TEST(Organizer, ToArgsIntegrity) {
entt::organizer organizer;
entt::registry registry;
organizer.emplace<&to_args_integrity>();
registry.ctx().emplace<std::size_t>(42u);
auto graph = organizer.graph();
graph[0u].callback()(graph[0u].data(), registry);
ASSERT_EQ(registry.ctx().at<std::size_t>(), 0u);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,409 @@
#include <algorithm>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/entity/entity.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/runtime_view.hpp>
struct stable_type {
static constexpr auto in_place_delete = true;
int value;
};
template<typename Type>
struct RuntimeView: testing::Test {
using type = Type;
};
using RuntimeViewTypes = ::testing::Types<entt::runtime_view, entt::const_runtime_view>;
TYPED_TEST_SUITE(RuntimeView, RuntimeViewTypes, );
TYPED_TEST(RuntimeView, Functionalities) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto e0 = registry.create();
const auto e1 = registry.create();
ASSERT_EQ(view.size_hint(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_FALSE(view.contains(e0));
ASSERT_FALSE(view.contains(e1));
// forces the creation of the pools
static_cast<void>(registry.storage<int>());
static_cast<void>(registry.storage<char>());
view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
ASSERT_EQ(view.size_hint(), 0u);
registry.emplace<char>(e0);
registry.emplace<int>(e1);
ASSERT_NE(view.size_hint(), 0u);
registry.emplace<char>(e1);
ASSERT_EQ(view.size_hint(), 1u);
auto it = view.begin();
ASSERT_EQ(*it, e1);
ASSERT_EQ(++it, (view.end()));
ASSERT_NO_FATAL_FAILURE((view.begin()++));
ASSERT_NO_FATAL_FAILURE((++view.begin()));
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.size_hint(), 1u);
registry.get<char>(e0) = '1';
registry.get<char>(e1) = '2';
registry.get<int>(e1) = 42;
for(auto entity: view) {
ASSERT_EQ(registry.get<int>(entity), 42);
ASSERT_EQ(registry.get<char>(entity), '2');
}
view.clear();
ASSERT_EQ(view.size_hint(), 0u);
ASSERT_EQ(view.begin(), view.end());
}
TYPED_TEST(RuntimeView, Constructors) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
registry.emplace<int>(entity);
view = runtime_view_type{std::allocator<int>{}};
view.iterate(registry.storage<int>());
ASSERT_TRUE(view.contains(entity));
runtime_view_type temp{view, view.get_allocator()};
runtime_view_type other{std::move(temp), view.get_allocator()};
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(temp.contains(entity));
ASSERT_TRUE(other.contains(entity));
}
TYPED_TEST(RuntimeView, Copy) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
view.iterate(registry.storage<int>());
ASSERT_TRUE(view.contains(entity));
runtime_view_type other{view};
ASSERT_TRUE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
other.iterate(registry.storage<int>()).exclude(registry.storage<char>());
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(other.contains(entity));
other = view;
ASSERT_TRUE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
}
TYPED_TEST(RuntimeView, Move) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
view.iterate(registry.storage<int>());
ASSERT_TRUE(view.contains(entity));
runtime_view_type other{std::move(view)};
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
view = other;
other.iterate(registry.storage<int>()).exclude(registry.storage<char>());
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(other.contains(entity));
other = std::move(view);
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
}
TYPED_TEST(RuntimeView, Swap) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
runtime_view_type other{};
const auto entity = registry.create();
registry.emplace<int>(entity);
view.iterate(registry.storage<int>());
ASSERT_EQ(view.size_hint(), 1u);
ASSERT_EQ(other.size_hint(), 0u);
ASSERT_TRUE(view.contains(entity));
ASSERT_FALSE(other.contains(entity));
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(other.begin(), other.end());
view.swap(other);
ASSERT_EQ(view.size_hint(), 0u);
ASSERT_EQ(other.size_hint(), 1u);
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(other.contains(entity));
ASSERT_EQ(view.begin(), view.end());
ASSERT_NE(other.begin(), other.end());
}
TYPED_TEST(RuntimeView, Iterator) {
using runtime_view_type = typename TestFixture::type;
using iterator = typename runtime_view_type::iterator;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
registry.emplace<int>(entity);
view.iterate(registry.storage<int>());
iterator end{view.begin()};
iterator begin{};
begin = view.end();
std::swap(begin, end);
ASSERT_EQ(begin, view.begin());
ASSERT_EQ(end, view.end());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, view.begin());
ASSERT_EQ(begin--, view.end());
ASSERT_EQ(++begin, view.end());
ASSERT_EQ(--begin, view.begin());
ASSERT_EQ(*begin, entity);
ASSERT_EQ(*begin.operator->(), entity);
}
TYPED_TEST(RuntimeView, Contains) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
const auto other = registry.create();
registry.emplace<int>(entity);
registry.emplace<int>(other);
registry.destroy(entity);
view.iterate(registry.storage<int>());
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(view.contains(other));
}
TYPED_TEST(RuntimeView, Empty) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
const auto other = registry.create();
registry.emplace<double>(entity);
registry.emplace<float>(other);
view.iterate(registry.storage<int>());
ASSERT_FALSE(view.contains(entity));
ASSERT_FALSE(view.contains(other));
ASSERT_EQ(view.begin(), view.end());
ASSERT_EQ((std::find(view.begin(), view.end(), entity)), view.end());
ASSERT_EQ((std::find(view.begin(), view.end(), other)), view.end());
}
TYPED_TEST(RuntimeView, Each) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
const auto other = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
registry.emplace<char>(other);
view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
view.each([entity](const auto entt) {
ASSERT_EQ(entt, entity);
});
}
TYPED_TEST(RuntimeView, EachWithHoles) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto e0 = registry.create();
const auto e1 = registry.create();
const auto e2 = registry.create();
registry.emplace<char>(e0, '0');
registry.emplace<char>(e1, '1');
registry.emplace<int>(e0, 0);
registry.emplace<int>(e2, 2);
view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
view.each([e0](auto entt) {
ASSERT_EQ(e0, entt);
});
}
TYPED_TEST(RuntimeView, ExcludedComponents) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto e0 = registry.create();
registry.emplace<int>(e0);
const auto e1 = registry.create();
registry.emplace<int>(e1);
registry.emplace<char>(e1);
view.iterate(registry.storage<int>())
.exclude(registry.storage<char>());
ASSERT_TRUE(view.contains(e0));
ASSERT_FALSE(view.contains(e1));
view.each([e0](auto entt) {
ASSERT_EQ(e0, entt);
});
}
TYPED_TEST(RuntimeView, StableType) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto e0 = registry.create();
const auto e1 = registry.create();
const auto e2 = registry.create();
registry.emplace<int>(e0);
registry.emplace<int>(e1);
registry.emplace<int>(e2);
registry.emplace<stable_type>(e0);
registry.emplace<stable_type>(e1);
registry.remove<stable_type>(e1);
view.iterate(registry.storage<int>()).iterate(registry.storage<stable_type>());
ASSERT_EQ(view.size_hint(), 2u);
ASSERT_TRUE(view.contains(e0));
ASSERT_FALSE(view.contains(e1));
ASSERT_EQ(*view.begin(), e0);
ASSERT_EQ(++view.begin(), view.end());
view.each([e0](const auto entt) {
ASSERT_EQ(e0, entt);
});
for(auto entt: view) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
ASSERT_EQ(e0, entt);
}
registry.compact();
ASSERT_EQ(view.size_hint(), 1u);
}
TYPED_TEST(RuntimeView, StableTypeWithExcludedComponent) {
using runtime_view_type = typename TestFixture::type;
entt::registry registry;
runtime_view_type view{};
const auto entity = registry.create();
const auto other = registry.create();
registry.emplace<stable_type>(entity, 0);
registry.emplace<stable_type>(other, 42);
registry.emplace<int>(entity);
view.iterate(registry.storage<stable_type>()).exclude(registry.storage<int>());
ASSERT_EQ(view.size_hint(), 2u);
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(view.contains(other));
registry.destroy(entity);
ASSERT_EQ(view.size_hint(), 2u);
ASSERT_FALSE(view.contains(entity));
ASSERT_TRUE(view.contains(other));
for(auto entt: view) {
constexpr entt::entity tombstone = entt::tombstone;
ASSERT_NE(entt, tombstone);
ASSERT_EQ(entt, other);
}
view.each([other](const auto entt) {
constexpr entt::entity tombstone = entt::tombstone;
ASSERT_NE(entt, tombstone);
ASSERT_EQ(entt, other);
});
}

View File

@ -0,0 +1,593 @@
#include <cstddef>
#include <map>
#include <queue>
#include <tuple>
#include <type_traits>
#include <vector>
#include <gtest/gtest.h>
#include <entt/entity/entity.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/snapshot.hpp>
struct noncopyable_component {
noncopyable_component()
: value{} {}
explicit noncopyable_component(int v)
: value{v} {}
noncopyable_component(const noncopyable_component &) = delete;
noncopyable_component(noncopyable_component &&) = default;
noncopyable_component &operator=(const noncopyable_component &) = delete;
noncopyable_component &operator=(noncopyable_component &&) = default;
int value;
};
template<typename Storage>
struct output_archive {
output_archive(Storage &instance)
: storage{instance} {}
template<typename... Value>
void operator()(const Value &...value) {
(std::get<std::queue<Value>>(storage).push(value), ...);
}
void operator()(const entt::entity &entity, const noncopyable_component &instance) {
(*this)(entity, instance.value);
}
private:
Storage &storage;
};
template<typename Storage>
struct input_archive {
input_archive(Storage &instance)
: storage{instance} {}
template<typename... Value>
void operator()(Value &...value) {
auto assign = [this](auto &val) {
auto &queue = std::get<std::queue<std::decay_t<decltype(val)>>>(storage);
val = queue.front();
queue.pop();
};
(assign(value), ...);
}
void operator()(entt::entity &entity, noncopyable_component &instance) {
(*this)(entity, instance.value);
}
private:
Storage &storage;
};
struct a_component {};
struct another_component {
int key;
int value;
};
struct what_a_component {
entt::entity bar;
std::vector<entt::entity> quux;
};
struct map_component {
std::map<entt::entity, int> keys;
std::map<int, entt::entity> values;
std::map<entt::entity, entt::entity> both;
};
TEST(Snapshot, Dump) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
const auto e0 = registry.create();
registry.emplace<int>(e0, 42);
registry.emplace<char>(e0, 'c');
registry.emplace<double>(e0, .1);
const auto e1 = registry.create();
const auto e2 = registry.create();
registry.emplace<int>(e2, 3);
const auto e3 = registry.create();
registry.emplace<a_component>(e3);
registry.emplace<char>(e3, '0');
registry.destroy(e1);
auto v1 = registry.current(e1);
using storage_type = std::tuple<
std::queue<typename traits_type::entity_type>,
std::queue<entt::entity>,
std::queue<int>,
std::queue<char>,
std::queue<double>,
std::queue<a_component>,
std::queue<another_component>>;
storage_type storage;
output_archive<storage_type> output{storage};
input_archive<storage_type> input{storage};
entt::snapshot{registry}.entities(output).component<int, char, double, a_component, another_component>(output);
registry.clear();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
ASSERT_FALSE(registry.valid(e2));
ASSERT_FALSE(registry.valid(e3));
entt::snapshot_loader{registry}.entities(input).component<int, char, double, a_component, another_component>(input).orphans();
ASSERT_TRUE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
ASSERT_TRUE(registry.valid(e2));
ASSERT_TRUE(registry.valid(e3));
ASSERT_FALSE(registry.orphan(e0));
ASSERT_FALSE(registry.orphan(e2));
ASSERT_FALSE(registry.orphan(e3));
ASSERT_EQ(registry.get<int>(e0), 42);
ASSERT_EQ(registry.get<char>(e0), 'c');
ASSERT_EQ(registry.get<double>(e0), .1);
ASSERT_EQ(registry.current(e1), v1);
ASSERT_EQ(registry.get<int>(e2), 3);
ASSERT_EQ(registry.get<char>(e3), '0');
ASSERT_TRUE(registry.all_of<a_component>(e3));
ASSERT_TRUE(registry.storage<another_component>().empty());
}
TEST(Snapshot, Partial) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
const auto e0 = registry.create();
registry.emplace<int>(e0, 42);
registry.emplace<char>(e0, 'c');
registry.emplace<double>(e0, .1);
const auto e1 = registry.create();
const auto e2 = registry.create();
registry.emplace<int>(e2, 3);
const auto e3 = registry.create();
registry.emplace<char>(e3, '0');
registry.destroy(e1);
auto v1 = registry.current(e1);
using storage_type = std::tuple<
std::queue<typename traits_type::entity_type>,
std::queue<entt::entity>,
std::queue<int>,
std::queue<char>,
std::queue<double>>;
storage_type storage;
output_archive<storage_type> output{storage};
input_archive<storage_type> input{storage};
entt::snapshot{registry}.entities(output).component<char, int>(output);
registry.clear();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
ASSERT_FALSE(registry.valid(e2));
ASSERT_FALSE(registry.valid(e3));
entt::snapshot_loader{registry}.entities(input).component<char, int>(input);
ASSERT_TRUE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
ASSERT_TRUE(registry.valid(e2));
ASSERT_TRUE(registry.valid(e3));
ASSERT_EQ(registry.get<int>(e0), 42);
ASSERT_EQ(registry.get<char>(e0), 'c');
ASSERT_FALSE(registry.all_of<double>(e0));
ASSERT_EQ(registry.current(e1), v1);
ASSERT_EQ(registry.get<int>(e2), 3);
ASSERT_EQ(registry.get<char>(e3), '0');
entt::snapshot{registry}.entities(output);
registry.clear();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
ASSERT_FALSE(registry.valid(e2));
ASSERT_FALSE(registry.valid(e3));
entt::snapshot_loader{registry}.entities(input).orphans();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
ASSERT_FALSE(registry.valid(e2));
ASSERT_FALSE(registry.valid(e3));
}
TEST(Snapshot, Iterator) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
for(auto i = 0; i < 50; ++i) {
const auto entity = registry.create();
registry.emplace<another_component>(entity, i, i);
registry.emplace<noncopyable_component>(entity, i);
if(i % 2) {
registry.emplace<a_component>(entity);
}
}
using storage_type = std::tuple<
std::queue<typename traits_type::entity_type>,
std::queue<entt::entity>,
std::queue<another_component>,
std::queue<int>>;
storage_type storage;
output_archive<storage_type> output{storage};
input_archive<storage_type> input{storage};
const auto view = registry.view<a_component>();
const auto size = view.size();
entt::snapshot{registry}.component<another_component, noncopyable_component>(output, view.begin(), view.end());
registry.clear();
entt::snapshot_loader{registry}.component<another_component, noncopyable_component>(input);
ASSERT_EQ(registry.view<another_component>().size(), size);
registry.view<another_component>().each([](const auto entity, const auto &) {
ASSERT_NE(entt::to_integral(entity) % 2u, 0u);
});
}
TEST(Snapshot, Continuous) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry src;
entt::registry dst;
entt::continuous_loader loader{dst};
std::vector<entt::entity> entities;
entt::entity entity;
using storage_type = std::tuple<
std::queue<typename traits_type::entity_type>,
std::queue<entt::entity>,
std::queue<another_component>,
std::queue<what_a_component>,
std::queue<map_component>,
std::queue<int>,
std::queue<double>>;
storage_type storage;
output_archive<storage_type> output{storage};
input_archive<storage_type> input{storage};
for(int i = 0; i < 10; ++i) {
static_cast<void>(src.create());
}
src.clear();
for(int i = 0; i < 5; ++i) {
entity = src.create();
entities.push_back(entity);
src.emplace<a_component>(entity);
src.emplace<another_component>(entity, i, i);
src.emplace<noncopyable_component>(entity, i);
if(i % 2) {
src.emplace<what_a_component>(entity, entity);
} else {
src.emplace<map_component>(entity);
}
}
src.view<what_a_component>().each([&entities](auto, auto &what_a_component) {
what_a_component.quux.insert(what_a_component.quux.begin(), entities.begin(), entities.end());
});
src.view<map_component>().each([&entities](auto, auto &map_component) {
for(std::size_t i = 0; i < entities.size(); ++i) {
map_component.keys.insert({entities[i], int(i)});
map_component.values.insert({int(i), entities[i]});
map_component.both.insert({entities[entities.size() - i - 1], entities[i]});
}
});
entity = dst.create();
dst.emplace<a_component>(entity);
dst.emplace<another_component>(entity, -1, -1);
dst.emplace<noncopyable_component>(entity, -1);
entt::snapshot{src}.entities(output).component<a_component, another_component, what_a_component, map_component, noncopyable_component>(output);
loader.entities(input)
.component<a_component, another_component, what_a_component, map_component, noncopyable_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both)
.orphans();
decltype(dst.size()) a_component_cnt{};
decltype(dst.size()) another_component_cnt{};
decltype(dst.size()) what_a_component_cnt{};
decltype(dst.size()) map_component_cnt{};
decltype(dst.size()) noncopyable_component_cnt{};
dst.each([&dst, &a_component_cnt](auto entt) {
ASSERT_TRUE(dst.all_of<a_component>(entt));
++a_component_cnt;
});
dst.view<another_component>().each([&another_component_cnt](auto, const auto &component) {
ASSERT_EQ(component.value, component.key < 0 ? -1 : component.key);
++another_component_cnt;
});
dst.view<what_a_component>().each([&dst, &what_a_component_cnt](auto entt, const auto &component) {
ASSERT_EQ(entt, component.bar);
for(auto child: component.quux) {
ASSERT_TRUE(dst.valid(child));
}
++what_a_component_cnt;
});
dst.view<map_component>().each([&dst, &map_component_cnt](const auto &component) {
for(auto child: component.keys) {
ASSERT_TRUE(dst.valid(child.first));
}
for(auto child: component.values) {
ASSERT_TRUE(dst.valid(child.second));
}
for(auto child: component.both) {
ASSERT_TRUE(dst.valid(child.first));
ASSERT_TRUE(dst.valid(child.second));
}
++map_component_cnt;
});
dst.view<noncopyable_component>().each([&dst, &noncopyable_component_cnt](auto, const auto &component) {
++noncopyable_component_cnt;
ASSERT_EQ(component.value, static_cast<int>(dst.storage<noncopyable_component>().size() - noncopyable_component_cnt - 1u));
});
src.view<another_component>().each([](auto, auto &component) {
component.value = 2 * component.key;
});
auto size = dst.size();
entt::snapshot{src}.entities(output).component<a_component, what_a_component, map_component, another_component>(output);
loader.entities(input)
.component<a_component, what_a_component, map_component, another_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both)
.orphans();
ASSERT_EQ(size, dst.size());
ASSERT_EQ(dst.storage<a_component>().size(), a_component_cnt);
ASSERT_EQ(dst.storage<another_component>().size(), another_component_cnt);
ASSERT_EQ(dst.storage<what_a_component>().size(), what_a_component_cnt);
ASSERT_EQ(dst.storage<map_component>().size(), map_component_cnt);
ASSERT_EQ(dst.storage<noncopyable_component>().size(), noncopyable_component_cnt);
dst.view<another_component>().each([](auto, auto &component) {
ASSERT_EQ(component.value, component.key < 0 ? -1 : (2 * component.key));
});
entity = src.create();
src.view<what_a_component>().each([entity](auto, auto &component) {
component.bar = entity;
});
entt::snapshot{src}.entities(output).component<what_a_component, map_component, a_component, another_component>(output);
loader.entities(input)
.component<what_a_component, map_component, a_component, another_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both)
.orphans();
dst.view<what_a_component>().each([&loader, entity](auto, auto &component) {
ASSERT_EQ(component.bar, loader.map(entity));
});
entities.clear();
for(auto entt: src.view<a_component>()) {
entities.push_back(entt);
}
src.destroy(entity);
loader.shrink();
entt::snapshot{src}.entities(output).component<a_component, another_component, what_a_component, map_component>(output);
loader.entities(input)
.component<a_component, another_component, what_a_component, map_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both)
.orphans()
.shrink();
dst.view<what_a_component>().each([&dst](auto, auto &component) {
ASSERT_FALSE(dst.valid(component.bar));
});
ASSERT_FALSE(loader.contains(entity));
entity = src.create();
src.view<what_a_component>().each([entity](auto, auto &component) {
component.bar = entity;
});
dst.clear<a_component>();
a_component_cnt = src.storage<a_component>().size();
entt::snapshot{src}.entities(output).component<a_component, what_a_component, map_component, another_component>(output);
loader.entities(input)
.component<a_component, what_a_component, map_component, another_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both)
.orphans();
ASSERT_EQ(dst.storage<a_component>().size(), a_component_cnt);
src.clear<a_component>();
a_component_cnt = {};
entt::snapshot{src}.entities(output).component<what_a_component, map_component, a_component, another_component>(output);
loader.entities(input)
.component<what_a_component, map_component, a_component, another_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both)
.orphans();
ASSERT_EQ(dst.storage<a_component>().size(), a_component_cnt);
}
TEST(Snapshot, MoreOnShrink) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry src;
entt::registry dst;
entt::continuous_loader loader{dst};
using storage_type = std::tuple<
std::queue<typename traits_type::entity_type>,
std::queue<entt::entity>>;
storage_type storage;
output_archive<storage_type> output{storage};
input_archive<storage_type> input{storage};
auto entity = src.create();
entt::snapshot{src}.entities(output);
loader.entities(input).shrink();
ASSERT_TRUE(dst.valid(entity));
loader.shrink();
ASSERT_FALSE(dst.valid(entity));
}
TEST(Snapshot, SyncDataMembers) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry src;
entt::registry dst;
entt::continuous_loader loader{dst};
using storage_type = std::tuple<
std::queue<typename traits_type::entity_type>,
std::queue<entt::entity>,
std::queue<what_a_component>,
std::queue<map_component>>;
storage_type storage;
output_archive<storage_type> output{storage};
input_archive<storage_type> input{storage};
static_cast<void>(src.create());
static_cast<void>(src.create());
src.clear();
auto parent = src.create();
auto child = src.create();
src.emplace<what_a_component>(parent, entt::null);
src.emplace<what_a_component>(child, parent).quux.push_back(child);
src.emplace<map_component>(
child,
decltype(map_component::keys){{{child, 10}}},
decltype(map_component::values){{{10, child}}},
decltype(map_component::both){{{child, child}}});
entt::snapshot{src}.entities(output).component<what_a_component, map_component>(output);
loader.entities(input).component<what_a_component, map_component>(
input,
&what_a_component::bar,
&what_a_component::quux,
&map_component::keys,
&map_component::values,
&map_component::both);
ASSERT_FALSE(dst.valid(parent));
ASSERT_FALSE(dst.valid(child));
ASSERT_TRUE(dst.all_of<what_a_component>(loader.map(parent)));
ASSERT_TRUE(dst.all_of<what_a_component>(loader.map(child)));
ASSERT_EQ(dst.get<what_a_component>(loader.map(parent)).bar, static_cast<entt::entity>(entt::null));
const auto &component = dst.get<what_a_component>(loader.map(child));
ASSERT_EQ(component.bar, loader.map(parent));
ASSERT_EQ(component.quux[0], loader.map(child));
const auto &foobar = dst.get<map_component>(loader.map(child));
ASSERT_EQ(foobar.keys.at(loader.map(child)), 10);
ASSERT_EQ(foobar.values.at(10), loader.map(child));
ASSERT_EQ(foobar.both.at(loader.map(child)), loader.map(child));
}

File diff suppressed because it is too large Load Diff

1933
test/entt/entity/storage.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,613 @@
#include <iterator>
#include <gtest/gtest.h>
#include <entt/entity/registry.hpp>
#include <entt/entity/storage.hpp>
#include "../common/throwing_allocator.hpp"
#include "../common/throwing_type.hpp"
struct empty_type {};
struct stable_type {
static constexpr auto in_place_delete = true;
int value{};
};
struct non_default_constructible {
non_default_constructible() = delete;
non_default_constructible(int v)
: value{v} {}
int value{};
};
struct counter {
int value{};
};
template<typename Registry>
void listener(counter &counter, Registry &, typename Registry::entity_type) {
++counter.value;
}
TEST(SighStorageMixin, GenericType) {
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
entt::sigh_storage_mixin<entt::storage<int>> pool;
entt::sparse_set &base = pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_NE(base.emplace(entities[0u]), base.end());
pool.emplace(entities[1u]);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 0);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.get(entities[0u]), 0);
ASSERT_EQ(pool.get(entities[1u]), 0);
base.erase(entities[0u]);
pool.erase(entities[1u]);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 2);
ASSERT_TRUE(pool.empty());
ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end());
ASSERT_EQ(pool.get(entities[0u]), 0);
ASSERT_EQ(pool.get(entities[1u]), 0);
ASSERT_FALSE(pool.empty());
base.erase(entities[1u]);
ASSERT_EQ(on_construct.value, 4);
ASSERT_EQ(on_destroy.value, 3);
ASSERT_FALSE(pool.empty());
base.erase(entities[0u]);
ASSERT_EQ(on_construct.value, 4);
ASSERT_EQ(on_destroy.value, 4);
ASSERT_TRUE(pool.empty());
pool.insert(std::begin(entities), std::end(entities), 3);
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 4);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.get(entities[0u]), 3);
ASSERT_EQ(pool.get(entities[1u]), 3);
pool.erase(std::begin(entities), std::end(entities));
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 6);
ASSERT_TRUE(pool.empty());
}
TEST(SighStorageMixin, StableType) {
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
entt::sigh_storage_mixin<entt::storage<stable_type>> pool;
entt::sparse_set &base = pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_NE(base.emplace(entities[0u]), base.end());
pool.emplace(entities[1u]);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 0);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.get(entities[0u]).value, 0);
ASSERT_EQ(pool.get(entities[1u]).value, 0);
base.erase(entities[0u]);
pool.erase(entities[1u]);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 2);
ASSERT_FALSE(pool.empty());
ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end());
ASSERT_EQ(pool.get(entities[0u]).value, 0);
ASSERT_EQ(pool.get(entities[1u]).value, 0);
ASSERT_FALSE(pool.empty());
base.erase(entities[1u]);
ASSERT_EQ(on_construct.value, 4);
ASSERT_EQ(on_destroy.value, 3);
ASSERT_FALSE(pool.empty());
base.erase(entities[0u]);
ASSERT_EQ(on_construct.value, 4);
ASSERT_EQ(on_destroy.value, 4);
ASSERT_FALSE(pool.empty());
pool.insert(std::begin(entities), std::end(entities), stable_type{3});
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 4);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.get(entities[0u]).value, 3);
ASSERT_EQ(pool.get(entities[1u]).value, 3);
pool.erase(std::begin(entities), std::end(entities));
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 6);
ASSERT_FALSE(pool.empty());
}
TEST(SighStorageMixin, EmptyType) {
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
entt::sigh_storage_mixin<entt::storage<empty_type>> pool;
entt::sparse_set &base = pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_NE(base.emplace(entities[0u]), base.end());
pool.emplace(entities[1u]);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 0);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(pool.contains(entities[0u]));
ASSERT_TRUE(pool.contains(entities[1u]));
base.erase(entities[0u]);
pool.erase(entities[1u]);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 2);
ASSERT_TRUE(pool.empty());
ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end());
ASSERT_TRUE(pool.contains(entities[0u]));
ASSERT_TRUE(pool.contains(entities[1u]));
ASSERT_FALSE(pool.empty());
base.erase(entities[1u]);
ASSERT_EQ(on_construct.value, 4);
ASSERT_EQ(on_destroy.value, 3);
ASSERT_FALSE(pool.empty());
base.erase(entities[0u]);
ASSERT_EQ(on_construct.value, 4);
ASSERT_EQ(on_destroy.value, 4);
ASSERT_TRUE(pool.empty());
pool.insert(std::begin(entities), std::end(entities));
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 4);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(pool.contains(entities[0u]));
ASSERT_TRUE(pool.contains(entities[1u]));
pool.erase(std::begin(entities), std::end(entities));
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 6);
ASSERT_TRUE(pool.empty());
}
TEST(SighStorageMixin, NonDefaultConstructibleType) {
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
entt::sigh_storage_mixin<entt::storage<non_default_constructible>> pool;
entt::sparse_set &base = pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_EQ(base.emplace(entities[0u]), base.end());
pool.emplace(entities[1u], 3);
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 0);
ASSERT_FALSE(pool.empty());
ASSERT_FALSE(pool.contains(entities[0u]));
ASSERT_EQ(pool.get(entities[1u]).value, 3);
base.erase(entities[1u]);
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 1);
ASSERT_TRUE(pool.empty());
ASSERT_EQ(base.insert(std::begin(entities), std::end(entities)), base.end());
ASSERT_FALSE(pool.contains(entities[0u]));
ASSERT_FALSE(pool.contains(entities[1u]));
ASSERT_TRUE(pool.empty());
pool.insert(std::begin(entities), std::end(entities), 3);
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 1);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.get(entities[0u]).value, 3);
ASSERT_EQ(pool.get(entities[1u]).value, 3);
pool.erase(std::begin(entities), std::end(entities));
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 3);
ASSERT_TRUE(pool.empty());
}
TEST(SighStorageMixin, VoidType) {
entt::sigh_storage_mixin<entt::storage<void>> pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
pool.emplace(entt::entity{99});
ASSERT_EQ(pool.type(), entt::type_id<void>());
ASSERT_TRUE(pool.contains(entt::entity{99}));
entt::sigh_storage_mixin<entt::storage<void>> other{std::move(pool)};
ASSERT_FALSE(pool.contains(entt::entity{99}));
ASSERT_TRUE(other.contains(entt::entity{99}));
pool = std::move(other);
ASSERT_TRUE(pool.contains(entt::entity{99}));
ASSERT_FALSE(other.contains(entt::entity{99}));
pool.clear();
ASSERT_EQ(on_construct.value, 1);
ASSERT_EQ(on_destroy.value, 1);
}
TEST(SighStorageMixin, Move) {
entt::sigh_storage_mixin<entt::storage<int>> pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
pool.emplace(entt::entity{3}, 3);
ASSERT_TRUE(std::is_move_constructible_v<decltype(pool)>);
ASSERT_TRUE(std::is_move_assignable_v<decltype(pool)>);
ASSERT_EQ(pool.type(), entt::type_id<int>());
entt::sigh_storage_mixin<entt::storage<int>> other{std::move(pool)};
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(other.type(), entt::type_id<int>());
ASSERT_EQ(pool.at(0u), static_cast<entt::entity>(entt::null));
ASSERT_EQ(other.at(0u), entt::entity{3});
ASSERT_EQ(other.get(entt::entity{3}), 3);
pool = std::move(other);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_EQ(pool.at(0u), entt::entity{3});
ASSERT_EQ(pool.get(entt::entity{3}), 3);
ASSERT_EQ(other.at(0u), static_cast<entt::entity>(entt::null));
other = entt::sigh_storage_mixin<entt::storage<int>>{};
other.bind(entt::forward_as_any(registry));
other.emplace(entt::entity{42}, 42);
other = std::move(pool);
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(pool.at(0u), static_cast<entt::entity>(entt::null));
ASSERT_EQ(other.at(0u), entt::entity{3});
ASSERT_EQ(other.get(entt::entity{3}), 3);
other.clear();
ASSERT_EQ(on_construct.value, 1);
ASSERT_EQ(on_destroy.value, 1);
}
TEST(SighStorageMixin, Swap) {
entt::sigh_storage_mixin<entt::storage<int>> pool;
entt::sigh_storage_mixin<entt::storage<int>> other;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
other.bind(entt::forward_as_any(registry));
other.on_construct().connect<&listener<entt::registry>>(on_construct);
other.on_destroy().connect<&listener<entt::registry>>(on_destroy);
pool.emplace(entt::entity{42}, 41);
other.emplace(entt::entity{9}, 8);
other.emplace(entt::entity{3}, 2);
other.erase(entt::entity{9});
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(other.size(), 1u);
pool.swap(other);
ASSERT_EQ(pool.type(), entt::type_id<int>());
ASSERT_EQ(other.type(), entt::type_id<int>());
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(pool.at(0u), entt::entity{3});
ASSERT_EQ(pool.get(entt::entity{3}), 2);
ASSERT_EQ(other.at(0u), entt::entity{42});
ASSERT_EQ(other.get(entt::entity{42}), 41);
pool.clear();
other.clear();
ASSERT_EQ(on_construct.value, 3);
ASSERT_EQ(on_destroy.value, 3);
}
TEST(SighStorageMixin, CustomAllocator) {
auto test = [](auto pool, auto alloc) {
using registry_type = typename decltype(pool)::registry_type;
registry_type registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().template connect<&listener<registry_type>>(on_construct);
pool.on_destroy().template connect<&listener<registry_type>>(on_destroy);
pool.reserve(1u);
ASSERT_NE(pool.capacity(), 0u);
pool.emplace(entt::entity{0});
pool.emplace(entt::entity{1});
decltype(pool) other{std::move(pool), alloc};
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
ASSERT_EQ(pool.capacity(), 0u);
ASSERT_NE(other.capacity(), 0u);
ASSERT_EQ(other.size(), 2u);
pool = std::move(other);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_EQ(other.capacity(), 0u);
ASSERT_NE(pool.capacity(), 0u);
ASSERT_EQ(pool.size(), 2u);
pool.swap(other);
pool = std::move(other);
ASSERT_FALSE(pool.empty());
ASSERT_TRUE(other.empty());
ASSERT_EQ(other.capacity(), 0u);
ASSERT_NE(pool.capacity(), 0u);
ASSERT_EQ(pool.size(), 2u);
pool.clear();
ASSERT_NE(pool.capacity(), 0u);
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 2);
};
test::throwing_allocator<entt::entity> allocator{};
test(entt::sigh_storage_mixin<entt::basic_storage<int, entt::entity, test::throwing_allocator<int>>>{allocator}, allocator);
test(entt::sigh_storage_mixin<entt::basic_storage<std::true_type, entt::entity, test::throwing_allocator<std::true_type>>>{allocator}, allocator);
test(entt::sigh_storage_mixin<entt::basic_storage<stable_type, entt::entity, test::throwing_allocator<stable_type>>>{allocator}, allocator);
}
TEST(SighStorageMixin, ThrowingAllocator) {
auto test = [](auto pool) {
using pool_allocator_type = typename decltype(pool)::allocator_type;
using value_type = typename decltype(pool)::value_type;
using registry_type = typename decltype(pool)::registry_type;
typename std::decay_t<decltype(pool)>::base_type &base = pool;
constexpr auto packed_page_size = entt::component_traits<typename decltype(pool)::value_type>::page_size;
constexpr auto sparse_page_size = entt::entt_traits<typename decltype(pool)::entity_type>::page_size;
registry_type registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().template connect<&listener<registry_type>>(on_construct);
pool.on_destroy().template connect<&listener<registry_type>>(on_destroy);
pool_allocator_type::trigger_on_allocate = true;
ASSERT_THROW(pool.reserve(1u), typename pool_allocator_type::exception_type);
ASSERT_EQ(pool.capacity(), 0u);
pool_allocator_type::trigger_after_allocate = true;
ASSERT_THROW(pool.reserve(2 * packed_page_size), typename pool_allocator_type::exception_type);
ASSERT_EQ(pool.capacity(), packed_page_size);
pool.shrink_to_fit();
ASSERT_EQ(pool.capacity(), 0u);
test::throwing_allocator<entt::entity>::trigger_on_allocate = true;
ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator<entt::entity>::exception_type);
ASSERT_FALSE(pool.contains(entt::entity{0}));
ASSERT_TRUE(pool.empty());
test::throwing_allocator<entt::entity>::trigger_on_allocate = true;
ASSERT_THROW(base.emplace(entt::entity{0}), test::throwing_allocator<entt::entity>::exception_type);
ASSERT_FALSE(base.contains(entt::entity{0}));
ASSERT_TRUE(base.empty());
pool_allocator_type::trigger_on_allocate = true;
ASSERT_THROW(pool.emplace(entt::entity{0}, 0), typename pool_allocator_type::exception_type);
ASSERT_FALSE(pool.contains(entt::entity{0}));
ASSERT_NO_FATAL_FAILURE(pool.compact());
ASSERT_TRUE(pool.empty());
pool.emplace(entt::entity{0}, 0);
const entt::entity entities[2u]{entt::entity{1}, entt::entity{sparse_page_size}};
test::throwing_allocator<entt::entity>::trigger_after_allocate = true;
ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), value_type{0}), test::throwing_allocator<entt::entity>::exception_type);
ASSERT_TRUE(pool.contains(entt::entity{1}));
ASSERT_FALSE(pool.contains(entt::entity{sparse_page_size}));
pool.erase(entt::entity{1});
const value_type components[2u]{value_type{1}, value_type{sparse_page_size}};
test::throwing_allocator<entt::entity>::trigger_on_allocate = true;
pool.compact();
ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), std::begin(components)), test::throwing_allocator<entt::entity>::exception_type);
ASSERT_TRUE(pool.contains(entt::entity{1}));
ASSERT_FALSE(pool.contains(entt::entity{sparse_page_size}));
ASSERT_EQ(on_construct.value, 1);
ASSERT_EQ(on_destroy.value, 1);
};
test(entt::sigh_storage_mixin<entt::basic_storage<int, entt::entity, test::throwing_allocator<int>>>{});
test(entt::sigh_storage_mixin<entt::basic_storage<stable_type, entt::entity, test::throwing_allocator<stable_type>>>{});
}
TEST(SighStorageMixin, ThrowingComponent) {
entt::sigh_storage_mixin<entt::storage<test::throwing_type>> pool;
using registry_type = typename decltype(pool)::registry_type;
registry_type registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<registry_type>>(on_construct);
pool.on_destroy().connect<&listener<registry_type>>(on_destroy);
test::throwing_type::trigger_on_value = 42;
// strong exception safety
ASSERT_THROW(pool.emplace(entt::entity{0}, test::throwing_type{42}), typename test::throwing_type::exception_type);
ASSERT_TRUE(pool.empty());
const entt::entity entities[2u]{entt::entity{42}, entt::entity{1}};
const test::throwing_type components[2u]{42, 1};
// basic exception safety
ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), test::throwing_type{42}), typename test::throwing_type::exception_type);
ASSERT_EQ(pool.size(), 0u);
ASSERT_FALSE(pool.contains(entt::entity{1}));
// basic exception safety
ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), std::begin(components)), typename test::throwing_type::exception_type);
ASSERT_EQ(pool.size(), 0u);
ASSERT_FALSE(pool.contains(entt::entity{1}));
// basic exception safety
ASSERT_THROW(pool.insert(std::rbegin(entities), std::rend(entities), std::rbegin(components)), typename test::throwing_type::exception_type);
ASSERT_EQ(pool.size(), 1u);
ASSERT_TRUE(pool.contains(entt::entity{1}));
ASSERT_EQ(pool.get(entt::entity{1}), 1);
pool.clear();
pool.emplace(entt::entity{1}, 1);
pool.emplace(entt::entity{42}, 42);
// basic exception safety
ASSERT_THROW(pool.erase(entt::entity{1}), typename test::throwing_type::exception_type);
ASSERT_EQ(pool.size(), 2u);
ASSERT_TRUE(pool.contains(entt::entity{42}));
ASSERT_TRUE(pool.contains(entt::entity{1}));
ASSERT_EQ(pool.at(0u), entt::entity{1});
ASSERT_EQ(pool.at(1u), entt::entity{42});
ASSERT_EQ(pool.get(entt::entity{42}), 42);
// the element may have been moved but it's still there
ASSERT_EQ(pool.get(entt::entity{1}), test::throwing_type::moved_from_value);
test::throwing_type::trigger_on_value = 99;
pool.erase(entt::entity{1});
ASSERT_EQ(pool.size(), 1u);
ASSERT_TRUE(pool.contains(entt::entity{42}));
ASSERT_FALSE(pool.contains(entt::entity{1}));
ASSERT_EQ(pool.at(0u), entt::entity{42});
ASSERT_EQ(pool.get(entt::entity{42}), 42);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_destroy.value, 3);
}

1290
test/entt/entity/view.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,432 @@
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/graph/adjacency_matrix.hpp>
#include "../common/throwing_allocator.hpp"
TEST(AdjacencyMatrix, Resize) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{2};
adjacency_matrix.insert(1u, 0u);
ASSERT_EQ(adjacency_matrix.size(), 2u);
ASSERT_TRUE(adjacency_matrix.contains(1u, 0u));
adjacency_matrix.resize(3u);
ASSERT_EQ(adjacency_matrix.size(), 3u);
ASSERT_TRUE(adjacency_matrix.contains(1u, 0u));
}
TEST(AdjacencyMatrix, Constructors) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{};
ASSERT_EQ(adjacency_matrix.size(), 0u);
adjacency_matrix = entt::adjacency_matrix<entt::directed_tag>{std::allocator<bool>{}};
adjacency_matrix = entt::adjacency_matrix<entt::directed_tag>{3u, std::allocator<bool>{}};
ASSERT_EQ(adjacency_matrix.size(), 3u);
adjacency_matrix.insert(0u, 1u);
entt::adjacency_matrix<entt::directed_tag> temp{adjacency_matrix, adjacency_matrix.get_allocator()};
entt::adjacency_matrix<entt::directed_tag> other{std::move(adjacency_matrix), adjacency_matrix.get_allocator()};
ASSERT_EQ(adjacency_matrix.size(), 0u);
ASSERT_EQ(other.size(), 3u);
ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(other.contains(0u, 1u));
}
TEST(AdjacencyMatrix, Copy) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
entt::adjacency_matrix<entt::directed_tag> other{adjacency_matrix};
ASSERT_EQ(adjacency_matrix.size(), 3u);
ASSERT_EQ(other.size(), 3u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(other.contains(0u, 1u));
adjacency_matrix.resize(4u);
adjacency_matrix.insert(0u, 2u);
other.insert(1u, 2u);
other = adjacency_matrix;
ASSERT_EQ(other.size(), 4u);
ASSERT_EQ(adjacency_matrix.size(), 4u);
ASSERT_TRUE(other.contains(0u, 1u));
ASSERT_FALSE(other.contains(1u, 2u));
ASSERT_TRUE(other.contains(0u, 2u));
}
TEST(AdjacencyMatrix, Move) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
entt::adjacency_matrix<entt::directed_tag> other{std::move(adjacency_matrix)};
ASSERT_EQ(adjacency_matrix.size(), 0u);
ASSERT_EQ(other.size(), 3u);
ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(other.contains(0u, 1u));
adjacency_matrix = {};
adjacency_matrix.resize(4u);
adjacency_matrix.insert(0u, 2u);
other.insert(1u, 2u);
other = std::move(adjacency_matrix);
ASSERT_EQ(other.size(), 4u);
ASSERT_EQ(adjacency_matrix.size(), 0u);
ASSERT_FALSE(other.contains(0u, 1u));
ASSERT_FALSE(other.contains(1u, 2u));
ASSERT_TRUE(other.contains(0u, 2u));
}
TEST(AdjacencyMatrix, Swap) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
entt::adjacency_matrix<entt::directed_tag> other{};
adjacency_matrix.insert(0u, 1u);
ASSERT_EQ(other.size(), 0u);
ASSERT_EQ(adjacency_matrix.size(), 3u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_FALSE(other.contains(0u, 1u));
adjacency_matrix.swap(other);
ASSERT_EQ(other.size(), 3u);
ASSERT_EQ(adjacency_matrix.size(), 0u);
ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(other.contains(0u, 1u));
}
TEST(AdjacencyMatrix, InsertDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
auto first = adjacency_matrix.insert(0u, 1u);
auto second = adjacency_matrix.insert(0u, 2u);
auto other = adjacency_matrix.insert(0u, 1u);
ASSERT_TRUE(first.second);
ASSERT_TRUE(second.second);
ASSERT_FALSE(other.second);
ASSERT_NE(first.first, second.first);
ASSERT_EQ(first.first, other.first);
ASSERT_EQ(*first.first, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(*second.first, std::make_pair(std::size_t{0u}, std::size_t{2u}));
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_FALSE(adjacency_matrix.contains(2u, 0u));
}
TEST(AdjacencyMatrix, InsertUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{3u};
auto first = adjacency_matrix.insert(0u, 1u);
auto second = adjacency_matrix.insert(0u, 2u);
auto other = adjacency_matrix.insert(0u, 1u);
ASSERT_TRUE(first.second);
ASSERT_TRUE(second.second);
ASSERT_FALSE(other.second);
ASSERT_NE(first.first, second.first);
ASSERT_EQ(first.first, other.first);
ASSERT_EQ(*first.first, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(*second.first, std::make_pair(std::size_t{0u}, std::size_t{2u}));
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(adjacency_matrix.contains(2u, 0u));
}
TEST(AdjacencyMatrix, EraseDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_FALSE(adjacency_matrix.contains(1u, 0u));
ASSERT_EQ(adjacency_matrix.erase(0u, 1u), 1u);
ASSERT_EQ(adjacency_matrix.erase(0u, 1u), 0u);
ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
ASSERT_FALSE(adjacency_matrix.contains(1u, 0u));
}
TEST(AdjacencyMatrix, EraseUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(adjacency_matrix.contains(1u, 0u));
ASSERT_EQ(adjacency_matrix.erase(0u, 1u), 1u);
ASSERT_EQ(adjacency_matrix.erase(0u, 1u), 0u);
ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
ASSERT_FALSE(adjacency_matrix.contains(1u, 0u));
}
TEST(AdjacencyMatrix, Clear) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(0u, 2u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_TRUE(adjacency_matrix.contains(0u, 2u));
ASSERT_EQ(adjacency_matrix.size(), 3u);
adjacency_matrix.clear();
ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
ASSERT_FALSE(adjacency_matrix.contains(0u, 2u));
ASSERT_EQ(adjacency_matrix.size(), 0u);
}
TEST(AdjacencyMatrix, VertexIterator) {
using iterator = typename entt::adjacency_matrix<entt::directed_tag>::vertex_iterator;
static_assert(std::is_same_v<iterator::value_type, std::size_t>);
static_assert(std::is_same_v<iterator::pointer, void>);
static_assert(std::is_same_v<iterator::reference, std::size_t>);
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{2u};
const auto iterable = adjacency_matrix.vertices();
iterator end{iterable.begin()};
iterator begin{};
begin = iterable.end();
std::swap(begin, end);
ASSERT_EQ(begin, iterable.begin());
ASSERT_EQ(end, iterable.end());
ASSERT_NE(begin, end);
ASSERT_EQ(*begin, 0u);
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(*begin, 1u);
ASSERT_EQ(++begin, iterable.end());
}
TEST(AdjacencyMatrix, EdgeIterator) {
using iterator = typename entt::adjacency_matrix<entt::directed_tag>::edge_iterator;
static_assert(std::is_same_v<iterator::value_type, std::pair<std::size_t, std::size_t>>);
static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<std::size_t, std::size_t>>>);
static_assert(std::is_same_v<iterator::reference, std::pair<std::size_t, std::size_t>>);
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(0u, 2u);
const auto iterable = adjacency_matrix.edges();
iterator end{iterable.begin()};
iterator begin{};
begin = iterable.end();
std::swap(begin, end);
ASSERT_EQ(begin, iterable.begin());
ASSERT_EQ(end, iterable.end());
ASSERT_NE(begin, end);
ASSERT_EQ(*begin, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(begin++, iterable.begin());
ASSERT_EQ(*begin.operator->(), std::make_pair(std::size_t{0u}, std::size_t{2u}));
ASSERT_EQ(++begin, iterable.end());
}
TEST(AdjacencyMatrix, Vertices) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{};
auto iterable = adjacency_matrix.vertices();
ASSERT_EQ(adjacency_matrix.size(), 0u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.resize(2u);
iterable = adjacency_matrix.vertices();
ASSERT_EQ(adjacency_matrix.size(), 2u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, 0u);
ASSERT_EQ(*it, 1u);
ASSERT_EQ(++it, iterable.end());
}
TEST(AdjacencyMatrix, EdgesDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
auto iterable = adjacency_matrix.edges();
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
iterable = adjacency_matrix.edges();
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(*it.operator->(), std::make_pair(std::size_t{1u}, std::size_t{2u}));
ASSERT_EQ(++it, iterable.end());
}
TEST(AdjacencyMatrix, EdgesUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{3u};
auto iterable = adjacency_matrix.edges();
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
iterable = adjacency_matrix.edges();
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(*it.operator->(), std::make_pair(std::size_t{1u}, std::size_t{0u}));
ASSERT_EQ(*(++it).operator->(), std::make_pair(std::size_t{1u}, std::size_t{2u}));
ASSERT_EQ(*++it, std::make_pair(std::size_t{2u}, std::size_t{1u}));
ASSERT_EQ(++it, iterable.end());
}
TEST(AdjacencyMatrix, OutEdgesDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
auto iterable = adjacency_matrix.out_edges(0u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
iterable = adjacency_matrix.out_edges(0u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(it, iterable.end());
iterable = adjacency_matrix.out_edges(2u);
it = iterable.cbegin();
ASSERT_EQ(it, iterable.cend());
}
TEST(AdjacencyMatrix, OutEdgesUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{3u};
auto iterable = adjacency_matrix.out_edges(0u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
iterable = adjacency_matrix.out_edges(0u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(it, iterable.end());
iterable = adjacency_matrix.out_edges(2u);
it = iterable.cbegin();
ASSERT_NE(it, iterable.cend());
ASSERT_EQ(*it++, std::make_pair(std::size_t{2u}, std::size_t{1u}));
ASSERT_EQ(it, iterable.cend());
}
TEST(AdjacencyMatrix, InEdgesDirected) {
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
auto iterable = adjacency_matrix.in_edges(1u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
iterable = adjacency_matrix.in_edges(1u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(it, iterable.end());
iterable = adjacency_matrix.in_edges(0u);
it = iterable.cbegin();
ASSERT_EQ(it, iterable.cend());
}
TEST(AdjacencyMatrix, InEdgesUndirected) {
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{3u};
auto iterable = adjacency_matrix.in_edges(1u);
ASSERT_EQ(iterable.begin(), iterable.end());
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
iterable = adjacency_matrix.in_edges(1u);
ASSERT_NE(iterable.begin(), iterable.end());
auto it = iterable.begin();
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u}));
ASSERT_EQ(it, iterable.end());
iterable = adjacency_matrix.in_edges(0u);
it = iterable.cbegin();
ASSERT_NE(it, iterable.cend());
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{0u}));
ASSERT_EQ(it, iterable.cend());
}
TEST(AdjacencyMatrix, ThrowingAllocator) {
using allocator = test::throwing_allocator<std::size_t>;
using exception = typename allocator::exception_type;
entt::adjacency_matrix<entt::directed_tag, allocator> adjacency_matrix{2u};
adjacency_matrix.insert(0u, 1u);
allocator::trigger_on_allocate = true;
ASSERT_EQ(adjacency_matrix.size(), 2u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
ASSERT_THROW(adjacency_matrix.resize(4u), exception);
ASSERT_EQ(adjacency_matrix.size(), 2u);
ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
}

62
test/entt/graph/dot.cpp Normal file
View File

@ -0,0 +1,62 @@
#include <sstream>
#include <string>
#include <gtest/gtest.h>
#include <entt/graph/adjacency_matrix.hpp>
#include <entt/graph/dot.hpp>
TEST(Dot, DirectedGraph) {
std::ostringstream output{};
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
adjacency_matrix.insert(0u, 2u);
entt::dot(output, adjacency_matrix);
const std::string expected = "digraph{0[];1[];2[];0->1;0->2;1->2;}";
const auto str = output.str();
ASSERT_FALSE(str.empty());
ASSERT_EQ(str, expected);
}
TEST(Dot, UndirectedGraph) {
std::ostringstream output{};
entt::adjacency_matrix<entt::undirected_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
adjacency_matrix.insert(0u, 2u);
entt::dot(output, adjacency_matrix);
const std::string expected = "graph{0[];1[];2[];0--1;0--2;1--0;1--2;2--0;2--1;}";
const auto str = output.str();
ASSERT_FALSE(str.empty());
ASSERT_EQ(str, expected);
}
TEST(Dot, CustomWriter) {
std::ostringstream output{};
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
adjacency_matrix.insert(0u, 1u);
adjacency_matrix.insert(1u, 2u);
adjacency_matrix.insert(0u, 2u);
entt::dot(output, adjacency_matrix, [&adjacency_matrix](std::ostream &out, std::size_t vertex) {
out << "label=\"v" << vertex << "\"";
if(auto in_edges = adjacency_matrix.in_edges(vertex); in_edges.cbegin() == in_edges.cend()) {
out << ",shape=\"box\"";
}
});
const std::string expected = "digraph{0[label=\"v0\",shape=\"box\"];1[label=\"v1\"];2[label=\"v2\"];0->1;0->2;1->2;}";
const auto str = output.str();
ASSERT_FALSE(str.empty());
ASSERT_EQ(str, expected);
}

289
test/entt/graph/flow.cpp Normal file
View File

@ -0,0 +1,289 @@
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/graph/flow.hpp>
#include "../common/throwing_allocator.hpp"
TEST(Flow, Constructors) {
entt::flow flow{};
ASSERT_EQ(flow.size(), 0u);
flow = entt::flow{std::allocator<entt::id_type>{}};
ASSERT_EQ(flow.size(), 0u);
flow.bind(0);
flow.bind(3);
flow.bind(99);
ASSERT_EQ(flow.size(), 3u);
entt::flow temp{flow, flow.get_allocator()};
entt::flow other{std::move(flow), flow.get_allocator()};
ASSERT_EQ(flow.size(), 0u);
ASSERT_EQ(other.size(), 3u);
ASSERT_EQ(other[0u], 0);
ASSERT_EQ(other[1u], 3);
ASSERT_EQ(other[2u], 99);
}
TEST(Flow, Copy) {
entt::flow flow{};
flow.bind(0);
flow.bind(3);
flow.bind(99);
entt::flow other{flow};
ASSERT_EQ(flow.size(), 3u);
ASSERT_EQ(other.size(), 3u);
ASSERT_EQ(other[0u], 0);
ASSERT_EQ(other[1u], 3);
ASSERT_EQ(other[2u], 99);
flow.bind(1);
other.bind(2);
other = flow;
ASSERT_EQ(other.size(), 4u);
ASSERT_EQ(flow.size(), 4u);
ASSERT_EQ(other[0u], 0);
ASSERT_EQ(other[1u], 3);
ASSERT_EQ(other[2u], 99);
ASSERT_EQ(other[3u], 1);
}
TEST(Flow, Move) {
entt::flow flow{};
flow.bind(0);
flow.bind(3);
flow.bind(99);
entt::flow other{std::move(flow)};
ASSERT_EQ(flow.size(), 0u);
ASSERT_EQ(other.size(), 3u);
ASSERT_EQ(other[0u], 0);
ASSERT_EQ(other[1u], 3);
ASSERT_EQ(other[2u], 99);
flow = {};
flow.bind(1);
other.bind(2);
other = std::move(flow);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(flow.size(), 0u);
ASSERT_EQ(other[0u], 1);
}
TEST(Flow, Swap) {
entt::flow flow{};
entt::flow other{};
flow.bind(7);
ASSERT_EQ(other.size(), 0u);
ASSERT_EQ(flow.size(), 1u);
ASSERT_EQ(flow[0u], 7);
flow.swap(other);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(flow.size(), 0u);
ASSERT_EQ(other[0u], 7);
}
TEST(Flow, Clear) {
entt::flow flow{};
flow.bind(0);
flow.bind(99);
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow[0u], 0);
ASSERT_EQ(flow[1u], 99);
flow.clear();
ASSERT_EQ(flow.size(), 0u);
}
TEST(Flow, Set) {
entt::flow flow{};
flow.bind(0).set(10, true).bind(1).set(10, true).set(11, false);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_NE(graph.edges().cbegin(), graph.edges().cend());
ASSERT_TRUE(graph.contains(0u, 1u));
ASSERT_FALSE(graph.contains(1u, 0u));
}
TEST(Flow, RO) {
entt::flow flow{};
flow.bind(0).ro(10).bind(1).ro(10).ro(11);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_EQ(graph.edges().cbegin(), graph.edges().cend());
}
TEST(Flow, RangeRO) {
entt::flow flow{};
const entt::id_type res[2u]{10, 11};
flow.bind(0).ro(res, res + 1).bind(1).ro(res, res + 2);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_EQ(graph.edges().cbegin(), graph.edges().cend());
}
TEST(Flow, RW) {
entt::flow flow{};
flow.bind(0).rw(10).bind(1).rw(10).rw(11);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_NE(graph.edges().cbegin(), graph.edges().cend());
ASSERT_TRUE(graph.contains(0u, 1u));
ASSERT_FALSE(graph.contains(1u, 0u));
}
TEST(Flow, RangeRW) {
entt::flow flow{};
const entt::id_type res[2u]{10, 11};
flow.bind(0).rw(res, res + 1).bind(1).rw(res, res + 2);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 2u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_NE(graph.edges().cbegin(), graph.edges().cend());
ASSERT_TRUE(graph.contains(0u, 1u));
ASSERT_FALSE(graph.contains(1u, 0u));
}
TEST(Flow, Graph) {
using namespace entt::literals;
entt::flow flow{};
flow.bind("task_0"_hs)
.ro("resource_0"_hs)
.rw("resource_1"_hs);
flow.bind("task_1"_hs)
.ro("resource_0"_hs)
.rw("resource_2"_hs);
flow.bind("task_2"_hs)
.ro("resource_1"_hs)
.rw("resource_3"_hs);
flow.bind("task_3"_hs)
.rw("resource_1"_hs)
.ro("resource_2"_hs);
flow.bind("task_4"_hs)
.rw("resource_0"_hs);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 5u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_EQ(flow[0u], "task_0"_hs);
ASSERT_EQ(flow[1u], "task_1"_hs);
ASSERT_EQ(flow[2u], "task_2"_hs);
ASSERT_EQ(flow[3u], "task_3"_hs);
ASSERT_EQ(flow[4u], "task_4"_hs);
auto it = graph.edges().cbegin();
const auto last = graph.edges().cend();
ASSERT_NE(it, last);
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{2u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{4u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{3u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{4u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{2u}, std::size_t{3u}));
ASSERT_EQ(it, last);
}
TEST(Flow, Sync) {
using namespace entt::literals;
entt::flow flow{};
flow.bind("task_0"_hs)
.ro("resource_0"_hs);
flow.bind("task_1"_hs)
.rw("resource_1"_hs);
flow.bind("task_2"_hs)
.sync();
flow.bind("task_3"_hs)
.ro("resource_0"_hs)
.rw("resource_2"_hs);
flow.bind("task_4"_hs)
.ro("resource_2"_hs);
auto graph = flow.graph();
ASSERT_EQ(flow.size(), 5u);
ASSERT_EQ(flow.size(), graph.size());
ASSERT_EQ(flow[0u], "task_0"_hs);
ASSERT_EQ(flow[1u], "task_1"_hs);
ASSERT_EQ(flow[2u], "task_2"_hs);
ASSERT_EQ(flow[3u], "task_3"_hs);
ASSERT_EQ(flow[4u], "task_4"_hs);
auto it = graph.edges().cbegin();
const auto last = graph.edges().cend();
ASSERT_NE(it, last);
ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{2u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{1u}, std::size_t{2u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{2u}, std::size_t{3u}));
ASSERT_EQ(*it++, std::make_pair(std::size_t{3u}, std::size_t{4u}));
ASSERT_EQ(it, last);
}
TEST(Flow, ThrowingAllocator) {
using allocator = test::throwing_allocator<entt::id_type>;
using task_allocator = test::throwing_allocator<std::pair<std::size_t, entt::id_type>>;
using task_exception = typename task_allocator::exception_type;
entt::basic_flow<allocator> flow{};
task_allocator::trigger_on_allocate = true;
ASSERT_EQ(flow.size(), 0u);
ASSERT_THROW(flow.bind(1), task_exception);
ASSERT_EQ(flow.size(), 0u);
flow.bind(1);
ASSERT_EQ(flow.size(), 1u);
}

View File

@ -0,0 +1,78 @@
#include <memory>
#include <gtest/gtest.h>
#include <entt/locator/locator.hpp>
#include "../common/config.h"
struct base_service {
virtual ~base_service() = default;
virtual void invoke() {}
};
struct null_service: base_service {
void invoke() override {
invoked = true;
}
static inline bool invoked{};
};
struct derived_service: base_service {
void invoke() override {
invoked = true;
}
static inline bool invoked{};
};
struct ServiceLocator: ::testing::Test {
void SetUp() override {
null_service::invoked = false;
derived_service::invoked = false;
}
};
using ServiceLocatorDeathTest = ServiceLocator;
TEST(ServiceLocator, Functionalities) {
ASSERT_FALSE(entt::locator<base_service>::has_value());
ASSERT_FALSE(derived_service::invoked);
ASSERT_FALSE(null_service::invoked);
entt::locator<base_service>::value_or<null_service>().invoke();
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_TRUE(null_service::invoked);
auto handle = entt::locator<base_service>::handle();
entt::locator<base_service>::reset();
ASSERT_FALSE(entt::locator<base_service>::has_value());
entt::locator<base_service>::reset(handle);
ASSERT_TRUE(entt::locator<base_service>::has_value());
entt::locator<base_service>::reset(decltype(handle){});
ASSERT_FALSE(entt::locator<base_service>::has_value());
entt::locator<base_service>::emplace<derived_service>();
entt::locator<base_service>::value().invoke();
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_TRUE(derived_service::invoked);
derived_service::invoked = false;
entt::locator<base_service>::allocate_emplace<derived_service>(std::allocator<derived_service>{}).invoke();
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_TRUE(derived_service::invoked);
}
ENTT_DEBUG_TEST(ServiceLocatorDeathTest, UninitializedValue) {
ASSERT_NO_FATAL_FAILURE(entt::locator<base_service>::value_or().invoke());
entt::locator<base_service>::reset();
ASSERT_DEATH(entt::locator<base_service>::value().invoke(), "");
}

1371
test/entt/meta/meta_any.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,193 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/node.hpp>
#include <entt/meta/resolve.hpp>
struct base_1_t {
base_1_t() = default;
int value_1{};
};
struct base_2_t {
base_2_t() = default;
operator int() const {
return value_2;
}
int value_2{};
};
struct base_3_t: base_2_t {
base_3_t() = default;
int value_3{};
};
struct derived_t: base_1_t, base_3_t {
derived_t() = default;
int value{};
};
struct MetaBase: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<base_1_t>()
.data<&base_1_t::value_1>("value_1"_hs);
entt::meta<base_2_t>()
.conv<int>()
.data<&base_2_t::value_2>("value_2"_hs);
entt::meta<base_3_t>()
.base<base_2_t>()
.data<&base_3_t::value_3>("value_3"_hs);
entt::meta<derived_t>()
.type("derived"_hs)
.base<base_1_t>()
.base<base_3_t>()
.data<&derived_t::value>("value"_hs);
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaBase, Functionalities) {
auto any = entt::resolve<derived_t>().construct();
any.cast<derived_t &>().value_1 = 42;
auto as_derived = any.as_ref();
ASSERT_TRUE(any.allow_cast<base_1_t &>());
ASSERT_FALSE(any.allow_cast<char>());
ASSERT_FALSE(as_derived.allow_cast<char>());
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<base_1_t &>().value_1, as_derived.cast<derived_t &>().value_1);
any.cast<base_1_t &>().value_1 = 3;
ASSERT_EQ(any.cast<const base_1_t &>().value_1, as_derived.cast<const derived_t &>().value_1);
}
TEST_F(MetaBase, SetGetWithMutatingThis) {
using namespace entt::literals;
derived_t instance;
auto any = entt::forward_as_meta(instance);
auto as_cref = std::as_const(any).as_ref();
ASSERT_NE(static_cast<const void *>(static_cast<const base_1_t *>(&instance)), static_cast<const void *>(static_cast<const base_2_t *>(&instance)));
ASSERT_NE(static_cast<const void *>(static_cast<const base_1_t *>(&instance)), static_cast<const void *>(static_cast<const base_3_t *>(&instance)));
ASSERT_EQ(static_cast<const void *>(static_cast<const base_2_t *>(&instance)), static_cast<const void *>(static_cast<const base_3_t *>(&instance)));
ASSERT_EQ(static_cast<const void *>(&instance), static_cast<const void *>(static_cast<const base_1_t *>(&instance)));
ASSERT_TRUE(any.set("value"_hs, 42));
ASSERT_TRUE(any.set("value_1"_hs, 1));
ASSERT_TRUE(any.set("value_2"_hs, 2));
ASSERT_TRUE(any.set("value_3"_hs, 3));
ASSERT_FALSE(as_cref.set("value"_hs, 0));
ASSERT_FALSE(as_cref.set("value_1"_hs, 0));
ASSERT_FALSE(as_cref.set("value_2"_hs, 0));
ASSERT_FALSE(as_cref.set("value_3"_hs, 0));
ASSERT_EQ(any.get("value"_hs).cast<int>(), 42);
ASSERT_EQ(any.get("value_1"_hs).cast<const int>(), 1);
ASSERT_EQ(any.get("value_2"_hs).cast<int>(), 2);
ASSERT_EQ(any.get("value_3"_hs).cast<const int>(), 3);
ASSERT_EQ(as_cref.get("value"_hs).cast<const int>(), 42);
ASSERT_EQ(as_cref.get("value_1"_hs).cast<int>(), 1);
ASSERT_EQ(as_cref.get("value_2"_hs).cast<const int>(), 2);
ASSERT_EQ(as_cref.get("value_3"_hs).cast<int>(), 3);
ASSERT_EQ(instance.value, 42);
ASSERT_EQ(instance.value_1, 1);
ASSERT_EQ(instance.value_2, 2);
ASSERT_EQ(instance.value_3, 3);
}
TEST_F(MetaBase, ConvWithMutatingThis) {
entt::meta_any any{derived_t{}};
auto &&ref = any.cast<derived_t &>();
auto as_cref = std::as_const(any).as_ref();
ref.value_2 = 42;
auto conv = std::as_const(any).allow_cast<int>();
auto from_cref = std::as_const(as_cref).allow_cast<int>();
ASSERT_TRUE(conv);
ASSERT_TRUE(from_cref);
ASSERT_EQ(conv.cast<int>(), 42);
ASSERT_EQ(from_cref.cast<int>(), 42);
ASSERT_TRUE(as_cref.allow_cast<int>());
ASSERT_TRUE(any.allow_cast<int>());
ASSERT_EQ(as_cref.cast<int>(), 42);
ASSERT_EQ(any.cast<int>(), 42);
}
TEST_F(MetaBase, OpaqueConvWithMutatingThis) {
entt::meta_any any{derived_t{}};
auto as_cref = std::as_const(any).as_ref();
any.cast<derived_t &>().value_2 = 42;
auto conv = std::as_const(any).allow_cast(entt::resolve<int>());
auto from_cref = std::as_const(as_cref).allow_cast(entt::resolve<int>());
ASSERT_TRUE(conv);
ASSERT_TRUE(from_cref);
ASSERT_EQ(conv.cast<int>(), 42);
ASSERT_EQ(from_cref.cast<int>(), 42);
ASSERT_TRUE(as_cref.allow_cast(entt::resolve<int>()));
ASSERT_TRUE(any.allow_cast(entt::resolve<int>()));
ASSERT_EQ(as_cref.cast<int>(), 42);
ASSERT_EQ(any.cast<int>(), 42);
}
TEST_F(MetaBase, AssignWithMutatingThis) {
using namespace entt::literals;
entt::meta_any dst{base_2_t{}};
entt::meta_any src{derived_t{}};
dst.cast<base_2_t &>().value_2 = 0;
src.cast<derived_t &>().value_2 = 42;
ASSERT_TRUE(dst.assign(src));
ASSERT_EQ(dst.get("value_2"_hs).cast<int>(), 42);
}
TEST_F(MetaBase, TransferWithMutatingThis) {
using namespace entt::literals;
entt::meta_any dst{base_2_t{}};
entt::meta_any src{derived_t{}};
dst.cast<base_2_t &>().value_2 = 0;
src.cast<derived_t &>().value_2 = 42;
ASSERT_TRUE(dst.assign(std::move(src)));
ASSERT_EQ(dst.get("value_2"_hs).cast<int>(), 42);
}
TEST_F(MetaBase, ReRegistration) {
SetUp();
auto &&node = entt::internal::resolve<derived_t>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
ASSERT_TRUE(node.details);
ASSERT_FALSE(node.details->base.empty());
ASSERT_EQ(node.details->base.size(), 2u);
}

View File

@ -0,0 +1,757 @@
#include <array>
#include <deque>
#include <list>
#include <map>
#include <set>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/container.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include "../common/config.h"
struct invalid_type {};
struct MetaContainer: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<double>()
.type("double"_hs);
entt::meta<int>()
.type("int"_hs);
}
void TearDown() override {
entt::meta_reset();
}
};
using MetaContainerDeathTest = MetaContainer;
TEST_F(MetaContainer, InvalidContainer) {
ASSERT_FALSE(entt::meta_any{42}.as_sequence_container());
ASSERT_FALSE(entt::meta_any{42}.as_associative_container());
ASSERT_FALSE((entt::meta_any{std::map<int, char>{}}.as_sequence_container()));
ASSERT_FALSE(entt::meta_any{std::vector<int>{}}.as_associative_container());
}
TEST_F(MetaContainer, EmptySequenceContainer) {
entt::meta_sequence_container container{};
ASSERT_FALSE(container);
entt::meta_any any{std::vector<int>{}};
container = any.as_sequence_container();
ASSERT_TRUE(container);
}
TEST_F(MetaContainer, EmptyAssociativeContainer) {
entt::meta_associative_container container{};
ASSERT_FALSE(container);
entt::meta_any any{std::map<int, char>{}};
container = any.as_associative_container();
ASSERT_TRUE(container);
}
TEST_F(MetaContainer, SequenceContainerIterator) {
std::vector<int> vec{2, 3, 4};
auto any = entt::forward_as_meta(vec);
entt::meta_sequence_container::iterator first{};
auto view = any.as_sequence_container();
ASSERT_FALSE(first);
first = view.begin();
const auto last = view.end();
ASSERT_TRUE(first);
ASSERT_TRUE(last);
ASSERT_FALSE(first == last);
ASSERT_TRUE(first != last);
ASSERT_EQ((first++)->cast<int>(), 2);
ASSERT_EQ((++first)->cast<int>(), 4);
ASSERT_NE(first++, last);
ASSERT_TRUE(first == last);
ASSERT_FALSE(first != last);
ASSERT_EQ(first--, last);
ASSERT_EQ((first--)->cast<int>(), 4);
ASSERT_EQ((--first)->cast<int>(), 2);
}
TEST_F(MetaContainer, AssociativeContainerIterator) {
std::map<int, char> map{{2, 'c'}, {3, 'd'}, {4, 'e'}};
auto any = entt::forward_as_meta(map);
entt::meta_associative_container::iterator first{};
auto view = any.as_associative_container();
ASSERT_FALSE(first);
first = view.begin();
const auto last = view.end();
ASSERT_TRUE(first);
ASSERT_TRUE(last);
ASSERT_FALSE(first == last);
ASSERT_TRUE(first != last);
ASSERT_NE(first, last);
ASSERT_EQ((first++)->first.cast<int>(), 2);
ASSERT_EQ((++first)->second.cast<char>(), 'e');
ASSERT_NE(first++, last);
ASSERT_EQ(first, last);
ASSERT_TRUE(first == last);
ASSERT_FALSE(first != last);
}
TEST_F(MetaContainer, StdVector) {
std::vector<int> vec{};
auto any = entt::forward_as_meta(vec);
auto view = any.as_sequence_container();
auto cview = std::as_const(any).as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_TRUE(view.resize(3u));
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
view[0].cast<int &>() = 2;
view[1].cast<int &>() = 3;
view[2].cast<int &>() = 4;
ASSERT_EQ(view[1u].cast<int>(), 3);
auto it = view.begin();
auto ret = view.insert(it, 0);
ASSERT_TRUE(ret);
ASSERT_FALSE(view.insert(ret, invalid_type{}));
ASSERT_TRUE(view.insert(++ret, 1.));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.begin()->cast<int>(), 0);
ASSERT_EQ((++view.begin())->cast<int>(), 1);
ret = view.insert(cview.end(), 42);
ASSERT_TRUE(ret);
ASSERT_EQ(*ret, 42);
it = view.begin();
ret = view.erase(it);
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(ret->cast<int>(), 1);
ret = view.erase(cview.begin());
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(ret->cast<int>(), 2);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, StdArray) {
std::array<int, 3> arr{};
auto any = entt::forward_as_meta(arr);
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
ASSERT_FALSE(view.resize(5u));
ASSERT_EQ(view.size(), 3u);
view[0].cast<int &>() = 2;
view[1].cast<int &>() = 3;
view[2].cast<int &>() = 4;
ASSERT_EQ(view[1u].cast<int>(), 3);
auto it = view.begin();
auto ret = view.insert(it, 0);
ASSERT_FALSE(ret);
ASSERT_FALSE(view.insert(it, 'c'));
ASSERT_FALSE(view.insert(++it, 1.));
ASSERT_EQ(view.size(), 3u);
ASSERT_EQ(view.begin()->cast<int>(), 2);
ASSERT_EQ((++view.begin())->cast<int>(), 3);
it = view.begin();
ret = view.erase(it);
ASSERT_FALSE(ret);
ASSERT_EQ(view.size(), 3u);
ASSERT_EQ(it->cast<int>(), 2);
ASSERT_FALSE(view.clear());
ASSERT_EQ(view.size(), 3u);
}
TEST_F(MetaContainer, StdList) {
std::list<int> list{};
auto any = entt::forward_as_meta(list);
auto view = any.as_sequence_container();
auto cview = std::as_const(any).as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_TRUE(view.resize(3u));
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
view[0].cast<int &>() = 2;
view[1].cast<int &>() = 3;
view[2].cast<int &>() = 4;
ASSERT_EQ(view[1u].cast<int>(), 3);
auto it = view.begin();
auto ret = view.insert(it, 0);
ASSERT_TRUE(ret);
ASSERT_FALSE(view.insert(ret, invalid_type{}));
ASSERT_TRUE(view.insert(++ret, 1.));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.begin()->cast<int>(), 0);
ASSERT_EQ((++view.begin())->cast<int>(), 1);
ret = view.insert(cview.end(), 42);
ASSERT_TRUE(ret);
ASSERT_EQ(*ret, 42);
it = view.begin();
ret = view.erase(it);
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(ret->cast<int>(), 1);
ret = view.erase(cview.begin());
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(ret->cast<int>(), 2);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, StdDeque) {
std::deque<int> deque{};
auto any = entt::forward_as_meta(deque);
auto view = any.as_sequence_container();
auto cview = std::as_const(any).as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_TRUE(view.resize(3u));
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
view[0].cast<int &>() = 2;
view[1].cast<int &>() = 3;
view[2].cast<int &>() = 4;
ASSERT_EQ(view[1u].cast<int>(), 3);
auto it = view.begin();
auto ret = view.insert(it, 0);
ASSERT_TRUE(ret);
ASSERT_FALSE(view.insert(ret, invalid_type{}));
ASSERT_TRUE(view.insert(++ret, 1.));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.begin()->cast<int>(), 0);
ASSERT_EQ((++view.begin())->cast<int>(), 1);
ret = view.insert(cview.end(), 42);
ASSERT_TRUE(ret);
ASSERT_EQ(*ret, 42);
it = view.begin();
ret = view.erase(it);
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(ret->cast<int>(), 1);
ret = view.erase(cview.begin());
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(ret->cast<int>(), 2);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, StdMap) {
std::map<int, char> map{{2, 'c'}, {3, 'd'}, {4, 'e'}};
auto any = entt::forward_as_meta(map);
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_FALSE(view.key_only());
ASSERT_EQ(view.key_type(), entt::resolve<int>());
ASSERT_EQ(view.mapped_type(), entt::resolve<char>());
ASSERT_EQ(view.value_type(), (entt::resolve<std::pair<const int, char>>()));
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.find(3)->second.cast<char>(), 'd');
ASSERT_FALSE(view.insert(invalid_type{}, 'a'));
ASSERT_FALSE(view.insert(1, invalid_type{}));
ASSERT_TRUE(view.insert(0, 'a'));
ASSERT_TRUE(view.insert(1., static_cast<int>('b')));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.find(0)->second.cast<char>(), 'a');
ASSERT_EQ(view.find(1.)->second.cast<char>(), 'b');
ASSERT_EQ(view.erase(invalid_type{}), 0u);
ASSERT_FALSE(view.find(invalid_type{}));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.erase(0), 1u);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(view.find(0), view.end());
view.find(1.)->second.cast<char &>() = 'f';
ASSERT_EQ(view.find(1.f)->second.cast<char>(), 'f');
ASSERT_EQ(view.erase(1.), 1u);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, StdSet) {
std::set<int> set{2, 3, 4};
auto any = entt::forward_as_meta(set);
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_TRUE(view.key_only());
ASSERT_EQ(view.key_type(), entt::resolve<int>());
ASSERT_EQ(view.mapped_type(), entt::meta_type{});
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.find(3)->first.cast<int>(), 3);
ASSERT_FALSE(view.insert(invalid_type{}));
ASSERT_TRUE(view.insert(.0));
ASSERT_TRUE(view.insert(1));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.find(0)->first.cast<int>(), 0);
ASSERT_EQ(view.find(1.)->first.cast<int>(), 1);
ASSERT_EQ(view.erase(invalid_type{}), 0u);
ASSERT_FALSE(view.find(invalid_type{}));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.erase(0), 1u);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(view.find(0), view.end());
ASSERT_EQ(view.find(1.f)->first.try_cast<int>(), nullptr);
ASSERT_NE(view.find(1.)->first.try_cast<const int>(), nullptr);
ASSERT_EQ(view.find(true)->first.cast<const int &>(), 1);
ASSERT_EQ(view.erase(1.), 1u);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, DenseMap) {
entt::dense_map<int, char> map{};
auto any = entt::forward_as_meta(map);
auto view = any.as_associative_container();
map.emplace(2, 'c');
map.emplace(3, 'd');
map.emplace(4, '3');
ASSERT_TRUE(view);
ASSERT_FALSE(view.key_only());
ASSERT_EQ(view.key_type(), entt::resolve<int>());
ASSERT_EQ(view.mapped_type(), entt::resolve<char>());
ASSERT_EQ(view.value_type(), (entt::resolve<std::pair<const int, char>>()));
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.find(3)->second.cast<char>(), 'd');
ASSERT_FALSE(view.insert(invalid_type{}, 'a'));
ASSERT_FALSE(view.insert(1, invalid_type{}));
ASSERT_TRUE(view.insert(0, 'a'));
ASSERT_TRUE(view.insert(1., static_cast<int>('b')));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.find(0)->second.cast<char>(), 'a');
ASSERT_EQ(view.find(1.)->second.cast<char>(), 'b');
ASSERT_EQ(view.erase(invalid_type{}), 0u);
ASSERT_FALSE(view.find(invalid_type{}));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.erase(0), 1u);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(view.find(0), view.end());
view.find(1.)->second.cast<char &>() = 'f';
ASSERT_EQ(view.find(1.f)->second.cast<char>(), 'f');
ASSERT_EQ(view.erase(1.), 1u);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, DenseSet) {
entt::dense_set<int> set{};
auto any = entt::forward_as_meta(set);
auto view = any.as_associative_container();
set.emplace(2);
set.emplace(3);
set.emplace(4);
ASSERT_TRUE(view);
ASSERT_TRUE(view.key_only());
ASSERT_EQ(view.key_type(), entt::resolve<int>());
ASSERT_EQ(view.mapped_type(), entt::meta_type{});
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.find(3)->first.cast<int>(), 3);
ASSERT_FALSE(view.insert(invalid_type{}));
ASSERT_TRUE(view.insert(.0));
ASSERT_TRUE(view.insert(1));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.find(0)->first.cast<int>(), 0);
ASSERT_EQ(view.find(1.)->first.cast<int>(), 1);
ASSERT_EQ(view.erase(invalid_type{}), 0u);
ASSERT_FALSE(view.find(invalid_type{}));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.erase(0), 1u);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(view.find(0), view.end());
ASSERT_EQ(view.find(1.f)->first.try_cast<int>(), nullptr);
ASSERT_NE(view.find(1.)->first.try_cast<const int>(), nullptr);
ASSERT_EQ(view.find(true)->first.cast<const int &>(), 1);
ASSERT_EQ(view.erase(1.), 1u);
ASSERT_TRUE(view.clear());
ASSERT_EQ(view.size(), 0u);
}
TEST_F(MetaContainer, ConstSequenceContainer) {
std::vector<int> vec{};
auto any = entt::forward_as_meta(std::as_const(vec));
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_FALSE(view.resize(3u));
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
vec.push_back(42);
ASSERT_EQ(view.size(), 1u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view[0].cast<const int &>(), 42);
auto it = view.begin();
auto ret = view.insert(it, 0);
ASSERT_FALSE(ret);
ASSERT_EQ(view.size(), 1u);
ASSERT_EQ(it->cast<int>(), 42);
ASSERT_EQ(++it, view.end());
it = view.begin();
ret = view.erase(it);
ASSERT_FALSE(ret);
ASSERT_EQ(view.size(), 1u);
ASSERT_FALSE(view.clear());
ASSERT_EQ(view.size(), 1u);
}
ENTT_DEBUG_TEST_F(MetaContainerDeathTest, ConstSequenceContainer) {
std::vector<int> vec{};
auto any = entt::forward_as_meta(std::as_const(vec));
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_DEATH(view[0].cast<int &>() = 2, "");
}
TEST_F(MetaContainer, ConstKeyValueAssociativeContainer) {
std::map<int, char> map{};
auto any = entt::forward_as_meta(std::as_const(map));
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_FALSE(view.key_only());
ASSERT_EQ(view.key_type(), entt::resolve<int>());
ASSERT_EQ(view.mapped_type(), entt::resolve<char>());
ASSERT_EQ(view.value_type(), (entt::resolve<std::pair<const int, char>>()));
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
map[2] = 'c';
ASSERT_EQ(view.size(), 1u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.find(2)->second.cast<const char &>(), 'c');
ASSERT_FALSE(view.insert(0, 'a'));
ASSERT_EQ(view.size(), 1u);
ASSERT_EQ(view.find(0), view.end());
ASSERT_EQ(view.find(2)->second.cast<char>(), 'c');
ASSERT_EQ(view.erase(2), 0u);
ASSERT_EQ(view.size(), 1u);
ASSERT_NE(view.find(2), view.end());
ASSERT_FALSE(view.clear());
ASSERT_EQ(view.size(), 1u);
}
ENTT_DEBUG_TEST_F(MetaContainerDeathTest, ConstKeyValueAssociativeContainer) {
std::map<int, char> map{};
auto any = entt::forward_as_meta(std::as_const(map));
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_DEATH(view.find(2)->second.cast<char &>() = 'a', "");
}
TEST_F(MetaContainer, ConstKeyOnlyAssociativeContainer) {
std::set<int> set{};
auto any = entt::forward_as_meta(std::as_const(set));
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_TRUE(view.key_only());
ASSERT_EQ(view.key_type(), entt::resolve<int>());
ASSERT_EQ(view.mapped_type(), entt::meta_type{});
ASSERT_EQ(view.value_type(), (entt::resolve<int>()));
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
set.insert(2);
ASSERT_EQ(view.size(), 1u);
ASSERT_NE(view.begin(), view.end());
ASSERT_EQ(view.find(2)->first.try_cast<int>(), nullptr);
ASSERT_NE(view.find(2)->first.try_cast<const int>(), nullptr);
ASSERT_EQ(view.find(2)->first.cast<int>(), 2);
ASSERT_EQ(view.find(2)->first.cast<const int &>(), 2);
ASSERT_FALSE(view.insert(0));
ASSERT_EQ(view.size(), 1u);
ASSERT_EQ(view.find(0), view.end());
ASSERT_EQ(view.find(2)->first.cast<int>(), 2);
ASSERT_EQ(view.erase(2), 0u);
ASSERT_EQ(view.size(), 1u);
ASSERT_NE(view.find(2), view.end());
ASSERT_FALSE(view.clear());
ASSERT_EQ(view.size(), 1u);
}
TEST_F(MetaContainer, SequenceContainerConstMetaAny) {
auto test = [](const entt::meta_any any) {
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<int>());
ASSERT_EQ(view[0].cast<const int &>(), 42);
};
std::vector<int> vec{42};
test(vec);
test(entt::forward_as_meta(vec));
test(entt::forward_as_meta(std::as_const(vec)));
}
ENTT_DEBUG_TEST_F(MetaContainerDeathTest, SequenceContainerConstMetaAny) {
auto test = [](const entt::meta_any any) {
auto view = any.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_DEATH(view[0].cast<int &>() = 2, "");
};
std::vector<int> vec{42};
test(vec);
test(entt::forward_as_meta(vec));
test(entt::forward_as_meta(std::as_const(vec)));
}
TEST_F(MetaContainer, KeyValueAssociativeContainerConstMetaAny) {
auto test = [](const entt::meta_any any) {
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), (entt::resolve<std::pair<const int, char>>()));
ASSERT_EQ(view.find(2)->second.cast<const char &>(), 'c');
};
std::map<int, char> map{{2, 'c'}};
test(map);
test(entt::forward_as_meta(map));
test(entt::forward_as_meta(std::as_const(map)));
}
ENTT_DEBUG_TEST_F(MetaContainerDeathTest, KeyValueAssociativeContainerConstMetaAny) {
auto test = [](const entt::meta_any any) {
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_DEATH(view.find(2)->second.cast<char &>() = 'a', "");
};
std::map<int, char> map{{2, 'c'}};
test(map);
test(entt::forward_as_meta(map));
test(entt::forward_as_meta(std::as_const(map)));
}
TEST_F(MetaContainer, KeyOnlyAssociativeContainerConstMetaAny) {
auto test = [](const entt::meta_any any) {
auto view = any.as_associative_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), (entt::resolve<int>()));
ASSERT_EQ(view.find(2)->first.try_cast<int>(), nullptr);
ASSERT_NE(view.find(2)->first.try_cast<const int>(), nullptr);
ASSERT_EQ(view.find(2)->first.cast<int>(), 2);
ASSERT_EQ(view.find(2)->first.cast<const int &>(), 2);
};
std::set<int> set{2};
test(set);
test(entt::forward_as_meta(set));
test(entt::forward_as_meta(std::as_const(set)));
}
TEST_F(MetaContainer, StdVectorBool) {
using proxy_type = typename std::vector<bool>::reference;
using const_proxy_type = typename std::vector<bool>::const_reference;
std::vector<bool> vec{};
auto any = entt::forward_as_meta(vec);
auto cany = std::as_const(any).as_ref();
auto view = any.as_sequence_container();
auto cview = cany.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<bool>());
ASSERT_EQ(view.size(), 0u);
ASSERT_EQ(view.begin(), view.end());
ASSERT_TRUE(view.resize(3u));
ASSERT_EQ(view.size(), 3u);
ASSERT_NE(view.begin(), view.end());
view[0].cast<proxy_type>() = true;
view[1].cast<proxy_type>() = true;
view[2].cast<proxy_type>() = false;
ASSERT_EQ(cview[1u].cast<const_proxy_type>(), true);
auto it = view.begin();
auto ret = view.insert(it, true);
ASSERT_TRUE(ret);
ASSERT_FALSE(view.insert(ret, invalid_type{}));
ASSERT_TRUE(view.insert(++ret, false));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(view.begin()->cast<proxy_type>(), true);
ASSERT_EQ((++cview.begin())->cast<const_proxy_type>(), false);
it = view.begin();
ret = view.erase(it);
ASSERT_TRUE(ret);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(ret->cast<proxy_type>(), false);
ASSERT_TRUE(view.clear());
ASSERT_EQ(cview.size(), 0u);
}

View File

@ -0,0 +1,505 @@
#include <unordered_map>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/container.hpp>
#include <entt/meta/context.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/pointer.hpp>
#include <entt/meta/template.hpp>
struct base {
base() = default;
base(char v)
: value{v} {}
char get() const {
return value;
}
char value;
};
struct clazz: base {
clazz() = default;
clazz(int v)
: base{},
value{v} {}
clazz(char c, int v)
: base{c},
value{v} {}
int func(int v) {
return (value = v);
}
int cfunc(int v) const {
return v;
}
static void move_to_bucket(const clazz &instance) {
bucket = instance.value;
}
int value{};
static inline int bucket{};
};
struct local_only {};
struct argument {
argument(int val)
: value{val} {}
int get() const {
return value;
}
int get_mul() const {
return value * 2;
}
private:
int value{};
};
template<typename...>
struct template_clazz {};
class MetaContext: public ::testing::Test {
void init_global_context() {
using namespace entt::literals;
entt::meta<int>()
.data<global_marker>("marker"_hs);
entt::meta<argument>()
.conv<&argument::get>();
entt::meta<clazz>()
.type("foo"_hs)
.prop("prop"_hs, prop_value)
.ctor<int>()
.data<&clazz::value>("value"_hs)
.data<&clazz::value>("rw"_hs)
.func<&clazz::func>("func"_hs);
entt::meta<template_clazz<int>>()
.type("template"_hs);
}
void init_local_context() {
using namespace entt::literals;
entt::meta<int>(context)
.data<local_marker>("marker"_hs);
entt::meta<local_only>(context)
.type("quux"_hs);
entt::meta<argument>(context)
.conv<&argument::get_mul>();
entt::meta<base>(context)
.data<&base::value>("char"_hs)
.func<&base::get>("get"_hs);
entt::meta<clazz>(context)
.type("bar"_hs)
.prop("prop"_hs, prop_value)
.base<base>()
.ctor<char, int>()
.dtor<&clazz::move_to_bucket>()
.data<nullptr, &clazz::value>("value"_hs)
.data<&clazz::value>("rw"_hs)
.func<&clazz::cfunc>("func"_hs);
entt::meta<template_clazz<int, char>>(context)
.type("template"_hs);
}
public:
void SetUp() override {
init_global_context();
init_local_context();
clazz::bucket = bucket_value;
}
void TearDown() override {
entt::meta_reset(context);
entt::meta_reset();
}
protected:
static constexpr int global_marker = 1;
static constexpr int local_marker = 42;
static constexpr int bucket_value = 99;
static constexpr int prop_value = 3;
entt::meta_ctx context{};
};
TEST_F(MetaContext, Resolve) {
using namespace entt::literals;
ASSERT_TRUE(entt::resolve<clazz>());
ASSERT_TRUE(entt::resolve<clazz>(context));
ASSERT_TRUE(entt::resolve<local_only>());
ASSERT_TRUE(entt::resolve<local_only>(context));
ASSERT_TRUE(entt::resolve(entt::type_id<clazz>()));
ASSERT_TRUE(entt::resolve(context, entt::type_id<clazz>()));
ASSERT_FALSE(entt::resolve(entt::type_id<local_only>()));
ASSERT_TRUE(entt::resolve(context, entt::type_id<local_only>()));
ASSERT_TRUE(entt::resolve("foo"_hs));
ASSERT_FALSE(entt::resolve(context, "foo"_hs));
ASSERT_FALSE(entt::resolve("bar"_hs));
ASSERT_TRUE(entt::resolve(context, "bar"_hs));
ASSERT_FALSE(entt::resolve("quux"_hs));
ASSERT_TRUE(entt::resolve(context, "quux"_hs));
ASSERT_EQ((std::distance(entt::resolve().cbegin(), entt::resolve().cend())), 4);
ASSERT_EQ((std::distance(entt::resolve(context).cbegin(), entt::resolve(context).cend())), 6);
}
TEST_F(MetaContext, MetaType) {
using namespace entt::literals;
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(context);
ASSERT_TRUE(global);
ASSERT_TRUE(local);
ASSERT_NE(global, local);
ASSERT_EQ(global, entt::resolve("foo"_hs));
ASSERT_EQ(local, entt::resolve(context, "bar"_hs));
ASSERT_EQ(global.id(), "foo"_hs);
ASSERT_EQ(local.id(), "bar"_hs);
clazz instance{'c', 99};
const argument value{2};
ASSERT_NE(instance.value, value.get());
ASSERT_EQ(global.invoke("func"_hs, instance, value).cast<int>(), value.get());
ASSERT_EQ(instance.value, value.get());
ASSERT_NE(instance.value, value.get_mul());
ASSERT_EQ(local.invoke("func"_hs, instance, value).cast<int>(), value.get_mul());
ASSERT_NE(instance.value, value.get_mul());
ASSERT_FALSE(global.invoke("get"_hs, instance));
ASSERT_EQ(local.invoke("get"_hs, instance).cast<char>(), 'c');
}
TEST_F(MetaContext, MetaBase) {
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(context);
ASSERT_EQ((std::distance(global.base().cbegin(), global.base().cend())), 0);
ASSERT_EQ((std::distance(local.base().cbegin(), local.base().cend())), 1);
ASSERT_EQ(local.base().cbegin()->second.info(), entt::type_id<base>());
ASSERT_FALSE(entt::resolve(entt::type_id<base>()));
ASSERT_TRUE(entt::resolve(context, entt::type_id<base>()));
}
TEST_F(MetaContext, MetaData) {
using namespace entt::literals;
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(context);
ASSERT_TRUE(global.data("value"_hs));
ASSERT_TRUE(local.data("value"_hs));
ASSERT_FALSE(global.data("value"_hs).is_const());
ASSERT_TRUE(local.data("value"_hs).is_const());
ASSERT_EQ(global.data("value"_hs).type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.data("value"_hs).type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ(global.data("rw"_hs).arg(0u).data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.data("rw"_hs).arg(0u).data("marker"_hs).get({}).cast<int>(), local_marker);
clazz instance{'c', 99};
const argument value{2};
ASSERT_NE(instance.value, value.get());
ASSERT_TRUE(global.data("rw"_hs).set(instance, value));
ASSERT_EQ(instance.value, value.get());
ASSERT_NE(instance.value, value.get_mul());
ASSERT_TRUE(local.data("rw"_hs).set(instance, value));
ASSERT_EQ(instance.value, value.get_mul());
ASSERT_FALSE(global.data("char"_hs));
ASSERT_EQ(local.data("char"_hs).get(instance).cast<char>(), 'c');
ASSERT_TRUE(local.data("char"_hs).set(instance, 'x'));
ASSERT_EQ(instance.base::value, 'x');
}
TEST_F(MetaContext, MetaFunc) {
using namespace entt::literals;
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(context);
ASSERT_TRUE(global.func("func"_hs));
ASSERT_TRUE(local.func("func"_hs));
ASSERT_FALSE(global.func("func"_hs).is_const());
ASSERT_TRUE(local.func("func"_hs).is_const());
ASSERT_EQ(global.func("func"_hs).arg(0u).data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.func("func"_hs).arg(0u).data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ(global.func("func"_hs).ret().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.func("func"_hs).ret().data("marker"_hs).get({}).cast<int>(), local_marker);
clazz instance{'c', 99};
const argument value{2};
ASSERT_NE(instance.value, value.get());
ASSERT_EQ(global.func("func"_hs).invoke(instance, value).cast<int>(), value.get());
ASSERT_EQ(instance.value, value.get());
ASSERT_NE(instance.value, value.get_mul());
ASSERT_EQ(local.func("func"_hs).invoke(instance, value).cast<int>(), value.get_mul());
ASSERT_NE(instance.value, value.get_mul());
ASSERT_FALSE(global.func("get"_hs));
ASSERT_EQ(local.func("get"_hs).invoke(instance).cast<char>(), 'c');
}
TEST_F(MetaContext, MetaCtor) {
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(context);
auto any = global.construct();
auto other = local.construct();
ASSERT_TRUE(any);
ASSERT_TRUE(other);
ASSERT_EQ(any.cast<const clazz &>().value, 0);
ASSERT_EQ(other.cast<const clazz &>().value, 0);
argument argument{2};
any = global.construct(argument);
other = local.construct(argument);
ASSERT_TRUE(any);
ASSERT_FALSE(other);
ASSERT_EQ(any.cast<const clazz &>().value, 2);
any = global.construct('c', argument);
other = local.construct('c', argument);
ASSERT_FALSE(any);
ASSERT_TRUE(other);
ASSERT_EQ(other.cast<const clazz &>().value, 4);
}
TEST_F(MetaContext, MetaConv) {
argument value{2};
auto global = entt::forward_as_meta(value);
auto local = entt::forward_as_meta(context, value);
ASSERT_TRUE(global.allow_cast<int>());
ASSERT_TRUE(local.allow_cast<int>());
ASSERT_EQ(global.cast<int>(), value.get());
ASSERT_EQ(local.cast<int>(), value.get_mul());
}
TEST_F(MetaContext, MetaDtor) {
auto global = entt::resolve<clazz>().construct();
auto local = entt::resolve<clazz>(context).construct();
ASSERT_EQ(clazz::bucket, bucket_value);
global.reset();
ASSERT_EQ(clazz::bucket, bucket_value);
local.reset();
ASSERT_NE(clazz::bucket, bucket_value);
}
TEST_F(MetaContext, MetaProp) {
using namespace entt::literals;
const auto global = entt::resolve<clazz>();
const auto local = entt::resolve<clazz>(context);
ASSERT_TRUE(global.prop("prop"_hs));
ASSERT_TRUE(local.prop("prop"_hs));
ASSERT_EQ(global.prop("prop"_hs).value().type(), entt::resolve<int>());
ASSERT_EQ(local.prop("prop"_hs).value().type(), entt::resolve<int>(context));
ASSERT_EQ(global.prop("prop"_hs).value().cast<int>(), prop_value);
ASSERT_EQ(local.prop("prop"_hs).value().cast<int>(), prop_value);
ASSERT_EQ(global.prop("prop"_hs).value().type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.prop("prop"_hs).value().type().data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, MetaTemplate) {
using namespace entt::literals;
const auto global = entt::resolve("template"_hs);
const auto local = entt::resolve(context, "template"_hs);
ASSERT_TRUE(global.is_template_specialization());
ASSERT_TRUE(local.is_template_specialization());
ASSERT_EQ(global.template_arity(), 1u);
ASSERT_EQ(local.template_arity(), 2u);
ASSERT_EQ(global.template_arg(0u), entt::resolve<int>());
ASSERT_EQ(local.template_arg(0u), entt::resolve<int>(context));
ASSERT_EQ(local.template_arg(1u), entt::resolve<char>(context));
ASSERT_EQ(global.template_arg(0u).data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.template_arg(0u).data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, MetaPointer) {
using namespace entt::literals;
int value = 42;
const entt::meta_any global{&value};
const entt::meta_any local{context, &value};
ASSERT_TRUE(global.type().is_pointer());
ASSERT_TRUE(local.type().is_pointer());
ASSERT_TRUE(global.type().is_pointer_like());
ASSERT_TRUE(local.type().is_pointer_like());
ASSERT_EQ((*global).type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ((*local).type().data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, MetaAssociativeContainer) {
using namespace entt::literals;
std::unordered_map<int, int> map{{{0, 0}}};
auto global = entt::forward_as_meta(map).as_associative_container();
auto local = entt::forward_as_meta(context, map).as_associative_container();
ASSERT_TRUE(global);
ASSERT_TRUE(local);
ASSERT_EQ(global.size(), 1u);
ASSERT_EQ(local.size(), 1u);
ASSERT_EQ(global.key_type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.key_type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ(global.mapped_type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.mapped_type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ((*global.begin()).first.type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ((*local.begin()).first.type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ((*global.begin()).second.type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ((*local.begin()).second.type().data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, MetaSequenceContainer) {
using namespace entt::literals;
std::vector<int> vec{0};
auto global = entt::forward_as_meta(vec).as_sequence_container();
auto local = entt::forward_as_meta(context, vec).as_sequence_container();
ASSERT_TRUE(global);
ASSERT_TRUE(local);
ASSERT_EQ(global.size(), 1u);
ASSERT_EQ(local.size(), 1u);
ASSERT_EQ(global.value_type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.value_type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ((*global.begin()).type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ((*local.begin()).type().data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, MetaAny) {
using namespace entt::literals;
entt::meta_any global{42};
entt::meta_any ctx_value{context, 42};
entt::meta_any in_place{context, std::in_place_type<int>, 42};
entt::meta_any two_step_local{entt::meta_ctx_arg, context};
ASSERT_TRUE(global);
ASSERT_TRUE(ctx_value);
ASSERT_TRUE(in_place);
ASSERT_FALSE(two_step_local);
two_step_local = 42;
ASSERT_TRUE(two_step_local);
ASSERT_EQ(global.type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(ctx_value.type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ(in_place.type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ(two_step_local.type().data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, MetaHandle) {
using namespace entt::literals;
int value = 42;
entt::meta_handle global{value};
entt::meta_handle ctx_value{context, value};
entt::meta_handle two_step_local{entt::meta_ctx_arg, context};
ASSERT_TRUE(global);
ASSERT_TRUE(ctx_value);
ASSERT_FALSE(two_step_local);
two_step_local->emplace<int &>(value);
ASSERT_TRUE(two_step_local);
ASSERT_EQ(global->type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(ctx_value->type().data("marker"_hs).get({}).cast<int>(), local_marker);
ASSERT_EQ(two_step_local->type().data("marker"_hs).get({}).cast<int>(), local_marker);
}
TEST_F(MetaContext, ForwardAsMeta) {
using namespace entt::literals;
const auto global = entt::forward_as_meta(42);
const auto local = entt::forward_as_meta(context, 42);
ASSERT_TRUE(global);
ASSERT_TRUE(local);
ASSERT_EQ(global.type().data("marker"_hs).get({}).cast<int>(), global_marker);
ASSERT_EQ(local.type().data("marker"_hs).get({}).cast<int>(), local_marker);
}

View File

@ -0,0 +1,71 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/node.hpp>
#include <entt/meta/resolve.hpp>
struct clazz_t {
clazz_t() = default;
operator int() const {
return value;
}
bool to_bool() const {
return (value != 0);
}
int value{};
};
double conv_to_double(const clazz_t &instance) {
return instance.value * 2.;
}
struct MetaConv: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<clazz_t>()
.type("clazz"_hs)
.conv<int>()
.conv<&clazz_t::to_bool>()
.conv<conv_to_double>();
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaConv, Functionalities) {
auto any = entt::resolve<clazz_t>().construct();
any.cast<clazz_t &>().value = 42;
const auto as_int = std::as_const(any).allow_cast<int>();
const auto as_bool = std::as_const(any).allow_cast<bool>();
const auto as_double = std::as_const(any).allow_cast<double>();
ASSERT_FALSE(any.allow_cast<char>());
ASSERT_TRUE(as_int);
ASSERT_TRUE(as_bool);
ASSERT_TRUE(as_double);
ASSERT_EQ(as_int.cast<int>(), any.cast<clazz_t &>().operator int());
ASSERT_EQ(as_bool.cast<bool>(), any.cast<clazz_t &>().to_bool());
ASSERT_EQ(as_double.cast<double>(), conv_to_double(any.cast<clazz_t &>()));
}
TEST_F(MetaConv, ReRegistration) {
SetUp();
auto &&node = entt::internal::resolve<clazz_t>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
ASSERT_TRUE(node.details);
ASSERT_FALSE(node.details->conv.empty());
ASSERT_EQ(node.details->conv.size(), 3u);
}

View File

@ -0,0 +1,215 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/utility.hpp>
#include <entt/entity/registry.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
struct base_t {
base_t()
: value{'c'} {}
char value;
};
struct derived_t: base_t {
derived_t()
: base_t{} {}
};
struct clazz_t {
clazz_t(const base_t &other, int &iv)
: clazz_t{iv, other.value} {}
clazz_t(const int &iv, char cv)
: i{iv}, c{cv} {}
operator int() const {
return i;
}
static clazz_t factory(int value) {
return {value, 'c'};
}
static clazz_t factory(base_t other, int value, int mul) {
return {value * mul, other.value};
}
int i{};
char c{};
};
double double_factory() {
return 42.;
}
struct MetaCtor: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<double>()
.type("double"_hs)
.ctor<double_factory>();
entt::meta<derived_t>()
.type("derived"_hs)
.base<base_t>();
entt::meta<clazz_t>()
.type("clazz"_hs)
.ctor<&entt::registry::emplace_or_replace<clazz_t, const int &, const char &>, entt::as_ref_t>()
.ctor<const base_t &, int &>()
.ctor<const int &, char>()
.ctor<entt::overload<clazz_t(int)>(clazz_t::factory)>()
.ctor<entt::overload<clazz_t(base_t, int, int)>(clazz_t::factory)>()
.conv<int>();
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaCtor, Functionalities) {
auto any = entt::resolve<clazz_t>().construct(42, 'c');
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, Func) {
auto any = entt::resolve<clazz_t>().construct(42);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, MetaAnyArgs) {
auto any = entt::resolve<clazz_t>().construct(entt::meta_any{42}, entt::meta_any{'c'});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, InvalidArgs) {
ASSERT_FALSE(entt::resolve<clazz_t>().construct(entt::meta_any{}, derived_t{}));
}
TEST_F(MetaCtor, CastAndConvert) {
auto any = entt::resolve<clazz_t>().construct(derived_t{}, clazz_t{42, 'd'});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, ArithmeticConversion) {
auto any = entt::resolve<clazz_t>().construct(true, 4.2);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 1);
ASSERT_EQ(any.cast<clazz_t>().c, char{4});
}
TEST_F(MetaCtor, ConstNonConstRefArgs) {
int ivalue = 42;
const char cvalue = 'c';
auto any = entt::resolve<clazz_t>().construct(entt::forward_as_meta(ivalue), entt::forward_as_meta(cvalue));
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, WrongConstness) {
int value = 42;
auto any = entt::resolve<clazz_t>().construct(derived_t{}, entt::forward_as_meta(value));
auto other = entt::resolve<clazz_t>().construct(derived_t{}, entt::forward_as_meta(std::as_const(value)));
ASSERT_TRUE(any);
ASSERT_FALSE(other);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, FuncMetaAnyArgs) {
auto any = entt::resolve<clazz_t>().construct(entt::meta_any{42});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, FuncCastAndConvert) {
auto any = entt::resolve<clazz_t>().construct(derived_t{}, 3., clazz_t{3, 'd'});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 9);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, FuncArithmeticConversion) {
auto any = entt::resolve<clazz_t>().construct(4.2);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().i, 4);
ASSERT_EQ(any.cast<clazz_t>().c, 'c');
}
TEST_F(MetaCtor, FuncConstNonConstRefArgs) {
int ivalue = 42;
auto any = entt::resolve<clazz_t>().construct(entt::forward_as_meta(ivalue));
auto other = entt::resolve<clazz_t>().construct(entt::forward_as_meta(std::as_const(ivalue)));
ASSERT_TRUE(any);
ASSERT_TRUE(other);
ASSERT_EQ(any.cast<clazz_t>().i, 42);
ASSERT_EQ(other.cast<clazz_t>().i, 42);
}
TEST_F(MetaCtor, ExternalMemberFunction) {
entt::registry registry;
const auto entity = registry.create();
ASSERT_FALSE(registry.all_of<clazz_t>(entity));
const auto any = entt::resolve<clazz_t>().construct(entt::forward_as_meta(registry), entity, 3, 'c');
ASSERT_TRUE(any);
ASSERT_TRUE(registry.all_of<clazz_t>(entity));
ASSERT_EQ(registry.get<clazz_t>(entity).i, 3);
ASSERT_EQ(registry.get<clazz_t>(entity).c, 'c');
}
TEST_F(MetaCtor, OverrideImplicitlyGeneratedDefaultConstructor) {
auto type = entt::resolve<double>();
auto any = type.construct();
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<double>());
ASSERT_EQ(any.cast<double>(), 42.);
}
TEST_F(MetaCtor, NonDefaultConstructibleType) {
auto type = entt::resolve<clazz_t>();
// no implicitly generated default constructor
ASSERT_FALSE(type.construct());
}
TEST_F(MetaCtor, ReRegistration) {
SetUp();
auto &&node = entt::internal::resolve<double>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
ASSERT_TRUE(node.details);
ASSERT_FALSE(node.details->ctor.empty());
// implicitly generated default constructor is not cleared
ASSERT_NE(node.default_constructor, nullptr);
}

View File

@ -0,0 +1,678 @@
#include <cstdlib>
#include <string>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/type_traits.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/node.hpp>
#include <entt/meta/resolve.hpp>
#include "../common/config.h"
struct base_t {
virtual ~base_t() = default;
static void destroy(base_t &) {
++counter;
}
inline static int counter = 0;
int value{3};
};
struct derived_t: base_t {
derived_t() {}
};
struct clazz_t {
clazz_t()
: i{0},
j{1},
base{} {}
operator int() const {
return h;
}
int i{0};
const int j{1};
base_t base{};
inline static int h{2};
inline static const int k{3};
};
struct setter_getter_t {
setter_getter_t()
: value{0} {}
int setter(double val) {
return value = static_cast<int>(val);
}
int getter() {
return value;
}
int setter_with_ref(const int &val) {
return value = val;
}
const int &getter_with_ref() {
return value;
}
static int static_setter(setter_getter_t &type, int value) {
return type.value = value;
}
static int static_getter(const setter_getter_t &type) {
return type.value;
}
int value;
};
struct multi_setter_t {
multi_setter_t()
: value{0} {}
void from_double(double val) {
value = val;
}
void from_string(const char *val) {
value = std::atoi(val);
}
int value;
};
struct array_t {
static inline int global[3];
int local[5];
};
enum class property_t : entt::id_type {
random,
value
};
struct MetaData: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<double>()
.type("double"_hs);
entt::meta<base_t>()
.type("base"_hs)
.dtor<base_t::destroy>()
.data<&base_t::value>("value"_hs);
entt::meta<derived_t>()
.type("derived"_hs)
.base<base_t>()
.dtor<derived_t::destroy>()
.data<&base_t::value>("value_from_base"_hs);
entt::meta<clazz_t>()
.type("clazz"_hs)
.data<&clazz_t::i, entt::as_ref_t>("i"_hs)
.prop(3u, 0)
.data<&clazz_t::i, entt::as_cref_t>("ci"_hs)
.data<&clazz_t::j>("j"_hs)
.prop("true"_hs, 1)
.data<&clazz_t::h>("h"_hs)
.prop(static_cast<entt::id_type>(property_t::random), 2)
.data<&clazz_t::k>("k"_hs)
.prop(static_cast<entt::id_type>(property_t::value), 3)
.data<&clazz_t::base>("base"_hs)
.data<&clazz_t::i, entt::as_void_t>("void"_hs)
.conv<int>();
entt::meta<setter_getter_t>()
.type("setter_getter"_hs)
.data<&setter_getter_t::static_setter, &setter_getter_t::static_getter>("x"_hs)
.data<&setter_getter_t::setter, &setter_getter_t::getter>("y"_hs)
.data<&setter_getter_t::static_setter, &setter_getter_t::getter>("z"_hs)
.data<&setter_getter_t::setter_with_ref, &setter_getter_t::getter_with_ref>("w"_hs)
.data<nullptr, &setter_getter_t::getter>("z_ro"_hs)
.data<nullptr, &setter_getter_t::value>("value"_hs);
entt::meta<multi_setter_t>()
.type("multi_setter"_hs)
.data<entt::value_list<&multi_setter_t::from_double, &multi_setter_t::from_string>, &multi_setter_t::value>("value"_hs);
entt::meta<array_t>()
.type("array"_hs)
.data<&array_t::global>("global"_hs)
.data<&array_t::local>("local"_hs);
base_t::counter = 0;
}
void TearDown() override {
entt::meta_reset();
}
};
using MetaDataDeathTest = MetaData;
TEST_F(MetaData, Functionalities) {
using namespace entt::literals;
auto data = entt::resolve<clazz_t>().data("i"_hs);
clazz_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_TRUE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 42);
for(auto curr: data.prop()) {
ASSERT_EQ(curr.first, 3u);
ASSERT_EQ(curr.second.value(), 0);
}
ASSERT_FALSE(data.prop(2));
ASSERT_FALSE(data.prop('c'));
auto prop = data.prop(3u);
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value(), 0);
}
TEST_F(MetaData, Const) {
using namespace entt::literals;
auto data = entt::resolve<clazz_t>().data("j"_hs);
clazz_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_TRUE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 1);
ASSERT_FALSE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 1);
for(auto curr: data.prop()) {
ASSERT_EQ(curr.first, "true"_hs);
ASSERT_EQ(curr.second.value(), 1);
}
ASSERT_FALSE(data.prop(false));
ASSERT_FALSE(data.prop('c'));
auto prop = data.prop("true"_hs);
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value(), 1);
}
TEST_F(MetaData, Static) {
using namespace entt::literals;
auto data = entt::resolve<clazz_t>().data("h"_hs);
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_FALSE(data.is_const());
ASSERT_TRUE(data.is_static());
ASSERT_EQ(data.get({}).cast<int>(), 2);
ASSERT_TRUE(data.set({}, 42));
ASSERT_EQ(data.get({}).cast<int>(), 42);
for(auto curr: data.prop()) {
ASSERT_EQ(curr.first, static_cast<entt::id_type>(property_t::random));
ASSERT_EQ(curr.second.value(), 2);
}
ASSERT_FALSE(data.prop(static_cast<entt::id_type>(property_t::value)));
ASSERT_FALSE(data.prop('c'));
auto prop = data.prop(static_cast<entt::id_type>(property_t::random));
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value(), 2);
}
TEST_F(MetaData, ConstStatic) {
using namespace entt::literals;
auto data = entt::resolve<clazz_t>().data("k"_hs);
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_TRUE(data.is_const());
ASSERT_TRUE(data.is_static());
ASSERT_EQ(data.get({}).cast<int>(), 3);
ASSERT_FALSE(data.set({}, 42));
ASSERT_EQ(data.get({}).cast<int>(), 3);
for(auto curr: data.prop()) {
ASSERT_EQ(curr.first, static_cast<entt::id_type>(property_t::value));
ASSERT_EQ(curr.second.value(), 3);
}
ASSERT_FALSE(data.prop(static_cast<entt::id_type>(property_t::random)));
ASSERT_FALSE(data.prop('c'));
auto prop = data.prop(static_cast<entt::id_type>(property_t::value));
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value(), 3);
}
TEST_F(MetaData, GetMetaAnyArg) {
using namespace entt::literals;
entt::meta_any any{clazz_t{}};
any.cast<clazz_t &>().i = 99;
const auto value = entt::resolve<clazz_t>().data("i"_hs).get(any);
ASSERT_TRUE(value);
ASSERT_TRUE(static_cast<bool>(value.cast<int>()));
ASSERT_EQ(value.cast<int>(), 99);
}
TEST_F(MetaData, GetInvalidArg) {
using namespace entt::literals;
auto instance = 0;
ASSERT_FALSE(entt::resolve<clazz_t>().data("i"_hs).get(instance));
}
TEST_F(MetaData, SetMetaAnyArg) {
using namespace entt::literals;
entt::meta_any any{clazz_t{}};
entt::meta_any value{42};
ASSERT_EQ(any.cast<clazz_t>().i, 0);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(any, value));
ASSERT_EQ(any.cast<clazz_t>().i, 42);
}
TEST_F(MetaData, SetInvalidArg) {
using namespace entt::literals;
ASSERT_FALSE(entt::resolve<clazz_t>().data("i"_hs).set({}, 'c'));
}
TEST_F(MetaData, SetCast) {
using namespace entt::literals;
clazz_t instance{};
ASSERT_EQ(base_t::counter, 0);
ASSERT_TRUE(entt::resolve<clazz_t>().data("base"_hs).set(instance, derived_t{}));
ASSERT_EQ(base_t::counter, 1);
}
TEST_F(MetaData, SetConvert) {
using namespace entt::literals;
clazz_t instance{};
instance.h = 42;
ASSERT_EQ(instance.i, 0);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(instance, instance));
ASSERT_EQ(instance.i, 42);
}
TEST_F(MetaData, SetByRef) {
using namespace entt::literals;
entt::meta_any any{clazz_t{}};
int value{42};
ASSERT_EQ(any.cast<clazz_t>().i, 0);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(any, entt::forward_as_meta(value)));
ASSERT_EQ(any.cast<clazz_t>().i, 42);
value = 3;
auto wrapper = entt::forward_as_meta(value);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(any, wrapper.as_ref()));
ASSERT_EQ(any.cast<clazz_t>().i, 3);
}
TEST_F(MetaData, SetByConstRef) {
using namespace entt::literals;
entt::meta_any any{clazz_t{}};
int value{42};
ASSERT_EQ(any.cast<clazz_t>().i, 0);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(any, entt::forward_as_meta(std::as_const(value))));
ASSERT_EQ(any.cast<clazz_t>().i, 42);
value = 3;
auto wrapper = entt::forward_as_meta(std::as_const(value));
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(any, wrapper.as_ref()));
ASSERT_EQ(any.cast<clazz_t>().i, 3);
}
TEST_F(MetaData, SetterGetterAsFreeFunctions) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter_t>().data("x"_hs);
setter_getter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_TRUE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 42);
}
TEST_F(MetaData, SetterGetterAsMemberFunctions) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter_t>().data("y"_hs);
setter_getter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<double>());
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_TRUE(data.set(instance, 42.));
ASSERT_EQ(data.get(instance).cast<int>(), 42);
ASSERT_TRUE(data.set(instance, 3));
ASSERT_EQ(data.get(instance).cast<int>(), 3);
}
TEST_F(MetaData, SetterGetterWithRefAsMemberFunctions) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter_t>().data("w"_hs);
setter_getter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_TRUE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 42);
}
TEST_F(MetaData, SetterGetterMixed) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter_t>().data("z"_hs);
setter_getter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_TRUE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 42);
}
TEST_F(MetaData, SetterGetterReadOnly) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter_t>().data("z_ro"_hs);
setter_getter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 0u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::meta_type{});
ASSERT_TRUE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_FALSE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 0);
}
TEST_F(MetaData, SetterGetterReadOnlyDataMember) {
using namespace entt::literals;
auto data = entt::resolve<setter_getter_t>().data("value"_hs);
setter_getter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 0u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::meta_type{});
ASSERT_TRUE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_FALSE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 0);
}
TEST_F(MetaData, MultiSetter) {
using namespace entt::literals;
auto data = entt::resolve<multi_setter_t>().data("value"_hs);
multi_setter_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 2u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<double>());
ASSERT_EQ(data.arg(1u), entt::resolve<const char *>());
ASSERT_EQ(data.arg(2u), entt::meta_type{});
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_TRUE(data.set(instance, 42));
ASSERT_EQ(data.get(instance).cast<int>(), 42);
ASSERT_TRUE(data.set(instance, 3.));
ASSERT_EQ(data.get(instance).cast<int>(), 3);
ASSERT_FALSE(data.set(instance, std::string{"99"}));
ASSERT_TRUE(data.set(instance, std::string{"99"}.c_str()));
ASSERT_EQ(data.get(instance).cast<int>(), 99);
}
TEST_F(MetaData, ConstInstance) {
using namespace entt::literals;
clazz_t instance{};
ASSERT_NE(entt::resolve<clazz_t>().data("i"_hs).get(instance).try_cast<int>(), nullptr);
ASSERT_NE(entt::resolve<clazz_t>().data("i"_hs).get(instance).try_cast<const int>(), nullptr);
ASSERT_EQ(entt::resolve<clazz_t>().data("i"_hs).get(std::as_const(instance)).try_cast<int>(), nullptr);
// as_ref_t adapts to the constness of the passed object and returns const references in case
ASSERT_NE(entt::resolve<clazz_t>().data("i"_hs).get(std::as_const(instance)).try_cast<const int>(), nullptr);
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).get(instance));
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).set(instance, 3));
ASSERT_TRUE(entt::resolve<clazz_t>().data("i"_hs).get(std::as_const(instance)));
ASSERT_FALSE(entt::resolve<clazz_t>().data("i"_hs).set(std::as_const(instance), 3));
ASSERT_TRUE(entt::resolve<clazz_t>().data("ci"_hs).get(instance));
ASSERT_TRUE(entt::resolve<clazz_t>().data("ci"_hs).set(instance, 3));
ASSERT_TRUE(entt::resolve<clazz_t>().data("ci"_hs).get(std::as_const(instance)));
ASSERT_FALSE(entt::resolve<clazz_t>().data("ci"_hs).set(std::as_const(instance), 3));
ASSERT_TRUE(entt::resolve<clazz_t>().data("j"_hs).get(instance));
ASSERT_FALSE(entt::resolve<clazz_t>().data("j"_hs).set(instance, 3));
ASSERT_TRUE(entt::resolve<clazz_t>().data("j"_hs).get(std::as_const(instance)));
ASSERT_FALSE(entt::resolve<clazz_t>().data("j"_hs).set(std::as_const(instance), 3));
}
TEST_F(MetaData, ArrayStatic) {
using namespace entt::literals;
auto data = entt::resolve<array_t>().data("global"_hs);
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int[3]>());
ASSERT_EQ(data.arg(0u), entt::resolve<int[3]>());
ASSERT_FALSE(data.is_const());
ASSERT_TRUE(data.is_static());
ASSERT_TRUE(data.type().is_array());
ASSERT_FALSE(data.get({}));
}
TEST_F(MetaData, Array) {
using namespace entt::literals;
auto data = entt::resolve<array_t>().data("local"_hs);
array_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int[5]>());
ASSERT_EQ(data.arg(0u), entt::resolve<int[5]>());
ASSERT_FALSE(data.is_const());
ASSERT_FALSE(data.is_static());
ASSERT_TRUE(data.type().is_array());
ASSERT_FALSE(data.get(instance));
}
TEST_F(MetaData, AsVoid) {
using namespace entt::literals;
auto data = entt::resolve<clazz_t>().data("void"_hs);
clazz_t instance{};
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_TRUE(data.set(instance, 42));
ASSERT_EQ(instance.i, 42);
ASSERT_EQ(data.get(instance), entt::meta_any{std::in_place_type<void>});
}
TEST_F(MetaData, AsRef) {
using namespace entt::literals;
clazz_t instance{};
auto data = entt::resolve<clazz_t>().data("i"_hs);
ASSERT_TRUE(data);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_NE(data.prop().cbegin(), data.prop().cend());
ASSERT_EQ(instance.i, 0);
data.get(instance).cast<int &>() = 3;
ASSERT_EQ(instance.i, 3);
}
TEST_F(MetaData, AsConstRef) {
using namespace entt::literals;
clazz_t instance{};
auto data = entt::resolve<clazz_t>().data("ci"_hs);
ASSERT_EQ(instance.i, 0);
ASSERT_EQ(data.arity(), 1u);
ASSERT_EQ(data.type(), entt::resolve<int>());
ASSERT_EQ(data.arg(0u), entt::resolve<int>());
ASSERT_EQ(data.get(instance).cast<const int &>(), 0);
ASSERT_EQ(data.get(instance).cast<int>(), 0);
ASSERT_EQ(data.prop().cbegin(), data.prop().cend());
ASSERT_EQ(instance.i, 0);
}
ENTT_DEBUG_TEST_F(MetaDataDeathTest, AsConstRef) {
using namespace entt::literals;
clazz_t instance{};
auto data = entt::resolve<clazz_t>().data("ci"_hs);
ASSERT_DEATH(data.get(instance).cast<int &>() = 3, "");
}
TEST_F(MetaData, SetGetBaseData) {
using namespace entt::literals;
auto type = entt::resolve<derived_t>();
derived_t instance{};
ASSERT_TRUE(type.data("value"_hs));
ASSERT_EQ(instance.value, 3);
ASSERT_TRUE(type.data("value"_hs).set(instance, 42));
ASSERT_EQ(type.data("value"_hs).get(instance).cast<int>(), 42);
ASSERT_EQ(instance.value, 42);
}
TEST_F(MetaData, SetGetFromBase) {
using namespace entt::literals;
auto type = entt::resolve<derived_t>();
derived_t instance{};
ASSERT_TRUE(type.data("value_from_base"_hs));
ASSERT_EQ(instance.value, 3);
ASSERT_TRUE(type.data("value_from_base"_hs).set(instance, 42));
ASSERT_EQ(type.data("value_from_base"_hs).get(instance).cast<int>(), 42);
ASSERT_EQ(instance.value, 42);
}
TEST_F(MetaData, ReRegistration) {
using namespace entt::literals;
SetUp();
auto &&node = entt::internal::resolve<base_t>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
auto type = entt::resolve<base_t>();
ASSERT_TRUE(node.details);
ASSERT_FALSE(node.details->data.empty());
ASSERT_EQ(node.details->data.size(), 1u);
ASSERT_TRUE(type.data("value"_hs));
entt::meta<base_t>().data<&base_t::value>("field"_hs);
ASSERT_TRUE(node.details);
ASSERT_EQ(node.details->data.size(), 2u);
ASSERT_TRUE(type.data("value"_hs));
ASSERT_TRUE(type.data("field"_hs));
}
TEST_F(MetaData, CollisionAndReuse) {
using namespace entt::literals;
ASSERT_TRUE(entt::resolve<clazz_t>().data("j"_hs));
ASSERT_FALSE(entt::resolve<clazz_t>().data("cj"_hs));
ASSERT_TRUE(entt::resolve<clazz_t>().data("j"_hs).is_const());
ASSERT_NO_FATAL_FAILURE(entt::meta<clazz_t>().data<&clazz_t::i>("j"_hs));
ASSERT_NO_FATAL_FAILURE(entt::meta<clazz_t>().data<&clazz_t::j>("cj"_hs));
ASSERT_TRUE(entt::resolve<clazz_t>().data("j"_hs));
ASSERT_TRUE(entt::resolve<clazz_t>().data("cj"_hs));
ASSERT_FALSE(entt::resolve<clazz_t>().data("j"_hs).is_const());
}

View File

@ -0,0 +1,113 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/node.hpp>
#include <entt/meta/resolve.hpp>
struct clazz_t {
clazz_t() {
++counter;
}
static void destroy_decr(clazz_t &) {
--counter;
}
void destroy_incr() const {
++counter;
}
inline static int counter = 0;
};
struct MetaDtor: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<clazz_t>()
.type("clazz"_hs)
.dtor<clazz_t::destroy_decr>();
clazz_t::counter = 0;
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaDtor, Functionalities) {
ASSERT_EQ(clazz_t::counter, 0);
auto any = entt::resolve<clazz_t>().construct();
auto cref = std::as_const(any).as_ref();
auto ref = any.as_ref();
ASSERT_TRUE(any);
ASSERT_TRUE(cref);
ASSERT_TRUE(ref);
ASSERT_EQ(clazz_t::counter, 1);
cref.reset();
ref.reset();
ASSERT_TRUE(any);
ASSERT_FALSE(cref);
ASSERT_FALSE(ref);
ASSERT_EQ(clazz_t::counter, 1);
any.reset();
ASSERT_FALSE(any);
ASSERT_FALSE(cref);
ASSERT_FALSE(ref);
ASSERT_EQ(clazz_t::counter, 0);
}
TEST_F(MetaDtor, AsRefConstruction) {
ASSERT_EQ(clazz_t::counter, 0);
clazz_t instance{};
auto any = entt::forward_as_meta(instance);
auto cany = entt::forward_as_meta(std::as_const(instance));
auto cref = cany.as_ref();
auto ref = any.as_ref();
ASSERT_TRUE(any);
ASSERT_TRUE(cany);
ASSERT_TRUE(cref);
ASSERT_TRUE(ref);
ASSERT_EQ(clazz_t::counter, 1);
any.reset();
cany.reset();
cref.reset();
ref.reset();
ASSERT_FALSE(any);
ASSERT_FALSE(cany);
ASSERT_FALSE(cref);
ASSERT_FALSE(ref);
ASSERT_EQ(clazz_t::counter, 1);
}
TEST_F(MetaDtor, ReRegistration) {
SetUp();
auto &&node = entt::internal::resolve<clazz_t>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
ASSERT_NE(node.dtor.dtor, nullptr);
entt::meta<clazz_t>().dtor<&clazz_t::destroy_incr>();
entt::resolve<clazz_t>().construct().reset();
ASSERT_EQ(clazz_t::counter, 2);
}

View File

@ -0,0 +1,608 @@
#include <cstddef>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/utility.hpp>
#include <entt/entity/registry.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include "../common/config.h"
struct base_t {
base_t() {}
virtual ~base_t() = default;
static void destroy(base_t &) {
++counter;
}
void setter(int v) {
value = v;
}
int getter() const {
return value;
}
static void static_setter(base_t &ref, int v) {
ref.value = v;
}
inline static int counter = 0;
int value{3};
};
void fake_member(base_t &instance, int value) {
instance.value = value;
}
int fake_const_member(const base_t &instance) {
return instance.value;
}
struct derived_t: base_t {
derived_t()
: base_t{} {}
};
struct func_t {
int f(const base_t &, int a, int b) {
return f(a, b);
}
int f(int a, int b) {
value = a;
return b * b;
}
int f(int v) const {
return v * v;
}
void g(int v) {
value = v * v;
}
static int h(int &v) {
return (v *= value);
}
static void k(int v) {
value = v;
}
int v(int v) const {
return (value = v);
}
int &a() const {
return value;
}
operator int() const {
return value;
}
inline static int value = 0;
};
struct MetaFunc: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<double>()
.type("double"_hs);
entt::meta<base_t>()
.type("base"_hs)
.dtor<base_t::destroy>()
.func<&base_t::setter>("setter"_hs)
.func<fake_member>("fake_member"_hs)
.func<fake_const_member>("fake_const_member"_hs);
entt::meta<derived_t>()
.type("derived"_hs)
.base<base_t>()
.func<&base_t::setter>("setter_from_base"_hs)
.func<&base_t::getter>("getter_from_base"_hs)
.func<&base_t::static_setter>("static_setter_from_base"_hs)
.dtor<derived_t::destroy>();
entt::meta<func_t>()
.type("func"_hs)
.func<&entt::registry::emplace_or_replace<func_t>>("emplace"_hs)
.func<entt::overload<int(const base_t &, int, int)>(&func_t::f)>("f3"_hs)
.func<entt::overload<int(int, int)>(&func_t::f)>("f2"_hs)
.prop("true"_hs, false)
.func<entt::overload<int(int) const>(&func_t::f)>("f1"_hs)
.prop("true"_hs, false)
.func<&func_t::g>("g"_hs)
.prop("true"_hs, false)
.func<func_t::h>("h"_hs)
.prop("true"_hs, false)
.func<func_t::k>("k"_hs)
.prop("true"_hs, false)
.func<&func_t::v, entt::as_void_t>("v"_hs)
.func<&func_t::a, entt::as_ref_t>("a"_hs)
.func<&func_t::a, entt::as_cref_t>("ca"_hs)
.conv<int>();
base_t::counter = 0;
}
void TearDown() override {
entt::meta_reset();
}
std::size_t reset_and_check() {
std::size_t count = 0;
for(auto func: entt::resolve<func_t>().func()) {
for(auto curr = func.second; curr; curr = curr.next()) {
++count;
}
}
SetUp();
for(auto func: entt::resolve<func_t>().func()) {
for(auto curr = func.second; curr; curr = curr.next()) {
--count;
}
}
return count;
};
};
using MetaFuncDeathTest = MetaFunc;
TEST_F(MetaFunc, Functionalities) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("f2"_hs);
func_t instance{};
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 2u);
ASSERT_FALSE(func.is_const());
ASSERT_FALSE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<int>());
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
ASSERT_EQ(func.arg(1u), entt::resolve<int>());
ASSERT_FALSE(func.arg(2u));
auto any = func.invoke(instance, 3, 2);
auto empty = func.invoke(instance);
ASSERT_FALSE(empty);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 4);
ASSERT_EQ(func_t::value, 3);
for(auto curr: func.prop()) {
ASSERT_EQ(curr.first, "true"_hs);
ASSERT_FALSE(curr.second.value().template cast<bool>());
}
ASSERT_FALSE(func.prop(false));
ASSERT_FALSE(func.prop('c'));
auto prop = func.prop("true"_hs);
ASSERT_TRUE(prop);
ASSERT_FALSE(prop.value().cast<bool>());
}
TEST_F(MetaFunc, Const) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("f1"_hs);
func_t instance{};
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_TRUE(func.is_const());
ASSERT_FALSE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<int>());
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
ASSERT_FALSE(func.arg(1u));
auto any = func.invoke(instance, 4);
auto empty = func.invoke(instance, derived_t{});
ASSERT_FALSE(empty);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 16);
for(auto curr: func.prop()) {
ASSERT_EQ(curr.first, "true"_hs);
ASSERT_FALSE(curr.second.value().template cast<bool>());
}
ASSERT_FALSE(func.prop(false));
ASSERT_FALSE(func.prop('c'));
auto prop = func.prop("true"_hs);
ASSERT_TRUE(prop);
ASSERT_FALSE(prop.value().cast<bool>());
}
TEST_F(MetaFunc, RetVoid) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("g"_hs);
func_t instance{};
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_FALSE(func.is_const());
ASSERT_FALSE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<void>());
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
ASSERT_FALSE(func.arg(1u));
auto any = func.invoke(instance, 5);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<void>());
ASSERT_EQ(func_t::value, 25);
for(auto curr: func.prop()) {
ASSERT_EQ(curr.first, "true"_hs);
ASSERT_FALSE(curr.second.value().template cast<bool>());
}
ASSERT_FALSE(func.prop(false));
ASSERT_FALSE(func.prop('c'));
auto prop = func.prop("true"_hs);
ASSERT_TRUE(prop);
ASSERT_FALSE(prop.value().cast<bool>());
}
TEST_F(MetaFunc, Static) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("h"_hs);
func_t::value = 2;
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_FALSE(func.is_const());
ASSERT_TRUE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<int>());
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
ASSERT_FALSE(func.arg(1u));
auto any = func.invoke({}, 3);
auto empty = func.invoke({}, derived_t{});
ASSERT_FALSE(empty);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 6);
for(auto curr: func.prop()) {
ASSERT_EQ(curr.first, "true"_hs);
ASSERT_FALSE(curr.second.value().template cast<bool>());
}
ASSERT_FALSE(func.prop(false));
ASSERT_FALSE(func.prop('c'));
auto prop = func.prop("true"_hs);
ASSERT_TRUE(prop);
ASSERT_FALSE(prop.value().cast<bool>());
}
TEST_F(MetaFunc, StaticRetVoid) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("k"_hs);
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_FALSE(func.is_const());
ASSERT_TRUE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<void>());
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
ASSERT_FALSE(func.arg(1u));
auto any = func.invoke({}, 42);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<void>());
ASSERT_EQ(func_t::value, 42);
for(auto curr: func.prop()) {
ASSERT_EQ(curr.first, "true"_hs);
ASSERT_FALSE(curr.second.value().template cast<bool>());
}
ASSERT_FALSE(func.prop(false));
ASSERT_FALSE(func.prop('c'));
auto prop = func.prop("true"_hs);
ASSERT_TRUE(prop);
ASSERT_FALSE(prop.value().cast<bool>());
}
TEST_F(MetaFunc, StaticAsMember) {
using namespace entt::literals;
base_t instance{};
auto func = entt::resolve<base_t>().func("fake_member"_hs);
auto any = func.invoke(instance, 42);
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 1u);
ASSERT_FALSE(func.is_const());
ASSERT_FALSE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<void>());
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
ASSERT_FALSE(func.arg(1u));
ASSERT_EQ(func.prop().cbegin(), func.prop().cend());
ASSERT_FALSE(func.invoke({}, 42));
ASSERT_FALSE(func.invoke(std::as_const(instance), 42));
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<void>());
ASSERT_EQ(instance.value, 42);
}
TEST_F(MetaFunc, StaticAsConstMember) {
using namespace entt::literals;
base_t instance{};
auto func = entt::resolve<base_t>().func("fake_const_member"_hs);
auto any = func.invoke(std::as_const(instance));
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 0u);
ASSERT_TRUE(func.is_const());
ASSERT_FALSE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<int>());
ASSERT_FALSE(func.arg(0u));
ASSERT_EQ(func.prop().cbegin(), func.prop().cend());
ASSERT_FALSE(func.invoke({}));
ASSERT_TRUE(func.invoke(instance));
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 3);
}
TEST_F(MetaFunc, MetaAnyArgs) {
using namespace entt::literals;
func_t instance;
auto any = entt::resolve<func_t>().func("f1"_hs).invoke(instance, 3);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 9);
}
TEST_F(MetaFunc, InvalidArgs) {
using namespace entt::literals;
int value = 3;
ASSERT_FALSE(entt::resolve<func_t>().func("f1"_hs).invoke(value, 'c'));
}
TEST_F(MetaFunc, CastAndConvert) {
using namespace entt::literals;
func_t instance;
instance.value = 3;
auto any = entt::resolve<func_t>().func("f3"_hs).invoke(instance, derived_t{}, 0, instance);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 9);
ASSERT_EQ(instance.value, 0);
}
TEST_F(MetaFunc, ArithmeticConversion) {
using namespace entt::literals;
func_t instance;
auto any = entt::resolve<func_t>().func("f2"_hs).invoke(instance, true, 4.2);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 16);
ASSERT_EQ(instance.value, 1);
}
TEST_F(MetaFunc, ArgsByRef) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("h"_hs);
func_t::value = 2;
entt::meta_any any{3};
int value = 4;
ASSERT_EQ(func.invoke({}, entt::forward_as_meta(value)).cast<int>(), 8);
ASSERT_EQ(func.invoke({}, any.as_ref()).cast<int>(), 6);
ASSERT_EQ(any.cast<int>(), 6);
ASSERT_EQ(value, 8);
}
TEST_F(MetaFunc, ArgsByConstRef) {
using namespace entt::literals;
func_t instance{};
auto func = entt::resolve<func_t>().func("g"_hs);
entt::meta_any any{2};
int value = 3;
ASSERT_TRUE(func.invoke(instance, entt::forward_as_meta(std::as_const(value))));
ASSERT_EQ(func_t::value, 9);
ASSERT_TRUE(func.invoke(instance, std::as_const(any).as_ref()));
ASSERT_EQ(func_t::value, 4);
}
TEST_F(MetaFunc, ConstInstance) {
using namespace entt::literals;
func_t instance{};
auto any = entt::resolve<func_t>().func("f1"_hs).invoke(std::as_const(instance), 2);
ASSERT_FALSE(entt::resolve<func_t>().func("g"_hs).invoke(std::as_const(instance), 42));
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<int>(), 4);
}
TEST_F(MetaFunc, AsVoid) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("v"_hs);
func_t instance{};
ASSERT_EQ(func.invoke(instance, 42), entt::meta_any{std::in_place_type<void>});
ASSERT_EQ(func.ret(), entt::resolve<void>());
ASSERT_EQ(instance.value, 42);
}
TEST_F(MetaFunc, AsRef) {
using namespace entt::literals;
func_t instance{};
auto func = entt::resolve<func_t>().func("a"_hs);
func.invoke(instance).cast<int &>() = 3;
ASSERT_EQ(func.ret(), entt::resolve<int>());
ASSERT_EQ(instance.value, 3);
}
TEST_F(MetaFunc, AsConstRef) {
using namespace entt::literals;
func_t instance{};
auto func = entt::resolve<func_t>().func("ca"_hs);
ASSERT_EQ(func.ret(), entt::resolve<int>());
ASSERT_EQ(func.invoke(instance).cast<const int &>(), 3);
ASSERT_EQ(func.invoke(instance).cast<int>(), 3);
}
ENTT_DEBUG_TEST_F(MetaFuncDeathTest, AsConstRef) {
using namespace entt::literals;
func_t instance{};
auto func = entt::resolve<func_t>().func("ca"_hs);
ASSERT_DEATH((func.invoke(instance).cast<int &>() = 3), "");
}
TEST_F(MetaFunc, InvokeBaseFunction) {
using namespace entt::literals;
auto type = entt::resolve<derived_t>();
derived_t instance{};
ASSERT_TRUE(type.func("setter"_hs));
ASSERT_EQ(instance.value, 3);
type.func("setter"_hs).invoke(instance, 42);
ASSERT_EQ(instance.value, 42);
}
TEST_F(MetaFunc, InvokeFromBase) {
using namespace entt::literals;
auto type = entt::resolve<derived_t>();
derived_t instance{};
auto setter_from_base = type.func("setter_from_base"_hs);
ASSERT_TRUE(setter_from_base);
ASSERT_EQ(instance.value, 3);
setter_from_base.invoke(instance, 42);
ASSERT_EQ(instance.value, 42);
auto getter_from_base = type.func("getter_from_base"_hs);
ASSERT_TRUE(getter_from_base);
ASSERT_EQ(getter_from_base.invoke(instance).cast<int>(), 42);
auto static_setter_from_base = type.func("static_setter_from_base"_hs);
ASSERT_TRUE(static_setter_from_base);
ASSERT_EQ(instance.value, 42);
static_setter_from_base.invoke(instance, 3);
ASSERT_EQ(instance.value, 3);
}
TEST_F(MetaFunc, ExternalMemberFunction) {
using namespace entt::literals;
auto func = entt::resolve<func_t>().func("emplace"_hs);
ASSERT_TRUE(func);
ASSERT_EQ(func.arity(), 2u);
ASSERT_FALSE(func.is_const());
ASSERT_TRUE(func.is_static());
ASSERT_EQ(func.ret(), entt::resolve<void>());
ASSERT_EQ(func.arg(0u), entt::resolve<entt::registry>());
ASSERT_EQ(func.arg(1u), entt::resolve<entt::entity>());
ASSERT_FALSE(func.arg(2u));
entt::registry registry;
const auto entity = registry.create();
ASSERT_FALSE(registry.all_of<func_t>(entity));
func.invoke({}, entt::forward_as_meta(registry), entity);
ASSERT_TRUE(registry.all_of<func_t>(entity));
}
TEST_F(MetaFunc, ReRegistration) {
using namespace entt::literals;
ASSERT_EQ(reset_and_check(), 0u);
func_t instance{};
auto type = entt::resolve<func_t>();
ASSERT_TRUE(type.func("f2"_hs));
ASSERT_FALSE(type.invoke("f2"_hs, instance, 0));
ASSERT_TRUE(type.invoke("f2"_hs, instance, 0, 0));
ASSERT_TRUE(type.func("f1"_hs));
ASSERT_TRUE(type.invoke("f1"_hs, instance, 0));
ASSERT_FALSE(type.invoke("f1"_hs, instance, 0, 0));
entt::meta<func_t>()
.func<entt::overload<int(int, int)>(&func_t::f)>("f"_hs)
.func<entt::overload<int(int) const>(&func_t::f)>("f"_hs);
ASSERT_TRUE(type.func("f1"_hs));
ASSERT_TRUE(type.func("f2"_hs));
ASSERT_TRUE(type.func("f"_hs));
ASSERT_TRUE(type.invoke("f"_hs, instance, 0));
ASSERT_TRUE(type.invoke("f"_hs, instance, 0, 0));
ASSERT_EQ(reset_and_check(), 0u);
}

View File

@ -0,0 +1,66 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
struct clazz_t {
clazz_t()
: value{} {}
void incr() {
++value;
}
void decr() {
--value;
}
int value;
};
struct MetaHandle: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<clazz_t>()
.type("clazz"_hs)
.func<&clazz_t::incr>("incr"_hs)
.func<&clazz_t::decr>("decr"_hs);
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaHandle, Functionalities) {
using namespace entt::literals;
clazz_t instance{};
entt::meta_handle handle{};
entt::meta_handle chandle{};
ASSERT_FALSE(handle);
ASSERT_FALSE(chandle);
handle = entt::meta_handle{instance};
chandle = entt::meta_handle{std::as_const(instance)};
ASSERT_TRUE(handle);
ASSERT_TRUE(chandle);
ASSERT_TRUE(handle->invoke("incr"_hs));
ASSERT_FALSE(chandle->invoke("incr"_hs));
ASSERT_FALSE(std::as_const(handle)->invoke("incr"_hs));
ASSERT_EQ(instance.value, 1);
auto any = entt::forward_as_meta(instance);
handle = entt::meta_handle{any};
chandle = entt::meta_handle{std::as_const(any)};
ASSERT_TRUE(handle->invoke("decr"_hs));
ASSERT_FALSE(chandle->invoke("decr"_hs));
ASSERT_FALSE(std::as_const(handle)->invoke("decr"_hs));
ASSERT_EQ(instance.value, 0);
}

View File

@ -0,0 +1,414 @@
#include <memory>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/meta/meta.hpp>
#include <entt/meta/pointer.hpp>
#include <entt/meta/resolve.hpp>
#include "../common/config.h"
template<typename Type>
struct wrapped_shared_ptr {
wrapped_shared_ptr(Type init)
: ptr{new Type{init}} {}
Type &deref() const {
return *ptr;
}
private:
std::shared_ptr<Type> ptr;
};
struct self_ptr {
using element_type = self_ptr;
self_ptr(int v)
: value{v} {}
const self_ptr &operator*() const {
return *this;
}
int value;
};
struct proxy_ptr {
using element_type = proxy_ptr;
proxy_ptr(int &v)
: value{&v} {}
proxy_ptr operator*() const {
return *this;
}
int *value;
};
template<typename Type>
struct adl_wrapped_shared_ptr: wrapped_shared_ptr<Type> {};
template<typename Type>
struct spec_wrapped_shared_ptr: wrapped_shared_ptr<Type> {};
template<typename Type>
struct entt::is_meta_pointer_like<adl_wrapped_shared_ptr<Type>>: std::true_type {};
template<typename Type>
struct entt::is_meta_pointer_like<spec_wrapped_shared_ptr<Type>>: std::true_type {};
template<>
struct entt::is_meta_pointer_like<self_ptr>: std::true_type {};
template<>
struct entt::is_meta_pointer_like<proxy_ptr>: std::true_type {};
template<typename Type>
struct entt::adl_meta_pointer_like<spec_wrapped_shared_ptr<Type>> {
static decltype(auto) dereference(const spec_wrapped_shared_ptr<Type> &ptr) {
return ptr.deref();
}
};
template<typename Type>
Type &dereference_meta_pointer_like(const adl_wrapped_shared_ptr<Type> &ptr) {
return ptr.deref();
}
int test_function() {
return 42;
}
struct not_copyable_t {
not_copyable_t() = default;
not_copyable_t(const not_copyable_t &) = delete;
not_copyable_t(not_copyable_t &&) = default;
not_copyable_t &operator=(const not_copyable_t &) = delete;
not_copyable_t &operator=(not_copyable_t &&) = default;
};
TEST(MetaPointerLike, DereferenceOperatorInvalidType) {
int value = 0;
entt::meta_any any{value};
ASSERT_FALSE(any.type().is_pointer());
ASSERT_FALSE(any.type().is_pointer_like());
ASSERT_EQ(any.type(), entt::resolve<int>());
auto deref = *any;
ASSERT_FALSE(deref);
}
TEST(MetaPointerLike, DereferenceOperatorConstType) {
const int value = 42;
entt::meta_any any{&value};
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(any.type(), entt::resolve<const int *>());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
ASSERT_EQ(deref.try_cast<int>(), nullptr);
ASSERT_EQ(deref.try_cast<const int>(), &value);
ASSERT_EQ(deref.cast<const int &>(), 42);
}
ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferenceOperatorConstType) {
const int value = 42;
entt::meta_any any{&value};
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_DEATH(deref.cast<int &>() = 0, "");
}
TEST(MetaPointerLike, DereferenceOperatorConstAnyNonConstType) {
int value = 42;
const entt::meta_any any{&value};
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
ASSERT_NE(deref.try_cast<int>(), nullptr);
ASSERT_NE(deref.try_cast<const int>(), nullptr);
ASSERT_EQ(deref.cast<int &>(), 42);
ASSERT_EQ(deref.cast<const int &>(), 42);
}
TEST(MetaPointerLike, DereferenceOperatorConstAnyConstType) {
const int value = 42;
const entt::meta_any any{&value};
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
ASSERT_EQ(deref.try_cast<int>(), nullptr);
ASSERT_NE(deref.try_cast<const int>(), nullptr);
ASSERT_EQ(deref.cast<const int &>(), 42);
}
ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferenceOperatorConstAnyConstType) {
const int value = 42;
const entt::meta_any any{&value};
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_DEATH(deref.cast<int &>() = 0, "");
}
TEST(MetaPointerLike, DereferenceOperatorRawPointer) {
int value = 0;
entt::meta_any any{&value};
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(any.type(), entt::resolve<int *>());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
deref.cast<int &>() = 42;
ASSERT_EQ(*any.cast<int *>(), 42);
ASSERT_EQ(value, 42);
}
TEST(MetaPointerLike, DereferenceOperatorSmartPointer) {
auto value = std::make_shared<int>(0);
entt::meta_any any{value};
ASSERT_FALSE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(any.type(), entt::resolve<std::shared_ptr<int>>());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
deref.cast<int &>() = 42;
ASSERT_EQ(*any.cast<std::shared_ptr<int>>(), 42);
ASSERT_EQ(*value, 42);
}
TEST(MetaPointerLike, PointerToConstMoveOnlyType) {
const not_copyable_t instance;
entt::meta_any any{&instance};
auto deref = *any;
ASSERT_TRUE(any);
ASSERT_TRUE(deref);
ASSERT_EQ(deref.try_cast<not_copyable_t>(), nullptr);
ASSERT_NE(deref.try_cast<const not_copyable_t>(), nullptr);
ASSERT_EQ(&deref.cast<const not_copyable_t &>(), &instance);
}
TEST(MetaPointerLike, AsRef) {
int value = 0;
int *ptr = &value;
entt::meta_any any{entt::forward_as_meta(ptr)};
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(any.type(), entt::resolve<int *>());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
deref.cast<int &>() = 42;
ASSERT_EQ(*any.cast<int *>(), 42);
ASSERT_EQ(value, 42);
}
TEST(MetaPointerLike, AsConstRef) {
int value = 42;
int *const ptr = &value;
entt::meta_any any{entt::forward_as_meta(ptr)};
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(any.type(), entt::resolve<int *>());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
deref.cast<int &>() = 42;
ASSERT_EQ(*any.cast<int *>(), 42);
ASSERT_EQ(value, 42);
}
TEST(MetaPointerLike, DereferenceOverload) {
auto test = [](entt::meta_any any) {
ASSERT_FALSE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
ASSERT_EQ(deref.cast<int &>(), 42);
ASSERT_EQ(deref.cast<const int &>(), 42);
};
test(adl_wrapped_shared_ptr<int>{42});
test(spec_wrapped_shared_ptr<int>{42});
}
TEST(MetaPointerLike, DereferencePointerToConstOverload) {
auto test = [](entt::meta_any any) {
ASSERT_FALSE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_FALSE(deref.type().is_pointer());
ASSERT_FALSE(deref.type().is_pointer_like());
ASSERT_EQ(deref.type(), entt::resolve<int>());
ASSERT_EQ(deref.cast<const int &>(), 42);
};
test(adl_wrapped_shared_ptr<const int>{42});
test(spec_wrapped_shared_ptr<const int>{42});
}
ENTT_DEBUG_TEST(MetaPointerLikeDeathTest, DereferencePointerToConstOverload) {
auto test = [](entt::meta_any any) {
auto deref = *any;
ASSERT_TRUE(deref);
ASSERT_DEATH(deref.cast<int &>() = 42, "");
};
test(adl_wrapped_shared_ptr<const int>{42});
test(spec_wrapped_shared_ptr<const int>{42});
}
TEST(MetaPointerLike, DereferencePointerToVoid) {
auto test = [](entt::meta_any any) {
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
auto deref = *any;
ASSERT_FALSE(deref);
};
test(static_cast<void *>(nullptr));
test(static_cast<const void *>(nullptr));
}
TEST(MetaPointerLike, DereferenceSmartPointerToVoid) {
auto test = [](entt::meta_any any) {
ASSERT_TRUE(any.type().is_class());
ASSERT_FALSE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
auto deref = *any;
ASSERT_FALSE(deref);
};
test(std::shared_ptr<void>{});
test(std::unique_ptr<void, void (*)(void *)>{nullptr, nullptr});
}
TEST(MetaPointerLike, DereferencePointerToFunction) {
auto test = [](entt::meta_any any) {
ASSERT_TRUE(any.type().is_pointer());
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_NE(any.try_cast<int (*)()>(), nullptr);
ASSERT_EQ(any.cast<int (*)()>()(), 42);
};
entt::meta_any func{&test_function};
test(func);
test(*func);
test(**func);
test(*std::as_const(func));
}
TEST(MetaPointerLike, DereferenceSelfPointer) {
self_ptr obj{42};
entt::meta_any any{entt::forward_as_meta(obj)};
entt::meta_any deref = *any;
ASSERT_TRUE(deref);
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(deref.cast<const self_ptr &>().value, obj.value);
ASSERT_FALSE(deref.try_cast<self_ptr>());
}
TEST(MetaPointerLike, DereferenceProxyPointer) {
int value = 3;
proxy_ptr obj{value};
entt::meta_any any{obj};
entt::meta_any deref = *any;
ASSERT_TRUE(deref);
ASSERT_TRUE(any.type().is_pointer_like());
ASSERT_EQ(*deref.cast<const proxy_ptr &>().value, value);
ASSERT_TRUE(deref.try_cast<proxy_ptr>());
*deref.cast<proxy_ptr &>().value = 42;
ASSERT_EQ(value, 42);
}
TEST(MetaPointerLike, DereferenceArray) {
entt::meta_any array{std::in_place_type<int[3]>};
entt::meta_any array_of_array{std::in_place_type<int[3][3]>};
ASSERT_EQ(array.type(), entt::resolve<int[3]>());
ASSERT_EQ(array_of_array.type(), entt::resolve<int[3][3]>());
ASSERT_FALSE(*array);
ASSERT_FALSE(*array_of_array);
}
TEST(MetaPointerLike, DereferenceVerifiableNullPointerLike) {
auto test = [](entt::meta_any any) {
ASSERT_TRUE(any);
ASSERT_FALSE(*any);
};
test(entt::meta_any{static_cast<int *>(nullptr)});
test(entt::meta_any{std::shared_ptr<int>{}});
test(entt::meta_any{std::unique_ptr<int>{}});
}

View File

@ -0,0 +1,111 @@
#include <cstring>
#include <tuple>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
struct base_1_t {};
struct base_2_t {};
struct base_3_t {};
struct derived_t: base_1_t, base_2_t, base_3_t {};
struct MetaProp: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<base_1_t>()
.type("base_1"_hs)
.prop("int"_hs, 42);
entt::meta<base_2_t>()
.type("base_2"_hs)
.prop("bool"_hs, false)
.prop("char[]"_hs, "char[]");
entt::meta<base_3_t>()
.type("base_3"_hs)
.prop("key_only"_hs)
.prop("key"_hs, 42);
entt::meta<derived_t>()
.type("derived"_hs)
.base<base_1_t>()
.base<base_2_t>()
.base<base_3_t>();
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaProp, Functionalities) {
using namespace entt::literals;
auto prop = entt::resolve<base_1_t>().prop("int"_hs);
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value(), 42);
}
TEST_F(MetaProp, FromBase) {
using namespace entt::literals;
auto type = entt::resolve<derived_t>();
auto prop_bool = type.prop("bool"_hs);
auto prop_int = type.prop("int"_hs);
auto key_only = type.prop("key_only"_hs);
auto key_value = type.prop("key"_hs);
ASSERT_TRUE(prop_bool);
ASSERT_TRUE(prop_int);
ASSERT_TRUE(key_only);
ASSERT_TRUE(key_value);
ASSERT_FALSE(prop_bool.value().cast<bool>());
ASSERT_EQ(prop_int.value().cast<int>(), 42);
ASSERT_FALSE(key_only.value());
ASSERT_EQ(key_value.value().cast<int>(), 42);
}
TEST_F(MetaProp, DeducedArrayType) {
using namespace entt::literals;
auto prop = entt::resolve<base_2_t>().prop("char[]"_hs);
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value().type(), entt::resolve<const char *>());
ASSERT_EQ(strcmp(prop.value().cast<const char *>(), "char[]"), 0);
}
TEST_F(MetaProp, ReRegistration) {
using namespace entt::literals;
SetUp();
auto &&node = entt::internal::resolve<base_1_t>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
auto type = entt::resolve<base_1_t>();
ASSERT_TRUE(node.details);
ASSERT_FALSE(node.details->prop.empty());
ASSERT_EQ(node.details->prop.size(), 1u);
ASSERT_TRUE(type.prop("int"_hs));
ASSERT_EQ(type.prop("int"_hs).value().cast<int>(), 42);
entt::meta<base_1_t>().prop("int"_hs, 0);
entt::meta<base_1_t>().prop("double"_hs, 3.);
ASSERT_TRUE(node.details);
ASSERT_FALSE(node.details->prop.empty());
ASSERT_EQ(node.details->prop.size(), 2u);
ASSERT_TRUE(type.prop("int"_hs));
ASSERT_TRUE(type.prop("double"_hs));
ASSERT_EQ(type.prop("int"_hs).value().cast<int>(), 0);
ASSERT_EQ(type.prop("double"_hs).value().cast<double>(), 3.);
}

View File

@ -0,0 +1,95 @@
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/range.hpp>
#include <entt/meta/resolve.hpp>
struct MetaRange: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<int>().type("int"_hs).data<42>("answer"_hs);
}
void TearDown() override {
entt::meta_reset();
}
};
TEST_F(MetaRange, EmptyRange) {
entt::meta_reset();
auto range = entt::resolve();
ASSERT_EQ(range.begin(), range.end());
}
TEST_F(MetaRange, Iterator) {
using namespace entt::literals;
using iterator = typename decltype(entt::resolve())::iterator;
static_assert(std::is_same_v<iterator::value_type, std::pair<entt::id_type, entt::meta_type>>);
static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<entt::id_type, entt::meta_type>>>);
static_assert(std::is_same_v<iterator::reference, std::pair<entt::id_type, entt::meta_type>>);
auto range = entt::resolve();
iterator end{range.begin()};
iterator begin{};
begin = range.end();
std::swap(begin, end);
ASSERT_EQ(begin, range.begin());
ASSERT_EQ(end, range.end());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, range.begin());
ASSERT_EQ(begin--, range.end());
ASSERT_EQ(begin + 1, range.end());
ASSERT_EQ(end - 1, range.begin());
ASSERT_EQ(++begin, range.end());
ASSERT_EQ(--begin, range.begin());
ASSERT_EQ(begin += 1, range.end());
ASSERT_EQ(begin -= 1, range.begin());
ASSERT_EQ(begin + (end - begin), range.end());
ASSERT_EQ(begin - (begin - end), range.end());
ASSERT_EQ(end - (end - begin), range.begin());
ASSERT_EQ(end + (begin - end), range.begin());
ASSERT_EQ(begin[0u].first, range.begin()->first);
ASSERT_EQ(begin[0u].second, (*range.begin()).second);
ASSERT_LT(begin, end);
ASSERT_LE(begin, range.begin());
ASSERT_GT(end, begin);
ASSERT_GE(end, range.end());
entt::meta<double>().type("double"_hs);
range = entt::resolve();
begin = range.begin();
ASSERT_EQ(begin[0u].first, entt::resolve<int>().info().hash());
ASSERT_EQ(begin[1u].second, entt::resolve("double"_hs));
}
TEST_F(MetaRange, DirectValue) {
using namespace entt::literals;
auto type = entt::resolve<int>();
auto range = type.data();
ASSERT_NE(range.cbegin(), range.cend());
for(auto &&[id, data]: range) {
ASSERT_EQ(id, "answer"_hs);
ASSERT_EQ(data.get({}).cast<int>(), 42);
}
}

View File

@ -0,0 +1,49 @@
#include <gtest/gtest.h>
#include <entt/core/type_traits.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include <entt/meta/template.hpp>
template<typename>
struct function_type;
template<typename Ret, typename... Args>
struct function_type<Ret(Args...)> {};
template<typename Ret, typename... Args>
struct entt::meta_template_traits<function_type<Ret(Args...)>> {
using class_type = meta_class_template_tag<function_type>;
using args_type = type_list<Ret, Args...>;
};
TEST(MetaTemplate, Invalid) {
const auto type = entt::resolve<int>();
ASSERT_FALSE(type.is_template_specialization());
ASSERT_EQ(type.template_arity(), 0u);
ASSERT_EQ(type.template_type(), entt::meta_type{});
ASSERT_EQ(type.template_arg(0u), entt::meta_type{});
}
TEST(MetaTemplate, Valid) {
const auto type = entt::resolve<entt::type_list<int, char>>();
ASSERT_TRUE(type.is_template_specialization());
ASSERT_EQ(type.template_arity(), 2u);
ASSERT_EQ(type.template_type(), entt::resolve<entt::meta_class_template_tag<entt::type_list>>());
ASSERT_EQ(type.template_arg(0u), entt::resolve<int>());
ASSERT_EQ(type.template_arg(1u), entt::resolve<char>());
ASSERT_EQ(type.template_arg(2u), entt::meta_type{});
}
TEST(MetaTemplate, CustomTraits) {
const auto type = entt::resolve<function_type<void(int, const char &)>>();
ASSERT_TRUE(type.is_template_specialization());
ASSERT_EQ(type.template_arity(), 3u);
ASSERT_EQ(type.template_type(), entt::resolve<entt::meta_class_template_tag<function_type>>());
ASSERT_EQ(type.template_arg(0u), entt::resolve<void>());
ASSERT_EQ(type.template_arg(1u), entt::resolve<int>());
ASSERT_EQ(type.template_arg(2u), entt::resolve<char>());
ASSERT_EQ(type.template_arg(3u), entt::meta_type{});
}

View File

@ -0,0 +1,765 @@
#include <algorithm>
#include <map>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/core/type_info.hpp>
#include <entt/core/utility.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/container.hpp>
#include <entt/meta/context.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/pointer.hpp>
#include <entt/meta/resolve.hpp>
#include <entt/meta/template.hpp>
#include "../common/config.h"
template<typename Type>
void set(Type &prop, Type value) {
prop = value;
}
template<typename Type>
Type get(Type &prop) {
return prop;
}
struct base_t {
base_t()
: value{'c'} {};
char value;
};
struct derived_t: base_t {
derived_t()
: base_t{} {}
};
struct abstract_t {
virtual ~abstract_t() = default;
virtual void func(int) {}
void base_only(int) {}
};
struct concrete_t: base_t, abstract_t {
void func(int v) override {
abstract_t::func(v);
value = v;
}
int value{3};
};
struct clazz_t {
clazz_t() = default;
clazz_t(const base_t &, int v)
: value{v} {}
void member() {}
static void func() {}
operator int() const {
return value;
}
int value{};
};
struct overloaded_func_t {
int e(int v) const {
return v + v;
}
int f(const base_t &, int a, int b) {
return f(a, b);
}
int f(int a, int b) {
value = a;
return g(b);
}
int f(int v) {
return 2 * std::as_const(*this).f(v);
}
int f(int v) const {
return g(v);
}
float f(int a, float b) {
value = a;
return static_cast<float>(e(static_cast<int>(b)));
}
int g(int v) const {
return v * v;
}
inline static int value = 0;
};
enum class property_t : entt::id_type {
random,
value,
key_only,
list
};
struct MetaType: ::testing::Test {
void SetUp() override {
using namespace entt::literals;
entt::meta<double>()
.type("double"_hs)
.data<set<double>, get<double>>("var"_hs);
entt::meta<unsigned int>()
.type("unsigned int"_hs)
.data<0u>("min"_hs)
.data<100u>("max"_hs);
entt::meta<base_t>()
.type("base"_hs)
.data<&base_t::value>("value"_hs);
entt::meta<derived_t>()
.type("derived"_hs)
.base<base_t>();
entt::meta<abstract_t>()
.type("abstract"_hs)
.func<&abstract_t::func>("func"_hs)
.func<&abstract_t::base_only>("base_only"_hs);
entt::meta<concrete_t>()
.type("concrete"_hs)
.base<base_t>()
.base<abstract_t>();
entt::meta<overloaded_func_t>()
.type("overloaded_func"_hs)
.func<&overloaded_func_t::e>("e"_hs)
.func<entt::overload<int(const base_t &, int, int)>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<int(int, int)>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<int(int)>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<int(int) const>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<float(int, float)>(&overloaded_func_t::f)>("f"_hs)
.func<&overloaded_func_t::g>("g"_hs);
entt::meta<property_t>()
.type("property"_hs)
.data<property_t::random>("random"_hs)
.prop(static_cast<entt::id_type>(property_t::random), 0)
.prop(static_cast<entt::id_type>(property_t::value), 3)
.data<property_t::value>("value"_hs)
.prop(static_cast<entt::id_type>(property_t::random), true)
.prop(static_cast<entt::id_type>(property_t::value), 0)
.prop(static_cast<entt::id_type>(property_t::key_only))
.prop(static_cast<entt::id_type>(property_t::list))
.data<property_t::key_only>("key_only"_hs)
.prop(static_cast<entt::id_type>(property_t::key_only))
.data<property_t::list>("list"_hs)
.prop(static_cast<entt::id_type>(property_t::random), false)
.prop(static_cast<entt::id_type>(property_t::value), 0)
.prop(static_cast<entt::id_type>(property_t::key_only))
.data<set<property_t>, get<property_t>>("var"_hs);
entt::meta<clazz_t>()
.type("clazz"_hs)
.prop(static_cast<entt::id_type>(property_t::value), 42)
.ctor<const base_t &, int>()
.data<&clazz_t::value>("value"_hs)
.func<&clazz_t::member>("member"_hs)
.func<clazz_t::func>("func"_hs)
.conv<int>();
}
void TearDown() override {
entt::meta_reset();
}
};
using MetaTypeDeathTest = MetaType;
TEST_F(MetaType, Resolve) {
using namespace entt::literals;
ASSERT_EQ(entt::resolve<double>(), entt::resolve("double"_hs));
ASSERT_EQ(entt::resolve<double>(), entt::resolve(entt::type_id<double>()));
ASSERT_FALSE(entt::resolve(entt::type_id<void>()));
auto range = entt::resolve();
// it could be "char"_hs rather than entt::hashed_string::value("char") if it weren't for a bug in VS2017
const auto it = std::find_if(range.begin(), range.end(), [](auto curr) { return curr.second.id() == entt::hashed_string::value("clazz"); });
ASSERT_NE(it, range.end());
ASSERT_EQ(it->second, entt::resolve<clazz_t>());
bool found = false;
for(auto curr: entt::resolve()) {
found = found || curr.second == entt::resolve<double>();
}
ASSERT_TRUE(found);
}
TEST_F(MetaType, Functionalities) {
using namespace entt::literals;
auto type = entt::resolve<clazz_t>();
ASSERT_TRUE(type);
ASSERT_NE(type, entt::meta_type{});
ASSERT_EQ(type.id(), "clazz"_hs);
ASSERT_EQ(type.info(), entt::type_id<clazz_t>());
for(auto curr: type.prop()) {
ASSERT_EQ(curr.first, static_cast<entt::id_type>(property_t::value));
ASSERT_EQ(curr.second.value(), 42);
}
ASSERT_FALSE(type.prop(static_cast<entt::id_type>(property_t::key_only)));
ASSERT_FALSE(type.prop("property"_hs));
auto prop = type.prop(static_cast<entt::id_type>(property_t::value));
ASSERT_TRUE(prop);
ASSERT_EQ(prop.value(), 42);
}
TEST_F(MetaType, SizeOf) {
ASSERT_EQ(entt::resolve<void>().size_of(), 0u);
ASSERT_EQ(entt::resolve<int>().size_of(), sizeof(int));
ASSERT_EQ(entt::resolve<int[]>().size_of(), 0u);
ASSERT_EQ(entt::resolve<int[3]>().size_of(), sizeof(int[3]));
}
TEST_F(MetaType, Traits) {
ASSERT_TRUE(entt::resolve<bool>().is_arithmetic());
ASSERT_TRUE(entt::resolve<double>().is_arithmetic());
ASSERT_FALSE(entt::resolve<clazz_t>().is_arithmetic());
ASSERT_TRUE(entt::resolve<int>().is_integral());
ASSERT_FALSE(entt::resolve<double>().is_integral());
ASSERT_FALSE(entt::resolve<clazz_t>().is_integral());
ASSERT_TRUE(entt::resolve<long>().is_signed());
ASSERT_FALSE(entt::resolve<unsigned int>().is_signed());
ASSERT_FALSE(entt::resolve<clazz_t>().is_signed());
ASSERT_TRUE(entt::resolve<int[5]>().is_array());
ASSERT_TRUE(entt::resolve<int[5][3]>().is_array());
ASSERT_FALSE(entt::resolve<int>().is_array());
ASSERT_TRUE(entt::resolve<property_t>().is_enum());
ASSERT_FALSE(entt::resolve<char>().is_enum());
ASSERT_TRUE(entt::resolve<derived_t>().is_class());
ASSERT_FALSE(entt::resolve<double>().is_class());
ASSERT_TRUE(entt::resolve<int *>().is_pointer());
ASSERT_FALSE(entt::resolve<int>().is_pointer());
ASSERT_TRUE(entt::resolve<int *>().is_pointer_like());
ASSERT_TRUE(entt::resolve<std::shared_ptr<int>>().is_pointer_like());
ASSERT_FALSE(entt::resolve<int>().is_pointer_like());
ASSERT_FALSE((entt::resolve<int>().is_sequence_container()));
ASSERT_TRUE(entt::resolve<std::vector<int>>().is_sequence_container());
ASSERT_FALSE((entt::resolve<std::map<int, char>>().is_sequence_container()));
ASSERT_FALSE((entt::resolve<int>().is_associative_container()));
ASSERT_TRUE((entt::resolve<std::map<int, char>>().is_associative_container()));
ASSERT_FALSE(entt::resolve<std::vector<int>>().is_associative_container());
}
TEST_F(MetaType, RemovePointer) {
ASSERT_EQ(entt::resolve<void *>().remove_pointer(), entt::resolve<void>());
ASSERT_EQ(entt::resolve<char **>().remove_pointer(), entt::resolve<char *>());
ASSERT_EQ(entt::resolve<int (*)(char, double)>().remove_pointer(), entt::resolve<int(char, double)>());
ASSERT_EQ(entt::resolve<derived_t>().remove_pointer(), entt::resolve<derived_t>());
}
TEST_F(MetaType, TemplateInfo) {
ASSERT_FALSE(entt::resolve<int>().is_template_specialization());
ASSERT_EQ(entt::resolve<int>().template_arity(), 0u);
ASSERT_EQ(entt::resolve<int>().template_type(), entt::meta_type{});
ASSERT_EQ(entt::resolve<int>().template_arg(0u), entt::meta_type{});
ASSERT_TRUE(entt::resolve<std::shared_ptr<int>>().is_template_specialization());
ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_arity(), 1u);
ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_type(), entt::resolve<entt::meta_class_template_tag<std::shared_ptr>>());
ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_arg(0u), entt::resolve<int>());
ASSERT_EQ(entt::resolve<std::shared_ptr<int>>().template_arg(1u), entt::meta_type{});
}
TEST_F(MetaType, Base) {
using namespace entt::literals;
auto type = entt::resolve<derived_t>();
ASSERT_NE(type.base().cbegin(), type.base().cend());
for(auto curr: type.base()) {
ASSERT_EQ(curr.first, entt::type_id<base_t>().hash());
ASSERT_EQ(curr.second, entt::resolve<base_t>());
}
}
TEST_F(MetaType, Ctor) {
derived_t derived;
base_t &base = derived;
auto type = entt::resolve<clazz_t>();
ASSERT_TRUE((type.construct(entt::forward_as_meta(derived), 42)));
ASSERT_TRUE((type.construct(entt::forward_as_meta(base), 42)));
// use the implicitly generated default constructor
auto any = type.construct();
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<clazz_t>());
}
TEST_F(MetaType, Data) {
using namespace entt::literals;
auto type = entt::resolve<clazz_t>();
int counter{};
for([[maybe_unused]] auto curr: type.data()) {
++counter;
}
ASSERT_EQ(counter, 1);
ASSERT_TRUE(type.data("value"_hs));
type = entt::resolve<void>();
ASSERT_TRUE(type);
ASSERT_EQ(type.data().cbegin(), type.data().cend());
}
TEST_F(MetaType, Func) {
using namespace entt::literals;
auto type = entt::resolve<clazz_t>();
clazz_t instance{};
int counter{};
for([[maybe_unused]] auto curr: type.func()) {
++counter;
}
ASSERT_EQ(counter, 2);
ASSERT_TRUE(type.func("member"_hs));
ASSERT_TRUE(type.func("func"_hs));
ASSERT_TRUE(type.func("member"_hs).invoke(instance));
ASSERT_TRUE(type.func("func"_hs).invoke({}));
type = entt::resolve<void>();
ASSERT_TRUE(type);
ASSERT_EQ(type.func().cbegin(), type.func().cend());
}
TEST_F(MetaType, Invoke) {
using namespace entt::literals;
auto type = entt::resolve<clazz_t>();
clazz_t instance{};
ASSERT_TRUE(type.invoke("member"_hs, instance));
ASSERT_FALSE(type.invoke("rebmem"_hs, {}));
}
TEST_F(MetaType, InvokeFromBase) {
using namespace entt::literals;
auto type = entt::resolve<concrete_t>();
concrete_t instance{};
ASSERT_TRUE(type.invoke("base_only"_hs, instance, 42));
ASSERT_FALSE(type.invoke("ylno_esab"_hs, {}, 'c'));
}
TEST_F(MetaType, OverloadedFunc) {
using namespace entt::literals;
const auto type = entt::resolve<overloaded_func_t>();
overloaded_func_t instance{};
entt::meta_any res{};
ASSERT_TRUE(type.func("f"_hs));
ASSERT_TRUE(type.func("e"_hs));
ASSERT_TRUE(type.func("g"_hs));
res = type.invoke("f"_hs, instance, base_t{}, 1, 2);
ASSERT_TRUE(res);
ASSERT_EQ(overloaded_func_t::value, 1);
ASSERT_NE(res.try_cast<int>(), nullptr);
ASSERT_EQ(res.cast<int>(), 4);
res = type.invoke("f"_hs, instance, 3, 4);
ASSERT_TRUE(res);
ASSERT_EQ(overloaded_func_t::value, 3);
ASSERT_NE(res.try_cast<int>(), nullptr);
ASSERT_EQ(res.cast<int>(), 16);
res = type.invoke("f"_hs, instance, 5);
ASSERT_TRUE(res);
ASSERT_EQ(overloaded_func_t::value, 3);
ASSERT_NE(res.try_cast<int>(), nullptr);
ASSERT_EQ(res.cast<int>(), 50);
res = type.invoke("f"_hs, std::as_const(instance), 5);
ASSERT_TRUE(res);
ASSERT_EQ(overloaded_func_t::value, 3);
ASSERT_NE(res.try_cast<int>(), nullptr);
ASSERT_EQ(res.cast<int>(), 25);
res = type.invoke("f"_hs, instance, 6, 7.f);
ASSERT_TRUE(res);
ASSERT_EQ(overloaded_func_t::value, 6);
ASSERT_NE(res.try_cast<float>(), nullptr);
ASSERT_EQ(res.cast<float>(), 14.f);
res = type.invoke("f"_hs, instance, 8, 9.f);
ASSERT_TRUE(res);
ASSERT_EQ(overloaded_func_t::value, 8);
ASSERT_NE(res.try_cast<float>(), nullptr);
ASSERT_EQ(res.cast<float>(), 18.f);
// it fails as an ambiguous call
ASSERT_FALSE(type.invoke("f"_hs, instance, 8, 9.));
}
TEST_F(MetaType, Construct) {
auto any = entt::resolve<clazz_t>().construct(base_t{}, 42);
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().value, 42);
}
TEST_F(MetaType, ConstructNoArgs) {
// this should work, no other tests required
auto any = entt::resolve<clazz_t>().construct();
ASSERT_TRUE(any);
}
TEST_F(MetaType, ConstructMetaAnyArgs) {
auto any = entt::resolve<clazz_t>().construct(entt::meta_any{base_t{}}, entt::meta_any{42});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().value, 42);
}
TEST_F(MetaType, ConstructInvalidArgs) {
ASSERT_FALSE(entt::resolve<clazz_t>().construct('c', base_t{}));
}
TEST_F(MetaType, LessArgs) {
ASSERT_FALSE(entt::resolve<clazz_t>().construct(base_t{}));
}
TEST_F(MetaType, ConstructCastAndConvert) {
auto any = entt::resolve<clazz_t>().construct(derived_t{}, clazz_t{derived_t{}, 42});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().value, 42);
}
TEST_F(MetaType, ConstructArithmeticConversion) {
auto any = entt::resolve<clazz_t>().construct(derived_t{}, clazz_t{derived_t{}, true});
ASSERT_TRUE(any);
ASSERT_EQ(any.cast<clazz_t>().value, 1);
}
TEST_F(MetaType, FromVoid) {
using namespace entt::literals;
ASSERT_FALSE(entt::resolve<double>().from_void(static_cast<double *>(nullptr)));
ASSERT_FALSE(entt::resolve<double>().from_void(static_cast<const double *>(nullptr)));
auto type = entt::resolve<double>();
double value = 4.2;
ASSERT_FALSE(entt::resolve<void>().from_void(static_cast<void *>(&value)));
ASSERT_FALSE(entt::resolve<void>().from_void(static_cast<const void *>(&value)));
auto as_void = type.from_void(static_cast<void *>(&value));
auto as_const_void = type.from_void(static_cast<const void *>(&value));
ASSERT_TRUE(as_void);
ASSERT_TRUE(as_const_void);
ASSERT_EQ(as_void.type(), entt::resolve<double>());
ASSERT_NE(as_void.try_cast<double>(), nullptr);
ASSERT_EQ(as_const_void.type(), entt::resolve<double>());
ASSERT_EQ(as_const_void.try_cast<double>(), nullptr);
ASSERT_NE(as_const_void.try_cast<const double>(), nullptr);
value = 1.2;
ASSERT_EQ(as_void.cast<double>(), as_const_void.cast<double>());
ASSERT_EQ(as_void.cast<double>(), 1.2);
}
TEST_F(MetaType, Reset) {
using namespace entt::literals;
ASSERT_TRUE(entt::resolve("clazz"_hs));
ASSERT_EQ(entt::resolve<clazz_t>().id(), "clazz"_hs);
ASSERT_TRUE(entt::resolve<clazz_t>().prop(static_cast<entt::id_type>(property_t::value)));
ASSERT_TRUE(entt::resolve<clazz_t>().data("value"_hs));
ASSERT_TRUE((entt::resolve<clazz_t>().construct(derived_t{}, clazz_t{})));
// implicitly generated default constructor
ASSERT_TRUE(entt::resolve<clazz_t>().construct());
entt::meta_reset("clazz"_hs);
ASSERT_FALSE(entt::resolve("clazz"_hs));
ASSERT_NE(entt::resolve<clazz_t>().id(), "clazz"_hs);
ASSERT_FALSE(entt::resolve<clazz_t>().prop(static_cast<entt::id_type>(property_t::value)));
ASSERT_FALSE(entt::resolve<clazz_t>().data("value"_hs));
ASSERT_FALSE((entt::resolve<clazz_t>().construct(derived_t{}, clazz_t{})));
// implicitly generated default constructor is not cleared
ASSERT_TRUE(entt::resolve<clazz_t>().construct());
entt::meta<clazz_t>().type("clazz"_hs);
ASSERT_TRUE(entt::resolve("clazz"_hs));
}
TEST_F(MetaType, ResetLast) {
auto id = (entt::resolve().cend() - 1u)->second.id();
ASSERT_TRUE(entt::resolve(id));
entt::meta_reset(id);
ASSERT_FALSE(entt::resolve(id));
}
TEST_F(MetaType, ResetAll) {
using namespace entt::literals;
ASSERT_NE(entt::resolve().begin(), entt::resolve().end());
ASSERT_TRUE(entt::resolve("clazz"_hs));
ASSERT_TRUE(entt::resolve("overloaded_func"_hs));
ASSERT_TRUE(entt::resolve("double"_hs));
entt::meta_reset();
ASSERT_FALSE(entt::resolve("clazz"_hs));
ASSERT_FALSE(entt::resolve("overloaded_func"_hs));
ASSERT_FALSE(entt::resolve("double"_hs));
ASSERT_EQ(entt::resolve().begin(), entt::resolve().end());
}
TEST_F(MetaType, AbstractClass) {
using namespace entt::literals;
auto type = entt::resolve<abstract_t>();
concrete_t instance;
ASSERT_EQ(type.info(), entt::type_id<abstract_t>());
ASSERT_EQ(instance.base_t::value, 'c');
ASSERT_EQ(instance.value, 3);
type.func("func"_hs).invoke(instance, 42);
ASSERT_EQ(instance.base_t::value, 'c');
ASSERT_EQ(instance.value, 42);
}
TEST_F(MetaType, EnumAndNamedConstants) {
using namespace entt::literals;
auto type = entt::resolve<property_t>();
ASSERT_TRUE(type.data("random"_hs));
ASSERT_TRUE(type.data("value"_hs));
ASSERT_EQ(type.data("random"_hs).type(), type);
ASSERT_EQ(type.data("value"_hs).type(), type);
ASSERT_FALSE(type.data("random"_hs).set({}, property_t::value));
ASSERT_FALSE(type.data("value"_hs).set({}, property_t::random));
ASSERT_EQ(type.data("random"_hs).get({}).cast<property_t>(), property_t::random);
ASSERT_EQ(type.data("value"_hs).get({}).cast<property_t>(), property_t::value);
}
TEST_F(MetaType, ArithmeticTypeAndNamedConstants) {
using namespace entt::literals;
auto type = entt::resolve<unsigned int>();
ASSERT_TRUE(type.data("min"_hs));
ASSERT_TRUE(type.data("max"_hs));
ASSERT_EQ(type.data("min"_hs).type(), type);
ASSERT_EQ(type.data("max"_hs).type(), type);
ASSERT_FALSE(type.data("min"_hs).set({}, 100u));
ASSERT_FALSE(type.data("max"_hs).set({}, 0u));
ASSERT_EQ(type.data("min"_hs).get({}).cast<unsigned int>(), 0u);
ASSERT_EQ(type.data("max"_hs).get({}).cast<unsigned int>(), 100u);
}
TEST_F(MetaType, Variables) {
using namespace entt::literals;
auto p_data = entt::resolve<property_t>().data("var"_hs);
auto d_data = entt::resolve("double"_hs).data("var"_hs);
property_t prop{property_t::key_only};
double d = 3.;
p_data.set(prop, property_t::random);
d_data.set(d, 42.);
ASSERT_EQ(p_data.get(prop).cast<property_t>(), property_t::random);
ASSERT_EQ(d_data.get(d).cast<double>(), 42.);
ASSERT_EQ(prop, property_t::random);
ASSERT_EQ(d, 42.);
}
TEST_F(MetaType, PropertiesAndCornerCases) {
using namespace entt::literals;
auto type = entt::resolve<property_t>();
ASSERT_EQ(type.prop().cbegin(), type.prop().cend());
ASSERT_EQ(type.data("random"_hs).prop(static_cast<entt::id_type>(property_t::random)).value().cast<int>(), 0);
ASSERT_EQ(type.data("random"_hs).prop(static_cast<entt::id_type>(property_t::value)).value().cast<int>(), 3);
ASSERT_EQ(type.data("value"_hs).prop(static_cast<entt::id_type>(property_t::random)).value().cast<bool>(), true);
ASSERT_EQ(type.data("value"_hs).prop(static_cast<entt::id_type>(property_t::value)).value().cast<int>(), 0);
ASSERT_TRUE(type.data("value"_hs).prop(static_cast<entt::id_type>(property_t::key_only)));
ASSERT_FALSE(type.data("value"_hs).prop(static_cast<entt::id_type>(property_t::key_only)).value());
ASSERT_TRUE(type.data("key_only"_hs).prop(static_cast<entt::id_type>(property_t::key_only)));
ASSERT_FALSE(type.data("key_only"_hs).prop(static_cast<entt::id_type>(property_t::key_only)).value());
ASSERT_EQ(type.data("list"_hs).prop(static_cast<entt::id_type>(property_t::random)).value().cast<bool>(), false);
ASSERT_EQ(type.data("list"_hs).prop(static_cast<entt::id_type>(property_t::value)).value().cast<int>(), 0);
ASSERT_TRUE(type.data("list"_hs).prop(static_cast<entt::id_type>(property_t::key_only)));
ASSERT_FALSE(type.data("list"_hs).prop(static_cast<entt::id_type>(property_t::key_only)).value());
type = entt::resolve<void>();
ASSERT_EQ(type.prop().cbegin(), type.prop().cend());
}
TEST_F(MetaType, ResetAndReRegistrationAfterReset) {
using namespace entt::literals;
ASSERT_FALSE(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()).value.empty());
entt::meta_reset<double>();
entt::meta_reset<unsigned int>();
entt::meta_reset<base_t>();
entt::meta_reset<derived_t>();
entt::meta_reset<abstract_t>();
entt::meta_reset<concrete_t>();
entt::meta_reset<overloaded_func_t>();
entt::meta_reset<property_t>();
entt::meta_reset<clazz_t>();
ASSERT_FALSE(entt::resolve("double"_hs));
ASSERT_FALSE(entt::resolve("base"_hs));
ASSERT_FALSE(entt::resolve("derived"_hs));
ASSERT_FALSE(entt::resolve("clazz"_hs));
ASSERT_TRUE(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()).value.empty());
ASSERT_FALSE(entt::resolve<clazz_t>().prop(static_cast<entt::id_type>(property_t::value)));
// implicitly generated default constructor is not cleared
ASSERT_TRUE(entt::resolve<clazz_t>().construct());
ASSERT_FALSE(entt::resolve<clazz_t>().data("value"_hs));
ASSERT_FALSE(entt::resolve<clazz_t>().func("member"_hs));
entt::meta<double>().type("double"_hs);
entt::meta_any any{42.};
ASSERT_TRUE(any);
ASSERT_TRUE(any.allow_cast<int>());
ASSERT_TRUE(any.allow_cast<float>());
ASSERT_FALSE(entt::resolve("derived"_hs));
ASSERT_TRUE(entt::resolve("double"_hs));
entt::meta<property_t>()
.type("property"_hs)
.data<property_t::random>("rand"_hs)
.prop(static_cast<entt::id_type>(property_t::value), 42)
.prop(static_cast<entt::id_type>(property_t::random), 3);
ASSERT_TRUE(entt::resolve<property_t>().data("rand"_hs).prop(static_cast<entt::id_type>(property_t::value)));
ASSERT_TRUE(entt::resolve<property_t>().data("rand"_hs).prop(static_cast<entt::id_type>(property_t::random)));
}
TEST_F(MetaType, ReRegistration) {
using namespace entt::literals;
int count = 0;
for([[maybe_unused]] auto type: entt::resolve()) {
++count;
}
SetUp();
for([[maybe_unused]] auto type: entt::resolve()) {
--count;
}
ASSERT_EQ(count, 0);
ASSERT_TRUE(entt::resolve("double"_hs));
entt::meta<double>().type("real"_hs);
ASSERT_FALSE(entt::resolve("double"_hs));
ASSERT_TRUE(entt::resolve("real"_hs));
ASSERT_TRUE(entt::resolve("real"_hs).data("var"_hs));
}
TEST_F(MetaType, NameCollision) {
using namespace entt::literals;
ASSERT_NO_FATAL_FAILURE(entt::meta<clazz_t>().type("clazz"_hs));
ASSERT_TRUE(entt::resolve("clazz"_hs));
ASSERT_NO_FATAL_FAILURE(entt::meta<clazz_t>().type("quux"_hs));
ASSERT_FALSE(entt::resolve("clazz"_hs));
ASSERT_TRUE(entt::resolve("quux"_hs));
}
ENTT_DEBUG_TEST_F(MetaTypeDeathTest, NameCollision) {
using namespace entt::literals;
ASSERT_DEATH(entt::meta<clazz_t>().type("abstract"_hs), "");
}

View File

@ -0,0 +1,268 @@
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_traits.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include <entt/meta/utility.hpp>
#include "../common/config.h"
struct clazz {
void setter(int v) {
member = v;
}
int getter() const {
return member;
}
static void static_setter(clazz &instance, int v) {
instance.member = v;
}
static int static_getter(const clazz &instance) {
return instance.member;
}
static void reset_value() {
value = 0;
}
static int get_value() {
return value;
}
static clazz factory(int v) {
clazz instance{};
instance.member = v;
return instance;
}
int member{};
const int cmember{};
inline static int value{};
inline static const int cvalue{};
inline static int arr[3u]{};
};
struct dummy {};
struct MetaUtility: ::testing::Test {
void SetUp() override {
clazz::value = 0;
}
};
using MetaUtilityDeathTest = MetaUtility;
TEST_F(MetaUtility, MetaDispatch) {
int value = 42;
auto as_void = entt::meta_dispatch<entt::as_void_t>(value);
auto as_ref = entt::meta_dispatch<entt::as_ref_t>(value);
auto as_cref = entt::meta_dispatch<entt::as_cref_t>(value);
auto as_is = entt::meta_dispatch(value);
ASSERT_EQ(as_void.type(), entt::resolve<void>());
ASSERT_EQ(as_ref.type(), entt::resolve<int>());
ASSERT_EQ(as_cref.type(), entt::resolve<int>());
ASSERT_EQ(as_is.type(), entt::resolve<int>());
ASSERT_NE(as_is.try_cast<int>(), nullptr);
ASSERT_NE(as_ref.try_cast<int>(), nullptr);
ASSERT_EQ(as_cref.try_cast<int>(), nullptr);
ASSERT_NE(as_cref.try_cast<const int>(), nullptr);
ASSERT_EQ(as_is.cast<int>(), 42);
ASSERT_EQ(as_ref.cast<int>(), 42);
ASSERT_EQ(as_cref.cast<int>(), 42);
}
TEST_F(MetaUtility, MetaDispatchMetaAny) {
entt::meta_any any{42};
auto from_any = entt::meta_dispatch(any);
auto from_const_any = entt::meta_dispatch(std::as_const(any));
ASSERT_EQ(from_any.type(), entt::resolve<int>());
ASSERT_EQ(from_const_any.type(), entt::resolve<int>());
ASSERT_NE(from_any.try_cast<int>(), nullptr);
ASSERT_NE(from_const_any.try_cast<int>(), nullptr);
ASSERT_EQ(from_any.cast<int>(), 42);
ASSERT_EQ(from_const_any.cast<int>(), 42);
}
TEST_F(MetaUtility, MetaDispatchMetaAnyAsRef) {
entt::meta_any any{42};
auto from_any = entt::meta_dispatch(any.as_ref());
auto from_const_any = entt::meta_dispatch(std::as_const(any).as_ref());
ASSERT_EQ(from_any.type(), entt::resolve<int>());
ASSERT_EQ(from_const_any.type(), entt::resolve<int>());
ASSERT_NE(from_any.try_cast<int>(), nullptr);
ASSERT_EQ(from_const_any.try_cast<int>(), nullptr);
ASSERT_NE(from_const_any.try_cast<const int>(), nullptr);
ASSERT_EQ(from_any.cast<int>(), 42);
ASSERT_EQ(from_const_any.cast<int>(), 42);
}
TEST_F(MetaUtility, MetaArg) {
ASSERT_EQ((entt::meta_arg<entt::type_list<int, char>>(0u)), entt::resolve<int>());
ASSERT_EQ((entt::meta_arg<entt::type_list<int, char>>(1u)), entt::resolve<char>());
}
ENTT_DEBUG_TEST_F(MetaUtilityDeathTest, MetaArg) {
ASSERT_DEATH([[maybe_unused]] auto type = entt::meta_arg<entt::type_list<>>(0u), "");
ASSERT_DEATH([[maybe_unused]] auto type = entt::meta_arg<entt::type_list<int>>(3u), "");
}
TEST_F(MetaUtility, MetaSetter) {
const int invalid{};
clazz instance{};
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::static_setter>(instance, instance)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::static_setter>(std::as_const(instance), 42)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::static_setter>(invalid, 42)));
ASSERT_TRUE((entt::meta_setter<clazz, &clazz::static_setter>(instance, 42)));
ASSERT_EQ(instance.member, 42);
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::setter>(instance, instance)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::setter>(std::as_const(instance), 3)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::setter>(invalid, 3)));
ASSERT_TRUE((entt::meta_setter<clazz, &clazz::setter>(instance, 3)));
ASSERT_EQ(instance.member, 3);
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::member>(instance, instance)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::member>(invalid, 99)));
ASSERT_TRUE((entt::meta_setter<clazz, &clazz::member>(instance, 99)));
ASSERT_EQ(instance.member, 99);
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::cmember>(instance, 99)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::cmember>(invalid, 99)));
ASSERT_EQ(instance.cmember, 0);
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::value>(instance, instance)));
ASSERT_TRUE((entt::meta_setter<clazz, &clazz::value>(invalid, 1)));
ASSERT_TRUE((entt::meta_setter<clazz, &clazz::value>(instance, 2)));
ASSERT_EQ(clazz::value, 2);
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::cvalue>(instance, 1)));
ASSERT_FALSE((entt::meta_setter<clazz, &clazz::cvalue>(invalid, 1)));
ASSERT_EQ(clazz::cvalue, 0);
}
TEST_F(MetaUtility, MetaGetter) {
const int invalid{};
clazz instance{};
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::static_getter>(invalid)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::static_getter>(instance)).cast<int>(), 0);
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::getter>(invalid)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::getter>(instance)).cast<int>(), 0);
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::member>(invalid)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::member>(instance)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::member>(std::as_const(instance))).cast<int>(), 0);
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::cmember>(invalid)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cmember>(instance)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cmember>(std::as_const(instance))).cast<int>(), 0);
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::arr>(invalid)));
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::arr>(instance)));
ASSERT_EQ((entt::meta_getter<clazz, &clazz::value>(invalid)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::value>(instance)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cvalue>(invalid)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cvalue>(instance)).cast<int>(), 0);
ASSERT_EQ((entt::meta_getter<clazz, 42>(invalid)).cast<int>(), 42);
ASSERT_EQ((entt::meta_getter<clazz, 42>(instance)).cast<int>(), 42);
}
TEST_F(MetaUtility, MetaInvokeWithCandidate) {
entt::meta_any args[2u]{clazz{}, 42};
args[0u].cast<clazz &>().value = 99;
ASSERT_FALSE((entt::meta_invoke<clazz>({}, &clazz::setter, nullptr)));
ASSERT_FALSE((entt::meta_invoke<clazz>({}, &clazz::getter, nullptr)));
ASSERT_TRUE((entt::meta_invoke<clazz>(args[0u], &clazz::setter, args + 1u)));
ASSERT_FALSE((entt::meta_invoke<clazz>(args[0u], &clazz::setter, args)));
ASSERT_EQ((entt::meta_invoke<clazz>(args[0u], &clazz::getter, nullptr)).cast<int>(), 42);
ASSERT_FALSE((entt::meta_invoke<clazz>(args[1u], &clazz::getter, nullptr)));
ASSERT_EQ((entt::meta_invoke<clazz>({}, &clazz::get_value, nullptr)).cast<int>(), 99);
ASSERT_TRUE((entt::meta_invoke<clazz>({}, &clazz::reset_value, nullptr)));
ASSERT_EQ(args[0u].cast<clazz &>().value, 0);
const auto setter = [](int &value) { value = 3; };
const auto getter = [](int value) { return value * 2; };
ASSERT_TRUE(entt::meta_invoke<dummy>({}, setter, args + 1u));
ASSERT_EQ(entt::meta_invoke<dummy>({}, getter, args + 1u).cast<int>(), 6);
}
TEST_F(MetaUtility, MetaInvoke) {
entt::meta_any args[2u]{clazz{}, 42};
args[0u].cast<clazz &>().value = 99;
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::setter>({}, nullptr)));
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::getter>({}, nullptr)));
ASSERT_TRUE((entt::meta_invoke<clazz, &clazz::setter>(args[0u], args + 1u)));
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::setter>(args[0u], args)));
ASSERT_EQ((entt::meta_invoke<clazz, &clazz::getter>(args[0u], nullptr)).cast<int>(), 42);
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::getter>(args[1u], nullptr)));
ASSERT_EQ((entt::meta_invoke<clazz, &clazz::get_value>({}, nullptr)).cast<int>(), 99);
ASSERT_TRUE((entt::meta_invoke<clazz, &clazz::reset_value>({}, nullptr)));
ASSERT_EQ(args[0u].cast<clazz &>().value, 0);
}
TEST_F(MetaUtility, MetaConstructArgsOnly) {
entt::meta_any args[2u]{clazz{}, 42};
const auto any = entt::meta_construct<clazz, int>(args + 1u);
ASSERT_TRUE(any);
ASSERT_FALSE((entt::meta_construct<clazz, int>(args)));
ASSERT_EQ(any.cast<const clazz &>().member, 42);
}
TEST_F(MetaUtility, MetaConstructWithCandidate) {
entt::meta_any args[2u]{clazz{}, 42};
const auto any = entt::meta_construct<clazz>(&clazz::factory, args + 1u);
ASSERT_TRUE(any);
ASSERT_FALSE((entt::meta_construct<clazz>(&clazz::factory, args)));
ASSERT_EQ(any.cast<const clazz &>().member, 42);
ASSERT_EQ(args[0u].cast<const clazz &>().member, 0);
ASSERT_TRUE((entt::meta_construct<clazz>(&clazz::static_setter, args)));
ASSERT_EQ(args[0u].cast<const clazz &>().member, 42);
const auto setter = [](int &value) { value = 3; };
const auto builder = [](int value) { return value * 2; };
ASSERT_TRUE(entt::meta_construct<dummy>(setter, args + 1u));
ASSERT_EQ(entt::meta_construct<dummy>(builder, args + 1u).cast<int>(), 6);
}
TEST_F(MetaUtility, MetaConstruct) {
entt::meta_any args[2u]{clazz{}, 42};
const auto any = entt::meta_construct<clazz, &clazz::factory>(args + 1u);
ASSERT_TRUE(any);
ASSERT_FALSE((entt::meta_construct<clazz, &clazz::factory>(args)));
ASSERT_EQ(any.cast<const clazz &>().member, 42);
ASSERT_EQ(args[0u].cast<const clazz &>().member, 0);
ASSERT_TRUE((entt::meta_construct<clazz, &clazz::static_setter>(args)));
ASSERT_EQ(args[0u].cast<const clazz &>().member, 42);
}

417
test/entt/poly/poly.cpp Normal file
View File

@ -0,0 +1,417 @@
#include <cstddef>
#include <cstdint>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/core/type_traits.hpp>
#include <entt/poly/poly.hpp>
#include "../common/config.h"
template<typename Base>
struct common_type: Base {
void incr() {
entt::poly_call<0>(*this);
}
void set(int v) {
entt::poly_call<1>(*this, v);
}
int get() const {
return entt::poly_call<2>(*this);
}
void decr() {
entt::poly_call<3>(*this);
}
int mul(int v) const {
return entt::poly_call<4>(*this, v);
}
int rand() const {
return entt::poly_call<5>(*this);
}
};
template<typename Type>
struct common_members {
static void decr(Type &self) {
self.set(self.get() - 1);
}
static double mul(const Type &self, double v) {
return v * self.get();
}
};
static int absolutely_random() {
return 42;
}
template<typename Type>
using common_impl = entt::value_list<
&Type::incr,
&Type::set,
&Type::get,
&common_members<Type>::decr,
&common_members<Type>::mul,
&absolutely_random>;
struct Deduced
: entt::type_list<> {
template<typename Base>
using type = common_type<Base>;
template<typename Type>
using members = common_members<Type>;
template<typename Type>
using impl = common_impl<Type>;
};
struct Defined
: entt::type_list<
void(),
void(int),
int() const,
void(),
int(int) const,
int() const> {
template<typename Base>
using type = common_type<Base>;
template<typename Type>
using members = common_members<Type>;
template<typename Type>
using impl = common_impl<Type>;
};
struct DeducedEmbedded
: entt::type_list<> {
template<typename Base>
struct type: Base {
int get() const {
return entt::poly_call<0>(*this);
}
};
template<typename Type>
using impl = entt::value_list<&Type::get>;
};
struct DefinedEmbedded
: entt::type_list<int()> {
template<typename Base>
struct type: Base {
// non-const get on purpose
int get() {
return entt::poly_call<0>(*this);
}
};
template<typename Type>
using impl = entt::value_list<&Type::get>;
};
struct impl {
impl() = default;
impl(int v)
: value{v} {}
void incr() {
++value;
}
void set(int v) {
value = v;
}
int get() const {
return value;
}
int value{};
};
struct alignas(64u) over_aligned: impl {};
template<typename Type>
struct Poly: testing::Test {
template<std::size_t... Args>
using type = entt::basic_poly<Type, Args...>;
};
template<typename Type>
using PolyDeathTest = Poly<Type>;
using PolyTypes = ::testing::Types<Deduced, Defined>;
TYPED_TEST_SUITE(Poly, PolyTypes, );
TYPED_TEST_SUITE(PolyDeathTest, PolyTypes, );
template<typename Type>
struct PolyEmbedded: testing::Test {
using type = entt::basic_poly<Type>;
};
using PolyEmbeddedTypes = ::testing::Types<DeducedEmbedded, DefinedEmbedded>;
TYPED_TEST_SUITE(PolyEmbedded, PolyEmbeddedTypes, );
TYPED_TEST(Poly, Functionalities) {
using poly_type = typename TestFixture::template type<>;
impl instance{};
poly_type empty{};
poly_type in_place{std::in_place_type<impl>, 3};
poly_type alias{std::in_place_type<impl &>, instance};
poly_type value{impl{}};
ASSERT_FALSE(empty);
ASSERT_TRUE(in_place);
ASSERT_TRUE(alias);
ASSERT_TRUE(value);
ASSERT_EQ(empty.type(), entt::type_id<void>());
ASSERT_EQ(in_place.type(), entt::type_id<impl>());
ASSERT_EQ(alias.type(), entt::type_id<impl>());
ASSERT_EQ(value.type(), entt::type_id<impl>());
ASSERT_EQ(alias.data(), &instance);
ASSERT_EQ(std::as_const(alias).data(), &instance);
ASSERT_EQ(value->rand(), 42);
empty = impl{};
ASSERT_TRUE(empty);
ASSERT_NE(empty.data(), nullptr);
ASSERT_NE(std::as_const(empty).data(), nullptr);
ASSERT_EQ(empty.type(), entt::type_id<impl>());
ASSERT_EQ(empty->get(), 0);
empty.template emplace<impl>(3);
ASSERT_TRUE(empty);
ASSERT_EQ(std::as_const(empty)->get(), 3);
poly_type ref = in_place.as_ref();
ASSERT_TRUE(ref);
ASSERT_NE(ref.data(), nullptr);
ASSERT_EQ(ref.data(), in_place.data());
ASSERT_EQ(std::as_const(ref).data(), std::as_const(in_place).data());
ASSERT_EQ(ref.type(), entt::type_id<impl>());
ASSERT_EQ(ref->get(), 3);
poly_type null{};
std::swap(empty, null);
ASSERT_FALSE(empty);
poly_type copy = in_place;
ASSERT_TRUE(copy);
ASSERT_EQ(copy->get(), 3);
poly_type move = std::move(copy);
ASSERT_TRUE(move);
ASSERT_TRUE(copy);
ASSERT_EQ(move->get(), 3);
move.reset();
ASSERT_FALSE(move);
ASSERT_EQ(move.type(), entt::type_id<void>());
}
TYPED_TEST(PolyEmbedded, EmbeddedVtable) {
using poly_type = typename TestFixture::type;
poly_type poly{impl{}};
auto *ptr = static_cast<impl *>(poly.data());
ASSERT_TRUE(poly);
ASSERT_NE(poly.data(), nullptr);
ASSERT_NE(std::as_const(poly).data(), nullptr);
ASSERT_EQ(poly->get(), 0);
ptr->value = 2;
ASSERT_EQ(poly->get(), 2);
}
TYPED_TEST(Poly, Owned) {
using poly_type = typename TestFixture::template type<>;
poly_type poly{impl{}};
auto *ptr = static_cast<impl *>(poly.data());
ASSERT_TRUE(poly);
ASSERT_NE(poly.data(), nullptr);
ASSERT_NE(std::as_const(poly).data(), nullptr);
ASSERT_EQ(ptr->value, 0);
ASSERT_EQ(poly->get(), 0);
poly->set(1);
poly->incr();
ASSERT_EQ(ptr->value, 2);
ASSERT_EQ(std::as_const(poly)->get(), 2);
ASSERT_EQ(poly->mul(3), 6);
poly->decr();
ASSERT_EQ(ptr->value, 1);
ASSERT_EQ(poly->get(), 1);
ASSERT_EQ(poly->mul(3), 3);
}
TYPED_TEST(Poly, Reference) {
using poly_type = typename TestFixture::template type<>;
impl instance{};
poly_type poly{std::in_place_type<impl &>, instance};
ASSERT_TRUE(poly);
ASSERT_NE(poly.data(), nullptr);
ASSERT_NE(std::as_const(poly).data(), nullptr);
ASSERT_EQ(instance.value, 0);
ASSERT_EQ(poly->get(), 0);
poly->set(1);
poly->incr();
ASSERT_EQ(instance.value, 2);
ASSERT_EQ(std::as_const(poly)->get(), 2);
ASSERT_EQ(poly->mul(3), 6);
poly->decr();
ASSERT_EQ(instance.value, 1);
ASSERT_EQ(poly->get(), 1);
ASSERT_EQ(poly->mul(3), 3);
}
ENTT_DEBUG_TYPED_TEST(Poly, ConstReference) {
using poly_type = typename TestFixture::template type<>;
impl instance{};
poly_type poly{std::in_place_type<const impl &>, instance};
ASSERT_TRUE(poly);
ASSERT_EQ(poly.data(), nullptr);
ASSERT_NE(std::as_const(poly).data(), nullptr);
ASSERT_EQ(instance.value, 0);
ASSERT_EQ(poly->get(), 0);
ASSERT_EQ(instance.value, 0);
ASSERT_EQ(std::as_const(poly)->get(), 0);
ASSERT_EQ(poly->mul(3), 0);
ASSERT_EQ(instance.value, 0);
ASSERT_EQ(poly->get(), 0);
ASSERT_EQ(poly->mul(3), 0);
}
TYPED_TEST(PolyDeathTest, ConstReference) {
using poly_type = typename TestFixture::template type<>;
impl instance{};
poly_type poly{std::in_place_type<const impl &>, instance};
ASSERT_TRUE(poly);
ASSERT_DEATH(poly->set(1), "");
}
TYPED_TEST(Poly, AsRef) {
using poly_type = typename TestFixture::template type<>;
poly_type poly{impl{}};
auto ref = poly.as_ref();
auto cref = std::as_const(poly).as_ref();
ASSERT_NE(poly.data(), nullptr);
ASSERT_NE(ref.data(), nullptr);
ASSERT_EQ(cref.data(), nullptr);
ASSERT_NE(std::as_const(cref).data(), nullptr);
std::swap(ref, cref);
ASSERT_EQ(ref.data(), nullptr);
ASSERT_NE(std::as_const(ref).data(), nullptr);
ASSERT_NE(cref.data(), nullptr);
ref = ref.as_ref();
cref = std::as_const(cref).as_ref();
ASSERT_EQ(ref.data(), nullptr);
ASSERT_NE(std::as_const(ref).data(), nullptr);
ASSERT_EQ(cref.data(), nullptr);
ASSERT_NE(std::as_const(cref).data(), nullptr);
ref = impl{};
cref = impl{};
ASSERT_NE(ref.data(), nullptr);
ASSERT_NE(cref.data(), nullptr);
}
TYPED_TEST(Poly, SBOVsZeroedSBOSize) {
using poly_type = typename TestFixture::template type<>;
using zeroed_type = typename TestFixture::template type<0u>;
poly_type poly{impl{}};
const auto broken = poly.data();
poly_type other = std::move(poly);
ASSERT_NE(broken, other.data());
zeroed_type dyn{impl{}};
const auto valid = dyn.data();
zeroed_type same = std::move(dyn);
ASSERT_EQ(valid, same.data());
// everything works as expected
same->incr();
ASSERT_EQ(same->get(), 1);
}
TYPED_TEST(Poly, SboAlignment) {
static constexpr auto alignment = alignof(over_aligned);
typename TestFixture::template type<alignment, alignment> sbo[2]{over_aligned{}, over_aligned{}};
const auto *data = sbo[0].data();
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[0u].data()) % alignment) == 0u);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[1u].data()) % alignment) == 0u);
std::swap(sbo[0], sbo[1]);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[0u].data()) % alignment) == 0u);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[1u].data()) % alignment) == 0u);
ASSERT_NE(data, sbo[1].data());
}
TYPED_TEST(Poly, NoSboAlignment) {
static constexpr auto alignment = alignof(over_aligned);
typename TestFixture::template type<alignment> nosbo[2]{over_aligned{}, over_aligned{}};
const auto *data = nosbo[0].data();
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[0u].data()) % alignment) == 0u);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[1u].data()) % alignment) == 0u);
std::swap(nosbo[0], nosbo[1]);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[0u].data()) % alignment) == 0u);
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[1u].data()) % alignment) == 0u);
ASSERT_EQ(data, nosbo[1].data());
}

View File

@ -0,0 +1,269 @@
#include <cstdint>
#include <gtest/gtest.h>
#include <entt/process/process.hpp>
struct fake_delta {};
template<typename Delta>
struct fake_process: entt::process<fake_process<Delta>, Delta> {
using process_type = entt::process<fake_process<Delta>, Delta>;
using delta_type = typename process_type::delta_type;
fake_process()
: init_invoked{false},
update_invoked{false},
succeeded_invoked{false},
failed_invoked{false},
aborted_invoked{false} {}
void succeed() noexcept {
process_type::succeed();
}
void fail() noexcept {
process_type::fail();
}
void pause() noexcept {
process_type::pause();
}
void unpause() noexcept {
process_type::unpause();
}
void init() {
init_invoked = true;
}
void succeeded() {
succeeded_invoked = true;
}
void failed() {
failed_invoked = true;
}
void aborted() {
aborted_invoked = true;
}
void update(typename entt::process<fake_process<Delta>, Delta>::delta_type, void *data) {
if(data) {
(*static_cast<int *>(data))++;
}
update_invoked = true;
}
bool init_invoked;
bool update_invoked;
bool succeeded_invoked;
bool failed_invoked;
bool aborted_invoked;
};
TEST(Process, Basics) {
fake_process<int> process{};
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
process.succeed();
process.fail();
process.abort();
process.pause();
process.unpause();
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
process.tick(0);
ASSERT_TRUE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
process.pause();
ASSERT_TRUE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_TRUE(process.paused());
ASSERT_FALSE(process.rejected());
process.unpause();
ASSERT_TRUE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
process.fail();
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
process.tick(0);
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_TRUE(process.rejected());
}
TEST(Process, Succeeded) {
fake_process<fake_delta> process{};
process.tick({});
process.tick({});
process.succeed();
process.tick({});
ASSERT_FALSE(process.alive());
ASSERT_TRUE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
ASSERT_TRUE(process.init_invoked);
ASSERT_TRUE(process.update_invoked);
ASSERT_TRUE(process.succeeded_invoked);
ASSERT_FALSE(process.failed_invoked);
ASSERT_FALSE(process.aborted_invoked);
}
TEST(Process, Fail) {
fake_process<int> process{};
process.tick(0);
process.tick(0);
process.fail();
process.tick(0);
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_TRUE(process.rejected());
ASSERT_TRUE(process.init_invoked);
ASSERT_TRUE(process.update_invoked);
ASSERT_FALSE(process.succeeded_invoked);
ASSERT_TRUE(process.failed_invoked);
ASSERT_FALSE(process.aborted_invoked);
}
TEST(Process, Data) {
fake_process<fake_delta> process{};
int value = 0;
process.tick({});
process.tick({}, &value);
process.succeed();
process.tick({}, &value);
ASSERT_FALSE(process.alive());
ASSERT_TRUE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_FALSE(process.rejected());
ASSERT_EQ(value, 1);
ASSERT_TRUE(process.init_invoked);
ASSERT_TRUE(process.update_invoked);
ASSERT_TRUE(process.succeeded_invoked);
ASSERT_FALSE(process.failed_invoked);
ASSERT_FALSE(process.aborted_invoked);
}
TEST(Process, AbortNextTick) {
fake_process<int> process{};
process.tick(0);
process.abort();
process.tick(0);
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_TRUE(process.rejected());
ASSERT_TRUE(process.init_invoked);
ASSERT_FALSE(process.update_invoked);
ASSERT_FALSE(process.succeeded_invoked);
ASSERT_FALSE(process.failed_invoked);
ASSERT_TRUE(process.aborted_invoked);
}
TEST(Process, AbortImmediately) {
fake_process<fake_delta> process{};
process.tick({});
process.abort(true);
ASSERT_FALSE(process.alive());
ASSERT_FALSE(process.finished());
ASSERT_FALSE(process.paused());
ASSERT_TRUE(process.rejected());
ASSERT_TRUE(process.init_invoked);
ASSERT_FALSE(process.update_invoked);
ASSERT_FALSE(process.succeeded_invoked);
ASSERT_FALSE(process.failed_invoked);
ASSERT_TRUE(process.aborted_invoked);
}
TEST(ProcessAdaptor, Resolved) {
bool updated = false;
auto lambda = [&updated](std::uint64_t, void *, auto resolve, auto) {
ASSERT_FALSE(updated);
updated = true;
resolve();
};
auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
process.tick(0);
process.tick(0);
ASSERT_TRUE(process.finished());
ASSERT_TRUE(updated);
}
TEST(ProcessAdaptor, Rejected) {
bool updated = false;
auto lambda = [&updated](std::uint64_t, void *, auto, auto rejected) {
ASSERT_FALSE(updated);
updated = true;
rejected();
};
auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
process.tick(0);
process.tick(0);
ASSERT_TRUE(process.rejected());
ASSERT_TRUE(updated);
}
TEST(ProcessAdaptor, Data) {
int value = 0;
auto lambda = [](std::uint64_t, void *data, auto resolve, auto) {
*static_cast<int *>(data) = 42;
resolve();
};
auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
process.tick(0);
process.tick(0, &value);
ASSERT_TRUE(process.finished());
ASSERT_EQ(value, 42);
}

View File

@ -0,0 +1,154 @@
#include <functional>
#include <utility>
#include <gtest/gtest.h>
#include <entt/process/process.hpp>
#include <entt/process/scheduler.hpp>
struct foo_process: entt::process<foo_process, int> {
foo_process(std::function<void()> upd, std::function<void()> abort)
: on_update{upd}, on_aborted{abort} {}
void update(delta_type, void *) {
on_update();
}
void aborted() {
on_aborted();
}
std::function<void()> on_update;
std::function<void()> on_aborted;
};
struct succeeded_process: entt::process<succeeded_process, int> {
void update(delta_type, void *) {
++invoked;
succeed();
}
static inline unsigned int invoked;
};
struct failed_process: entt::process<failed_process, int> {
void update(delta_type, void *) {
++invoked;
fail();
}
static inline unsigned int invoked;
};
struct Scheduler: ::testing::Test {
void SetUp() override {
succeeded_process::invoked = 0u;
failed_process::invoked = 0u;
}
};
TEST_F(Scheduler, Functionalities) {
entt::scheduler<int> scheduler{};
bool updated = false;
bool aborted = false;
ASSERT_EQ(scheduler.size(), 0u);
ASSERT_TRUE(scheduler.empty());
scheduler.attach<foo_process>(
[&updated]() { updated = true; },
[&aborted]() { aborted = true; });
ASSERT_NE(scheduler.size(), 0u);
ASSERT_FALSE(scheduler.empty());
scheduler.update(0);
scheduler.abort(true);
ASSERT_TRUE(updated);
ASSERT_TRUE(aborted);
ASSERT_NE(scheduler.size(), 0u);
ASSERT_FALSE(scheduler.empty());
scheduler.clear();
ASSERT_EQ(scheduler.size(), 0u);
ASSERT_TRUE(scheduler.empty());
}
TEST_F(Scheduler, Then) {
entt::scheduler<int> scheduler;
// failing process with successor
scheduler.attach<succeeded_process>()
.then<succeeded_process>()
.then<failed_process>()
.then<succeeded_process>();
// failing process without successor
scheduler.attach<succeeded_process>()
.then<succeeded_process>()
.then<failed_process>();
// non-failing process
scheduler.attach<succeeded_process>()
.then<succeeded_process>();
ASSERT_EQ(succeeded_process::invoked, 0u);
ASSERT_EQ(failed_process::invoked, 0u);
while(!scheduler.empty()) {
scheduler.update(0);
}
ASSERT_EQ(succeeded_process::invoked, 6u);
ASSERT_EQ(failed_process::invoked, 2u);
}
TEST_F(Scheduler, Functor) {
entt::scheduler<int> scheduler;
bool first_functor = false;
bool second_functor = false;
auto attach = [&first_functor](auto, void *, auto resolve, auto) {
ASSERT_FALSE(first_functor);
first_functor = true;
resolve();
};
auto then = [&second_functor](auto, void *, auto, auto reject) {
ASSERT_FALSE(second_functor);
second_functor = true;
reject();
};
scheduler.attach(std::move(attach)).then(std::move(then)).then([](auto...) { FAIL(); });
while(!scheduler.empty()) {
scheduler.update(0);
}
ASSERT_TRUE(first_functor);
ASSERT_TRUE(second_functor);
ASSERT_TRUE(scheduler.empty());
}
TEST_F(Scheduler, SpawningProcess) {
entt::scheduler<int> scheduler;
scheduler.attach([&scheduler](auto, void *, auto resolve, auto) {
scheduler.attach<succeeded_process>().then<failed_process>();
resolve();
});
ASSERT_EQ(succeeded_process::invoked, 0u);
ASSERT_EQ(failed_process::invoked, 0u);
while(!scheduler.empty()) {
scheduler.update(0);
}
ASSERT_EQ(succeeded_process::invoked, 1u);
ASSERT_EQ(failed_process::invoked, 1u);
}

View File

@ -0,0 +1,144 @@
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/resource/resource.hpp>
struct base {
virtual ~base() = default;
virtual const entt::type_info &type() const noexcept {
return entt::type_id<base>();
}
};
struct derived: base {
const entt::type_info &type() const noexcept override {
return entt::type_id<derived>();
}
};
template<typename Type, typename Other>
entt::resource<Type> dynamic_resource_cast(const entt::resource<Other> &other) {
if(other->type() == entt::type_id<Type>()) {
return entt::resource<Type>{other, static_cast<Type &>(*other)};
}
return {};
}
TEST(Resource, Functionalities) {
entt::resource<derived> resource{};
ASSERT_FALSE(resource);
ASSERT_EQ(resource.operator->(), nullptr);
ASSERT_EQ(resource.handle().use_count(), 0l);
const auto value = std::make_shared<derived>();
entt::resource<derived> other{value};
ASSERT_TRUE(other);
ASSERT_EQ(other.operator->(), value.get());
ASSERT_EQ(&static_cast<derived &>(other), value.get());
ASSERT_EQ(&*other, value.get());
ASSERT_EQ(other.handle().use_count(), 2l);
entt::resource<derived> copy{resource};
entt::resource<derived> move{std::move(other)};
ASSERT_FALSE(copy);
ASSERT_TRUE(move);
copy = std::move(move);
move = copy;
ASSERT_TRUE(copy);
ASSERT_TRUE(move);
ASSERT_EQ(copy, move);
}
TEST(Resource, DerivedToBase) {
entt::resource<derived> resource{std::make_shared<derived>()};
entt::resource<base> other{resource};
entt::resource<const base> cother{resource};
ASSERT_TRUE(resource);
ASSERT_TRUE(other);
ASSERT_TRUE(cother);
ASSERT_EQ(resource, other);
ASSERT_EQ(other, cother);
other = resource;
cother = resource;
ASSERT_EQ(resource, other);
ASSERT_EQ(other, cother);
}
TEST(Resource, ConstNonConstAndAllInBetween) {
entt::resource<derived> resource{std::make_shared<derived>()};
entt::resource<derived> other{resource};
static_assert(std::is_same_v<decltype(*resource), derived &>);
static_assert(std::is_same_v<decltype(*entt::resource<const derived>{other}), const derived &>);
static_assert(std::is_same_v<decltype(*std::as_const(resource)), derived &>);
entt::resource<const derived> copy{resource};
entt::resource<const derived> move{std::move(other)};
ASSERT_TRUE(resource);
ASSERT_FALSE(other);
ASSERT_TRUE(copy);
ASSERT_EQ(copy, resource);
ASSERT_NE(copy, entt::resource<derived>{});
ASSERT_EQ(copy.handle().use_count(), 3u);
ASSERT_TRUE(move);
ASSERT_EQ(move, resource);
ASSERT_NE(move, entt::resource<derived>{});
ASSERT_EQ(move.handle().use_count(), 3u);
copy = resource;
move = std::move(resource);
ASSERT_FALSE(resource);
ASSERT_FALSE(other);
ASSERT_TRUE(copy);
ASSERT_TRUE(move);
ASSERT_EQ(copy.handle().use_count(), 2u);
}
TEST(Resource, DynamicResourceHandleCast) {
entt::resource<derived> resource{std::make_shared<derived>()};
entt::resource<const base> other = resource;
ASSERT_TRUE(other);
ASSERT_EQ(resource.handle().use_count(), 2u);
ASSERT_EQ(resource, other);
entt::resource<const derived> cast = dynamic_resource_cast<const derived>(other);
ASSERT_TRUE(cast);
ASSERT_EQ(resource.handle().use_count(), 3u);
ASSERT_EQ(resource, cast);
other = entt::resource<base>{std::make_shared<base>()};
cast = dynamic_resource_cast<const derived>(other);
ASSERT_FALSE(cast);
ASSERT_EQ(resource.handle().use_count(), 1u);
}
TEST(Resource, Comparison) {
entt::resource<derived> resource{std::make_shared<derived>()};
entt::resource<const base> other = resource;
ASSERT_TRUE(resource == other);
ASSERT_FALSE(resource != other);
ASSERT_FALSE(resource < other);
ASSERT_FALSE(resource > other);
ASSERT_TRUE(resource <= other);
ASSERT_TRUE(resource >= other);
}

View File

@ -0,0 +1,428 @@
#include <cstddef>
#include <memory>
#include <tuple>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/resource/cache.hpp>
#include <entt/resource/loader.hpp>
#include "../common/throwing_allocator.hpp"
struct broken_tag {};
struct with_callback {};
template<typename Type>
struct loader {
using result_type = std::shared_ptr<Type>;
template<typename... Args>
result_type operator()(Args &&...args) const {
return std::make_shared<Type>(std::forward<Args>(args)...);
}
template<typename Func>
result_type operator()(with_callback, Func &&func) const {
return std::forward<Func>(func)();
}
template<typename... Args>
result_type operator()(broken_tag, Args &&...) const {
return {};
}
};
TEST(ResourceCache, Functionalities) {
using namespace entt::literals;
entt::resource_cache<int> cache;
ASSERT_NO_FATAL_FAILURE([[maybe_unused]] auto alloc = cache.get_allocator());
ASSERT_TRUE(cache.empty());
ASSERT_EQ(cache.size(), 0u);
ASSERT_EQ(cache.begin(), cache.end());
ASSERT_EQ(std::as_const(cache).begin(), std::as_const(cache).end());
ASSERT_EQ(cache.cbegin(), cache.cend());
ASSERT_FALSE(cache.contains("resource"_hs));
cache.load("resource"_hs, 42);
ASSERT_FALSE(cache.empty());
ASSERT_EQ(cache.size(), 1u);
ASSERT_NE(cache.begin(), cache.end());
ASSERT_NE(std::as_const(cache).begin(), std::as_const(cache).end());
ASSERT_NE(cache.cbegin(), cache.cend());
ASSERT_TRUE(cache.contains("resource"_hs));
cache.clear();
ASSERT_TRUE(cache.empty());
ASSERT_EQ(cache.size(), 0u);
ASSERT_EQ(cache.begin(), cache.end());
ASSERT_EQ(std::as_const(cache).begin(), std::as_const(cache).end());
ASSERT_EQ(cache.cbegin(), cache.cend());
ASSERT_FALSE(cache.contains("resource"_hs));
}
TEST(ResourceCache, Constructors) {
using namespace entt::literals;
entt::resource_cache<int> cache;
cache = entt::resource_cache<int>{std::allocator<int>{}};
cache = entt::resource_cache<int>{entt::resource_loader<int>{}, std::allocator<float>{}};
cache.load("resource"_hs, 42u);
entt::resource_cache<int> temp{cache, cache.get_allocator()};
entt::resource_cache<int> other{std::move(temp), cache.get_allocator()};
ASSERT_EQ(cache.size(), 1u);
ASSERT_EQ(other.size(), 1u);
}
TEST(ResourceCache, Copy) {
using namespace entt::literals;
entt::resource_cache<std::size_t> cache;
cache.load("resource"_hs, 42u);
entt::resource_cache<std::size_t> other{cache};
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_TRUE(other.contains("resource"_hs));
cache.load("foo"_hs, 99u);
cache.load("bar"_hs, 77u);
other.load("quux"_hs, 0u);
other = cache;
ASSERT_TRUE(other.contains("resource"_hs));
ASSERT_TRUE(other.contains("foo"_hs));
ASSERT_TRUE(other.contains("bar"_hs));
ASSERT_FALSE(other.contains("quux"_hs));
ASSERT_EQ(other["resource"_hs], 42u);
ASSERT_EQ(other["foo"_hs], 99u);
ASSERT_EQ(other["bar"_hs], 77u);
}
TEST(ResourceCache, Move) {
using namespace entt::literals;
entt::resource_cache<std::size_t> cache;
cache.load("resource"_hs, 42u);
entt::resource_cache<std::size_t> other{std::move(cache)};
ASSERT_EQ(cache.size(), 0u);
ASSERT_TRUE(other.contains("resource"_hs));
cache = other;
cache.load("foo"_hs, 99u);
cache.load("bar"_hs, 77u);
other.load("quux"_hs, 0u);
other = std::move(cache);
ASSERT_EQ(cache.size(), 0u);
ASSERT_TRUE(other.contains("resource"_hs));
ASSERT_TRUE(other.contains("foo"_hs));
ASSERT_TRUE(other.contains("bar"_hs));
ASSERT_FALSE(other.contains("quux"_hs));
ASSERT_EQ(other["resource"_hs], 42u);
ASSERT_EQ(other["foo"_hs], 99u);
ASSERT_EQ(other["bar"_hs], 77u);
}
TEST(ResourceCache, Iterator) {
using namespace entt::literals;
using iterator = typename entt::resource_cache<int>::iterator;
static_assert(std::is_same_v<iterator::value_type, std::pair<entt::id_type, entt::resource<int>>>);
static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<entt::id_type, entt::resource<int>>>>);
static_assert(std::is_same_v<iterator::reference, std::pair<entt::id_type, entt::resource<int>>>);
entt::resource_cache<int> cache;
cache.load("resource"_hs, 42);
iterator end{cache.begin()};
iterator begin{};
begin = cache.end();
std::swap(begin, end);
ASSERT_EQ(begin, cache.begin());
ASSERT_EQ(end, cache.end());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, cache.begin());
ASSERT_EQ(begin--, cache.end());
ASSERT_EQ(begin + 1, cache.end());
ASSERT_EQ(end - 1, cache.begin());
ASSERT_EQ(++begin, cache.end());
ASSERT_EQ(--begin, cache.begin());
ASSERT_EQ(begin += 1, cache.end());
ASSERT_EQ(begin -= 1, cache.begin());
ASSERT_EQ(begin + (end - begin), cache.end());
ASSERT_EQ(begin - (begin - end), cache.end());
ASSERT_EQ(end - (end - begin), cache.begin());
ASSERT_EQ(end + (begin - end), cache.begin());
ASSERT_EQ(begin[0u].first, cache.begin()->first);
ASSERT_EQ(begin[0u].second, (*cache.begin()).second);
ASSERT_LT(begin, end);
ASSERT_LE(begin, cache.begin());
ASSERT_GT(end, begin);
ASSERT_GE(end, cache.end());
cache.load("other"_hs, 3);
begin = cache.begin();
ASSERT_EQ(begin[0u].first, "resource"_hs);
ASSERT_EQ(begin[1u].second, 3);
}
TEST(ResourceCache, ConstIterator) {
using namespace entt::literals;
using iterator = typename entt::resource_cache<int>::const_iterator;
static_assert(std::is_same_v<iterator::value_type, std::pair<entt::id_type, entt::resource<const int>>>);
static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<entt::id_type, entt::resource<const int>>>>);
static_assert(std::is_same_v<iterator::reference, std::pair<entt::id_type, entt::resource<const int>>>);
entt::resource_cache<int> cache;
cache.load("resource"_hs, 42);
iterator cend{cache.cbegin()};
iterator cbegin{};
cbegin = cache.cend();
std::swap(cbegin, cend);
ASSERT_EQ(cbegin, cache.cbegin());
ASSERT_EQ(cend, cache.cend());
ASSERT_NE(cbegin, cend);
ASSERT_EQ(cbegin++, cache.cbegin());
ASSERT_EQ(cbegin--, cache.cend());
ASSERT_EQ(cbegin + 1, cache.cend());
ASSERT_EQ(cend - 1, cache.cbegin());
ASSERT_EQ(++cbegin, cache.cend());
ASSERT_EQ(--cbegin, cache.cbegin());
ASSERT_EQ(cbegin += 1, cache.cend());
ASSERT_EQ(cbegin -= 1, cache.cbegin());
ASSERT_EQ(cbegin + (cend - cbegin), cache.cend());
ASSERT_EQ(cbegin - (cbegin - cend), cache.cend());
ASSERT_EQ(cend - (cend - cbegin), cache.cbegin());
ASSERT_EQ(cend + (cbegin - cend), cache.cbegin());
ASSERT_EQ(cbegin[0u].first, cache.cbegin()->first);
ASSERT_EQ(cbegin[0u].second, (*cache.cbegin()).second);
ASSERT_LT(cbegin, cend);
ASSERT_LE(cbegin, cache.cbegin());
ASSERT_GT(cend, cbegin);
ASSERT_GE(cend, cache.cend());
cache.load("other"_hs, 3);
cbegin = cache.cbegin();
ASSERT_EQ(cbegin[0u].first, "resource"_hs);
ASSERT_EQ(cbegin[1u].second, 3);
}
TEST(ResourceCache, IteratorConversion) {
using namespace entt::literals;
entt::resource_cache<int> cache;
cache.load("resource"_hs, 42);
typename entt::resource_cache<int>::iterator it = cache.begin();
typename entt::resource_cache<int>::const_iterator cit = it;
static_assert(std::is_same_v<decltype(*it), std::pair<entt::id_type, entt::resource<int>>>);
static_assert(std::is_same_v<decltype(*cit), std::pair<entt::id_type, entt::resource<const int>>>);
ASSERT_EQ(it->first, "resource"_hs);
ASSERT_EQ((*it).second, 42);
ASSERT_EQ(it->first, cit->first);
ASSERT_EQ((*it).second, (*cit).second);
ASSERT_EQ(it - cit, 0);
ASSERT_EQ(cit - it, 0);
ASSERT_LE(it, cit);
ASSERT_LE(cit, it);
ASSERT_GE(it, cit);
ASSERT_GE(cit, it);
ASSERT_EQ(it, cit);
ASSERT_NE(++cit, it);
}
TEST(ResourceCache, Load) {
using namespace entt::literals;
entt::resource_cache<int> cache;
typename entt::resource_cache<int>::iterator it;
bool result;
ASSERT_TRUE(cache.empty());
ASSERT_EQ(cache.size(), 0u);
ASSERT_EQ(cache["resource"_hs], entt::resource<int>{});
ASSERT_EQ(std::as_const(cache)["resource"_hs], entt::resource<const int>{});
ASSERT_FALSE(cache.contains("resource"_hs));
std::tie(it, result) = cache.load("resource"_hs, 1);
ASSERT_TRUE(result);
ASSERT_EQ(cache.size(), 1u);
ASSERT_EQ(it, --cache.end());
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_NE(cache["resource"_hs], entt::resource<int>{});
ASSERT_NE(std::as_const(cache)["resource"_hs], entt::resource<const int>{});
ASSERT_EQ(it->first, "resource"_hs);
ASSERT_EQ(it->second, 1);
std::tie(it, result) = cache.load("resource"_hs, 2);
ASSERT_FALSE(result);
ASSERT_EQ(cache.size(), 1u);
ASSERT_EQ(it, --cache.end());
ASSERT_EQ(it->second, 1);
std::tie(it, result) = cache.force_load("resource"_hs, 3);
ASSERT_TRUE(result);
ASSERT_EQ(cache.size(), 1u);
ASSERT_EQ(it, --cache.end());
ASSERT_EQ(it->second, 3);
}
TEST(ResourceCache, Erase) {
static constexpr std::size_t resource_count = 8u;
entt::resource_cache<std::size_t> cache;
for(std::size_t next{}, last = resource_count + 1u; next < last; ++next) {
cache.load(next, next);
}
ASSERT_EQ(cache.size(), resource_count + 1u);
for(std::size_t next{}, last = resource_count + 1u; next < last; ++next) {
ASSERT_TRUE(cache.contains(next));
}
auto it = cache.erase(++cache.begin());
it = cache.erase(it, it + 1);
ASSERT_EQ((--cache.end())->first, 6u);
ASSERT_EQ(cache.erase(6u), 1u);
ASSERT_EQ(cache.erase(6u), 0u);
ASSERT_EQ(cache.size(), resource_count + 1u - 3u);
ASSERT_EQ(it, ++cache.begin());
ASSERT_EQ(it->first, 7u);
ASSERT_EQ((--cache.end())->first, 5u);
for(std::size_t next{}, last = resource_count + 1u; next < last; ++next) {
if(next == 1u || next == 8u || next == 6u) {
ASSERT_FALSE(cache.contains(next));
} else {
ASSERT_TRUE(cache.contains(next));
}
}
cache.erase(cache.begin(), cache.end());
for(std::size_t next{}, last = resource_count + 1u; next < last; ++next) {
ASSERT_FALSE(cache.contains(next));
}
ASSERT_EQ(cache.size(), 0u);
}
TEST(ResourceCache, Indexing) {
using namespace entt::literals;
entt::resource_cache<int> cache;
ASSERT_FALSE(cache.contains("resource"_hs));
ASSERT_FALSE(cache["resource"_hs]);
ASSERT_FALSE(std::as_const(cache)["resource"_hs]);
cache.load("resource"_hs, 99);
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_EQ(cache[std::move("resource"_hs)], 99);
ASSERT_EQ(std::as_const(cache)["resource"_hs], 99);
}
TEST(ResourceCache, LoaderDispatching) {
using namespace entt::literals;
entt::resource_cache<int, loader<int>> cache;
cache.force_load("resource"_hs, 99);
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_EQ(cache["resource"_hs], 99);
cache.force_load("resource"_hs, with_callback{}, []() { return std::make_shared<int>(42); });
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_EQ(cache["resource"_hs], 42);
}
TEST(ResourceCache, BrokenLoader) {
using namespace entt::literals;
entt::resource_cache<int, loader<int>> cache;
cache.load("resource"_hs, broken_tag{});
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_FALSE(cache["resource"_hs]);
cache.force_load("resource"_hs, 42);
ASSERT_TRUE(cache.contains("resource"_hs));
ASSERT_TRUE(cache["resource"_hs]);
}
TEST(ResourceCache, ThrowingAllocator) {
using namespace entt::literals;
using allocator = test::throwing_allocator<std::size_t>;
using packed_allocator = test::throwing_allocator<entt::internal::dense_map_node<entt::id_type, std::shared_ptr<std::size_t>>>;
using packed_exception = typename packed_allocator::exception_type;
entt::resource_cache<std::size_t, entt::resource_loader<std::size_t>, allocator> cache{};
packed_allocator::trigger_on_allocate = true;
ASSERT_THROW(cache.load("resource"_hs), packed_exception);
ASSERT_FALSE(cache.contains("resource"_hs));
packed_allocator::trigger_on_allocate = true;
ASSERT_THROW(cache.force_load("resource"_hs), packed_exception);
ASSERT_FALSE(cache.contains("resource"_hs));
}

View File

@ -0,0 +1,13 @@
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/resource/loader.hpp>
TEST(ResourceLoader, Functionalities) {
using loader_type = entt::resource_loader<int>;
const auto resource = loader_type{}(42);
static_assert(std::is_same_v<typename loader_type::result_type, std::shared_ptr<int>>);
ASSERT_TRUE(resource);
ASSERT_EQ(*resource, 42);
}

View File

@ -0,0 +1,433 @@
#include <memory>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/signal/delegate.hpp>
#include "../common/config.h"
int delegate_function(const int &i) {
return i * i;
}
int curried_by_ref(const int &i, int j) {
return i + j;
}
int curried_by_ptr(const int *i, int j) {
return (*i) * j;
}
int non_const_reference(int &i) {
return i *= i;
}
int move_only_type(std::unique_ptr<int> ptr) {
return *ptr;
}
struct delegate_functor {
int operator()(int i) {
return i + i;
}
int identity(int i) const {
return i;
}
static const int static_value = 3;
const int data_member = 42;
};
struct const_nonconst_noexcept {
void f() {
++cnt;
}
void g() noexcept {
++cnt;
}
void h() const {
++cnt;
}
void i() const noexcept {
++cnt;
}
int u{};
const int v{};
mutable int cnt{0};
};
TEST(Delegate, Functionalities) {
entt::delegate<int(int)> ff_del;
entt::delegate<int(int)> mf_del;
entt::delegate<int(int)> lf_del;
delegate_functor functor;
ASSERT_FALSE(ff_del);
ASSERT_FALSE(mf_del);
ASSERT_EQ(ff_del, mf_del);
ff_del.connect<&delegate_function>();
mf_del.connect<&delegate_functor::operator()>(functor);
lf_del.connect([](const void *ptr, int value) { return static_cast<const delegate_functor *>(ptr)->identity(value); }, &functor);
ASSERT_TRUE(ff_del);
ASSERT_TRUE(mf_del);
ASSERT_TRUE(lf_del);
ASSERT_EQ(ff_del(3), 9);
ASSERT_EQ(mf_del(3), 6);
ASSERT_EQ(lf_del(3), 3);
ff_del.reset();
ASSERT_FALSE(ff_del);
ASSERT_EQ(ff_del, entt::delegate<int(int)>{});
ASSERT_NE(mf_del, entt::delegate<int(int)>{});
ASSERT_NE(lf_del, entt::delegate<int(int)>{});
ASSERT_NE(ff_del, mf_del);
ASSERT_NE(ff_del, lf_del);
ASSERT_NE(mf_del, lf_del);
mf_del.reset();
ASSERT_FALSE(ff_del);
ASSERT_FALSE(mf_del);
ASSERT_TRUE(lf_del);
ASSERT_EQ(ff_del, entt::delegate<int(int)>{});
ASSERT_EQ(mf_del, entt::delegate<int(int)>{});
ASSERT_NE(lf_del, entt::delegate<int(int)>{});
ASSERT_EQ(ff_del, mf_del);
ASSERT_NE(ff_del, lf_del);
ASSERT_NE(mf_del, lf_del);
}
ENTT_DEBUG_TEST(DelegateDeathTest, InvokeEmpty) {
entt::delegate<int(int)> del;
ASSERT_FALSE(del);
ASSERT_DEATH(del(42), "");
ASSERT_DEATH(std::as_const(del)(42), "");
}
TEST(Delegate, DataMembers) {
entt::delegate<double()> delegate;
delegate_functor functor;
delegate.connect<&delegate_functor::data_member>(functor);
ASSERT_EQ(delegate(), 42);
}
TEST(Delegate, Comparison) {
entt::delegate<int(int)> lhs;
entt::delegate<int(int)> rhs;
delegate_functor functor;
delegate_functor other;
const int value = 0;
ASSERT_EQ(lhs, entt::delegate<int(int)>{});
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&delegate_function>();
ASSERT_EQ(lhs, entt::delegate<int(int)>{entt::connect_arg<&delegate_function>});
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&delegate_function>();
ASSERT_EQ(rhs, entt::delegate<int(int)>{entt::connect_arg<&delegate_function>});
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&curried_by_ref>(value);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_by_ref>, value}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&curried_by_ref>(value);
ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_by_ref>, value}));
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&curried_by_ptr>(&value);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_by_ptr>, &value}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&curried_by_ptr>(&value);
ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_by_ptr>, &value}));
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&delegate_functor::operator()>(functor);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, functor}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&delegate_functor::operator()>(functor);
ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, functor}));
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&delegate_functor::operator()>(other);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, other}));
ASSERT_NE(lhs.data(), rhs.data());
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
lhs.connect([](const void *ptr, int val) { return static_cast<const delegate_functor *>(ptr)->identity(val) * val; }, &functor);
ASSERT_NE(lhs, (entt::delegate<int(int)>{[](const void *, int val) { return val + val; }, &functor}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect([](const void *ptr, int val) { return static_cast<const delegate_functor *>(ptr)->identity(val) + val; }, &functor);
ASSERT_NE(rhs, (entt::delegate<int(int)>{[](const void *, int val) { return val * val; }, &functor}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
lhs.reset();
ASSERT_EQ(lhs, (entt::delegate<int(int)>{}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.reset();
ASSERT_EQ(rhs, (entt::delegate<int(int)>{}));
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
}
TEST(Delegate, ConstNonConstNoExcept) {
entt::delegate<void()> delegate;
const_nonconst_noexcept functor;
delegate.connect<&const_nonconst_noexcept::f>(functor);
delegate();
delegate.connect<&const_nonconst_noexcept::g>(functor);
delegate();
delegate.connect<&const_nonconst_noexcept::h>(functor);
delegate();
delegate.connect<&const_nonconst_noexcept::i>(functor);
delegate();
ASSERT_EQ(functor.cnt, 4);
}
TEST(Delegate, DeductionGuide) {
const_nonconst_noexcept functor;
int value = 0;
entt::delegate func{entt::connect_arg<&delegate_function>};
entt::delegate curried_func_with_ref{entt::connect_arg<&curried_by_ref>, value};
entt::delegate curried_func_with_const_ref{entt::connect_arg<&curried_by_ref>, std::as_const(value)};
entt::delegate curried_func_with_ptr{entt::connect_arg<&curried_by_ptr>, &value};
entt::delegate curried_func_with_const_ptr{entt::connect_arg<&curried_by_ptr>, &std::as_const(value)};
entt::delegate member_func_f{entt::connect_arg<&const_nonconst_noexcept::f>, functor};
entt::delegate member_func_g{entt::connect_arg<&const_nonconst_noexcept::g>, functor};
entt::delegate member_func_h{entt::connect_arg<&const_nonconst_noexcept::h>, &functor};
entt::delegate member_func_h_const{entt::connect_arg<&const_nonconst_noexcept::h>, &std::as_const(functor)};
entt::delegate member_func_i{entt::connect_arg<&const_nonconst_noexcept::i>, functor};
entt::delegate member_func_i_const{entt::connect_arg<&const_nonconst_noexcept::i>, std::as_const(functor)};
entt::delegate data_member_u{entt::connect_arg<&const_nonconst_noexcept::u>, functor};
entt::delegate data_member_v{entt::connect_arg<&const_nonconst_noexcept::v>, &functor};
entt::delegate data_member_v_const{entt::connect_arg<&const_nonconst_noexcept::v>, &std::as_const(functor)};
entt::delegate lambda{+[](const void *, int) { return 0; }};
static_assert(std::is_same_v<typename decltype(func)::type, int(const int &)>);
static_assert(std::is_same_v<typename decltype(curried_func_with_ref)::type, int(int)>);
static_assert(std::is_same_v<typename decltype(curried_func_with_const_ref)::type, int(int)>);
static_assert(std::is_same_v<typename decltype(curried_func_with_ptr)::type, int(int)>);
static_assert(std::is_same_v<typename decltype(curried_func_with_const_ptr)::type, int(int)>);
static_assert(std::is_same_v<typename decltype(member_func_f)::type, void()>);
static_assert(std::is_same_v<typename decltype(member_func_g)::type, void()>);
static_assert(std::is_same_v<typename decltype(member_func_h)::type, void()>);
static_assert(std::is_same_v<typename decltype(member_func_h_const)::type, void()>);
static_assert(std::is_same_v<typename decltype(member_func_i)::type, void()>);
static_assert(std::is_same_v<typename decltype(member_func_i_const)::type, void()>);
static_assert(std::is_same_v<typename decltype(data_member_u)::type, int()>);
static_assert(std::is_same_v<typename decltype(data_member_v)::type, const int()>);
static_assert(std::is_same_v<typename decltype(data_member_v_const)::type, const int()>);
static_assert(std::is_same_v<typename decltype(lambda)::type, int(int)>);
ASSERT_TRUE(func);
ASSERT_TRUE(curried_func_with_ref);
ASSERT_TRUE(curried_func_with_const_ref);
ASSERT_TRUE(curried_func_with_ptr);
ASSERT_TRUE(curried_func_with_const_ptr);
ASSERT_TRUE(member_func_f);
ASSERT_TRUE(member_func_g);
ASSERT_TRUE(member_func_h);
ASSERT_TRUE(member_func_h_const);
ASSERT_TRUE(member_func_i);
ASSERT_TRUE(member_func_i_const);
ASSERT_TRUE(data_member_u);
ASSERT_TRUE(data_member_v);
ASSERT_TRUE(data_member_v_const);
ASSERT_TRUE(lambda);
}
TEST(Delegate, ConstInstance) {
entt::delegate<int(int)> delegate;
const delegate_functor functor;
ASSERT_FALSE(delegate);
delegate.connect<&delegate_functor::identity>(functor);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(3), 3);
delegate.reset();
ASSERT_FALSE(delegate);
ASSERT_EQ(delegate, entt::delegate<int(int)>{});
}
TEST(Delegate, NonConstReference) {
entt::delegate<int(int &)> delegate;
delegate.connect<&non_const_reference>();
int value = 3;
ASSERT_EQ(delegate(value), value);
ASSERT_EQ(value, 9);
}
TEST(Delegate, MoveOnlyType) {
entt::delegate<int(std::unique_ptr<int>)> delegate;
auto ptr = std::make_unique<int>(3);
delegate.connect<&move_only_type>();
ASSERT_EQ(delegate(std::move(ptr)), 3);
ASSERT_FALSE(ptr);
}
TEST(Delegate, CurriedFunction) {
entt::delegate<int(int)> delegate;
const auto value = 3;
delegate.connect<&curried_by_ref>(value);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(1), 4);
delegate.connect<&curried_by_ptr>(&value);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(2), 6);
}
TEST(Delegate, Constructors) {
delegate_functor functor;
const auto value = 2;
entt::delegate<int(int)> empty{};
entt::delegate<int(int)> func{entt::connect_arg<&delegate_function>};
entt::delegate<int(int)> ref{entt::connect_arg<&curried_by_ref>, value};
entt::delegate<int(int)> ptr{entt::connect_arg<&curried_by_ptr>, &value};
entt::delegate<int(int)> member{entt::connect_arg<&delegate_functor::operator()>, functor};
ASSERT_FALSE(empty);
ASSERT_TRUE(func);
ASSERT_EQ(9, func(3));
ASSERT_TRUE(ref);
ASSERT_EQ(5, ref(3));
ASSERT_TRUE(ptr);
ASSERT_EQ(6, ptr(3));
ASSERT_TRUE(member);
ASSERT_EQ(6, member(3));
}
TEST(Delegate, VoidVsNonVoidReturnType) {
delegate_functor functor;
entt::delegate<void(int)> func{entt::connect_arg<&delegate_function>};
entt::delegate<void(int)> member{entt::connect_arg<&delegate_functor::operator()>, &functor};
entt::delegate<void(int)> cmember{entt::connect_arg<&delegate_functor::identity>, &std::as_const(functor)};
ASSERT_TRUE(func);
ASSERT_TRUE(member);
ASSERT_TRUE(cmember);
}
TEST(Delegate, UnboundDataMember) {
entt::delegate<int(const delegate_functor &)> delegate;
delegate.connect<&delegate_functor::data_member>();
delegate_functor functor;
ASSERT_EQ(delegate(functor), 42);
}
TEST(Delegate, UnboundMemberFunction) {
entt::delegate<int(delegate_functor *, const int &i)> delegate;
delegate.connect<&delegate_functor::operator()>();
delegate_functor functor;
ASSERT_EQ(delegate(&functor, 3), 6);
}
TEST(Delegate, TheLessTheBetter) {
entt::delegate<int(int, char)> bound;
entt::delegate<int(delegate_functor &, int, char)> unbound;
delegate_functor functor;
// int delegate_function(const int &);
bound.connect<&delegate_function>();
ASSERT_EQ(bound(3, 'c'), 9);
// int delegate_functor::operator()(int);
bound.connect<&delegate_functor::operator()>(functor);
ASSERT_EQ(bound(3, 'c'), 6);
// int delegate_functor::operator()(int);
bound.connect<&delegate_functor::identity>(&functor);
ASSERT_EQ(bound(3, 'c'), 3);
// int delegate_functor::operator()(int);
unbound.connect<&delegate_functor::operator()>();
ASSERT_EQ(unbound(functor, 3, 'c'), 6);
}

View File

@ -0,0 +1,200 @@
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/signal/dispatcher.hpp>
struct an_event {};
struct another_event {};
// makes the type non-aggregate
struct one_more_event {
one_more_event(int) {}
};
struct receiver {
static void forward(entt::dispatcher &dispatcher, an_event &event) {
dispatcher.enqueue(event);
}
void receive(const an_event &) {
++cnt;
}
void reset() {
cnt = 0;
}
int cnt{0};
};
TEST(Dispatcher, Functionalities) {
entt::dispatcher dispatcher;
entt::dispatcher other;
receiver receiver;
ASSERT_NO_FATAL_FAILURE(entt::dispatcher{std::move(dispatcher)});
ASSERT_NO_FATAL_FAILURE(dispatcher = std::move(other));
ASSERT_EQ(dispatcher.size<an_event>(), 0u);
ASSERT_EQ(dispatcher.size(), 0u);
dispatcher.trigger(one_more_event{42});
dispatcher.enqueue<one_more_event>(42);
dispatcher.update<one_more_event>();
dispatcher.sink<an_event>().connect<&receiver::receive>(receiver);
dispatcher.trigger<an_event>();
dispatcher.enqueue<an_event>();
ASSERT_EQ(dispatcher.size<one_more_event>(), 0u);
ASSERT_EQ(dispatcher.size<an_event>(), 1u);
ASSERT_EQ(dispatcher.size(), 1u);
ASSERT_EQ(receiver.cnt, 1);
dispatcher.enqueue(another_event{});
dispatcher.update<another_event>();
ASSERT_EQ(dispatcher.size<another_event>(), 0u);
ASSERT_EQ(dispatcher.size<an_event>(), 1u);
ASSERT_EQ(dispatcher.size(), 1u);
ASSERT_EQ(receiver.cnt, 1);
dispatcher.update<an_event>();
dispatcher.trigger<an_event>();
ASSERT_EQ(dispatcher.size<an_event>(), 0u);
ASSERT_EQ(dispatcher.size(), 0u);
ASSERT_EQ(receiver.cnt, 3);
dispatcher.enqueue<an_event>();
dispatcher.clear<an_event>();
dispatcher.update();
dispatcher.enqueue(an_event{});
dispatcher.clear();
dispatcher.update();
ASSERT_EQ(dispatcher.size<an_event>(), 0u);
ASSERT_EQ(dispatcher.size(), 0u);
ASSERT_EQ(receiver.cnt, 3);
receiver.reset();
an_event event{};
dispatcher.sink<an_event>().disconnect<&receiver::receive>(receiver);
dispatcher.trigger<an_event>();
dispatcher.enqueue(event);
dispatcher.update();
dispatcher.trigger(std::as_const(event));
ASSERT_EQ(receiver.cnt, 0);
}
TEST(Dispatcher, Swap) {
entt::dispatcher dispatcher;
entt::dispatcher other;
receiver receiver;
dispatcher.sink<an_event>().connect<&receiver::receive>(receiver);
dispatcher.enqueue<an_event>();
ASSERT_EQ(dispatcher.size(), 1u);
ASSERT_EQ(other.size(), 0u);
ASSERT_EQ(receiver.cnt, 0);
dispatcher.swap(other);
dispatcher.update();
ASSERT_EQ(dispatcher.size(), 0u);
ASSERT_EQ(other.size(), 1u);
ASSERT_EQ(receiver.cnt, 0);
other.update();
ASSERT_EQ(dispatcher.size(), 0u);
ASSERT_EQ(other.size(), 0u);
ASSERT_EQ(receiver.cnt, 1);
}
TEST(Dispatcher, StopAndGo) {
entt::dispatcher dispatcher;
receiver receiver;
dispatcher.sink<an_event>().connect<&receiver::forward>(dispatcher);
dispatcher.sink<an_event>().connect<&receiver::receive>(receiver);
dispatcher.enqueue<an_event>();
dispatcher.update();
ASSERT_EQ(receiver.cnt, 1);
dispatcher.sink<an_event>().disconnect<&receiver::forward>(dispatcher);
dispatcher.update();
ASSERT_EQ(receiver.cnt, 2);
}
TEST(Dispatcher, OpaqueDisconnect) {
entt::dispatcher dispatcher;
receiver receiver;
dispatcher.sink<an_event>().connect<&receiver::receive>(receiver);
dispatcher.trigger<an_event>();
ASSERT_EQ(receiver.cnt, 1);
dispatcher.disconnect(receiver);
dispatcher.trigger<an_event>();
ASSERT_EQ(receiver.cnt, 1);
}
TEST(Dispatcher, NamedQueue) {
using namespace entt::literals;
entt::dispatcher dispatcher;
receiver receiver;
dispatcher.sink<an_event>("named"_hs).connect<&receiver::receive>(receiver);
dispatcher.trigger<an_event>();
ASSERT_EQ(receiver.cnt, 0);
dispatcher.trigger("named"_hs, an_event{});
ASSERT_EQ(receiver.cnt, 1);
dispatcher.enqueue<an_event>();
dispatcher.enqueue(an_event{});
dispatcher.enqueue_hint<an_event>("named"_hs);
dispatcher.enqueue_hint("named"_hs, an_event{});
dispatcher.update<an_event>();
ASSERT_EQ(receiver.cnt, 1);
dispatcher.clear<an_event>();
dispatcher.update<an_event>("named"_hs);
ASSERT_EQ(receiver.cnt, 3);
dispatcher.enqueue_hint<an_event>("named"_hs);
dispatcher.clear<an_event>("named"_hs);
dispatcher.update<an_event>("named"_hs);
ASSERT_EQ(receiver.cnt, 3);
}
TEST(Dispatcher, CustomAllocator) {
std::allocator<void> allocator;
entt::dispatcher dispatcher{allocator};
ASSERT_EQ(dispatcher.get_allocator(), allocator);
ASSERT_FALSE(dispatcher.get_allocator() != allocator);
dispatcher.enqueue<an_event>();
decltype(dispatcher) other{std::move(dispatcher), allocator};
ASSERT_EQ(other.size<an_event>(), 1u);
}

View File

@ -0,0 +1,170 @@
#include <functional>
#include <utility>
#include <gtest/gtest.h>
#include <entt/signal/emitter.hpp>
struct test_emitter: entt::emitter<test_emitter> {
using entt::emitter<test_emitter>::emitter;
};
struct foo_event {
int i;
};
struct bar_event {};
struct quux_event {};
TEST(Emitter, Move) {
test_emitter emitter;
emitter.on<foo_event>([](auto &, const auto &) {});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<foo_event>());
test_emitter other{std::move(emitter)};
ASSERT_FALSE(other.empty());
ASSERT_TRUE(other.contains<foo_event>());
ASSERT_TRUE(emitter.empty());
emitter = std::move(other);
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<foo_event>());
ASSERT_TRUE(other.empty());
}
TEST(Emitter, Swap) {
test_emitter emitter;
test_emitter other;
int value{};
emitter.on<foo_event>([&value](auto &event, const auto &) {
value = event.i;
});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(other.empty());
emitter.swap(other);
emitter.publish(foo_event{42});
ASSERT_EQ(value, 0);
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(other.empty());
other.publish(foo_event{42});
ASSERT_EQ(value, 42);
}
TEST(Emitter, Clear) {
test_emitter emitter;
ASSERT_TRUE(emitter.empty());
emitter.on<foo_event>([](auto &, const auto &) {});
emitter.on<quux_event>([](const auto &, const auto &) {});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<foo_event>());
ASSERT_TRUE(emitter.contains<quux_event>());
ASSERT_FALSE(emitter.contains<bar_event>());
emitter.erase<bar_event>();
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<foo_event>());
ASSERT_TRUE(emitter.contains<quux_event>());
ASSERT_FALSE(emitter.contains<bar_event>());
emitter.erase<foo_event>();
ASSERT_FALSE(emitter.empty());
ASSERT_FALSE(emitter.contains<foo_event>());
ASSERT_TRUE(emitter.contains<quux_event>());
ASSERT_FALSE(emitter.contains<bar_event>());
emitter.on<foo_event>([](auto &, const auto &) {});
emitter.on<bar_event>([](const auto &, const auto &) {});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<foo_event>());
ASSERT_TRUE(emitter.contains<quux_event>());
ASSERT_TRUE(emitter.contains<bar_event>());
emitter.clear();
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(emitter.contains<foo_event>());
ASSERT_FALSE(emitter.contains<bar_event>());
}
TEST(Emitter, ClearFromCallback) {
test_emitter emitter;
ASSERT_TRUE(emitter.empty());
emitter.on<foo_event>([](auto &, auto &owner) {
owner.template on<foo_event>([](auto &, auto &) {});
owner.template erase<foo_event>();
});
emitter.on<bar_event>([](const auto &, auto &owner) {
owner.template on<bar_event>([](const auto &, auto &) {});
owner.template erase<bar_event>();
});
ASSERT_FALSE(emitter.empty());
emitter.publish(foo_event{});
emitter.publish(bar_event{});
ASSERT_TRUE(emitter.empty());
}
TEST(Emitter, On) {
test_emitter emitter;
int value{};
emitter.on<foo_event>([&value](auto &event, const auto &) {
value = event.i;
});
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<foo_event>());
ASSERT_EQ(value, 0);
emitter.publish(foo_event{42});
ASSERT_EQ(value, 42);
}
TEST(Emitter, OnAndErase) {
test_emitter emitter;
std::function<void(bar_event &, test_emitter &)> func{};
emitter.on(func);
ASSERT_FALSE(emitter.empty());
ASSERT_TRUE(emitter.contains<bar_event>());
emitter.erase<bar_event>();
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(emitter.contains<bar_event>());
}
TEST(Emitter, CustomAllocator) {
std::allocator<void> allocator;
test_emitter emitter{allocator};
ASSERT_EQ(emitter.get_allocator(), allocator);
ASSERT_FALSE(emitter.get_allocator() != allocator);
emitter.on<foo_event>([](auto &, const auto &) {});
decltype(emitter) other{std::move(emitter), allocator};
ASSERT_TRUE(emitter.empty());
ASSERT_FALSE(other.empty());
}

577
test/entt/signal/sigh.cpp Normal file
View File

@ -0,0 +1,577 @@
#include <memory>
#include <utility>
#include <gtest/gtest.h>
#include <entt/signal/sigh.hpp>
struct sigh_listener {
static void f(int &v) {
v = 42;
}
bool g(int) {
k = !k;
return true;
}
bool h(const int &) {
return k;
}
// useless definition just because msvc does weird things if both are empty
void l() {
k = true && k;
}
bool k{false};
};
struct before_after {
void add(int v) {
value += v;
}
void mul(int v) {
value *= v;
}
static void static_add(int v) {
before_after::value += v;
}
static void static_mul(before_after &instance, int v) {
instance.value *= v;
}
static inline int value{};
};
struct SigH: ::testing::Test {
void SetUp() override {
before_after::value = 0;
}
};
struct const_nonconst_noexcept {
void f() {
++cnt;
}
void g() noexcept {
++cnt;
}
void h() const {
++cnt;
}
void i() const noexcept {
++cnt;
}
mutable int cnt{0};
};
TEST_F(SigH, Lifetime) {
using signal = entt::sigh<void(void)>;
ASSERT_NO_FATAL_FAILURE(signal{});
signal src{}, other{};
ASSERT_NO_FATAL_FAILURE(signal{src});
ASSERT_NO_FATAL_FAILURE(signal{std::move(other)});
ASSERT_NO_FATAL_FAILURE(src = other);
ASSERT_NO_FATAL_FAILURE(src = std::move(other));
ASSERT_NO_FATAL_FAILURE(delete new signal{});
}
TEST_F(SigH, Clear) {
entt::sigh<void(int &)> sigh;
entt::sink sink{sigh};
sink.connect<&sigh_listener::f>();
ASSERT_FALSE(sink.empty());
ASSERT_FALSE(sigh.empty());
sink.disconnect(static_cast<const void *>(nullptr));
ASSERT_FALSE(sink.empty());
ASSERT_FALSE(sigh.empty());
sink.disconnect();
ASSERT_TRUE(sink.empty());
ASSERT_TRUE(sigh.empty());
}
TEST_F(SigH, Swap) {
entt::sigh<void(int &)> sigh1;
entt::sigh<void(int &)> sigh2;
entt::sink sink1{sigh1};
entt::sink sink2{sigh2};
sink1.connect<&sigh_listener::f>();
ASSERT_FALSE(sink1.empty());
ASSERT_TRUE(sink2.empty());
ASSERT_FALSE(sigh1.empty());
ASSERT_TRUE(sigh2.empty());
sigh1.swap(sigh2);
ASSERT_TRUE(sink1.empty());
ASSERT_FALSE(sink2.empty());
ASSERT_TRUE(sigh1.empty());
ASSERT_FALSE(sigh2.empty());
}
TEST_F(SigH, Functions) {
entt::sigh<void(int &)> sigh;
entt::sink sink{sigh};
int v = 0;
sink.connect<&sigh_listener::f>();
sigh.publish(v);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(1u, sigh.size());
ASSERT_EQ(42, v);
v = 0;
sink.disconnect<&sigh_listener::f>();
sigh.publish(v);
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(0u, sigh.size());
ASSERT_EQ(v, 0);
}
TEST_F(SigH, FunctionsWithPayload) {
entt::sigh<void()> sigh;
entt::sink sink{sigh};
int v = 0;
sink.connect<&sigh_listener::f>(v);
sigh.publish();
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(1u, sigh.size());
ASSERT_EQ(42, v);
v = 0;
sink.disconnect<&sigh_listener::f>(v);
sigh.publish();
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(0u, sigh.size());
ASSERT_EQ(v, 0);
sink.connect<&sigh_listener::f>(v);
sink.disconnect(v);
sigh.publish();
ASSERT_EQ(v, 0);
}
TEST_F(SigH, Members) {
sigh_listener l1, l2;
entt::sigh<bool(int)> sigh;
entt::sink sink{sigh};
sink.connect<&sigh_listener::g>(l1);
sigh.publish(42);
ASSERT_TRUE(l1.k);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(1u, sigh.size());
sink.disconnect<&sigh_listener::g>(l1);
sigh.publish(42);
ASSERT_TRUE(l1.k);
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(0u, sigh.size());
sink.connect<&sigh_listener::g>(&l1);
sink.connect<&sigh_listener::h>(l2);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(2u, sigh.size());
sink.disconnect(static_cast<const void *>(nullptr));
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(2u, sigh.size());
sink.disconnect(&l1);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(1u, sigh.size());
}
TEST_F(SigH, Collector) {
sigh_listener listener;
entt::sigh<bool(int)> sigh;
entt::sink sink{sigh};
int cnt = 0;
sink.connect<&sigh_listener::g>(&listener);
sink.connect<&sigh_listener::h>(listener);
auto no_return = [&listener, &cnt](bool value) {
ASSERT_TRUE(value);
listener.k = true;
++cnt;
};
listener.k = true;
sigh.collect(std::move(no_return), 42);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(cnt, 2);
auto bool_return = [&cnt](bool value) {
// gtest and its macro hell are sometimes really annoying...
[](auto v) { ASSERT_TRUE(v); }(value);
++cnt;
return true;
};
cnt = 0;
sigh.collect(std::move(bool_return), 42);
ASSERT_EQ(cnt, 1);
}
TEST_F(SigH, CollectorVoid) {
sigh_listener listener;
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
int cnt = 0;
sink.connect<&sigh_listener::g>(&listener);
sink.connect<&sigh_listener::h>(listener);
sigh.collect([&cnt]() { ++cnt; }, 42);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(cnt, 2);
auto test = [&cnt]() {
++cnt;
return true;
};
cnt = 0;
sigh.collect(std::move(test), 42);
ASSERT_EQ(cnt, 1);
}
TEST_F(SigH, Connection) {
entt::sigh<void(int &)> sigh;
entt::sink sink{sigh};
int v = 0;
auto conn = sink.connect<&sigh_listener::f>();
sigh.publish(v);
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(conn);
ASSERT_EQ(42, v);
v = 0;
conn.release();
sigh.publish(v);
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(conn);
ASSERT_EQ(0, v);
}
TEST_F(SigH, ScopedConnection) {
sigh_listener listener;
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
{
ASSERT_FALSE(listener.k);
entt::scoped_connection conn = sink.connect<&sigh_listener::g>(listener);
sigh.publish(42);
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(listener.k);
ASSERT_TRUE(conn);
}
sigh.publish(42);
ASSERT_TRUE(sigh.empty());
ASSERT_TRUE(listener.k);
}
TEST_F(SigH, ScopedConnectionMove) {
sigh_listener listener;
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
entt::scoped_connection outer{sink.connect<&sigh_listener::g>(listener)};
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(outer);
{
entt::scoped_connection inner{std::move(outer)};
ASSERT_FALSE(listener.k);
ASSERT_FALSE(outer);
ASSERT_TRUE(inner);
sigh.publish(42);
ASSERT_TRUE(listener.k);
}
ASSERT_TRUE(sigh.empty());
outer = sink.connect<&sigh_listener::g>(listener);
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(outer);
{
entt::scoped_connection inner{};
ASSERT_TRUE(listener.k);
ASSERT_TRUE(outer);
ASSERT_FALSE(inner);
inner = std::move(outer);
ASSERT_FALSE(outer);
ASSERT_TRUE(inner);
sigh.publish(42);
ASSERT_FALSE(listener.k);
}
ASSERT_TRUE(sigh.empty());
}
TEST_F(SigH, ScopedConnectionConstructorsAndOperators) {
sigh_listener listener;
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
{
entt::scoped_connection inner{};
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(listener.k);
ASSERT_FALSE(inner);
inner = sink.connect<&sigh_listener::g>(listener);
sigh.publish(42);
ASSERT_FALSE(sigh.empty());
ASSERT_TRUE(listener.k);
ASSERT_TRUE(inner);
inner.release();
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(inner);
auto basic = sink.connect<&sigh_listener::g>(listener);
inner = std::as_const(basic);
sigh.publish(42);
ASSERT_FALSE(sigh.empty());
ASSERT_FALSE(listener.k);
ASSERT_TRUE(inner);
}
sigh.publish(42);
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(listener.k);
}
TEST_F(SigH, ConstNonConstNoExcept) {
entt::sigh<void()> sigh;
entt::sink sink{sigh};
const_nonconst_noexcept functor;
const const_nonconst_noexcept cfunctor;
sink.connect<&const_nonconst_noexcept::f>(functor);
sink.connect<&const_nonconst_noexcept::g>(&functor);
sink.connect<&const_nonconst_noexcept::h>(cfunctor);
sink.connect<&const_nonconst_noexcept::i>(&cfunctor);
sigh.publish();
ASSERT_EQ(functor.cnt, 2);
ASSERT_EQ(cfunctor.cnt, 2);
sink.disconnect<&const_nonconst_noexcept::f>(functor);
sink.disconnect<&const_nonconst_noexcept::g>(&functor);
sink.disconnect<&const_nonconst_noexcept::h>(cfunctor);
sink.disconnect<&const_nonconst_noexcept::i>(&cfunctor);
sigh.publish();
ASSERT_EQ(functor.cnt, 2);
ASSERT_EQ(cfunctor.cnt, 2);
}
TEST_F(SigH, BeforeFunction) {
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
before_after functor;
sink.connect<&before_after::add>(functor);
sink.connect<&before_after::static_add>();
sink.before<&before_after::static_add>().connect<&before_after::mul>(functor);
sigh.publish(2);
ASSERT_EQ(functor.value, 6);
}
TEST_F(SigH, BeforeMemberFunction) {
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
before_after functor;
sink.connect<&before_after::static_add>();
sink.connect<&before_after::add>(functor);
sink.before<&before_after::add>(functor).connect<&before_after::mul>(functor);
sigh.publish(2);
ASSERT_EQ(functor.value, 6);
}
TEST_F(SigH, BeforeFunctionWithPayload) {
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
before_after functor;
sink.connect<&before_after::static_add>();
sink.connect<&before_after::static_mul>(functor);
sink.before<&before_after::static_mul>(functor).connect<&before_after::add>(functor);
sigh.publish(2);
ASSERT_EQ(functor.value, 8);
}
TEST_F(SigH, BeforeInstanceOrPayload) {
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
before_after functor;
sink.connect<&before_after::static_mul>(functor);
sink.connect<&before_after::add>(functor);
sink.before(functor).connect<&before_after::static_add>();
sigh.publish(2);
ASSERT_EQ(functor.value, 6);
}
TEST_F(SigH, BeforeAnythingElse) {
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
before_after functor;
sink.connect<&before_after::add>(functor);
sink.before().connect<&before_after::mul>(functor);
sigh.publish(2);
ASSERT_EQ(functor.value, 2);
}
TEST_F(SigH, BeforeListenerNotPresent) {
entt::sigh<void(int)> sigh;
entt::sink sink{sigh};
before_after functor;
sink.connect<&before_after::mul>(functor);
sink.before<&before_after::add>(&functor).connect<&before_after::add>(functor);
sigh.publish(2);
ASSERT_EQ(functor.value, 2);
}
TEST_F(SigH, UnboundDataMember) {
sigh_listener listener;
entt::sigh<bool &(sigh_listener &)> sigh;
entt::sink sink{sigh};
ASSERT_FALSE(listener.k);
sink.connect<&sigh_listener::k>();
sigh.collect([](bool &value) { value = !value; }, listener);
ASSERT_TRUE(listener.k);
}
TEST_F(SigH, UnboundMemberFunction) {
sigh_listener listener;
entt::sigh<void(sigh_listener *, int)> sigh;
entt::sink sink{sigh};
ASSERT_FALSE(listener.k);
sink.connect<&sigh_listener::g>();
sigh.publish(&listener, 42);
ASSERT_TRUE(listener.k);
}
TEST_F(SigH, CustomAllocator) {
std::allocator<void (*)(int)> allocator;
entt::sigh<void(int), decltype(allocator)> sigh{allocator};
ASSERT_EQ(sigh.get_allocator(), allocator);
ASSERT_FALSE(sigh.get_allocator() != allocator);
ASSERT_TRUE(sigh.empty());
entt::sink sink{sigh};
sigh_listener listener;
sink.template connect<&sigh_listener::g>(listener);
decltype(sigh) copy{sigh, allocator};
sink.disconnect(listener);
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(copy.empty());
sigh = copy;
ASSERT_FALSE(sigh.empty());
ASSERT_FALSE(copy.empty());
decltype(sigh) move{std::move(copy), allocator};
ASSERT_TRUE(copy.empty());
ASSERT_FALSE(move.empty());
sink = entt::sink{move};
sink.disconnect(&listener);
ASSERT_TRUE(copy.empty());
ASSERT_TRUE(move.empty());
sink.template connect<&sigh_listener::g>(listener);
copy.swap(move);
ASSERT_FALSE(copy.empty());
ASSERT_TRUE(move.empty());
sink = entt::sink{copy};
sink.disconnect();
ASSERT_TRUE(copy.empty());
ASSERT_TRUE(move.empty());
}

View File

@ -0,0 +1,53 @@
#include <cstdint>
#include <gtest/gtest.h>
#include <entt/entity/entity.hpp>
#include <entt/entity/registry.hpp>
struct entity_id {
using entity_type = std::uint32_t;
static constexpr auto null = entt::null;
constexpr entity_id(entity_type value = null) noexcept
: entt{value} {}
constexpr entity_id(const entity_id &other) noexcept
: entt{other.entt} {}
constexpr operator entity_type() const noexcept {
return entt;
}
private:
entity_type entt;
};
TEST(Example, CustomIdentifier) {
entt::basic_registry<entity_id> registry{};
entity_id entity{};
ASSERT_FALSE(registry.valid(entity));
ASSERT_TRUE(entity == entt::null);
entity = registry.create();
ASSERT_TRUE(registry.valid(entity));
ASSERT_TRUE(entity != entt::null);
ASSERT_FALSE((registry.all_of<int, char>(entity)));
ASSERT_EQ(registry.try_get<int>(entity), nullptr);
registry.emplace<int>(entity, 42);
ASSERT_TRUE((registry.any_of<int, char>(entity)));
ASSERT_EQ(registry.get<int>(entity), 42);
registry.destroy(entity);
ASSERT_FALSE(registry.valid(entity));
ASSERT_TRUE(entity != entt::null);
entity = registry.create();
ASSERT_TRUE(registry.valid(entity));
ASSERT_TRUE(entity != entt::null);
}

View File

@ -0,0 +1,78 @@
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/entity/registry.hpp>
enum class my_entity : entt::id_type {};
TEST(Example, EntityCopy) {
using namespace entt::literals;
entt::registry registry{};
auto &&custom = registry.storage<double>("custom"_hs);
const auto src = registry.create();
const auto dst = registry.create();
const auto other = registry.create();
custom.emplace(src, 1.);
registry.emplace<int>(src, 42);
registry.emplace<char>(src, 'c');
registry.emplace<double>(other, 3.);
ASSERT_TRUE(custom.contains(src));
ASSERT_FALSE(registry.all_of<double>(src));
ASSERT_TRUE((registry.all_of<int, char>(src)));
ASSERT_FALSE((registry.any_of<int, char, double>(dst)));
ASSERT_FALSE(custom.contains(dst));
for(auto [id, storage]: registry.storage()) {
// discard the custom storage because why not, this is just an example after all
if(id != "custom"_hs && storage.contains(src)) {
storage.emplace(dst, storage.get(src));
}
}
ASSERT_TRUE((registry.all_of<int, char>(dst)));
ASSERT_FALSE((registry.all_of<double>(dst)));
ASSERT_FALSE(custom.contains(dst));
ASSERT_EQ(registry.get<int>(dst), 42);
ASSERT_EQ(registry.get<char>(dst), 'c');
}
TEST(Example, DifferentRegistryTypes) {
using namespace entt::literals;
entt::basic_registry<entt::entity> src{};
entt::basic_registry<my_entity> dst{};
/*
TODO These are currently needed to ensure that the source and
target registries have the proper storage initialized
prior to copying, as this isn't done automatically
when emplacing storages (as is done below).
There is an open issue about this, and these two
lines should be removed when a fix is properly landed.
https://github.com/skypjack/entt/issues/827
*/
static_cast<void>(src.storage<double>());
static_cast<void>(dst.storage<int>());
const auto entity = src.create();
const auto copy = dst.create();
src.emplace<int>(entity, 42);
src.emplace<char>(entity, 'c');
for(auto [id, storage]: src.storage()) {
if(auto *other = dst.storage(id); other && storage.contains(entity)) {
other->emplace(copy, storage.get(entity));
}
}
ASSERT_TRUE((src.all_of<int, char>(entity)));
ASSERT_FALSE(dst.all_of<char>(copy));
ASSERT_TRUE(dst.all_of<int>(copy));
ASSERT_EQ(dst.get<int>(copy), 42);
}

View File

@ -0,0 +1,43 @@
#include <iterator>
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/entity/entity.hpp>
#include <entt/entity/registry.hpp>
template<typename Type, typename Entity>
struct entt::storage_type<Type, Entity> {
// no signal regardless of component type ...
using type = basic_storage<Type, Entity>;
};
template<typename Entity>
struct entt::storage_type<char, Entity> {
// ... unless it's char, because yes.
using type = sigh_storage_mixin<basic_storage<char, Entity>>;
};
template<typename, typename, typename = void>
struct has_on_construct: std::false_type {};
template<typename Entity, typename Type>
struct has_on_construct<Entity, Type, std::void_t<decltype(&entt::storage_type_t<Type>::on_construct)>>: std::true_type {};
template<typename Entity, typename Type>
inline constexpr auto has_on_construct_v = has_on_construct<Entity, Type>::value;
TEST(Example, SignalLess) {
// invoking registry::on_construct<int> is a compile-time error
static_assert(!has_on_construct_v<entt::entity, int>);
static_assert(has_on_construct_v<entt::entity, char>);
entt::registry registry;
const entt::entity entity[1u]{registry.create()};
// literally a test for storage_adapter_mixin
registry.emplace<int>(entity[0], 0);
registry.erase<int>(entity[0]);
registry.insert<int>(std::begin(entity), std::end(entity), 3);
registry.patch<int>(entity[0], [](auto &value) { value = 42; });
ASSERT_EQ(registry.get<int>(entity[0]), 42);
}

View File

@ -0,0 +1,8 @@
#include <entt/core/attribute.h>
#include <entt/signal/dispatcher.hpp>
#include "types.h"
ENTT_API void trigger(entt::dispatcher &dispatcher) {
dispatcher.trigger<event>();
dispatcher.trigger(message{42});
}

View File

@ -0,0 +1,28 @@
#include <gtest/gtest.h>
#include <entt/core/attribute.h>
#include <entt/core/utility.hpp>
#include <entt/signal/dispatcher.hpp>
#include <entt/signal/sigh.hpp>
#include "types.h"
ENTT_API void trigger(entt::dispatcher &);
struct listener {
void on(message msg) {
value = msg.payload;
}
int value{};
};
TEST(Lib, Dispatcher) {
entt::dispatcher dispatcher;
listener listener;
ASSERT_EQ(listener.value, 0);
dispatcher.sink<message>().connect<entt::overload<void(message)>(&listener::on)>(listener);
trigger(dispatcher);
ASSERT_EQ(listener.value, 42);
}

View File

@ -0,0 +1,12 @@
#ifndef ENTT_LIB_DISPATCHER_TYPES_H
#define ENTT_LIB_DISPATCHER_TYPES_H
#include <entt/core/attribute.h>
struct ENTT_API message {
int payload;
};
struct ENTT_API event {};
#endif

View File

@ -0,0 +1,35 @@
#define CR_HOST
#include <gtest/gtest.h>
#include <cr.h>
#include <entt/signal/dispatcher.hpp>
#include <entt/signal/sigh.hpp>
#include "types.h"
struct listener {
void on(message msg) {
value = msg.payload;
}
int value{};
};
TEST(Lib, Dispatcher) {
entt::dispatcher dispatcher;
listener listener;
ASSERT_EQ(listener.value, 0);
dispatcher.sink<message>().connect<&listener::on>(listener);
cr_plugin ctx;
cr_plugin_load(ctx, PLUGIN);
ctx.userdata = &dispatcher;
cr_plugin_update(ctx);
ASSERT_EQ(listener.value, 42);
dispatcher = {};
cr_plugin_close(ctx);
}

View File

@ -0,0 +1,19 @@
#include <cr.h>
#include <entt/signal/dispatcher.hpp>
#include "types.h"
CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
switch(operation) {
case CR_STEP:
static_cast<entt::dispatcher *>(ctx->userdata)->trigger<event>();
static_cast<entt::dispatcher *>(ctx->userdata)->trigger(message{42});
break;
case CR_CLOSE:
case CR_LOAD:
case CR_UNLOAD:
// nothing to do here, this is only a test.
break;
}
return 0;
}

View File

@ -0,0 +1,10 @@
#ifndef ENTT_LIB_DISPATCHER_PLUGIN_TYPES_H
#define ENTT_LIB_DISPATCHER_PLUGIN_TYPES_H
struct message {
int payload;
};
struct event {};
#endif

8
test/lib/emitter/lib.cpp Normal file
View File

@ -0,0 +1,8 @@
#include <entt/core/attribute.h>
#include "types.h"
ENTT_API void emit(test_emitter &emitter) {
emitter.publish(event{});
emitter.publish(message{42});
emitter.publish(message{3});
}

21
test/lib/emitter/main.cpp Normal file
View File

@ -0,0 +1,21 @@
#include <gtest/gtest.h>
#include <entt/core/attribute.h>
#include "types.h"
ENTT_API void emit(test_emitter &);
TEST(Lib, Emitter) {
test_emitter emitter;
int value{};
ASSERT_EQ(value, 0);
emitter.on<message>([&](message msg, test_emitter &owner) {
value = msg.payload;
owner.erase<message>();
});
emit(emitter);
ASSERT_EQ(value, 42);
}

16
test/lib/emitter/types.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef ENTT_LIB_EMITTER_TYPES_H
#define ENTT_LIB_EMITTER_TYPES_H
#include <entt/core/attribute.h>
#include <entt/signal/emitter.hpp>
struct ENTT_API test_emitter
: entt::emitter<test_emitter> {};
struct ENTT_API message {
int payload;
};
struct ENTT_API event {};
#endif

View File

@ -0,0 +1,28 @@
#define CR_HOST
#include <gtest/gtest.h>
#include <cr.h>
#include "types.h"
TEST(Lib, Emitter) {
test_emitter emitter;
int value{};
ASSERT_EQ(value, 0);
emitter.on<message>([&](message msg, test_emitter &owner) {
value = msg.payload;
owner.erase<message>();
});
cr_plugin ctx;
cr_plugin_load(ctx, PLUGIN);
ctx.userdata = &emitter;
cr_plugin_update(ctx);
ASSERT_EQ(value, 42);
emitter = {};
cr_plugin_close(ctx);
}

View File

@ -0,0 +1,19 @@
#include <cr.h>
#include "types.h"
CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
switch(operation) {
case CR_STEP:
static_cast<test_emitter *>(ctx->userdata)->publish(event{});
static_cast<test_emitter *>(ctx->userdata)->publish(message{42});
static_cast<test_emitter *>(ctx->userdata)->publish(message{3});
break;
case CR_CLOSE:
case CR_LOAD:
case CR_UNLOAD:
// nothing to do here, this is only a test.
break;
}
return 0;
}

View File

@ -0,0 +1,15 @@
#ifndef ENTT_LIB_EMITTER_PLUGIN_TYPES_H
#define ENTT_LIB_EMITTER_PLUGIN_TYPES_H
#include <entt/signal/emitter.hpp>
struct test_emitter
: entt::emitter<test_emitter> {};
struct message {
int payload;
};
struct event {};
#endif

11
test/lib/locator/lib.cpp Normal file
View File

@ -0,0 +1,11 @@
#include <entt/core/attribute.h>
#include <entt/locator/locator.hpp>
#include "types.h"
ENTT_API void set_up(const entt::locator<service>::node_type &handle) {
entt::locator<service>::reset(handle);
}
ENTT_API void use_service(int value) {
entt::locator<service>::value().value = value;
}

24
test/lib/locator/main.cpp Normal file
View File

@ -0,0 +1,24 @@
#include <gtest/gtest.h>
#include <entt/core/attribute.h>
#include <entt/locator/locator.hpp>
#include "types.h"
ENTT_API void set_up(const entt::locator<service>::node_type &);
ENTT_API void use_service(int);
TEST(Lib, Locator) {
entt::locator<service>::emplace().value = 42;
ASSERT_EQ(entt::locator<service>::value().value, 42);
set_up(entt::locator<service>::handle());
use_service(3);
ASSERT_EQ(entt::locator<service>::value().value, 3);
// service updates do not propagate across boundaries
entt::locator<service>::emplace().value = 42;
use_service(3);
ASSERT_EQ(entt::locator<service>::value().value, 42);
}

8
test/lib/locator/types.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef ENTT_LIB_LOCATOR_TYPES_H
#define ENTT_LIB_LOCATOR_TYPES_H
struct service {
int value;
};
#endif

View File

@ -0,0 +1,32 @@
#define CR_HOST
#include <gtest/gtest.h>
#include <cr.h>
#include <entt/locator/locator.hpp>
#include "types.h"
TEST(Lib, Locator) {
entt::locator<service>::emplace().value = 42;
ASSERT_EQ(entt::locator<service>::value().value, 42);
userdata ud{};
ud.handle = entt::locator<service>::handle();
ud.value = 3;
cr_plugin ctx;
ctx.userdata = &ud;
cr_plugin_load(ctx, PLUGIN);
cr_plugin_update(ctx);
ASSERT_EQ(entt::locator<service>::value().value, ud.value);
// service updates do not propagate across boundaries
entt::locator<service>::emplace().value = 42;
cr_plugin_update(ctx);
ASSERT_NE(entt::locator<service>::value().value, ud.value);
cr_plugin_close(ctx);
}

View File

@ -0,0 +1,19 @@
#include <cr.h>
#include <entt/locator/locator.hpp>
#include "types.h"
CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
switch(operation) {
case CR_LOAD:
entt::locator<service>::reset(static_cast<userdata *>(ctx->userdata)->handle);
break;
case CR_STEP:
entt::locator<service>::value().value = static_cast<userdata *>(ctx->userdata)->value;
break;
case CR_UNLOAD:
case CR_CLOSE:
break;
}
return 0;
}

View File

@ -0,0 +1,16 @@
#ifndef ENTT_LIB_LOCATOR_PLUGIN_TYPES_H
#define ENTT_LIB_LOCATOR_PLUGIN_TYPES_H
#include <entt/locator/locator.hpp>
struct service {
int value;
};
struct userdata {
using node_type = typename entt::locator<service>::node_type;
node_type handle;
int value;
};
#endif

38
test/lib/meta/lib.cpp Normal file
View File

@ -0,0 +1,38 @@
#include <entt/core/attribute.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include "types.h"
position create_position(int x, int y) {
return position{x, y};
}
ENTT_API void share(entt::locator<entt::meta_ctx>::node_type handle) {
entt::locator<entt::meta_ctx>::reset(handle);
}
ENTT_API void set_up() {
using namespace entt::literals;
entt::meta<position>()
.type("position"_hs)
.ctor<&create_position>()
.data<&position::x>("x"_hs)
.data<&position::y>("y"_hs);
entt::meta<velocity>()
.type("velocity"_hs)
.ctor<>()
.data<&velocity::dx>("dx"_hs)
.data<&velocity::dy>("dy"_hs);
}
ENTT_API void tear_down() {
entt::meta_reset<position>();
entt::meta_reset<velocity>();
}
ENTT_API entt::meta_any wrap_int(int value) {
return value;
}

55
test/lib/meta/main.cpp Normal file
View File

@ -0,0 +1,55 @@
#include <gtest/gtest.h>
#include <entt/core/attribute.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include "types.h"
ENTT_API void share(entt::locator<entt::meta_ctx>::node_type);
ENTT_API void set_up();
ENTT_API void tear_down();
ENTT_API entt::meta_any wrap_int(int);
TEST(Lib, Meta) {
using namespace entt::literals;
ASSERT_FALSE(entt::resolve("position"_hs));
ASSERT_FALSE(entt::resolve("velocity"_hs));
share(entt::locator<entt::meta_ctx>::handle());
set_up();
entt::meta<double>().conv<int>();
ASSERT_TRUE(entt::resolve("position"_hs));
ASSERT_TRUE(entt::resolve("velocity"_hs));
ASSERT_EQ(entt::resolve<position>(), entt::resolve("position"_hs));
ASSERT_EQ(entt::resolve<velocity>(), entt::resolve("velocity"_hs));
auto pos = entt::resolve("position"_hs).construct(42., 3.);
auto vel = entt::resolve("velocity"_hs).construct();
ASSERT_TRUE(pos && vel);
ASSERT_EQ(pos.type().data("x"_hs).type(), entt::resolve<int>());
ASSERT_NE(pos.type().data("y"_hs).get(pos).try_cast<int>(), nullptr);
ASSERT_EQ(pos.type().data("x"_hs).get(pos).cast<int>(), 42);
ASSERT_EQ(pos.type().data("y"_hs).get(pos).cast<int>(), 3);
ASSERT_EQ(vel.type().data("dx"_hs).type(), entt::resolve<double>());
ASSERT_TRUE(vel.type().data("dy"_hs).get(vel).allow_cast<double>());
ASSERT_EQ(vel.type().data("dx"_hs).get(vel).cast<double>(), 0.);
ASSERT_EQ(vel.type().data("dy"_hs).get(vel).cast<double>(), 0.);
pos.reset();
vel.reset();
ASSERT_EQ(wrap_int(42).type(), entt::resolve<int>());
ASSERT_EQ(wrap_int(42).cast<int>(), 42);
tear_down();
ASSERT_FALSE(entt::resolve("position"_hs));
ASSERT_FALSE(entt::resolve("velocity"_hs));
}

14
test/lib/meta/types.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef ENTT_LIB_META_TYPES_H
#define ENTT_LIB_META_TYPES_H
struct position {
int x;
int y;
};
struct velocity {
double dx;
double dy;
};
#endif

View File

@ -0,0 +1,58 @@
#define CR_HOST
#include <gtest/gtest.h>
#include <cr.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include "types.h"
TEST(Lib, Meta) {
using namespace entt::literals;
ASSERT_FALSE(entt::resolve("position"_hs));
userdata ud{};
ud.ctx = entt::locator<entt::meta_ctx>::handle();
cr_plugin ctx;
ctx.userdata = &ud;
cr_plugin_load(ctx, PLUGIN);
cr_plugin_update(ctx);
entt::meta<double>().conv<int>();
ASSERT_TRUE(entt::resolve("position"_hs));
ASSERT_TRUE(entt::resolve("velocity"_hs));
auto pos = entt::resolve("position"_hs).construct(42., 3.);
auto vel = entt::resolve("velocity"_hs).construct();
ASSERT_TRUE(pos && vel);
ASSERT_EQ(pos.type().data("x"_hs).type(), entt::resolve<int>());
ASSERT_NE(pos.type().data("y"_hs).get(pos).try_cast<int>(), nullptr);
ASSERT_EQ(pos.type().data("x"_hs).get(pos).cast<int>(), 42);
ASSERT_EQ(pos.type().data("y"_hs).get(pos).cast<int>(), 3);
ASSERT_EQ(vel.type().data("dx"_hs).type(), entt::resolve<double>());
ASSERT_TRUE(vel.type().data("dy"_hs).get(vel).allow_cast<double>());
ASSERT_EQ(vel.type().data("dx"_hs).get(vel).cast<double>(), 0.);
ASSERT_EQ(vel.type().data("dy"_hs).get(vel).cast<double>(), 0.);
ASSERT_EQ(ud.any.type(), entt::resolve<int>());
ASSERT_EQ(ud.any.cast<int>(), 42);
// these objects have been initialized from a different context
pos.emplace<void>();
vel.emplace<void>();
ud.any.emplace<void>();
cr_plugin_close(ctx);
ASSERT_FALSE(entt::resolve("position"_hs));
ASSERT_FALSE(entt::resolve("velocity"_hs));
}

View File

@ -0,0 +1,50 @@
#include <cr.h>
#include <entt/core/hashed_string.hpp>
#include <entt/locator/locator.hpp>
#include <entt/meta/context.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include "types.h"
position create_position(int x, int y) {
return position{x, y};
}
void set_up() {
using namespace entt::literals;
entt::meta<position>()
.type("position"_hs)
.ctor<&create_position>()
.data<&position::x>("x"_hs)
.data<&position::y>("y"_hs);
entt::meta<velocity>()
.type("velocity"_hs)
.ctor<>()
.data<&velocity::dx>("dx"_hs)
.data<&velocity::dy>("dy"_hs);
}
void tear_down() {
entt::meta_reset<position>();
entt::meta_reset<velocity>();
}
CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
switch(operation) {
case CR_LOAD:
entt::locator<entt::meta_ctx>::reset(static_cast<userdata *>(ctx->userdata)->ctx);
set_up();
break;
case CR_STEP:
static_cast<userdata *>(ctx->userdata)->any = 42;
break;
case CR_UNLOAD:
case CR_CLOSE:
tear_down();
break;
}
return 0;
}

View File

@ -0,0 +1,22 @@
#ifndef ENTT_LIB_META_PLUGIN_TYPES_H
#define ENTT_LIB_META_PLUGIN_TYPES_H
#include <entt/locator/locator.hpp>
#include <entt/meta/meta.hpp>
struct position {
int x;
int y;
};
struct velocity {
double dx;
double dy;
};
struct userdata {
entt::locator<entt::meta_ctx>::node_type ctx;
entt::meta_any any;
};
#endif

View File

@ -0,0 +1,57 @@
#define CR_HOST
#include <gtest/gtest.h>
#include <cr.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
#include "types.h"
TEST(Lib, Meta) {
using namespace entt::literals;
ASSERT_FALSE(entt::resolve("position"_hs));
userdata ud{};
ud.ctx = entt::locator<entt::meta_ctx>::handle();
cr_plugin ctx;
ctx.userdata = &ud;
cr_plugin_load(ctx, PLUGIN);
cr_plugin_update(ctx);
entt::meta<double>().conv<int>();
ASSERT_TRUE(entt::resolve("position"_hs));
ASSERT_TRUE(entt::resolve("velocity"_hs));
auto pos = entt::resolve("position"_hs).construct(42., 3.);
auto vel = entt::resolve("velocity"_hs).construct();
ASSERT_TRUE(pos && vel);
ASSERT_EQ(pos.type().data("x"_hs).type(), entt::resolve<int>());
ASSERT_NE(pos.type().data("y"_hs).get(pos).try_cast<int>(), nullptr);
ASSERT_EQ(pos.type().data("x"_hs).get(pos).cast<int>(), 42);
ASSERT_EQ(pos.type().data("y"_hs).get(pos).cast<int>(), 3);
ASSERT_EQ(vel.type().data("dx"_hs).type(), entt::resolve<double>());
ASSERT_TRUE(vel.type().data("dy"_hs).get(vel).allow_cast<double>());
ASSERT_EQ(vel.type().data("dx"_hs).get(vel).cast<double>(), 0.);
ASSERT_EQ(vel.type().data("dy"_hs).get(vel).cast<double>(), 0.);
ASSERT_EQ(ud.any.type(), entt::resolve<int>());
ASSERT_EQ(ud.any.cast<int>(), 42);
// these objects have been initialized from a different context
pos.emplace<void>();
vel.emplace<void>();
ud.any.emplace<void>();
cr_plugin_close(ctx);
ASSERT_FALSE(entt::resolve("position"_hs));
ASSERT_FALSE(entt::resolve("velocity"_hs));
}

View File

@ -0,0 +1,49 @@
#include <cr.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/context.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include "types.h"
position create_position(int x, int y) {
return position{x, y};
}
void set_up() {
using namespace entt::literals;
entt::meta<position>()
.type("position"_hs)
.ctor<&create_position>()
.data<&position::x>("x"_hs)
.data<&position::y>("y"_hs);
entt::meta<velocity>()
.type("velocity"_hs)
.ctor<>()
.data<&velocity::dx>("dx"_hs)
.data<&velocity::dy>("dy"_hs);
}
void tear_down() {
entt::meta_reset<position>();
entt::meta_reset<velocity>();
}
CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
switch(operation) {
case CR_LOAD:
entt::locator<entt::meta_ctx>::reset(static_cast<userdata *>(ctx->userdata)->ctx);
set_up();
break;
case CR_STEP:
static_cast<userdata *>(ctx->userdata)->any = 42;
break;
case CR_UNLOAD:
case CR_CLOSE:
tear_down();
break;
}
return 0;
}

View File

@ -0,0 +1,42 @@
#ifndef ENTT_LIB_META_PLUGIN_TYPES_STD_H
#define ENTT_LIB_META_PLUGIN_TYPES_STD_H
#include <type_traits>
#include <entt/core/hashed_string.hpp>
#include <entt/core/type_info.hpp>
#include <entt/meta/meta.hpp>
template<typename>
struct custom_type_hash;
#define ASSIGN_TYPE_ID(clazz) \
template<> \
struct entt::type_hash<clazz> { \
static constexpr entt::id_type value() noexcept { \
return entt::basic_hashed_string<std::remove_const_t<std::remove_pointer_t<std::decay_t<decltype(#clazz)>>>>{#clazz}; \
} \
}
struct position {
int x;
int y;
};
struct velocity {
double dx;
double dy;
};
struct userdata {
entt::locator<entt::meta_ctx>::node_type ctx;
entt::meta_any any;
};
ASSIGN_TYPE_ID(void);
ASSIGN_TYPE_ID(std::size_t);
ASSIGN_TYPE_ID(position);
ASSIGN_TYPE_ID(velocity);
ASSIGN_TYPE_ID(double);
ASSIGN_TYPE_ID(int);
#endif

19
test/lib/registry/lib.cpp Normal file
View File

@ -0,0 +1,19 @@
#include <entt/core/attribute.h>
#include <entt/entity/registry.hpp>
#include "types.h"
ENTT_API void update_position(entt::registry &registry) {
registry.view<position, velocity>().each([](auto &pos, auto &vel) {
pos.x += static_cast<int>(16 * vel.dx);
pos.y += static_cast<int>(16 * vel.dy);
});
}
ENTT_API void emplace_velocity(entt::registry &registry) {
// forces the creation of the pool for the velocity component
static_cast<void>(registry.storage<velocity>());
for(auto entity: registry.view<position>()) {
registry.emplace<velocity>(entity, 1., 1.);
}
}

View File

@ -0,0 +1,27 @@
#include <gtest/gtest.h>
#include <entt/core/attribute.h>
#include <entt/entity/entity.hpp>
#include <entt/entity/registry.hpp>
#include "types.h"
ENTT_API void update_position(entt::registry &);
ENTT_API void emplace_velocity(entt::registry &);
TEST(Lib, Registry) {
entt::registry registry;
for(auto i = 0; i < 3; ++i) {
const auto entity = registry.create();
registry.emplace<position>(entity, i, i);
}
emplace_velocity(registry);
update_position(registry);
ASSERT_EQ(registry.storage<position>().size(), registry.storage<velocity>().size());
registry.view<position>().each([](auto entity, auto &position) {
ASSERT_EQ(position.x, static_cast<int>(entt::to_integral(entity) + 16));
ASSERT_EQ(position.y, static_cast<int>(entt::to_integral(entity) + 16));
});
}

Some files were not shown because too many files have changed in this diff Show More