From 67c21e5c6b06bd8cdef8fd9f906b2c4e4c457a92 Mon Sep 17 00:00:00 2001 From: gk646 Date: Thu, 9 May 2024 23:55:48 +0200 Subject: [PATCH] Import Update 3 added getNodes functions every node file should now be parsable completely and every use case works -> quality of life functions might be missing split up tests added more tests every import function is tested tests run on gcc aswell added test ci have to figure out dependencies --- .clang-format | 2 +- .github/workflows/cmake-multi-platform.yml | 59 +++++ .gitignore | 2 +- cmake/LoadLocalLibs.cmake | 2 +- .../context/impl/ContextPersist.cpp | 2 +- src/raynodes/import/RnImport.h | 246 ++++++++++++------ src/raynodes/node/Node.h | 1 + test/CMakeLists.txt | 3 +- test/ImportTest.cpp | 101 ++++++- test/PersistTest.cpp | 4 +- test/res/Test1.rn | 12 +- 11 files changed, 328 insertions(+), 106 deletions(-) create mode 100644 .github/workflows/cmake-multi-platform.yml diff --git a/.clang-format b/.clang-format index a1b499d..b7b5a73 100644 --- a/.clang-format +++ b/.clang-format @@ -38,7 +38,7 @@ BreakBeforeBinaryOperators: NonAssignment BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeColon BreakInheritanceList: BeforeColon -ColumnLimit: 110 +ColumnLimit: 117 CompactNamespaces: false ContinuationIndentWidth: 4 Cpp11BracedListStyle: true diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml new file mode 100644 index 0000000..37a2848 --- /dev/null +++ b/.github/workflows/cmake-multi-platform.yml @@ -0,0 +1,59 @@ +name: CMake on multiple platforms + +on: + + +jobs: + build-and-test: + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + build_type: [Release] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + + steps: + - uses: actions/checkout@v4 + + - name: Cache CMake builds + uses: actions/cache@v2 + with: + path: ${{ github.workspace }}/build + key: ${{ runner.os }}-build-${{ hashFiles('**/CMakeLists.txt', '**/*.cpp', '**/*.h') }} + restore-keys: | + ${{ runner.os }}-build- + + - name: Configure CMake + run: | + cmake -B build \ + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \ + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} \ + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ + -S . + working-directory: ${{ github.workspace }} + + - name: Build + run: cmake --build build --config ${{ matrix.build_type }} + working-directory: ${{ github.workspace }} + + - name: Run Tests + run: ctest -C ${{ matrix.build_type }} --output-on-failure + working-directory: ${{ github.workspace }}/build + + - name: Upload Test Results + if: always() + uses: actions/upload-artifact@v2 + with: + name: Test Results + path: ${{ github.workspace }}/build/Testing/**/*.xml \ No newline at end of file diff --git a/.gitignore b/.gitignore index aefc735..2df33ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -cmake-* +/cmake-* .idea saves test/res/__GEN1__.rn diff --git a/cmake/LoadLocalLibs.cmake b/cmake/LoadLocalLibs.cmake index b7beccf..f0ba252 100644 --- a/cmake/LoadLocalLibs.cmake +++ b/cmake/LoadLocalLibs.cmake @@ -1,4 +1,4 @@ -set(DEPENDENCIES_PATH "C:/Users/gk646/Documents/Libaries") +set(DEPENDENCIES_PATH "/mnt/c/Users/gk646/Documents/Libaries") include_directories( "${DEPENDENCIES_PATH}/raylib-master/src" diff --git a/src/raynodes/application/context/impl/ContextPersist.cpp b/src/raynodes/application/context/impl/ContextPersist.cpp index 8d34270..f7ea404 100644 --- a/src/raynodes/application/context/impl/ContextPersist.cpp +++ b/src/raynodes/application/context/impl/ContextPersist.cpp @@ -280,7 +280,7 @@ bool Persist::loadFromFile(EditorContext& ec) { const auto* path = openedFilePath.c_str(); - fopen_s(&file, path, "rb"); + file = fopen( path, "rb"); if (file == nullptr) { fprintf(stderr, "Unable to open file %s\n", path); diff --git a/src/raynodes/import/RnImport.h b/src/raynodes/import/RnImport.h index 6efc0d7..6ae500f 100644 --- a/src/raynodes/import/RnImport.h +++ b/src/raynodes/import/RnImport.h @@ -22,22 +22,27 @@ #define IMPORTER_H #include +#include #include -#include -#include -#include +#include // for strlen() on gcc // ============================== // IMPORT INTERFACE // ============================== + // This is a Cross-Platform, Single Header, No Dependencies, util header for importing the ".rn" format // Its meant to be a optimized and memory efficient abstraction over raw parsing // This interface provide easy and structured random access to connections, nodes, components and their data -// The focus is on reducing allocations and memory usage with more runtime overhead +// The focus is on reducing memory footprint with more runtime overhead +// For most operations it provides both allocating and non allocating return containers (vector or array) +// ................................................................................................ +// It basically just does one pass and saves byte markers where information is stored within the file +// This later allows for fast, random and frequent access without any more internal allocations +// ................................................................................................ +// The RnImport interface also abstracts away the fileType so new filetypes like JSON or .csv can be added -// Current memory footprint (bytes): -// Total Memory = FileSize + nodeCount * NodeData(32) + connectionCount * ConnectionData(8) + Misc(100)(vectors,variables...) -// ComponentData = COMPONENTS_PER_NODE * ComponentData(4) + NameHash(4) +// Current memory footprint thats dynamically allocated (bytes): +// Total Memory = FileSize + nodes * NodeData(8) + connections * ConnectionData(8) + nodeTypes * NodeTemplate(2) namespace raynodes { struct RnImport; @@ -78,7 +83,7 @@ enum DataType : uint8_t { VECTOR_2, }; -struct ConnectionData { +struct Connection { NodeID fromNode; // Value between 0 and nodeCnt-1 or ]0 - nodeCnt] or (0 - nodeCnt] int8_t fromComponent; // -1 if its a node-to-node connection uint8_t fromPin; @@ -88,52 +93,29 @@ struct ConnectionData { [[nodiscard]] bool isValid() const { return fromNode != UINT16_MAX; } }; -class ComponentData { - // This uses a single 32bit number to store both the startByte and the ComponentLabel - // Current split 24/8 - uint32_t dataHolder = 0; - [[nodiscard]] ComponentIndex getID() const { - // Extract the lower 8 bits - return static_cast(dataHolder & 0xFF); - } - [[nodiscard]] ByteIndex getStartByte() const { - // Shift right by 8 bits and then mask to get the original 24 bits - return static_cast((dataHolder >> 8) & 0xFFFFFF); - } - - public: - // Internal function - dont call it - static void AssignData(ComponentData& comp, ByteIndex startByte, ComponentIndex id) { - // Mask and shift `startByte` into the upper 24 bits, and `id` into the lower 8 bits - comp.dataHolder = (static_cast(startByte) & 0xFFFFFF) << 8; - comp.dataHolder |= static_cast(id) & 0xFF; - } -}; - struct NodeData { ByteIndex startByte = 0; - const NodeID nodeID = 0; - const TemplateID templateID = 0; - // Returns the data of the index-th save call made inside the component - 0 based - // io_save(file, first), io_save(file, second), io_save(file, third); - // IMPORTANT: strings will be returned as allocated "char*" + const NodeID id = 0; + const TemplateID tID = 0; + template - auto getData(char* file, ComponentIndex id, int index) const; + auto getData(char* fileData, ComponentIndex id, int index) const; }; struct NodeTemplate { const uint16_t startByte = 0; ComponentIndex getCompIndex(char* fileData, const char* label) const; + bool isNodeName(const char* fileData, const char* nodeName) const; }; // All members are openly accessible to allow custom tinkering - only use them if you know what your doing! struct RnImport final { - uint16_t templateCnt = 0; // Amount of templates - NodeTemplate* templates = nullptr; // Node templates - uint16_t nodeCnt = 0; // Amount of nodes - NodeData* nodes = nullptr; // Internal data holder - uint16_t connCnt = 0; // Amount of connections - ConnectionData* connections = nullptr; // Internal data holder + uint16_t templateCnt = 0; // Amount of templates + NodeTemplate* templates = nullptr; // Node templates + uint16_t nodeCnt = 0; // Amount of nodes + NodeData* nodes = nullptr; // Internal data holder + uint16_t connCnt = 0; // Amount of connections + Connection* connections = nullptr; // Internal data holder char* fileData = nullptr; // Allocated string containing the whole file data uint32_t size = 0; // Size of fileData @@ -157,7 +139,7 @@ struct RnImport final { // Failure: returns a type appropriate dummy (false, 0, 0.0F, empty string). // IMPORTANT: strings will be returned as std::string() template - [[nodiscard]] auto getComponentData(NodeID nodeID, const char* label, int saveIndex = 0) const; + [[nodiscard]] auto getComponentData(NodeID node, const char* label, int saveIndex = 0) const; // Returns the data of the component at the specified index within the node - 0 based indexing // Its up to the user to know the correct type @@ -165,25 +147,48 @@ struct RnImport final { // Failure: returns a type appropriate dummy (false, 0, 0.0F, empty string). // IMPORTANT: strings will be returned as std::string() template - [[nodiscard]] auto getComponentData(NodeID nodeID, int componentIndex, int saveIndex = 0) const; + [[nodiscard]] auto getComponentData(NodeID node, int component, int saveIndex = 0) const; + + // Returns an array of outgoing connections for a specified node + // If `component` or `pin` is not specified, all possible connections are included + // Use component = -1 to get the node-to-node connection pins + // Failure: array will always be filled - empty connections will contain max values - use Connection::isValid() + template + [[nodiscard]] std::array getConnectionsOut(NodeID node, int component = -2, int pin = -1) const; + + // Returns a vector of outgoing connections for a specified node - reserves 5 upfront + // If `component` or `pin` is not specified, all possible connections are included + // Use component = -1 to get the node-to-node connection pins + // Failure: will never fail - vector is empty or filled with valid connections + [[nodiscard]] std::vector getConnectionsOut(NodeID node, int component = -2, int pin = -1) const; + + // Returns an array of incoming connections for a specified node + // If `component` or `pin` is not specified, all possible connections are included + // Use component = -1 to get the node-to-node connection pins + // Failure: array will always be filled - empty connections will contain max values - use Connection::isValid() + template + [[nodiscard]] std::array getConnectionsIn(NodeID node, int component = -2, int pin = -1) const; - // Returns the connection to the first found node from the specified component - 0 based indexing - // Index can be -1 to get the node-to-node connection - // Useful if a component only has 1 connection - // Failure: returns connection with all values set to the max - use isValid() - [[nodiscard]] ConnectionData getConnection(NodeID nodeID, int componentIndex) const; + // Returns a vector of incoming connections for a specified node - reserves 5 upfront + // If `component` or `pin` is not specified, all possible connections are included + // Use component = -1 to get the node-to-node connection pins + // Failure: will never fail - vector is empty or filled with valid connections + [[nodiscard]] std::vector getConnectionsIn(NodeID node, int component = -2, int pin = -1) const; - // Returns and arry of connections from the specified component - 0 based indexing - // Index can be -1 to get the node-to-node connections - // Failure: array will always be filled - empty connections will contain max values - use isValid() + // Returns an array of nodes matching the name + // Failure: array will always be filled - empty values will be UINT16_MAX template - [[nodiscard]] std::array getConnections(NodeID nodeID, int componentIndex) const; + [[nodiscard]] std::array getNodes(const char* name) const; + + // Returns a vector of nodes matching the name + // Failure: array will always be filled - empty values will be UINT16_MAX + [[nodiscard]] std::vector getNodes(const char* name) const; private: [[nodiscard]] const NodeData* getNodeData(const NodeID id) const { if (id >= nodeCnt) return nullptr; for (int i = 0; i < nodeCnt; ++i) { - if (nodes[i].nodeID == id) return &nodes[i]; + if (nodes[i].id == id) return &nodes[i]; } return nullptr; } @@ -293,7 +298,7 @@ bool str_cmp_stop_at(const char* newArg, const char* control, int max, char stop //-----------HELPER_CLASSES-----------// namespace raynodes { template -auto NodeData::getData(char* fileData, ComponentIndex id, int index) const { +auto NodeData::getData(char* fileData, const ComponentIndex id, const int index) const { char* workPtr = fileData + startByte; SkipCharacter(workPtr, USED_LINE_SEPARATOR, 1); // Skip the node id @@ -313,7 +318,7 @@ auto NodeData::getData(char* fileData, ComponentIndex id, int index) const { } else if constexpr (dt == INTEGER) { return std::strtoll(workPtr, nullptr, 10); } else if constexpr (dt == FLOAT) { - return std::strtof(workPtr, nullptr); + return std::strtod(workPtr, nullptr); } else if constexpr (dt == VECTOR_2) { static_assert(dt == STRING, "Not yet supported"); } else if constexpr (dt == VECTOR_3) { @@ -322,20 +327,26 @@ auto NodeData::getData(char* fileData, ComponentIndex id, int index) const { static_assert(dt == STRING, "Wont be supported... How?"); } } -inline ComponentIndex NodeTemplate::getCompIndex(char* file, const char* label) const { - char* workPtr = file + startByte; +inline ComponentIndex NodeTemplate::getCompIndex(char* fileData, const char* label) const { + char* workPtr = fileData + startByte; SkipCharacter(workPtr, USED_LINE_SEPARATOR, 1); for (uint8_t i = 0; i < COMPS_PER_NODE; ++i) { if (workPtr[0] == '\n') return UINT8_MAX; // Dummy value if (str_cmp_stop_at(label, workPtr, RN_MAX_NAME_LEN, USED_LINE_SEPARATOR)) return i; + SkipCharacter(workPtr, USED_LINE_SEPARATOR, 1); } return UINT8_MAX; // Dummy value } + +inline bool NodeTemplate::isNodeName(const char* fileData, const char* nodeName) const { + return str_cmp_stop_at(nodeName, fileData + startByte, RN_MAX_NAME_LEN, USED_LINE_SEPARATOR); +} + } // namespace raynodes //-----------RN_IMPORT-----------// namespace raynodes { -inline RnImport raynodes::importRN(const char* path) { +inline RnImport importRN(const char* path) { FILE* file = fopen(path, "rb"); // Open in binary mode to avoid text translation if (file == nullptr) { perror("Failed to open file securely"); @@ -403,7 +414,7 @@ inline RnImport::RnImport(char* fileData, ByteIndex size) : fileData(fileData), templateCnt = str_parse_int(indexPtr); nodes = static_cast(malloc(nodeCnt * sizeof(NodeData))); - connections = static_cast(malloc(connCnt * sizeof(ConnectionData))); + connections = static_cast(malloc(connCnt * sizeof(Connection))); templates = static_cast(malloc(templateCnt * sizeof(NodeTemplate))); } // Parsing the templates @@ -444,53 +455,124 @@ inline RnImport::RnImport(char* fileData, ByteIndex size) : fileData(fileData), const int to = str_parse_int(indexPtr); SkipCharacter(indexPtr, USED_LINE_SEPARATOR, 1); const int in = str_parse_int(indexPtr); - connections[i] = {(NodeID)fromNode, (int8_t)from, (uint8_t)out, - (NodeID)toNode, (int8_t)to, (uint8_t)in}; + connections[i] = {(NodeID)fromNode, (int8_t)from, (uint8_t)out, (NodeID)toNode, (int8_t)to, (uint8_t)in}; } } } template -auto RnImport::getComponentData(const NodeID nodeID, const char* label, const int saveIndex) const { +auto RnImport::getComponentData(const NodeID node, const char* label, const int saveIndex) const { if (label == nullptr || saveIndex < 0) [[unlikely]] { return GetDefaultValue
(); } - const auto* nData = getNodeData(nodeID); + const auto* nData = getNodeData(node); if (nData == nullptr) [[unlikely]] { return GetDefaultValue
(); } - const auto* nTemplate = getNodeTemplate(nData->templateID); + const auto* nTemplate = getNodeTemplate(nData->tID); if (nTemplate == nullptr) [[unlikely]] { return GetDefaultValue
(); } ComponentIndex compIndex = nTemplate->getCompIndex(fileData, label); if (compIndex == UINT8_MAX) [[unlikely]] { return GetDefaultValue
(); } return nData->getData
(fileData, compIndex, saveIndex); } template -auto RnImport::getComponentData(const NodeID nodeID, const int componentIndex, const int saveIndex) const { - if (componentIndex < 0 || saveIndex < 0) [[unlikely]] { return GetDefaultValue
(); } - const auto* nData = getNodeData(nodeID); +auto RnImport::getComponentData(const NodeID node, const int component, const int saveIndex) const { + if (component < 0 || saveIndex < 0) [[unlikely]] { return GetDefaultValue
(); } + const auto* nData = getNodeData(node); if (nData == nullptr) [[unlikely]] { return GetDefaultValue
(); } - return nData->getData
(fileData, componentIndex, saveIndex); + return nData->getData
(fileData, component, saveIndex); } -inline ConnectionData RnImport::getConnection(NodeID nodeID, int componentIndex) const { +template +std::array RnImport::getConnectionsOut(const NodeID node, const int component, int pin) const { + int index = 0; + std::array retval; for (int i = 0; i < connCnt; ++i) { - if (connections[i].fromNode == nodeID && connections[i].fromComponent == componentIndex) { - return connections[i]; - } + if (connections[i].fromNode != node) [[likely]] { continue; } + if (component != -2 && connections[i].fromComponent != component) [[likely]] { continue; } + if (pin != -1 && connections[i].fromPin != pin) [[likely]] { continue; } + retval[index++] = connections[i]; + if (index >= size) [[unlikely]] { break; } + } + while (index < size) [[likely]] { + retval[index++] = {UINT16_MAX, INT8_MAX, UINT8_MAX, UINT16_MAX, INT8_MAX, UINT8_MAX}; } - // Failure - return {UINT16_MAX, INT8_MAX, UINT8_MAX, UINT16_MAX, INT8_MAX, UINT8_MAX}; + return retval; +} +inline std::vector RnImport::getConnectionsOut(NodeID node, int component, int pin) const { + std::vector retval; + retval.reserve(5); + for (int i = 0; i < connCnt; ++i) { + if (connections[i].fromNode != node) [[likely]] { continue; } + if (component != -2 && connections[i].fromComponent != component) [[likely]] { continue; } + if (pin != -1 && connections[i].fromPin != pin) [[likely]] { continue; } + retval.push_back(connections[i]); + } + return retval; } template -std::array RnImport::getConnections(NodeID nodeID, int componentIndex) const { +std::array RnImport::getConnectionsIn(NodeID node, int component, int pin) const { int index = 0; - std::array retval; + std::array retval; + for (int i = 0; i < connCnt; ++i) { - if (connections[i].fromNode == nodeID && connections[i].fromComponent == componentIndex) { - retval[index++] = connections[i]; - if (index >= size) break; - } + if (connections[i].toNode != node) [[likely]] { continue; } + if (component != -2 && connections[i].toComponent != component) [[likely]] { continue; } + if (pin != -1 && connections[i].toPin != pin) [[likely]] { continue; } + retval[index++] = connections[i]; + if (index >= size) [[unlikely]] { break; } } - while (index < size) { + while (index < size) [[likely]] { retval[index++] = {UINT16_MAX, INT8_MAX, UINT8_MAX, UINT16_MAX, INT8_MAX, UINT8_MAX}; } + + return retval; +} +inline std::vector RnImport::getConnectionsIn(NodeID node, int component, int pin) const { + std::vector retval; + retval.reserve(5); + for (int i = 0; i < connCnt; ++i) { + if (connections[i].toNode != node) [[likely]] { continue; } + if (component != -2 && connections[i].toComponent != component) [[likely]] { continue; } + if (pin != -1 && connections[i].toPin != pin) [[likely]] { continue; } + retval.push_back(connections[i]); + } return retval; } +template +std::array RnImport::getNodes(const char* name) const { + int index = 0; + std::array retVal; + std::memset(&retVal, 255, size * sizeof(NodeID)); // Setting all bytes to 1's -> biggest possible value + if (name == nullptr) return retVal; + TemplateID id = UINT8_MAX; + for (int i = 0; i < templateCnt; ++i) { + if (templates[i].isNodeName(fileData, name)) [[unlikely]] { + id = static_cast(i); + break; + } + } + if (id == UINT8_MAX) return retVal; + for (int i = 0; i < nodeCnt; ++i) { + if (nodes[i].tID == id) [[unlikely]] { + retVal[index++] = nodes[i].id; + if (index >= size) break; + } + } + return retVal; +} +inline std::vector RnImport::getNodes(const char* name) const { + std::vector retVal; + if (name == nullptr) return retVal; + TemplateID id = UINT8_MAX; + for (int i = 0; i < templateCnt; ++i) { + if (templates[i].isNodeName(fileData, name)) [[unlikely]] { + id = static_cast(i); + break; + } + } + if (id == UINT8_MAX) return retVal; + for (int i = 0; i < nodeCnt; ++i) { + if (nodes[i].tID == id) [[unlikely]] { + retVal.push_back(nodes[i].id); + } + } + return retVal; +} } // namespace raynodes #endif //IMPORTER_H \ No newline at end of file diff --git a/src/raynodes/node/Node.h b/src/raynodes/node/Node.h index 33d9640..3f506ed 100644 --- a/src/raynodes/node/Node.h +++ b/src/raynodes/node/Node.h @@ -23,6 +23,7 @@ #include "shared/fwd.h" +#include // For strcmp on gcc #include #include "component/Component.h" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 02b6c47..f028527 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,4 +8,5 @@ target_include_directories(raynodes_test PRIVATE "${CMAKE_SOURCE_DIR}/src/raynod target_link_libraries(raynodes_test PRIVATE catch2 raylib editor) # Register the test with CMake - run from binary dir -add_test(NAME RaynodesTest COMMAND raynodes_test --benchmark-samples 5 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) \ No newline at end of file +add_test(NAME ImportTest COMMAND raynodes_test [Import] --benchmark-samples 5 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +add_test(NAME PersistTest COMMAND raynodes_test [Persist] --benchmark-samples 5 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) \ No newline at end of file diff --git a/test/ImportTest.cpp b/test/ImportTest.cpp index 27f5308..0cd7566 100644 --- a/test/ImportTest.cpp +++ b/test/ImportTest.cpp @@ -23,18 +23,14 @@ #include "import/RnImport.h" #include "TestUtil.h" -TEST_CASE("Test a simple import of a rn file") { +TEST_CASE("Test getComponentData", "[Import]") { TestUtil::SetupCWD(); auto rn = raynodes::importRN("res/Test1.rn"); - REQUIRE(rn.connCnt == 1); - REQUIRE(rn.nodeCnt == 2); + REQUIRE(rn.connCnt == 3); + REQUIRE(rn.nodeCnt == 6); REQUIRE(rn.templateCnt == 10); - REQUIRE(rn.connections[0].fromNode == 0); - REQUIRE(rn.connections[0].fromComponent == -1); - REQUIRE(rn.connections[0].fromPin == 0); - // String data test auto str = rn.getComponentData(0, "TextField"); auto str1 = rn.getComponentData(0, 0); @@ -46,14 +42,33 @@ TEST_CASE("Test a simple import of a rn file") { REQUIRE(str == "Test2"); REQUIRE(str == str1); - // Bool test + // Aggregate test + str = rn.getComponentData(5, "DisplayText"); + REQUIRE(str == "Display"); + str1 = rn.getComponentData(5, "Choice2"); + REQUIRE(str1 == "C2"); + auto str2 = rn.getComponentData(5, "Choice4"); + REQUIRE(str2 == "C4"); - // Connection test + auto str3 = rn.getComponentData(5, 0); + REQUIRE(str == str3); + auto str4 = rn.getComponentData(5, 2); + REQUIRE(str1 == str4); + auto str5 = rn.getComponentData(5, 4); + REQUIRE(str2 == str5); - //Test single - { - auto conn = rn.getConnection(0, -1); + // Bool test + // Integer test + auto val = rn.getComponentData(4, "Operation"); + REQUIRE(val == 1); +} +TEST_CASE("Test getConnectionOut", "[Import]") { + TestUtil::SetupCWD(); + auto rn = raynodes::importRN("res/Test1.rn"); + { + auto conns = rn.getConnectionsOut<4>(0, -1); + const auto conn = conns[0]; REQUIRE(conn.isValid() == true); REQUIRE(conn.fromNode == 0); REQUIRE(conn.fromComponent == -1); // node-to-node connection @@ -61,11 +76,34 @@ TEST_CASE("Test a simple import of a rn file") { REQUIRE(conn.toNode == 1); REQUIRE(conn.toComponent == -1); // node-to-node connection REQUIRE(conn.toPin == 0); + + for (int i = 1; i < 4; ++i) { + REQUIRE(conns[i].isValid() == false); + } } + // Test vector + { + auto conns = rn.getConnectionsOut(0, -1); + const auto conn = conns[0]; + REQUIRE(conn.isValid() == true); + REQUIRE(conn.fromNode == 0); + REQUIRE(conn.fromComponent == -1); // node-to-node connection + REQUIRE(conn.fromPin == 0); + REQUIRE(conn.toNode == 1); + REQUIRE(conn.toComponent == -1); // node-to-node connection + REQUIRE(conn.toPin == 0); + for (const auto c : conns) { + REQUIRE(c.isValid() == true); + } + } +} +TEST_CASE("Test getConnectionIn", "[Import]") { + TestUtil::SetupCWD(); + auto rn = raynodes::importRN("res/Test1.rn"); // Test multiple { - auto conns = rn.getConnections<4>(0, -1); + auto conns = rn.getConnectionsIn<4>(1, -1); const auto conn = conns[0]; REQUIRE(conn.isValid() == true); REQUIRE(conn.fromNode == 0); @@ -79,9 +117,44 @@ TEST_CASE("Test a simple import of a rn file") { REQUIRE(conns[i].isValid() == false); } } + // Test multiple vector + { + auto conns = rn.getConnectionsIn(1, -1); + const auto conn = conns[0]; + REQUIRE(conn.isValid() == true); + REQUIRE(conn.fromNode == 0); + REQUIRE(conn.fromComponent == -1); // node-to-node connection + REQUIRE(conn.fromPin == 0); + REQUIRE(conn.toNode == 1); + REQUIRE(conn.toComponent == -1); // node-to-node connection + REQUIRE(conn.toPin == 0); + + for (const auto c : conns) { + REQUIRE(c.isValid() == true); + } + } +} +TEST_CASE("Test getNodes", "[Import]") { + TestUtil::SetupCWD(); + auto rn = raynodes::importRN("res/Test1.rn"); + // Test array + { + auto nodes = rn.getNodes<4>("TextField"); + REQUIRE(nodes[0] == 0); + REQUIRE(nodes[1] == 1); + REQUIRE(nodes[2] == UINT16_MAX); + REQUIRE(nodes[3] == UINT16_MAX); + } + // Test multiple vector + { + auto nodes = rn.getNodes("TextField"); + REQUIRE(nodes.size() == 2); + REQUIRE(nodes[0] == 0); + REQUIRE(nodes[1] == 1); + } } -TEST_CASE("Benchmark the import") { +TEST_CASE("Benchmark the import", "[Import]") { TestUtil::SetupCWD(); auto* path = "./res/__GEN2__.rn"; BENCHMARK("Import") { diff --git a/test/PersistTest.cpp b/test/PersistTest.cpp index 322c0c8..c5b396f 100644 --- a/test/PersistTest.cpp +++ b/test/PersistTest.cpp @@ -23,7 +23,7 @@ #include "TestUtil.h" -TEST_CASE("Test correct saving and loading", "PersistTest1") { +TEST_CASE("Test correct saving and loading", "[Persist]") { TestUtil::SetupCWD(); auto* testPath = "./res/__GEN1__.rn"; auto ec = TestUtil::getBasicContext(); @@ -49,7 +49,7 @@ TEST_CASE("Test correct saving and loading", "PersistTest1") { REQUIRE(std::filesystem::exists(testPath) == true); } -TEST_CASE("Benchmark saving and loading", "PersistBench1") { +TEST_CASE("Benchmark saving and loading", "[Persist]") { TestUtil::SetupCWD(); auto* testPath = "./res/__GEN2__.rn"; auto ec = TestUtil::getBasicContext(); diff --git a/test/res/Test1.rn b/test/res/Test1.rn index a07470e..a49be5e 100644 --- a/test/res/Test1.rn +++ b/test/res/Test1.rn @@ -1,5 +1,5 @@ --EditorData-- -2|1|-127.000|-126.000|1.000|1| +6|3|-94.000|-152.000|1.000|1| --Templates-- 10| 0|Dialog Choice|DisplayText|Choice1|Choice2|Choice3|Choice4||255| @@ -13,7 +13,13 @@ 8|Dialog|DisplayText||||||255| 9|QuestHeader|Name|Description|Zone|Level|||255| --Nodes-- -2|0|Test1|$-500|-183| -2|1|Test2|$-139|-251| +2|0|Test1|$-598|-195| +2|1|Test2|$-87|-195| +6|2|1024|$-80|-36| +3|3|1|1|1|$-597|-35| +1|4|1|$-294|77| +0|5|Display|$C1|$C2|$C3|$C4|$-331|-522| --Connections-- 0|-1|0|1|-1|0| +3|-1|0|4|-1|0| +4|-1|0|2|-1|0|