mirror of
https://github.com/hyprwm/hyprutils.git
synced 2025-12-20 03:30:22 +01:00
tests: move to gtest (#85)
--------- Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
This commit is contained in:
parent
9a9745d7aa
commit
01afe9245b
28 changed files with 1400 additions and 1463 deletions
4
.github/workflows/arch.yml
vendored
4
.github/workflows/arch.yml
vendored
|
|
@ -17,7 +17,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||||
pacman --noconfirm --noprogressbar -Syyu
|
pacman --noconfirm --noprogressbar -Syyu
|
||||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ pixman
|
pacman --noconfirm --noprogressbar -Sy gcc gtest base-devel cmake clang libc++ pixman
|
||||||
|
|
||||||
- name: Build hyprutils with gcc
|
- name: Build hyprutils with gcc
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -44,7 +44,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||||
pacman --noconfirm --noprogressbar -Syyu
|
pacman --noconfirm --noprogressbar -Syyu
|
||||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ pixman
|
pacman --noconfirm --noprogressbar -Sy gcc gtest base-devel cmake clang libc++ pixman
|
||||||
|
|
||||||
- name: Build hyprutils with clang
|
- name: Build hyprutils with clang
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
|
|
@ -53,81 +53,22 @@ set_target_properties(hyprutils PROPERTIES VERSION ${hyprutils_VERSION}
|
||||||
SOVERSION 9)
|
SOVERSION 9)
|
||||||
target_link_libraries(hyprutils PkgConfig::deps)
|
target_link_libraries(hyprutils PkgConfig::deps)
|
||||||
|
|
||||||
# tests
|
if(BUILD_TESTING)
|
||||||
add_custom_target(tests)
|
# GTest
|
||||||
|
find_package(GTest CONFIG REQUIRED)
|
||||||
add_executable(hyprutils_memory "tests/memory.cpp")
|
include(GoogleTest)
|
||||||
target_link_libraries(hyprutils_memory PRIVATE hyprutils PkgConfig::deps)
|
add_executable(hyprutils_inline_tests ${SRCFILES})
|
||||||
add_test(
|
target_compile_definitions(hyprutils_inline_tests PRIVATE HU_UNIT_TESTS=1)
|
||||||
NAME "Memory"
|
target_compile_options(hyprutils_inline_tests PRIVATE --coverage)
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
target_link_options(hyprutils_inline_tests PRIVATE --coverage)
|
||||||
COMMAND hyprutils_memory "memory")
|
target_include_directories(
|
||||||
add_dependencies(tests hyprutils_memory)
|
hyprutils_inline_tests
|
||||||
|
PUBLIC "./include"
|
||||||
add_executable(hyprutils_string "tests/string.cpp")
|
PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}")
|
||||||
target_link_libraries(hyprutils_string PRIVATE hyprutils PkgConfig::deps)
|
target_link_libraries(hyprutils_inline_tests PRIVATE GTest::gtest_main
|
||||||
add_test(
|
PkgConfig::deps)
|
||||||
NAME "String"
|
gtest_discover_tests(hyprutils_inline_tests)
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
endif()
|
||||||
COMMAND hyprutils_string "string")
|
|
||||||
add_dependencies(tests hyprutils_string)
|
|
||||||
|
|
||||||
add_executable(hyprutils_signal "tests/signal.cpp")
|
|
||||||
target_link_libraries(hyprutils_signal PRIVATE hyprutils PkgConfig::deps)
|
|
||||||
add_test(
|
|
||||||
NAME "Signal"
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
|
||||||
COMMAND hyprutils_signal "signal")
|
|
||||||
add_dependencies(tests hyprutils_signal)
|
|
||||||
|
|
||||||
add_executable(hyprutils_math "tests/math.cpp")
|
|
||||||
target_link_libraries(hyprutils_math PRIVATE hyprutils PkgConfig::deps)
|
|
||||||
add_test(
|
|
||||||
NAME "Math"
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
|
||||||
COMMAND hyprutils_math "math")
|
|
||||||
add_dependencies(tests hyprutils_math)
|
|
||||||
|
|
||||||
add_executable(hyprutils_os "tests/os.cpp")
|
|
||||||
target_link_libraries(hyprutils_os PRIVATE hyprutils PkgConfig::deps)
|
|
||||||
add_test(
|
|
||||||
NAME "OS"
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
|
||||||
COMMAND hyprutils_os "os")
|
|
||||||
add_dependencies(tests hyprutils_os)
|
|
||||||
|
|
||||||
add_executable(hyprutils_filedescriptor "tests/filedescriptor.cpp")
|
|
||||||
target_link_libraries(hyprutils_filedescriptor PRIVATE hyprutils PkgConfig::deps)
|
|
||||||
add_test(
|
|
||||||
NAME "Filedescriptor"
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
|
||||||
COMMAND hyprutils_filedescriptor "filedescriptor")
|
|
||||||
add_dependencies(tests hyprutils_filedescriptor)
|
|
||||||
|
|
||||||
add_executable(hyprutils_animation "tests/animation.cpp")
|
|
||||||
target_link_libraries(hyprutils_animation PRIVATE hyprutils PkgConfig::deps)
|
|
||||||
add_test(
|
|
||||||
NAME "Animation"
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
|
||||||
COMMAND hyprutils_animation "utils")
|
|
||||||
add_dependencies(tests hyprutils_animation)
|
|
||||||
|
|
||||||
add_executable(hyprutils_beziercurve "tests/beziercurve.cpp")
|
|
||||||
target_link_libraries(hyprutils_beziercurve PRIVATE hyprutils PkgConfig::deps)
|
|
||||||
add_test(
|
|
||||||
NAME "BezierCurve"
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
|
||||||
COMMAND hyprutils_beziercurve "beziercurve")
|
|
||||||
add_dependencies(tests hyprutils_beziercurve)
|
|
||||||
|
|
||||||
add_executable(hyprutils_i18n "tests/i18n.cpp")
|
|
||||||
target_link_libraries(hyprutils_i18n PRIVATE hyprutils PkgConfig::deps)
|
|
||||||
add_test(
|
|
||||||
NAME "I18n"
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
|
||||||
COMMAND hyprutils_i18n "i18n")
|
|
||||||
add_dependencies(tests hyprutils_i18n)
|
|
||||||
|
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
install(TARGETS hyprutils)
|
install(TARGETS hyprutils)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
stdenvAdapters,
|
stdenvAdapters,
|
||||||
cmake,
|
cmake,
|
||||||
pkg-config,
|
pkg-config,
|
||||||
|
gtest,
|
||||||
pixman,
|
pixman,
|
||||||
version ? "git",
|
version ? "git",
|
||||||
doCheck ? false,
|
doCheck ? false,
|
||||||
|
|
@ -11,10 +12,12 @@
|
||||||
# whether to use the mold linker
|
# whether to use the mold linker
|
||||||
# disable this for older machines without SSE4_2 and AVX2 support
|
# disable this for older machines without SSE4_2 and AVX2 support
|
||||||
withMold ? true,
|
withMold ? true,
|
||||||
}: let
|
}:
|
||||||
|
let
|
||||||
inherit (builtins) foldl';
|
inherit (builtins) foldl';
|
||||||
inherit (lib.lists) flatten;
|
inherit (lib.attrsets) mapAttrsToList;
|
||||||
inherit (lib.strings) optionalString;
|
inherit (lib.lists) flatten optional;
|
||||||
|
inherit (lib.strings) cmakeBool optionalString;
|
||||||
|
|
||||||
adapters = flatten [
|
adapters = flatten [
|
||||||
(lib.optional withMold stdenvAdapters.useMoldLinker)
|
(lib.optional withMold stdenvAdapters.useMoldLinker)
|
||||||
|
|
@ -23,7 +26,7 @@
|
||||||
|
|
||||||
customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters;
|
customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters;
|
||||||
in
|
in
|
||||||
customStdenv.mkDerivation {
|
customStdenv.mkDerivation {
|
||||||
pname = "hyprutils" + optionalString debug "-debug";
|
pname = "hyprutils" + optionalString debug "-debug";
|
||||||
inherit version doCheck;
|
inherit version doCheck;
|
||||||
src = ../.;
|
src = ../.;
|
||||||
|
|
@ -33,16 +36,21 @@ in
|
||||||
pkg-config
|
pkg-config
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = flatten [
|
||||||
|
(optional doCheck gtest)
|
||||||
pixman
|
pixman
|
||||||
];
|
];
|
||||||
|
|
||||||
outputs = ["out" "dev"];
|
outputs = [
|
||||||
|
"out"
|
||||||
|
"dev"
|
||||||
|
];
|
||||||
|
|
||||||
cmakeBuildType =
|
cmakeBuildType = if debug then "Debug" else "RelWithDebInfo";
|
||||||
if debug
|
|
||||||
then "Debug"
|
cmakeFlags = mapAttrsToList cmakeBool {
|
||||||
else "RelWithDebInfo";
|
"DISABLE_TESTING" = !doCheck;
|
||||||
|
};
|
||||||
|
|
||||||
meta = with lib; {
|
meta = with lib; {
|
||||||
homepage = "https://github.com/hyprwm/hyprutils";
|
homepage = "https://github.com/hyprwm/hyprutils";
|
||||||
|
|
@ -50,4 +58,4 @@ in
|
||||||
license = licenses.bsd3;
|
license = licenses.bsd3;
|
||||||
platforms = platforms.linux;
|
platforms = platforms.linux;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,3 +99,404 @@ const std::unordered_map<std::string, SP<CBezierCurve>>& CAnimationManager::getA
|
||||||
CWeakPointer<CAnimationManager::SAnimationManagerSignals> CAnimationManager::getSignals() const {
|
CWeakPointer<CAnimationManager::SAnimationManagerSignals> CAnimationManager::getSignals() const {
|
||||||
return m_events;
|
return m_events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <hyprutils/animation/AnimationConfig.hpp>
|
||||||
|
#include <hyprutils/animation/AnimationManager.hpp>
|
||||||
|
#include <hyprutils/animation/AnimatedVariable.hpp>
|
||||||
|
#include <hyprutils/memory/WeakPtr.hpp>
|
||||||
|
#include <hyprutils/memory/UniquePtr.hpp>
|
||||||
|
|
||||||
|
#define SP CSharedPointer
|
||||||
|
#define WP CWeakPointer
|
||||||
|
#define UP CUniquePointer
|
||||||
|
|
||||||
|
using namespace Hyprutils::Animation;
|
||||||
|
using namespace Hyprutils::Math;
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
|
class EmtpyContext {};
|
||||||
|
|
||||||
|
template <typename VarType>
|
||||||
|
using CAnimatedVariable = CGenericAnimatedVariable<VarType, EmtpyContext>;
|
||||||
|
|
||||||
|
template <typename VarType>
|
||||||
|
using PANIMVAR = SP<CAnimatedVariable<VarType>>;
|
||||||
|
|
||||||
|
template <typename VarType>
|
||||||
|
using PANIMVARREF = WP<CAnimatedVariable<VarType>>;
|
||||||
|
|
||||||
|
enum eAVTypes {
|
||||||
|
INT = 1,
|
||||||
|
TEST,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SomeTestType {
|
||||||
|
bool done = false;
|
||||||
|
bool operator==(const SomeTestType& other) const {
|
||||||
|
return done == other.done;
|
||||||
|
}
|
||||||
|
SomeTestType& operator=(const SomeTestType& other) {
|
||||||
|
done = other.done;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CAnimationConfigTree animationTree;
|
||||||
|
|
||||||
|
class CMyAnimationManager : public CAnimationManager {
|
||||||
|
public:
|
||||||
|
void tick() {
|
||||||
|
for (size_t i = 0; i < m_vActiveAnimatedVariables.size(); i++) {
|
||||||
|
const auto PAV = m_vActiveAnimatedVariables[i].lock();
|
||||||
|
if (!PAV || !PAV->ok() || !PAV->isBeingAnimated())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto SPENT = PAV->getPercent();
|
||||||
|
const auto PBEZIER = getBezier(PAV->getBezierName());
|
||||||
|
|
||||||
|
if (SPENT >= 1.f || !PAV->enabled()) {
|
||||||
|
PAV->warp(true, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto POINTY = PBEZIER->getYForPoint(SPENT);
|
||||||
|
|
||||||
|
switch (PAV->m_Type) {
|
||||||
|
case eAVTypes::INT: {
|
||||||
|
auto avInt = dc<CAnimatedVariable<int>*>(PAV.get());
|
||||||
|
if (!avInt)
|
||||||
|
std::cout << "Dynamic cast upcast failed\n";
|
||||||
|
|
||||||
|
const auto DELTA = avInt->goal() - avInt->value();
|
||||||
|
avInt->value() = avInt->begun() + (DELTA * POINTY);
|
||||||
|
} break;
|
||||||
|
case eAVTypes::TEST: {
|
||||||
|
auto avCustom = dc<CAnimatedVariable<SomeTestType>*>(PAV.get());
|
||||||
|
if (!avCustom)
|
||||||
|
std::cout << "Dynamic cast upcast failed\n";
|
||||||
|
|
||||||
|
if (SPENT >= 1.f)
|
||||||
|
avCustom->value().done = true;
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
std::cout << "What are we even doing?\n";
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PAV->onUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
tickDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename VarType>
|
||||||
|
void createAnimation(const VarType& v, PANIMVAR<VarType>& av, const std::string& animationConfigName) {
|
||||||
|
constexpr const eAVTypes EAVTYPE = std::is_same_v<VarType, int> ? eAVTypes::INT : eAVTypes::TEST;
|
||||||
|
const auto PAV = makeShared<CGenericAnimatedVariable<VarType, EmtpyContext>>();
|
||||||
|
|
||||||
|
PAV->create(EAVTYPE, sc<CAnimationManager*>(this), PAV, v);
|
||||||
|
PAV->setConfig(animationTree.getConfig(animationConfigName));
|
||||||
|
av = std::move(PAV);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void scheduleTick() {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void onTicked() {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
UP<CMyAnimationManager> pAnimationManager;
|
||||||
|
|
||||||
|
class Subject {
|
||||||
|
public:
|
||||||
|
Subject(const int& a, const int& b) {
|
||||||
|
pAnimationManager->createAnimation(a, m_iA, "default");
|
||||||
|
pAnimationManager->createAnimation(b, m_iB, "internal");
|
||||||
|
pAnimationManager->createAnimation({}, m_iC, "default");
|
||||||
|
}
|
||||||
|
PANIMVAR<int> m_iA;
|
||||||
|
PANIMVAR<int> m_iB;
|
||||||
|
PANIMVAR<SomeTestType> m_iC;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int config() {
|
||||||
|
pAnimationManager = makeUnique<CMyAnimationManager>();
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
animationTree.createNode("global");
|
||||||
|
animationTree.createNode("internal");
|
||||||
|
|
||||||
|
animationTree.createNode("foo", "internal");
|
||||||
|
animationTree.createNode("default", "global");
|
||||||
|
animationTree.createNode("bar", "default");
|
||||||
|
|
||||||
|
/*
|
||||||
|
internal
|
||||||
|
↳ foo
|
||||||
|
global
|
||||||
|
↳ default
|
||||||
|
↳ bar
|
||||||
|
*/
|
||||||
|
|
||||||
|
auto barCfg = animationTree.getConfig("bar");
|
||||||
|
auto internalCfg = animationTree.getConfig("internal");
|
||||||
|
|
||||||
|
// internal is a root node and should point to itself
|
||||||
|
EXPECT_EQ(internalCfg->pParentAnimation.get(), internalCfg.get());
|
||||||
|
EXPECT_EQ(internalCfg->pValues.get(), internalCfg.get());
|
||||||
|
|
||||||
|
animationTree.setConfigForNode("global", 1, 4.0, "default", "asdf");
|
||||||
|
|
||||||
|
EXPECT_EQ(barCfg->internalEnabled, -1);
|
||||||
|
{
|
||||||
|
const auto PVALUES = barCfg->pValues.lock();
|
||||||
|
EXPECT_EQ(PVALUES->internalEnabled, 1);
|
||||||
|
EXPECT_EQ(PVALUES->internalBezier, "default");
|
||||||
|
EXPECT_EQ(PVALUES->internalStyle, "asdf");
|
||||||
|
EXPECT_EQ(PVALUES->internalSpeed, 4.0);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(barCfg->pParentAnimation.get(), animationTree.getConfig("default").get());
|
||||||
|
|
||||||
|
// Overwrite our own values
|
||||||
|
animationTree.setConfigForNode("bar", 1, 4.2, "test", "qwer");
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto PVALUES = barCfg->pValues.lock();
|
||||||
|
EXPECT_EQ(PVALUES->internalEnabled, 1);
|
||||||
|
EXPECT_EQ(PVALUES->internalBezier, "test");
|
||||||
|
EXPECT_EQ(PVALUES->internalStyle, "qwer");
|
||||||
|
EXPECT_EQ(PVALUES->internalSpeed, 4.2f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now overwrite the parent
|
||||||
|
animationTree.setConfigForNode("default", 0, 0.0, "zxcv", "foo");
|
||||||
|
|
||||||
|
{
|
||||||
|
// Expecting no change
|
||||||
|
const auto PVALUES = barCfg->pValues.lock();
|
||||||
|
EXPECT_EQ(PVALUES->internalEnabled, 1);
|
||||||
|
EXPECT_EQ(PVALUES->internalBezier, "test");
|
||||||
|
EXPECT_EQ(PVALUES->internalStyle, "qwer");
|
||||||
|
EXPECT_EQ(PVALUES->internalSpeed, 4.2f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Animation, animation) {
|
||||||
|
config();
|
||||||
|
|
||||||
|
animationTree.createNode("global");
|
||||||
|
animationTree.createNode("internal");
|
||||||
|
|
||||||
|
animationTree.createNode("default", "global");
|
||||||
|
animationTree.setConfigForNode("global", 1, 4.0, "default", "asdf");
|
||||||
|
|
||||||
|
Subject s(0, 0);
|
||||||
|
|
||||||
|
EXPECT_EQ(s.m_iA->value(), 0);
|
||||||
|
EXPECT_EQ(s.m_iB->value(), 0);
|
||||||
|
|
||||||
|
// Test destruction of a CAnimatedVariable
|
||||||
|
{
|
||||||
|
Subject s2(10, 10);
|
||||||
|
// Adds them to active
|
||||||
|
*s2.m_iA = 1;
|
||||||
|
*s2.m_iB = 2;
|
||||||
|
// We deliberately do not tick here, to make sure the destructor removes active animated variables
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(pAnimationManager->shouldTickForNext(), false);
|
||||||
|
EXPECT_EQ(s.m_iC->value().done, false);
|
||||||
|
|
||||||
|
*s.m_iA = 10;
|
||||||
|
*s.m_iB = 100;
|
||||||
|
*s.m_iC = SomeTestType(true);
|
||||||
|
|
||||||
|
EXPECT_EQ(s.m_iC->value().done, false);
|
||||||
|
|
||||||
|
while (pAnimationManager->shouldTickForNext()) {
|
||||||
|
pAnimationManager->tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(s.m_iA->value(), 10);
|
||||||
|
EXPECT_EQ(s.m_iB->value(), 100);
|
||||||
|
EXPECT_EQ(s.m_iC->value().done, true);
|
||||||
|
|
||||||
|
s.m_iA->setValue(0);
|
||||||
|
s.m_iB->setValue(0);
|
||||||
|
|
||||||
|
while (pAnimationManager->shouldTickForNext()) {
|
||||||
|
pAnimationManager->tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(s.m_iA->value(), 10);
|
||||||
|
EXPECT_EQ(s.m_iB->value(), 100);
|
||||||
|
|
||||||
|
// Test config stuff
|
||||||
|
EXPECT_EQ(s.m_iA->getBezierName(), "default");
|
||||||
|
EXPECT_EQ(s.m_iA->getStyle(), "asdf");
|
||||||
|
EXPECT_EQ(s.m_iA->enabled(), true);
|
||||||
|
|
||||||
|
animationTree.getConfig("global")->internalEnabled = 0;
|
||||||
|
|
||||||
|
EXPECT_EQ(s.m_iA->enabled(), false);
|
||||||
|
|
||||||
|
*s.m_iA = 50;
|
||||||
|
pAnimationManager->tick(); // Expecting a warp
|
||||||
|
EXPECT_EQ(s.m_iA->value(), 50);
|
||||||
|
|
||||||
|
// Test missing pValues
|
||||||
|
animationTree.getConfig("global")->internalEnabled = 0;
|
||||||
|
animationTree.getConfig("default")->pValues.reset();
|
||||||
|
|
||||||
|
EXPECT_EQ(s.m_iA->enabled(), false);
|
||||||
|
EXPECT_EQ(s.m_iA->getBezierName(), "default");
|
||||||
|
EXPECT_EQ(s.m_iA->getStyle(), "");
|
||||||
|
EXPECT_EQ(s.m_iA->getPercent(), 1.f);
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
animationTree.setConfigForNode("default", 1, 1, "default");
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test callbacks
|
||||||
|
//
|
||||||
|
int beginCallbackRan = 0;
|
||||||
|
int updateCallbackRan = 0;
|
||||||
|
int endCallbackRan = 0;
|
||||||
|
s.m_iA->setCallbackOnBegin([&beginCallbackRan](WP<CBaseAnimatedVariable> pav) { beginCallbackRan++; });
|
||||||
|
s.m_iA->setUpdateCallback([&updateCallbackRan](WP<CBaseAnimatedVariable> pav) { updateCallbackRan++; });
|
||||||
|
s.m_iA->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> pav) { endCallbackRan++; }, false);
|
||||||
|
|
||||||
|
s.m_iA->setValueAndWarp(42);
|
||||||
|
|
||||||
|
EXPECT_EQ(beginCallbackRan, 0);
|
||||||
|
EXPECT_EQ(updateCallbackRan, 1);
|
||||||
|
EXPECT_EQ(endCallbackRan, 2); // first called when setting the callback, then when warping.
|
||||||
|
|
||||||
|
*s.m_iA = 1337;
|
||||||
|
while (pAnimationManager->shouldTickForNext()) {
|
||||||
|
pAnimationManager->tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(beginCallbackRan, 1);
|
||||||
|
EXPECT_EQ(updateCallbackRan > 2, true);
|
||||||
|
EXPECT_EQ(endCallbackRan, 3);
|
||||||
|
|
||||||
|
std::vector<PANIMVAR<int>> vars;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
vars.resize(vars.size() + 1);
|
||||||
|
pAnimationManager->createAnimation(1, vars.back(), "default");
|
||||||
|
*vars.back() = 1337;
|
||||||
|
}
|
||||||
|
|
||||||
|
// test adding / removing vars during a tick
|
||||||
|
s.m_iA->resetAllCallbacks();
|
||||||
|
s.m_iA->setUpdateCallback([&vars](WP<CBaseAnimatedVariable> v) {
|
||||||
|
if (v.lock() != vars.back())
|
||||||
|
vars.back()->warp();
|
||||||
|
});
|
||||||
|
s.m_iA->setCallbackOnEnd([&s, &vars](auto) {
|
||||||
|
vars.resize(vars.size() + 1);
|
||||||
|
pAnimationManager->createAnimation(1, vars.back(), "default");
|
||||||
|
*vars.back() = 1337;
|
||||||
|
});
|
||||||
|
|
||||||
|
*s.m_iA = 1000000;
|
||||||
|
|
||||||
|
while (pAnimationManager->shouldTickForNext()) {
|
||||||
|
pAnimationManager->tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(s.m_iA->value(), 1000000);
|
||||||
|
// all vars should be set to 1337
|
||||||
|
EXPECT_EQ(std::find_if(vars.begin(), vars.end(), [](const auto& v) { return v->value() != 1337; }) == vars.end(), true);
|
||||||
|
|
||||||
|
// test one-time callbacks
|
||||||
|
s.m_iA->resetAllCallbacks();
|
||||||
|
s.m_iA->setCallbackOnEnd([&endCallbackRan](auto) { endCallbackRan++; }, true);
|
||||||
|
|
||||||
|
EXPECT_EQ(endCallbackRan, 4);
|
||||||
|
|
||||||
|
s.m_iA->setValueAndWarp(10);
|
||||||
|
|
||||||
|
EXPECT_EQ(endCallbackRan, 4);
|
||||||
|
EXPECT_EQ(s.m_iA->value(), 10);
|
||||||
|
|
||||||
|
// test warp
|
||||||
|
*s.m_iA = 3;
|
||||||
|
s.m_iA->setCallbackOnEnd([&endCallbackRan](auto) { endCallbackRan++; }, false);
|
||||||
|
|
||||||
|
s.m_iA->warp(false);
|
||||||
|
EXPECT_EQ(endCallbackRan, 4);
|
||||||
|
|
||||||
|
*s.m_iA = 4;
|
||||||
|
s.m_iA->warp(true);
|
||||||
|
EXPECT_EQ(endCallbackRan, 5);
|
||||||
|
|
||||||
|
// test getCurveValue
|
||||||
|
*s.m_iA = 0;
|
||||||
|
EXPECT_EQ(s.m_iA->getCurveValue(), 0.f);
|
||||||
|
s.m_iA->warp();
|
||||||
|
EXPECT_EQ(s.m_iA->getCurveValue(), 1.f);
|
||||||
|
EXPECT_EQ(endCallbackRan, 6);
|
||||||
|
|
||||||
|
// test end callback readding the var
|
||||||
|
*s.m_iA = 5;
|
||||||
|
s.m_iA->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> v) {
|
||||||
|
endCallbackRan++;
|
||||||
|
const auto PAV = dc<CAnimatedVariable<int>*>(v.lock().get());
|
||||||
|
|
||||||
|
*PAV = 10;
|
||||||
|
PAV->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> v) { endCallbackRan++; });
|
||||||
|
});
|
||||||
|
|
||||||
|
while (pAnimationManager->shouldTickForNext()) {
|
||||||
|
pAnimationManager->tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(endCallbackRan, 8);
|
||||||
|
EXPECT_EQ(s.m_iA->value(), 10);
|
||||||
|
|
||||||
|
// Test duplicate active anim vars are not allowed
|
||||||
|
{
|
||||||
|
EXPECT_EQ(pAnimationManager->m_vActiveAnimatedVariables.size(), 0);
|
||||||
|
PANIMVAR<int> a;
|
||||||
|
pAnimationManager->createAnimation(1, a, "default");
|
||||||
|
EXPECT_EQ(pAnimationManager->m_vActiveAnimatedVariables.size(), 0);
|
||||||
|
*a = 10;
|
||||||
|
EXPECT_EQ(pAnimationManager->m_vActiveAnimatedVariables.size(), 1);
|
||||||
|
*a = 20;
|
||||||
|
EXPECT_EQ(pAnimationManager->m_vActiveAnimatedVariables.size(), 1);
|
||||||
|
a->warp();
|
||||||
|
EXPECT_EQ(pAnimationManager->m_vActiveAnimatedVariables.size(), 0);
|
||||||
|
EXPECT_EQ(a->value(), 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test no crash when animation manager gets destroyed
|
||||||
|
{
|
||||||
|
PANIMVAR<int> a;
|
||||||
|
pAnimationManager->createAnimation(1, a, "default");
|
||||||
|
*a = 10;
|
||||||
|
pAnimationManager.reset();
|
||||||
|
EXPECT_EQ(a->isAnimationManagerDead(), true);
|
||||||
|
a->setValueAndWarp(11);
|
||||||
|
EXPECT_EQ(a->value(), 11);
|
||||||
|
*a = 12;
|
||||||
|
a->warp();
|
||||||
|
EXPECT_EQ(a->value(), 12);
|
||||||
|
*a = 13;
|
||||||
|
} // a gets destroyed
|
||||||
|
|
||||||
|
EXPECT_EQ(pAnimationManager.get(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -105,3 +105,84 @@ float CBezierCurve::getYForPoint(float const& x) const {
|
||||||
const std::vector<Hyprutils::Math::Vector2D>& CBezierCurve::getControlPoints() const {
|
const std::vector<Hyprutils::Math::Vector2D>& CBezierCurve::getControlPoints() const {
|
||||||
return m_vPoints;
|
return m_vPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using Hyprutils::Animation::CBezierCurve;
|
||||||
|
using Hyprutils::Math::Vector2D;
|
||||||
|
|
||||||
|
static void test_nonmonotonic4_clamps_out_of_range() {
|
||||||
|
// Non-monotonic curve in X
|
||||||
|
// This used to drive the step-halving search to OOB. It should now clamp
|
||||||
|
CBezierCurve curve;
|
||||||
|
std::array<Vector2D, 4> pts = {
|
||||||
|
Vector2D{0.5f, 1.0f}, // P0
|
||||||
|
Vector2D{1.0f, 1.0f}, // P1
|
||||||
|
Vector2D{0.0f, 0.0f}, // P2
|
||||||
|
Vector2D{0.5f, 0.0f} // P3
|
||||||
|
};
|
||||||
|
curve.setup4(pts);
|
||||||
|
|
||||||
|
// x > last baked x
|
||||||
|
EXPECT_EQ(std::isfinite(curve.getYForPoint(0.6f)), true);
|
||||||
|
// Far beyond range
|
||||||
|
EXPECT_EQ(std::isfinite(curve.getYForPoint(std::numeric_limits<float>::max())), true);
|
||||||
|
EXPECT_EQ(std::isfinite(curve.getYForPoint(-std::numeric_limits<float>::max())), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_adjacent_baked_x_equal() {
|
||||||
|
// Curve with flat tail (X=1, Y=1)
|
||||||
|
CBezierCurve curve;
|
||||||
|
std::array<Vector2D, 4> pts = {
|
||||||
|
Vector2D{0.0f, 0.0f}, // P0
|
||||||
|
Vector2D{0.2f, 0.2f}, // P1
|
||||||
|
Vector2D{1.0f, 1.0f}, // P2
|
||||||
|
Vector2D{1.0f, 1.0f} // P3
|
||||||
|
};
|
||||||
|
curve.setup4(pts);
|
||||||
|
|
||||||
|
// Exactly at last baked X
|
||||||
|
const float y_at_end = curve.getYForPoint(1.0f);
|
||||||
|
// Slightly beyond last baked X
|
||||||
|
const float y_past_end = curve.getYForPoint(1.0001f);
|
||||||
|
|
||||||
|
EXPECT_EQ(y_at_end, 1.0f);
|
||||||
|
EXPECT_EQ(y_past_end, y_at_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_all_baked_x_equal() {
|
||||||
|
// Extreme case: X is constant along the whole curve
|
||||||
|
CBezierCurve curve;
|
||||||
|
std::array<Vector2D, 4> pts = {
|
||||||
|
Vector2D{0.0f, 0.0f}, // P0
|
||||||
|
Vector2D{0.0f, 0.3f}, // P1
|
||||||
|
Vector2D{0.0f, 0.7f}, // P2
|
||||||
|
Vector2D{0.0f, 1.0f} // P3
|
||||||
|
};
|
||||||
|
curve.setup4(pts);
|
||||||
|
|
||||||
|
// Below any baked X
|
||||||
|
const float y_lo = curve.getYForPoint(-100.0f);
|
||||||
|
const float y_0 = curve.getYForPoint(0.0f);
|
||||||
|
// Above any baked X
|
||||||
|
const float y_hi = curve.getYForPoint(100.0f);
|
||||||
|
|
||||||
|
EXPECT_EQ(std::isfinite(y_lo), true);
|
||||||
|
EXPECT_EQ(std::isfinite(y_0), true);
|
||||||
|
EXPECT_EQ(std::isfinite(y_hi), true);
|
||||||
|
|
||||||
|
// For this curve Y should stay within [0,1]
|
||||||
|
EXPECT_EQ((y_lo >= 0.0f && y_lo <= 1.0f), true);
|
||||||
|
EXPECT_EQ((y_0 >= 0.0f && y_0 <= 1.0f), true);
|
||||||
|
EXPECT_EQ((y_hi >= 0.0f && y_hi <= 1.0f), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Animation, beziercurve) {
|
||||||
|
test_nonmonotonic4_clamps_out_of_range();
|
||||||
|
test_adjacent_baked_x_equal();
|
||||||
|
test_all_baked_x_equal();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -106,5 +106,69 @@ std::string CI18nEngine::localizeEntry(const std::string& locale, uint64_t key,
|
||||||
}
|
}
|
||||||
|
|
||||||
CI18nLocale CI18nEngine::getSystemLocale() {
|
CI18nLocale CI18nEngine::getSystemLocale() {
|
||||||
|
try {
|
||||||
return CI18nLocale(std::locale("").name());
|
return CI18nLocale(std::locale("").name());
|
||||||
|
} catch (...) { return CI18nLocale("en_US.UTF-8"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
enum eTxtKeys : uint64_t {
|
||||||
|
TXT_KEY_HELLO,
|
||||||
|
TXT_KEY_I_HAVE_APPLES,
|
||||||
|
TXT_KEY_FALLBACK,
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(I18n, Engine) {
|
||||||
|
CI18nEngine engine;
|
||||||
|
|
||||||
|
engine.setFallbackLocale("en_US");
|
||||||
|
|
||||||
|
engine.registerEntry("en_US", TXT_KEY_HELLO, "Hello World!");
|
||||||
|
engine.registerEntry("en_US", TXT_KEY_I_HAVE_APPLES, [](const translationVarMap& m) {
|
||||||
|
if (std::stoi(m.at("count")) == 1)
|
||||||
|
return "I have {count} apple.";
|
||||||
|
else
|
||||||
|
return "I have {count} apples.";
|
||||||
|
});
|
||||||
|
engine.registerEntry("en_US", TXT_KEY_FALLBACK, "Fallback string!");
|
||||||
|
|
||||||
|
engine.registerEntry("pl_PL", TXT_KEY_HELLO, "Witaj świecie!");
|
||||||
|
engine.registerEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, [](const translationVarMap& m) {
|
||||||
|
const auto COUNT = std::stoi(m.at("count"));
|
||||||
|
if (COUNT == 1)
|
||||||
|
return "Mam {count} jabłko.";
|
||||||
|
else if (COUNT < 5)
|
||||||
|
return "Mam {count} jabłka.";
|
||||||
|
else
|
||||||
|
return "Mam {count} jabłek.";
|
||||||
|
});
|
||||||
|
|
||||||
|
engine.registerEntry("es_XX", TXT_KEY_FALLBACK, "I don't speak spanish");
|
||||||
|
engine.registerEntry("es_ES", TXT_KEY_FALLBACK, "I don't speak spanish here either");
|
||||||
|
|
||||||
|
EXPECT_EQ(engine.localizeEntry("en_US", TXT_KEY_HELLO, {}), "Hello World!");
|
||||||
|
EXPECT_EQ(engine.localizeEntry("pl_PL", TXT_KEY_HELLO, {}), "Witaj świecie!");
|
||||||
|
EXPECT_EQ(engine.localizeEntry("de_DE", TXT_KEY_HELLO, {}), "Hello World!");
|
||||||
|
|
||||||
|
EXPECT_EQ(engine.localizeEntry("en_US", TXT_KEY_I_HAVE_APPLES, {{"count", "1"}}), "I have 1 apple.");
|
||||||
|
EXPECT_EQ(engine.localizeEntry("en_US", TXT_KEY_I_HAVE_APPLES, {{"count", "2"}}), "I have 2 apples.");
|
||||||
|
|
||||||
|
EXPECT_EQ(engine.localizeEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, {{"count", "1"}}), "Mam 1 jabłko.");
|
||||||
|
EXPECT_EQ(engine.localizeEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, {{"count", "2"}}), "Mam 2 jabłka.");
|
||||||
|
EXPECT_EQ(engine.localizeEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, {{"count", "5"}}), "Mam 5 jabłek.");
|
||||||
|
|
||||||
|
EXPECT_EQ(engine.localizeEntry("pl", TXT_KEY_I_HAVE_APPLES, {{"count", "5"}}), "Mam 5 jabłek.");
|
||||||
|
|
||||||
|
EXPECT_EQ(engine.localizeEntry("pl_XX", TXT_KEY_I_HAVE_APPLES, {{"count", "5"}}), "Mam 5 jabłek.");
|
||||||
|
EXPECT_EQ(engine.localizeEntry("en_XX", TXT_KEY_I_HAVE_APPLES, {{"count", "2"}}), "I have 2 apples.");
|
||||||
|
|
||||||
|
EXPECT_EQ(engine.localizeEntry("es_YY", TXT_KEY_FALLBACK, {}), "I don't speak spanish here either");
|
||||||
|
EXPECT_EQ(engine.localizeEntry("es_XX", TXT_KEY_FALLBACK, {}), "I don't speak spanish");
|
||||||
|
|
||||||
|
EXPECT_EQ(engine.localizeEntry("pl_PL", TXT_KEY_FALLBACK, {}), "Fallback string!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -42,3 +42,19 @@ std::string CI18nLocale::stem() {
|
||||||
std::string CI18nLocale::full() {
|
std::string CI18nLocale::full() {
|
||||||
return m_rawFullLocale;
|
return m_rawFullLocale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <hyprutils/i18n/I18nEngine.hpp>
|
||||||
|
|
||||||
|
TEST(I18n, Locale) {
|
||||||
|
CI18nEngine engine;
|
||||||
|
|
||||||
|
EXPECT_EQ(extractLocale("pl_PL.UTF-8"), "pl_PL");
|
||||||
|
EXPECT_EQ(extractLocale("POSIX"), "en_US");
|
||||||
|
EXPECT_EQ(extractLocale("*"), "en_US");
|
||||||
|
EXPECT_EQ(extractLocale("LC_CTYPE=pl_PL.UTF-8"), "pl_PL");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -235,3 +235,78 @@ Vector2D Hyprutils::Math::CBox::closestPoint(const Vector2D& vec) const {
|
||||||
SBoxExtents Hyprutils::Math::CBox::extentsFrom(const CBox& small) {
|
SBoxExtents Hyprutils::Math::CBox::extentsFrom(const CBox& small) {
|
||||||
return {.topLeft = {small.x - x, small.y - y}, .bottomRight = {w - small.w - (small.x - x), h - small.h - (small.y - y)}};
|
return {.topLeft = {small.x - x, small.y - y}, .bottomRight = {w - small.w - (small.x - x), h - small.h - (small.y - y)}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(Math, box) {
|
||||||
|
// Test default constructor and accessors
|
||||||
|
{
|
||||||
|
CBox box1;
|
||||||
|
EXPECT_EQ(box1.x, 0);
|
||||||
|
EXPECT_EQ(box1.y, 0);
|
||||||
|
EXPECT_EQ(box1.width, 0);
|
||||||
|
EXPECT_EQ(box1.height, 0);
|
||||||
|
|
||||||
|
// Test parameterized constructor and accessors
|
||||||
|
CBox box2(10, 20, 30, 40);
|
||||||
|
EXPECT_EQ(box2.x, 10);
|
||||||
|
EXPECT_EQ(box2.y, 20);
|
||||||
|
EXPECT_EQ(box2.width, 30);
|
||||||
|
EXPECT_EQ(box2.height, 40);
|
||||||
|
|
||||||
|
// Test setters and getters
|
||||||
|
box2.translate(Vector2D(5, -5));
|
||||||
|
EXPECT_EQ(box2.pos(), Vector2D(15, 15));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Test Scaling and Transformation
|
||||||
|
{
|
||||||
|
CBox box(10, 10, 20, 30);
|
||||||
|
|
||||||
|
// Test scaling
|
||||||
|
box.scale(2.0);
|
||||||
|
EXPECT_EQ(box.size(), Vector2D(40, 60));
|
||||||
|
EXPECT_EQ(box.pos(), Vector2D(20, 20));
|
||||||
|
|
||||||
|
// Test scaling from center
|
||||||
|
box.scaleFromCenter(0.5);
|
||||||
|
EXPECT_EQ(box.size(), Vector2D(20, 30));
|
||||||
|
EXPECT_EQ(box.pos(), Vector2D(30, 35));
|
||||||
|
|
||||||
|
// Test transformation
|
||||||
|
box.transform(HYPRUTILS_TRANSFORM_90, 100, 200);
|
||||||
|
EXPECT_EQ(box.pos(), Vector2D(135, 30));
|
||||||
|
EXPECT_EQ(box.size(), Vector2D(30, 20));
|
||||||
|
|
||||||
|
// Test Intersection and Extents
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CBox box1(0, 0, 100, 100);
|
||||||
|
CBox box2(50, 50, 100, 100);
|
||||||
|
|
||||||
|
CBox intersection = box1.intersection(box2);
|
||||||
|
EXPECT_EQ(intersection.pos(), Vector2D(50, 50));
|
||||||
|
EXPECT_EQ(intersection.size(), Vector2D(50, 50));
|
||||||
|
|
||||||
|
SBoxExtents extents = box1.extentsFrom(box2);
|
||||||
|
EXPECT_EQ(extents.topLeft, Vector2D(50, 50));
|
||||||
|
EXPECT_EQ(extents.bottomRight, Vector2D(-50, -50));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Boundary Conditions and Special Cases
|
||||||
|
{
|
||||||
|
CBox box(0, 0, 50, 50);
|
||||||
|
|
||||||
|
EXPECT_EQ(box.empty(), false);
|
||||||
|
|
||||||
|
EXPECT_EQ(box.containsPoint(Vector2D(25, 25)), true);
|
||||||
|
EXPECT_EQ(box.containsPoint(Vector2D(60, 60)), false);
|
||||||
|
EXPECT_EQ(box.overlaps(CBox(25, 25, 50, 50)), true);
|
||||||
|
EXPECT_EQ(box.inside(CBox(0, 0, 100, 100)), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -153,3 +153,26 @@ std::string Mat3x3::toString() const {
|
||||||
return std::format("[mat3x3: {}, {}, {}, {}, {}, {}, {}, {}, {}]", matrix.at(0), matrix.at(1), matrix.at(2), matrix.at(3), matrix.at(4), matrix.at(5), matrix.at(6),
|
return std::format("[mat3x3: {}, {}, {}, {}, {}, {}, {}, {}, {}]", matrix.at(0), matrix.at(1), matrix.at(2), matrix.at(3), matrix.at(4), matrix.at(5), matrix.at(6),
|
||||||
matrix.at(7), matrix.at(8));
|
matrix.at(7), matrix.at(8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(Math, mat3x3) {
|
||||||
|
Mat3x3 jeremy = Mat3x3::outputProjection({1920, 1080}, HYPRUTILS_TRANSFORM_FLIPPED_90);
|
||||||
|
Mat3x3 matrixBox = jeremy.projectBox(CBox{10, 10, 200, 200}, HYPRUTILS_TRANSFORM_NORMAL).translate({100, 100}).scale({1.25F, 1.5F}).transpose();
|
||||||
|
|
||||||
|
Mat3x3 expected = std::array<float, 9>{0, 0.46296296, 0, 0.3125, 0, 0, 19.84375, 36.055557, 1};
|
||||||
|
// we need to do this to avoid precision errors on 32-bit archs
|
||||||
|
EXPECT_EQ(std::abs(expected.getMatrix().at(0) - matrixBox.getMatrix().at(0)) < 0.1, true);
|
||||||
|
EXPECT_EQ(std::abs(expected.getMatrix().at(1) - matrixBox.getMatrix().at(1)) < 0.1, true);
|
||||||
|
EXPECT_EQ(std::abs(expected.getMatrix().at(2) - matrixBox.getMatrix().at(2)) < 0.1, true);
|
||||||
|
EXPECT_EQ(std::abs(expected.getMatrix().at(3) - matrixBox.getMatrix().at(3)) < 0.1, true);
|
||||||
|
EXPECT_EQ(std::abs(expected.getMatrix().at(4) - matrixBox.getMatrix().at(4)) < 0.1, true);
|
||||||
|
EXPECT_EQ(std::abs(expected.getMatrix().at(5) - matrixBox.getMatrix().at(5)) < 0.1, true);
|
||||||
|
EXPECT_EQ(std::abs(expected.getMatrix().at(6) - matrixBox.getMatrix().at(6)) < 0.1, true);
|
||||||
|
EXPECT_EQ(std::abs(expected.getMatrix().at(7) - matrixBox.getMatrix().at(7)) < 0.1, true);
|
||||||
|
EXPECT_EQ(std::abs(expected.getMatrix().at(8) - matrixBox.getMatrix().at(8)) < 0.1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -220,3 +220,22 @@ Vector2D Hyprutils::Math::CRegion::closestPoint(const Vector2D& vec) const {
|
||||||
|
|
||||||
return leader;
|
return leader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(Math, region) {
|
||||||
|
CRegion rg(CBox{{20, 20}, {40, 40}});
|
||||||
|
|
||||||
|
auto extents = rg.getExtents();
|
||||||
|
EXPECT_EQ(extents.pos(), Vector2D(20, 20));
|
||||||
|
EXPECT_EQ(extents.size(), Vector2D(40, 40));
|
||||||
|
|
||||||
|
rg.scale(2);
|
||||||
|
extents = rg.getExtents();
|
||||||
|
EXPECT_EQ(extents.pos(), Vector2D(40, 40));
|
||||||
|
EXPECT_EQ(extents.size(), Vector2D(80, 80));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -57,3 +57,23 @@ Vector2D Hyprutils::Math::Vector2D::transform(eTransform transform, const Vector
|
||||||
default: return *this;
|
default: return *this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(Math, vector2d) {
|
||||||
|
Vector2D original(30, 40);
|
||||||
|
Vector2D monitorSize(100, 200);
|
||||||
|
|
||||||
|
EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_NORMAL, monitorSize), Vector2D(30, 40));
|
||||||
|
EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_90, monitorSize), Vector2D(40, 200 - 30));
|
||||||
|
EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_180, monitorSize), Vector2D(100 - 30, 200 - 40));
|
||||||
|
EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_270, monitorSize), Vector2D(100 - 40, 30));
|
||||||
|
EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_FLIPPED, monitorSize), Vector2D(100 - 30, 40));
|
||||||
|
EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_FLIPPED_90, monitorSize), Vector2D(40, 30));
|
||||||
|
EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_FLIPPED_180, monitorSize), Vector2D(30, 200 - 40));
|
||||||
|
EXPECT_EQ(original.transform(HYPRUTILS_TRANSFORM_FLIPPED_270, monitorSize), Vector2D(100 - 40, 200 - 30));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
#include <hyprutils/memory/UniquePtr.hpp>
|
|
||||||
#include <hyprutils/memory/SharedPtr.hpp>
|
|
||||||
#include <hyprutils/memory/WeakPtr.hpp>
|
#include <hyprutils/memory/WeakPtr.hpp>
|
||||||
#include <hyprutils/memory/Atomic.hpp>
|
#include <hyprutils/memory/Atomic.hpp>
|
||||||
#include "shared.hpp"
|
#include <hyprutils/memory/Casts.hpp>
|
||||||
#include <chrono>
|
|
||||||
#include <print>
|
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace Hyprutils::Memory;
|
using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#define SP CSharedPointer
|
#define SP CSharedPointer
|
||||||
#define WP CWeakPointer
|
#define WP CWeakPointer
|
||||||
#define UP CUniquePointer
|
#define UP CUniquePointer
|
||||||
|
|
@ -19,9 +17,7 @@ using namespace Hyprutils::Memory;
|
||||||
#define NTHREADS 8
|
#define NTHREADS 8
|
||||||
#define ITERATIONS 10000
|
#define ITERATIONS 10000
|
||||||
|
|
||||||
static int testAtomicImpl() {
|
static void testAtomicImpl() {
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// Using makeShared here could lead to invalid refcounts.
|
// Using makeShared here could lead to invalid refcounts.
|
||||||
ASP<int> shared = makeAtomicShared<int>(0);
|
ASP<int> shared = makeAtomicShared<int>(0);
|
||||||
|
|
@ -45,7 +41,7 @@ static int testAtomicImpl() {
|
||||||
// Actual count is not incremented in a thread-safe manner here, so we can't check it.
|
// Actual count is not incremented in a thread-safe manner here, so we can't check it.
|
||||||
// We just want to check that the concurent refcounting doesn't cause any memory corruption.
|
// We just want to check that the concurent refcounting doesn't cause any memory corruption.
|
||||||
shared.reset();
|
shared.reset();
|
||||||
EXPECT(shared, false);
|
EXPECT_EQ(shared, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -71,15 +67,15 @@ static int testAtomicImpl() {
|
||||||
thread.join();
|
thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT(shared.strongRef(), 0);
|
EXPECT_EQ(shared.strongRef(), 0);
|
||||||
EXPECT(weak.valid(), false);
|
EXPECT_EQ(weak.valid(), false);
|
||||||
|
|
||||||
auto shared2 = weak.lock();
|
auto shared2 = weak.lock();
|
||||||
EXPECT(shared, false);
|
EXPECT_EQ(shared, false);
|
||||||
EXPECT(shared2.get(), nullptr);
|
EXPECT_EQ(shared2.get(), nullptr);
|
||||||
EXPECT(shared.strongRef(), 0);
|
EXPECT_EQ(shared.strongRef(), 0);
|
||||||
EXPECT(weak.valid(), false);
|
EXPECT_EQ(weak.valid(), false);
|
||||||
EXPECT(weak.expired(), true);
|
EXPECT_EQ(weak.expired(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // This tests recursive deletion. When foo will be deleted, bar will be deleted within the foo dtor.
|
{ // This tests recursive deletion. When foo will be deleted, bar will be deleted within the foo dtor.
|
||||||
|
|
@ -120,38 +116,34 @@ static int testAtomicImpl() {
|
||||||
genericNormal = derivedNormal;
|
genericNormal = derivedNormal;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT(!!genericAtomic, true);
|
EXPECT_EQ(!!genericAtomic, true);
|
||||||
EXPECT(!!genericNormal, true);
|
EXPECT_EQ(!!genericNormal, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv, char** envp) {
|
TEST(Memory, memory) {
|
||||||
SP<int> intPtr = makeShared<int>(10);
|
SP<int> intPtr = makeShared<int>(10);
|
||||||
SP<int> intPtr2 = makeShared<int>(-1337);
|
SP<int> intPtr2 = makeShared<int>(-1337);
|
||||||
UP<int> intUnique = makeUnique<int>(420);
|
UP<int> intUnique = makeUnique<int>(420);
|
||||||
|
|
||||||
int ret = 0;
|
EXPECT_EQ(*intPtr, 10);
|
||||||
|
EXPECT_EQ(intPtr.strongRef(), 1);
|
||||||
EXPECT(*intPtr, 10);
|
EXPECT_EQ(*intUnique, 420);
|
||||||
EXPECT(intPtr.strongRef(), 1);
|
|
||||||
EXPECT(*intUnique, 420);
|
|
||||||
|
|
||||||
WP<int> weak = intPtr;
|
WP<int> weak = intPtr;
|
||||||
WP<int> weakUnique = intUnique;
|
WP<int> weakUnique = intUnique;
|
||||||
|
|
||||||
EXPECT(*intPtr, 10);
|
EXPECT_EQ(*intPtr, 10);
|
||||||
EXPECT(intPtr.strongRef(), 1);
|
EXPECT_EQ(intPtr.strongRef(), 1);
|
||||||
EXPECT(*weak, 10);
|
EXPECT_EQ(*weak, 10);
|
||||||
EXPECT(weak.expired(), false);
|
EXPECT_EQ(weak.expired(), false);
|
||||||
EXPECT(!!weak.lock(), true);
|
EXPECT_EQ(!!weak.lock(), true);
|
||||||
EXPECT(*weakUnique, 420);
|
EXPECT_EQ(*weakUnique, 420);
|
||||||
EXPECT(weakUnique.expired(), false);
|
EXPECT_EQ(weakUnique.expired(), false);
|
||||||
EXPECT(intUnique.impl_->wref(), 1);
|
EXPECT_EQ(intUnique.impl_->wref(), 1);
|
||||||
|
|
||||||
SP<int> sharedFromUnique = weakUnique.lock();
|
SP<int> sharedFromUnique = weakUnique.lock();
|
||||||
EXPECT(sharedFromUnique, nullptr);
|
EXPECT_EQ(sharedFromUnique, nullptr);
|
||||||
|
|
||||||
std::vector<SP<int>> sps;
|
std::vector<SP<int>> sps;
|
||||||
sps.push_back(intPtr);
|
sps.push_back(intPtr);
|
||||||
|
|
@ -163,24 +155,25 @@ int main(int argc, char** argv, char** envp) {
|
||||||
intPtr.reset();
|
intPtr.reset();
|
||||||
intUnique.reset();
|
intUnique.reset();
|
||||||
|
|
||||||
EXPECT(weak.impl_->ref(), 0);
|
EXPECT_EQ(weak.impl_->ref(), 0);
|
||||||
EXPECT(weakUnique.impl_->ref(), 0);
|
EXPECT_EQ(weakUnique.impl_->ref(), 0);
|
||||||
EXPECT(weakUnique.impl_->wref(), 1);
|
EXPECT_EQ(weakUnique.impl_->wref(), 1);
|
||||||
EXPECT(intPtr2.strongRef(), 3);
|
EXPECT_EQ(intPtr2.strongRef(), 3);
|
||||||
|
|
||||||
EXPECT(weak.expired(), true);
|
EXPECT_EQ(weak.expired(), true);
|
||||||
EXPECT(weakUnique.expired(), true);
|
EXPECT_EQ(weakUnique.expired(), true);
|
||||||
|
|
||||||
auto intPtr2AsUint = reinterpretPointerCast<unsigned int>(intPtr2);
|
auto intPtr2AsUint = reinterpretPointerCast<unsigned int>(intPtr2);
|
||||||
EXPECT(intPtr2.strongRef(), 4);
|
EXPECT_EQ(intPtr2.strongRef(), 4);
|
||||||
EXPECT(intPtr2AsUint.strongRef(), 4);
|
EXPECT_EQ(intPtr2AsUint.strongRef(), 4);
|
||||||
|
|
||||||
EXPECT(*intPtr2AsUint > 0, true);
|
EXPECT_EQ(*intPtr2AsUint > 0, true);
|
||||||
EXPECT(*intPtr2AsUint, (unsigned int)(int)-1337);
|
EXPECT_EQ(*intPtr2AsUint, (unsigned int)(int)-1337);
|
||||||
*intPtr2AsUint = 10;
|
*intPtr2AsUint = 10;
|
||||||
EXPECT(*intPtr2AsUint, 10);
|
EXPECT_EQ(*intPtr2AsUint, 10);
|
||||||
EXPECT(*intPtr2, 10);
|
EXPECT_EQ(*intPtr2, 10);
|
||||||
|
|
||||||
EXPECT(testAtomicImpl(), 0);
|
testAtomicImpl();
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -84,3 +84,49 @@ bool CFileDescriptor::isReadable(int fd) {
|
||||||
|
|
||||||
return poll(&pfd, 1, 0) > 0 && (pfd.revents & POLLIN);
|
return poll(&pfd, 1, 0) > 0 && (pfd.revents & POLLIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
TEST(OS, fd) {
|
||||||
|
std::string name = "/test_filedescriptors";
|
||||||
|
CFileDescriptor fd(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
|
||||||
|
|
||||||
|
EXPECT_EQ(fd.isValid(), true);
|
||||||
|
EXPECT_EQ(fd.isReadable(), true);
|
||||||
|
|
||||||
|
int flags = fd.getFlags();
|
||||||
|
EXPECT_EQ(fd.getFlags(), FD_CLOEXEC);
|
||||||
|
flags &= ~FD_CLOEXEC;
|
||||||
|
fd.setFlags(flags);
|
||||||
|
EXPECT_EQ(fd.getFlags(), !FD_CLOEXEC);
|
||||||
|
|
||||||
|
CFileDescriptor fd2 = fd.duplicate();
|
||||||
|
EXPECT_EQ(fd.isValid(), true);
|
||||||
|
EXPECT_EQ(fd.isReadable(), true);
|
||||||
|
EXPECT_EQ(fd2.isValid(), true);
|
||||||
|
EXPECT_EQ(fd2.isReadable(), true);
|
||||||
|
|
||||||
|
CFileDescriptor fd3(fd2.take());
|
||||||
|
EXPECT_EQ(fd.isValid(), true);
|
||||||
|
EXPECT_EQ(fd.isReadable(), true);
|
||||||
|
EXPECT_EQ(fd2.isValid(), false);
|
||||||
|
EXPECT_EQ(fd2.isReadable(), false);
|
||||||
|
|
||||||
|
// .duplicate default flags is FD_CLOEXEC
|
||||||
|
EXPECT_EQ(fd3.getFlags(), FD_CLOEXEC);
|
||||||
|
|
||||||
|
fd.reset();
|
||||||
|
fd2.reset();
|
||||||
|
fd3.reset();
|
||||||
|
|
||||||
|
EXPECT_EQ(fd.isReadable(), false);
|
||||||
|
EXPECT_EQ(fd2.isReadable(), false);
|
||||||
|
EXPECT_EQ(fd3.isReadable(), false);
|
||||||
|
|
||||||
|
shm_unlink(name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -283,3 +283,32 @@ void Hyprutils::OS::CProcess::setStdoutFD(int fd) {
|
||||||
void Hyprutils::OS::CProcess::setStderrFD(int fd) {
|
void Hyprutils::OS::CProcess::setStderrFD(int fd) {
|
||||||
m_impl->stderrFD = fd;
|
m_impl->stderrFD = fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(OS, process) {
|
||||||
|
CProcess process("sh", {"-c", "echo \"Hello $WORLD!\""});
|
||||||
|
process.addEnv("WORLD", "World");
|
||||||
|
|
||||||
|
EXPECT_EQ(process.runAsync(), true);
|
||||||
|
EXPECT_EQ(process.runSync(), true);
|
||||||
|
|
||||||
|
EXPECT_EQ(process.stdOut(), std::string{"Hello World!\n"});
|
||||||
|
EXPECT_EQ(process.stdErr(), std::string{""});
|
||||||
|
EXPECT_EQ(process.exitCode(), 0);
|
||||||
|
|
||||||
|
CProcess process2("sh", {"-c", "while true; do sleep 1; done;"});
|
||||||
|
|
||||||
|
EXPECT_EQ(process2.runAsync(), true);
|
||||||
|
EXPECT_EQ(getpgid(process2.pid()) >= 0, true);
|
||||||
|
|
||||||
|
kill(process2.pid(), SIGKILL);
|
||||||
|
|
||||||
|
CProcess process3("sh", {"-c", "cat /geryueruggbuergheruger/reugiheruygyuerghuryeghyer/eruihgyuerguyerghyuerghuyergerguyer/NON_EXISTENT"});
|
||||||
|
EXPECT_EQ(process3.runSync(), true);
|
||||||
|
EXPECT_EQ(process3.exitCode(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -58,3 +58,376 @@ void Hyprutils::Signal::CSignalBase::registerStaticListenerInternal(std::functio
|
||||||
void Hyprutils::Signal::CSignal::emit(std::any data) {
|
void Hyprutils::Signal::CSignal::emit(std::any data) {
|
||||||
CSignalT::emit(data);
|
CSignalT::emit(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <any>
|
||||||
|
#include <hyprutils/signal/Signal.hpp>
|
||||||
|
#include <hyprutils/signal/Listener.hpp>
|
||||||
|
#include <hyprutils/memory/WeakPtr.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace Hyprutils::Signal;
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
//
|
||||||
|
|
||||||
|
static void legacy() {
|
||||||
|
CSignal signal;
|
||||||
|
int data = 0;
|
||||||
|
auto listener = signal.registerListener([&]([[maybe_unused]] std::any d) { data = 1; });
|
||||||
|
|
||||||
|
signal.emit();
|
||||||
|
|
||||||
|
EXPECT_EQ(data, 1);
|
||||||
|
|
||||||
|
data = 0;
|
||||||
|
|
||||||
|
listener.reset();
|
||||||
|
|
||||||
|
signal.emit();
|
||||||
|
|
||||||
|
EXPECT_EQ(data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void legacyListenerEmit() {
|
||||||
|
int data = 0;
|
||||||
|
CSignal signal;
|
||||||
|
auto listener = signal.registerListener([&](std::any d) { data = std::any_cast<int>(d); });
|
||||||
|
|
||||||
|
listener->emit(1); // not a typo
|
||||||
|
EXPECT_EQ(data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void legacyListeners() {
|
||||||
|
int data = 0;
|
||||||
|
|
||||||
|
CSignalT<> signal0;
|
||||||
|
CSignalT<int> signal1;
|
||||||
|
|
||||||
|
auto listener0 = signal0.registerListener([&](std::any d) { data += 1; });
|
||||||
|
auto listener1 = signal1.registerListener([&](std::any d) { data += std::any_cast<int>(d); });
|
||||||
|
|
||||||
|
signal0.registerStaticListener([&](void* o, std::any d) { data += 10; }, nullptr);
|
||||||
|
signal1.registerStaticListener([&](void* o, std::any d) { data += std::any_cast<int>(d) * 10; }, nullptr);
|
||||||
|
|
||||||
|
signal0.emit();
|
||||||
|
signal1.emit(2);
|
||||||
|
|
||||||
|
EXPECT_EQ(data, 33);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
//
|
||||||
|
|
||||||
|
static void empty() {
|
||||||
|
int data = 0;
|
||||||
|
|
||||||
|
CSignalT<> signal;
|
||||||
|
auto listener = signal.listen([&] { data = 1; });
|
||||||
|
|
||||||
|
signal.emit();
|
||||||
|
EXPECT_EQ(data, 1);
|
||||||
|
|
||||||
|
data = 0;
|
||||||
|
listener.reset();
|
||||||
|
signal.emit();
|
||||||
|
EXPECT_EQ(data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void typed() {
|
||||||
|
int data = 0;
|
||||||
|
|
||||||
|
CSignalT<int> signal;
|
||||||
|
auto listener = signal.listen([&](int newData) { data = newData; });
|
||||||
|
|
||||||
|
signal.emit(1);
|
||||||
|
EXPECT_EQ(data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ignoreParams() {
|
||||||
|
int data = 0;
|
||||||
|
|
||||||
|
CSignalT<int> signal;
|
||||||
|
auto listener = signal.listen([&] { data += 1; });
|
||||||
|
|
||||||
|
signal.listenStatic([&] { data += 1; });
|
||||||
|
|
||||||
|
signal.emit(2);
|
||||||
|
EXPECT_EQ(data, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void typedMany() {
|
||||||
|
int data1 = 0;
|
||||||
|
int data2 = 0;
|
||||||
|
int data3 = 0;
|
||||||
|
|
||||||
|
CSignalT<int, int, int> signal;
|
||||||
|
auto listener = signal.listen([&](int d1, int d2, int d3) {
|
||||||
|
data1 = d1;
|
||||||
|
data2 = d2;
|
||||||
|
data3 = d3;
|
||||||
|
});
|
||||||
|
|
||||||
|
signal.emit(1, 2, 3);
|
||||||
|
EXPECT_EQ(data1, 1);
|
||||||
|
EXPECT_EQ(data2, 2);
|
||||||
|
EXPECT_EQ(data3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ref() {
|
||||||
|
int count = 0;
|
||||||
|
int data = 0;
|
||||||
|
|
||||||
|
CSignalT<int&> signal;
|
||||||
|
auto l1 = signal.listen([&](int& v) { v += 1; });
|
||||||
|
auto l2 = signal.listen([&](int v) { count += v; });
|
||||||
|
signal.emit(data);
|
||||||
|
|
||||||
|
CSignalT<const int&> constSignal;
|
||||||
|
auto l3 = constSignal.listen([&](const int& v) { count += v; });
|
||||||
|
auto l4 = constSignal.listen([&](int v) { count += v; });
|
||||||
|
constSignal.emit(data);
|
||||||
|
|
||||||
|
EXPECT_EQ(data, 1);
|
||||||
|
EXPECT_EQ(count, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void refMany() {
|
||||||
|
int count = 0;
|
||||||
|
int data1 = 0;
|
||||||
|
int data2 = 10;
|
||||||
|
|
||||||
|
CSignalT<int&, const int&> signal;
|
||||||
|
auto l1 = signal.listen([&](int& v, const int&) { v += 1; });
|
||||||
|
auto l2 = signal.listen([&](int v1, int v2) { count += v1 + v2; });
|
||||||
|
|
||||||
|
signal.emit(data1, data2);
|
||||||
|
EXPECT_EQ(data1, 1);
|
||||||
|
EXPECT_EQ(count, 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void autoRefTypes() {
|
||||||
|
class CCopyCounter {
|
||||||
|
public:
|
||||||
|
CCopyCounter(int& createCount, int& destroyCount) : createCount(createCount), destroyCount(destroyCount) {
|
||||||
|
createCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CCopyCounter(CCopyCounter&& other) noexcept : CCopyCounter(other.createCount, other.destroyCount) {}
|
||||||
|
CCopyCounter(const CCopyCounter& other) noexcept : CCopyCounter(other.createCount, other.destroyCount) {}
|
||||||
|
|
||||||
|
~CCopyCounter() {
|
||||||
|
destroyCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int& createCount;
|
||||||
|
int& destroyCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto createCount = 0;
|
||||||
|
auto destroyCount = 0;
|
||||||
|
|
||||||
|
CSignalT<CCopyCounter> signal;
|
||||||
|
auto listener = signal.listen([](const CCopyCounter& counter) {});
|
||||||
|
|
||||||
|
signal.emit(CCopyCounter(createCount, destroyCount));
|
||||||
|
EXPECT_EQ(createCount, 1);
|
||||||
|
EXPECT_EQ(destroyCount, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void forward() {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
CSignalT<int> sig;
|
||||||
|
CSignalT<int> connected1;
|
||||||
|
CSignalT<> connected2;
|
||||||
|
|
||||||
|
auto conn1 = sig.forward(connected1);
|
||||||
|
auto conn2 = sig.forward(connected2);
|
||||||
|
|
||||||
|
auto listener1 = connected1.listen([&](int v) { count += v; });
|
||||||
|
auto listener2 = connected2.listen([&] { count += 1; });
|
||||||
|
|
||||||
|
sig.emit(2);
|
||||||
|
|
||||||
|
EXPECT_EQ(count, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void listenerAdded() {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
CSignalT<> signal;
|
||||||
|
CHyprSignalListener secondListener;
|
||||||
|
|
||||||
|
auto listener = signal.listen([&] {
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
if (!secondListener)
|
||||||
|
secondListener = signal.listen([&] { count += 1; });
|
||||||
|
});
|
||||||
|
|
||||||
|
signal.emit();
|
||||||
|
EXPECT_EQ(count, 1); // second should NOT be invoked as it was registed during emit
|
||||||
|
|
||||||
|
signal.emit();
|
||||||
|
EXPECT_EQ(count, 3); // second should be invoked
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lastListenerSwapped() {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
CSignalT<> signal;
|
||||||
|
CHyprSignalListener removedListener;
|
||||||
|
CHyprSignalListener addedListener;
|
||||||
|
|
||||||
|
auto firstListener = signal.listen([&] {
|
||||||
|
removedListener.reset(); // dropped and should NOT be invoked
|
||||||
|
|
||||||
|
if (!addedListener)
|
||||||
|
addedListener = signal.listen([&] { count += 2; });
|
||||||
|
});
|
||||||
|
|
||||||
|
removedListener = signal.listen([&] { count += 1; });
|
||||||
|
|
||||||
|
signal.emit();
|
||||||
|
EXPECT_EQ(count, 0); // neither the removed nor added listeners should fire
|
||||||
|
|
||||||
|
signal.emit();
|
||||||
|
EXPECT_EQ(count, 2); // only the new listener should fire
|
||||||
|
}
|
||||||
|
|
||||||
|
static void signalDestroyed() {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
auto signal = std::make_unique<CSignalT<>>();
|
||||||
|
|
||||||
|
// This ensures a destructor of a listener called before signal reset is safe.
|
||||||
|
auto preListener = signal->listen([&] { count += 1; });
|
||||||
|
|
||||||
|
auto listener = signal->listen([&] { signal.reset(); });
|
||||||
|
|
||||||
|
// This ensures a destructor of a listener called after signal reset is safe
|
||||||
|
// and gets called.
|
||||||
|
auto postListener = signal->listen([&] { count += 1; });
|
||||||
|
|
||||||
|
signal->emit();
|
||||||
|
EXPECT_EQ(count, 2); // all listeners should fire regardless of signal deletion
|
||||||
|
}
|
||||||
|
|
||||||
|
// purely an asan test
|
||||||
|
static void signalDestroyedBeforeListener() {
|
||||||
|
CHyprSignalListener listener1;
|
||||||
|
CHyprSignalListener listener2;
|
||||||
|
|
||||||
|
CSignalT<> signal;
|
||||||
|
|
||||||
|
listener1 = signal.listen([] {});
|
||||||
|
listener2 = signal.listen([] {});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void signalDestroyedWithAddedListener() {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
auto signal = std::make_unique<CSignalT<>>();
|
||||||
|
CHyprSignalListener shouldNotRun;
|
||||||
|
|
||||||
|
auto listener = signal->listen([&] {
|
||||||
|
shouldNotRun = signal->listen([&] { count += 2; });
|
||||||
|
signal.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
signal->emit();
|
||||||
|
EXPECT_EQ(count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void signalDestroyedWithRemovedAndAddedListener() {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
auto signal = std::make_unique<CSignalT<>>();
|
||||||
|
CHyprSignalListener removed;
|
||||||
|
CHyprSignalListener shouldNotRun;
|
||||||
|
|
||||||
|
auto listener = signal->listen([&] {
|
||||||
|
removed.reset();
|
||||||
|
shouldNotRun = signal->listen([&] { count += 2; });
|
||||||
|
signal.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
removed = signal->listen([&] { count += 1; });
|
||||||
|
|
||||||
|
signal->emit();
|
||||||
|
EXPECT_EQ(count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void staticListener() {
|
||||||
|
int data = 0;
|
||||||
|
|
||||||
|
CSignalT<int> signal;
|
||||||
|
signal.listenStatic([&](int newData) { data = newData; });
|
||||||
|
|
||||||
|
signal.emit(1);
|
||||||
|
EXPECT_EQ(data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void staticListenerDestroy() {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
auto signal = makeShared<CSignalT<>>();
|
||||||
|
signal->listenStatic([&] { count += 1; });
|
||||||
|
|
||||||
|
signal->listenStatic([&] {
|
||||||
|
// should not fire but SHOULD be freed
|
||||||
|
signal->listenStatic([&] { count += 3; });
|
||||||
|
|
||||||
|
signal.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
signal->listenStatic([&] { count += 1; });
|
||||||
|
|
||||||
|
signal->emit();
|
||||||
|
EXPECT_EQ(count, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// purely an asan test
|
||||||
|
static void listenerDestroysSelf() {
|
||||||
|
CSignalT<> signal;
|
||||||
|
|
||||||
|
CHyprSignalListener listener;
|
||||||
|
listener = signal.listen([&] { listener.reset(); });
|
||||||
|
|
||||||
|
// the static signal case is taken care of above
|
||||||
|
|
||||||
|
signal.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Signal, signal) {
|
||||||
|
legacy();
|
||||||
|
legacyListenerEmit();
|
||||||
|
legacyListeners();
|
||||||
|
empty();
|
||||||
|
typed();
|
||||||
|
ignoreParams();
|
||||||
|
typedMany();
|
||||||
|
ref();
|
||||||
|
refMany();
|
||||||
|
autoRefTypes();
|
||||||
|
forward();
|
||||||
|
listenerAdded();
|
||||||
|
lastListenerSwapped();
|
||||||
|
signalDestroyed();
|
||||||
|
signalDestroyedBeforeListener();
|
||||||
|
signalDestroyedWithAddedListener();
|
||||||
|
signalDestroyedWithRemovedAndAddedListener();
|
||||||
|
staticListener();
|
||||||
|
staticListenerDestroy();
|
||||||
|
signalDestroyed();
|
||||||
|
listenerDestroysSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,19 @@ std::string CConstVarList::join(const std::string& joiner, size_t from, size_t t
|
||||||
|
|
||||||
return rolling;
|
return rolling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(String, constvarlist) {
|
||||||
|
CConstVarList listConst("hello world!", 0, 's', true);
|
||||||
|
EXPECT_EQ(listConst[0], "hello");
|
||||||
|
EXPECT_EQ(listConst[1], "world!");
|
||||||
|
|
||||||
|
CConstVarList listConst2("0 set", 2, ' ');
|
||||||
|
EXPECT_EQ(listConst2[0], "0");
|
||||||
|
EXPECT_EQ(listConst2[1], "set");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -88,3 +88,38 @@ void Hyprutils::String::replaceInString(std::string& string, const std::string&
|
||||||
pos += to.length();
|
pos += to.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(String, string) {
|
||||||
|
EXPECT_EQ(trim(" a "), "a");
|
||||||
|
EXPECT_EQ(trim(" a a "), "a a");
|
||||||
|
EXPECT_EQ(trim("a"), "a");
|
||||||
|
EXPECT_EQ(trim(" "), "");
|
||||||
|
|
||||||
|
EXPECT_EQ(isNumber("99214123434"), true);
|
||||||
|
EXPECT_EQ(isNumber("-35252345234"), true);
|
||||||
|
EXPECT_EQ(isNumber("---3423--432"), false);
|
||||||
|
EXPECT_EQ(isNumber("s---3423--432"), false);
|
||||||
|
EXPECT_EQ(isNumber("---3423--432s"), false);
|
||||||
|
EXPECT_EQ(isNumber("1s"), false);
|
||||||
|
EXPECT_EQ(isNumber(""), false);
|
||||||
|
EXPECT_EQ(isNumber("-"), false);
|
||||||
|
EXPECT_EQ(isNumber("--0"), false);
|
||||||
|
EXPECT_EQ(isNumber("abc"), false);
|
||||||
|
EXPECT_EQ(isNumber("0.0", true), true);
|
||||||
|
EXPECT_EQ(isNumber("0.2", true), true);
|
||||||
|
EXPECT_EQ(isNumber("0.", true), false);
|
||||||
|
EXPECT_EQ(isNumber(".0", true), false);
|
||||||
|
EXPECT_EQ(isNumber("", true), false);
|
||||||
|
EXPECT_EQ(isNumber("vvss", true), false);
|
||||||
|
EXPECT_EQ(isNumber("0.9999s", true), false);
|
||||||
|
EXPECT_EQ(isNumber("s0.9999", true), false);
|
||||||
|
EXPECT_EQ(isNumber("-1.0", true), true);
|
||||||
|
EXPECT_EQ(isNumber("-1..0", true), false);
|
||||||
|
EXPECT_EQ(isNumber("-10.0000000001", true), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -36,3 +36,15 @@ std::string Hyprutils::String::CVarList::join(const std::string& joiner, size_t
|
||||||
|
|
||||||
return rolling;
|
return rolling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(String, varlist) {
|
||||||
|
CVarList list("hello world!", 0, 's', true);
|
||||||
|
EXPECT_EQ(list[0], "hello");
|
||||||
|
EXPECT_EQ(list[1], "world!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
using namespace Hyprutils::String;
|
using namespace Hyprutils::String;
|
||||||
|
|
||||||
CVarList2::CVarList2(std::string&& in, const size_t lastArgNo, const char delim, const bool removeEmpty, const bool allowEscape) : m_inString(std::move(in)) {
|
CVarList2::CVarList2(std::string&& in, const size_t lastArgNo, const char delim, const bool removeEmpty, const bool allowEscape) : m_inString(std::move(in)) {
|
||||||
if (!removeEmpty && m_inString.empty())
|
if (m_inString.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto isDelimiter = [&delim](const char& c) { return delim == 's' ? std::isspace(c) : delim == c; };
|
auto isDelimiter = [&delim](const char& c) { return delim == 's' ? std::isspace(c) : delim == c; };
|
||||||
|
|
@ -21,8 +21,7 @@ CVarList2::CVarList2(std::string&& in, const size_t lastArgNo, const char delim,
|
||||||
|
|
||||||
if (allowEscape) {
|
if (allowEscape) {
|
||||||
// we allow escape, so this might be escaped. Check first
|
// we allow escape, so this might be escaped. Check first
|
||||||
if (i - argBegin == 0)
|
if (i - argBegin != 0) {
|
||||||
continue; // can't be
|
|
||||||
const char& previousC = m_inString[i - 1];
|
const char& previousC = m_inString[i - 1];
|
||||||
if (i - argBegin == 1) {
|
if (i - argBegin == 1) {
|
||||||
if (previousC == '\\') {
|
if (previousC == '\\') {
|
||||||
|
|
@ -44,6 +43,8 @@ CVarList2::CVarList2(std::string&& in, const size_t lastArgNo, const char delim,
|
||||||
|
|
||||||
// fall to breaking, not escaped, but mark the \\ to be popped
|
// fall to breaking, not escaped, but mark the \\ to be popped
|
||||||
}
|
}
|
||||||
|
// fall to breaking, couldn't be escaped
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// here we found a delimiter and need to break up the string (not escaped)
|
// here we found a delimiter and need to break up the string (not escaped)
|
||||||
|
|
@ -91,12 +92,12 @@ CVarList2::CVarList2(std::string&& in, const size_t lastArgNo, const char delim,
|
||||||
|
|
||||||
std::string CVarList2::join(const std::string& joiner, size_t from, size_t to) const {
|
std::string CVarList2::join(const std::string& joiner, size_t from, size_t to) const {
|
||||||
if (to == 0 || to <= from)
|
if (to == 0 || to <= from)
|
||||||
return "";
|
to = m_args.size();
|
||||||
|
|
||||||
std::string roll;
|
std::string roll;
|
||||||
for (size_t i = from; i < to; ++i) {
|
for (size_t i = from; i < to && i < m_args.size(); ++i) {
|
||||||
roll += m_args[i];
|
roll += m_args[i];
|
||||||
if (i + 1 < to)
|
if (i + 1 < to && i + 1 < m_args.size())
|
||||||
roll += joiner;
|
roll += joiner;
|
||||||
}
|
}
|
||||||
return roll;
|
return roll;
|
||||||
|
|
@ -110,3 +111,71 @@ void CVarList2::append(std::string&& arg) {
|
||||||
bool CVarList2::contains(const std::string& el) {
|
bool CVarList2::contains(const std::string& el) {
|
||||||
return std::ranges::any_of(m_args, [&el](const auto& e) { return e == el; });
|
return std::ranges::any_of(m_args, [&el](const auto& e) { return e == el; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HU_UNIT_TESTS
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(String, varlist2) {
|
||||||
|
CVarList2 varList2("0 set", 2, ' ');
|
||||||
|
EXPECT_EQ(varList2[0], "0");
|
||||||
|
EXPECT_EQ(varList2[1], "set");
|
||||||
|
|
||||||
|
varList2.append("Hello");
|
||||||
|
|
||||||
|
EXPECT_EQ(varList2[1], "set");
|
||||||
|
EXPECT_EQ(varList2[2], "Hello");
|
||||||
|
EXPECT_EQ(varList2[3], "");
|
||||||
|
EXPECT_EQ(varList2.contains("set"), true);
|
||||||
|
EXPECT_EQ(varList2.contains("sett"), false);
|
||||||
|
EXPECT_EQ(varList2.contains(""), false);
|
||||||
|
EXPECT_EQ(varList2.size(), 3);
|
||||||
|
|
||||||
|
CVarList2 varList2B("hello, world\\, ok?", 0, ',', true, true);
|
||||||
|
EXPECT_EQ(varList2B[0], "hello");
|
||||||
|
EXPECT_EQ(varList2B[1], "world, ok?");
|
||||||
|
|
||||||
|
CVarList2 varList2C("hello, , ok?", 0, ',', true, true);
|
||||||
|
EXPECT_EQ(varList2C[0], "hello");
|
||||||
|
EXPECT_EQ(varList2C[1], "ok?");
|
||||||
|
|
||||||
|
CVarList2 varList2D("\\\\, , ok?", 0, ',', true, true);
|
||||||
|
EXPECT_EQ(varList2D[0], "\\");
|
||||||
|
EXPECT_EQ(varList2D[1], "ok?");
|
||||||
|
|
||||||
|
CVarList2 varList2E("\\, , ok?", 0, ',', true, true);
|
||||||
|
EXPECT_EQ(varList2E[0], ",");
|
||||||
|
EXPECT_EQ(varList2E[1], "ok?");
|
||||||
|
|
||||||
|
CVarList2 varList2F("Hello, world\\\\, ok?", 0, ',', true, true);
|
||||||
|
EXPECT_EQ(varList2F[0], "Hello");
|
||||||
|
EXPECT_EQ(varList2F[1], "world\\");
|
||||||
|
EXPECT_EQ(varList2F[2], "ok?");
|
||||||
|
|
||||||
|
CVarList2 varList2G("Hello,\\, ok?", 0, ',', true, true);
|
||||||
|
EXPECT_EQ(varList2G[0], "Hello");
|
||||||
|
EXPECT_EQ(varList2G[1], ", ok?");
|
||||||
|
|
||||||
|
CVarList2 varList2H("Hello,\\\\, ok?", 0, ',', true, true);
|
||||||
|
EXPECT_EQ(varList2H[0], "Hello");
|
||||||
|
EXPECT_EQ(varList2H[1], "\\");
|
||||||
|
EXPECT_EQ(varList2H[2], "ok?");
|
||||||
|
|
||||||
|
CVarList2 varList2I("Hello,\\, ok?", 0, ',', true, false);
|
||||||
|
EXPECT_EQ(varList2I[0], "Hello");
|
||||||
|
EXPECT_EQ(varList2I[1], "\\");
|
||||||
|
EXPECT_EQ(varList2I[2], "ok?");
|
||||||
|
|
||||||
|
CVarList2 varList2J("", 0, ',', true, false);
|
||||||
|
EXPECT_EQ(varList2J[0], "");
|
||||||
|
|
||||||
|
CVarList2 varList2K(",\\, ok?", 0, ',', true);
|
||||||
|
EXPECT_EQ(varList2K[0], ", ok?");
|
||||||
|
|
||||||
|
CVarList2 varList2L("Hello, world", 0, ',', true);
|
||||||
|
EXPECT_EQ(varList2L.join(" "), "Hello world");
|
||||||
|
EXPECT_EQ(varList2L.join(" ", 0, 1000), "Hello world");
|
||||||
|
EXPECT_EQ(varList2L.join(" ", 0, 1), "Hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,397 +0,0 @@
|
||||||
#include <hyprutils/animation/AnimationConfig.hpp>
|
|
||||||
#include <hyprutils/animation/AnimationManager.hpp>
|
|
||||||
#include <hyprutils/animation/AnimatedVariable.hpp>
|
|
||||||
#include <hyprutils/memory/WeakPtr.hpp>
|
|
||||||
#include <hyprutils/memory/UniquePtr.hpp>
|
|
||||||
#include "shared.hpp"
|
|
||||||
|
|
||||||
#define SP CSharedPointer
|
|
||||||
#define WP CWeakPointer
|
|
||||||
#define UP CUniquePointer
|
|
||||||
|
|
||||||
using namespace Hyprutils::Animation;
|
|
||||||
using namespace Hyprutils::Math;
|
|
||||||
using namespace Hyprutils::Memory;
|
|
||||||
|
|
||||||
class EmtpyContext {};
|
|
||||||
|
|
||||||
template <typename VarType>
|
|
||||||
using CAnimatedVariable = CGenericAnimatedVariable<VarType, EmtpyContext>;
|
|
||||||
|
|
||||||
template <typename VarType>
|
|
||||||
using PANIMVAR = SP<CAnimatedVariable<VarType>>;
|
|
||||||
|
|
||||||
template <typename VarType>
|
|
||||||
using PANIMVARREF = WP<CAnimatedVariable<VarType>>;
|
|
||||||
|
|
||||||
enum eAVTypes {
|
|
||||||
INT = 1,
|
|
||||||
TEST,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SomeTestType {
|
|
||||||
bool done = false;
|
|
||||||
bool operator==(const SomeTestType& other) const {
|
|
||||||
return done == other.done;
|
|
||||||
}
|
|
||||||
SomeTestType& operator=(const SomeTestType& other) {
|
|
||||||
done = other.done;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
CAnimationConfigTree animationTree;
|
|
||||||
|
|
||||||
class CMyAnimationManager : public CAnimationManager {
|
|
||||||
public:
|
|
||||||
void tick() {
|
|
||||||
for (size_t i = 0; i < m_vActiveAnimatedVariables.size(); i++) {
|
|
||||||
const auto PAV = m_vActiveAnimatedVariables[i].lock();
|
|
||||||
if (!PAV || !PAV->ok() || !PAV->isBeingAnimated())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const auto SPENT = PAV->getPercent();
|
|
||||||
const auto PBEZIER = getBezier(PAV->getBezierName());
|
|
||||||
|
|
||||||
if (SPENT >= 1.f || !PAV->enabled()) {
|
|
||||||
PAV->warp(true, false);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto POINTY = PBEZIER->getYForPoint(SPENT);
|
|
||||||
|
|
||||||
switch (PAV->m_Type) {
|
|
||||||
case eAVTypes::INT: {
|
|
||||||
auto avInt = dc<CAnimatedVariable<int>*>(PAV.get());
|
|
||||||
if (!avInt)
|
|
||||||
std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET;
|
|
||||||
|
|
||||||
const auto DELTA = avInt->goal() - avInt->value();
|
|
||||||
avInt->value() = avInt->begun() + (DELTA * POINTY);
|
|
||||||
} break;
|
|
||||||
case eAVTypes::TEST: {
|
|
||||||
auto avCustom = dc<CAnimatedVariable<SomeTestType>*>(PAV.get());
|
|
||||||
if (!avCustom)
|
|
||||||
std::cout << Colors::RED << "Dynamic cast upcast failed" << Colors::RESET;
|
|
||||||
|
|
||||||
if (SPENT >= 1.f)
|
|
||||||
avCustom->value().done = true;
|
|
||||||
} break;
|
|
||||||
default: {
|
|
||||||
std::cout << Colors::RED << "What are we even doing?" << Colors::RESET;
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
|
|
||||||
PAV->onUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
tickDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename VarType>
|
|
||||||
void createAnimation(const VarType& v, PANIMVAR<VarType>& av, const std::string& animationConfigName) {
|
|
||||||
constexpr const eAVTypes EAVTYPE = std::is_same_v<VarType, int> ? eAVTypes::INT : eAVTypes::TEST;
|
|
||||||
const auto PAV = makeShared<CGenericAnimatedVariable<VarType, EmtpyContext>>();
|
|
||||||
|
|
||||||
PAV->create(EAVTYPE, sc<CAnimationManager*>(this), PAV, v);
|
|
||||||
PAV->setConfig(animationTree.getConfig(animationConfigName));
|
|
||||||
av = std::move(PAV);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void scheduleTick() {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void onTicked() {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
UP<CMyAnimationManager> pAnimationManager;
|
|
||||||
|
|
||||||
class Subject {
|
|
||||||
public:
|
|
||||||
Subject(const int& a, const int& b) {
|
|
||||||
pAnimationManager->createAnimation(a, m_iA, "default");
|
|
||||||
pAnimationManager->createAnimation(b, m_iB, "internal");
|
|
||||||
pAnimationManager->createAnimation({}, m_iC, "default");
|
|
||||||
}
|
|
||||||
PANIMVAR<int> m_iA;
|
|
||||||
PANIMVAR<int> m_iB;
|
|
||||||
PANIMVAR<SomeTestType> m_iC;
|
|
||||||
};
|
|
||||||
|
|
||||||
int config() {
|
|
||||||
pAnimationManager = makeUnique<CMyAnimationManager>();
|
|
||||||
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
animationTree.createNode("global");
|
|
||||||
animationTree.createNode("internal");
|
|
||||||
|
|
||||||
animationTree.createNode("foo", "internal");
|
|
||||||
animationTree.createNode("default", "global");
|
|
||||||
animationTree.createNode("bar", "default");
|
|
||||||
|
|
||||||
/*
|
|
||||||
internal
|
|
||||||
↳ foo
|
|
||||||
global
|
|
||||||
↳ default
|
|
||||||
↳ bar
|
|
||||||
*/
|
|
||||||
|
|
||||||
auto barCfg = animationTree.getConfig("bar");
|
|
||||||
auto internalCfg = animationTree.getConfig("internal");
|
|
||||||
|
|
||||||
// internal is a root node and should point to itself
|
|
||||||
EXPECT(internalCfg->pParentAnimation.get(), internalCfg.get());
|
|
||||||
EXPECT(internalCfg->pValues.get(), internalCfg.get());
|
|
||||||
|
|
||||||
animationTree.setConfigForNode("global", 1, 4.0, "default", "asdf");
|
|
||||||
|
|
||||||
EXPECT(barCfg->internalEnabled, -1);
|
|
||||||
{
|
|
||||||
const auto PVALUES = barCfg->pValues.lock();
|
|
||||||
EXPECT(PVALUES->internalEnabled, 1);
|
|
||||||
EXPECT(PVALUES->internalBezier, "default");
|
|
||||||
EXPECT(PVALUES->internalStyle, "asdf");
|
|
||||||
EXPECT(PVALUES->internalSpeed, 4.0);
|
|
||||||
}
|
|
||||||
EXPECT(barCfg->pParentAnimation.get(), animationTree.getConfig("default").get());
|
|
||||||
|
|
||||||
// Overwrite our own values
|
|
||||||
animationTree.setConfigForNode("bar", 1, 4.2, "test", "qwer");
|
|
||||||
|
|
||||||
{
|
|
||||||
const auto PVALUES = barCfg->pValues.lock();
|
|
||||||
EXPECT(PVALUES->internalEnabled, 1);
|
|
||||||
EXPECT(PVALUES->internalBezier, "test");
|
|
||||||
EXPECT(PVALUES->internalStyle, "qwer");
|
|
||||||
EXPECT(PVALUES->internalSpeed, 4.2f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now overwrite the parent
|
|
||||||
animationTree.setConfigForNode("default", 0, 0.0, "zxcv", "foo");
|
|
||||||
|
|
||||||
{
|
|
||||||
// Expecting no change
|
|
||||||
const auto PVALUES = barCfg->pValues.lock();
|
|
||||||
EXPECT(PVALUES->internalEnabled, 1);
|
|
||||||
EXPECT(PVALUES->internalBezier, "test");
|
|
||||||
EXPECT(PVALUES->internalStyle, "qwer");
|
|
||||||
EXPECT(PVALUES->internalSpeed, 4.2f);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv, char** envp) {
|
|
||||||
int ret = config();
|
|
||||||
|
|
||||||
animationTree.createNode("global");
|
|
||||||
animationTree.createNode("internal");
|
|
||||||
|
|
||||||
animationTree.createNode("default", "global");
|
|
||||||
animationTree.setConfigForNode("global", 1, 4.0, "default", "asdf");
|
|
||||||
|
|
||||||
Subject s(0, 0);
|
|
||||||
|
|
||||||
EXPECT(s.m_iA->value(), 0);
|
|
||||||
EXPECT(s.m_iB->value(), 0);
|
|
||||||
|
|
||||||
// Test destruction of a CAnimatedVariable
|
|
||||||
{
|
|
||||||
Subject s2(10, 10);
|
|
||||||
// Adds them to active
|
|
||||||
*s2.m_iA = 1;
|
|
||||||
*s2.m_iB = 2;
|
|
||||||
// We deliberately do not tick here, to make sure the destructor removes active animated variables
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT(pAnimationManager->shouldTickForNext(), false);
|
|
||||||
EXPECT(s.m_iC->value().done, false);
|
|
||||||
|
|
||||||
*s.m_iA = 10;
|
|
||||||
*s.m_iB = 100;
|
|
||||||
*s.m_iC = SomeTestType(true);
|
|
||||||
|
|
||||||
EXPECT(s.m_iC->value().done, false);
|
|
||||||
|
|
||||||
while (pAnimationManager->shouldTickForNext()) {
|
|
||||||
pAnimationManager->tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT(s.m_iA->value(), 10);
|
|
||||||
EXPECT(s.m_iB->value(), 100);
|
|
||||||
EXPECT(s.m_iC->value().done, true);
|
|
||||||
|
|
||||||
s.m_iA->setValue(0);
|
|
||||||
s.m_iB->setValue(0);
|
|
||||||
|
|
||||||
while (pAnimationManager->shouldTickForNext()) {
|
|
||||||
pAnimationManager->tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT(s.m_iA->value(), 10);
|
|
||||||
EXPECT(s.m_iB->value(), 100);
|
|
||||||
|
|
||||||
// Test config stuff
|
|
||||||
EXPECT(s.m_iA->getBezierName(), "default");
|
|
||||||
EXPECT(s.m_iA->getStyle(), "asdf");
|
|
||||||
EXPECT(s.m_iA->enabled(), true);
|
|
||||||
|
|
||||||
animationTree.getConfig("global")->internalEnabled = 0;
|
|
||||||
|
|
||||||
EXPECT(s.m_iA->enabled(), false);
|
|
||||||
|
|
||||||
*s.m_iA = 50;
|
|
||||||
pAnimationManager->tick(); // Expecting a warp
|
|
||||||
EXPECT(s.m_iA->value(), 50);
|
|
||||||
|
|
||||||
// Test missing pValues
|
|
||||||
animationTree.getConfig("global")->internalEnabled = 0;
|
|
||||||
animationTree.getConfig("default")->pValues.reset();
|
|
||||||
|
|
||||||
EXPECT(s.m_iA->enabled(), false);
|
|
||||||
EXPECT(s.m_iA->getBezierName(), "default");
|
|
||||||
EXPECT(s.m_iA->getStyle(), "");
|
|
||||||
EXPECT(s.m_iA->getPercent(), 1.f);
|
|
||||||
|
|
||||||
// Reset
|
|
||||||
animationTree.setConfigForNode("default", 1, 1, "default");
|
|
||||||
|
|
||||||
//
|
|
||||||
// Test callbacks
|
|
||||||
//
|
|
||||||
int beginCallbackRan = 0;
|
|
||||||
int updateCallbackRan = 0;
|
|
||||||
int endCallbackRan = 0;
|
|
||||||
s.m_iA->setCallbackOnBegin([&beginCallbackRan](WP<CBaseAnimatedVariable> pav) { beginCallbackRan++; });
|
|
||||||
s.m_iA->setUpdateCallback([&updateCallbackRan](WP<CBaseAnimatedVariable> pav) { updateCallbackRan++; });
|
|
||||||
s.m_iA->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> pav) { endCallbackRan++; }, false);
|
|
||||||
|
|
||||||
s.m_iA->setValueAndWarp(42);
|
|
||||||
|
|
||||||
EXPECT(beginCallbackRan, 0);
|
|
||||||
EXPECT(updateCallbackRan, 1);
|
|
||||||
EXPECT(endCallbackRan, 2); // first called when setting the callback, then when warping.
|
|
||||||
|
|
||||||
*s.m_iA = 1337;
|
|
||||||
while (pAnimationManager->shouldTickForNext()) {
|
|
||||||
pAnimationManager->tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT(beginCallbackRan, 1);
|
|
||||||
EXPECT(updateCallbackRan > 2, true);
|
|
||||||
EXPECT(endCallbackRan, 3);
|
|
||||||
|
|
||||||
std::vector<PANIMVAR<int>> vars;
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
vars.resize(vars.size() + 1);
|
|
||||||
pAnimationManager->createAnimation(1, vars.back(), "default");
|
|
||||||
*vars.back() = 1337;
|
|
||||||
}
|
|
||||||
|
|
||||||
// test adding / removing vars during a tick
|
|
||||||
s.m_iA->resetAllCallbacks();
|
|
||||||
s.m_iA->setUpdateCallback([&vars](WP<CBaseAnimatedVariable> v) {
|
|
||||||
if (v.lock() != vars.back())
|
|
||||||
vars.back()->warp();
|
|
||||||
});
|
|
||||||
s.m_iA->setCallbackOnEnd([&s, &vars](auto) {
|
|
||||||
vars.resize(vars.size() + 1);
|
|
||||||
pAnimationManager->createAnimation(1, vars.back(), "default");
|
|
||||||
*vars.back() = 1337;
|
|
||||||
});
|
|
||||||
|
|
||||||
*s.m_iA = 1000000;
|
|
||||||
|
|
||||||
while (pAnimationManager->shouldTickForNext()) {
|
|
||||||
pAnimationManager->tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT(s.m_iA->value(), 1000000);
|
|
||||||
// all vars should be set to 1337
|
|
||||||
EXPECT(std::find_if(vars.begin(), vars.end(), [](const auto& v) { return v->value() != 1337; }) == vars.end(), true);
|
|
||||||
|
|
||||||
// test one-time callbacks
|
|
||||||
s.m_iA->resetAllCallbacks();
|
|
||||||
s.m_iA->setCallbackOnEnd([&endCallbackRan](auto) { endCallbackRan++; }, true);
|
|
||||||
|
|
||||||
EXPECT(endCallbackRan, 4);
|
|
||||||
|
|
||||||
s.m_iA->setValueAndWarp(10);
|
|
||||||
|
|
||||||
EXPECT(endCallbackRan, 4);
|
|
||||||
EXPECT(s.m_iA->value(), 10);
|
|
||||||
|
|
||||||
// test warp
|
|
||||||
*s.m_iA = 3;
|
|
||||||
s.m_iA->setCallbackOnEnd([&endCallbackRan](auto) { endCallbackRan++; }, false);
|
|
||||||
|
|
||||||
s.m_iA->warp(false);
|
|
||||||
EXPECT(endCallbackRan, 4);
|
|
||||||
|
|
||||||
*s.m_iA = 4;
|
|
||||||
s.m_iA->warp(true);
|
|
||||||
EXPECT(endCallbackRan, 5);
|
|
||||||
|
|
||||||
// test getCurveValue
|
|
||||||
*s.m_iA = 0;
|
|
||||||
EXPECT(s.m_iA->getCurveValue(), 0.f);
|
|
||||||
s.m_iA->warp();
|
|
||||||
EXPECT(s.m_iA->getCurveValue(), 1.f);
|
|
||||||
EXPECT(endCallbackRan, 6);
|
|
||||||
|
|
||||||
// test end callback readding the var
|
|
||||||
*s.m_iA = 5;
|
|
||||||
s.m_iA->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> v) {
|
|
||||||
endCallbackRan++;
|
|
||||||
const auto PAV = dc<CAnimatedVariable<int>*>(v.lock().get());
|
|
||||||
|
|
||||||
*PAV = 10;
|
|
||||||
PAV->setCallbackOnEnd([&endCallbackRan](WP<CBaseAnimatedVariable> v) { endCallbackRan++; });
|
|
||||||
});
|
|
||||||
|
|
||||||
while (pAnimationManager->shouldTickForNext()) {
|
|
||||||
pAnimationManager->tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT(endCallbackRan, 8);
|
|
||||||
EXPECT(s.m_iA->value(), 10);
|
|
||||||
|
|
||||||
// Test duplicate active anim vars are not allowed
|
|
||||||
{
|
|
||||||
EXPECT(pAnimationManager->m_vActiveAnimatedVariables.size(), 0);
|
|
||||||
PANIMVAR<int> a;
|
|
||||||
pAnimationManager->createAnimation(1, a, "default");
|
|
||||||
EXPECT(pAnimationManager->m_vActiveAnimatedVariables.size(), 0);
|
|
||||||
*a = 10;
|
|
||||||
EXPECT(pAnimationManager->m_vActiveAnimatedVariables.size(), 1);
|
|
||||||
*a = 20;
|
|
||||||
EXPECT(pAnimationManager->m_vActiveAnimatedVariables.size(), 1);
|
|
||||||
a->warp();
|
|
||||||
EXPECT(pAnimationManager->m_vActiveAnimatedVariables.size(), 0);
|
|
||||||
EXPECT(a->value(), 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test no crash when animation manager gets destroyed
|
|
||||||
{
|
|
||||||
PANIMVAR<int> a;
|
|
||||||
pAnimationManager->createAnimation(1, a, "default");
|
|
||||||
*a = 10;
|
|
||||||
pAnimationManager.reset();
|
|
||||||
EXPECT(a->isAnimationManagerDead(), true);
|
|
||||||
a->setValueAndWarp(11);
|
|
||||||
EXPECT(a->value(), 11);
|
|
||||||
*a = 12;
|
|
||||||
a->warp();
|
|
||||||
EXPECT(a->value(), 12);
|
|
||||||
*a = 13;
|
|
||||||
} // a gets destroyed
|
|
||||||
|
|
||||||
EXPECT(pAnimationManager.get(), nullptr);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
#include <cmath>
|
|
||||||
#include <hyprutils/animation/BezierCurve.hpp>
|
|
||||||
#include <hyprutils/math/Vector2D.hpp>
|
|
||||||
#include "shared.hpp"
|
|
||||||
|
|
||||||
using Hyprutils::Animation::CBezierCurve;
|
|
||||||
using Hyprutils::Math::Vector2D;
|
|
||||||
|
|
||||||
static void test_nonmonotonic4_clamps_out_of_range(int& ret) {
|
|
||||||
// Non-monotonic curve in X
|
|
||||||
// This used to drive the step-halving search to OOB. It should now clamp
|
|
||||||
CBezierCurve curve;
|
|
||||||
std::array<Vector2D, 4> pts = {
|
|
||||||
Vector2D{0.5f, 1.0f}, // P0
|
|
||||||
Vector2D{1.0f, 1.0f}, // P1
|
|
||||||
Vector2D{0.0f, 0.0f}, // P2
|
|
||||||
Vector2D{0.5f, 0.0f} // P3
|
|
||||||
};
|
|
||||||
curve.setup4(pts);
|
|
||||||
|
|
||||||
// x > last baked x
|
|
||||||
EXPECT(std::isfinite(curve.getYForPoint(0.6f)), true);
|
|
||||||
// Far beyond range
|
|
||||||
EXPECT(std::isfinite(curve.getYForPoint(std::numeric_limits<float>::max())), true);
|
|
||||||
EXPECT(std::isfinite(curve.getYForPoint(-std::numeric_limits<float>::max())), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_adjacent_baked_x_equal(int& ret) {
|
|
||||||
// Curve with flat tail (X=1, Y=1)
|
|
||||||
CBezierCurve curve;
|
|
||||||
std::array<Vector2D, 4> pts = {
|
|
||||||
Vector2D{0.0f, 0.0f}, // P0
|
|
||||||
Vector2D{0.2f, 0.2f}, // P1
|
|
||||||
Vector2D{1.0f, 1.0f}, // P2
|
|
||||||
Vector2D{1.0f, 1.0f} // P3
|
|
||||||
};
|
|
||||||
curve.setup4(pts);
|
|
||||||
|
|
||||||
// Exactly at last baked X
|
|
||||||
const float y_at_end = curve.getYForPoint(1.0f);
|
|
||||||
// Slightly beyond last baked X
|
|
||||||
const float y_past_end = curve.getYForPoint(1.0001f);
|
|
||||||
|
|
||||||
EXPECT(y_at_end, 1.0f);
|
|
||||||
EXPECT(y_past_end, y_at_end);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_all_baked_x_equal(int& ret) {
|
|
||||||
// Extreme case: X is constant along the whole curve
|
|
||||||
CBezierCurve curve;
|
|
||||||
std::array<Vector2D, 4> pts = {
|
|
||||||
Vector2D{0.0f, 0.0f}, // P0
|
|
||||||
Vector2D{0.0f, 0.3f}, // P1
|
|
||||||
Vector2D{0.0f, 0.7f}, // P2
|
|
||||||
Vector2D{0.0f, 1.0f} // P3
|
|
||||||
};
|
|
||||||
curve.setup4(pts);
|
|
||||||
|
|
||||||
// Below any baked X
|
|
||||||
const float y_lo = curve.getYForPoint(-100.0f);
|
|
||||||
const float y_0 = curve.getYForPoint(0.0f);
|
|
||||||
// Above any baked X
|
|
||||||
const float y_hi = curve.getYForPoint(100.0f);
|
|
||||||
|
|
||||||
EXPECT(std::isfinite(y_lo), true);
|
|
||||||
EXPECT(std::isfinite(y_0), true);
|
|
||||||
EXPECT(std::isfinite(y_hi), true);
|
|
||||||
|
|
||||||
// For this curve Y should stay within [0,1]
|
|
||||||
EXPECT((y_lo >= 0.0f && y_lo <= 1.0f), true);
|
|
||||||
EXPECT((y_0 >= 0.0f && y_0 <= 1.0f), true);
|
|
||||||
EXPECT((y_hi >= 0.0f && y_hi <= 1.0f), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
test_nonmonotonic4_clamps_out_of_range(ret);
|
|
||||||
test_adjacent_baked_x_equal(ret);
|
|
||||||
test_all_baked_x_equal(ret);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
#include <hyprutils/os/FileDescriptor.hpp>
|
|
||||||
#include "shared.hpp"
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
using namespace Hyprutils::OS;
|
|
||||||
|
|
||||||
int main(int argc, char** argv, char** envp) {
|
|
||||||
std::string name = "/test_filedescriptors";
|
|
||||||
CFileDescriptor fd(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
|
|
||||||
|
|
||||||
int ret = 0;
|
|
||||||
EXPECT(fd.isValid(), true);
|
|
||||||
EXPECT(fd.isReadable(), true);
|
|
||||||
|
|
||||||
int flags = fd.getFlags();
|
|
||||||
EXPECT(fd.getFlags(), FD_CLOEXEC);
|
|
||||||
flags &= ~FD_CLOEXEC;
|
|
||||||
fd.setFlags(flags);
|
|
||||||
EXPECT(fd.getFlags(), !FD_CLOEXEC);
|
|
||||||
|
|
||||||
CFileDescriptor fd2 = fd.duplicate();
|
|
||||||
EXPECT(fd.isValid(), true);
|
|
||||||
EXPECT(fd.isReadable(), true);
|
|
||||||
EXPECT(fd2.isValid(), true);
|
|
||||||
EXPECT(fd2.isReadable(), true);
|
|
||||||
|
|
||||||
CFileDescriptor fd3(fd2.take());
|
|
||||||
EXPECT(fd.isValid(), true);
|
|
||||||
EXPECT(fd.isReadable(), true);
|
|
||||||
EXPECT(fd2.isValid(), false);
|
|
||||||
EXPECT(fd2.isReadable(), false);
|
|
||||||
|
|
||||||
// .duplicate default flags is FD_CLOEXEC
|
|
||||||
EXPECT(fd3.getFlags(), FD_CLOEXEC);
|
|
||||||
|
|
||||||
fd.reset();
|
|
||||||
fd2.reset();
|
|
||||||
fd3.reset();
|
|
||||||
|
|
||||||
EXPECT(fd.isReadable(), false);
|
|
||||||
EXPECT(fd2.isReadable(), false);
|
|
||||||
EXPECT(fd3.isReadable(), false);
|
|
||||||
|
|
||||||
shm_unlink(name.c_str());
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
#include <hyprutils/i18n/I18nEngine.hpp>
|
|
||||||
#include "shared.hpp"
|
|
||||||
#include <print>
|
|
||||||
|
|
||||||
using namespace Hyprutils::I18n;
|
|
||||||
|
|
||||||
enum eTxtKeys : uint64_t {
|
|
||||||
TXT_KEY_HELLO,
|
|
||||||
TXT_KEY_I_HAVE_APPLES,
|
|
||||||
TXT_KEY_FALLBACK,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char** argv, char** envp) {
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
CI18nEngine engine;
|
|
||||||
|
|
||||||
std::println("System locale: {}, stem: {}", engine.getSystemLocale().locale(), engine.getSystemLocale().stem());
|
|
||||||
|
|
||||||
engine.setFallbackLocale("en_US");
|
|
||||||
|
|
||||||
engine.registerEntry("en_US", TXT_KEY_HELLO, "Hello World!");
|
|
||||||
engine.registerEntry("en_US", TXT_KEY_I_HAVE_APPLES, [](const translationVarMap& m) {
|
|
||||||
if (std::stoi(m.at("count")) == 1)
|
|
||||||
return "I have {count} apple.";
|
|
||||||
else
|
|
||||||
return "I have {count} apples.";
|
|
||||||
});
|
|
||||||
engine.registerEntry("en_US", TXT_KEY_FALLBACK, "Fallback string!");
|
|
||||||
|
|
||||||
engine.registerEntry("pl_PL", TXT_KEY_HELLO, "Witaj świecie!");
|
|
||||||
engine.registerEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, [](const translationVarMap& m) {
|
|
||||||
const auto COUNT = std::stoi(m.at("count"));
|
|
||||||
if (COUNT == 1)
|
|
||||||
return "Mam {count} jabłko.";
|
|
||||||
else if (COUNT < 5)
|
|
||||||
return "Mam {count} jabłka.";
|
|
||||||
else
|
|
||||||
return "Mam {count} jabłek.";
|
|
||||||
});
|
|
||||||
|
|
||||||
engine.registerEntry("es_XX", TXT_KEY_FALLBACK, "I don't speak spanish");
|
|
||||||
engine.registerEntry("es_ES", TXT_KEY_FALLBACK, "I don't speak spanish here either");
|
|
||||||
|
|
||||||
EXPECT(engine.localizeEntry("en_US", TXT_KEY_HELLO, {}), "Hello World!");
|
|
||||||
EXPECT(engine.localizeEntry("pl_PL", TXT_KEY_HELLO, {}), "Witaj świecie!");
|
|
||||||
EXPECT(engine.localizeEntry("de_DE", TXT_KEY_HELLO, {}), "Hello World!");
|
|
||||||
|
|
||||||
EXPECT(engine.localizeEntry("en_US", TXT_KEY_I_HAVE_APPLES, {{"count", "1"}}), "I have 1 apple.");
|
|
||||||
EXPECT(engine.localizeEntry("en_US", TXT_KEY_I_HAVE_APPLES, {{"count", "2"}}), "I have 2 apples.");
|
|
||||||
|
|
||||||
EXPECT(engine.localizeEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, {{"count", "1"}}), "Mam 1 jabłko.");
|
|
||||||
EXPECT(engine.localizeEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, {{"count", "2"}}), "Mam 2 jabłka.");
|
|
||||||
EXPECT(engine.localizeEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, {{"count", "5"}}), "Mam 5 jabłek.");
|
|
||||||
|
|
||||||
EXPECT(engine.localizeEntry("pl", TXT_KEY_I_HAVE_APPLES, {{"count", "5"}}), "Mam 5 jabłek.");
|
|
||||||
|
|
||||||
EXPECT(engine.localizeEntry("pl_XX", TXT_KEY_I_HAVE_APPLES, {{"count", "5"}}), "Mam 5 jabłek.");
|
|
||||||
EXPECT(engine.localizeEntry("en_XX", TXT_KEY_I_HAVE_APPLES, {{"count", "2"}}), "I have 2 apples.");
|
|
||||||
|
|
||||||
EXPECT(engine.localizeEntry("es_YY", TXT_KEY_FALLBACK, {}), "I don't speak spanish here either");
|
|
||||||
EXPECT(engine.localizeEntry("es_XX", TXT_KEY_FALLBACK, {}), "I don't speak spanish");
|
|
||||||
|
|
||||||
EXPECT(engine.localizeEntry("pl_PL", TXT_KEY_FALLBACK, {}), "Fallback string!");
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
136
tests/math.cpp
136
tests/math.cpp
|
|
@ -1,136 +0,0 @@
|
||||||
#include <hyprutils/math/Region.hpp>
|
|
||||||
#include <hyprutils/math/Mat3x3.hpp>
|
|
||||||
#include "shared.hpp"
|
|
||||||
|
|
||||||
using namespace Hyprutils::Math;
|
|
||||||
|
|
||||||
int main(int argc, char** argv, char** envp) {
|
|
||||||
CRegion rg = {0, 0, 100, 100};
|
|
||||||
rg.add(CBox{{}, {20, 200}});
|
|
||||||
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
EXPECT(rg.getExtents().height, 200);
|
|
||||||
EXPECT(rg.getExtents().width, 100);
|
|
||||||
|
|
||||||
rg.intersect(CBox{10, 10, 300, 300});
|
|
||||||
|
|
||||||
EXPECT(rg.getExtents().width, 90);
|
|
||||||
EXPECT(rg.getExtents().height, 190);
|
|
||||||
|
|
||||||
/*Box.cpp test cases*/
|
|
||||||
// Test default constructor and accessors
|
|
||||||
{
|
|
||||||
CBox box1;
|
|
||||||
EXPECT(box1.x, 0);
|
|
||||||
EXPECT(box1.y, 0);
|
|
||||||
EXPECT(box1.width, 0);
|
|
||||||
EXPECT(box1.height, 0);
|
|
||||||
|
|
||||||
// Test parameterized constructor and accessors
|
|
||||||
CBox box2(10, 20, 30, 40);
|
|
||||||
EXPECT(box2.x, 10);
|
|
||||||
EXPECT(box2.y, 20);
|
|
||||||
EXPECT(box2.width, 30);
|
|
||||||
EXPECT(box2.height, 40);
|
|
||||||
|
|
||||||
// Test setters and getters
|
|
||||||
box2.translate(Vector2D(5, -5));
|
|
||||||
EXPECT_VECTOR2D(box2.pos(), Vector2D(15, 15));
|
|
||||||
}
|
|
||||||
|
|
||||||
//Test Scaling and Transformation
|
|
||||||
{
|
|
||||||
CBox box(10, 10, 20, 30);
|
|
||||||
|
|
||||||
// Test scaling
|
|
||||||
box.scale(2.0);
|
|
||||||
EXPECT_VECTOR2D(box.size(), Vector2D(40, 60));
|
|
||||||
EXPECT_VECTOR2D(box.pos(), Vector2D(20, 20));
|
|
||||||
|
|
||||||
// Test scaling from center
|
|
||||||
box.scaleFromCenter(0.5);
|
|
||||||
EXPECT_VECTOR2D(box.size(), Vector2D(20, 30));
|
|
||||||
EXPECT_VECTOR2D(box.pos(), Vector2D(30, 35));
|
|
||||||
|
|
||||||
// Test transformation
|
|
||||||
box.transform(HYPRUTILS_TRANSFORM_90, 100, 200);
|
|
||||||
EXPECT_VECTOR2D(box.pos(), Vector2D(135, 30));
|
|
||||||
EXPECT_VECTOR2D(box.size(), Vector2D(30, 20));
|
|
||||||
|
|
||||||
// Test Intersection and Extents
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
CBox box1(0, 0, 100, 100);
|
|
||||||
CBox box2(50, 50, 100, 100);
|
|
||||||
|
|
||||||
CBox intersection = box1.intersection(box2);
|
|
||||||
EXPECT_VECTOR2D(intersection.pos(), Vector2D(50, 50));
|
|
||||||
EXPECT_VECTOR2D(intersection.size(), Vector2D(50, 50));
|
|
||||||
|
|
||||||
SBoxExtents extents = box1.extentsFrom(box2);
|
|
||||||
EXPECT_VECTOR2D(extents.topLeft, Vector2D(50, 50));
|
|
||||||
EXPECT_VECTOR2D(extents.bottomRight, Vector2D(-50, -50));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test Boundary Conditions and Special Cases
|
|
||||||
{
|
|
||||||
CBox box(0, 0, 50, 50);
|
|
||||||
|
|
||||||
EXPECT(box.empty(), false);
|
|
||||||
|
|
||||||
EXPECT(box.containsPoint(Vector2D(25, 25)), true);
|
|
||||||
EXPECT(box.containsPoint(Vector2D(60, 60)), false);
|
|
||||||
EXPECT(box.overlaps(CBox(25, 25, 50, 50)), true);
|
|
||||||
EXPECT(box.inside(CBox(0, 0, 100, 100)), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test matrices
|
|
||||||
{
|
|
||||||
Mat3x3 jeremy = Mat3x3::outputProjection({1920, 1080}, HYPRUTILS_TRANSFORM_FLIPPED_90);
|
|
||||||
Mat3x3 matrixBox = jeremy.projectBox(CBox{10, 10, 200, 200}, HYPRUTILS_TRANSFORM_NORMAL).translate({100, 100}).scale({1.25F, 1.5F}).transpose();
|
|
||||||
|
|
||||||
Mat3x3 expected = std::array<float, 9>{0, 0.46296296, 0, 0.3125, 0, 0, 19.84375, 36.055557, 1};
|
|
||||||
// we need to do this to avoid precision errors on 32-bit archs
|
|
||||||
EXPECT(std::abs(expected.getMatrix().at(0) - matrixBox.getMatrix().at(0)) < 0.1, true);
|
|
||||||
EXPECT(std::abs(expected.getMatrix().at(1) - matrixBox.getMatrix().at(1)) < 0.1, true);
|
|
||||||
EXPECT(std::abs(expected.getMatrix().at(2) - matrixBox.getMatrix().at(2)) < 0.1, true);
|
|
||||||
EXPECT(std::abs(expected.getMatrix().at(3) - matrixBox.getMatrix().at(3)) < 0.1, true);
|
|
||||||
EXPECT(std::abs(expected.getMatrix().at(4) - matrixBox.getMatrix().at(4)) < 0.1, true);
|
|
||||||
EXPECT(std::abs(expected.getMatrix().at(5) - matrixBox.getMatrix().at(5)) < 0.1, true);
|
|
||||||
EXPECT(std::abs(expected.getMatrix().at(6) - matrixBox.getMatrix().at(6)) < 0.1, true);
|
|
||||||
EXPECT(std::abs(expected.getMatrix().at(7) - matrixBox.getMatrix().at(7)) < 0.1, true);
|
|
||||||
EXPECT(std::abs(expected.getMatrix().at(8) - matrixBox.getMatrix().at(8)) < 0.1, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test Region Scaling
|
|
||||||
{
|
|
||||||
CRegion rg(CBox{{20, 20}, {40, 40}});
|
|
||||||
|
|
||||||
auto extents = rg.getExtents();
|
|
||||||
EXPECT_VECTOR2D(extents.pos(), Vector2D(20, 20));
|
|
||||||
EXPECT_VECTOR2D(extents.size(), Vector2D(40, 40));
|
|
||||||
|
|
||||||
rg.scale(2);
|
|
||||||
extents = rg.getExtents();
|
|
||||||
EXPECT_VECTOR2D(extents.pos(), Vector2D(40, 40));
|
|
||||||
EXPECT_VECTOR2D(extents.size(), Vector2D(80, 80));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Vector2D original(30, 40);
|
|
||||||
Vector2D monitorSize(100, 200);
|
|
||||||
|
|
||||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_NORMAL, monitorSize), Vector2D(30, 40 ));
|
|
||||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_90, monitorSize), Vector2D(40, 200 - 30));
|
|
||||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_180, monitorSize), Vector2D(100 - 30, 200 - 40));
|
|
||||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_270, monitorSize), Vector2D(100 - 40, 30 ));
|
|
||||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_FLIPPED, monitorSize), Vector2D(100 - 30, 40 ));
|
|
||||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_FLIPPED_90, monitorSize), Vector2D(40, 30 ));
|
|
||||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_FLIPPED_180, monitorSize), Vector2D(30, 200 - 40));
|
|
||||||
EXPECT_VECTOR2D(original.transform(HYPRUTILS_TRANSFORM_FLIPPED_270, monitorSize), Vector2D(100 - 40, 200 - 30));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
35
tests/os.cpp
35
tests/os.cpp
|
|
@ -1,35 +0,0 @@
|
||||||
#include <csignal>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <hyprutils/os/Process.hpp>
|
|
||||||
#include "shared.hpp"
|
|
||||||
|
|
||||||
using namespace Hyprutils::OS;
|
|
||||||
|
|
||||||
int main(int argc, char** argv, char** envp) {
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
CProcess process("sh", {"-c", "echo \"Hello $WORLD!\""});
|
|
||||||
process.addEnv("WORLD", "World");
|
|
||||||
|
|
||||||
EXPECT(process.runAsync(), true);
|
|
||||||
EXPECT(process.runSync(), true);
|
|
||||||
|
|
||||||
EXPECT(process.stdOut(), std::string{"Hello World!\n"});
|
|
||||||
EXPECT(process.stdErr(), std::string{""});
|
|
||||||
EXPECT(process.exitCode(), 0);
|
|
||||||
|
|
||||||
CProcess process2("sh", {"-c", "while true; do sleep 1; done;"});
|
|
||||||
|
|
||||||
EXPECT(process2.runAsync(), true);
|
|
||||||
EXPECT(getpgid(process2.pid()) >= 0, true);
|
|
||||||
|
|
||||||
kill(process2.pid(), SIGKILL);
|
|
||||||
|
|
||||||
CProcess process3("sh", {"-c", "cat /geryueruggbuergheruger/reugiheruygyuerghuryeghyer/eruihgyuerguyerghyuerghuyergerguyer/NON_EXISTENT"});
|
|
||||||
EXPECT(process3.runSync(), true);
|
|
||||||
EXPECT(process3.exitCode(), 1);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace Colors {
|
|
||||||
constexpr const char* RED = "\x1b[31m";
|
|
||||||
constexpr const char* GREEN = "\x1b[32m";
|
|
||||||
constexpr const char* YELLOW = "\x1b[33m";
|
|
||||||
constexpr const char* BLUE = "\x1b[34m";
|
|
||||||
constexpr const char* MAGENTA = "\x1b[35m";
|
|
||||||
constexpr const char* CYAN = "\x1b[36m";
|
|
||||||
constexpr const char* RESET = "\x1b[0m";
|
|
||||||
};
|
|
||||||
|
|
||||||
#define EXPECT(expr, val) \
|
|
||||||
if (const auto RESULT = expr; RESULT != (val)) { \
|
|
||||||
std::cout << Colors::RED << "Failed: " << Colors::RESET << #expr << ", expected " << val << " but got " << RESULT << "\n"; \
|
|
||||||
ret = 1; \
|
|
||||||
} else { \
|
|
||||||
std::cout << Colors::GREEN << "Passed " << Colors::RESET << #expr << ". Got " << val << "\n"; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define EXPECT_VECTOR2D(expr, val) \
|
|
||||||
do { \
|
|
||||||
const auto& RESULT = expr; \
|
|
||||||
const auto& EXPECTED = val; \
|
|
||||||
if (!(std::abs(RESULT.x - EXPECTED.x) < 1e-6 && std::abs(RESULT.y - EXPECTED.y) < 1e-6)) { \
|
|
||||||
std::cout << Colors::RED << "Failed: " << Colors::RESET << #expr << ", expected (" << EXPECTED.x << ", " << EXPECTED.y << ") but got (" << RESULT.x << ", " \
|
|
||||||
<< RESULT.y << ")\n"; \
|
|
||||||
ret = 1; \
|
|
||||||
} else { \
|
|
||||||
std::cout << Colors::GREEN << "Passed " << Colors::RESET << #expr << ". Got (" << RESULT.x << ", " << RESULT.y << ")\n"; \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define EXPECT_NEAR(actual, expected, tolerance) \
|
|
||||||
do { \
|
|
||||||
auto _a = (actual); \
|
|
||||||
auto _e = (expected); \
|
|
||||||
auto _t = (tolerance); \
|
|
||||||
if (!(std::fabs((_a) - (_e)) <= (_t))) { \
|
|
||||||
std::cout << Colors::RED << "Failed: " << Colors::RESET << " EXPECT_NEAR(" #actual ", " #expected ", " #tolerance ") got=" << _a << " expected=" << _e << " ± " << _t \
|
|
||||||
<< "\n"; \
|
|
||||||
ret = 1; \
|
|
||||||
} else { \
|
|
||||||
std::cout << Colors::GREEN << "Passed " << Colors::RESET << " |" #actual " - " #expected "| <= " #tolerance "\n"; \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
370
tests/signal.cpp
370
tests/signal.cpp
|
|
@ -1,370 +0,0 @@
|
||||||
#include <any>
|
|
||||||
#include <hyprutils/signal/Signal.hpp>
|
|
||||||
#include <hyprutils/memory/WeakPtr.hpp>
|
|
||||||
#include <memory>
|
|
||||||
#include "hyprutils/memory/SharedPtr.hpp"
|
|
||||||
#include "hyprutils/signal/Listener.hpp"
|
|
||||||
#include "shared.hpp"
|
|
||||||
|
|
||||||
using namespace Hyprutils::Signal;
|
|
||||||
using namespace Hyprutils::Memory;
|
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
//
|
|
||||||
|
|
||||||
void legacy(int& ret) {
|
|
||||||
CSignal signal;
|
|
||||||
int data = 0;
|
|
||||||
auto listener = signal.registerListener([&]([[maybe_unused]] std::any d) { data = 1; });
|
|
||||||
|
|
||||||
signal.emit();
|
|
||||||
|
|
||||||
EXPECT(data, 1);
|
|
||||||
|
|
||||||
data = 0;
|
|
||||||
|
|
||||||
listener.reset();
|
|
||||||
|
|
||||||
signal.emit();
|
|
||||||
|
|
||||||
EXPECT(data, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void legacyListenerEmit(int& ret) {
|
|
||||||
int data = 0;
|
|
||||||
CSignal signal;
|
|
||||||
auto listener = signal.registerListener([&](std::any d) { data = std::any_cast<int>(d); });
|
|
||||||
|
|
||||||
listener->emit(1); // not a typo
|
|
||||||
EXPECT(data, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void legacyListeners(int& ret) {
|
|
||||||
int data = 0;
|
|
||||||
|
|
||||||
CSignalT<> signal0;
|
|
||||||
CSignalT<int> signal1;
|
|
||||||
|
|
||||||
auto listener0 = signal0.registerListener([&](std::any d) { data += 1; });
|
|
||||||
auto listener1 = signal1.registerListener([&](std::any d) { data += std::any_cast<int>(d); });
|
|
||||||
|
|
||||||
signal0.registerStaticListener([&](void* o, std::any d) { data += 10; }, nullptr);
|
|
||||||
signal1.registerStaticListener([&](void* o, std::any d) { data += std::any_cast<int>(d) * 10; }, nullptr);
|
|
||||||
|
|
||||||
signal0.emit();
|
|
||||||
signal1.emit(2);
|
|
||||||
|
|
||||||
EXPECT(data, 33);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
//
|
|
||||||
|
|
||||||
void empty(int& ret) {
|
|
||||||
int data = 0;
|
|
||||||
|
|
||||||
CSignalT<> signal;
|
|
||||||
auto listener = signal.listen([&] { data = 1; });
|
|
||||||
|
|
||||||
signal.emit();
|
|
||||||
EXPECT(data, 1);
|
|
||||||
|
|
||||||
data = 0;
|
|
||||||
listener.reset();
|
|
||||||
signal.emit();
|
|
||||||
EXPECT(data, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void typed(int& ret) {
|
|
||||||
int data = 0;
|
|
||||||
|
|
||||||
CSignalT<int> signal;
|
|
||||||
auto listener = signal.listen([&](int newData) { data = newData; });
|
|
||||||
|
|
||||||
signal.emit(1);
|
|
||||||
EXPECT(data, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ignoreParams(int& ret) {
|
|
||||||
int data = 0;
|
|
||||||
|
|
||||||
CSignalT<int> signal;
|
|
||||||
auto listener = signal.listen([&] { data += 1; });
|
|
||||||
|
|
||||||
signal.listenStatic([&] { data += 1; });
|
|
||||||
|
|
||||||
signal.emit(2);
|
|
||||||
EXPECT(data, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void typedMany(int& ret) {
|
|
||||||
int data1 = 0;
|
|
||||||
int data2 = 0;
|
|
||||||
int data3 = 0;
|
|
||||||
|
|
||||||
CSignalT<int, int, int> signal;
|
|
||||||
auto listener = signal.listen([&](int d1, int d2, int d3) {
|
|
||||||
data1 = d1;
|
|
||||||
data2 = d2;
|
|
||||||
data3 = d3;
|
|
||||||
});
|
|
||||||
|
|
||||||
signal.emit(1, 2, 3);
|
|
||||||
EXPECT(data1, 1);
|
|
||||||
EXPECT(data2, 2);
|
|
||||||
EXPECT(data3, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ref(int& ret) {
|
|
||||||
int count = 0;
|
|
||||||
int data = 0;
|
|
||||||
|
|
||||||
CSignalT<int&> signal;
|
|
||||||
auto l1 = signal.listen([&](int& v) { v += 1; });
|
|
||||||
auto l2 = signal.listen([&](int v) { count += v; });
|
|
||||||
signal.emit(data);
|
|
||||||
|
|
||||||
CSignalT<const int&> constSignal;
|
|
||||||
auto l3 = constSignal.listen([&](const int& v) { count += v; });
|
|
||||||
auto l4 = constSignal.listen([&](int v) { count += v; });
|
|
||||||
constSignal.emit(data);
|
|
||||||
|
|
||||||
EXPECT(data, 1);
|
|
||||||
EXPECT(count, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void refMany(int& ret) {
|
|
||||||
int count = 0;
|
|
||||||
int data1 = 0;
|
|
||||||
int data2 = 10;
|
|
||||||
|
|
||||||
CSignalT<int&, const int&> signal;
|
|
||||||
auto l1 = signal.listen([&](int& v, const int&) { v += 1; });
|
|
||||||
auto l2 = signal.listen([&](int v1, int v2) { count += v1 + v2; });
|
|
||||||
|
|
||||||
signal.emit(data1, data2);
|
|
||||||
EXPECT(data1, 1);
|
|
||||||
EXPECT(count, 11);
|
|
||||||
}
|
|
||||||
|
|
||||||
void autoRefTypes(int& ret) {
|
|
||||||
class CCopyCounter {
|
|
||||||
public:
|
|
||||||
CCopyCounter(int& createCount, int& destroyCount) : createCount(createCount), destroyCount(destroyCount) {
|
|
||||||
createCount += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
CCopyCounter(CCopyCounter&& other) noexcept : CCopyCounter(other.createCount, other.destroyCount) {}
|
|
||||||
CCopyCounter(const CCopyCounter& other) noexcept : CCopyCounter(other.createCount, other.destroyCount) {}
|
|
||||||
|
|
||||||
~CCopyCounter() {
|
|
||||||
destroyCount += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
int& createCount;
|
|
||||||
int& destroyCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto createCount = 0;
|
|
||||||
auto destroyCount = 0;
|
|
||||||
|
|
||||||
CSignalT<CCopyCounter> signal;
|
|
||||||
auto listener = signal.listen([](const CCopyCounter& counter) {});
|
|
||||||
|
|
||||||
signal.emit(CCopyCounter(createCount, destroyCount));
|
|
||||||
EXPECT(createCount, 1);
|
|
||||||
EXPECT(destroyCount, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void forward(int& ret) {
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
CSignalT<int> sig;
|
|
||||||
CSignalT<int> connected1;
|
|
||||||
CSignalT<> connected2;
|
|
||||||
|
|
||||||
auto conn1 = sig.forward(connected1);
|
|
||||||
auto conn2 = sig.forward(connected2);
|
|
||||||
|
|
||||||
auto listener1 = connected1.listen([&](int v) { count += v; });
|
|
||||||
auto listener2 = connected2.listen([&] { count += 1; });
|
|
||||||
|
|
||||||
sig.emit(2);
|
|
||||||
|
|
||||||
EXPECT(count, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void listenerAdded(int& ret) {
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
CSignalT<> signal;
|
|
||||||
CHyprSignalListener secondListener;
|
|
||||||
|
|
||||||
auto listener = signal.listen([&] {
|
|
||||||
count += 1;
|
|
||||||
|
|
||||||
if (!secondListener)
|
|
||||||
secondListener = signal.listen([&] { count += 1; });
|
|
||||||
});
|
|
||||||
|
|
||||||
signal.emit();
|
|
||||||
EXPECT(count, 1); // second should NOT be invoked as it was registed during emit
|
|
||||||
|
|
||||||
signal.emit();
|
|
||||||
EXPECT(count, 3); // second should be invoked
|
|
||||||
}
|
|
||||||
|
|
||||||
void lastListenerSwapped(int& ret) {
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
CSignalT<> signal;
|
|
||||||
CHyprSignalListener removedListener;
|
|
||||||
CHyprSignalListener addedListener;
|
|
||||||
|
|
||||||
auto firstListener = signal.listen([&] {
|
|
||||||
removedListener.reset(); // dropped and should NOT be invoked
|
|
||||||
|
|
||||||
if (!addedListener)
|
|
||||||
addedListener = signal.listen([&] { count += 2; });
|
|
||||||
});
|
|
||||||
|
|
||||||
removedListener = signal.listen([&] { count += 1; });
|
|
||||||
|
|
||||||
signal.emit();
|
|
||||||
EXPECT(count, 0); // neither the removed nor added listeners should fire
|
|
||||||
|
|
||||||
signal.emit();
|
|
||||||
EXPECT(count, 2); // only the new listener should fire
|
|
||||||
}
|
|
||||||
|
|
||||||
void signalDestroyed(int& ret) {
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
auto signal = std::make_unique<CSignalT<>>();
|
|
||||||
|
|
||||||
// This ensures a destructor of a listener called before signal reset is safe.
|
|
||||||
auto preListener = signal->listen([&] { count += 1; });
|
|
||||||
|
|
||||||
auto listener = signal->listen([&] { signal.reset(); });
|
|
||||||
|
|
||||||
// This ensures a destructor of a listener called after signal reset is safe
|
|
||||||
// and gets called.
|
|
||||||
auto postListener = signal->listen([&] { count += 1; });
|
|
||||||
|
|
||||||
signal->emit();
|
|
||||||
EXPECT(count, 2); // all listeners should fire regardless of signal deletion
|
|
||||||
}
|
|
||||||
|
|
||||||
// purely an asan test
|
|
||||||
void signalDestroyedBeforeListener() {
|
|
||||||
CHyprSignalListener listener1;
|
|
||||||
CHyprSignalListener listener2;
|
|
||||||
|
|
||||||
CSignalT<> signal;
|
|
||||||
|
|
||||||
listener1 = signal.listen([] {});
|
|
||||||
listener2 = signal.listen([] {});
|
|
||||||
}
|
|
||||||
|
|
||||||
void signalDestroyedWithAddedListener(int& ret) {
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
auto signal = std::make_unique<CSignalT<>>();
|
|
||||||
CHyprSignalListener shouldNotRun;
|
|
||||||
|
|
||||||
auto listener = signal->listen([&] {
|
|
||||||
shouldNotRun = signal->listen([&] { count += 2; });
|
|
||||||
signal.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
signal->emit();
|
|
||||||
EXPECT(count, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void signalDestroyedWithRemovedAndAddedListener(int& ret) {
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
auto signal = std::make_unique<CSignalT<>>();
|
|
||||||
CHyprSignalListener removed;
|
|
||||||
CHyprSignalListener shouldNotRun;
|
|
||||||
|
|
||||||
auto listener = signal->listen([&] {
|
|
||||||
removed.reset();
|
|
||||||
shouldNotRun = signal->listen([&] { count += 2; });
|
|
||||||
signal.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
removed = signal->listen([&] { count += 1; });
|
|
||||||
|
|
||||||
signal->emit();
|
|
||||||
EXPECT(count, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void staticListener(int& ret) {
|
|
||||||
int data = 0;
|
|
||||||
|
|
||||||
CSignalT<int> signal;
|
|
||||||
signal.listenStatic([&](int newData) { data = newData; });
|
|
||||||
|
|
||||||
signal.emit(1);
|
|
||||||
EXPECT(data, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void staticListenerDestroy(int& ret) {
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
auto signal = makeShared<CSignalT<>>();
|
|
||||||
signal->listenStatic([&] { count += 1; });
|
|
||||||
|
|
||||||
signal->listenStatic([&] {
|
|
||||||
// should not fire but SHOULD be freed
|
|
||||||
signal->listenStatic([&] { count += 3; });
|
|
||||||
|
|
||||||
signal.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
signal->listenStatic([&] { count += 1; });
|
|
||||||
|
|
||||||
signal->emit();
|
|
||||||
EXPECT(count, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// purely an asan test
|
|
||||||
void listenerDestroysSelf() {
|
|
||||||
CSignalT<> signal;
|
|
||||||
|
|
||||||
CHyprSignalListener listener;
|
|
||||||
listener = signal.listen([&] { listener.reset(); });
|
|
||||||
|
|
||||||
// the static signal case is taken care of above
|
|
||||||
|
|
||||||
signal.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv, char** envp) {
|
|
||||||
int ret = 0;
|
|
||||||
legacy(ret);
|
|
||||||
legacyListenerEmit(ret);
|
|
||||||
legacyListeners(ret);
|
|
||||||
empty(ret);
|
|
||||||
typed(ret);
|
|
||||||
ignoreParams(ret);
|
|
||||||
typedMany(ret);
|
|
||||||
ref(ret);
|
|
||||||
refMany(ret);
|
|
||||||
autoRefTypes(ret);
|
|
||||||
forward(ret);
|
|
||||||
listenerAdded(ret);
|
|
||||||
lastListenerSwapped(ret);
|
|
||||||
signalDestroyed(ret);
|
|
||||||
signalDestroyedBeforeListener();
|
|
||||||
signalDestroyedWithAddedListener(ret);
|
|
||||||
signalDestroyedWithRemovedAndAddedListener(ret);
|
|
||||||
staticListener(ret);
|
|
||||||
staticListenerDestroy(ret);
|
|
||||||
signalDestroyed(ret);
|
|
||||||
listenerDestroysSelf();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
100
tests/string.cpp
100
tests/string.cpp
|
|
@ -1,100 +0,0 @@
|
||||||
#include <hyprutils/string/String.hpp>
|
|
||||||
#include <hyprutils/string/VarList.hpp>
|
|
||||||
#include <hyprutils/string/VarList2.hpp>
|
|
||||||
#include <hyprutils/string/ConstVarList.hpp>
|
|
||||||
#include "shared.hpp"
|
|
||||||
|
|
||||||
using namespace Hyprutils::String;
|
|
||||||
|
|
||||||
int main(int argc, char** argv, char** envp) {
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
EXPECT(trim(" a "), "a");
|
|
||||||
EXPECT(trim(" a a "), "a a");
|
|
||||||
EXPECT(trim("a"), "a");
|
|
||||||
EXPECT(trim(" "), "");
|
|
||||||
|
|
||||||
EXPECT(isNumber("99214123434"), true);
|
|
||||||
EXPECT(isNumber("-35252345234"), true);
|
|
||||||
EXPECT(isNumber("---3423--432"), false);
|
|
||||||
EXPECT(isNumber("s---3423--432"), false);
|
|
||||||
EXPECT(isNumber("---3423--432s"), false);
|
|
||||||
EXPECT(isNumber("1s"), false);
|
|
||||||
EXPECT(isNumber(""), false);
|
|
||||||
EXPECT(isNumber("-"), false);
|
|
||||||
EXPECT(isNumber("--0"), false);
|
|
||||||
EXPECT(isNumber("abc"), false);
|
|
||||||
EXPECT(isNumber("0.0", true), true);
|
|
||||||
EXPECT(isNumber("0.2", true), true);
|
|
||||||
EXPECT(isNumber("0.", true), false);
|
|
||||||
EXPECT(isNumber(".0", true), false);
|
|
||||||
EXPECT(isNumber("", true), false);
|
|
||||||
EXPECT(isNumber("vvss", true), false);
|
|
||||||
EXPECT(isNumber("0.9999s", true), false);
|
|
||||||
EXPECT(isNumber("s0.9999", true), false);
|
|
||||||
EXPECT(isNumber("-1.0", true), true);
|
|
||||||
EXPECT(isNumber("-1..0", true), false);
|
|
||||||
EXPECT(isNumber("-10.0000000001", true), true);
|
|
||||||
|
|
||||||
CVarList list("hello world!", 0, 's', true);
|
|
||||||
EXPECT(list[0], "hello");
|
|
||||||
EXPECT(list[1], "world!");
|
|
||||||
|
|
||||||
CConstVarList listConst("hello world!", 0, 's', true);
|
|
||||||
EXPECT(listConst[0], "hello");
|
|
||||||
EXPECT(listConst[1], "world!");
|
|
||||||
|
|
||||||
CConstVarList listConst2("0 set", 2, ' ');
|
|
||||||
EXPECT(listConst2[0], "0");
|
|
||||||
EXPECT(listConst2[1], "set");
|
|
||||||
|
|
||||||
CVarList2 varList2("0 set", 2, ' ');
|
|
||||||
EXPECT(varList2[0], "0");
|
|
||||||
EXPECT(varList2[1], "set");
|
|
||||||
|
|
||||||
varList2.append("Hello");
|
|
||||||
|
|
||||||
EXPECT(varList2[1], "set");
|
|
||||||
EXPECT(varList2[2], "Hello");
|
|
||||||
|
|
||||||
CVarList2 varList2B("hello, world\\, ok?", 0, ',', true, true);
|
|
||||||
EXPECT(varList2B[0], "hello");
|
|
||||||
EXPECT(varList2B[1], "world, ok?");
|
|
||||||
|
|
||||||
CVarList2 varList2C("hello, , ok?", 0, ',', true, true);
|
|
||||||
EXPECT(varList2C[0], "hello");
|
|
||||||
EXPECT(varList2C[1], "ok?");
|
|
||||||
|
|
||||||
CVarList2 varList2D("\\\\, , ok?", 0, ',', true, true);
|
|
||||||
EXPECT(varList2D[0], "\\");
|
|
||||||
EXPECT(varList2D[1], "ok?");
|
|
||||||
|
|
||||||
CVarList2 varList2E("\\, , ok?", 0, ',', true, true);
|
|
||||||
EXPECT(varList2E[0], ",");
|
|
||||||
EXPECT(varList2E[1], "ok?");
|
|
||||||
|
|
||||||
CVarList2 varList2F("Hello, world\\\\, ok?", 0, ',', true, true);
|
|
||||||
EXPECT(varList2F[0], "Hello");
|
|
||||||
EXPECT(varList2F[1], "world\\");
|
|
||||||
EXPECT(varList2F[2], "ok?");
|
|
||||||
|
|
||||||
CVarList2 varList2G("Hello,\\, ok?", 0, ',', true, true);
|
|
||||||
EXPECT(varList2G[0], "Hello");
|
|
||||||
EXPECT(varList2G[1], ", ok?");
|
|
||||||
|
|
||||||
CVarList2 varList2H("Hello,\\\\, ok?", 0, ',', true, true);
|
|
||||||
EXPECT(varList2H[0], "Hello");
|
|
||||||
EXPECT(varList2H[1], "\\");
|
|
||||||
EXPECT(varList2H[2], "ok?");
|
|
||||||
|
|
||||||
CVarList2 varList2I("Hello,\\, ok?", 0, ',', true, false);
|
|
||||||
EXPECT(varList2I[0], "Hello");
|
|
||||||
EXPECT(varList2I[1], "\\");
|
|
||||||
EXPECT(varList2I[2], "ok?");
|
|
||||||
|
|
||||||
std::string hello = "hello world!";
|
|
||||||
replaceInString(hello, "hello", "hi");
|
|
||||||
EXPECT(hello, "hi world!");
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue