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

93 lines
6.0 KiB
C++

/**
* test_main_window.cpp — Tests for the GUI skeleton + LDF loading (C++).
* Uses QTreeWidget instead of QTableWidget.
*/
#include <QtTest/QtTest>
#include <QDockWidget>
#include <QTreeWidget>
#include <QSpinBox>
#include <QLineEdit>
#include <QPushButton>
#include <QCheckBox>
#include <QComboBox>
#include <QLabel>
#include "main_window.h"
class TestMainWindow : public QObject
{
Q_OBJECT
private:
MainWindow* m_window;
private slots:
void init() { m_window = new MainWindow(); }
void cleanup() { delete m_window; m_window = nullptr; }
// ─── Window Basics ────────────────────────────────────────────
void test_windowTitle() { QCOMPARE(m_window->windowTitle(), QString("LIN Simulator")); }
void test_minimumSize() { QVERIFY(m_window->minimumWidth() >= 1024); QVERIFY(m_window->minimumHeight() >= 768); }
void test_centralWidgetExists() { QVERIFY(m_window->centralWidget() != nullptr); }
// ─── Menu Bar ─────────────────────────────────────────────────
void test_menuBarExists() { QVERIFY(m_window->menuBar() != nullptr); }
void test_loadLdfActionExists() { QVERIFY(m_window->loadLdfAction() != nullptr); QCOMPARE(m_window->loadLdfAction()->text(), QString("&Load LDF...")); }
void test_loadLdfShortcut() { QCOMPARE(m_window->loadLdfAction()->shortcut().toString(), QString("Ctrl+O")); }
// ─── LDF Toolbar ──────────────────────────────────────────────
void test_ldfPathFieldExists() { QVERIFY(m_window->ldfPathEdit() != nullptr); QVERIFY(m_window->ldfPathEdit()->isReadOnly()); }
void test_ldfPathPlaceholder() { QCOMPARE(m_window->ldfPathEdit()->placeholderText(), QString("No LDF file loaded")); }
void test_browseButtonExists() { QVERIFY(m_window->browseButton() != nullptr); }
void test_autoReloadDefaultChecked() { QVERIFY(m_window->autoReloadCheck()->isChecked()); }
void test_hexModeDefaultChecked() { QVERIFY(m_window->hexModeCheck()->isChecked()); }
// ─── Tx Tree ──────────────────────────────────────────────────
void test_txTableExists() { QVERIFY(qobject_cast<QTreeWidget*>(m_window->txTable()) != nullptr); }
void test_txTableColumns()
{
QCOMPARE(m_window->txTable()->columnCount(), 6);
QStringList expected = {"Name", "ID / Bit", "Length / Width", "Interval (ms)", "Value", "Action"};
for (int i = 0; i < m_window->txTable()->columnCount(); ++i)
QCOMPARE(m_window->txTable()->headerItem()->text(i), expected[i]);
}
void test_txTableAlternatingColors() { QVERIFY(m_window->txTable()->alternatingRowColors()); }
void test_txTableIsDecorated() { QVERIFY(m_window->txTable()->rootIsDecorated()); }
// ─── Rx Tree ──────────────────────────────────────────────────
void test_rxTableExists() { QVERIFY(qobject_cast<QTreeWidget*>(m_window->rxTable()) != nullptr); }
void test_rxTableColumns()
{
QCOMPARE(m_window->rxTable()->columnCount(), 5);
QStringList expected = {"Timestamp", "Name", "ID / Bit", "Length / Width", "Value"};
for (int i = 0; i < m_window->rxTable()->columnCount(); ++i)
QCOMPARE(m_window->rxTable()->headerItem()->text(i), expected[i]);
}
void test_rxTableIsDecorated() { QVERIFY(m_window->rxTable()->rootIsDecorated()); }
// ─── Connection Dock ──────────────────────────────────────────
void test_dockExists() { auto docks = m_window->findChildren<QDockWidget*>(); QCOMPARE(docks.size(), 1); QCOMPARE(docks[0]->windowTitle(), QString("Connection")); }
void test_deviceComboExists() { QVERIFY(m_window->deviceCombo() != nullptr); }
void test_connectButtonExists() { QVERIFY(m_window->connectButton() != nullptr); QVERIFY(m_window->connectButton()->isEnabled()); }
void test_disconnectButtonDisabledInitially() { QVERIFY(!m_window->disconnectButton()->isEnabled()); }
void test_statusLabelShowsDisconnected() { QVERIFY(m_window->connStatusLabel()->text().contains("Disconnected")); }
void test_baudRateLabelExists() { QVERIFY(m_window->baudRateLabel() != nullptr); }
void test_baudRateShowsPlaceholderBeforeLdf() { QVERIFY(m_window->baudRateLabel()->text().contains("load LDF")); }
// ─── Control Bar ──────────────────────────────────────────────
void test_scheduleComboExists() { QVERIFY(m_window->scheduleCombo() != nullptr); }
void test_schedulerButtonsDisabledInitially() { QVERIFY(!m_window->startButton()->isEnabled()); QVERIFY(!m_window->stopButton()->isEnabled()); QVERIFY(!m_window->pauseButton()->isEnabled()); }
void test_manualSendDisabledInitially() { QVERIFY(!m_window->manualSendButton()->isEnabled()); }
void test_globalRateSpinboxExists() { QVERIFY(m_window->globalRateSpin() != nullptr); }
void test_globalRateDefault50ms() { QCOMPARE(m_window->globalRateSpin()->value(), 50); }
void test_globalRateRange() { QCOMPARE(m_window->globalRateSpin()->minimum(), 1); QCOMPARE(m_window->globalRateSpin()->maximum(), 10000); }
void test_globalRateSuffix() { QCOMPARE(m_window->globalRateSpin()->suffix(), QString(" ms")); }
// ─── Status Bar ───────────────────────────────────────────────
void test_statusBarExists() { QVERIFY(m_window->statusBar() != nullptr); }
void test_connectionStatusLabel() { QVERIFY(m_window->statusConnectionLabel()->text().contains("Disconnected")); }
};
QTEST_MAIN(TestMainWindow)
#include "test_main_window.moc"