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>
189 lines
6.7 KiB
C++
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"
|