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|