mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
Add type selector popup, view root switching, and new type creation
- Type selector chevron [▸] on command row opens searchable popup - Popup lists all root structs with filter, keyboard nav, side-triangle indicator - Selecting a type switches the editor view via setViewRootId - "Create new type" inserts a new root struct with no name - Command row displays the active view root's name - Tests for chevron detection, span compatibility, view switching, undo
This commit is contained in:
@@ -54,18 +54,16 @@ private slots:
|
||||
QString result = rcx::renderCpp(tree, rootId);
|
||||
|
||||
// Header
|
||||
QVERIFY(result.contains("Generated by ReclassX"));
|
||||
QVERIFY(result.contains("#pragma once"));
|
||||
QVERIFY(result.contains("#include <cstdint>"));
|
||||
QVERIFY(!result.contains("#include <cstdint>"));
|
||||
QVERIFY(!result.contains("#pragma pack"));
|
||||
|
||||
// Struct definition
|
||||
QVERIFY(result.contains("#pragma pack(push, 1)"));
|
||||
QVERIFY(result.contains("struct Player {"));
|
||||
QVERIFY(result.contains("int32_t health;"));
|
||||
QVERIFY(result.contains("float speed;"));
|
||||
QVERIFY(result.contains("uint64_t id;"));
|
||||
QVERIFY(result.contains("};"));
|
||||
QVERIFY(result.contains("#pragma pack(pop)"));
|
||||
|
||||
// static_assert - struct is 16 bytes (0+4 + 4+4 + 8+8 = 16)
|
||||
QVERIFY(result.contains("static_assert(sizeof(Player) == 0x10"));
|
||||
@@ -485,7 +483,6 @@ private slots:
|
||||
|
||||
QString result = rcx::renderCppAll(tree);
|
||||
|
||||
QVERIFY(result.contains("Full SDK export"));
|
||||
QVERIFY(result.contains("struct StructA {"));
|
||||
QVERIFY(result.contains("struct StructB {"));
|
||||
QVERIFY(result.contains("uint32_t valueA;"));
|
||||
|
||||
361
tests/test_rendered_view.cpp
Normal file
361
tests/test_rendered_view.cpp
Normal file
@@ -0,0 +1,361 @@
|
||||
#include <QtTest/QTest>
|
||||
#include <QApplication>
|
||||
#include <Qsci/qsciscintilla.h>
|
||||
#include <Qsci/qsciscintillabase.h>
|
||||
#include <Qsci/qscilexercpp.h>
|
||||
#include <QColor>
|
||||
#include <QFont>
|
||||
|
||||
#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<QsciLexerCPP*>(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<QsciLexerCPP*>(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<QsciLexerCPP*>(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<QsciLexerCPP*>(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<QsciLexerCPP*>(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<QsciLexerCPP*>(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<QsciLexerCPP*>(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 <cstdint>"));
|
||||
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"
|
||||
223
tests/test_type_selector.cpp
Normal file
223
tests/test_type_selector.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
#include <QtTest/QTest>
|
||||
#include <QtTest/QSignalSpy>
|
||||
#include <QApplication>
|
||||
#include <QSplitter>
|
||||
#include <Qsci/qsciscintilla.h>
|
||||
#include "controller.h"
|
||||
#include "typeselectorpopup.h"
|
||||
#include "core.h"
|
||||
|
||||
using namespace rcx;
|
||||
|
||||
static void buildTwoRootTree(NodeTree& tree) {
|
||||
tree.baseAddress = 0x1000;
|
||||
|
||||
Node a;
|
||||
a.kind = NodeKind::Struct;
|
||||
a.name = "Alpha";
|
||||
a.structTypeName = "Alpha";
|
||||
a.parentId = 0;
|
||||
a.offset = 0;
|
||||
int ai = tree.addNode(a);
|
||||
uint64_t aId = tree.nodes[ai].id;
|
||||
|
||||
{ Node n; n.kind = NodeKind::Int32; n.name = "x"; n.parentId = aId; n.offset = 0; tree.addNode(n); }
|
||||
{ Node n; n.kind = NodeKind::Int32; n.name = "y"; n.parentId = aId; n.offset = 4; tree.addNode(n); }
|
||||
|
||||
Node b;
|
||||
b.kind = NodeKind::Struct;
|
||||
b.name = "Bravo";
|
||||
b.structTypeName = "Bravo";
|
||||
b.parentId = 0;
|
||||
b.offset = 0x100;
|
||||
int bi = tree.addNode(b);
|
||||
uint64_t bId = tree.nodes[bi].id;
|
||||
|
||||
{ Node n; n.kind = NodeKind::Float; n.name = "speed"; n.parentId = bId; n.offset = 0; tree.addNode(n); }
|
||||
}
|
||||
|
||||
static QByteArray makeBuffer() {
|
||||
return QByteArray(0x200, '\0');
|
||||
}
|
||||
|
||||
class TestTypeSelector : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
|
||||
// ── Chevron span detection ──
|
||||
|
||||
void testChevronSpanDetected() {
|
||||
QString text = QStringLiteral("[\u25B8] source\u25BE \u00B7 0x1000 \u00B7 struct\u25BE Alpha {");
|
||||
ColumnSpan span = commandRowChevronSpan(text);
|
||||
QVERIFY(span.valid);
|
||||
QCOMPARE(span.start, 0);
|
||||
QCOMPARE(span.end, 3);
|
||||
}
|
||||
|
||||
void testChevronSpanRejects() {
|
||||
QVERIFY(!commandRowChevronSpan(QStringLiteral("Hi")).valid);
|
||||
QVERIFY(!commandRowChevronSpan(QStringLiteral("\u25B8 source")).valid);
|
||||
// Old down-triangle glyph must not match
|
||||
QVERIFY(!commandRowChevronSpan(QStringLiteral("[\u25BE] source")).valid);
|
||||
}
|
||||
|
||||
// ── Existing spans unbroken by chevron prefix ──
|
||||
|
||||
void testSpansWithPrefix() {
|
||||
QString text = QStringLiteral("[\u25B8] source\u25BE \u00B7 0x1000 \u00B7 struct\u25BE Alpha {");
|
||||
|
||||
ColumnSpan src = commandRowSrcSpan(text);
|
||||
QVERIFY(src.valid);
|
||||
QVERIFY(text.mid(src.start, src.end - src.start).contains("source"));
|
||||
|
||||
ColumnSpan addr = commandRowAddrSpan(text);
|
||||
QVERIFY(addr.valid);
|
||||
QVERIFY(text.mid(addr.start, addr.end - addr.start).contains("0x1000"));
|
||||
|
||||
ColumnSpan rootName = commandRowRootNameSpan(text);
|
||||
QVERIFY(rootName.valid);
|
||||
QCOMPARE(text.mid(rootName.start, rootName.end - rootName.start).trimmed(), QString("Alpha"));
|
||||
}
|
||||
|
||||
// ── Popup data model ──
|
||||
|
||||
void testPopupListsRootStructs() {
|
||||
NodeTree tree;
|
||||
buildTwoRootTree(tree);
|
||||
|
||||
QVector<TypeEntry> types;
|
||||
for (const auto& n : tree.nodes) {
|
||||
if (n.parentId == 0 && n.kind == NodeKind::Struct) {
|
||||
types.append({n.id, n.structTypeName.isEmpty() ? n.name : n.structTypeName,
|
||||
n.resolvedClassKeyword()});
|
||||
}
|
||||
}
|
||||
|
||||
QCOMPARE(types.size(), 2);
|
||||
QCOMPARE(types[0].displayName, QString("Alpha"));
|
||||
QCOMPARE(types[1].displayName, QString("Bravo"));
|
||||
}
|
||||
|
||||
// ── Popup signals ──
|
||||
|
||||
void testPopupSignals() {
|
||||
TypeSelectorPopup popup;
|
||||
popup.setTypes({{1, "A", "struct"}, {2, "B", "struct"}}, 1);
|
||||
|
||||
QSignalSpy typeSpy(&popup, &TypeSelectorPopup::typeSelected);
|
||||
QSignalSpy createSpy(&popup, &TypeSelectorPopup::createNewTypeRequested);
|
||||
|
||||
emit popup.typeSelected(2);
|
||||
QCOMPARE(typeSpy.count(), 1);
|
||||
QCOMPARE(typeSpy.at(0).at(0).toULongLong(), (uint64_t)2);
|
||||
|
||||
emit popup.createNewTypeRequested();
|
||||
QCOMPARE(createSpy.count(), 1);
|
||||
}
|
||||
|
||||
// ── Full GUI integration ──
|
||||
// Single test method to avoid QScintilla reinit issues.
|
||||
|
||||
void testViewSwitchingAndCreateType() {
|
||||
auto* doc = new RcxDocument();
|
||||
buildTwoRootTree(doc->tree);
|
||||
doc->provider = std::make_unique<BufferProvider>(makeBuffer());
|
||||
|
||||
auto* splitter = new QSplitter();
|
||||
auto* ctrl = new RcxController(doc, nullptr);
|
||||
auto* editor = ctrl->addSplitEditor(splitter);
|
||||
|
||||
splitter->resize(800, 600);
|
||||
splitter->show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(splitter));
|
||||
|
||||
// Initial refresh so compose populates meta + editor text
|
||||
ctrl->refresh();
|
||||
QApplication::processEvents();
|
||||
|
||||
auto* sci = editor->scintilla();
|
||||
|
||||
// -- Command row starts with [U+25B8] --
|
||||
{
|
||||
const LineMeta* meta = editor->metaForLine(0);
|
||||
QVERIFY(meta);
|
||||
QCOMPARE(meta->lineKind, LineKind::CommandRow);
|
||||
|
||||
QString line0 = sci->text(0);
|
||||
if (line0.endsWith('\n')) line0.chop(1);
|
||||
QVERIFY2(line0.startsWith(QStringLiteral("[\u25B8]")),
|
||||
qPrintable("Expected chevron prefix, got: " + line0.left(10)));
|
||||
}
|
||||
|
||||
// -- Find root IDs --
|
||||
uint64_t alphaId = 0, bravoId = 0;
|
||||
for (const auto& n : doc->tree.nodes) {
|
||||
if (n.parentId == 0 && n.kind == NodeKind::Struct) {
|
||||
if (n.name == "Alpha") alphaId = n.id;
|
||||
if (n.name == "Bravo") bravoId = n.id;
|
||||
}
|
||||
}
|
||||
QVERIFY(alphaId != 0);
|
||||
QVERIFY(bravoId != 0);
|
||||
QCOMPARE(ctrl->viewRootId(), (uint64_t)0);
|
||||
|
||||
// -- Switch to Bravo: command row + fields update --
|
||||
ctrl->setViewRootId(bravoId);
|
||||
QApplication::processEvents();
|
||||
|
||||
QCOMPARE(ctrl->viewRootId(), bravoId);
|
||||
QVERIFY2(sci->text(0).contains("Bravo"),
|
||||
qPrintable("Expected 'Bravo' in command row, got: " + sci->text(0)));
|
||||
QVERIFY2(sci->text().contains("speed"),
|
||||
"View should show Bravo's 'speed' field");
|
||||
|
||||
// -- Switch to Alpha --
|
||||
ctrl->setViewRootId(alphaId);
|
||||
QApplication::processEvents();
|
||||
|
||||
QCOMPARE(ctrl->viewRootId(), alphaId);
|
||||
QVERIFY2(sci->text(0).contains("Alpha"),
|
||||
qPrintable("Expected 'Alpha' in command row, got: " + sci->text(0)));
|
||||
|
||||
// -- Create new type (no name) --
|
||||
int nodesBefore = doc->tree.nodes.size();
|
||||
|
||||
Node newNode;
|
||||
newNode.kind = NodeKind::Struct;
|
||||
newNode.name = QString();
|
||||
newNode.parentId = 0;
|
||||
newNode.offset = 0;
|
||||
newNode.id = doc->tree.reserveId();
|
||||
uint64_t newId = newNode.id;
|
||||
|
||||
doc->undoStack.push(new RcxCommand(ctrl, cmd::Insert{newNode}));
|
||||
ctrl->setViewRootId(newId);
|
||||
QApplication::processEvents();
|
||||
|
||||
// Verify new struct
|
||||
int idx = doc->tree.indexOfId(newId);
|
||||
QVERIFY(idx >= 0);
|
||||
QVERIFY(doc->tree.nodes[idx].name.isEmpty());
|
||||
QCOMPARE(doc->tree.nodes[idx].kind, NodeKind::Struct);
|
||||
QCOMPARE(doc->tree.nodes[idx].parentId, (uint64_t)0);
|
||||
QCOMPARE(ctrl->viewRootId(), newId);
|
||||
|
||||
// Command row shows "<no name>"
|
||||
QVERIFY2(sci->text(0).contains("<no name>"),
|
||||
qPrintable("Expected '<no name>' in command row, got: " + sci->text(0)));
|
||||
|
||||
// -- Undo removes the new struct --
|
||||
doc->undoStack.undo();
|
||||
QApplication::processEvents();
|
||||
QCOMPARE(doc->tree.nodes.size(), nodesBefore);
|
||||
|
||||
// Cleanup
|
||||
delete ctrl;
|
||||
delete splitter;
|
||||
delete doc;
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(TestTypeSelector)
|
||||
#include "test_type_selector.moc"
|
||||
Reference in New Issue
Block a user