mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-30 13:05:25 -05:00
tests: Refactor to add support for other types of tests
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <hex/pattern_language/pattern_data.hpp>
|
||||
|
||||
#define TEST(name) (hex::test::TestPattern*) new hex::test::TestPattern ## name ()
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
using namespace pl;
|
||||
|
||||
enum class Mode {
|
||||
Succeeding,
|
||||
Failing
|
||||
};
|
||||
|
||||
class TestPattern {
|
||||
public:
|
||||
explicit TestPattern(const std::string &name, Mode mode = Mode::Succeeding) : m_mode(mode) {
|
||||
TestPattern::s_tests.insert({ name, this });
|
||||
}
|
||||
|
||||
virtual ~TestPattern() {
|
||||
for (auto &pattern : this->m_patterns)
|
||||
delete pattern;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T* create(const std::string &typeName, const std::string &varName, auto ... args) {
|
||||
auto pattern = new T(args...);
|
||||
pattern->setTypeName(typeName);
|
||||
pattern->setVariableName(varName);
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
virtual std::string getSourceCode() const = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual const std::vector<PatternData*>& getPatterns() const final { return this->m_patterns; }
|
||||
virtual void addPattern(PatternData *pattern) final {
|
||||
this->m_patterns.push_back(pattern);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto failing() {
|
||||
this->m_mode = Mode::Failing;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
Mode getMode() {
|
||||
return this->m_mode;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static auto& getTests() {
|
||||
return TestPattern::s_tests;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<PatternData*> m_patterns;
|
||||
Mode m_mode;
|
||||
|
||||
static inline std::map<std::string, TestPattern*> s_tests;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternBitfields : public TestPattern {
|
||||
public:
|
||||
TestPatternBitfields() : TestPattern("Bitfields") {
|
||||
auto testBitfield = create<PatternDataBitfield>("TestBitfield", "testBitfield", 0x12, (4 * 4) / 8, nullptr);
|
||||
testBitfield->setEndian(std::endian::big);
|
||||
testBitfield->setFields({
|
||||
create<PatternDataBitfieldField>("", "a", 0x12, 0, 4, nullptr),
|
||||
create<PatternDataBitfieldField>("", "b", 0x12, 4, 4, nullptr),
|
||||
create<PatternDataBitfieldField>("", "c", 0x12, 8, 4, nullptr),
|
||||
create<PatternDataBitfieldField>("", "d", 0x12, 12, 4, nullptr)
|
||||
});
|
||||
|
||||
addPattern(testBitfield);
|
||||
}
|
||||
~TestPatternBitfields() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
bitfield TestBitfield {
|
||||
a : 4;
|
||||
b : 4;
|
||||
c : 4;
|
||||
d : 4;
|
||||
};
|
||||
|
||||
be TestBitfield testBitfield @ 0x12;
|
||||
|
||||
std::assert(testBitfield.a == 0x0A, "Field A invalid");
|
||||
std::assert(testBitfield.b == 0x00, "Field B invalid");
|
||||
std::assert(testBitfield.c == 0x04, "Field C invalid");
|
||||
std::assert(testBitfield.d == 0x03, "Field D invalid");
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternEnums : public TestPattern {
|
||||
public:
|
||||
TestPatternEnums() : TestPattern("Enums"){
|
||||
auto testEnum = create<PatternDataEnum>("TestEnum", "testEnum", 0x08, sizeof(u32), nullptr);
|
||||
testEnum->setEnumValues({
|
||||
{ u128(0x0000), "A" },
|
||||
{ s128(0x0C), "B" },
|
||||
{ u128(0x0D), "C" },
|
||||
{ u128(0x0E), "D" },
|
||||
});
|
||||
testEnum->setEndian(std::endian::big);
|
||||
|
||||
addPattern(testEnum);
|
||||
}
|
||||
~TestPatternEnums() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
enum TestEnum : u32 {
|
||||
A,
|
||||
B = 0x0C,
|
||||
C,
|
||||
D
|
||||
};
|
||||
|
||||
be TestEnum testEnum @ 0x08;
|
||||
|
||||
std::assert(testEnum == TestEnum::C, "Invalid enum value");
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternExample : public TestPattern {
|
||||
public:
|
||||
TestPatternExample() : TestPattern("") {
|
||||
|
||||
}
|
||||
~TestPatternExample() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternExtraSemicolon : public TestPattern {
|
||||
public:
|
||||
TestPatternExtraSemicolon() : TestPattern("ExtraSemicolon") {
|
||||
|
||||
}
|
||||
~TestPatternExtraSemicolon() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
struct Test {
|
||||
u32 x;;;
|
||||
u8 y;
|
||||
float z;;
|
||||
};;
|
||||
|
||||
struct Test2 {
|
||||
u32 x;
|
||||
u32 y;
|
||||
};
|
||||
|
||||
Test test @ 0x00;;;
|
||||
Test test2 @ 0x10;
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternFailingAssert : public TestPattern {
|
||||
public:
|
||||
TestPatternFailingAssert() : TestPattern("FailingAssert", Mode::Failing) {
|
||||
|
||||
}
|
||||
~TestPatternFailingAssert() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
#define MSG "Error"
|
||||
|
||||
std::assert(false, MSG);
|
||||
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternLiterals : public TestPattern {
|
||||
public:
|
||||
TestPatternLiterals() : TestPattern("Literals") {
|
||||
|
||||
}
|
||||
~TestPatternLiterals() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
#define MSG "Invalid literal"
|
||||
|
||||
std::assert(255 == 0xFF, MSG);
|
||||
std::assert(0xAA == 0b10101010, MSG);
|
||||
std::assert(12345 != 67890, MSG);
|
||||
std::assert(100U == 0x64U, MSG);
|
||||
std::assert(-100 == -0x64, MSG);
|
||||
std::assert(3.14159F > 1.414D, MSG);
|
||||
std::assert('A' == 0x41, MSG);
|
||||
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternMath : public TestPattern {
|
||||
public:
|
||||
TestPatternMath() : TestPattern("Math") {
|
||||
|
||||
}
|
||||
~TestPatternMath() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
// Compare operations
|
||||
std::assert(123 == 123, "== operation error");
|
||||
std::assert(123 != 567, "!= operation error");
|
||||
std::assert(111 < 222, "< operation error");
|
||||
std::assert(333 > 222, "> operation error");
|
||||
std::assert(100 >= 100, ">= operation error");
|
||||
std::assert(200 <= 200, "<= operation error");
|
||||
|
||||
// Boolean operations
|
||||
std::assert(true, "true literal invalid");
|
||||
std::assert(true && true, "&& operator error");
|
||||
std::assert(false || true, "|| operator error");
|
||||
std::assert(true ^^ false, "^^ operator error");
|
||||
std::assert(!false, "! operator error");
|
||||
|
||||
// Bitwise operations
|
||||
std::assert(0xFF00FF | 0x00AA00 == 0xFFAAFF, "| operator error");
|
||||
std::assert(0xFFFFFF & 0x00FF00 == 0x00FF00, "& operator error");
|
||||
std::assert(0xFFFFFF ^ 0x00AA00 == 0xFF55FF, "^ operator error");
|
||||
std::assert(~0x00 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "~ operator error");
|
||||
std::assert(0xAA >> 4 == 0x0A, ">> operator error");
|
||||
std::assert(0xAA << 4 == 0xAA0, "<< operator error");
|
||||
|
||||
// Basic operations
|
||||
std::assert(100 + 200 == 300, "+ operator error");
|
||||
std::assert(400 - 200 == 200, "- operator error");
|
||||
std::assert(10 * 20 == 200, "* operator error");
|
||||
std::assert(200 / 100 == 2, "/ operator error");
|
||||
std::assert(100 % 2 == 0, "% operator error");
|
||||
|
||||
// Special operators
|
||||
std::assert($ == 0, "$ operator error");
|
||||
std::assert(((10 == 20) ? 30 : 40) == 40, "?: operator error");
|
||||
|
||||
// Type operators
|
||||
struct TypeTest { u32 x, y, z; };
|
||||
TypeTest typeTest @ 0x100;
|
||||
|
||||
std::assert(addressof(typeTest) == 0x100, "addressof operator error");
|
||||
std::assert(sizeof(typeTest) == 3 * 4, "sizeof operator error");
|
||||
|
||||
// Properties
|
||||
std::assert(100 + 200 == 200 + 100, "+ operator commutativity error");
|
||||
std::assert(100 - 200 != 200 - 100, "- operator commutativity error");
|
||||
std::assert(100 * 200 == 200 * 100, "* operator commutativity error");
|
||||
std::assert(100F / 200F != 200F / 100F, "/ operator commutativity error");
|
||||
|
||||
std::assert(10 + (20 + 30) == (10 + 20) + 30, "+ operator associativity error");
|
||||
std::assert(10 - (20 - 30) != (10 - 20) - 30, "- operator associativity error");
|
||||
std::assert(10 * (20 * 30) == (10 * 20) * 30, "* operator associativity error");
|
||||
std::assert(10F / (20F / 30F) != (10F / 20F) / 30F, "/ operator associativity error");
|
||||
|
||||
std::assert(10 * (20 + 30) == 10 * 20 + 10 * 30, "* operator distributivity error");
|
||||
std::assert(10F / (20F + 30F) != 10F / 20F + 10F / 30F, "/ operator distributivity error");
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternNamespaces : public TestPattern {
|
||||
public:
|
||||
TestPatternNamespaces() : TestPattern("Namespaces") {
|
||||
|
||||
}
|
||||
~TestPatternNamespaces() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
namespace A {
|
||||
struct Test {
|
||||
u32 x;
|
||||
};
|
||||
}
|
||||
|
||||
namespace B {
|
||||
struct Test {
|
||||
u16 x;
|
||||
};
|
||||
}
|
||||
|
||||
using ATest = A::Test;
|
||||
|
||||
A::Test test1 @ 0x10;
|
||||
ATest test2 @ 0x20;
|
||||
B::Test test3 @ 0x20;
|
||||
|
||||
std::assert(sizeof(test1) == sizeof(test2), "error using namespaced type");
|
||||
std::assert(sizeof(test2) != sizeof(test3), "error differentiating two namespace types with same name");
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternPadding : public TestPattern {
|
||||
public:
|
||||
TestPatternPadding() : TestPattern("Padding") {
|
||||
auto testStruct = create<PatternDataStruct>("TestStruct", "testStruct", 0x100, sizeof(s32) + 20 + sizeof(u8[0x10]), nullptr);
|
||||
|
||||
auto variable = create<PatternDataSigned>("s32", "variable", 0x100, sizeof(s32), nullptr);
|
||||
auto padding = create<PatternDataPadding>("padding", "", 0x100 + sizeof(s32), 20, nullptr);
|
||||
auto array = create<PatternDataStaticArray>("u8", "array", 0x100 + sizeof(s32) + 20, sizeof(u8[0x10]), nullptr);
|
||||
array->setEntries(create<PatternDataUnsigned>("u8", "", 0x100 + sizeof(s32) + 20, sizeof(u8), nullptr), 0x10);
|
||||
|
||||
testStruct->setMembers({ variable, padding, array });
|
||||
|
||||
addPattern(testStruct);
|
||||
}
|
||||
~TestPatternPadding() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
struct TestStruct {
|
||||
s32 variable;
|
||||
padding[20];
|
||||
u8 array[0x10];
|
||||
};
|
||||
|
||||
TestStruct testStruct @ 0x100;
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternPlacement : public TestPattern {
|
||||
public:
|
||||
TestPatternPlacement() : TestPattern("Placement") {
|
||||
// placementVar
|
||||
{
|
||||
addPattern(create<PatternDataUnsigned>("u32", "placementVar", 0x00, sizeof(u32), nullptr));
|
||||
}
|
||||
|
||||
// placementArray
|
||||
{
|
||||
auto placementArray = create<PatternDataStaticArray>("u8", "placementArray", 0x10, sizeof(u8) * 10, nullptr);
|
||||
placementArray->setEntries(create<PatternDataUnsigned>("u8", "", 0x10, sizeof(u8), nullptr), 10);
|
||||
addPattern(placementArray);
|
||||
}
|
||||
|
||||
}
|
||||
~TestPatternPlacement() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
u32 placementVar @ 0x00;
|
||||
u8 placementArray[10] @ 0x10;
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternPointers : public TestPattern {
|
||||
public:
|
||||
TestPatternPointers() : TestPattern("Pointers") {
|
||||
// placementPointer
|
||||
{
|
||||
auto placementPointer = create<PatternDataPointer>("", "placementPointer", 0x0C, sizeof(u8), nullptr);
|
||||
auto pointedTo = create<PatternDataUnsigned>("u32", "", 0x49, sizeof(u32), nullptr);
|
||||
placementPointer->setPointedAtPattern(pointedTo);
|
||||
addPattern(placementPointer);
|
||||
}
|
||||
|
||||
}
|
||||
~TestPatternPointers() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
u32 *placementPointer : u8 @ 0x0C;
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternRValues : public TestPattern {
|
||||
public:
|
||||
TestPatternRValues() : TestPattern("RValues") {
|
||||
|
||||
}
|
||||
~TestPatternRValues() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
union C {
|
||||
u8 y;
|
||||
u8 array[parent.parent.x];
|
||||
};
|
||||
|
||||
struct B {
|
||||
C *c : u8;
|
||||
};
|
||||
|
||||
struct A {
|
||||
u8 x;
|
||||
B b;
|
||||
};
|
||||
|
||||
A a @ 0x00;
|
||||
|
||||
std::assert(sizeof(a.b.c) == a.x && a.x != 0x00, "RValue parent test failed!");
|
||||
std::assert(a.b.c.y == a.b.c.array[0], "RValue array access test failed!");
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternStructs : public TestPattern {
|
||||
public:
|
||||
TestPatternStructs() : TestPattern("Structs") {
|
||||
auto testStruct = create<PatternDataStruct>("TestStruct", "testStruct", 0x100, sizeof(s32) + sizeof(u8[0x10]), nullptr);
|
||||
|
||||
auto variable = create<PatternDataSigned>("s32", "variable", 0x100, sizeof(s32), nullptr);
|
||||
auto array = create<PatternDataStaticArray>("u8", "array", 0x100 + sizeof(s32), sizeof(u8[0x10]), nullptr);
|
||||
array->setEntries(create<PatternDataUnsigned>("u8", "", 0x100 + sizeof(s32), sizeof(u8), nullptr), 0x10);
|
||||
|
||||
testStruct->setMembers({ variable, array });
|
||||
|
||||
addPattern(testStruct);
|
||||
}
|
||||
~TestPatternStructs() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
struct TestStruct {
|
||||
s32 variable;
|
||||
u8 array[0x10];
|
||||
};
|
||||
|
||||
TestStruct testStruct @ 0x100;
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternSucceedingAssert : public TestPattern {
|
||||
public:
|
||||
TestPatternSucceedingAssert() : TestPattern("SucceedingAssert") {
|
||||
|
||||
}
|
||||
~TestPatternSucceedingAssert() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
#define MSG "Error"
|
||||
|
||||
std::assert(true, MSG);
|
||||
std::assert(100 == 100, MSG);
|
||||
std::assert(50 < 100, MSG);
|
||||
std::assert(1, MSG);
|
||||
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "test_pattern.hpp"
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
class TestPatternUnions : public TestPattern {
|
||||
public:
|
||||
TestPatternUnions() : TestPattern("Unions") {
|
||||
auto testUnion = create<PatternDataUnion>("TestUnion", "testUnion", 0x200, sizeof(u128), nullptr);
|
||||
|
||||
auto array = create<PatternDataStaticArray>("s32", "array", 0x200, sizeof(s32[2]), nullptr);
|
||||
array->setEntries(create<PatternDataSigned>("s32", "", 0x200, sizeof(s32), nullptr), 2);
|
||||
auto variable = create<PatternDataUnsigned>("u128", "variable", 0x200, sizeof(u128), nullptr);
|
||||
|
||||
testUnion->setMembers({ array, variable });
|
||||
|
||||
addPattern(testUnion);
|
||||
}
|
||||
~TestPatternUnions() override = default;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string getSourceCode() const override {
|
||||
return R"(
|
||||
union TestUnion {
|
||||
s32 array[2];
|
||||
u128 variable;
|
||||
};
|
||||
|
||||
TestUnion testUnion @ 0x200;
|
||||
)";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user