flatten 20260225
This commit is contained in:
11
tjp/core/testing/Defines.h
Normal file
11
tjp/core/testing/Defines.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "NO_WHEN.h"
|
||||
#include "TESTING_ONLY.h"
|
||||
#include "TEST_SIGNAL.h"
|
||||
#include "wait_until.h"
|
||||
|
||||
11
tjp/core/testing/NO_WHEN.h
Normal file
11
tjp/core/testing/NO_WHEN.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#define NO_WHEN(x) if (false)
|
||||
#define NO_GIVEN(x) if (false)
|
||||
#define NO_THEN(x) if (false)
|
||||
#define NO_SCENARIO(...) static void no_scenario##__COUNTER__ ()
|
||||
|
||||
12
tjp/core/testing/TESTING_ONLY.h
Normal file
12
tjp/core/testing/TESTING_ONLY.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef TESTING
|
||||
#define TESTING_ONLY(...) __VA_ARGS__
|
||||
#else
|
||||
#define TESTING_ONLY(...)
|
||||
#endif
|
||||
|
||||
182
tjp/core/testing/TEST_SIGNAL.cpp
Normal file
182
tjp/core/testing/TEST_SIGNAL.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#define TESTING
|
||||
#include "TEST_SIGNAL.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <list>
|
||||
|
||||
#include "../threads/Lock.h"
|
||||
#include "../timer/Timer.hpp"
|
||||
|
||||
#include "../log/Log.h"
|
||||
#include "../log/LogOf.h"
|
||||
#include "../algorithm/starts_with.hpp"
|
||||
#include "../algorithm/ends_with.hpp"
|
||||
#include "../assert/debug_assert.h"
|
||||
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
void TestSignal::clear ()
|
||||
{
|
||||
auto l = lock_of(mutex);
|
||||
clear_no_lock();
|
||||
}
|
||||
|
||||
void TestSignal::clear_no_lock ()
|
||||
{
|
||||
events.clear();
|
||||
cleared = true;
|
||||
}
|
||||
|
||||
std::ostream &TestSignal::print (std::ostream &s)
|
||||
{
|
||||
auto l = lock_of(mutex);
|
||||
return print_no_lock(s);
|
||||
}
|
||||
|
||||
std::ostream &TestSignal::print_no_lock (std::ostream &s)
|
||||
{
|
||||
s << "TSE: test events\n";
|
||||
for (auto &e : events)
|
||||
{
|
||||
s << "TSE: \t" << e.object << "\t" << e.message << std::endl;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream &TestSignal::print_clear (std::ostream &s)
|
||||
{
|
||||
auto l = lock_of(mutex);
|
||||
auto &r = print_no_lock(s);
|
||||
clear_no_lock();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
void TestSignal::append (Event &&e)
|
||||
{
|
||||
xLogDebug("TESTING_EVENT " << logVar(e.object) << logVar(e.message));
|
||||
|
||||
auto l = lock_of(mutex);
|
||||
events.emplace_back(std::move(e));
|
||||
signal.notify_all();
|
||||
}
|
||||
|
||||
TestSignal::Events::iterator TestSignal::find(const Event &e, Events::iterator i)
|
||||
{
|
||||
if (i == events.end())
|
||||
i = events.begin();
|
||||
else
|
||||
++i;
|
||||
|
||||
bool hasStar = false;
|
||||
std::string_view search;
|
||||
if (ends_with(e.message, STAR))
|
||||
{
|
||||
search = { e.message.c_str(), e.message.length() - STAR.length() };
|
||||
hasStar = true;
|
||||
}
|
||||
|
||||
for (; i!=events.end(); ++i)
|
||||
{
|
||||
auto &event = *i;
|
||||
if (e.object == nullptr || e.object == event.object)
|
||||
{
|
||||
|
||||
if (hasStar)
|
||||
{
|
||||
if (starts_with(event.message, search))
|
||||
return i;
|
||||
}
|
||||
else
|
||||
if (e.message == event.message)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return events.end();
|
||||
}
|
||||
|
||||
bool TestSignal::waitFor(const Event &e, double time, std::function<void(Events::iterator&)> &&f)
|
||||
{
|
||||
Timer t;
|
||||
auto l = lock_of(mutex);
|
||||
auto i = events.end();
|
||||
while (t.elapsed() < time)
|
||||
{
|
||||
if (cleared)
|
||||
{
|
||||
i = events.end();
|
||||
cleared = false;
|
||||
}
|
||||
|
||||
i = find(e, i);
|
||||
if (i != events.end())
|
||||
{
|
||||
f(i);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
i--;
|
||||
}
|
||||
|
||||
signal.wait_for(l, std::chrono::milliseconds(int(1000 * time)));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TestSignal::waitForNot(const Event &e, double time, double settleTime)
|
||||
{
|
||||
Timer t;
|
||||
Timer settleTimer;
|
||||
auto i = events.end();
|
||||
|
||||
auto l = lock_of(mutex);
|
||||
cleared = false;
|
||||
while (t.elapsed() < time && settleTimer.elapsed() < settleTime)
|
||||
{
|
||||
debug_assert(!cleared);
|
||||
|
||||
auto j = find(e, i);
|
||||
|
||||
if (j != events.end())
|
||||
{
|
||||
i = j;
|
||||
settleTimer.restart();
|
||||
}
|
||||
|
||||
auto remainingTime = time - t.elapsed();
|
||||
auto waitTime = std::min(remainingTime, settleTime);
|
||||
signal.wait_for(l, std::chrono::milliseconds(int(1000 * waitTime)));
|
||||
}
|
||||
|
||||
return t.elapsed() < time;
|
||||
}
|
||||
|
||||
bool TestSignal::waitFor(const Event &e, double time)
|
||||
{
|
||||
return waitFor(e, time, [](auto &i){});
|
||||
}
|
||||
|
||||
bool TestSignal::waitForErase(const Event &e, double time)
|
||||
{
|
||||
return waitFor(e, time, [this](auto &i){ events.erase(i); });
|
||||
}
|
||||
|
||||
bool TestSignal::waitForClear(const Event &e, double time)
|
||||
{
|
||||
return waitFor(e, time, [this](auto &i){ events.clear(); });
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
163
tjp/core/testing/TEST_SIGNAL.h
Normal file
163
tjp/core/testing/TEST_SIGNAL.h
Normal file
@@ -0,0 +1,163 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef TESTING
|
||||
|
||||
#include "../threads/Lock.hpp"
|
||||
#include "../timer/Timer.hpp"
|
||||
#include <functional>
|
||||
|
||||
#include <sstream>
|
||||
#include <list>
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
|
||||
struct TestSignal
|
||||
{
|
||||
core::Event::Mutex mutex;
|
||||
core::Event signal;
|
||||
bool cleared = false;
|
||||
|
||||
struct Event {
|
||||
void *object;
|
||||
std::string message;
|
||||
} ;
|
||||
|
||||
typedef std::list<Event> Events;
|
||||
Events events;
|
||||
|
||||
public:
|
||||
void clear ();
|
||||
void clear_no_lock ();
|
||||
void append (Event &&e);
|
||||
|
||||
const std::string STAR = "*";
|
||||
Events::iterator find(const Event &e, Events::iterator i);
|
||||
|
||||
bool waitFor(const Event &e, double time, std::function<void(Events::iterator&)> &&f);
|
||||
bool waitForNot(const Event &e, double time, double settleTime);
|
||||
bool waitFor(const Event &e, double time);
|
||||
bool waitForErase(const Event &e, double time);
|
||||
bool waitForClear(const Event &e, double time);
|
||||
|
||||
std::ostream &print (std::ostream &s);
|
||||
std::ostream &print_no_lock (std::ostream &s);
|
||||
std::ostream &print_clear (std::ostream &s);
|
||||
} ;
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct TestSignalSingleton
|
||||
{
|
||||
static TestSignal *singleton;
|
||||
|
||||
static TestSignal *getInstance ()
|
||||
{
|
||||
if (!singleton)
|
||||
singleton = new TestSignal();
|
||||
|
||||
return singleton;
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
TestSignal *TestSignalSingleton<T>::singleton = nullptr;
|
||||
|
||||
|
||||
// -------
|
||||
|
||||
|
||||
template<typename S, typename T, typename ...X>
|
||||
void process_test_event_string_args(S &s, T &t)
|
||||
{
|
||||
s << t;
|
||||
}
|
||||
|
||||
template<typename S, typename T, typename ...X>
|
||||
void process_test_event_string_args(S &s, T &&t, X&&...x)
|
||||
{
|
||||
s << t << ",";
|
||||
process_test_event_string_args(s, x...);
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
std::string process_test_event_string(T&& ...t)
|
||||
{
|
||||
std::ostringstream s;
|
||||
process_test_event_string_args(s, t...);
|
||||
return s.str();
|
||||
}
|
||||
|
||||
template<typename...T>
|
||||
void process_test_event_clear()
|
||||
{
|
||||
TestSignalSingleton<void>::getInstance()->clear ();
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
S &process_test_event_print(S &stream)
|
||||
{
|
||||
return TestSignalSingleton<void>::getInstance()->print (stream);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
S &process_test_event_print_clear(S &stream)
|
||||
{
|
||||
return TestSignalSingleton<void>::getInstance()->print_clear (stream);
|
||||
}
|
||||
|
||||
template<typename...T>
|
||||
void process_test_event(void *v, T&&...t)
|
||||
{
|
||||
TestSignalSingleton<void>::getInstance()->append (TestSignal::Event{ v, process_test_event_string(t...) });
|
||||
}
|
||||
|
||||
template<typename...T>
|
||||
bool wait_test_event(double time, void *v, T&&...t)
|
||||
{
|
||||
return TestSignalSingleton<void>::getInstance()->waitFor(TestSignal::Event{ v, process_test_event_string(t...) }, time);
|
||||
}
|
||||
|
||||
template<typename...T>
|
||||
bool wait_test_event_clear(double time, void *v, T&&...t)
|
||||
{
|
||||
return TestSignalSingleton<void>::getInstance()->waitForClear(TestSignal::Event{ v, process_test_event_string(t...) }, time);
|
||||
}
|
||||
|
||||
template<typename...T>
|
||||
bool wait_test_event_erase(double time, void *v, T&&...t)
|
||||
{
|
||||
return TestSignalSingleton<void>::getInstance()->waitForErase(TestSignal::Event{ v, process_test_event_string(t...) }, time);
|
||||
}
|
||||
|
||||
template<typename...T>
|
||||
bool wait_test_event_settle(double time, double settle, void *v, T&&...t)
|
||||
{
|
||||
return TestSignalSingleton<void>::getInstance()->waitForNot(TestSignal::Event{ v, process_test_event_string(t...) }, time, settle);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
#define TEST_SIGNAL_CLEAR() tjp::core::process_test_event_clear()
|
||||
#define TEST_SIGNAL_PRINT(stream) tjp::core::process_test_event_print(stream)
|
||||
#define TEST_SIGNAL_PRINT_CLEAR(stream) tjp::core::process_test_event_print_clear(stream)
|
||||
#define TEST_SIGNAL(v,...) tjp::core::process_test_event(v,__VA_ARGS__)
|
||||
#define TEST_WAIT(delay, v, ...) tjp::core::wait_test_event(delay, v, __VA_ARGS__)
|
||||
#define TEST_WAIT_CLEAR(delay, v, ...) tjp::core::wait_test_event_clear(delay, v, __VA_ARGS__)
|
||||
#define TEST_WAIT_ERASE(delay, v, ...) tjp::core::wait_test_event_erase(delay, v, __VA_ARGS__)
|
||||
#define TEST_WAIT_SETTLE(delay, settle, v, ...) tjp::core::wait_test_event_settle(delay, settle, v, __VA_ARGS__)
|
||||
|
||||
#else
|
||||
|
||||
#define TEST_SIGNAL_CLEAR(...)
|
||||
#define TEST_SIGNAL(...)
|
||||
#define TEST_WAIT(...) true
|
||||
#define TEST_WAIT_ERASE(...) true
|
||||
#define TEST_WAIT_CLEAR(...) true
|
||||
|
||||
#endif
|
||||
8
tjp/core/testing/THEN_REQUIRE.h
Normal file
8
tjp/core/testing/THEN_REQUIRE.h
Normal file
@@ -0,0 +1,8 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#define THEN_REQUIRE(x, ...) THEN(x) { REQUIRE(__VA_ARGS__); }
|
||||
|
||||
59
tjp/core/testing/_tests/TEST_SIGNAL.cpp
Normal file
59
tjp/core/testing/_tests/TEST_SIGNAL.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#ifdef TESTS_WHICH_REQUIRE_LIB
|
||||
|
||||
#define TESTING
|
||||
#include <tjp/core/testing/TEST_SIGNAL.h>
|
||||
#include <tjp/core/testing/Defines.h>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
SCENARIO("test signaling", "[core::future]" )
|
||||
{
|
||||
GIVEN("a signal")
|
||||
{
|
||||
TEST_SIGNAL_CLEAR();
|
||||
TEST_SIGNAL((void *)1, "hello");
|
||||
|
||||
WHEN("testing exactly")
|
||||
{
|
||||
REQUIRE(TEST_WAIT(1.0, (void *)1, "hello"));
|
||||
}
|
||||
|
||||
WHEN("testing any same message")
|
||||
{
|
||||
REQUIRE(TEST_WAIT(1.0, nullptr, "hello"));
|
||||
}
|
||||
|
||||
WHEN("testing any message same object")
|
||||
{
|
||||
REQUIRE(TEST_WAIT(1.0, (void *)1, "*"));
|
||||
}
|
||||
|
||||
WHEN("testing any object wrong message")
|
||||
{
|
||||
REQUIRE(!TEST_WAIT(0.1, nullptr, "goodbye"));
|
||||
}
|
||||
|
||||
WHEN("testing any message wrong object")
|
||||
{
|
||||
REQUIRE(!TEST_WAIT(0.1, (void *)2, "*"));
|
||||
}
|
||||
|
||||
WHEN("testing any message and then not")
|
||||
{
|
||||
REQUIRE(TEST_WAIT_ERASE(0.1, (void *)nullptr, "hello"));
|
||||
REQUIRE(!TEST_WAIT_ERASE(0.1, (void *)nullptr, "hello"));
|
||||
}
|
||||
|
||||
WHEN("testing any message and then not")
|
||||
{
|
||||
REQUIRE(TEST_WAIT_CLEAR(0.1, (void *)nullptr, "hello"));
|
||||
REQUIRE(!TEST_WAIT_ERASE(0.1, (void *)nullptr, "hello"));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
27
tjp/core/testing/catch.cpp
Normal file
27
tjp/core/testing/catch.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#include "catch2.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
int CATCH2_REPEAT_TEST_QUANTITY = 1;
|
||||
|
||||
namespace tjp::core::testing {
|
||||
|
||||
void cleanup(int)
|
||||
{
|
||||
kill(0, SIGKILL); // Kill all processes in the group
|
||||
}
|
||||
|
||||
void setup_kill_childprocesses_on_exit()
|
||||
{
|
||||
signal(SIGHUP, cleanup); // Handle Xcode stop signal
|
||||
signal(SIGTERM, cleanup); // Handle Xcode stop signal
|
||||
setpgid(0, 0); // Set this process as the leader of a new process group
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
21
tjp/core/testing/catch.hpp
Normal file
21
tjp/core/testing/catch.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "catch2.hpp"
|
||||
#include "../string/to_string.hpp"
|
||||
|
||||
extern int CATCH2_REPEAT_TEST_QUANTITY;
|
||||
|
||||
#define REPEAT_TEST() \
|
||||
for (auto i=0; i<CATCH2_REPEAT_TEST_QUANTITY; ++i) \
|
||||
GIVEN("repeat " + core::to_string(i))
|
||||
|
||||
|
||||
namespace tjp::core::testing {
|
||||
|
||||
void setup_kill_childprocesses_on_exit();
|
||||
|
||||
}
|
||||
17802
tjp/core/testing/catch2.hpp
Normal file
17802
tjp/core/testing/catch2.hpp
Normal file
File diff suppressed because it is too large
Load Diff
43
tjp/core/testing/wait_until.h
Normal file
43
tjp/core/testing/wait_until.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
|
||||
template<typename D, typename F>
|
||||
inline auto wait_until(D &&d, F &&f, int incrementMS=100)
|
||||
{
|
||||
typedef std::chrono::high_resolution_clock Clock;
|
||||
|
||||
auto then = Clock::now();
|
||||
|
||||
auto expire = then + d;
|
||||
while (!f())
|
||||
{
|
||||
auto now = Clock::now();
|
||||
if (now > expire)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(incrementMS));
|
||||
}
|
||||
|
||||
auto now = Clock::now();
|
||||
return now - then;
|
||||
}
|
||||
|
||||
template<typename D, typename F>
|
||||
inline auto performance_wait_until(D &&d, F &&f)
|
||||
{
|
||||
return wait_until(std::move(d), std::move(f), 1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user