diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 31adbd5..eaf6a59 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,6 +15,7 @@ jobs: submodules: recursive - name: Install dependencies run: | + sudo apt-get update sudo apt-get install -y cmake ninja-build ccache scons - name: ccache uses: hendrikmuhs/ccache-action@v1.2 @@ -37,6 +38,7 @@ jobs: submodules: recursive - name: Install dependencies run: | + sudo apt-get update sudo apt-get install -y cmake ninja-build ccache scons - name: ccache uses: hendrikmuhs/ccache-action@v1.2 @@ -81,6 +83,7 @@ jobs: submodules: true - name: Install dependencies run: | + sudo apt-get update sudo apt-get install -y cmake ninja-build ccache gcovr lcov scons - uses: actions/checkout@v4 with: @@ -102,7 +105,7 @@ jobs: cmake --build build --parallel - name: Test run: | - build/bin/run_tests + build/test/run_test env: CTEST_OUTPUT_ON_FAILURE: 1 - name: Generate lcov Coverage Data diff --git a/CMakeLists.txt b/CMakeLists.txt index a8e8360..62c28f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,21 @@ cmake_minimum_required(VERSION 3.20) -project(cpp_template) - -include(cmake/configure.cmake) +set(ProjectName "itlab") +project(${ProjectName}) include_directories(include) enable_testing() -add_subdirectory(3rdparty) -add_subdirectory(app) -add_subdirectory(include) -add_subdirectory(src) + +add_subdirectory(3rdparty/googletest) +add_subdirectory(src/graph) add_subdirectory(test) + +# REPORT +message( STATUS "") +message( STATUS "General configuration for ${PROJECT_NAME}") +message( STATUS "======================================") +message( STATUS "") +message( STATUS " Configuration: ${CMAKE_BUILD_TYPE}") +message( STATUS "") \ No newline at end of file diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt deleted file mode 100644 index e69de29..0000000 diff --git a/include/graph/graph.h b/include/graph/graph.h new file mode 100644 index 0000000..734edc6 --- /dev/null +++ b/include/graph/graph.h @@ -0,0 +1,42 @@ +#ifndef GRAPH_H +#define GRAPH_H + +#include +#include +#include + +class Vertex { + private: + int id_; + std::list neighbors_; + + public: + Vertex(int id_); + void addNeighbor(int neighbor); + void removeNeighbor(int neighbor); + void print() const; + int getId() const; + const std::list& getNeighbors() const; +}; + +class Graph { + private: + std::unordered_map vertices_; + + public: + Graph(); + void addVertex(int id_); + void addEdge(int u, int v); + void removeEdge(int u, int v); + void removeVertex(int id_); + int getVertices() const; + int getEdges() const; + bool empty() const; + void printGraph() const; + bool bfs_helper(int start, int vert, bool flag, std::vector* v_ord); + bool hasPath(int u, int v); + std::vector BFS(int start); + ~Graph(); +}; + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/graph/CMakeLists.txt b/src/graph/CMakeLists.txt new file mode 100644 index 0000000..af77d7d --- /dev/null +++ b/src/graph/CMakeLists.txt @@ -0,0 +1,7 @@ +file(GLOB_RECURSE HEADER_FILES "${CMAKE_SOURCE_DIR}/include/*.h") +file(GLOB_RECURSE SOURCE_FILES "${CMAKE_SOURCE_DIR}/src/*.cpp") + +add_library(${ProjectName} STATIC ${SOURCE_FILES} ${HEADER_FILES}) +target_sources(${ProjectName} PRIVATE ${HEADER_FILES}) + +target_include_directories(${ProjectName} PUBLIC ${CMAKE_SOURCE_DIR}/src) \ No newline at end of file diff --git a/src/graph/graph.cpp b/src/graph/graph.cpp new file mode 100644 index 0000000..54d7065 --- /dev/null +++ b/src/graph/graph.cpp @@ -0,0 +1,132 @@ +#include "./graph/graph.h" + +#include +#include +#include +#include +#include + +Vertex::Vertex(int id_) : id_(id_) {} + +void Vertex::addNeighbor(int neighbor) { + if (neighbor != id_) { + neighbors_.push_back(neighbor); + } +} + +void Vertex::removeNeighbor(int neighbor) { neighbors_.remove(neighbor); } + +void Vertex::print() const { + std::cout << id_ << ": "; + for (const int& neighbor : neighbors_) { + std::cout << neighbor << " "; + } + std::cout << '\n'; +} + +int Vertex::getId() const { return id_; } + +const std::list& Vertex::getNeighbors() const { return neighbors_; } + +Graph::Graph() = default; + +void Graph::addVertex(int id_) { + if (vertices_.find(id_) == vertices_.end()) { + vertices_[id_] = new Vertex(id_); + } +} + +void Graph::addEdge(int u, int v) { + if (vertices_.find(u) == vertices_.end()) { + addVertex(u); + } + if (vertices_.find(v) == vertices_.end()) { + addVertex(v); + } + vertices_[u]->addNeighbor(v); +} + +void Graph::removeEdge(int u, int v) { + if (vertices_.find(u) != vertices_.end()) { + vertices_[u]->removeNeighbor(v); + } +} + +void Graph::removeVertex(int id_) { + for (auto& pair : vertices_) { + pair.second->removeNeighbor(id_); + } + auto it = vertices_.find(id_); + if (it != vertices_.end()) { + delete it->second; + vertices_.erase(it); + } +} + +int Graph::getVertices() const { return static_cast(vertices_.size()); } + +int Graph::getEdges() const { + int count = 0; + for (const auto& vertice : vertices_) { + count += (vertice.second->getNeighbors()).size(); + } + return count; +} + +bool Graph::empty() const { return vertices_.empty(); } + +void Graph::printGraph() const { + for (const auto& pair : vertices_) { + pair.second->print(); + } +} + +bool Graph::bfs_helper(int start, int vert, bool flag, + std::vector* v_ord) { + std::unordered_map visited; + std::queue queue; + queue.push(start); + visited[start] = true; + + while (!queue.empty()) { + int current = queue.front(); + queue.pop(); + + if (flag && current == vert) { + return true; + } + if (v_ord != nullptr) { + v_ord->push_back(current); + } + + if (vertices_.find(current) != vertices_.end()) { + for (const int& neighbor : vertices_[current]->getNeighbors()) { + if (!visited[neighbor]) { + visited[neighbor] = true; + queue.push(neighbor); + } + } + } + } + return false; +} + +bool Graph::hasPath(int u, int v) { + if (vertices_.find(u) == vertices_.end() || + vertices_.find(v) == vertices_.end()) { + return false; + } + return bfs_helper(u, v, true, nullptr); +} + +std::vector Graph::BFS(int start) { + std::vector v_ord; + bfs_helper(start, -1, false, &v_ord); + return v_ord; +} + +Graph::~Graph() { + for (auto& pair : vertices_) { + delete pair.second; + } +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9dada0c..431473b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,8 @@ -file(GLOB_RECURSE TEST_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) -add_executable(run_tests ${TEST_SRC_FILES}) -target_link_libraries(run_tests PUBLIC - gtest_main -) +file(GLOB_RECURSE TEST_FILES ./*.cpp) + +set(TestsName "run_test") + +add_executable(${TestsName} ${TEST_FILES}) + +target_link_libraries(${TestsName} PRIVATE ${ProjectName} gtest) diff --git a/test/main.cpp b/test/graph/main.cpp similarity index 53% rename from test/main.cpp rename to test/graph/main.cpp index 4d820af..9a17845 100644 --- a/test/main.cpp +++ b/test/graph/main.cpp @@ -1,6 +1,6 @@ -#include +#include "gtest/gtest.h" -int main(int argc, char **argv) { +int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} +} \ No newline at end of file diff --git a/test/graph/test_graph.cpp b/test/graph/test_graph.cpp new file mode 100644 index 0000000..a382832 --- /dev/null +++ b/test/graph/test_graph.cpp @@ -0,0 +1,145 @@ +#include + +#include "graph/graph.h" +#include "gtest/gtest.h" + +TEST(Graph, can_add_vertex_to_graph) { + Graph g; + + g.addVertex(1); + + ASSERT_EQ(1, g.getVertices()); +} + +TEST(Graph, can_add_edge_to_graph) { + Graph g; + + g.addEdge(1, 0); + + ASSERT_EQ(1, g.getEdges()); +} + +TEST(Graph, cant_add_edge_with_same_id_to_graph) { + Graph g; + + g.addEdge(1, 1); + + ASSERT_EQ(0, g.getEdges()); +} + +TEST(Graph, can_add_vertex_and_edge_to_graph) { + Graph g; + + g.addEdge(1, 0); + g.addVertex(1); + + ASSERT_EQ(1, g.getEdges()); + ASSERT_EQ(1, g.getEdges()); +} + +TEST(Graph, can_remove_vertex) { + Graph g; + g.addEdge(1, 0); + + g.removeVertex(1); + + ASSERT_EQ(g.getVertices(), 1); +} + +TEST(Graph, can_get_vertices_count) { + Graph g; + + g.addEdge(1, 0); + g.addVertex(2); + g.addVertex(3); + g.removeVertex(1); + g.addVertex(1); + g.removeVertex(3); + + ASSERT_EQ(g.getVertices(), 3); +} + +TEST(Graph, can_get_edges_count) { + Graph g; + + g.addEdge(1, 0); + g.addEdge(2, 0); + g.addEdge(0, 2); + g.addEdge(2, 2); + + ASSERT_EQ(g.getEdges(), 3); +} + +TEST(Graph, check_graph_is_empty) { + Graph g; + + ASSERT_TRUE(g.empty()); +} + +TEST(Graph, check_graph_is_not_empty) { + Graph g; + + g.addEdge(1, 0); + + ASSERT_FALSE(g.empty()); +} + +TEST(Graph, check_graph_no_path_between_vertexes) { + Graph g; + + g.addEdge(0, 1); + g.addEdge(0, 3); + g.addEdge(1, 2); + g.addEdge(3, 1); + + ASSERT_FALSE(g.hasPath(1, 0)); +} + +TEST(Graph, check_graph_has_path_between_vertexes) { + Graph g; + + g.addEdge(0, 1); + g.addEdge(0, 3); + g.addEdge(1, 2); + g.addEdge(3, 1); + + ASSERT_TRUE(g.hasPath(3, 1)); +} + +TEST(Graph, check_graph_can_find_path_between_vertexes_after_delete) { + Graph g; + + g.addEdge(0, 1); + g.addEdge(0, 3); + g.addEdge(1, 2); + g.addEdge(3, 1); + g.removeVertex(3); + + ASSERT_FALSE(g.hasPath(3, 1)); + ASSERT_FALSE(g.hasPath(0, 3)); +} + +TEST(Graph, can_create_bfs_path_in_empty_graph) { + Graph g; + std::vector v1 = {0}; + + ASSERT_EQ(g.BFS(0), v1); +} + +TEST(Graph, check_bfs_path) { + Graph g; + std::vector v1 = {0, 1, 3, 2, 4, 6, 7, 5, 8, 9}; + + g.addEdge(0, 1); + g.addEdge(0, 3); + g.addEdge(1, 2); + g.addEdge(3, 4); + g.addEdge(4, 5); + g.addEdge(3, 6); + g.addEdge(3, 7); + g.addEdge(7, 8); + g.addEdge(8, 9); + std::vector v = g.BFS(0); + + ASSERT_EQ(v, v1); +} \ No newline at end of file