Lin_Simulator/cpp/tests/test_rx_realtime.cpp
Mohamed Salem cb60c2ad5d Steps 2-7: LDF loading, signal editing, Rx display, connection, BabyLIN backend, scheduler
Step 2 - LDF Loading:
- ldfparser integration (Python) / custom regex parser (C++)
- QTreeWidget with expandable signal rows, merged Value column
- Hex/Dec toggle, FreeFormat schedule entries, auto-reload
- Baud rate auto-detection from LDF

Step 3 - Signal Editing:
- Bit packing/unpacking (signal value ↔ frame bytes)
- ReadOnlyColumnDelegate for per-column editability
- Value clamping to signal width, recursion guard

Step 4 - Rx Panel:
- receive_rx_frame() API with timestamp, signal unpacking
- Change highlighting (yellow), auto-scroll toggle, clear button
- Dashboard view (in-place update per frame_id)

Step 5 - Connection Panel:
- ConnectionManager with state machine (Disconnected/Connecting/Connected/Error)
- Port scanning (pyserial / QSerialPort), connect/disconnect with UI mapping

Step 6 - BabyLIN Backend:
- BabyLinBackend wrapping Lipowsky BabyLIN_library.py DLL
- Mock mode for macOS/CI, device scan, SDF loading, signal access
- Frame callbacks, raw command access

Step 7 - Master Scheduler:
- QTimer-based schedule execution with start/stop/pause
- Frame sent callback with visual highlighting
- Mock Rx simulation, manual send, global rate override

Tests: Python 171 | C++ 124 (Steps 1-5 parity, Steps 6-7 Python-first)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 14:21:24 +02:00

175 lines
5.9 KiB
C++

/**
* test_rx_realtime.cpp — Tests for Step 4: Rx panel real-time display.
*/
#include <QtTest/QtTest>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QCheckBox>
#include <QPushButton>
#include "main_window.h"
#ifndef LDF_SAMPLE_PATH
#error "LDF_SAMPLE_PATH must be defined by CMake"
#endif
class TestRxRealtime : public QObject
{
Q_OBJECT
private:
MainWindow* m_window;
private slots:
void init()
{
m_window = new MainWindow();
m_window->loadLdfFile(QString(LDF_SAMPLE_PATH));
}
void cleanup() { delete m_window; m_window = nullptr; }
// ─── Frame Reception ──────────────────────────────────────────
void test_timestampUpdates()
{
m_window->receiveRxFrame(0x20, {0x03, 0xC8});
auto* item = m_window->rxTable()->topLevelItem(0);
QString ts = item->text(0);
QVERIFY(ts != QString::fromUtf8(""));
QVERIFY(ts.contains(":"));
}
void test_frameBytesStored()
{
m_window->receiveRxFrame(0x20, {0x03, 0xC8});
auto* item = m_window->rxTable()->topLevelItem(0);
QVariantList bytes = item->data(4, Qt::UserRole).toList();
QCOMPARE(bytes[0].toInt(), 0x03);
QCOMPARE(bytes[1].toInt(), 0xC8);
}
void test_frameValueDisplayed()
{
m_window->receiveRxFrame(0x20, {0x03, 0xC8});
auto* item = m_window->rxTable()->topLevelItem(0);
QVERIFY(item->text(4).contains("C8"));
}
void test_signalValuesUnpacked()
{
// Motor_Status: MotorStatus (bit 0, w2), MotorTemp (bit 8, w8)
m_window->receiveRxFrame(0x20, {0x03, 0xC8});
auto* item = m_window->rxTable()->topLevelItem(0);
QCOMPARE(item->child(0)->data(4, Qt::UserRole).toInt(), 3); // MotorStatus
QCOMPARE(item->child(1)->data(4, Qt::UserRole).toInt(), 200); // MotorTemp
}
void test_unknownFrameIdIgnored()
{
m_window->receiveRxFrame(0xFF, {0x00});
// No crash
}
void test_updateSameFrameTwice()
{
m_window->receiveRxFrame(0x20, {0x01, 0x10});
m_window->receiveRxFrame(0x20, {0x02, 0x20});
auto* item = m_window->rxTable()->topLevelItem(0);
QVariantList bytes = item->data(4, Qt::UserRole).toList();
QCOMPARE(bytes[0].toInt(), 0x02);
QCOMPARE(bytes[1].toInt(), 0x20);
}
// ─── Change Highlighting ──────────────────────────────────────
void test_changedSignalHighlighted()
{
m_window->receiveRxFrame(0x20, {0x01, 0x10});
m_window->receiveRxFrame(0x20, {0x01, 0x20});
auto* tempSig = m_window->rxTable()->topLevelItem(0)->child(1);
QColor bg = tempSig->background(4).color();
QCOMPARE(bg.red(), 255);
QCOMPARE(bg.green(), 255);
}
void test_unchangedSignalNotHighlighted()
{
m_window->receiveRxFrame(0x20, {0x01, 0x10});
m_window->receiveRxFrame(0x20, {0x01, 0x20});
auto* statusSig = m_window->rxTable()->topLevelItem(0)->child(0);
QCOMPARE(statusSig->background(4).style(), Qt::NoBrush);
}
void test_firstReceptionNoHighlight()
{
m_window->receiveRxFrame(0x20, {0x01, 0x10});
auto* item = m_window->rxTable()->topLevelItem(0);
for (int j = 0; j < item->childCount(); ++j)
QCOMPARE(item->child(j)->background(4).style(), Qt::NoBrush);
}
// ─── Auto-scroll ─────────────────────────────────────────────
void test_autoScrollDefaultOn()
{
QVERIFY(m_window->autoScrollCheck()->isChecked());
}
void test_autoScrollCanBeDisabled()
{
m_window->autoScrollCheck()->setChecked(false);
QVERIFY(!m_window->autoScrollCheck()->isChecked());
}
// ─── Clear ───────────────────────────────────────────────────
void test_clearResetsTimestamps()
{
m_window->receiveRxFrame(0x20, {0x01, 0x10});
m_window->onClearRx();
for (int i = 0; i < m_window->rxTable()->topLevelItemCount(); ++i)
QCOMPARE(m_window->rxTable()->topLevelItem(i)->text(0), QString::fromUtf8(""));
}
void test_clearResetsValues()
{
m_window->receiveRxFrame(0x20, {0x01, 0x10});
m_window->onClearRx();
for (int i = 0; i < m_window->rxTable()->topLevelItemCount(); ++i)
QCOMPARE(m_window->rxTable()->topLevelItem(i)->text(4), QString::fromUtf8(""));
}
void test_clearResetsHighlights()
{
m_window->receiveRxFrame(0x20, {0x01, 0x10});
m_window->receiveRxFrame(0x20, {0x02, 0x20});
m_window->onClearRx();
auto* item = m_window->rxTable()->topLevelItem(0);
for (int j = 0; j < item->childCount(); ++j)
QCOMPARE(item->child(j)->background(4).style(), Qt::NoBrush);
}
// ─── Hex/Dec ─────────────────────────────────────────────────
void test_rxHexMode()
{
m_window->receiveRxFrame(0x20, {0x03, 0xC8});
auto* item = m_window->rxTable()->topLevelItem(0);
QVERIFY(item->text(4).contains("C8"));
QVERIFY(item->child(1)->text(4).startsWith("0x"));
}
void test_rxDecMode()
{
m_window->hexModeCheck()->setChecked(false);
m_window->receiveRxFrame(0x20, {0x03, 0xC8});
auto* item = m_window->rxTable()->topLevelItem(0);
QVERIFY(item->text(4).contains("200"));
QVERIFY(!item->child(1)->text(4).startsWith("0x"));
m_window->hexModeCheck()->setChecked(true);
}
};
QTEST_MAIN(TestRxRealtime)
#include "test_rx_realtime.moc"