Compare commits
4 Commits
cc29845657
...
9cc912b0a3
| Author | SHA1 | Date | |
|---|---|---|---|
| 9cc912b0a3 | |||
| 32426b3028 | |||
| 040cf974f4 | |||
| 499584f856 |
@ -25,6 +25,15 @@ add_library(core
|
||||
target_include_directories(core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
target_link_libraries(core PUBLIC Qt5::Core)
|
||||
|
||||
# Main Application
|
||||
add_executable(app
|
||||
src/app/main.cxx
|
||||
src/app/MainWindow.cxx
|
||||
include/MainWindow.hpp
|
||||
)
|
||||
target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
target_link_libraries(app PRIVATE core Qt5::Widgets)
|
||||
|
||||
#tests
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
|
||||
32
include/MainWindow.hpp
Normal file
32
include/MainWindow.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
// MainWindow.hpp
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
// Author: Unai Blazquez <unaibg2000@gmail.com>
|
||||
|
||||
#include <QLabel>
|
||||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
/// @brief Minimal GUI window that displays the last integer received
|
||||
/// from the ConsumerThread. Never blocks — values arrive via
|
||||
/// Qt's queued signal/slot mechanism.
|
||||
class MainWindow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget* parent = nullptr);
|
||||
|
||||
/// @brief Returns the current text shown in the value label (for testing).
|
||||
QString lastDisplayedText() const;
|
||||
|
||||
public slots:
|
||||
/// @brief Slot connected to ConsumerThread::valueReceived.
|
||||
/// Updates the label with the new value.
|
||||
void onValueReceived(int value);
|
||||
|
||||
private:
|
||||
QLabel* m_title_label;
|
||||
QLabel* m_value_label;
|
||||
};
|
||||
33
src/app/MainWindow.cxx
Normal file
33
src/app/MainWindow.cxx
Normal file
@ -0,0 +1,33 @@
|
||||
// MainWindow.cxx
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Author: Unai Blazquez <unaibg2000@gmail.com>
|
||||
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
MainWindow::MainWindow(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
setWindowTitle("Azkoyen IPC Monitor");
|
||||
setMinimumSize(320, 120);
|
||||
|
||||
auto* layout = new QVBoxLayout(this);
|
||||
|
||||
m_title_label = new QLabel("Last received value:", this);
|
||||
m_value_label = new QLabel("(waiting...)", this);
|
||||
|
||||
// Make the value label stand out a bit
|
||||
QFont font = m_value_label->font();
|
||||
font.setPointSize(24);
|
||||
font.setBold(true);
|
||||
m_value_label->setFont(font);
|
||||
m_value_label->setAlignment(Qt::AlignCenter);
|
||||
|
||||
layout->addWidget(m_title_label);
|
||||
layout->addWidget(m_value_label);
|
||||
}
|
||||
|
||||
QString MainWindow::lastDisplayedText() const { return m_value_label->text(); }
|
||||
|
||||
void MainWindow::onValueReceived(int value)
|
||||
{
|
||||
m_value_label->setText(QString::number(value));
|
||||
}
|
||||
58
src/app/main.cxx
Normal file
58
src/app/main.cxx
Normal file
@ -0,0 +1,58 @@
|
||||
// main.cxx
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Author: Unai Blazquez <unaibg2000@gmail.com>
|
||||
|
||||
#include <QApplication>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "Consumer.hpp"
|
||||
#include "MainWindow.hpp"
|
||||
#include "Producer.hpp"
|
||||
#include "UnixIpcBridge.hpp"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
const std::string socket_path = "/tmp/azkoyen.sock";
|
||||
const std::string sysfs_path = "./fake_sysfs_input";
|
||||
|
||||
// 1. Consumer — listens on the socket, emits Qt signal on receive
|
||||
ConsumerThread consumer(socket_path);
|
||||
|
||||
// 2. GUI — minimal window that displays received values
|
||||
MainWindow window;
|
||||
window.show();
|
||||
|
||||
// Connect consumer signal → window slot (auto-queued across threads,
|
||||
// so the GUI never blocks even if the producer is stuck in cool-down)
|
||||
QObject::connect(&consumer, &ConsumerThread::valueReceived, &window,
|
||||
&MainWindow::onValueReceived);
|
||||
|
||||
consumer.start();
|
||||
|
||||
// 3. Bridge — sends ints over the UNIX domain socket
|
||||
UnixIpcBridge bridge(socket_path);
|
||||
|
||||
// 4. Producer — reads sysfs, generates random int, sends via bridge.
|
||||
// Logs to a file instead of console (console is for the consumer).
|
||||
std::ofstream log_file("producer.log", std::ios::app);
|
||||
|
||||
Producer producer(
|
||||
sysfs_path, [&bridge](int value) { bridge.send(value); },
|
||||
[]() { return std::rand() % 1000; },
|
||||
[&log_file](const std::string& msg) { log_file << msg << std::endl; });
|
||||
|
||||
producer.start();
|
||||
|
||||
// 5. Run the Qt event loop (GUI stays responsive, signals are delivered)
|
||||
int result = app.exec();
|
||||
|
||||
// 6. Graceful shutdown
|
||||
producer.stop();
|
||||
consumer.stop();
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -53,3 +53,23 @@ target_link_libraries(test_consumer
|
||||
)
|
||||
|
||||
add_test(NAME test_consumer COMMAND test_consumer)
|
||||
|
||||
add_executable(test_main_window
|
||||
test_main_window.cxx
|
||||
${CMAKE_SOURCE_DIR}/src/app/MainWindow.cxx
|
||||
${CMAKE_SOURCE_DIR}/include/MainWindow.hpp
|
||||
)
|
||||
|
||||
target_include_directories(test_main_window PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
||||
|
||||
target_link_libraries(test_main_window
|
||||
PRIVATE
|
||||
core
|
||||
gtest
|
||||
gtest_main
|
||||
Qt5::Core
|
||||
Qt5::Widgets
|
||||
Qt5::Test
|
||||
)
|
||||
|
||||
add_test(NAME test_main_window COMMAND test_main_window)
|
||||
|
||||
47
tests/test_main_window.cxx
Normal file
47
tests/test_main_window.cxx
Normal file
@ -0,0 +1,47 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QLabel>
|
||||
#include <QSignalSpy>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
// QWidget-based tests need a full QApplication (not QCoreApplication).
|
||||
// Use offscreen platform so tests run headless in containers.
|
||||
static int argc_ = 0;
|
||||
static char* argv_[] = {nullptr};
|
||||
static struct SetupOffscreen
|
||||
{
|
||||
SetupOffscreen() { qputenv("QT_QPA_PLATFORM", "offscreen"); }
|
||||
} setup_offscreen_;
|
||||
static QApplication app_(argc_, argv_);
|
||||
|
||||
TEST(MainWindowTest, LabelUpdatesOnValueReceived)
|
||||
{
|
||||
MainWindow window;
|
||||
// Simulate receiving a value from ConsumerThread
|
||||
window.onValueReceived(42);
|
||||
|
||||
// The label should display the received value
|
||||
EXPECT_NE(window.lastDisplayedText().toStdString().find("42"),
|
||||
std::string::npos);
|
||||
}
|
||||
|
||||
TEST(MainWindowTest, LabelUpdatesMultipleTimes)
|
||||
{
|
||||
MainWindow window;
|
||||
window.onValueReceived(10);
|
||||
window.onValueReceived(20);
|
||||
window.onValueReceived(30);
|
||||
|
||||
// Label should show the most recent value
|
||||
EXPECT_NE(window.lastDisplayedText().toStdString().find("30"),
|
||||
std::string::npos);
|
||||
}
|
||||
|
||||
TEST(MainWindowTest, WindowTitleIsSet)
|
||||
{
|
||||
MainWindow window;
|
||||
EXPECT_FALSE(window.windowTitle().isEmpty());
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user