diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6b1da0a..cd27c91 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -23,7 +23,8 @@ jobs: sudo apt-get update sudo apt-get install -y \ build-essential \ - cmake + cmake \ + libcriterion-dev - name: Configure CMake run: | @@ -32,7 +33,7 @@ jobs: cmake .. - name: Build - run: cmake --build build + run: cmake --build build --target xpscan_tests - name: Run tests - run: cd build && make test + run: cd build && ./xpscan_tests diff --git a/CMakeLists.txt b/CMakeLists.txt index 81b35f5..7d2f2b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,16 +5,9 @@ set(CMAKE_CXX_STANDARD 17) set(SOURCES src/Scanner.cpp src/Exporter.cpp src/Printer.cpp) set(TEST_SOURCES tests/ExporterTest.cpp tests/ScannerTest.cpp tests/PrinterTest.cpp) -# Fetch Criterion (Testing library) -include(FetchContent) -FetchContent_Declare(criterion - GIT_REPOSITORY https://github.com/Snaipe/Criterion.git - GIT_TAG v2.4.2 - GIT_SHALLOW TRUE - GIT_PROGRESS TRUE -) -set(FETCHCONTENT_QUIET FALSE) -FetchContent_MakeAvailable(criterion) +# Find Criterion (Testing library) +find_package(PkgConfig REQUIRED) +pkg_check_modules(CRITERION REQUIRED criterion) # Look for threads find_package(Threads REQUIRED) @@ -28,7 +21,8 @@ target_link_libraries(xpscan PRIVATE Threads::Threads) # Tests runner add_executable(xpscan_tests EXCLUDE_FROM_ALL ${TEST_SOURCES} ${SOURCES}) -target_link_libraries(xpscan_tests PRIVATE Threads::Threads criterion) +target_include_directories(xpscan_tests PRIVATE ${CRITERION_INCLUDE_DIRS}) +target_link_libraries(xpscan_tests PRIVATE Threads::Threads ${CRITERION_LIBRARIES}) # Test target add_custom_target(test diff --git a/tests/ExporterTest.cpp b/tests/ExporterTest.cpp index 44029cf..bc815a0 100644 --- a/tests/ExporterTest.cpp +++ b/tests/ExporterTest.cpp @@ -6,12 +6,14 @@ #include #include #include +#include +#include // Helper functions signatures -std::string createTestDirectory(); +std::string createTestDirectory(const std::string &test_name); std::string createTestConfig(const std::string &export_path, const std::string &suffix = ""); -void cleanupTestFiles(const std::string &pattern); +void cleanupTestFiles(const std::string &directory); Test(Exporter, empty_results_should_skip_json_export) { Exporter exporter; @@ -39,7 +41,7 @@ Test(Exporter, empty_results_should_skip_text_export) { } Test(Exporter, should_export_valid_json_with_results) { - std::string test_dir = createTestDirectory(); + std::string test_dir = createTestDirectory("json_test"); std::string config_path = createTestConfig(test_dir, "_json"); std::vector results = {{80, "http"}, {443, "https"}, {22, "ssh"}}; @@ -79,13 +81,12 @@ Test(Exporter, should_export_valid_json_with_results) { cr_assert(json_content.find("\"service\": \"http\"") != std::string::npos, "JSON should contain http service"); - cleanupTestFiles(test_dir); std::filesystem::remove(config_path); + std::filesystem::remove_all(test_dir); } Test(Exporter, should_export_valid_text_with_results) { - std::string test_dir = createTestDirectory(); - cleanupTestFiles(test_dir); + std::string test_dir = createTestDirectory("txt_test"); std::string config_path = createTestConfig(test_dir, "_txt"); std::vector results = {{80, "http"}, {443, "https"}}; @@ -126,12 +127,12 @@ Test(Exporter, should_export_valid_text_with_results) { cr_assert(txt_content.find("Service: http") != std::string::npos, "TXT should contain http service"); - cleanupTestFiles(test_dir); std::filesystem::remove(config_path); + std::filesystem::remove_all(test_dir); } Test(Exporter, getExportPath_should_read_from_config) { - std::string test_dir = "/tmp/custom_export_path/"; + std::string test_dir = createTestDirectory("getpath_test"); std::string config_path = createTestConfig(test_dir, "_getpath"); Exporter exporter(config_path); @@ -143,26 +144,50 @@ Test(Exporter, getExportPath_should_read_from_config) { result.c_str(), test_dir.c_str()); std::filesystem::remove(config_path); + std::filesystem::remove_all(test_dir); } -std::string createTestDirectory() { - std::string test_dir = "/tmp/xpscan_test/"; +std::string createTestDirectory(const std::string &test_name) { + // Create unique directory using timestamp and random number to avoid collisions + auto now = std::chrono::high_resolution_clock::now(); + auto timestamp = std::chrono::duration_cast(now.time_since_epoch()).count(); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(1000, 9999); + int random_id = dis(gen); + + std::string test_dir = "/tmp/xpscan_test_" + test_name + "_" + + std::to_string(timestamp) + "_" + + std::to_string(random_id) + "/"; std::filesystem::create_directories(test_dir); return test_dir; } std::string createTestConfig(const std::string &export_path, const std::string &suffix) { - std::string config_path = "/tmp/test_config" + suffix + ".conf"; + // Create unique config file using timestamp and random number + auto now = std::chrono::high_resolution_clock::now(); + auto timestamp = std::chrono::duration_cast(now.time_since_epoch()).count(); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(1000, 9999); + int random_id = dis(gen); + + std::string config_path = "/tmp/test_config" + suffix + "_" + + std::to_string(timestamp) + "_" + + std::to_string(random_id) + ".conf"; std::ofstream config_file(config_path); config_file << export_path; config_file.close(); return config_path; } -void cleanupTestFiles(const std::string &pattern) { - for (const auto &entry : - std::filesystem::directory_iterator("/tmp/xpscan_test/")) { - std::filesystem::remove(entry.path()); +void cleanupTestFiles(const std::string &directory) { + if (std::filesystem::exists(directory)) { + for (const auto &entry : std::filesystem::directory_iterator(directory)) { + std::filesystem::remove(entry.path()); + } } }