fix: CI test failures from collapsed=true default

- compose.cpp: show static fields for root structs even when collapsed
- test_compose: set collapsed=false on nodes needing expanded rendering
- test_disasm: set collapsed=false on vtable pointer nodes
- test_static_fields: rewrite collapsed test to use non-root child struct
This commit is contained in:
IChooseYou
2026-03-07 11:58:08 -07:00
committed by IChooseYou
parent 70c7404556
commit f0fc85f60f
4 changed files with 66 additions and 9 deletions

View File

@@ -587,7 +587,7 @@ void composeParent(ComposeState& state, const NodeTree& tree,
} }
// ── Static fields: render after regular children, before footer ── // ── Static fields: render after regular children, before footer ──
if (!staticIdxs.isEmpty() && !node.collapsed) { if (!staticIdxs.isEmpty() && (!node.collapsed || isRootHeader)) {
// Build identifier resolver for static field expressions // Build identifier resolver for static field expressions
auto makeResolver = [&](uint64_t parentAbsAddr) { auto makeResolver = [&](uint64_t parentAbsAddr) {
AddressParserCallbacks cbs; AddressParserCallbacks cbs;

View File

@@ -234,6 +234,7 @@ private slots:
child.name = "Child"; child.name = "Child";
child.parentId = rootId; child.parentId = rootId;
child.offset = 0; child.offset = 0;
child.collapsed = false;
int ci = tree.addNode(child); int ci = tree.addNode(child);
uint64_t childId = tree.nodes[ci].id; uint64_t childId = tree.nodes[ci].id;
@@ -281,6 +282,7 @@ private slots:
inner.name = "Inner"; inner.name = "Inner";
inner.parentId = rootId; inner.parentId = rootId;
inner.offset = 4; inner.offset = 4;
inner.collapsed = false;
int ii = tree.addNode(inner); int ii = tree.addNode(inner);
uint64_t innerId = tree.nodes[ii].id; uint64_t innerId = tree.nodes[ii].id;
@@ -354,6 +356,7 @@ private slots:
tmpl.name = "VTable"; tmpl.name = "VTable";
tmpl.parentId = 0; tmpl.parentId = 0;
tmpl.offset = 200; // far away so standalone rendering uses offset 200 tmpl.offset = 200; // far away so standalone rendering uses offset 200
tmpl.collapsed = false;
int ti = tree.addNode(tmpl); int ti = tree.addNode(tmpl);
uint64_t tmplId = tree.nodes[ti].id; uint64_t tmplId = tree.nodes[ti].id;
@@ -378,6 +381,7 @@ private slots:
ptr.parentId = mainId; ptr.parentId = mainId;
ptr.offset = 4; ptr.offset = 4;
ptr.refId = tmplId; ptr.refId = tmplId;
ptr.collapsed = false;
tree.addNode(ptr); tree.addNode(ptr);
// Provider: pointer at offset 4 points to address 100 // Provider: pointer at offset 4 points to address 100
@@ -434,6 +438,7 @@ private slots:
tmpl.name = "Target"; tmpl.name = "Target";
tmpl.parentId = 0; tmpl.parentId = 0;
tmpl.offset = 200; tmpl.offset = 200;
tmpl.collapsed = false;
int ti = tree.addNode(tmpl); int ti = tree.addNode(tmpl);
uint64_t tmplId = tree.nodes[ti].id; uint64_t tmplId = tree.nodes[ti].id;
@@ -450,6 +455,7 @@ private slots:
ptr.parentId = mainId; ptr.parentId = mainId;
ptr.offset = 0; ptr.offset = 0;
ptr.refId = tmplId; ptr.refId = tmplId;
ptr.collapsed = false;
tree.addNode(ptr); tree.addNode(ptr);
// All zeros = null pointer // All zeros = null pointer
@@ -493,6 +499,7 @@ private slots:
tmpl.name = "Target"; tmpl.name = "Target";
tmpl.parentId = 0; tmpl.parentId = 0;
tmpl.offset = 200; tmpl.offset = 200;
tmpl.collapsed = false; // standalone rendering shows children
int ti = tree.addNode(tmpl); int ti = tree.addNode(tmpl);
uint64_t tmplId = tree.nodes[ti].id; uint64_t tmplId = tree.nodes[ti].id;
@@ -509,7 +516,7 @@ private slots:
ptr.parentId = mainId; ptr.parentId = mainId;
ptr.offset = 0; ptr.offset = 0;
ptr.refId = tmplId; ptr.refId = tmplId;
ptr.collapsed = true; // collapsed ptr.collapsed = true; // collapsed — this is the test condition
tree.addNode(ptr); tree.addNode(ptr);
// Non-null pointer // Non-null pointer
@@ -548,6 +555,7 @@ private slots:
tmpl.name = "Recursive"; tmpl.name = "Recursive";
tmpl.parentId = 0; tmpl.parentId = 0;
tmpl.offset = 200; tmpl.offset = 200;
tmpl.collapsed = false;
int ti = tree.addNode(tmpl); int ti = tree.addNode(tmpl);
uint64_t tmplId = tree.nodes[ti].id; uint64_t tmplId = tree.nodes[ti].id;
@@ -565,6 +573,7 @@ private slots:
backPtr.parentId = tmplId; backPtr.parentId = tmplId;
backPtr.offset = 4; backPtr.offset = 4;
backPtr.refId = tmplId; // points back to same struct backPtr.refId = tmplId; // points back to same struct
backPtr.collapsed = false;
tree.addNode(backPtr); tree.addNode(backPtr);
// Pointer in Main → Recursive // Pointer in Main → Recursive
@@ -574,6 +583,7 @@ private slots:
ptr.parentId = mainId; ptr.parentId = mainId;
ptr.offset = 0; ptr.offset = 0;
ptr.refId = tmplId; ptr.refId = tmplId;
ptr.collapsed = false;
tree.addNode(ptr); tree.addNode(ptr);
// Provider: main ptr at offset 0 points to 100 // Provider: main ptr at offset 0 points to 100
@@ -696,6 +706,7 @@ private slots:
arr.offset = 0; arr.offset = 0;
arr.elementKind = NodeKind::Int32; arr.elementKind = NodeKind::Int32;
arr.arrayLen = 10; arr.arrayLen = 10;
arr.collapsed = false;
tree.addNode(arr); tree.addNode(arr);
NullProvider prov; NullProvider prov;
@@ -847,6 +858,7 @@ private slots:
arr.offset = 0; arr.offset = 0;
arr.elementKind = NodeKind::Int32; arr.elementKind = NodeKind::Int32;
arr.arrayLen = 2; arr.arrayLen = 2;
arr.collapsed = false;
int ai = tree.addNode(arr); int ai = tree.addNode(arr);
uint64_t arrId = tree.nodes[ai].id; uint64_t arrId = tree.nodes[ai].id;
@@ -856,6 +868,7 @@ private slots:
elem0.name = "Item"; elem0.name = "Item";
elem0.parentId = arrId; elem0.parentId = arrId;
elem0.offset = 0; elem0.offset = 0;
elem0.collapsed = false;
int e0i = tree.addNode(elem0); int e0i = tree.addNode(elem0);
uint64_t elem0Id = tree.nodes[e0i].id; uint64_t elem0Id = tree.nodes[e0i].id;
@@ -871,6 +884,7 @@ private slots:
elem1.name = "Item"; elem1.name = "Item";
elem1.parentId = arrId; elem1.parentId = arrId;
elem1.offset = 4; elem1.offset = 4;
elem1.collapsed = false;
int e1i = tree.addNode(elem1); int e1i = tree.addNode(elem1);
uint64_t elem1Id = tree.nodes[e1i].id; uint64_t elem1Id = tree.nodes[e1i].id;
@@ -1035,6 +1049,7 @@ private slots:
arr.offset = 0; arr.offset = 0;
arr.elementKind = NodeKind::UInt32; arr.elementKind = NodeKind::UInt32;
arr.arrayLen = 4; arr.arrayLen = 4;
arr.collapsed = false;
tree.addNode(arr); tree.addNode(arr);
// Buffer with known values: 0x11, 0x22, 0x33, 0x44 // Buffer with known values: 0x11, 0x22, 0x33, 0x44
@@ -1140,6 +1155,7 @@ private slots:
arr.offset = 0; arr.offset = 0;
arr.elementKind = NodeKind::Struct; arr.elementKind = NodeKind::Struct;
arr.arrayLen = 1; arr.arrayLen = 1;
arr.collapsed = false;
int ai = tree.addNode(arr); int ai = tree.addNode(arr);
uint64_t arrId = tree.nodes[ai].id; uint64_t arrId = tree.nodes[ai].id;
@@ -1149,6 +1165,7 @@ private slots:
elem.name = "Item"; elem.name = "Item";
elem.parentId = arrId; elem.parentId = arrId;
elem.offset = 0; elem.offset = 0;
elem.collapsed = false;
int ei = tree.addNode(elem); int ei = tree.addNode(elem);
uint64_t elemId = tree.nodes[ei].id; uint64_t elemId = tree.nodes[ei].id;
@@ -1481,6 +1498,7 @@ private slots:
structC.structTypeName = "InnerData"; structC.structTypeName = "InnerData";
structC.parentId = 0; structC.parentId = 0;
structC.offset = 300; structC.offset = 300;
structC.collapsed = false;
int ci = tree.addNode(structC); int ci = tree.addNode(structC);
uint64_t structCId = tree.nodes[ci].id; uint64_t structCId = tree.nodes[ci].id;
@@ -1498,6 +1516,7 @@ private slots:
structB.structTypeName = "Wrapper"; structB.structTypeName = "Wrapper";
structB.parentId = 0; structB.parentId = 0;
structB.offset = 200; structB.offset = 200;
structB.collapsed = false;
int bi = tree.addNode(structB); int bi = tree.addNode(structB);
uint64_t structBId = tree.nodes[bi].id; uint64_t structBId = tree.nodes[bi].id;
@@ -1514,6 +1533,7 @@ private slots:
bptr.parentId = structBId; bptr.parentId = structBId;
bptr.offset = 4; bptr.offset = 4;
bptr.refId = structCId; // points to InnerData bptr.refId = structCId; // points to InnerData
bptr.collapsed = false;
tree.addNode(bptr); tree.addNode(bptr);
// Root's pointer to StructB // Root's pointer to StructB
@@ -1523,6 +1543,7 @@ private slots:
rptr.parentId = rootId; rptr.parentId = rootId;
rptr.offset = 0; rptr.offset = 0;
rptr.refId = structBId; rptr.refId = structBId;
rptr.collapsed = false;
tree.addNode(rptr); tree.addNode(rptr);
// Provider: rptr at 0 → addr 100, bptr at 100+4=104 → addr 150 // Provider: rptr at 0 → addr 100, bptr at 100+4=104 → addr 150
@@ -1591,6 +1612,7 @@ private slots:
structB.name = "StructB"; structB.name = "StructB";
structB.parentId = 0; structB.parentId = 0;
structB.offset = 200; structB.offset = 200;
structB.collapsed = false;
int bi = tree.addNode(structB); int bi = tree.addNode(structB);
uint64_t structBId = tree.nodes[bi].id; uint64_t structBId = tree.nodes[bi].id;
@@ -1608,6 +1630,7 @@ private slots:
ptrToB.parentId = mainId; ptrToB.parentId = mainId;
ptrToB.offset = 4; ptrToB.offset = 4;
ptrToB.refId = structBId; ptrToB.refId = structBId;
ptrToB.collapsed = false;
tree.addNode(ptrToB); tree.addNode(ptrToB);
// StructB → Main pointer (creates cycle!) // StructB → Main pointer (creates cycle!)
@@ -1617,6 +1640,7 @@ private slots:
ptrToMain.parentId = structBId; ptrToMain.parentId = structBId;
ptrToMain.offset = 4; ptrToMain.offset = 4;
ptrToMain.refId = mainId; ptrToMain.refId = mainId;
ptrToMain.collapsed = false;
tree.addNode(ptrToMain); tree.addNode(ptrToMain);
// Provider: Main.to_b at offset 4 → addr 100 // Provider: Main.to_b at offset 4 → addr 100
@@ -2009,6 +2033,7 @@ private slots:
u.name = "u1"; u.name = "u1";
u.parentId = rootId; u.parentId = rootId;
u.offset = 0; u.offset = 0;
u.collapsed = false;
int ui = tree.addNode(u); int ui = tree.addNode(u);
uint64_t uId = tree.nodes[ui].id; uint64_t uId = tree.nodes[ui].id;
@@ -2655,6 +2680,7 @@ private slots:
inner.kind = NodeKind::Struct; inner.kind = NodeKind::Struct;
inner.name = "NewClass"; inner.name = "NewClass";
inner.parentId = 0; inner.parentId = 0;
inner.collapsed = false;
inner.offset = 200; inner.offset = 200;
int ii = tree.addNode(inner); int ii = tree.addNode(inner);
uint64_t innerId = tree.nodes[ii].id; uint64_t innerId = tree.nodes[ii].id;
@@ -2688,6 +2714,7 @@ private slots:
ptr.parentId = rootId; ptr.parentId = rootId;
ptr.offset = 8; ptr.offset = 8;
ptr.refId = innerId; ptr.refId = innerId;
ptr.collapsed = false;
tree.addNode(ptr); tree.addNode(ptr);
// Last child: hex64 at depth 1 // Last child: hex64 at depth 1

View File

@@ -214,6 +214,7 @@ private slots:
vptr.parentId = rootId; vptr.parentId = rootId;
vptr.offset = 0; vptr.offset = 0;
vptr.refId = vtId; vptr.refId = vtId;
vptr.collapsed = false;
tree.addNode(vptr); tree.addNode(vptr);
// Compose the tree // Compose the tree
@@ -408,6 +409,7 @@ private slots:
Node vptr; vptr.kind = NodeKind::Pointer64; vptr.name = "__vptr"; Node vptr; vptr.kind = NodeKind::Pointer64; vptr.name = "__vptr";
vptr.parentId = rootId; vptr.offset = 0; vptr.refId = vtId; vptr.parentId = rootId; vptr.offset = 0; vptr.refId = vtId;
vptr.collapsed = false;
tree.addNode(vptr); tree.addNode(vptr);
// Compose with the snapshot (like production: compose uses snapshot) // Compose with the snapshot (like production: compose uses snapshot)

View File

@@ -360,17 +360,45 @@ private slots:
} }
void testComposeNoStaticFieldsWhenCollapsed() { void testComposeNoStaticFieldsWhenCollapsed() {
TestTree t; // Use a non-root struct to test collapsed behavior
t.addField("x", NodeKind::Float, 0); // (root structs are always expanded via isRootHeader)
t.addStaticField("h", "base"); NodeTree tree;
// Collapse the root struct Node root;
t.tree.nodes[0].collapsed = true; root.kind = NodeKind::Struct;
root.name = "Root";
root.parentId = 0;
int ri = tree.addNode(root);
uint64_t rootId = tree.nodes[ri].id;
Node child;
child.kind = NodeKind::Struct;
child.name = "Child";
child.parentId = rootId;
child.offset = 0;
child.collapsed = true; // collapsed child struct
int ci = tree.addNode(child);
uint64_t childId = tree.nodes[ci].id;
Node f;
f.kind = NodeKind::Float;
f.name = "x";
f.parentId = childId;
f.offset = 0;
tree.addNode(f);
Node sf;
sf.kind = NodeKind::Hex64;
sf.name = "h";
sf.parentId = childId;
sf.offset = 0;
sf.isStatic = true;
sf.offsetExpr = QStringLiteral("base");
tree.addNode(sf);
NullProvider prov; NullProvider prov;
ComposeResult result = compose(t.tree, prov); ComposeResult result = compose(tree, prov);
// When collapsed, no static field lines should appear // When collapsed, no static field lines should appear
QStringList lines = result.text.split('\n');
for (const auto& lm : result.meta) for (const auto& lm : result.meta)
QVERIFY2(!lm.isStaticLine, QVERIFY2(!lm.isStaticLine,
"Static field line should not appear when struct is collapsed"); "Static field line should not appear when struct is collapsed");