#include #include #include #include #include #include #include #include "core.h" #include "generator.h" // Raw Scintilla message IDs not exposed by QsciScintillaBase wrapper static constexpr int SCI_GETSELBACK = 2477; static constexpr int SCI_GETSELFORE = 2476; // ── Helper: extract BGR long from QColor (Scintilla stores colors as 0x00BBGGRR) ── static long toBGR(const QColor& c) { return (long)c.red() | ((long)c.green() << 8) | ((long)c.blue() << 16); } // ── Replicates MainWindow::setupRenderedSci so the test stays in sync ── static void setupRenderedSci(QsciScintilla* sci) { QFont f("Consolas", 12); f.setFixedPitch(true); sci->setFont(f); sci->setReadOnly(false); sci->setWrapMode(QsciScintilla::WrapNone); sci->setTabWidth(4); sci->setIndentationsUseTabs(false); sci->SendScintilla(QsciScintillaBase::SCI_SETEXTRAASCENT, (long)2); sci->SendScintilla(QsciScintillaBase::SCI_SETEXTRADESCENT, (long)2); // Line number margin sci->setMarginType(0, QsciScintilla::NumberMargin); sci->setMarginWidth(0, "00000"); sci->setMarginsBackgroundColor(QColor("#252526")); sci->setMarginsForegroundColor(QColor("#858585")); sci->setMarginsFont(f); sci->setMarginWidth(1, 0); sci->setMarginWidth(2, 0); // Lexer FIRST — setLexer() resets caret/selection/paper colors auto* lexer = new QsciLexerCPP(sci); lexer->setFont(f); lexer->setColor(QColor("#569cd6"), QsciLexerCPP::Keyword); lexer->setColor(QColor("#569cd6"), QsciLexerCPP::KeywordSet2); lexer->setColor(QColor("#b5cea8"), QsciLexerCPP::Number); lexer->setColor(QColor("#ce9178"), QsciLexerCPP::DoubleQuotedString); lexer->setColor(QColor("#ce9178"), QsciLexerCPP::SingleQuotedString); lexer->setColor(QColor("#6a9955"), QsciLexerCPP::Comment); lexer->setColor(QColor("#6a9955"), QsciLexerCPP::CommentLine); lexer->setColor(QColor("#6a9955"), QsciLexerCPP::CommentDoc); lexer->setColor(QColor("#d4d4d4"), QsciLexerCPP::Default); lexer->setColor(QColor("#d4d4d4"), QsciLexerCPP::Identifier); lexer->setColor(QColor("#c586c0"), QsciLexerCPP::PreProcessor); lexer->setColor(QColor("#d4d4d4"), QsciLexerCPP::Operator); for (int i = 0; i <= 127; i++) { lexer->setPaper(QColor("#1e1e1e"), i); lexer->setFont(f, i); } sci->setLexer(lexer); sci->setBraceMatching(QsciScintilla::NoBraceMatch); // Colors AFTER setLexer() — the lexer resets these on attach sci->setPaper(QColor("#1e1e1e")); sci->setColor(QColor("#d4d4d4")); sci->setCaretForegroundColor(QColor("#d4d4d4")); sci->setCaretLineVisible(true); sci->setCaretLineBackgroundColor(QColor(43, 43, 43)); sci->setSelectionBackgroundColor(QColor("#264f78")); sci->setSelectionForegroundColor(QColor("#d4d4d4")); } // ── Test tree helper ── static rcx::NodeTree makeTestTree() { rcx::NodeTree tree; rcx::Node root; root.kind = rcx::NodeKind::Struct; root.name = "TestStruct"; root.structTypeName = "TestStruct"; root.parentId = 0; root.offset = 0; int ri = tree.addNode(root); uint64_t rootId = tree.nodes[ri].id; rcx::Node f1; f1.kind = rcx::NodeKind::Int32; f1.name = "health"; f1.parentId = rootId; f1.offset = 0; tree.addNode(f1); rcx::Node f2; f2.kind = rcx::NodeKind::Float; f2.name = "speed"; f2.parentId = rootId; f2.offset = 4; tree.addNode(f2); return tree; } // ── Test class ── class TestRenderedView : public QObject { Q_OBJECT private slots: // ── Verify caret line background is NOT yellow after setup ── void testCaretLineBackgroundNotYellow() { QsciScintilla sci; setupRenderedSci(&sci); sci.show(); sci.setText("struct Foo {\n int x;\n};\n"); QTest::qWait(50); long bgr = sci.SendScintilla(QsciScintillaBase::SCI_GETCARETLINEBACK); long expected = toBGR(QColor(43, 43, 43)); // Yellow would be 0x00FFFF or similar high-value — ours should be dark long yellow = toBGR(QColor(255, 255, 0)); QVERIFY2(bgr != yellow, qPrintable(QString("Caret line is yellow (0x%1), expected dark (0x%2)") .arg(bgr, 6, 16, QChar('0')) .arg(expected, 6, 16, QChar('0')))); QCOMPARE(bgr, expected); } // ── Verify caret line is enabled ── void testCaretLineEnabled() { QsciScintilla sci; setupRenderedSci(&sci); long visible = sci.SendScintilla(QsciScintillaBase::SCI_GETCARETLINEVISIBLE); QCOMPARE(visible, (long)1); } // ── Verify editor background (paper) is dark ── void testPaperColor() { QsciScintilla sci; setupRenderedSci(&sci); // Query default style background via Scintilla long bgr = sci.SendScintilla(QsciScintillaBase::SCI_STYLEGETBACK, (unsigned long)0 /*STYLE_DEFAULT*/); long expected = toBGR(QColor("#1e1e1e")); QCOMPARE(bgr, expected); } // ── Verify caret (cursor) foreground color ── void testCaretForegroundColor() { QsciScintilla sci; setupRenderedSci(&sci); long bgr = sci.SendScintilla(QsciScintillaBase::SCI_GETCARETFORE); long expected = toBGR(QColor("#d4d4d4")); QCOMPARE(bgr, expected); } // ── Verify selection colors are set (no direct Scintilla getter, but we can // verify they survive a round-trip through the SCI_SETSEL* messages by // checking the element colour API introduced in Scintilla 5.x) ── void testSelectionColorsApplied() { QsciScintilla sci; setupRenderedSci(&sci); sci.show(); sci.setText("int x = 42;\n"); QTest::qWait(50); // Select text and verify rendering doesn't crash sci.SendScintilla(QsciScintillaBase::SCI_SETSEL, (unsigned long)0, (long)3); QTest::qWait(50); // SCI_GETELEMENTCOLOUR (element 10 = SC_ELEMENT_SELECTION_BACK) returns // the selection back colour on Scintilla >= 5.2. If not available, fall // back to verifying the calls didn't throw and caret line is still correct. constexpr int SCI_GETELEMENTCOLOUR = 2753; constexpr int SC_ELEMENT_SELECTION_BACK = 10; long selBack = sci.SendScintilla(SCI_GETELEMENTCOLOUR, (unsigned long)SC_ELEMENT_SELECTION_BACK); if (selBack != 0) { // Scintilla 5.x: colour stored as 0xAABBGGRR (with alpha in high byte) long bgrMask = selBack & 0x00FFFFFF; long expected = toBGR(QColor("#264f78")); QCOMPARE(bgrMask, expected); } else { // Older Scintilla: just verify caret line is still correct as a proxy long caretBg = sci.SendScintilla(QsciScintillaBase::SCI_GETCARETLINEBACK); long expected = toBGR(QColor(43, 43, 43)); QCOMPARE(caretBg, expected); } } // ── Verify lexer keyword color is VS Code blue, not default ── void testKeywordColor() { QsciScintilla sci; setupRenderedSci(&sci); auto* lexer = qobject_cast(sci.lexer()); QVERIFY(lexer != nullptr); QColor kw = lexer->color(QsciLexerCPP::Keyword); QCOMPARE(kw, QColor("#569cd6")); } // ── Verify comment color is VS Code green ── void testCommentColor() { QsciScintilla sci; setupRenderedSci(&sci); auto* lexer = qobject_cast(sci.lexer()); QVERIFY(lexer != nullptr); QCOMPARE(lexer->color(QsciLexerCPP::Comment), QColor("#6a9955")); QCOMPARE(lexer->color(QsciLexerCPP::CommentLine), QColor("#6a9955")); } // ── Verify number color is VS Code light green ── void testNumberColor() { QsciScintilla sci; setupRenderedSci(&sci); auto* lexer = qobject_cast(sci.lexer()); QVERIFY(lexer != nullptr); QCOMPARE(lexer->color(QsciLexerCPP::Number), QColor("#b5cea8")); } // ── Verify string color is VS Code orange ── void testStringColor() { QsciScintilla sci; setupRenderedSci(&sci); auto* lexer = qobject_cast(sci.lexer()); QVERIFY(lexer != nullptr); QCOMPARE(lexer->color(QsciLexerCPP::DoubleQuotedString), QColor("#ce9178")); QCOMPARE(lexer->color(QsciLexerCPP::SingleQuotedString), QColor("#ce9178")); } // ── Verify preprocessor color is VS Code purple ── void testPreprocessorColor() { QsciScintilla sci; setupRenderedSci(&sci); auto* lexer = qobject_cast(sci.lexer()); QVERIFY(lexer != nullptr); QCOMPARE(lexer->color(QsciLexerCPP::PreProcessor), QColor("#c586c0")); } // ── Verify default/identifier text color ── void testDefaultTextColor() { QsciScintilla sci; setupRenderedSci(&sci); auto* lexer = qobject_cast(sci.lexer()); QVERIFY(lexer != nullptr); QCOMPARE(lexer->color(QsciLexerCPP::Default), QColor("#d4d4d4")); QCOMPARE(lexer->color(QsciLexerCPP::Identifier), QColor("#d4d4d4")); QCOMPARE(lexer->color(QsciLexerCPP::Operator), QColor("#d4d4d4")); } // ── Verify all 128 lexer styles have dark paper ── void testAllStylesHaveDarkPaper() { QsciScintilla sci; setupRenderedSci(&sci); auto* lexer = qobject_cast(sci.lexer()); QVERIFY(lexer != nullptr); QColor expected("#1e1e1e"); for (int i = 0; i <= 127; i++) { QColor paper = lexer->paper(i); QVERIFY2(paper == expected, qPrintable(QString("Style %1 paper is %2, expected %3") .arg(i).arg(paper.name()).arg(expected.name()))); } } // ── Verify margin colors match dark theme ── void testMarginColors() { QsciScintilla sci; setupRenderedSci(&sci); // Query margin background via Scintilla (style 33 = STYLE_LINENUMBER) long marginBg = sci.SendScintilla(QsciScintillaBase::SCI_STYLEGETBACK, (unsigned long)33); long expectedBg = toBGR(QColor("#252526")); QCOMPARE(marginBg, expectedBg); long marginFg = sci.SendScintilla(QsciScintillaBase::SCI_STYLEGETFORE, (unsigned long)33); long expectedFg = toBGR(QColor("#858585")); QCOMPARE(marginFg, expectedFg); } // ── End-to-end: generate C++ and load into rendered view ── void testGeneratedCodeInRenderedView() { auto tree = makeTestTree(); uint64_t rootId = tree.nodes[0].id; QString code = rcx::renderCpp(tree, rootId); // Verify generated code has no pragma pack / cstdint QVERIFY(!code.contains("#pragma pack")); QVERIFY(!code.contains("#include ")); QVERIFY(code.contains("#pragma once")); QVERIFY(code.contains("struct TestStruct")); // Load into rendered sci and verify colors survive QsciScintilla sci; setupRenderedSci(&sci); sci.show(); sci.setText(code); QTest::qWait(100); // Caret line must still be dark after text load long caretBg = sci.SendScintilla(QsciScintillaBase::SCI_GETCARETLINEBACK); long expected = toBGR(QColor(43, 43, 43)); QCOMPARE(caretBg, expected); // Paper must still be dark long paperBg = sci.SendScintilla(QsciScintillaBase::SCI_STYLEGETBACK, (unsigned long)0); QCOMPARE(paperBg, toBGR(QColor("#1e1e1e"))); } // ── Verify brace matching is disabled ── void testBraceMatchDisabled() { QsciScintilla sci; setupRenderedSci(&sci); QCOMPARE(sci.braceMatching(), QsciScintilla::NoBraceMatch); } }; QTEST_MAIN(TestRenderedView) #include "test_rendered_view.moc"