/** * test_main_window.cpp — Tests for the GUI skeleton (Step 1, C++). * * QTest FRAMEWORK: * ================ * Qt provides its own test framework, QTest. It's simpler than GoogleTest * but integrates perfectly with Qt's event loop and widget system. * * Key differences from pytest: * pytest: assert window.windowTitle() == "..." * QTest: QCOMPARE(window.windowTitle(), QString("...")) * * Test discovery: * - Each test class inherits QObject and has Q_OBJECT * - Test methods are private slots named test_*() or *_test() * - QTEST_MAIN() generates main() that runs all test slots * * QCOMPARE vs QVERIFY: * QVERIFY(condition) — like assert condition * QCOMPARE(actual, expected) — like assert actual == expected, but prints both values on failure */ #include #include #include #include #include #include #include #include #include #include "main_window.h" class TestMainWindow : public QObject { Q_OBJECT private: MainWindow* m_window; private slots: // ── Setup/Teardown ── // init() runs BEFORE each test, cleanup() runs AFTER each test. // This gives each test a fresh MainWindow (like pytest fixtures). 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()); } // ─── Tx Table ───────────────────────────────────────────────── void test_txTableExists() { auto* table = m_window->txTable(); QVERIFY(table != nullptr); // qobject_cast is Qt's type-safe dynamic cast. // Returns nullptr if the object isn't the expected type. QVERIFY(qobject_cast(table) != nullptr); } void test_txTableColumns() { auto* table = m_window->txTable(); QCOMPARE(table->columnCount(), 7); QStringList expected = { "Frame Name", "Frame ID", "Length", "Interval (ms)", "Data", "Signals", "Action" }; for (int i = 0; i < table->columnCount(); ++i) { QCOMPARE(table->horizontalHeaderItem(i)->text(), expected[i]); } } void test_txTableAlternatingColors() { QVERIFY(m_window->txTable()->alternatingRowColors()); } // ─── Rx Table ───────────────────────────────────────────────── void test_rxTableExists() { auto* table = m_window->rxTable(); QVERIFY(table != nullptr); QVERIFY(qobject_cast(table) != nullptr); } void test_rxTableColumns() { auto* table = m_window->rxTable(); QCOMPARE(table->columnCount(), 5); QStringList expected = { "Timestamp", "Frame Name", "Frame ID", "Data", "Signals" }; for (int i = 0; i < table->columnCount(); ++i) { QCOMPARE(table->horizontalHeaderItem(i)->text(), expected[i]); } } void test_rxTableNotEditable() { QCOMPARE(m_window->rxTable()->editTriggers(), QAbstractItemView::NoEditTriggers); } // ─── Connection Dock ────────────────────────────────────────── void test_dockExists() { // findChildren() searches the widget tree for all children of type T auto docks = m_window->findChildren(); 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 generates a main() function that: // 1. Creates a QApplication // 2. Instantiates TestMainWindow // 3. Runs all private slots as test cases // 4. Reports results QTEST_MAIN(TestMainWindow) // This #include is required when the test class is defined in a .cpp file // (not a .h file). It includes the MOC-generated code for our Q_OBJECT class. // Without it, the linker would fail with "undefined reference to vtable". #include "test_main_window.moc"