Files
archived-Reclass/tests/test_typeinfer.cpp
IChooseYou 25afbe373b feat: status bar format, tab titles with source, taller tabs, pill hover, source switch base fix
- Status bar: show StructName.field +0xOFFSET with dimmed offset suffix
- Status bar: sync font to global editor font (JetBrains Mono 10pt)
- Dock tab title: include active source name (StructName — source.exe)
- Dock tabs +10% height (28→31), pane tabs (24→26), workspace title (26→29)
- Footer pills (+1024, Trim, +10): add visual hover highlight via IND_HOVER_SPAN
- Fix source switch keeping old base address for plugin providers
2026-03-08 16:29:12 -06:00

199 lines
6.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <QtTest/QTest>
#include <cstring>
#include "typeinfer.h"
using namespace rcx;
class TestTypeInfer : public QObject {
Q_OBJECT
private slots:
// ── NULL / zero → empty ──
void nullPtr() {
QVERIFY(inferTypes(nullptr, 8).isEmpty());
}
void zeroLen() {
uint8_t d[4] = {};
QVERIFY(inferTypes(d, 0).isEmpty());
}
void allZeros8() {
uint8_t d[8] = {};
QVERIFY(inferTypes(d, 8).isEmpty());
}
void allZeros4() {
uint8_t d[4] = {};
QVERIFY(inferTypes(d, 4).isEmpty());
}
void allZeros2() {
uint8_t d[2] = {};
QVERIFY(inferTypes(d, 2).isEmpty());
}
// ── Hex64: float pair ──
// {21.0488f, 547.3f} — two clear floats with fractional parts;
// whole-width Double/Ptr64 score poorly → Float×2 dominates
void hex64_floatPair() {
float a = 21.0488f, b = 547.3f;
uint8_t d[8];
std::memcpy(d, &a, 4);
std::memcpy(d + 4, &b, 4);
auto r = inferTypes(d, 8);
QVERIFY(!r.isEmpty());
auto& top = r[0];
QCOMPARE(top.kinds.size(), 2);
QCOMPARE(top.kinds[0], NodeKind::Float);
QVERIFY(top.strength >= 3); // strong
}
// ── Hex64: int32 pair ──
// {42, 99} — two small integers
void hex64_intPair() {
int32_t a = 42, b = 99;
uint8_t d[8];
std::memcpy(d, &a, 4);
std::memcpy(d + 4, &b, 4);
auto r = inferTypes(d, 8);
QVERIFY(!r.isEmpty());
auto& top = r[0];
QVERIFY(top.kinds.size() == 2);
QVERIFY(top.kinds[0] == NodeKind::Int32 || top.kinds[0] == NodeKind::UInt32);
}
// ── Hex64: UTF-8 string ──
void hex64_utf8() {
uint8_t d[8] = {'I', 'C', 'h', 'o', 'o', 's', 'e', 'Y'};
auto r = inferTypes(d, 8);
QVERIFY(!r.isEmpty());
// Top should be UTF8 (strong)
bool foundUtf8 = false;
for (const auto& s : r) {
if (s.kinds.size() == 1 && s.kinds[0] == NodeKind::UTF8) {
foundUtf8 = true;
QVERIFY(s.strength >= 3); // strong
}
}
QVERIFY(foundUtf8);
}
// ── Hex64: pointer-like value ──
void hex64_pointer() {
// 0x00007FF6A0B01000 — typical Windows user-mode address
uint8_t d[8] = {0x00, 0x10, 0xB0, 0xA0, 0xF6, 0x7F, 0x00, 0x00};
auto r = inferTypes(d, 8);
QVERIFY(!r.isEmpty());
bool foundPtr = false;
for (const auto& s : r)
if (s.kinds.size() == 1 && s.kinds[0] == NodeKind::Pointer64)
foundPtr = true;
QVERIFY(foundPtr);
}
// ── Hex32: clear float ──
void hex32_float() {
// 21.0488f = 0x41A86600
uint8_t d[4] = {0x00, 0x66, 0xA8, 0x41};
auto r = inferTypes(d, 4);
QVERIFY(!r.isEmpty());
QCOMPARE(r[0].kinds.size(), 1);
QCOMPARE(r[0].kinds[0], NodeKind::Float);
QVERIFY(r[0].strength >= 2);
}
// ── Hex32: small integer with monotonic history ──
void hex32_int_monotonic() {
// Value: 0x0000BFFC = 49148 (signed: 49148)
uint8_t d[4] = {0xFC, 0xBF, 0x00, 0x00};
InferHints h;
h.monotonic = true;
h.sampleCount = 10;
uint8_t minB[4] = {0x10, 0x00, 0x00, 0x00}; // 16
uint8_t maxB[4] = {0xFC, 0xBF, 0x00, 0x00}; // 49148
h.minObserved = minB;
h.maxObserved = maxB;
auto r = inferTypes(d, 4, h);
QVERIFY(!r.isEmpty());
QVERIFY(r[0].kinds[0] == NodeKind::Int32 || r[0].kinds[0] == NodeKind::UInt32);
QVERIFY(r[0].strength >= 2);
}
// ── Hex16: small unsigned ──
void hex16_uint() {
uint8_t d[2] = {0x5F, 0x00}; // 95
auto r = inferTypes(d, 2);
QVERIFY(!r.isEmpty());
QVERIFY(r[0].kinds[0] == NodeKind::Int16 || r[0].kinds[0] == NodeKind::UInt16);
}
// ── Hex8: bool-like ──
void hex8_bool() {
uint8_t d[1] = {1};
auto r = inferTypes(d, 1);
QVERIFY(!r.isEmpty());
bool foundBool = false;
for (const auto& s : r)
if (s.kinds.size() == 1 && s.kinds[0] == NodeKind::Bool)
foundBool = true;
QVERIFY(foundBool);
}
// ── formatHint ──
void formatHint_strong() {
TypeSuggestion s;
s.kinds = {NodeKind::Float};
s.strength = 3;
QCOMPARE(formatHint(s), QStringLiteral("float strong"));
}
void formatHint_moderate() {
TypeSuggestion s;
s.kinds = {NodeKind::Float};
s.strength = 2;
QCOMPARE(formatHint(s), QStringLiteral("float moderate"));
}
void formatHint_split() {
TypeSuggestion s;
s.kinds = {NodeKind::Float, NodeKind::Float};
s.strength = 3;
QString h = formatHint(s);
QVERIFY(h.contains("float"));
QVERIFY(h.contains("2"));
QVERIFY(h.endsWith("strong"));
}
// ── Denormal rejection ──
void denormalRejected() {
// Denormal float: exp=0, mantissa non-zero → 0x00000001
uint8_t d[4] = {0x01, 0x00, 0x00, 0x00};
auto r = inferTypes(d, 4);
// Should NOT suggest Float as top pick
if (!r.isEmpty() && r[0].kinds.size() == 1)
QVERIFY(r[0].kinds[0] != NodeKind::Float);
}
// ── Benchmark: single call ──
void bench_singleCall() {
uint8_t d[8] = {0x00, 0x00, 0x80, 0x3F, 0xCD, 0xCC, 0x4C, 0x3E};
QBENCHMARK {
inferTypes(d, 8);
}
}
// ── Benchmark: 200-node batch (simulates one refresh) ──
void bench_batchRefresh() {
// Prepare 200 varied byte patterns
QVector<std::array<uint8_t, 8>> data(200);
for (int i = 0; i < 200; ++i) {
uint32_t seed = (uint32_t)(i * 7919 + 1);
for (int j = 0; j < 8; ++j)
data[i][j] = (uint8_t)((seed >> (j * 3)) ^ (i + j));
}
QBENCHMARK {
for (int i = 0; i < 200; ++i)
inferTypes(data[i].data(), 8);
}
}
};
QTEST_MAIN(TestTypeInfer)
#include "test_typeinfer.moc"