Lin_Simulator/cpp/tests/test_ldf_loading.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

189 lines
6.7 KiB
C++

/**
* test_ldf_loading.cpp — Tests for LDF loading GUI integration (C++).
* Tests QTreeWidget population, hex/dec toggle, schedule combo.
*/
#include <QtTest/QtTest>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QCheckBox>
#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
#include "main_window.h"
#ifndef LDF_SAMPLE_PATH
#error "LDF_SAMPLE_PATH must be defined by CMake"
#endif
class TestLdfLoading : public QObject
{
Q_OBJECT
private:
MainWindow* m_window;
QString m_samplePath;
private slots:
void init()
{
m_window = new MainWindow();
m_samplePath = QString(LDF_SAMPLE_PATH);
m_window->loadLdfFile(m_samplePath);
}
void cleanup() { delete m_window; m_window = nullptr; }
// ─── LDF Loading ──────────────────────────────────────────────
void test_ldfPathShown() { QVERIFY(m_window->ldfPathEdit()->text().contains("sample.ldf")); }
void test_baudRateUpdated() { QVERIFY(m_window->baudRateLabel()->text().contains("19200")); }
void test_ldfDataStored() { QVERIFY(m_window->ldfData() != nullptr); QCOMPARE(m_window->ldfData()->baudrate, 19200); }
// ─── Tx Tree Population ───────────────────────────────────────
void test_txFrameCount() { QCOMPARE(m_window->txTable()->topLevelItemCount(), 2); }
void test_txFrameNames()
{
QStringList names;
for (int i = 0; i < m_window->txTable()->topLevelItemCount(); ++i)
names << m_window->txTable()->topLevelItem(i)->text(0);
QVERIFY(names.contains("Motor_Command"));
QVERIFY(names.contains("Door_Command"));
}
void test_txFrameIds()
{
QStringList ids;
for (int i = 0; i < m_window->txTable()->topLevelItemCount(); ++i)
ids << m_window->txTable()->topLevelItem(i)->text(1);
QVERIFY(ids.contains("0x10"));
QVERIFY(ids.contains("0x11"));
}
void test_txFrameLengths()
{
for (int i = 0; i < m_window->txTable()->topLevelItemCount(); ++i)
QCOMPARE(m_window->txTable()->topLevelItem(i)->text(2), QString("2"));
}
void test_txValueColumnShowsBytes()
{
for (int i = 0; i < m_window->txTable()->topLevelItemCount(); ++i)
QCOMPARE(m_window->txTable()->topLevelItem(i)->text(4), QString("00 00"));
}
void test_txSignalsAsChildren()
{
auto* item = m_window->txTable()->topLevelItem(0);
QVERIFY(item->childCount() >= 2);
}
void test_txSignalNames()
{
auto* item = m_window->txTable()->topLevelItem(0);
QStringList names;
for (int j = 0; j < item->childCount(); ++j)
names << item->child(j)->text(0).trimmed();
QString all = names.join(" ");
QVERIFY(all.contains("Motor") || all.contains("Door"));
}
void test_txIntervalFromSchedule()
{
QStringList intervals;
for (int i = 0; i < m_window->txTable()->topLevelItemCount(); ++i)
intervals << m_window->txTable()->topLevelItem(i)->text(3);
QVERIFY(intervals.contains("10"));
}
// ─── Rx Tree Population ───────────────────────────────────────
void test_rxFrameCount() { QCOMPARE(m_window->rxTable()->topLevelItemCount(), 2); }
void test_rxFrameNames()
{
QStringList names;
for (int i = 0; i < m_window->rxTable()->topLevelItemCount(); ++i)
names << m_window->rxTable()->topLevelItem(i)->text(1);
QVERIFY(names.contains("Motor_Status"));
QVERIFY(names.contains("Door_Status"));
}
void test_rxFrameIds()
{
QStringList ids;
for (int i = 0; i < m_window->rxTable()->topLevelItemCount(); ++i)
ids << m_window->rxTable()->topLevelItem(i)->text(2);
QVERIFY(ids.contains("0x20"));
QVERIFY(ids.contains("0x21"));
}
void test_rxTimestampPlaceholder()
{
for (int i = 0; i < m_window->rxTable()->topLevelItemCount(); ++i)
QCOMPARE(m_window->rxTable()->topLevelItem(i)->text(0), QString::fromUtf8(""));
}
void test_rxSignalsAsChildren()
{
auto* item = m_window->rxTable()->topLevelItem(0);
QVERIFY(item->childCount() >= 1);
}
// ─── Schedule Combo ───────────────────────────────────────────
void test_scheduleCount() { QCOMPARE(m_window->scheduleCombo()->count(), 2); }
void test_scheduleNames()
{
QStringList items;
for (int i = 0; i < m_window->scheduleCombo()->count(); ++i)
items << m_window->scheduleCombo()->itemText(i);
QVERIFY(items.contains("NormalSchedule"));
QVERIFY(items.contains("FastSchedule"));
}
// ─── Hex/Dec Toggle ───────────────────────────────────────────
void test_hexModeDefault() { QVERIFY(m_window->hexModeCheck()->isChecked()); }
void test_signalValueHexFormat()
{
auto* item = m_window->txTable()->topLevelItem(0);
auto* sig = item->child(0);
QVERIFY(sig->text(4).startsWith("0x"));
}
void test_signalValueDecFormat()
{
m_window->hexModeCheck()->setChecked(false);
auto* item = m_window->txTable()->topLevelItem(0);
auto* sig = item->child(0);
QVERIFY(!sig->text(4).startsWith("0x"));
m_window->hexModeCheck()->setChecked(true);
}
void test_frameValueHexFormat()
{
auto* item = m_window->txTable()->topLevelItem(0);
QString val = item->text(4);
QStringList parts = val.split(' ');
for (const auto& p : parts)
QCOMPARE(p.length(), 2);
}
// ─── Error Handling ───────────────────────────────────────────
void test_reloadClearsPrevious()
{
QCOMPARE(m_window->txTable()->topLevelItemCount(), 2);
m_window->loadLdfFile(m_samplePath);
QCOMPARE(m_window->txTable()->topLevelItemCount(), 2);
}
// ─── Auto-reload ──────────────────────────────────────────────
void test_autoReloadCheckboxControlsReload()
{
m_window->autoReloadCheck()->setChecked(false);
m_window->onLdfFileChanged(m_samplePath);
QVERIFY(m_window->ldfData() != nullptr);
}
};
QTEST_MAIN(TestLdfLoading)
#include "test_ldf_loading.moc"