Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,172 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#include "gtest/gtest.h"
#include <everest/util/queue/simple_queue.hpp>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <utility>
using namespace everest::lib::util;
// =================================================================
// 2. Test Fixture Setup
// =================================================================
template <typename T> class SimpleQueueTest : public ::testing::Test {
protected:
simple_queue<T> queue;
};
// Typed Test Suite for standard types
using QueueTypes = ::testing::Types<int, std::string>;
TYPED_TEST_SUITE(SimpleQueueTest, QueueTypes);
// =================================================================
// A. Basic Functionality Tests (FIFO & Empty Checks)
// =================================================================
TYPED_TEST(SimpleQueueTest, InitialStateIsEmpty) {
ASSERT_TRUE(this->queue.empty());
ASSERT_EQ(this->queue.size(), 0);
ASSERT_FALSE(this->queue.pop().has_value());
}
TYPED_TEST(SimpleQueueTest, PushAndEmptyCheck) {
TypeParam value;
// Use if constexpr to initialize value correctly
if constexpr (std::is_same_v<TypeParam, int>) {
value = 10;
} else if constexpr (std::is_same_v<TypeParam, std::string>) {
value = "Test_10";
} else {
return;
}
this->queue.push(value);
ASSERT_FALSE(this->queue.empty());
ASSERT_EQ(this->queue.size(), 1);
}
TYPED_TEST(SimpleQueueTest, PushPopAndEmptyCheck) {
TypeParam expected_value;
// Use if constexpr to initialize value correctly
if constexpr (std::is_same_v<TypeParam, int>) {
expected_value = 42;
} else if constexpr (std::is_same_v<TypeParam, std::string>) {
expected_value = "Test_42";
} else {
return;
}
this->queue.push(expected_value);
std::optional<TypeParam> result = this->queue.pop();
ASSERT_TRUE(result.has_value());
ASSERT_EQ(result.value(), expected_value);
ASSERT_TRUE(this->queue.empty());
ASSERT_EQ(this->queue.size(), 0);
}
TYPED_TEST(SimpleQueueTest, MultiplePushAndPopOrder) {
const int count = 3;
// Push elements (0, 1, 2)
for (int i = 0; i < count; ++i) {
if constexpr (std::is_same_v<TypeParam, int>) {
this->queue.push(i);
} else {
this->queue.push(std::to_string(i));
}
}
// Pop elements and verify FIFO order (0, 1, 2)
for (int i = 0; i < count; ++i) {
std::optional<TypeParam> result = this->queue.pop();
TypeParam expected_value;
if constexpr (std::is_same_v<TypeParam, int>) {
expected_value = i;
} else {
expected_value = std::to_string(i);
}
ASSERT_TRUE(result.has_value());
ASSERT_EQ(result.value(), expected_value) << "Element popped out of FIFO order.";
}
ASSERT_TRUE(this->queue.empty());
}
// =================================================================
// B. Reference Tests (front() and back())
// =================================================================
TYPED_TEST(SimpleQueueTest, FrontAndBackReferences) {
TypeParam val1, val2;
if constexpr (std::is_same_v<TypeParam, int>) {
val1 = 100;
val2 = 200;
} else if constexpr (std::is_same_v<TypeParam, std::string>) {
val1 = "Front";
val2 = "Back";
} else {
return;
}
this->queue.push(val1);
this->queue.push(val2);
// Verify front()
ASSERT_EQ(this->queue.front(), val1);
// Verify back()
ASSERT_EQ(this->queue.back(), val2);
// After pop, front should change
this->queue.pop();
ASSERT_EQ(this->queue.front(), val2);
ASSERT_EQ(this->queue.back(), val2);
}
// =================================================================
// C. Move-Only Type Compatibility Test (Verifying the pop() fix)
// =================================================================
// Test suite for std::unique_ptr<int> (a move-only type)
class SimpleQueueMoveOnlyTest : public ::testing::Test {
protected:
simple_queue<std::unique_ptr<int>> queue;
};
TEST_F(SimpleQueueMoveOnlyTest, PushAndPopMoveOnlyType) {
const int value1 = 10;
const int value2 = 20;
// Push: Requires the r-value push overload
this->queue.push(std::make_unique<int>(value1));
this->queue.push(std::make_unique<int>(value2));
ASSERT_EQ(this->queue.size(), 2);
// Pop: Requires the fixed move-based pop()
std::optional<std::unique_ptr<int>> opt_result1 = this->queue.pop();
// Verify the value was retrieved
ASSERT_TRUE(opt_result1.has_value());
ASSERT_NE(opt_result1.value(), nullptr);
ASSERT_EQ(*opt_result1.value(), value1);
// Pop the second item
std::optional<std::unique_ptr<int>> opt_result2 = this->queue.pop();
ASSERT_TRUE(opt_result2.has_value());
ASSERT_EQ(*opt_result2.value(), value2);
ASSERT_TRUE(this->queue.empty());
}

View File

@@ -0,0 +1,161 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#include "gtest/gtest.h"
#include <atomic>
#include <chrono>
#include <everest/util/queue/thread_safe_bounded_queue.hpp>
#include <optional>
#include <thread>
#include <vector>
using namespace std::chrono_literals;
using namespace everest::lib::util;
/**
* @brief Helper struct to mimic the TrackedAction used in the thread pool,
* as the queue now expects a type with an .arrival member.
*/
struct TestTask {
int value;
std::chrono::steady_clock::time_point arrival;
explicit TestTask(int v = 0) : value(v), arrival(std::chrono::steady_clock::now()) {
}
};
// =================================================================
// 1. Bounded Functionality Tests (Backpressure)
// =================================================================
TEST(ThreadSafeBoundedQueueTest, PushBlocksWhenFull) {
const size_t limit = 2;
thread_safe_bounded_queue<TestTask> queue(limit);
// Fill the queue to the limit
queue.push(TestTask(1));
queue.push(TestTask(2));
std::atomic<bool> push_completed{false};
std::thread producer([&] {
// This should block until a consumer pops an item
queue.push(TestTask(3));
push_completed = true;
});
// Give the thread a moment to start and block
std::this_thread::sleep_for(50ms);
ASSERT_FALSE(push_completed.load());
// Pop an item, which should unblock the producer
auto popped = queue.try_pop(100ms);
ASSERT_TRUE(popped.has_value());
ASSERT_EQ(popped->value, 1);
producer.join();
ASSERT_TRUE(push_completed.load());
}
// =================================================================
// 2. Latency Interface Tests
// =================================================================
TEST(ThreadSafeBoundedQueueTest, OldestArrivalTracking) {
thread_safe_bounded_queue<TestTask> queue(10);
auto t1 = std::chrono::steady_clock::now();
queue.push(TestTask(100));
std::this_thread::sleep_for(10ms);
auto t2 = std::chrono::steady_clock::now();
queue.push(TestTask(200));
auto oldest = queue.oldest_arrival();
// The oldest arrival should be close to t1, certainly before t2
ASSERT_GE(oldest, t1);
ASSERT_LT(oldest, t2);
}
// =================================================================
// 3. Stop and Signaling Tests
// =================================================================
TEST(ThreadSafeBoundedQueueTest, StopUnblocksBlockedProducers) {
thread_safe_bounded_queue<TestTask> queue(1);
queue.push(TestTask(1)); // Fill it
std::atomic<bool> producer_exited{false};
std::thread producer([&] {
// This blocks because queue is full
size_t result = queue.push(TestTask(2));
// result should be 0 because the queue was stopped
if (result == 0) {
producer_exited = true;
}
});
std::this_thread::sleep_for(50ms);
queue.stop(); // This should wake the producer up
producer.join();
ASSERT_TRUE(producer_exited.load());
}
TEST(ThreadSafeBoundedQueueTest, StopReturnsNullOptToConsumers) {
thread_safe_bounded_queue<TestTask> queue(5);
std::thread consumer([&] {
auto result = queue.try_pop(1s);
ASSERT_FALSE(result.has_value());
});
std::this_thread::sleep_for(20ms);
queue.stop();
consumer.join();
}
// =================================================================
// 4. Stress Tests (Concurrent Producers and Consumers)
// =================================================================
TEST(ThreadSafeBoundedQueueTest, HighContentionStressTest) {
const int num_producers = 4;
const int num_consumers = 4;
const int items_per_producer = 1000;
const size_t queue_limit = 10;
thread_safe_bounded_queue<TestTask> queue(queue_limit);
std::atomic<int> total_popped{0};
std::atomic<int> sum_popped{0};
std::vector<std::thread> workers;
// Consumers
for (int i = 0; i < num_consumers; ++i) {
workers.emplace_back([&] {
while (total_popped < (num_producers * items_per_producer)) {
auto val = queue.try_pop(10ms);
if (val) {
sum_popped += val->value;
total_popped++;
}
}
});
}
// Producers
for (int i = 0; i < num_producers; ++i) {
workers.emplace_back([&] {
for (int j = 0; j < items_per_producer; ++j) {
queue.push(TestTask(1));
}
});
}
for (auto& w : workers)
w.join();
ASSERT_EQ(total_popped.load(), num_producers * items_per_producer);
ASSERT_EQ(sum_popped.load(), num_producers * items_per_producer);
}

View File

@@ -0,0 +1,311 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#include "gtest/gtest.h"
#include <algorithm>
#include <atomic>
#include <chrono>
#include <everest/util/queue/thread_safe_queue.hpp>
#include <memory> // For std::unique_ptr
#include <numeric>
#include <optional>
#include <set>
#include <thread>
#include <type_traits> // For std::is_same_v
#include <vector>
// Note: Add includes for your simple_queue and thread_safe_queue here
// #include "simple_queue.h"
// #include "thread_safe_queue.h"
using namespace std::chrono_literals;
using namespace everest::lib::util;
// =================================================================
// 1. Test Fixture Setup
// =================================================================
// Helper to initialize TypeParam correctly in typed tests
template <typename T> T initialize_value(int id) {
if constexpr (std::is_same_v<T, int>) {
return id;
} else if constexpr (std::is_same_v<T, std::string>) {
return "Value_" + std::to_string(id);
} else {
// Fallback for other non-tested types
return T{};
}
}
// Test Fixture
template <typename T> class ThreadSafeQueueTest : public ::testing::Test {
protected:
thread_safe_queue<T> queue;
};
// Typed Test Suite for int and std::string
using QueueTypes = ::testing::Types<int, std::string>;
TYPED_TEST_SUITE(ThreadSafeQueueTest, QueueTypes);
// Define a test suite specifically for concurrency checks (using int)
using ThreadSafeQueueIntTest = ThreadSafeQueueTest<int>;
// =================================================================
// 2. Basic Functionality Tests (Single Thread)
// =================================================================
TYPED_TEST(ThreadSafeQueueTest, PushAndPopSimple) {
TypeParam expected_value = initialize_value<TypeParam>(42);
this->queue.push(expected_value);
// Test the non-blocking pop
std::optional<TypeParam> result = this->queue.try_pop();
ASSERT_TRUE(result.has_value());
ASSERT_EQ(result.value(), expected_value);
ASSERT_FALSE(this->queue.try_pop().has_value());
}
TYPED_TEST(ThreadSafeQueueTest, MultiplePushAndPopOrder) {
const int count = 5;
for (int i = 0; i < count; ++i) {
this->queue.push(initialize_value<TypeParam>(i));
}
for (int i = 0; i < count; ++i) {
TypeParam expected_value = initialize_value<TypeParam>(i);
std::optional<TypeParam> result = this->queue.try_pop();
ASSERT_TRUE(result.has_value());
ASSERT_EQ(result.value(), expected_value);
}
ASSERT_FALSE(this->queue.try_pop().has_value());
}
// =================================================================
// 3. Time-Based Functionality Tests
// =================================================================
TYPED_TEST(ThreadSafeQueueTest, TryPopWithTimeout_Timeout) {
auto start = std::chrono::steady_clock::now();
// Try to pop with a short timeout
std::optional<TypeParam> result = this->queue.try_pop(10ms);
auto end = std::chrono::steady_clock::now();
ASSERT_FALSE(result.has_value());
auto elapsed = end - start;
ASSERT_GE(elapsed, 9ms);
ASSERT_LE(elapsed, 50ms);
}
TYPED_TEST(ThreadSafeQueueTest, TryPopWithTimeout_ImmediateSuccess) {
TypeParam value = initialize_value<TypeParam>(101);
this->queue.push(value);
auto start = std::chrono::steady_clock::now();
std::optional<TypeParam> result = this->queue.try_pop(10s);
auto end = std::chrono::steady_clock::now();
ASSERT_TRUE(result.has_value());
ASSERT_EQ(result.value(), value);
ASSERT_LT(end - start, 5ms);
}
// =================================================================
// 4. Synchronization and Blocking Tests
// =================================================================
TEST_F(ThreadSafeQueueIntTest, BlockingPopUnblocksOnPush) {
const int expected_value = 123;
std::atomic<int> result = 0;
std::thread consumer([this, &result] {
// Blocking pop() call
result = this->queue.pop();
});
std::this_thread::sleep_for(100ms);
this->queue.push(expected_value);
consumer.join();
ASSERT_EQ(result.load(), expected_value);
}
TEST_F(ThreadSafeQueueIntTest, MultipleWaitersUnblockedSequentially) {
const int num_waiters = 5;
std::vector<std::thread> consumers;
std::atomic<int> pops_received = 0;
for (int i = 0; i < num_waiters; ++i) {
consumers.emplace_back([this, &pops_received] {
this->queue.pop();
pops_received++;
});
}
std::this_thread::sleep_for(100ms);
// Push exactly the number of waiters—only one waiter should be released per push
for (int i = 0; i < num_waiters; ++i) {
this->queue.push(i);
}
for (auto& t : consumers) {
t.join();
}
ASSERT_EQ(pops_received.load(), num_waiters);
}
// =================================================================
// 5. Stress and Race Condition Tests
// =================================================================
TEST_F(ThreadSafeQueueIntTest, ConcurrentPushConsistency) {
const int num_producers = 10;
const int items_per_producer = 1000;
const int total_items = num_producers * items_per_producer;
std::vector<std::thread> producers;
std::set<int> expected_values;
for (int i = 0; i < num_producers; ++i) {
producers.emplace_back([this, i, items_per_producer] {
int start_value = i * items_per_producer;
for (int j = 0; j < items_per_producer; ++j) {
this->queue.push(start_value + j);
}
});
int start_value = i * items_per_producer;
for (int j = 0; j < items_per_producer; ++j) {
expected_values.insert(start_value + j);
}
}
for (auto& t : producers) {
t.join();
}
// Drain the queue and check for consistency
std::set<int> retrieved_values;
for (int i = 0; i < total_items; ++i) {
auto val = this->queue.pop();
retrieved_values.insert(val);
}
ASSERT_EQ(retrieved_values.size(), total_items);
ASSERT_EQ(retrieved_values, expected_values);
}
TEST_F(ThreadSafeQueueIntTest, ConcurrentPopNoDuplicate) {
const int total_items = 10000;
const int num_consumers = 10;
// Producer pushes all items
for (int i = 0; i < total_items; ++i) {
this->queue.push(i);
}
// Consumers pop concurrently
std::vector<std::thread> consumers;
std::mutex result_mtx;
std::set<int> retrieved_values;
std::atomic<int> pop_count = 0;
for (int i = 0; i < num_consumers; ++i) {
consumers.emplace_back([this, &result_mtx, &retrieved_values, &pop_count, total_items] {
while (pop_count.load() < total_items) {
// Use try_pop so threads don't block indefinitely waiting for a push
// that won't come until the other threads finish.
if (auto val = this->queue.try_pop(); val.has_value()) {
std::lock_guard lock(result_mtx);
retrieved_values.insert(val.value());
pop_count++;
}
std::this_thread::yield();
}
});
}
for (auto& t : consumers) {
t.join();
}
// Check consistency
ASSERT_EQ(pop_count.load(), total_items) << "Total pops do not match total pushed items.";
ASSERT_EQ(retrieved_values.size(), total_items) << "Duplicate items were retrieved.";
}
// =================================================================
// 6. Move-Only Type Compatibility Test (Verifying the push/pop fix)
// =================================================================
// Test fixture for std::unique_ptr<int> (a move-only type)
class ThreadSafeQueueMoveOnlyTest : public ::testing::Test {
protected:
thread_safe_queue<std::unique_ptr<int>> queue;
};
TEST_F(ThreadSafeQueueMoveOnlyTest, HandlesConcurrentMoveOnlyTypes) {
const int total_items = 1000;
const int num_threads = 5;
std::vector<std::thread> threads;
std::atomic<int> pop_count = 0;
// Producer/Consumer set for unique ownership verification
std::set<int> retrieved_values;
std::mutex result_mtx;
// Start 5 threads: 3 producers, 2 consumers
for (int i = 0; i < num_threads; ++i) {
if (i < 3) { // Producers
threads.emplace_back([this, i, total_items] {
int start_value = i * total_items;
for (int j = 0; j < total_items; ++j) {
// Requires thread_safe_queue::push(T&&)
this->queue.push(std::make_unique<int>(start_value + j));
}
});
} else { // Consumers
threads.emplace_back([this, &pop_count, &result_mtx, &retrieved_values, total_items] {
int pops = 0;
while (pops < total_items * 3 / 2) { // Try to pop 1500 times
// Requires thread_safe_queue::try_pop()
if (auto opt_ptr = this->queue.try_pop(); opt_ptr.has_value()) {
std::lock_guard lock(result_mtx);
retrieved_values.insert(*opt_ptr.value());
pop_count++;
pops++;
}
std::this_thread::yield();
}
});
}
}
for (auto& t : threads) {
if (t.joinable()) {
t.join();
}
}
// Drain any remaining items in the main thread (should be few/none)
while (auto opt_ptr = this->queue.try_pop()) {
std::lock_guard lock(result_mtx);
retrieved_values.insert(*opt_ptr.value());
pop_count++;
}
const int total_expected = total_items * 3; // 3 producers * 1000 items
ASSERT_EQ(pop_count.load(), total_expected) << "Total items popped does not match total pushed.";
ASSERT_EQ(retrieved_values.size(), total_expected)
<< "Duplicate pointers/values were retrieved, indicating a race condition failure or a failed move.";
}