diff --git a/oi/type_graph/TypeGraph.h b/oi/type_graph/TypeGraph.h index 12fd2dd..dea1c04 100644 --- a/oi/type_graph/TypeGraph.h +++ b/oi/type_graph/TypeGraph.h @@ -55,21 +55,27 @@ class TypeGraph { template Primitive& makeType(Primitive::Kind kind); + template + T& makeType(NodeId id, Args&&... args) { + static_assert(std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v, + "Unnecessary node ID provided"); + auto type_unique_ptr = std::make_unique(id, std::forward(args)...); + auto type_raw_ptr = type_unique_ptr.get(); + types_.push_back(std::move(type_unique_ptr)); + return *type_raw_ptr; + } + template T& makeType(Args&&... args) { - static_assert(!std::is_same::value, + static_assert(!std::is_same_v, "Primitive singleton override should be used"); - if constexpr (std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value) { + if constexpr (std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v) { // Node ID required - auto type_unique_ptr = - std::make_unique(next_id_++, std::forward(args)...); - auto type_raw_ptr = type_unique_ptr.get(); - types_.push_back(std::move(type_unique_ptr)); - return *type_raw_ptr; + return makeType(next_id_++, std::forward(args)...); } else { // No Node ID auto type_unique_ptr = std::make_unique(std::forward(args)...); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 02a494d..f93bca5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(test_type_graph test_topo_sorter.cpp test_type_identifier.cpp type_graph_utils.cpp + TypeGraphParser.cpp ) add_dependencies(test_type_graph integration_test_target) target_compile_definitions(test_type_graph PRIVATE diff --git a/test/TypeGraphParser.cpp b/test/TypeGraphParser.cpp new file mode 100644 index 0000000..98363c8 --- /dev/null +++ b/test/TypeGraphParser.cpp @@ -0,0 +1,422 @@ +#include "TypeGraphParser.h" + +#include + +#include "oi/type_graph/TypeGraph.h" + +namespace { + +/* + * Sets line to the contents of the current line. + * Advances input to the beginning of the next line. + */ +bool getline(std::string_view& input, std::string_view& line) { + auto nextline = input.find('\n'); + if (nextline == input.npos) { + return false; + } + line = input.substr(0, nextline); + input = input.substr(nextline + 1); + return true; +} + +Primitive::Kind getKind(std::string_view kindStr) { + if (kindStr == "int8_t") + return Primitive::Kind::Int8; + if (kindStr == "int16_t") + return Primitive::Kind::Int16; + if (kindStr == "int32_t") + return Primitive::Kind::Int32; + if (kindStr == "int64_t") + return Primitive::Kind::Int64; + if (kindStr == "uint8_t") + return Primitive::Kind::UInt8; + if (kindStr == "uint16_t") + return Primitive::Kind::UInt16; + if (kindStr == "uint32_t") + return Primitive::Kind::UInt32; + if (kindStr == "uint64_t") + return Primitive::Kind::UInt64; + if (kindStr == "float") + return Primitive::Kind::Float32; + if (kindStr == "double") + return Primitive::Kind::Float64; + if (kindStr == "long double") + return Primitive::Kind::Float128; + if (kindStr == "bool") + return Primitive::Kind::Bool; + if (kindStr == "uintptr_t") + return Primitive::Kind::UIntPtr; + if (kindStr == "void") + return Primitive::Kind::Void; + throw std::runtime_error("Invalid Primitive::Kind: " + std::string{kindStr}); +} + +ContainerInfo getContainerInfo(std::string_view name) { + if (name == "std::vector") { + ContainerInfo info{"std::vector", SEQ_TYPE, "vector"}; + info.stubTemplateParams = {1}; + return info; + } + if (name == "std::map") { + ContainerInfo info{"std::map", STD_MAP_TYPE, "utility"}; + info.stubTemplateParams = {2, 3}; + return info; + } + if (name == "std::pair") { + ContainerInfo info{"std::pair", SEQ_TYPE, "utility"}; + return info; + } + if (name == "std::allocator") { + ContainerInfo info{"std::allocator", DUMMY_TYPE, "memory"}; + return info; + } + throw std::runtime_error("Unsupported container: " + std::string{name}); +} + +Qualifier getQualifier(std::string_view line) { + if (line == "const") { + return Qualifier::Const; + } + throw std::runtime_error("Unsupported qualifier: " + std::string{line}); +} + +size_t stripIndent(std::string_view& line) { + auto indent = line.find_first_not_of(" "); + line.remove_prefix(indent); + return indent; +} + +bool tryRemovePrefix(std::string_view& line, std::string_view prefix) { + if (!line.starts_with(prefix)) + return false; + line.remove_prefix(prefix.size()); + return true; +} + +void removePrefix(std::string_view& line, std::string_view prefix) { + if (!tryRemovePrefix(line, prefix)) + throw std::runtime_error("Unexpected line prefix. Expected '" + + std::string{prefix} + "'. Got '" + + std::string{line} + "'."); +} + +std::optional tryParseIntAttribute(std::string_view line, + std::string_view marker) { + auto attrStartPos = line.find(marker); + if (attrStartPos == line.npos) + return {}; + + auto valStartPos = attrStartPos + marker.size(); + auto valEndPos = line.find_first_not_of("0123456789", valStartPos); + + auto valStr = line.substr(valStartPos, valEndPos); + uint64_t val = std::stoi(std::string{valStr}); // Makes a string copy :'( + return val; +} + +uint64_t parseIntAttribute(std::string_view line, + std::string_view type, + std::string_view marker) { + auto val = tryParseIntAttribute(line, marker); + if (!val) + throw std::runtime_error(std::string{type} + " must have an attribute: '" + + std::string{marker} + "'. Got: '" + + std::string{line} + "'"); + return *val; +} + +std::optional tryParseStringValue(std::string_view& input, + std::string_view marker, + size_t rootIndent) { + std::string_view modifiedInput = input; + std::string_view line; + getline(modifiedInput, line); + + size_t indent = stripIndent(line); + if (indent != rootIndent) + return {}; + + if (!tryRemovePrefix(line, marker)) + return {}; + + auto val = line; + + // Update input to point to after the value we have read + input = modifiedInput; + + return val; +} + +NodeId getId(std::string_view str, size_t* idLen = nullptr) { + if (idLen) + *idLen = 0; + if (str[0] != '[') + return -1; + + auto closeBracket = str.find(']'); + if (closeBracket == str.npos) + return -1; + + auto idStr = str.substr(1, closeBracket); + NodeId id = std::stoi(std::string{idStr}); // Makes a string copy :'( + + if (idLen) + *idLen = closeBracket + 2; // +2 for the trailing "] " + return id; +} + +} // namespace + +void TypeGraphParser::parse(std::string_view input) { + size_t rootIndent = input.find_first_not_of("[]0123456789 "); + while (!input.empty()) { + Type& type = parseType(input, rootIndent); + typeGraph_.addRoot(type); + } +} + +Type& TypeGraphParser::parseType(std::string_view& input, size_t rootIndent) { + std::string_view line; + getline(input, line); + + size_t idLen = 0; + NodeId id = getId(line, &idLen); + line.remove_prefix(idLen); + + size_t indent = stripIndent(line) + idLen; + if (indent != rootIndent) + throw std::runtime_error("Unexpected indent for line: " + + std::string{line}); + + auto nodeEndPos = line.find_first_of(": \n"); + auto nodeTypeName = line.substr(0, nodeEndPos); + + Type* type = nullptr; + if (NodeId refId = getId(nodeTypeName); refId != -1) { + auto it = nodesById_.find(refId); + if (it == nodesById_.end()) + throw std::runtime_error("Node ID referenced before definition: " + + std::to_string(refId)); + + type = &it->second.get(); + } else if (nodeTypeName == "Class" || nodeTypeName == "Struct" || + nodeTypeName == "Union") { + // Format: "Class: MyClass (size: 12)" + Class::Kind kind; + if (nodeTypeName == "Class") + kind = Class::Kind::Class; + else if (nodeTypeName == "Struct") + kind = Class::Kind::Struct; + else if (nodeTypeName == "Union") + kind = Class::Kind::Union; + else + abort(); + + // Extract name + auto nameStartPos = line.find(' ') + 1; + auto nameEndPos = line.find('(', nameStartPos + 1); + auto name = line.substr(nameStartPos, nameEndPos - nameStartPos - 1); + + auto size = parseIntAttribute(line, nodeTypeName, "size: "); + auto align = tryParseIntAttribute(line, "align: "); + + Class& c = typeGraph_.makeType(id, kind, std::string{name}, size); + if (align) + c.setAlign(*align); + nodesById_.insert({id, c}); + + parseParams(c, input, indent + 2); + parseParents(c, input, indent + 2); + parseMembers(c, input, indent + 2); + parseFunctions(c, input, indent + 2); + parseChildren(c, input, indent + 2); + + type = &c; + } else if (nodeTypeName == "Container") { + // Format: "Container: std::vector (size: 24) + + // Extract container type name + auto nameStartPos = line.find(' ') + 1; + auto nameEndPos = line.find('(', nameStartPos + 1); + auto name = line.substr(nameStartPos, nameEndPos - nameStartPos - 1); + + auto info = getContainerInfo(name); + + auto size = parseIntAttribute(line, nodeTypeName, "size: "); + + Container& c = typeGraph_.makeType(id, info, size); + nodesById_.insert({id, c}); + + parseParams(c, input, indent + 2); + + type = &c; + } else if (nodeTypeName == "Primitive") { + // Format: "Primitive: int32_t" + removePrefix(line, "Primitive: "); + auto kind = getKind(line); + type = &typeGraph_.makeType(kind); + } else if (nodeTypeName == "Enum") { + // Format: "Enum: MyEnum (size: 4)" + removePrefix(line, "Enum: "); + auto nameEndPos = line.find(' '); + auto name = line.substr(0, nameEndPos); + auto size = parseIntAttribute(line, nodeTypeName, "size: "); + type = &typeGraph_.makeType(std::string{name}, size); + } else if (nodeTypeName == "Array") { + // Format: "Array: (length: 5) + auto len = parseIntAttribute(line, nodeTypeName, "length: "); + auto& elementType = parseType(input, indent + 2); + type = &typeGraph_.makeType(id, elementType, len); + } else if (nodeTypeName == "Typedef") { + // Format: "Typedef: myTypedef" + removePrefix(line, "Typedef: "); + auto name = line; + auto& underlyingType = parseType(input, indent + 2); + type = &typeGraph_.makeType(id, std::string{name}, underlyingType); + nodesById_.insert({id, *type}); + } else if (nodeTypeName == "Pointer") { + // Format: "Pointer" + auto& pointeeType = parseType(input, indent + 2); + type = &typeGraph_.makeType(id, pointeeType); + nodesById_.insert({id, *type}); + } else { + throw std::runtime_error("Unsupported node type: " + + std::string{nodeTypeName}); + } + + return *type; +} + +template +void TypeGraphParser::parseParams(T& c, + std::string_view& input, + size_t rootIndent) { + std::string_view origInput = input; + for (std::string_view line; getline(input, line); origInput = input) { + size_t indent = stripIndent(line); + if (indent != rootIndent) + break; + + // Format: "Param" + if (!tryRemovePrefix(line, "Param")) + break; + + if (auto value = tryParseStringValue(input, "Value: ", rootIndent + 2); + value) { + c.templateParams.emplace_back(std::string{*value}); + } else { + Type& type = parseType(input, rootIndent + 2); + TemplateParam param{type}; + + if (auto qualStr = + tryParseStringValue(input, "Qualifiers: ", rootIndent + 2); + qualStr) { + Qualifier qual = getQualifier(*qualStr); + param.qualifiers[qual] = true; + } + + c.templateParams.push_back(param); + } + } + // No more params for us - put back the line we just read + input = origInput; +} + +void TypeGraphParser::parseParents(Class& c, + std::string_view& input, + size_t rootIndent) { + std::string_view origInput = input; + for (std::string_view line; getline(input, line); origInput = input) { + size_t indent = stripIndent(line); + if (indent != rootIndent) + break; + + // Format: "Parent (offset: 0)" + if (!tryRemovePrefix(line, "Parent ")) + break; + + auto offset = parseIntAttribute(line, "Parent", "offset: "); + Type& type = parseType(input, rootIndent + 2); + + c.parents.emplace_back(type, offset * 8); + } + // No more parents for us - put back the line we just read + input = origInput; +} + +void TypeGraphParser::parseMembers(Class& c, + std::string_view& input, + size_t rootIndent) { + std::string_view origInput = input; + for (std::string_view line; getline(input, line); origInput = input) { + size_t indent = stripIndent(line); + if (indent != rootIndent) + break; + + // Format: "Member: memberName (offset: 0)" + if (!tryRemovePrefix(line, "Member: ")) + break; + + auto nameEndPos = line.find(' '); + auto name = line.substr(0, nameEndPos); + auto offset = parseIntAttribute(line, "Member", "offset: "); + auto align = tryParseIntAttribute(line, "align: "); + Type& type = parseType(input, rootIndent + 2); + + Member member{type, std::string{name}, offset * 8}; + if (align) + member.align = *align; + + c.members.push_back(member); + } + // No more members for us - put back the line we just read + input = origInput; +} + +void TypeGraphParser::parseFunctions(Class& c, + std::string_view& input, + size_t rootIndent) { + std::string_view origInput = input; + for (std::string_view line; getline(input, line); origInput = input) { + size_t indent = stripIndent(line); + if (indent != rootIndent) + break; + + // Format: "Function: funcName" + if (!tryRemovePrefix(line, "Function: ")) + break; + + auto name = line; + + Function func{std::string{name}}; + + c.functions.push_back(func); + } + // No more functions for us - put back the line we just read + input = origInput; +} + +void TypeGraphParser::parseChildren(Class& c, + std::string_view& input, + size_t rootIndent) { + std::string_view origInput = input; + for (std::string_view line; getline(input, line); origInput = input) { + size_t indent = stripIndent(line); + if (indent != rootIndent) + break; + + // Format: "Child" + if (!tryRemovePrefix(line, "Child")) + break; + + Type& type = parseType(input, rootIndent + 2); + auto* childClass = dynamic_cast(&type); + if (!childClass) + throw std::runtime_error("Invalid type for child"); + + c.children.push_back(*childClass); + } + // No more children for us - put back the line we just read + input = origInput; +} diff --git a/test/TypeGraphParser.h b/test/TypeGraphParser.h new file mode 100644 index 0000000..3a27d53 --- /dev/null +++ b/test/TypeGraphParser.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include "oi/type_graph/Types.h" + +using namespace type_graph; + +namespace type_graph { +class TypeGraph; +} // namespace type_graph + +/* + * TypeGraphParser + * + * Parses a textual type graph, as emitted by Printer. + */ +class TypeGraphParser { + public: + TypeGraphParser(TypeGraph& typeGraph) : typeGraph_(typeGraph) { + } + + void parse(std::string_view input); + + private: + TypeGraph& typeGraph_; + std::unordered_map> nodesById_; + + Type& parseType(std::string_view& input, size_t rootIndent); + template + void parseParams(T& c, std::string_view& input, size_t rootIndent); + void parseParents(Class& c, std::string_view& input, size_t rootIndent); + void parseMembers(Class& c, std::string_view& input, size_t rootIndent); + void parseFunctions(Class& c, std::string_view& input, size_t rootIndent); + void parseChildren(Class& c, std::string_view& input, size_t rootIndent); +}; diff --git a/test/test_flattener.cpp b/test/test_flattener.cpp index bed284b..e0da517 100644 --- a/test/test_flattener.cpp +++ b/test/test_flattener.cpp @@ -7,6 +7,7 @@ using namespace type_graph; TEST(FlattenerTest, NoParents) { + // No change // Original and flattened: // struct MyStruct { int n0; }; // class MyClass { @@ -14,18 +15,7 @@ TEST(FlattenerTest, NoParents) { // MyEnum e; // MyStruct mystruct; // }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto myenum = Enum{"MyEnum", 4}; - auto mystruct = Class{1, Class::Kind::Struct, "MyStruct", 4}; - auto myclass = Class{0, Class::Kind::Class, "MyClass", 12}; - - mystruct.members.push_back(Member{myint, "n0", 0}); - - myclass.members.push_back(Member{myint, "n", 0}); - myclass.members.push_back(Member{myenum, "e", 4 * 8}); - myclass.members.push_back(Member{mystruct, "mystruct", 8 * 8}); - - test(Flattener::createPass(), {myclass}, R"( + testNoChange(Flattener::createPass(), R"( [0] Class: MyClass (size: 12) Member: n (offset: 0) Primitive: int32_t @@ -49,18 +39,18 @@ TEST(FlattenerTest, OnlyParents) { // int b; // int c; // }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{0, Class::Kind::Class, "ClassA", 8}; - auto classB = Class{1, Class::Kind::Class, "ClassB", 4}; - auto classC = Class{2, Class::Kind::Class, "ClassC", 4}; - - classC.members.push_back(Member{myint, "c", 0}); - classB.members.push_back(Member{myint, "b", 0}); - - classA.parents.push_back(Parent{classB, 0}); - classA.parents.push_back(Parent{classC, 4 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassA (size: 8) + Parent (offset: 0) +[1] Class: ClassB (size: 4) + Member: b (offset: 0) + Primitive: int32_t + Parent (offset: 4) +[2] Class: ClassC (size: 4) + Member: c (offset: 0) + Primitive: int32_t +)", + R"( [0] Class: ClassA (size: 8) Member: b (offset: 0) Primitive: int32_t @@ -81,19 +71,21 @@ TEST(FlattenerTest, ParentsFirst) { // int c; // int a; // }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{0, Class::Kind::Class, "ClassA", 12}; - auto classB = Class{1, Class::Kind::Class, "ClassB", 4}; - auto classC = Class{2, Class::Kind::Class, "ClassC", 4}; - classC.members.push_back(Member{myint, "c", 0}); - classB.members.push_back(Member{myint, "b", 0}); - - classA.parents.push_back(Parent{classB, 0}); - classA.parents.push_back(Parent{classC, 4 * 8}); - classA.members.push_back(Member{myint, "a", 8 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassA (size: 12) + Parent (offset: 0) +[1] Class: ClassB (size: 4) + Member: b (offset: 0) + Primitive: int32_t + Parent (offset: 4) +[2] Class: ClassC (size: 4) + Member: c (offset: 0) + Primitive: int32_t + Member: a (offset: 8) + Primitive: int32_t +)", + R"( [0] Class: ClassA (size: 12) Member: b (offset: 0) Primitive: int32_t @@ -116,20 +108,21 @@ TEST(FlattenerTest, MembersFirst) { // int b; // int c; // }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{0, Class::Kind::Class, "ClassA", 12}; - auto classB = Class{1, Class::Kind::Class, "ClassB", 4}; - auto classC = Class{2, Class::Kind::Class, "ClassC", 4}; - classC.members.push_back(Member{myint, "c", 0}); - - classB.members.push_back(Member{myint, "b", 0}); - - classA.members.push_back(Member{myint, "a", 0}); - classA.parents.push_back(Parent{classB, 4 * 8}); - classA.parents.push_back(Parent{classC, 8 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassA (size: 12) + Parent (offset: 4) +[1] Class: ClassB (size: 4) + Member: b (offset: 0) + Primitive: int32_t + Parent (offset: 8) +[2] Class: ClassC (size: 4) + Member: c (offset: 0) + Primitive: int32_t + Member: a (offset: 0) + Primitive: int32_t +)", + R"( [0] Class: ClassA (size: 12) Member: a (offset: 0) Primitive: int32_t @@ -153,21 +146,23 @@ TEST(FlattenerTest, MixedMembersAndParents) { // int a2; // int c; // }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{0, Class::Kind::Class, "ClassA", 16}; - auto classB = Class{1, Class::Kind::Class, "ClassB", 4}; - auto classC = Class{2, Class::Kind::Class, "ClassC", 4}; - classC.members.push_back(Member{myint, "c", 0}); - - classB.members.push_back(Member{myint, "b", 0}); - - classA.parents.push_back(Parent{classB, 0}); - classA.members.push_back(Member{myint, "a1", 4 * 8}); - classA.members.push_back(Member{myint, "a2", 8 * 8}); - classA.parents.push_back(Parent{classC, 12 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassA (size: 16) + Parent (offset: 0) +[1] Class: ClassB (size: 4) + Member: b (offset: 0) + Primitive: int32_t + Parent (offset: 12) +[2] Class: ClassC (size: 4) + Member: c (offset: 0) + Primitive: int32_t + Member: a1 (offset: 4) + Primitive: int32_t + Member: a2 (offset: 8) + Primitive: int32_t +)", + R"( [0] Class: ClassA (size: 16) Member: b (offset: 0) Primitive: int32_t @@ -192,19 +187,21 @@ TEST(FlattenerTest, EmptyParent) { // int a1; // int a2; // }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{0, Class::Kind::Class, "ClassA", 12}; - auto classB = Class{1, Class::Kind::Class, "ClassB", 0}; - auto classC = Class{2, Class::Kind::Class, "ClassC", 4}; - classC.members.push_back(Member{myint, "c", 0}); - - classA.members.push_back(Member{myint, "a1", 4 * 8}); - classA.members.push_back(Member{myint, "a2", 8 * 8}); - classA.parents.push_back(Parent{classB, 0}); - classA.parents.push_back(Parent{classC, 0}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassA (size: 12) + Parent (offset: 0) +[1] Class: ClassB (size: 0) + Parent (offset: 0) +[2] Class: ClassC (size: 4) + Member: c (offset: 0) + Primitive: int32_t + Member: a1 (offset: 4) + Primitive: int32_t + Member: a2 (offset: 8) + Primitive: int32_t +)", + R"( [0] Class: ClassA (size: 12) Member: c (offset: 0) Primitive: int32_t @@ -229,24 +226,25 @@ TEST(FlattenerTest, TwoDeep) { // int c; // int a; // }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{0, Class::Kind::Class, "ClassA", 16}; - auto classB = Class{1, Class::Kind::Class, "ClassB", 8}; - auto classC = Class{2, Class::Kind::Class, "ClassC", 4}; - auto classD = Class{3, Class::Kind::Class, "ClassD", 4}; - classD.members.push_back(Member{myint, "d", 0}); - - classC.members.push_back(Member{myint, "c", 0}); - - classB.parents.push_back(Parent{classD, 0}); - classB.members.push_back(Member{myint, "b", 4 * 8}); - - classA.parents.push_back(Parent{classB, 0}); - classA.parents.push_back(Parent{classC, 8 * 8}); - classA.members.push_back(Member{myint, "a", 12 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassA (size: 16) + Parent (offset: 0) +[1] Class: ClassB (size: 8) + Parent (offset: 0) +[2] Class: ClassD (size: 4) + Member: d (offset: 0) + Primitive: int32_t + Member: b (offset: 4) + Primitive: int32_t + Parent (offset: 8) +[3] Class: ClassC (size: 4) + Member: c (offset: 0) + Primitive: int32_t + Member: a (offset: 12) + Primitive: int32_t +)", + R"( [0] Class: ClassA (size: 16) Member: d (offset: 0) Primitive: int32_t @@ -272,21 +270,23 @@ TEST(FlattenerTest, DiamondInheritance) { // int c1; // int a; // }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{0, Class::Kind::Class, "ClassA", 16}; - auto classB = Class{1, Class::Kind::Class, "ClassB", 8}; - auto classC = Class{2, Class::Kind::Class, "ClassC", 4}; - classC.members.push_back(Member{myint, "c", 0}); - - classB.parents.push_back(Parent{classC, 0}); - classB.members.push_back(Member{myint, "b", 4 * 8}); - - classA.parents.push_back(Parent{classB, 0}); - classA.parents.push_back(Parent{classC, 8 * 8}); - classA.members.push_back(Member{myint, "a", 12 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassA (size: 16) + Parent (offset: 0) +[1] Class: ClassB (size: 8) + Parent (offset: 0) +[2] Class: ClassC (size: 4) + Member: c (offset: 0) + Primitive: int32_t + Member: b (offset: 4) + Primitive: int32_t + Parent (offset: 8) + [2] + Member: a (offset: 12) + Primitive: int32_t +)", + R"( [0] Class: ClassA (size: 16) Member: c (offset: 0) Primitive: int32_t @@ -308,20 +308,21 @@ TEST(FlattenerTest, Member) { // Flattened: // class B { int c; int b; }; // Class A { int a; B b; }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{0, Class::Kind::Class, "ClassA", 12}; - auto classB = Class{1, Class::Kind::Class, "ClassB", 8}; - auto classC = Class{2, Class::Kind::Class, "ClassC", 4}; - classC.members.push_back(Member{myint, "c", 0}); - - classB.parents.push_back(Parent{classC, 0}); - classB.members.push_back(Member{myint, "b", 4 * 8}); - - classA.members.push_back(Member{myint, "a", 0}); - classA.members.push_back(Member{classB, "b", 4 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassA (size: 12) + Member: a (offset: 0) + Primitive: int32_t + Member: b (offset: 4) +[1] Class: ClassB (size: 8) + Parent (offset: 0) +[2] Class: ClassC (size: 4) + Member: c (offset: 0) + Primitive: int32_t + Member: b (offset: 4) + Primitive: int32_t +)", + R"( [0] Class: ClassA (size: 12) Member: a (offset: 0) Primitive: int32_t @@ -343,20 +344,21 @@ TEST(FlattenerTest, MemberOfParent) { // Flattened: // class C { int c; }; // class A { int b; C c; int a; }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{0, Class::Kind::Class, "ClassA", 12}; - auto classB = Class{1, Class::Kind::Class, "ClassB", 8}; - auto classC = Class{2, Class::Kind::Class, "ClassC", 4}; - classC.members.push_back(Member{myint, "c", 0}); - - classB.members.push_back(Member{myint, "b", 0}); - classB.members.push_back(Member{classC, "c", 4 * 8}); - - classA.parents.push_back(Parent{classB, 0}); - classA.members.push_back(Member{myint, "a", 8 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassA (size: 12) + Parent (offset: 0) +[1] Class: ClassB (size: 8) + Member: b (offset: 0) + Primitive: int32_t + Member: c (offset: 4) +[2] Class: ClassC (size: 4) + Member: c (offset: 0) + Primitive: int32_t + Member: a (offset: 8) + Primitive: int32_t +)", + R"( [0] Class: ClassA (size: 12) Member: b (offset: 0) Primitive: int32_t @@ -378,20 +380,21 @@ TEST(FlattenerTest, ContainerParam) { // Flattened: // class A { int b; int a; }; // std::vector - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{1, Class::Kind::Class, "ClassA", 8}; - auto classB = Class{2, Class::Kind::Class, "ClassB", 4}; - auto container = getVector(); - classB.members.push_back(Member{myint, "b", 0}); - - classA.parents.push_back(Parent{classB, 0}); - classA.members.push_back(Member{myint, "a", 4 * 8}); - - container.templateParams.push_back(TemplateParam{classA}); - container.templateParams.push_back(TemplateParam{myint}); - - test(Flattener::createPass(), {container}, R"( + test(Flattener::createPass(), R"( +[0] Container: std::vector (size: 24) + Param +[1] Class: ClassA (size: 8) + Parent (offset: 0) +[2] Class: ClassB (size: 4) + Member: b (offset: 0) + Primitive: int32_t + Member: a (offset: 4) + Primitive: int32_t + Param + Primitive: int32_t +)", + R"( [0] Container: std::vector (size: 24) Param [1] Class: ClassA (size: 8) @@ -409,18 +412,18 @@ TEST(FlattenerTest, Array) { // class B { int b; }; // class A : B { int a; }; // A[5] - auto myint = Primitive{Primitive::Kind::Int32}; - auto classB = Class{2, Class::Kind::Class, "ClassB", 4}; - classB.members.push_back(Member{myint, "b", 0}); - - auto classA = Class{1, Class::Kind::Class, "ClassA", 8}; - classA.parents.push_back(Parent{classB, 0}); - classA.members.push_back(Member{myint, "a", 4 * 8}); - - auto arrayA = Array{0, classA, 5}; - - test(Flattener::createPass(), {arrayA}, R"( + test(Flattener::createPass(), R"( +[0] Array: (length: 5) +[1] Class: ClassA (size: 8) + Parent (offset: 0) +[2] Class: ClassB (size: 4) + Member: b (offset: 0) + Primitive: int32_t + Member: a (offset: 4) + Primitive: int32_t +)", + R"( [0] Array: (length: 5) [1] Class: ClassA (size: 8) Member: b (offset: 0) @@ -435,17 +438,18 @@ TEST(FlattenerTest, Typedef) { // class B { int b; }; // class A : B { int a; }; // using aliasA = A; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classB = Class{2, Class::Kind::Class, "ClassB", 4}; - classB.members.push_back(Member{myint, "b", 0}); - auto classA = Class{1, Class::Kind::Class, "ClassA", 8}; - classA.parents.push_back(Parent{classB, 0}); - classA.members.push_back(Member{myint, "a", 4 * 8}); - - auto aliasA = Typedef{0, "aliasA", classA}; - - test(Flattener::createPass(), {aliasA}, R"( + test(Flattener::createPass(), R"( +[0] Typedef: aliasA +[1] Class: ClassA (size: 8) + Parent (offset: 0) +[2] Class: ClassB (size: 4) + Member: b (offset: 0) + Primitive: int32_t + Member: a (offset: 4) + Primitive: int32_t +)", + R"( [0] Typedef: aliasA [1] Class: ClassA (size: 8) Member: b (offset: 0) @@ -460,17 +464,18 @@ TEST(FlattenerTest, TypedefParent) { // class B { int b; }; // using aliasB = B; // class A : aliasB { int a; }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classB = Class{1, Class::Kind::Class, "ClassB", 4}; - classB.members.push_back(Member{myint, "b", 0}); - auto aliasB = Typedef{2, "aliasB", classB}; - - auto classA = Class{0, Class::Kind::Class, "ClassA", 8}; - classA.parents.push_back(Parent{aliasB, 0}); - classA.members.push_back(Member{myint, "a", 4 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassA (size: 8) + Parent (offset: 0) +[1] Typedef: aliasB +[2] Class: ClassB (size: 4) + Member: b (offset: 0) + Primitive: int32_t + Member: a (offset: 4) + Primitive: int32_t +)", + R"( [0] Class: ClassA (size: 8) Member: b (offset: 0) Primitive: int32_t @@ -483,7 +488,7 @@ TEST(FlattenerTest, Pointer) { // Original: // class B { int b; }; // class A : B { int a; }; - // class C { A a; }; + // class C { A* a; }; auto myint = Primitive{Primitive::Kind::Int32}; auto classB = Class{3, Class::Kind::Class, "ClassB", 4}; @@ -497,7 +502,19 @@ TEST(FlattenerTest, Pointer) { auto classC = Class{0, Class::Kind::Class, "ClassC", 8}; classC.members.push_back(Member{ptrA, "a", 0}); - test(Flattener::createPass(), {classC}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassC (size: 8) + Member: a (offset: 0) +[1] Pointer +[2] Class: ClassA (size: 8) + Parent (offset: 0) +[3] Class: ClassB (size: 4) + Member: b (offset: 0) + Primitive: int32_t + Member: a (offset: 4) + Primitive: int32_t +)", + R"( [0] Class: ClassC (size: 8) Member: a (offset: 0) [1] Pointer @@ -511,7 +528,7 @@ TEST(FlattenerTest, Pointer) { TEST(FlattenerTest, PointerCycle) { // Original: - // class B { A a }; + // class B { A* a }; // class A { B b; }; // // Flattened: @@ -522,7 +539,16 @@ TEST(FlattenerTest, PointerCycle) { classA.members.push_back(Member{classB, "b", 0}); classB.members.push_back(Member{ptrA, "a", 0}); - test(Flattener::createPass(), {classA, classB}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassA (size: 69) + Member: b (offset: 0) +[1] Class: ClassB (size: 69) + Member: a (offset: 0) +[2] Pointer + [0] + [1] +)", + R"( [0] Class: ClassA (size: 69) Member: b (offset: 0) [1] Class: ClassB (size: 69) @@ -538,23 +564,8 @@ TEST(FlattenerTest, Alignment) { // class alignas(16) C { int c; }; // class B { alignas(8) int b; }; // class A : B, C { int a; }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{0, Class::Kind::Class, "ClassA", 12}; - auto classB = Class{1, Class::Kind::Class, "ClassB", 4}; - auto classC = Class{2, Class::Kind::Class, "ClassC", 4}; - classC.setAlign(16); - classC.members.push_back(Member{myint, "c", 0}); - - Member memberB{myint, "b", 0}; - memberB.align = 8; - classB.members.push_back(memberB); - - classA.parents.push_back(Parent{classB, 0}); - classA.parents.push_back(Parent{classC, 4 * 8}); - classA.members.push_back(Member{myint, "a", 8 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( [0] Class: ClassA (size: 12) Parent (offset: 0) [1] Class: ClassB (size: 4) @@ -594,7 +605,17 @@ TEST(FlattenerTest, Functions) { classB.functions.push_back(Function{"funcB"}); classC.functions.push_back(Function{"funcC"}); - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassA (size: 0) + Parent (offset: 0) +[1] Class: ClassB (size: 0) + Function: funcB + Parent (offset: 0) +[2] Class: ClassC (size: 0) + Function: funcC + Function: funcA +)", + R"( [0] Class: ClassA (size: 0) Function: funcA Function: funcB @@ -607,21 +628,23 @@ TEST(FlattenerTest, Children) { // class C { int c; }; // class B { int b; }; // class A : B, C { }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{1, Class::Kind::Class, "ClassA", 8}; - auto classB = Class{0, Class::Kind::Class, "ClassB", 4}; - auto classC = Class{2, Class::Kind::Class, "ClassC", 4}; - classC.members.push_back(Member{myint, "c", 0}); - classB.members.push_back(Member{myint, "b", 0}); - - classA.parents.push_back(Parent{classB, 0}); - classA.parents.push_back(Parent{classC, 4 * 8}); - - classB.children.push_back(classA); - classC.children.push_back(classA); - - test(Flattener::createPass(), {classB}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassB (size: 4) + Member: b (offset: 0) + Primitive: int32_t + Child +[1] Class: ClassA (size: 8) + Parent (offset: 0) + [0] + Parent (offset: 4) +[2] Class: ClassC (size: 4) + Member: c (offset: 0) + Primitive: int32_t + Child + [1] +)", + R"( [0] Class: ClassB (size: 4) Member: b (offset: 0) Primitive: int32_t @@ -640,28 +663,31 @@ TEST(FlattenerTest, ChildrenTwoDeep) { // class C { int c; }; // class B : D { int b; }; // class A : B, C { int a; }; - auto myint = Primitive{Primitive::Kind::Int32}; - auto classA = Class{2, Class::Kind::Class, "ClassA", 16}; - auto classB = Class{1, Class::Kind::Class, "ClassB", 8}; - auto classC = Class{3, Class::Kind::Class, "ClassC", 4}; - auto classD = Class{0, Class::Kind::Class, "ClassD", 4}; - classD.members.push_back(Member{myint, "d", 0}); - - classC.members.push_back(Member{myint, "c", 0}); - - classB.parents.push_back(Parent{classD, 0}); - classB.members.push_back(Member{myint, "b", 4 * 8}); - - classA.parents.push_back(Parent{classB, 0}); - classA.parents.push_back(Parent{classC, 8 * 8}); - classA.members.push_back(Member{myint, "a", 12 * 8}); - - classD.children.push_back(classB); - classB.children.push_back(classA); - classC.children.push_back(classA); - - test(Flattener::createPass(), {classD}, R"( + test(Flattener::createPass(), R"( +[0] Class: ClassD (size: 4) + Member: d (offset: 0) + Primitive: int32_t + Child +[1] Class: ClassB (size: 8) + Parent (offset: 0) + [0] + Member: b (offset: 4) + Primitive: int32_t + Child +[2] Class: ClassA (size: 16) + Parent (offset: 0) + [1] + Parent (offset: 8) +[3] Class: ClassC (size: 4) + Member: c (offset: 0) + Primitive: int32_t + Child + [2] + Member: a (offset: 12) + Primitive: int32_t +)", + R"( [0] Class: ClassD (size: 4) Member: d (offset: 0) Primitive: int32_t @@ -687,16 +713,7 @@ TEST(FlattenerTest, ChildrenTwoDeep) { } TEST(FlattenerTest, ParentContainer) { - auto myint = Primitive{Primitive::Kind::Int32}; - - auto vector = getVector(); - vector.templateParams.push_back(TemplateParam{myint}); - - auto classA = Class{0, Class::Kind::Class, "ClassA", 32}; - classA.parents.push_back(Parent{vector, 0}); - classA.members.push_back(Member{myint, "a", 24 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( [0] Class: ClassA (size: 32) Parent (offset: 0) [1] Container: std::vector (size: 24) @@ -717,16 +734,7 @@ TEST(FlattenerTest, ParentContainer) { } TEST(FlattenerTest, ParentTwoContainers) { - auto myint = Primitive{Primitive::Kind::Int32}; - - auto vector = getVector(); - vector.templateParams.push_back(TemplateParam{myint}); - - auto classA = Class{0, Class::Kind::Class, "ClassA", 48}; - classA.parents.push_back(Parent{vector, 0}); - classA.parents.push_back(Parent{vector, 24 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( [0] Class: ClassA (size: 48) Parent (offset: 0) [1] Container: std::vector (size: 24) @@ -747,19 +755,7 @@ TEST(FlattenerTest, ParentTwoContainers) { } TEST(FlattenerTest, ParentClassAndContainer) { - auto myint = Primitive{Primitive::Kind::Int32}; - - auto vector = getVector(); - vector.templateParams.push_back(TemplateParam{myint}); - - auto classB = Class{1, Class::Kind::Class, "ClassB", 4}; - classB.members.push_back(Member{myint, "b", 0}); - - auto classA = Class{0, Class::Kind::Class, "ClassA", 32}; - classA.parents.push_back(Parent{classB, 0}); - classA.parents.push_back(Parent{vector, 8 * 8}); - - test(Flattener::createPass(), {classA}, R"( + test(Flattener::createPass(), R"( [0] Class: ClassA (size: 32) Parent (offset: 0) [1] Class: ClassB (size: 4) @@ -782,33 +778,7 @@ TEST(FlattenerTest, ParentClassAndContainer) { } TEST(FlattenerTest, AllocatorParamInParent) { - ContainerInfo mapInfo{"std::map", STD_MAP_TYPE, "utility"}; - mapInfo.stubTemplateParams = {2, 3}; - - Primitive myint{Primitive::Kind::Int32}; - - auto pair = getPair(3); - pair.templateParams.push_back(TemplateParam{myint, {Qualifier::Const}}); - pair.templateParams.push_back(TemplateParam{myint}); - - Class myallocBase{2, Class::Kind::Struct, - "MyAllocBase>", 1}; - myallocBase.templateParams.push_back(TemplateParam{pair}); - myallocBase.functions.push_back(Function{"allocate"}); - myallocBase.functions.push_back(Function{"deallocate"}); - - Class myalloc{1, Class::Kind::Struct, "MyAlloc>", - 1}; - myalloc.parents.push_back(Parent{myallocBase, 0}); - myalloc.functions.push_back(Function{"allocate"}); - myalloc.functions.push_back(Function{"deallocate"}); - - Container map{0, mapInfo, 24}; - map.templateParams.push_back(TemplateParam{myint}); - map.templateParams.push_back(TemplateParam{myint}); - map.templateParams.push_back(TemplateParam{myalloc}); - - test(Flattener::createPass(), {map}, R"( + test(Flattener::createPass(), R"( [0] Container: std::map (size: 24) Param Primitive: int32_t @@ -853,26 +823,7 @@ TEST(FlattenerTest, AllocatorParamInParent) { } TEST(FlattenerTest, AllocatorUnfixableNoParent) { - Primitive myint{Primitive::Kind::Int32}; - - Class myalloc{1, Class::Kind::Struct, "MyAlloc", 1}; - myalloc.functions.push_back(Function{"allocate"}); - myalloc.functions.push_back(Function{"deallocate"}); - - auto vector = getVector(); - vector.templateParams.push_back(TemplateParam{myint}); - vector.templateParams.push_back(TemplateParam{myalloc}); - - test(Flattener::createPass(), {vector}, R"( -[0] Container: std::vector (size: 24) - Param - Primitive: int32_t - Param -[1] Struct: MyAlloc (size: 1) - Function: allocate - Function: deallocate -)", - R"( + testNoChange(Flattener::createPass(), R"( [0] Container: std::vector (size: 24) Param Primitive: int32_t @@ -885,26 +836,7 @@ TEST(FlattenerTest, AllocatorUnfixableNoParent) { TEST(FlattenerTest, AllocatorUnfixableParentNotClass) { // This could be supported if need-be, we just don't do it yet - Primitive myint{Primitive::Kind::Int32}; - - auto pair = getPair(3); - pair.templateParams.push_back(TemplateParam{myint, {Qualifier::Const}}); - pair.templateParams.push_back(TemplateParam{myint}); - - ContainerInfo stdAllocatorInfo{"std::allocator", DUMMY_TYPE, "memory"}; - Container stdAllocator{2, stdAllocatorInfo, 1}; - stdAllocator.templateParams.push_back(TemplateParam{pair}); - - Class myalloc{1, Class::Kind::Struct, "MyAlloc", 1}; - myalloc.parents.push_back(Parent{stdAllocator, 0}); - myalloc.functions.push_back(Function{"allocate"}); - myalloc.functions.push_back(Function{"deallocate"}); - - auto vector = getVector(); - vector.templateParams.push_back(TemplateParam{myint}); - vector.templateParams.push_back(TemplateParam{myalloc}); - - test(Flattener::createPass(), {vector}, R"( + test(Flattener::createPass(), R"( [0] Container: std::vector (size: 24) Param Primitive: int32_t @@ -943,22 +875,7 @@ TEST(FlattenerTest, AllocatorUnfixableParentNotClass) { } TEST(FlattenerTest, AllocatorUnfixableParentNoParams) { - Primitive myint{Primitive::Kind::Int32}; - - Class myallocBase{2, Class::Kind::Struct, "MyAllocBase", 1}; - myallocBase.functions.push_back(Function{"allocate"}); - myallocBase.functions.push_back(Function{"deallocate"}); - - Class myalloc{1, Class::Kind::Struct, "MyAlloc", 1}; - myalloc.parents.push_back(Parent{myallocBase, 0}); - myalloc.functions.push_back(Function{"allocate"}); - myalloc.functions.push_back(Function{"deallocate"}); - - auto vector = getVector(); - vector.templateParams.push_back(TemplateParam{myint}); - vector.templateParams.push_back(TemplateParam{myalloc}); - - test(Flattener::createPass(), {vector}, R"( + test(Flattener::createPass(), R"( [0] Container: std::vector (size: 24) Param Primitive: int32_t @@ -985,27 +902,7 @@ TEST(FlattenerTest, AllocatorUnfixableParentNoParams) { } TEST(FlattenerTest, AllocatorUnfixableParentParamIsValue) { - ContainerInfo mapInfo{"std::map", STD_MAP_TYPE, "utility"}; - mapInfo.stubTemplateParams = {2, 3}; - - Primitive myint{Primitive::Kind::Int32}; - - Class myallocBase{2, Class::Kind::Struct, "MyAllocBase", 1}; - myallocBase.templateParams.push_back(TemplateParam{"123"}); - myallocBase.functions.push_back(Function{"allocate"}); - myallocBase.functions.push_back(Function{"deallocate"}); - - Class myalloc{1, Class::Kind::Struct, "MyAlloc", 1}; - myalloc.parents.push_back(Parent{myallocBase, 0}); - myalloc.functions.push_back(Function{"allocate"}); - myalloc.functions.push_back(Function{"deallocate"}); - - Container map{0, mapInfo, 24}; - map.templateParams.push_back(TemplateParam{myint}); - map.templateParams.push_back(TemplateParam{myint}); - map.templateParams.push_back(TemplateParam{myalloc}); - - test(Flattener::createPass(), {map}, R"( + test(Flattener::createPass(), R"( [0] Container: std::map (size: 24) Param Primitive: int32_t @@ -1038,16 +935,7 @@ TEST(FlattenerTest, AllocatorUnfixableParentParamIsValue) { } TEST(FlattenerTest, ClassParam) { - auto myint = Primitive{Primitive::Kind::Int32}; - auto mychild = Class{1, Class::Kind::Class, "MyChild", 4}; - auto myparent = Class{2, Class::Kind::Class, "MyParent", 4}; - myparent.members.push_back(Member{myint, "a", 0}); - mychild.parents.push_back(Parent{myparent, 0}); - - auto myclass = Class{0, Class::Kind::Class, "MyClass", 4}; - myclass.templateParams.push_back(TemplateParam{mychild}); - - test(Flattener::createPass(), {myclass}, R"( + test(Flattener::createPass(), R"( [0] Class: MyClass (size: 4) Param [1] Class: MyChild (size: 4) diff --git a/test/type_graph_utils.cpp b/test/type_graph_utils.cpp index 89b41b5..9cfbb10 100644 --- a/test/type_graph_utils.cpp +++ b/test/type_graph_utils.cpp @@ -6,6 +6,7 @@ #include "oi/type_graph/PassManager.h" #include "oi/type_graph/Printer.h" #include "oi/type_graph/TypeGraph.h" +#include "test/TypeGraphParser.h" using type_graph::Container; using type_graph::NodeId; @@ -26,10 +27,39 @@ void check(const std::vector>& types, printer.print(type); } - expected.remove_prefix(1); // Remove initial '\n' + if (expected[0] == '\n') + expected.remove_prefix(1); // Remove initial '\n' ASSERT_EQ(expected, out.str()) << "Test failure " << comment; } +void test(type_graph::Pass pass, + std::string_view input, + std::string_view expectedAfter) { + input.remove_prefix(1); // Remove initial '\n' + TypeGraph typeGraph; + TypeGraphParser parser{typeGraph}; + parser.parse(input); + + // Validate input formatting + check(typeGraph.rootTypes(), input, " parsing input graph"); + + // Run pass and check results + test(pass, typeGraph.rootTypes(), expectedAfter); +} + +void testNoChange(type_graph::Pass pass, std::string_view input) { + input.remove_prefix(1); // Remove initial '\n' + TypeGraph typeGraph; + TypeGraphParser parser{typeGraph}; + parser.parse(input); + + // Validate input formatting + check(typeGraph.rootTypes(), input, " parsing input graph"); + + // Run pass and check results + test(pass, typeGraph.rootTypes(), input); +} + void test(type_graph::Pass pass, std::vector> rootTypes, std::string_view expectedBefore, diff --git a/test/type_graph_utils.h b/test/type_graph_utils.h index f86cde9..b7caf0b 100644 --- a/test/type_graph_utils.h +++ b/test/type_graph_utils.h @@ -14,6 +14,12 @@ void check(const std::vector>& types, std::string_view expected, std::string_view comment); +void test(type_graph::Pass pass, + std::string_view input, + std::string_view expectedAfter); + +void testNoChange(type_graph::Pass pass, std::string_view input); + void test(type_graph::Pass pass, std::vector> rootTypes, std::string_view expectedBefore,