implemented Producer API and header, stub and tests. Builds cleanly, tests fail for now
This commit is contained in:
parent
a798caf1a8
commit
9c2117e64b
46
include/Producer.hpp
Normal file
46
include/Producer.hpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
// Producer.hpp
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
// Author: Unai Blazquez <unaibg2000@gmail.com>
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "SysfsRead.hpp"
|
||||||
|
|
||||||
|
using RandomFn = std::function<int()>;
|
||||||
|
using SleepFn = std::function<void(std::chrono::milliseconds)>;
|
||||||
|
class Producer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @brief Construct a Producer bound to a sysfs-like control file.
|
||||||
|
/// @param sysfs_path Path to the control file (e.g. "./fake_sysfs_input").
|
||||||
|
/// @param send_fn Function called whenever a new integer should be sent.
|
||||||
|
/// @param sleep_fn Stub for sleeping function, allows reducing sleep on
|
||||||
|
/// testing
|
||||||
|
Producer(const std::filesystem::path& sysfs_path,
|
||||||
|
std::function<void(int)> send_fn, RandomFn random_fn,
|
||||||
|
SleepFn sleep_fn = default_sleep);
|
||||||
|
|
||||||
|
/// @brief Start the worker thread. Safe to call only once.
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/// @brief Request the worker thread to stop and wait for it to finish.
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// @brief Main loop executed by the worker thread.
|
||||||
|
void run_loop();
|
||||||
|
|
||||||
|
std::thread m_thread;
|
||||||
|
std::atomic<bool> m_running;
|
||||||
|
SysfsReader m_reader;
|
||||||
|
RandomFn m_random;
|
||||||
|
SleepFn m_sleep;
|
||||||
|
static void default_sleep(std::chrono::milliseconds d)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(d);
|
||||||
|
}
|
||||||
|
std::function<void(int)> m_send;
|
||||||
|
};
|
||||||
55
src/core/Producer.cxx
Normal file
55
src/core/Producer.cxx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Producer.cxx
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
// Author: Unai Blazquez <unaibg2000@gmail.com>
|
||||||
|
|
||||||
|
#include "Producer.hpp"
|
||||||
|
|
||||||
|
#include "SysfsRead.hpp"
|
||||||
|
|
||||||
|
Producer::Producer(const std::filesystem::path& sysfs_path,
|
||||||
|
std::function<void(int)> send_fn, RandomFn random_fn
|
||||||
|
: m_reader(sysfs_path),
|
||||||
|
m_send(std::move(send_fn)),
|
||||||
|
m_random(std::move(random_fn)),
|
||||||
|
m_sleep(std::move(sleep_fn))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void Producer::start()
|
||||||
|
{
|
||||||
|
m_running.store(true);
|
||||||
|
m_thread = std::thread(&Producer::run_loop, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Producer::stop()
|
||||||
|
{
|
||||||
|
m_running.store(false);
|
||||||
|
if (m_thread.joinable())
|
||||||
|
{
|
||||||
|
m_thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Producer::run_loop()
|
||||||
|
{
|
||||||
|
auto status = m_reader.read_status();
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case SysfsStatus::Enabled:
|
||||||
|
m_send(m_random());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SysfsStatus::Unreachable:
|
||||||
|
// do nothing for now
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SysfsStatus::Empty:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SysfsStatus::ErrorTempTooHigh:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SysfsStatus::UnexpectedValue:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_sleep(delay);
|
||||||
|
// Thread will end here (for now) stop will join it
|
||||||
|
}
|
||||||
110
tests/test_producer.cxx
Normal file
110
tests/test_producer.cxx
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "Producer.hpp"
|
||||||
|
|
||||||
|
TEST(ProducerTest, ProducerCallsBackWhenEnabled)
|
||||||
|
{
|
||||||
|
// Arrange create the file and write "1\n" into it.
|
||||||
|
{
|
||||||
|
std::ofstream out("fake_sysfs_input");
|
||||||
|
out << "1\n";
|
||||||
|
}
|
||||||
|
// create a fake callback function
|
||||||
|
std::vector<int> outputs;
|
||||||
|
|
||||||
|
// construct a producer with fake file and callback
|
||||||
|
Producer producer{"fake_sysfs_input", [&outputs](int value)
|
||||||
|
{ outputs.push_back(value); }, []() { return 42; }};
|
||||||
|
// Act: initialize producer and stop it.
|
||||||
|
producer.start();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // ← 1ms window
|
||||||
|
producer.stop();
|
||||||
|
|
||||||
|
// Assert: we expect one output being 42
|
||||||
|
ASSERT_EQ(outputs.size(), 1u);
|
||||||
|
EXPECT_EQ(outputs[0], 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ProducerTest, ProducerDoesNotCallWhenUnexpectedValue)
|
||||||
|
{
|
||||||
|
// Arrange create the file and write "0\n" into it.
|
||||||
|
{
|
||||||
|
std::ofstream out("fake_sysfs_input");
|
||||||
|
out << "0\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> outputs;
|
||||||
|
|
||||||
|
Producer producer{"fake_sysfs_input",
|
||||||
|
[&outputs](int value) { outputs.push_back(value); },
|
||||||
|
[]() { return 42; }, [](std::chrono::milliseconds) {}};
|
||||||
|
|
||||||
|
producer.start();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // ← 1ms window
|
||||||
|
producer.stop();
|
||||||
|
|
||||||
|
// Assert: we expect no output
|
||||||
|
EXPECT_TRUE(outputs.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ProducerTest, ProducerDoesNotCallWhenEmpty)
|
||||||
|
{
|
||||||
|
// Arrange create the file and write "0\n" into it.
|
||||||
|
{
|
||||||
|
std::ofstream out("fake_sysfs_input");
|
||||||
|
out << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> outputs;
|
||||||
|
|
||||||
|
Producer producer{"fake_sysfs_input",
|
||||||
|
[&outputs](int value) { outputs.push_back(value); },
|
||||||
|
[]() { return 42; }, [](std::chrono::milliseconds) {}};
|
||||||
|
|
||||||
|
producer.start();
|
||||||
|
producer.stop();
|
||||||
|
|
||||||
|
// Assert: we expect no output
|
||||||
|
EXPECT_TRUE(outputs.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ProducerTest, ProducerDoesNotCallWhenUnreachable)
|
||||||
|
{
|
||||||
|
std::vector<int> outputs;
|
||||||
|
std::vector<std::string> logs;
|
||||||
|
|
||||||
|
Producer producer{"nonexistant_sysfs_input",
|
||||||
|
[&outputs](int value) { outputs.push_back(value); },
|
||||||
|
[]() { return 42; }, [](std::chrono::milliseconds) {}};
|
||||||
|
|
||||||
|
producer.start();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // ← 1ms window
|
||||||
|
producer.stop();
|
||||||
|
|
||||||
|
// Assert: we expect no output
|
||||||
|
EXPECT_TRUE(outputs.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ProducerTest, ProducerDoesNotCallWhenTempTooHigh)
|
||||||
|
{
|
||||||
|
// create a file that contains error
|
||||||
|
{
|
||||||
|
std::ofstream out("fake_sysfs_input");
|
||||||
|
out << "error: temp too high";
|
||||||
|
}
|
||||||
|
std::vector<int> outputs;
|
||||||
|
|
||||||
|
Producer producer{"fake_sysfs_input",
|
||||||
|
[&outputs](int value) { outputs.push_back(value); },
|
||||||
|
[]() { return 42; }, [](std::chrono::milliseconds) {}};
|
||||||
|
|
||||||
|
producer.start();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // ← 1ms window
|
||||||
|
producer.stop();
|
||||||
|
|
||||||
|
// Assert: we expect no output
|
||||||
|
EXPECT_TRUE(outputs.empty());
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user