flatten 20260225

This commit is contained in:
Timothy Prepscius
2026-02-25 12:43:06 -05:00
commit 996151a56a
521 changed files with 46722 additions and 0 deletions

View 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"

View 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__ ()

View 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

View 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

View 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

View 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__); }

View 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

View 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
}
}

View 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

File diff suppressed because it is too large Load Diff

View 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