diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f2fe89d..6d69493 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,7 +2,7 @@ name: Tests on: push: - branches: [core, main] + branches: [dev, main, test/all] pull_request: branches: [main] @@ -31,4 +31,12 @@ jobs: run: cmake --build build - name: Run tests - run: ctest --test-dir build --output-on-failure + run: cd build && ./test_config_manager && ./test_task_runner && ./test_session_logger && ./test_flowhook_core + + - name: Install flowhook binary + run: | + sudo cmake --install build + which flowhook || echo "flowhook not found after install" + + - name: Run CLI tests + run: bash cli/tests/test_cli.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 424b37e..29470f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,10 +51,17 @@ add_executable(flowhook_cli cli/src/main.cpp cli/src/commands/run.cpp cli/src/commands/init.cpp cli/src/commands/list.cpp - cli/src/commands/remove.cpp) + cli/src/commands/remove.cpp + cli/src/commands/set.cpp + cli/src/commands/check.cpp) target_link_libraries(flowhook_cli PRIVATE flowhook_lib) target_include_directories(flowhook_cli PRIVATE cli/src) +install(TARGETS flowhook_cli DESTINATION bin) +install(PROGRAMS $ + DESTINATION bin + RENAME flowhook) + enable_testing() add_test(NAME flowhook_filewatcher_tests COMMAND test_filewatcher) add_test(NAME flowhook_task_runner_tests COMMAND test_task_runner) diff --git a/cli/src/commands/add.cpp b/cli/src/commands/add.cpp index 206c736..933d319 100644 --- a/cli/src/commands/add.cpp +++ b/cli/src/commands/add.cpp @@ -44,7 +44,7 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) { if (add_n_path.empty() && add_command.empty() && command_on_success.empty() && command_on_failure.empty() && ignored_path.empty() && ignored_pattern.empty()) { - std::cerr << "At least one of --path, --command, --on-success, --on-failure, --ignored-path, or --ignored-pattern is required\n" + std::cout << "Error: At least one of --path, --command, --on-success, --on-failure, --ignored-path, or --ignored-pattern is required\n" << std::endl; return; } @@ -52,7 +52,7 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) { if (!add_n_path.empty()) { auto r = fh->set_task_path(task_id, add_n_path); if (r.isErr()) { - std::cerr << "Failed to set task path: " << r.getErrMessage() + std::cout << "Failed to set task path: " << r.getErrMessage() << std::endl; return; } @@ -61,7 +61,7 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) { if (!add_command.empty()) { auto r = fh->set_task_command(task_id, add_command); if (r.isErr()) { - std::cerr << "Failed to set task command: " << r.getErrMessage() + std::cout << "Failed to set task command: " << r.getErrMessage() << std::endl; return; } @@ -70,7 +70,7 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) { if (!command_on_success.empty()) { auto r = fh->set_task_on_success(task_id, command_on_success); if (r.isErr()) { - std::cerr << "Failed to set task on success: " << r.getErrMessage() + std::cout << "Failed to set task on success: " << r.getErrMessage() << std::endl; return; } @@ -79,7 +79,7 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) { if (!command_on_failure.empty()) { auto r = fh->set_task_on_failure(task_id, command_on_failure); if (r.isErr()) { - std::cerr << "Failed to set task on failure: " << r.getErrMessage() + std::cout << "Failed to set task on failure: " << r.getErrMessage() << std::endl; return; } @@ -88,7 +88,7 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) { if (!ignored_path.empty()) { auto r = fh->set_ignored_path(task_id, ignored_path); if (r.isErr()) { - std::cerr << "Failed to set ignored path: " << r.getErrMessage() + std::cout << "Failed to set ignored path: " << r.getErrMessage() << std::endl; return; } @@ -97,7 +97,7 @@ void register_add(CLI::App *app, flowhook::FlowHookCore *fh) { if (!ignored_pattern.empty()) { auto r = fh->set_ignored_pattern(task_id, ignored_pattern); if (r.isErr()) { - std::cerr << "Failed to set ignored pattern: " << r.getErrMessage() + std::cout << "Failed to set ignored pattern: " << r.getErrMessage() << std::endl; return; } diff --git a/cli/src/commands/check.cpp b/cli/src/commands/check.cpp new file mode 100644 index 0000000..e8f4e8f --- /dev/null +++ b/cli/src/commands/check.cpp @@ -0,0 +1,78 @@ +#include "../../src/include/flowhook_core.h" +#include "../include/CLI11.hpp" +#include "../../src/include/macros.hpp" + + +namespace fs = std::filesystem; + +namespace flowhook_cli { + void register_check(CLI::App* app, flowhook::FlowHookCore* fh) + { + auto* check = app->add_subcommand("check", "check the status of the task"); + + // this is a purely ceremonial flag(so that CLI11 won't say there is unrecognized flag) + // the actual parsing of verbose and setting of the environment variable is done in main.cpp + check->add_flag("--debug", FLOWHOOK_DEBUG, "Enable debug output. \n// is an extremely detailed output that shows every step of the process."); + check->add_flag("--verbose", FLOWHOOK_VERBOSE, "Enable verbose output. \n// shows a summary of the process."); + + + static bool active = false; + check->add_flag("--active", active, "Check if task is labeled as active"); + + static bool deactive = false; + check->add_flag("--deactive", deactive, "Check if task is labeled as deactive"); + + static bool depth; + check->add_flag("--depth", depth, "Outputs the watching depth set from your working directory."); + + check->callback([=]() mutable { + std::string check_task_id = fs::current_path().string(); + + if(!active && !deactive && !depth) { + if(!fh->is_task(check_task_id)) { + std::cout << "FlowHook task has not been initialized in the current directory." << std::endl; + return; + }else { + std::cout << "FlowHook task is initialized in the current directory." << std::endl; + return; + } + } + + if (active) { + auto r = fh->is_task_active(check_task_id); + if (r.isErr()) { + std::cout << "Failed to check if task is active: " << r.getErrMessage() + << std::endl; + return; + } + if (r.unwrap()) { + std::cout << "Task " << check_task_id << " is active" << std::endl; + } else { + std::cout << "Task " << check_task_id << " is not active" << std::endl; + } + } + if(deactive) { + auto r = fh->is_task_active(check_task_id); + if (r.isErr()) { + std::cout << "Failed to check if task is deactive: " << r.getErrMessage() + << std::endl; + return; + } + if (r.unwrap()) { + std::cout << "Task " << check_task_id << " is not deactive" << std::endl; + } else { + std::cout << "Task " << check_task_id << " is deactive" << std::endl; + } + } + if(depth) { + auto r = fh->get_task_depth(check_task_id); + if (r.isErr()) { + std::cout << "Failed to check task depth: " << r.getErrMessage() + << std::endl; + return; + } + std::cout << "Task depth is " << r.unwrap() << std::endl; + } + }); + } +} diff --git a/cli/src/commands/init.cpp b/cli/src/commands/init.cpp index f45042b..fdc51ea 100644 --- a/cli/src/commands/init.cpp +++ b/cli/src/commands/init.cpp @@ -30,12 +30,12 @@ namespace flowhook_cli { auto r = fh->create_task(task_name, n_path); if (r.isErr()) { - std::cerr << "Failed to create task: " << r.getErrMessage() << std::endl; + std::cout << "Failed to create task: " << r.getErrMessage() << std::endl; return; } auto p = fh->set_task_path(n_path, n_path); if (p.isErr()) { - std::cerr << "Failed to set task path: " << p.getErrMessage() << std::endl; + std::cout << "Failed to set task path: " << p.getErrMessage() << std::endl; return; } diff --git a/cli/src/commands/list.cpp b/cli/src/commands/list.cpp index 0c41480..7f1fbe2 100644 --- a/cli/src/commands/list.cpp +++ b/cli/src/commands/list.cpp @@ -46,54 +46,63 @@ namespace flowhook_cli { if (list_tasks) { - std::cout << "All registered Tasks:\n" << std::endl; + std::cout << "\nAll registered Tasks:\n" << std::endl; for (const auto& task : tasks) { - std::cout << "Name: " << task.name << std::endl; - std::cout << "Working Directory: " << task.id << "\n" << std::endl; + std::cout << " Name: " << task.name << std::endl; + std::cout << " Working Directory: " << task.id << "\n" << std::endl; } } if (list_paths) { if(ts == nullptr) { - std::cerr << "Error: No flowhook task found for the current directory." << std::endl; + std::cout << "Error: No flowhook task found for the current directory." << std::endl; return; } - std::cout << "Paths:\n" << std::endl; - for(const auto& path : ts->paths) { - std::cout << path << std::endl; + std::cout << "\nWatched Directories:\n" << std::endl; + for(const auto& path : ts->dir_paths) { + std::cout << " " << path << std::endl; + } + + std::cout << "\nStandalone File:\n" << std::endl; + for(const auto& path : ts->file_paths) { + std::cout << " " << path << std::endl; + } + + std::cout << "All Currently Watched Files:\n" << std::endl; + for(const auto& path : fh->get_resolved_files(ts->id)) { + std::cout << " " << path << std::endl; } + } if (list_commands) { if(ts == nullptr) { - std::cerr << "Error: No flowhook task found for the current directory." << std::endl; + std::cout << "Error: No flowhook task found for the current directory." << std::endl; return; } - std::cout << "Commands:\n" << std::endl; + std::cout << "\nCommands:\n" << std::endl; for(const auto& command : ts->commands) { - std::cout << command << std::endl; + std::cout << " " << command << std::endl; } } if (list_ignored) { if(ts == nullptr) { - std::cerr << "Error: No flowhook task found for the current directory." << std::endl; + std::cout << "Error: No flowhook task found for the current directory." << std::endl; return; } - std::cout << "Ignored paths:\n" << std::endl; + std::cout << "\nIgnored paths:\n" << std::endl; for(const auto& ignored_paths : ts->ignored_paths) { - std::cout << ignored_paths << std::endl; + std::cout << " " << ignored_paths << std::endl; } - std::cout << std::endl; - std::cout << "Ignored patterns:\n" << std::endl; + std::cout << "\nIgnored patterns:\n" << std::endl; for(const auto& ignored_pattern : ts->ignored_patterns) { - std::cout << ignored_pattern << std::endl; + std::cout << " " << ignored_pattern << std::endl; } - std::cout << std::endl; } if (list_on_success) { @@ -102,9 +111,9 @@ namespace flowhook_cli { return; } - std::cout << "On success:\n" << std::endl; + std::cout << "\nOn success:\n" << std::endl; for(const auto& command : ts->on_success) { - std::cout << command << std::endl; + std::cout << " " << command << std::endl; } std::cout << std::endl; } @@ -115,9 +124,9 @@ namespace flowhook_cli { return; } - std::cout << "On failure:\n" << std::endl; + std::cout << "\nOn failure:\n" << std::endl; for(const auto& command : ts->on_failure) { - std::cout << command << std::endl; + std::cout << " " << command << std::endl; } std::cout << std::endl; } diff --git a/cli/src/commands/remove.cpp b/cli/src/commands/remove.cpp index a0d055b..27ce824 100644 --- a/cli/src/commands/remove.cpp +++ b/cli/src/commands/remove.cpp @@ -10,15 +10,13 @@ void register_remove(CLI::App *app, flowhook::FlowHookCore *fh) { // add subcommand auto *remove = - app->add_subcommand("remove", "remove an element from a task\n"); + app->add_subcommand("remove", "remove an element from a task\n // removes the task itself if no OPTION is provided"); // this is a purely ceremonial flag(so that CLI11 won't say there is unrecognized flag) // the actual parsing of verbose and setting of the environment variable is done in main.cpp remove->add_flag("--debug", FLOWHOOK_DEBUG, "Enable debug output. \n// is an extremely detailed output that shows every step of the process."); remove->add_flag("--verbose", FLOWHOOK_VERBOSE, "Enable verbose output. \n// shows a summary of the process."); - - static std::string task_id = ""; static std::string removed_path = ""; remove->add_option("--path", removed_path, "Path to the task [optional] \n// use to add a new path to be watched.\n"); @@ -38,29 +36,36 @@ void register_remove(CLI::App *app, flowhook::FlowHookCore *fh) { remove->add_option("--ignored-path", ignored_path, "Ignored path [optional] \n// if not provided, all files will be watched in the working directory\n"); remove->add_option("--ignored-pattern", ignored_pattern, "Ignored pattern [optional] \n// if not provided, no pattern will be ignored in the working directory\n"); + static bool force = false; + remove->add_flag("-f", force, "removes flowhook instance without providing a dialog to check certainity"); + remove->callback([=]() mutable { fs::path cwd = fs::current_path(); task_id = cwd.string(); bool remove_task = false; if(removed_path.empty() && removed_command.empty() && - command_on_success.empty() && command_on_failure.empty() && - ignored_path.empty() && ignored_pattern.empty()) { + command_on_success.empty() && command_on_failure.empty() && + ignored_path.empty() && ignored_pattern.empty()) { remove_task = true; } if (remove_task) { - std::cout << "Are you sure you want to remove the flowhook instance from the current directory?" << std::endl; - std::cout << "Type 'yes' to confirm: "; - std::string confirmation; - std::cin >> confirmation; - if (confirmation != "yes") { - std::cout << "Removal cancelled." << std::endl; - return; + if(!force){ + std::cout << "Are you sure you want to remove the flowhook instance from the current directory?" << std::endl; + std::cout << "Type 'yes' to confirm: "; + std::string confirmation; + std::cin >> confirmation; + if (confirmation != "yes") { + std::cout << "Removal cancelled." << std::endl; + return; + } } auto r = fh->delete_task(task_id); if (r.isErr()) { - std::cerr << "Failed to remove task: " << r.getErrMessage() << std::endl; + std::cout << "Failed to remove task: " << r.getErrMessage() << std::endl; + } else { + std::cout << "Task removed successfully." << std::endl; } return; } diff --git a/cli/src/commands/run.cpp b/cli/src/commands/run.cpp index da401d8..825bc87 100644 --- a/cli/src/commands/run.cpp +++ b/cli/src/commands/run.cpp @@ -1,47 +1,61 @@ #include "../../src/include/flowhook_core.h" -#include "../include/CLI11.hpp" #include "../../src/include/macros.hpp" +#include "../include/CLI11.hpp" +#include namespace fs = std::filesystem; namespace flowhook_cli { - void register_run(CLI::App* app, flowhook::FlowHookCore* fh) - { - auto* run = app->add_subcommand("run", "running a task"); - - // this is a purely ceremonial flag(so that CLI11 won't say there is unrecognized flag) - // the actual parsing of verbose and setting of the environment variable is done in main.cpp - run->add_flag("--debug", FLOWHOOK_DEBUG, "Enable debug output. \n// is an extremely detailed output that shows every step of the process."); - run->add_flag("--verbose", FLOWHOOK_VERBOSE, "Enable verbose output. \n// shows a summary of the process."); - run->add_flag("--quiet", FLOWHOOK_QUIET, "Removes build output from terminal"); - - - static bool run_all = false; - run->add_flag("--all", run_all, "Run all task instances"); - - run->callback([=]() mutable { - std::string run_task_id = fs::current_path().string(); - - if (run_all) { - auto r = fh->start_all(); - if (r.isErr()) { - std::cerr << "Failed to run all tasks: " << r.getErrMessage() - << std::endl; - return; - } - pause(); - std::cout << "Watching all tasks..." << std::endl; - return; - } else if (!run_task_id.empty()) { - auto r = fh->start_task(run_task_id); - if (r.isErr()) { - std::cerr << "Failed to run task: " << r.getErrMessage() - << std::endl; - return; - } - std::cout << "Watching " << run_task_id << " (use --quiet to suppress build output)" << std::endl; } - pause(); - }); +void register_run(CLI::App *app, flowhook::FlowHookCore *fh) { + auto *run = app->add_subcommand("run", "running a task"); + + // this is a purely ceremonial flag(so that CLI11 won't say there is + // unrecognized flag) the actual parsing of verbose and setting of the + // environment variable is done in main.cpp + run->add_flag("--debug", FLOWHOOK_DEBUG, + "Enable debug output. \n// is an extremely detailed output " + "that shows every step of the process."); + run->add_flag("--verbose", FLOWHOOK_VERBOSE, + "Enable verbose output. \n// shows a summary of the process."); + run->add_flag("--quiet", FLOWHOOK_QUIET, + "Removes build output from terminal"); + + static bool run_all = false; + run->add_flag("--all", run_all, "Run all task instances labeled as active."); + + static bool run_active = false; + run->add_flag("--active", run_active, "Run active task instances"); + + run->callback([=]() mutable { + std::string run_task_id = fs::current_path().string(); + + if (run_all) { + auto r = fh->start_all(); + if (r.isErr()) { + std::cout << "Failed to run all tasks: " << r.getErrMessage() + << std::endl; + return; + } + pause(); + } else if (run_active) { + auto r = fh->start_active(); + if (r.isErr()) { + std::cout << "Failed to run active tasks: " << r.getErrMessage() + << std::endl; + return; + } + pause(); + } else if (!run_task_id.empty()) { + auto r = fh->start_task(run_task_id); + if (r.isErr()) { + std::cout << "Failed to run task: " << r.getErrMessage() << std::endl; + return; + } } + std::cout << "Watching " << run_task_id + << " (use --quiet to suppress build output, press Ctrl+C to stop)" << std::endl; + pause(); + }); } +} // namespace flowhook_cli diff --git a/cli/src/commands/set.cpp b/cli/src/commands/set.cpp new file mode 100644 index 0000000..609b07f --- /dev/null +++ b/cli/src/commands/set.cpp @@ -0,0 +1,62 @@ +#include "../../src/include/flowhook_core.h" +#include "../include/CLI11.hpp" +#include "../../src/include/macros.hpp" + +#include + + +namespace fs = std::filesystem; + +namespace flowhook_cli { + void register_set(CLI::App* app, flowhook::FlowHookCore* fh) + { + auto* set = app->add_subcommand("set", "setting a property for the task"); + + // this is a purely ceremonial flag(so that CLI11 won't say there is unrecognized flag) + // the actual parsing of verbose and setting of the environment variable is done in main.cpp + set->add_flag("--debug", FLOWHOOK_DEBUG, "Enable debug output. \n// is an extremely detailed output that shows every step of the process."); + set->add_flag("--verbose", FLOWHOOK_VERBOSE, "Enable verbose output. \n// shows a summary of the process."); + + + static bool active = false; + set->add_flag("--active", active, "Set task as active \n // enables you to run specified tasks with the `run active` command"); + + static bool deactive = false; + set->add_flag("--deactive", deactive, "Set a task enables active as deactive"); + + static int depth = INT_MIN; + set->add_option("--depth", depth, "Set the depth to watch from your working directory."); + + set->callback([=]() mutable { + std::string set_task_id = fs::current_path().string(); + + if (active) { + auto r = fh->activate_task(set_task_id); + if (r.isErr()) { + std::cout << "Failed to set task as active: " << r.getErrMessage() + << std::endl; + return; + } + std::cout << "Task " << set_task_id << " is now active" << std::endl; + } + if(deactive) { + auto r = fh->deactivate_task(set_task_id); + if (r.isErr()) { + std::cout << "Failed to set task as deactive: " << r.getErrMessage() + << std::endl; + return; + } + std::cout << "Task " << set_task_id << " is now deactive" << std::endl; + } + if(depth != INT_MIN) { + auto r = fh->set_depth(set_task_id, depth); + if (r.isErr()) { + std::cout << "Failed to set depth: " << r.getErrMessage() + << std::endl; + return; + } + std::cout << "Depth set to " << depth << std::endl; + } + }); + } +} diff --git a/cli/src/main.cpp b/cli/src/main.cpp index 0880089..fef22d3 100644 --- a/cli/src/main.cpp +++ b/cli/src/main.cpp @@ -15,6 +15,8 @@ namespace flowhook_cli { void register_init(CLI::App*, flowhook::FlowHookCore*); void register_list(CLI::App*, flowhook::FlowHookCore*); void register_remove(CLI::App*, flowhook::FlowHookCore*); + void register_set(CLI::App*, flowhook::FlowHookCore*); + void register_check(CLI::App*, flowhook::FlowHookCore*); } namespace fs = std::filesystem; @@ -80,7 +82,7 @@ int main(int argc, char **argv) { if(_fh.getErrCode() == ErrorCode::CONFIG_PARSE_FAILED) { std::cerr << "Config file is corrupted." << std::endl; - fs::path config_path = get_home_directory() / ".config" / "flowhook"/ "tasks.json"; + fs::path config_path = get_home_directory() / ".config" / "flowhook"/ "config.json"; std::cout << "Path : " << config_path << std::endl; std::cerr << "Options: " << std::endl; std::cout << " [1] clear all data and start fresh" << std::endl; @@ -118,6 +120,8 @@ int main(int argc, char **argv) { flowhook_cli::register_run(&app, fh); flowhook_cli::register_list(&app, fh); flowhook_cli::register_remove(&app, fh); + flowhook_cli::register_set(&app, fh); + flowhook_cli::register_check(&app, fh); CLI11_PARSE(app, argc, argv); diff --git a/cli/tests/test_add.sh b/cli/tests/test_add.sh new file mode 100644 index 0000000..5fcd32f --- /dev/null +++ b/cli/tests/test_add.sh @@ -0,0 +1,190 @@ +#!/usr/bin/env bash +set -uo pipefail + +PASS=0 +FAIL=0 + +source test_helpers.sh + +# --- add --path --- + +test_add_path_appears_in_list() { + fresh_dir + flowhook init >/dev/null 2>&1 + mkdir -p subdir + flowhook add --path ./subdir >/dev/null 2>&1 + local out + out=$(flowhook list --paths 2>&1) + assert_contains "$out" "subdir" "add --path shows up in list --paths" +} + +test_add_path_nonexistent_fails() { + fresh_dir + flowhook init >/dev/null 2>&1 + local out + out=$(flowhook add --path ./does_not_exist 2>&1) + assert_contains "$out" "Error:" "add --path on nonexistent path errors" +} + +test_add_duplicate_path_no_duplicate() { + fresh_dir + flowhook init >/dev/null 2>&1 + mkdir -p dup + flowhook add --path ./dup >/dev/null 2>&1 + flowhook add --path ./dup >/dev/null 2>&1 + local out count + out=$(flowhook list --paths 2>&1) + count=$(grep -o "dup" <<< "$out" | wc -l) + assert_exit 2 "$count" "adding the same path twice does not duplicate" +} +# --- add --command --- + +test_add_command_appears_in_list() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --command "echo hi" >/dev/null 2>&1 + local out + out=$(flowhook list --commands 2>&1) + assert_contains "$out" "echo hi" "add --command shows up in list --commands" +} + +test_add_duplicate_command_fails() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --command "echo hi" >/dev/null 2>&1 + local out + out=$(flowhook add --command "echo hi" 2>&1) + assert_contains "$out" "Error:" "adding the same command twice errors" +} + +# --- add --ignored-path --- + +test_add_ignored_path_appears_in_list() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --ignored-path .git >/dev/null 2>&1 + local out + out=$(flowhook list --ignored 2>&1) + assert_contains "$out" ".git" "add --ignored-path shows up in list --ignored" +} + +test_add_duplicate_ignored_path_fails() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --ignored-path .git >/dev/null 2>&1 + local out + out=$(flowhook add --ignored-path .git 2>&1) + assert_contains "$out" "Error:" "adding the same ignored-path twice errors" +} + +# --- add --ignored-pattern --- + +test_add_ignored_pattern_appears_in_list() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --ignored-pattern "*.o" >/dev/null 2>&1 + local out + out=$(flowhook list --ignored 2>&1) + assert_contains "$out" "*.o" "add --ignored-pattern shows up in list --ignored" +} + +test_add_duplicate_ignored_pattern_fails() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --ignored-pattern "*.o" >/dev/null 2>&1 + local out + out=$(flowhook add --ignored-pattern "*.o" 2>&1) + assert_contains "$out" "Error:" "adding the same ignored-pattern twice errors" +} + +# --- ignore-before-add interaction --- +# A directory ignored by name should not appear in list --paths once added, +# since add_path should respect already-registered ignores. + +test_ignored_path_prevents_add_from_listing() { + fresh_dir + flowhook init >/dev/null 2>&1 + mkdir -p ignoreme + flowhook add --ignored-path ignoreme >/dev/null 2>&1 + flowhook add --path ./ignoreme >/dev/null 2>&1 + local out + out=$(flowhook list --paths 2>&1) + assert_not_contains "$out" "ignoreme" "ignored path should not appear in list --paths" +} + +# --- add --on-success --- + +test_add_on_success_appears_in_list() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --on-success "echo success" >/dev/null 2>&1 + local out + out=$(flowhook list --on-success 2>&1) + assert_contains "$out" "echo success" "add --on-success shows up in list --on-success" +} + +test_add_duplicate_on_success_no_duplicate() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --on-success "echo success" >/dev/null 2>&1 + flowhook add --on-success "echo success" >/dev/null 2>&1 + local out count + out=$(flowhook list --on-success 2>&1) + count=$(grep -o "echo success" <<< "$out" | wc -l) + assert_exit 1 "$count" "adding the same on-success command twice does not duplicate" +} + +# --- add --on-failure --- + +test_add_on_failure_appears_in_list() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --on-failure "echo failure" >/dev/null 2>&1 + local out + out=$(flowhook list --on-failure 2>&1) + assert_contains "$out" "echo failure" "add --on-failure shows up in list --on-failure" +} + +test_add_duplicate_on_failure_no_duplicate() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --on-failure "echo failure" >/dev/null 2>&1 + flowhook add --on-failure "echo failure" >/dev/null 2>&1 + local out count + out=$(flowhook list --on-failure 2>&1) + count=$(grep -o "echo failure" <<< "$out" | wc -l) + assert_exit 1 "$count" "adding the same on-failure command twice does not duplicate" +} + +# add with no flags at all +test_add_with_no_flags() { + fresh_dir + flowhook init >/dev/null 2>&1 + local out + out=$(flowhook add 2>&1) + assert_contains "$out" "Error:" "add with no flags errors" +} + +# add --command with an empty +test_add_empty_command() { + fresh_dir + flowhook init >/dev/null 2>&1 + local out + out=$(flowhook add --command "" 2>&1) + assert_contains "$out" "Error:" "add --command empty string errors" +} + +# multiple flags in one add call — path + command + ignored-pattern together +test_add_multiple_flags_at_once() { + fresh_dir + flowhook init >/dev/null 2>&1 + mkdir -p combo + flowhook add --path ./combo --command "echo combo" --ignored-pattern "*.tmp" >/dev/null 2>&1 + local out + out=$(flowhook list --paths 2>&1) + assert_contains "$out" "combo" "multi-flag add: path registered" + out=$(flowhook list --commands 2>&1) + assert_contains "$out" "echo combo" "multi-flag add: command registered" + out=$(flowhook list --ignored 2>&1) + assert_contains "$out" "*.tmp" "multi-flag add: ignored-pattern registered" +} diff --git a/cli/tests/test_cli.sh b/cli/tests/test_cli.sh new file mode 100644 index 0000000..4871ad6 --- /dev/null +++ b/cli/tests/test_cli.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +set -uo pipefail +cd "$(dirname "$0")" || exit 1 + +PASS=0 +FAIL=0 + +source ./test_add.sh +source ./test_init.sh +source ./test_list.sh +source ./test_run.sh +source ./test_remove.sh +source ./test_set.sh + + +# -- TEST INIT -- +test_init_creates_task +test_init_twice_fails + +# -- TEST ADD -- +test_add_path_appears_in_list +test_add_path_nonexistent_fails +test_add_duplicate_path_no_duplicate +test_add_command_appears_in_list +test_add_duplicate_command_fails +test_add_ignored_path_appears_in_list +test_add_duplicate_ignored_path_fails +test_add_ignored_pattern_appears_in_list +test_add_duplicate_ignored_pattern_fails +test_ignored_path_prevents_add_from_listing +test_add_with_no_flags +test_add_empty_command +test_add_on_success_appears_in_list +test_add_duplicate_on_success_no_duplicate +test_add_on_failure_appears_in_list +test_add_duplicate_on_failure_no_duplicate +test_add_multiple_flags_at_once + +# -- TEST LIST -- + +test_list_no_flags_is_noop +test_list_tasks_works_without_init_in_cwd +test_list_paths_fails_without_init_in_cwd +test_list_ignored_shows_defaults +test_list_combined_flags_when_initialized + +# -- TEST RUN -- + +test_run_default_starts_and_exits_on_sigint +test_run_all_starts_and_exits_on_sigint +test_run_active_with_no_active_tasks_errors +test_run_active_with_active_task_starts_and_exits +test_run_quiet_suppresses_command_output + +# -- TEST REMOVE -- + +test_remove_path_removes_from_list +test_remove_nonexistent_path_errors +test_remove_command_removes_from_list +test_remove_nonexistent_command_errors +test_remove_on_success_removes_from_list +test_remove_nonexistent_on_success_errors +test_remove_on_failure_removes_from_list +test_remove_nonexistent_on_failure_errors +test_remove_ignored_path_removes_from_list +test_remove_nonexistent_ignored_path_errors +test_remove_ignored_pattern_removes_from_list +test_remove_nonexistent_ignored_pattern_errors +test_remove_whole_task_with_force_flag +test_remove_whole_task_confirm_yes_deletes +test_remove_whole_task_decline_keeps_task + + +# -- TEST SET -- + +test_set_active +test_set_deactive +test_set_active_then_check_not_deactive +test_set_deactive_then_check_not_active +test_set_depth +test_set_depth_overwrite +test_set_active_and_deactive_together_last_wins +test_set_no_flags_is_noop + + +echo "DEBUG config dir: $FLOWHOOK_CONFIG_DIR_TEST" +echo "Passed: $PASS, Failed: $FAIL" +[[ "$FAIL" -eq 0 ]] || exit 1 diff --git a/cli/tests/test_helpers.sh b/cli/tests/test_helpers.sh new file mode 100644 index 0000000..4dd419f --- /dev/null +++ b/cli/tests/test_helpers.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +set -uo pipefail + +PASS=0 +FAIL=0 + +# --helper functions-- +assert_exit() { + local expected="$1" actual="$2" desc="$3" + if [[ "$expected" == "$actual" ]]; then + PASS=$((PASS+1)) + else + FAIL=$((FAIL+1)) + echo "FAIL: $desc (expected $expected, got $actual)" + fi +} + +assert_contains() { + local haystack="$1" needle="$2" desc="$3" + if [[ "$haystack" == *"$needle"* ]]; then + PASS=$((PASS+1)) + else + FAIL=$((FAIL+1)) + echo "FAIL: $desc (expected output to contain: $needle)" + echo " actual output: $haystack" + fi +} + +assert_not_contains() { + local haystack="$1" needle="$2" desc="$3" + if [[ "$haystack" != *"$needle"* ]]; then + PASS=$((PASS+1)) + else + FAIL=$((FAIL+1)) + echo "FAIL: $desc (output should NOT contain: $needle)" + echo " actual output: $haystack" + fi +} + +fresh_dir() { + local d + d=$(mktemp -d) + export FLOWHOOK_CONFIG_DIR_TEST="$d/config" + mkdir -p "$FLOWHOOK_CONFIG_DIR_TEST" + cd "$d" || exit 1 +} diff --git a/cli/tests/test_init.sh b/cli/tests/test_init.sh new file mode 100644 index 0000000..62592e3 --- /dev/null +++ b/cli/tests/test_init.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -uo pipefail + +PASS=0 +FAIL=0 + +source test_helpers.sh + +# --- init --- + +test_init_creates_task() { + fresh_dir + flowhook init >/dev/null 2>&1 + local code=$? + assert_exit 0 "$code" "init creates a task" + + local files + files=$(ls "$FLOWHOOK_CONFIG_DIR_TEST" 2>/dev/null) + if [[ -z "$files" ]]; then + FAIL=$((FAIL+1)) + echo "FAIL: init creates a task (no config file written)" + else + PASS=$((PASS+1)) + fi +} + +test_init_twice_fails() { + fresh_dir + flowhook init >/dev/null 2>&1 + local out + out=$(flowhook init 2>&1) + assert_contains "$out" "Error:" "init twice should print an Error" +} diff --git a/cli/tests/test_list.sh b/cli/tests/test_list.sh new file mode 100644 index 0000000..1c7ee1b --- /dev/null +++ b/cli/tests/test_list.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +set -uo pipefail + +PASS=0 +FAIL=0 + +source ./test_helpers.sh + +# test_list.sh + +test_list_no_flags_is_noop() { + fresh_dir + flowhook init >/dev/null 2>&1 + local out code + out=$(flowhook list 2>&1) + code=$? + assert_exit 0 "$code" "list with no flags exits 0" + if [[ -z "$out" ]]; then + PASS=$((PASS+1)) + else + FAIL=$((FAIL+1)) + echo "FAIL: list with no flags should print nothing" + echo " actual output: $out" + fi +} + +test_list_tasks_works_without_init_in_cwd() { + fresh_dir + # init in one dir, then list --tasks from a different, uninitialized dir + flowhook init >/dev/null 2>&1 + local task_dir + task_dir=$(pwd) + mkdir -p ../other_dir + cd ../other_dir || exit 1 + local out + out=$(flowhook list --tasks 2>&1) + assert_contains "$out" "$task_dir" "list --tasks shows tasks regardless of cwd" +} + +test_list_paths_fails_without_init_in_cwd() { + fresh_dir + # no init at all in this fresh dir + local out + out=$(flowhook list --paths --commands 2>&1) + assert_contains "$out" "Error: No flowhook task found" "list --paths/--commands errors when cwd has no task" +} + +test_list_ignored_shows_defaults() { + fresh_dir + flowhook init >/dev/null 2>&1 + local out + out=$(flowhook list --ignored 2>&1) + assert_contains "$out" ".git" "list --ignored shows default ignores even with no user additions" +} + +test_list_combined_flags_when_initialized() { + fresh_dir + flowhook init >/dev/null 2>&1 + mkdir -p combo + flowhook add --path ./combo --command "echo combo" >/dev/null 2>&1 + local out + out=$(flowhook list --paths --commands 2>&1) + assert_contains "$out" "combo" "combined list flags: path shown" + assert_contains "$out" "echo combo" "combined list flags: command shown" +} diff --git a/cli/tests/test_remove.sh b/cli/tests/test_remove.sh new file mode 100644 index 0000000..cc3a9c6 --- /dev/null +++ b/cli/tests/test_remove.sh @@ -0,0 +1,162 @@ +#!/usr/bin/env bash +set -uo pipefail + +PASS=0 +FAIL=0 + +# test_remove.sh + +# --- remove --path --- + +test_remove_path_removes_from_list() { + fresh_dir + flowhook init >/dev/null 2>&1 + local outside_dir + outside_dir=$(mktemp -d) + flowhook add --path "$outside_dir" >/dev/null 2>&1 + flowhook remove --path "$outside_dir" >/dev/null 2>&1 + local out + out=$(flowhook list --paths 2>&1) + assert_not_contains "$out" "$outside_dir" "remove --path removes externally-added dir from list --paths" + rm -rf "$outside_dir" +} +test_remove_nonexistent_path_errors() { + fresh_dir + flowhook init >/dev/null 2>&1 + local out + out=$(flowhook remove --path "nonexistent path" 2>&1) + assert_contains "$out" "Error:" "removing a path never added errors" +} + +# --- remove --command --- + +test_remove_command_removes_from_list() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --command "echo bye" >/dev/null 2>&1 + flowhook remove --command "echo bye" >/dev/null 2>&1 + local out + out=$(flowhook list --commands 2>&1) + assert_not_contains "$out" "echo bye" "remove --command removes it from list --commands" +} + +test_remove_nonexistent_command_errors() { + fresh_dir + flowhook init >/dev/null 2>&1 + local out + out=$(flowhook remove --command "nonexistent command" 2>&1) + assert_contains "$out" "Error:" "removing a command never added errors" +} + +# --- remove --on-success --- + +test_remove_on_success_removes_from_list() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --on-success "echo success" >/dev/null 2>&1 + flowhook remove --on-success "echo success" >/dev/null 2>&1 + local out + out=$(flowhook list --on-success 2>&1) + assert_not_contains "$out" "echo success" "remove --on-success removes it from list --on-success" +} + +test_remove_nonexistent_on_success_errors() { + fresh_dir + flowhook init >/dev/null 2>&1 + local out + out=$(flowhook remove --on-success "nonexistent" 2>&1) + assert_contains "$out" "Error:" "removing on-success never added errors" +} + +# --- remove --on-failure --- + +test_remove_on_failure_removes_from_list() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --on-failure "echo failure" >/dev/null 2>&1 + flowhook remove --on-failure "echo failure" >/dev/null 2>&1 + local out + out=$(flowhook list --on-failure 2>&1) + assert_not_contains "$out" "echo failure" "remove --on-failure removes it from list --on-failure" +} + +test_remove_nonexistent_on_failure_errors() { + fresh_dir + flowhook init >/dev/null 2>&1 + local out + out=$(flowhook remove --on-failure "nonexistent" 2>&1) + assert_contains "$out" "Error:" "removing on-failure never added errors" +} + +# --- remove --ignored-path --- + +test_remove_ignored_path_removes_from_list() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --ignored-path "myignoredpath" >/dev/null 2>&1 + flowhook remove --ignored-path "myignoredpath" >/dev/null 2>&1 + local out + out=$(flowhook list --ignored 2>&1) + assert_not_contains "$out" "myignoredpath" "remove --ignored-path removes it from list --ignored" +} + +test_remove_nonexistent_ignored_path_errors() { + fresh_dir + flowhook init >/dev/null 2>&1 + local out + out=$(flowhook remove --ignored-path "nonexistent" 2>&1) + assert_contains "$out" "Error:" "removing ignored-path never added errors" +} + +# --- remove --ignored-pattern --- + +test_remove_ignored_pattern_removes_from_list() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --ignored-pattern "*.jpeg" >/dev/null 2>&1 + flowhook remove --ignored-pattern "*.jpeg" >/dev/null 2>&1 + local out + out=$(flowhook list --ignored 2>&1) + assert_not_contains "$out" "*.jpeg" "remove --ignored-pattern removes it from list --ignored" +} + +test_remove_nonexistent_ignored_pattern_errors() { + fresh_dir + flowhook init >/dev/null 2>&1 + local out + out=$(flowhook remove --ignored-pattern "*.nonexistent" 2>&1) + assert_contains "$out" "Error:" "removing ignored-pattern never added errors" +} + +test_remove_whole_task_with_force_flag() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook remove -f >/dev/null 2>&1 + local out + out=$(flowhook list --tasks 2>&1) + local task_dir + task_dir=$(pwd) + assert_not_contains "$out" "$task_dir" "remove -f deletes the whole task" +} + +test_remove_whole_task_confirm_yes_deletes() { + fresh_dir + flowhook init >/dev/null 2>&1 + local task_dir + task_dir=$(pwd) + echo "yes" | flowhook remove >/dev/null 2>&1 + local out + out=$(flowhook list --tasks 2>&1) + assert_not_contains "$out" "$task_dir" "remove with 'yes' confirmation deletes the task" +} + +test_remove_whole_task_decline_keeps_task() { + fresh_dir + flowhook init >/dev/null 2>&1 + local task_dir + task_dir=$(pwd) + echo "no" | flowhook remove >/dev/null 2>&1 + local out + out=$(flowhook list --tasks 2>&1) + assert_contains "$out" "$task_dir" "remove declining confirmation keeps the task" +} diff --git a/cli/tests/test_run.sh b/cli/tests/test_run.sh new file mode 100644 index 0000000..34af7c3 --- /dev/null +++ b/cli/tests/test_run.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +set -uo pipefail + +PASS=0 +FAIL=0 + +source ./test_helpers.sh +# test_run.sh + +test_run_default_starts_and_exits_on_sigint() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --command "echo hi" >/dev/null 2>&1 + + local outfile + outfile=$(mktemp) + + flowhook run > "$outfile" 2>&1 & + local pid=$! + + sleep 1 + kill -INT "$pid" 2>/dev/null + + # Give it up to 3 seconds to exit gracefully; force-kill if it doesn't. + local waited=0 + while kill -0 "$pid" 2>/dev/null && [[ $waited -lt 3 ]]; do + sleep 1 + waited=$((waited+1)) + done + if kill -0 "$pid" 2>/dev/null; then + kill -9 "$pid" 2>/dev/null + FAIL=$((FAIL+1)) + echo "FAIL: run did not exit on SIGINT within 3s, force-killed" + else + PASS=$((PASS+1)) + fi + + local out + out=$(cat "$outfile") + assert_contains "$out" "Exiting safely" "run prints clean exit message on SIGINT" + rm -f "$outfile" +} + +test_run_all_starts_and_exits_on_sigint() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --command "echo hi" >/dev/null 2>&1 + + local outfile + outfile=$(mktemp) + + flowhook run --all > "$outfile" 2>&1 & + local pid=$! + + sleep 1 + kill -INT "$pid" + wait "$pid" 2>/dev/null + + local out + out=$(cat "$outfile") + assert_contains "$out" "Exiting safely" "run --all exits cleanly on SIGINT" + rm -f "$outfile" +} + +test_run_active_with_no_active_tasks_errors() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --command "echo hi" >/dev/null 2>&1 + # never set --active + + local out code + out=$(flowhook run --active 2>&1) + code=$? + assert_contains "$out" "Error: no active tasks to start" "run --active with no active tasks errors" +} + +test_run_active_with_active_task_starts_and_exits() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --command "echo hi" >/dev/null 2>&1 + flowhook set --active >/dev/null 2>&1 + + local outfile + outfile=$(mktemp) + + flowhook run --active > "$outfile" 2>&1 & + local pid=$! + + sleep 1 + kill -INT "$pid" + wait "$pid" 2>/dev/null + + local out + out=$(cat "$outfile") + assert_contains "$out" "Exiting safely" "run --active with an active task exits cleanly on SIGINT" + rm -f "$outfile" +} + +test_run_quiet_suppresses_command_output() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook add --command "echo SHOULD_NOT_APPEAR" >/dev/null 2>&1 + + local outfile + outfile=$(mktemp) + flowhook run --quiet > "$outfile" 2>&1 & + local pid=$! + sleep 1 + kill -INT "$pid" + wait "$pid" 2>/dev/null + + local out + out=$(cat "$outfile") + assert_not_contains "$out" "SHOULD_NOT_APPEAR" "run --quiet suppresses build output" + rm -f "$outfile" +} diff --git a/cli/tests/test_set.sh b/cli/tests/test_set.sh new file mode 100644 index 0000000..87f2ce9 --- /dev/null +++ b/cli/tests/test_set.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +set -uo pipefail + +PASS=0 +FAIL=0 + +source ./test_helpers.sh +# test_set.sh + +test_set_active() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook set --active >/dev/null 2>&1 + local out + out=$(flowhook check --active 2>&1) + assert_contains "$out" "is active" "set --active makes task active" +} + +test_set_deactive() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook set --active >/dev/null 2>&1 + flowhook set --deactive >/dev/null 2>&1 + local out + out=$(flowhook check --deactive 2>&1) + assert_contains "$out" "is deactive" "set --deactive makes task deactive" +} + +test_set_active_then_check_not_deactive() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook set --active >/dev/null 2>&1 + local out + out=$(flowhook check --deactive 2>&1) + assert_contains "$out" "not deactive" "active task is not deactive" +} + +test_set_deactive_then_check_not_active() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook set --deactive >/dev/null 2>&1 + local out + out=$(flowhook check --active 2>&1) + assert_contains "$out" "not active" "deactive task is not active" +} + +test_set_depth() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook set --depth 7 >/dev/null 2>&1 + local out + out=$(flowhook check --depth 2>&1) + assert_contains "$out" "7" "set --depth 7 reflected in check --depth" +} + +test_set_depth_overwrite() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook set --depth 3 >/dev/null 2>&1 + flowhook set --depth 9 >/dev/null 2>&1 + local out + out=$(flowhook check --depth 2>&1) + assert_contains "$out" "9" "set --depth overwrites previous depth" + assert_not_contains "$out" "3" "old depth not present after overwrite" +} + +test_set_active_and_deactive_together_last_wins() { + fresh_dir + flowhook init >/dev/null 2>&1 + flowhook set --active --deactive >/dev/null 2>&1 + local out + out=$(flowhook check --deactive 2>&1) + assert_contains "$out" "is deactive" "set --active --deactive together: deactive wins (last processed)" +} + +test_set_no_flags_is_noop() { + fresh_dir + flowhook init >/dev/null 2>&1 + local code + flowhook set >/dev/null 2>&1 + code=$? + assert_exit 0 "$code" "set with no flags does not crash" +} diff --git a/src/config_manager.cpp b/src/config_manager.cpp index b7085fd..2b3c405 100644 --- a/src/config_manager.cpp +++ b/src/config_manager.cpp @@ -25,9 +25,9 @@ namespace flowhook if (override) { - return std::filesystem::path(override) / "tasks.json"; + return std::filesystem::path(override) / "config.json"; } - return std::filesystem::path(home) / ".config" / "flowhook" / "tasks.json"; + return std::filesystem::path(home) / ".config" / "flowhook" / "config.json"; } Result ensure_config_dir() @@ -117,12 +117,19 @@ namespace flowhook } _task.commands = _commands; - vector _paths; - for (auto &cmd : json_task.at("paths")) + vector _file_paths; + for (auto &cmd : json_task.at("file_paths")) { - _paths.push_back(cmd); + _file_paths.push_back(cmd); } - _task.paths = _paths; + _task.file_paths = _file_paths; + + vector _dir_paths; + for (auto &cmd : json_task.at("dir_paths")) + { + _dir_paths.push_back(cmd); + } + _task.dir_paths = _dir_paths; vector _on_success; for (auto &cmd : json_task.at("on_success")) @@ -136,6 +143,7 @@ namespace flowhook { _on_failure.push_back(cmd); } + _task.on_failure = _on_failure; vector _ignored_paths; for (auto &path : json_task.at("ignored_paths")) @@ -151,8 +159,8 @@ namespace flowhook } _task.ignored_patterns = _ignored_patterns; - _task.on_failure = _on_failure; _task.isActive = json_task.at("isActive"); + _task.watching_depth = json_task.at("watching_depth"); return Result::Ok(_task); } @@ -166,11 +174,18 @@ namespace flowhook { _json_task["commands"].push_back(cmd); } - _json_task["paths"] = json::array(); - for (auto &path : task.paths) + _json_task["file_paths"] = json::array(); + for (auto &path : task.file_paths) { - _json_task["paths"].push_back(path); + _json_task["file_paths"].push_back(path); } + + _json_task["dir_paths"] = json::array(); + for (auto &path : task.dir_paths) + { + _json_task["dir_paths"].push_back(path); + } + _json_task["on_success"] = json::array(); for (auto &cmd : task.on_success) { @@ -190,6 +205,7 @@ namespace flowhook _json_task["ignored_patterns"].push_back(pattern); } _json_task["isActive"] = task.isActive; + _json_task["watching_depth"] = task.watching_depth; return Result::Ok(_json_task); } @@ -227,6 +243,7 @@ namespace flowhook { json _json_task = TRY(convert_task_to_json(task), void); *it = _json_task; + isflushed = false; flush(); return Result::Ok(); } @@ -279,12 +296,14 @@ namespace flowhook _task.name = it->at("task_name"); _task.id = it->at("working_directory"); _task.commands = it->at("commands"); - _task.paths = it->at("paths"); + _task.file_paths = it->at("file_paths"); + _task.dir_paths = it->at("dir_paths"); _task.on_success = it->at("on_success"); _task.on_failure = it->at("on_failure"); _task.ignored_paths = it->at("ignored_paths"); _task.ignored_patterns = it->at("ignored_patterns"); _task.isActive = it->at("isActive"); + _task.watching_depth = it->at("watching_depth"); tasks.push_back(_task); } return Result>::Ok(tasks); diff --git a/src/display.cpp b/src/display.cpp deleted file mode 100644 index 4517c7a..0000000 --- a/src/display.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include - -#include "./include/display.h" -#include "./include/filewatcher.h" - -using namespace std; - -namespace flowhook -{ - void print_header() - { - cout << "===================================================" << endl; - cout << "------------ FLOWHOOK v0.0.1 -------------------" << endl; - cout << "===================================================" << endl; - } - - string receive_input(const string &msg) - { - cout << msg; - string text_buffer; - getline(cin, text_buffer); - return text_buffer; - } - - void print_list(const string &msg, const vector &list) - { - cout << "__________________________________________________" << endl; - cout << msg << endl; - for (auto &path : list) - { - cout << path << endl; - } - } - - void print_numbered_list(const string msg, const vector &list) - { - cout << "__________________________________________________" << endl; - cout << msg << endl; - int i = 1; - for (auto &path : list) - { - cout << "[" << i << "] " << path << endl; - i++; - } - } -} diff --git a/src/filewatcher.cpp b/src/filewatcher.cpp index 1817e32..019769e 100644 --- a/src/filewatcher.cpp +++ b/src/filewatcher.cpp @@ -109,6 +109,14 @@ Result FileWatcher::handle_events(int fd, vector wd, Result FileWatcher::add_path(const string &arg) { lock_guard lock(registry_mutex); + for(auto [wd, path]: watch_registry) + { + if(path == arg){ + return Result::Err( + ErrorCode::PATH_ALREADY_EXISTS, + "Error: path " + arg + " already exists ✗"); + } + } int wd = inotify_add_watch(inotify_fd, arg.c_str(), IN_MOVED_TO | IN_MOVED_FROM | IN_MODIFY | IN_CLOSE_WRITE); @@ -125,10 +133,6 @@ Result FileWatcher::add_path(const string &arg) { Result FileWatcher::remove_path(const string &arg) { lock_guard lock(registry_mutex); - return remove_path_internal(arg); -} - -Result FileWatcher::remove_path_internal(const string &arg) { // check if path exists bool path_exists = false; for (auto [w, p] : watch_registry) { diff --git a/src/flowhook_core.cpp b/src/flowhook_core.cpp index c236cb6..01547ed 100644 --- a/src/flowhook_core.cpp +++ b/src/flowhook_core.cpp @@ -1,512 +1,560 @@ -#include -#include #include +#include +#include -#include "include/flowhook_core.h" -#include "include/error/result.h" #include "include/error/error.h" -#include "include/task_runner.h" +#include "include/error/result.h" +#include "include/flowhook_core.h" #include "include/macros.hpp" +#include "include/task_runner.h" using namespace std; namespace fs = std::filesystem; namespace flowhook { - Result FlowHookCore::create(){ - FlowHookCore* core = new FlowHookCore(); - TEST_OVERLOADED(core->init(), FlowHookCore*); - return Result::Ok(core); - } +Result FlowHookCore::create() { + FlowHookCore *core = new FlowHookCore(); + TEST_OVERLOADED(core->init(), FlowHookCore *); + return Result::Ok(core); +} - FlowHookCore::~FlowHookCore() - { - config_manager->flush(); - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - delete (*it); - } - } +FlowHookCore::~FlowHookCore() { + config_manager->flush(); + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + delete (*it); + } +} - Result FlowHookCore::set_default_ignored() - { - default_ignored_patterns = { - "*.o", "*.a", "*.so", "*.out", "*.exe", - "*.swp", "*.swo", "*~", ".#*", - "*.class", "*.pyc", "*.log", ".git" - }; - default_ignored_paths = { - ".git" - }; - return Result::Ok(); +Result FlowHookCore::set_default_ignored() { + default_ignored_patterns = {"*.o", "*.a", "*.so", "*.out", "*.exe", + "*.swp", "*.swo", "*~", ".#*", "*.class", + "*.pyc", "*.log", "*.git"}; + default_ignored_paths = {".git"}; + return Result::Ok(); +} + +Result FlowHookCore::init() { + config_manager = TRY(ConfigManager::create(), void); + FW_LOG("[DEBUG] initializing a FlowHook core instance ...."); + + vector tasks = TRY(config_manager->get_tasks(), void); + set_default_ignored(); + + FW_LOG("[DEBUG] loading tasks from config file ...."); + for (auto task : tasks) { + TaskRunner *tr = TRY(TaskRunner::create(task.name, task.id), void); + for (auto i : task.ignored_paths) + TEST(tr->add_ignored_path(i)); + for (auto ip : task.ignored_patterns) + TEST(tr->add_ignored_pattern(ip)); + if (task.isActive) { + tr->activate(); + } else { + tr->deactivate(); } + tr->set_depth(task.watching_depth); - Result FlowHookCore::init() + for (auto c : task.commands) + TEST(tr->add_command(c)); + for (auto p : task.file_paths) { - config_manager = TRY(ConfigManager::create(), void); - FW_LOG("[DEBUG] initializing a FlowHook core instance ...."); - - vector tasks = TRY(config_manager->get_tasks(), void); - set_default_ignored(); - - FW_LOG("[DEBUG] loading tasks from config file ...."); - for(auto task: tasks) + auto r = tr->add_path(p); + if(r.isErr()) { - TaskRunner* tr = TRY(TaskRunner::create(task.name, task.id), void); - - for(auto c: task.commands) - tr->add_command(c); - for(auto p: task.paths) - tr->add_path(p); - for(auto s: task.on_success) - tr->add_on_success(s); - for(auto f: task.on_failure) - tr->add_on_failure(f); - - for(auto i: task.ignored_paths) - tr->add_ignored_path(i); - for(auto ip: task.ignored_patterns) - tr->add_ignored_pattern(ip); - if(task.isActive) + if(r.getErrCode() == ErrorCode::PATH_NOT_FOUND) { - tr->activate(); - }else { - tr->deactivate(); + tr->delete_path(p); } - task_runners.push_back(tr); - } - FW_LOG("[DEBUG] loading tasks from config file completed. ✓"); - FW_VERBOSE("[FLOWHOOK] Flowhook core initialized."); - return Result::Ok(); - } - - Result FlowHookCore::create_task(const std::string &task_name, const std::string &task_id) - { - FW_LOG("[DEBUG] Creating task..."); - if(task_name.empty()) - { - return Result::Err(FWError::make(ErrorCode::EMPTY_VALUE, "Error: task name cannot be empty ✗")); - } - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - if((*it) == nullptr) continue; - string id = (*it)->get_task_id(); - if(id == task_id) + else { - return Result::Err(FWError::make(ErrorCode::DUPLICATE_ENTRY, "Error: task already exists ✗")); + return Result::Err(r.unwrapErr()); } } - - if(!fs::exists(task_id) || !fs::is_directory(task_id)) - { - return Result::Err(FWError::make(ErrorCode::PATH_NOT_FOUND, "Error: working directory not found ✗")); - } - - if(task_runners.size() >= 100) - { - return Result::Err(FWError::make(ErrorCode::TASK_FULL, "Error: task limit reached ✗")); - } - auto result = TRY(TaskRunner::create(task_name, task_id), void); - - for(auto i: default_ignored_paths) - result->add_ignored_path(i); - for(auto ip: default_ignored_patterns) - result->add_ignored_pattern(ip); - - - task_runners.push_back(result); - TEST(config_manager->log_task(result->get_task())); - - FW_LOG("[DEBUG] logging task to config file completed. ✓"); - FW_VERBOSE("[FLOWHOOK] Task created: name='" + task_name + "' path='" + task_id + "' ✓"); - return Result::Ok(); } - - Result FlowHookCore::delete_task(const std::string &task_id) + for (auto p : task.dir_paths) { - FW_LOG("[DEBUG] Deleting task..."); - for(auto it = task_runners.begin(); it != task_runners.end(); it++) + auto r = tr->add_path(p); + if(r.isErr()) { - string name = (*it)->get_task_id(); - if(name == task_id) + if(r.getErrCode() == ErrorCode::PATH_NOT_FOUND) { - Task _task_to_be_deleted = (*it)->get_task(); - TEST(config_manager->delete_task(_task_to_be_deleted)); - task_runners.erase(it); - FW_VERBOSE("[FLOWHOOK] Task deleted: " + task_id + "' ✓"); - return Result::Ok(); + tr->delete_path(p); + } + else + { + return Result::Err(r.unwrapErr()); } } + } + for (auto s : task.on_success) + TEST(tr->add_on_success(s)); + for (auto f : task.on_failure) + TEST(tr->add_on_failure(f)); + + task_runners.push_back(tr); + } + FW_LOG("[DEBUG] loading tasks from config file completed. ✓"); + FW_VERBOSE("[FLOWHOOK] Flowhook core initialized."); + return Result::Ok(); +} +Result FlowHookCore::create_task(const std::string &task_name, + const std::string &task_id) { + FW_LOG("[DEBUG] Creating task..."); + if (task_name.empty()) { + return Result::Err(FWError::make( + ErrorCode::EMPTY_VALUE, "Error: task name cannot be empty ✗")); + } + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + if ((*it) == nullptr) + continue; + string id = (*it)->get_task_id(); + if (id == task_id) { + return Result::Err(FWError::make(ErrorCode::DUPLICATE_ENTRY, + "Error: task already exists ✗")); + } + } + + if (!fs::exists(task_id) || !fs::is_directory(task_id)) { + return Result::Err(FWError::make( + ErrorCode::PATH_NOT_FOUND, "Error: working directory not found ✗")); + } + + if (task_runners.size() >= 200) { + return Result::Err( + FWError::make(ErrorCode::TASK_FULL, "Error: task limit reached ✗")); + } + auto result = TRY(TaskRunner::create(task_name, task_id), void); + + for (auto i : default_ignored_paths) + result->add_ignored_path(i); + for (auto ip : default_ignored_patterns) + result->add_ignored_pattern(ip); + + task_runners.push_back(result); + TEST(config_manager->log_task(result->get_task())); + + FW_LOG("[DEBUG] logging task to config file completed. ✓"); + FW_VERBOSE("[FLOWHOOK] Task created: name='" + task_name + "' path='" + + task_id + "' ✓"); + return Result::Ok(); +} - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +Result FlowHookCore::delete_task(const std::string &task_id) { + FW_LOG("[DEBUG] Deleting task..."); + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string name = (*it)->get_task_id(); + if (name == task_id) { + Task _task_to_be_deleted = (*it)->get_task(); + TEST(config_manager->delete_task(_task_to_be_deleted)); + task_runners.erase(it); + FW_VERBOSE("[FLOWHOOK] Task deleted: " + task_id + "' ✓"); + return Result::Ok(); } + } - Result FlowHookCore::activate_task(const std::string &task_name) - { - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string name = (*it)->get_task_name(); - if(name == task_name) - { - (*it)->activate(); - config_manager->update_task((*it)->get_task()); - FW_VERBOSE("[FLOWHOOK] Task activated: " + task_name + " ✓"); - return Result::Ok(); - } - } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +} - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_name + " ✗")); +bool FlowHookCore::is_task(const std::string &task_id) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + if ((*it)->get_task_id() == task_id) { + return true; } + } + return false; +} - Result FlowHookCore::deactivate_task(const std::string &task_id) - { - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - (*it)->deactivate(); - FW_VERBOSE("[FLOWHOOk] Task deactivated: " + task_id + " ✓"); - return Result::Ok(); - } - } - - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +Result FlowHookCore::set_depth(const std::string &task_id, int depth) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + if ((*it)->get_task_id() == task_id) { + TEST((*it)->set_depth(depth)); + config_manager->update_task((*it)->get_task()); + return Result::Ok(); } + } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +} - Result FlowHookCore::set_task_path(const std::string &task_id, const std::string &path) - { - if(!fs::exists(path)) - { - return Result::Err(FWError::make(ErrorCode::PATH_NOT_FOUND, "Error: path not found " + path + " ✗")); - } - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - TEST((*it)->add_path(path)); - config_manager->update_task((*it)->get_task()); - FW_LOG("[DEBUG] Task path set: " + task_id + " ✓"); - return Result::Ok(); - } - } +std::vector +FlowHookCore::get_resolved_files(const std::string task_id) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + if ((*it)->get_task_id() == task_id) { + return (*it)->get_resolved_files(); + } + } + return std::vector(); +} - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +Result FlowHookCore::get_task_depth(const std::string &task_id) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + if ((*it)->get_task_id() == task_id) { + auto t = (*it)->get_task(); + return Result::Ok(t.watching_depth); } + } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +} - Result FlowHookCore::delete_task_path(const std::string &task_id, const std::string &path) - { - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - TEST((*it)->delete_path(path)); - config_manager->update_task((*it)->get_task()); - FW_LOG("[DEBUG] Task path deleted: " + task_id + " ✓"); - return Result::Ok(); - } - } +Result FlowHookCore::is_task_active(const std::string &task_id) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + if ((*it)->get_task_id() == task_id) { + return Result::Ok((*it)->is_active()); + } + } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +} - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +Result FlowHookCore::activate_task(const std::string &task_id) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + (*it)->activate(); + config_manager->update_task((*it)->get_task()); + FW_VERBOSE("[FLOWHOOK] Task activated: " + task_id + " ✓"); + return Result::Ok(); } + } - Result FlowHookCore::set_ignored_path(const std::string &task_id, const std::string &path) - { - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - TEST((*it)->add_ignored_path(path)); - config_manager->update_task((*it)->get_task()); - FW_LOG("[DEBUG] Task ignore path set: " + task_id + " ✓"); - return Result::Ok(); - } - } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +} - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +Result FlowHookCore::deactivate_task(const std::string &task_id) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + (*it)->deactivate(); + config_manager->update_task((*it)->get_task()); + FW_VERBOSE("[FLOWHOOk] Task deactivated: " + task_id + " ✓"); + return Result::Ok(); } + } - Result FlowHookCore::set_ignored_pattern(const std::string &task_id, const std::string &pattern) - { - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - TEST((*it)->add_ignored_pattern(pattern)); - config_manager->update_task((*it)->get_task()); - FW_LOG("[DEBUG] Task ignore pattern set: " + task_id + " ✓"); - return Result::Ok(); - } - } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +} - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +Result FlowHookCore::set_task_path(const std::string &task_id, + const std::string &path) { + if (!fs::exists(path)) { + return Result::Err(FWError::make( + ErrorCode::PATH_NOT_FOUND, "Error: path not found " + path + " ✗")); + } + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + TEST((*it)->add_path(path)); + config_manager->update_task((*it)->get_task()); + FW_LOG("[DEBUG] Task path set: " + task_id + " ✓"); + return Result::Ok(); } + } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +} - Result FlowHookCore::remove_ignored_path(const std::string &task_id, const std::string &path) - { - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - TEST((*it)->remove_ignored_path(path)); - config_manager->update_task((*it)->get_task()); - FW_LOG("[DEBUG] Ignored path removed: " + task_id + " ✓"); - return Result::Ok(); - } - } - - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +Result FlowHookCore::delete_task_path(const std::string &task_id, + const std::string &path) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + TEST((*it)->delete_path(path)); + config_manager->update_task((*it)->get_task()); + FW_LOG("[DEBUG] Task path deleted: " + task_id + " ✓"); + return Result::Ok(); } + } - Result FlowHookCore::remove_ignored_pattern(const std::string &task_id, const std::string &pattern) - { - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - TEST((*it)->remove_ignored_pattern(pattern)); - config_manager->update_task((*it)->get_task()); - FW_LOG("[DEBUG] Ignored pattern removed: " + task_id + " ✓"); - return Result::Ok(); - } - } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +} - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +Result FlowHookCore::set_ignored_path(const std::string &task_id, + const std::string &path) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + TEST((*it)->add_ignored_path(path)); + config_manager->update_task((*it)->get_task()); + FW_LOG("[DEBUG] Task ignore path set: " + task_id + " ✓"); + return Result::Ok(); } + } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +} - Result FlowHookCore::set_task_command(const std::string &task_id, const std::string &command) - { - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - TEST((*it)->add_command(command)); - config_manager->update_task((*it)->get_task()); - FW_LOG("[DEBUG] Task command set: " + task_id + " ✓"); - return Result::Ok(); - } - } - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +Result FlowHookCore::set_ignored_pattern(const std::string &task_id, + const std::string &pattern) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + TEST((*it)->add_ignored_pattern(pattern)); + config_manager->update_task((*it)->get_task()); + FW_LOG("[DEBUG] Task ignore pattern set: " + task_id + " ✓"); + return Result::Ok(); } + } - Result FlowHookCore::delete_task_command(const std::string &task_id, const std::string &command) - { - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - TEST((*it)->delete_command(command)); - config_manager->update_task((*it)->get_task()); - FW_LOG("[DEBUG] Task command deleted: " + task_id + " ✓"); - return Result::Ok(); - } - } - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +} + +Result FlowHookCore::remove_ignored_path(const std::string &task_id, + const std::string &path) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + TEST((*it)->remove_ignored_path(path)); + config_manager->update_task((*it)->get_task()); + FW_LOG("[DEBUG] Ignored path removed: " + task_id + " ✓"); + return Result::Ok(); } + } - Result FlowHookCore::set_task_on_success(const std::string &task_id, const std::string &command) - { - if(task_id.empty() || command.empty()) - { - return Result::Err(FWError::make(ErrorCode::EMPTY_VALUE, "Error: task id and command cannot be empty " + task_id + " ✗ ")); - } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +} - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - TEST((*it)->add_on_success(command)); - config_manager->update_task((*it)->get_task()); - FW_LOG("[DEBUG] Task on success set: " + task_id + " ✓"); - return Result::Ok(); - } - } - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +Result FlowHookCore::remove_ignored_pattern(const std::string &task_id, + const std::string &pattern) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + TEST((*it)->remove_ignored_pattern(pattern)); + config_manager->update_task((*it)->get_task()); + FW_LOG("[DEBUG] Ignored pattern removed: " + task_id + " ✓"); + return Result::Ok(); } + } - Result FlowHookCore::delete_task_on_success(const std::string &task_id, const std::string &command) - { - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - TEST((*it)->delete_on_success(command)); - config_manager->update_task((*it)->get_task()); - FW_LOG("[DEBUG] Task on success deleted: " + task_id + " ✓"); - return Result::Ok(); - } - } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +} - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +Result FlowHookCore::set_task_command(const std::string &task_id, + const std::string &command) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + TEST((*it)->add_command(command)); + config_manager->update_task((*it)->get_task()); + FW_LOG("[DEBUG] Task command set: " + task_id + " ✓"); + return Result::Ok(); } + } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +} - Result FlowHookCore::set_task_on_failure(const std::string &task_id, const std::string &command) - { - if(task_id.empty() || command.empty()) - { - return Result::Err(FWError::make(ErrorCode::EMPTY_VALUE, "Error: task id and command cannot be empty " + task_id + " ✗ ")); - } - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - TEST((*it)->add_on_failure(command)); - config_manager->update_task((*it)->get_task()); - FW_LOG("[DEBUG] Task on failure set: " + task_id + " ✓"); - return Result::Ok(); - } - } - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +Result FlowHookCore::delete_task_command(const std::string &task_id, + const std::string &command) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + TEST((*it)->delete_command(command)); + config_manager->update_task((*it)->get_task()); + FW_LOG("[DEBUG] Task command deleted: " + task_id + " ✓"); + return Result::Ok(); } + } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +} - Result FlowHookCore::delete_task_on_failure(const std::string &task_id, const std::string &command) - { - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - TEST((*it)->delete_on_failure(command)); - config_manager->update_task((*it)->get_task()); - FW_LOG("[DEBUG] Task on failure deleted: " + task_id + " ✓"); - return Result::Ok(); - } - } - - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +Result FlowHookCore::set_task_on_success(const std::string &task_id, + const std::string &command) { + if (task_id.empty() || command.empty()) { + return Result::Err(FWError::make( + ErrorCode::EMPTY_VALUE, + "Error: task id and command cannot be empty " + task_id + " ✗ ")); + } + + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + TEST((*it)->add_on_success(command)); + config_manager->update_task((*it)->get_task()); + FW_LOG("[DEBUG] Task on success set: " + task_id + " ✓"); + return Result::Ok(); } + } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +} +Result FlowHookCore::delete_task_on_success(const std::string &task_id, + const std::string &command) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + TEST((*it)->delete_on_success(command)); + config_manager->update_task((*it)->get_task()); + FW_LOG("[DEBUG] Task on success deleted: " + task_id + " ✓"); + return Result::Ok(); + } + } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +} - Result FlowHookCore::start_task(const std::string &task_id) - { - FW_LOG("[DEBUG] Starting task: " + task_id + " ..."); - FW_LOG("[DEBUG] Looping through tasks to find the correct one ..."); - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - FW_LOG("[DEBUG] Task found: " + task_id + " ✓"); - if((*it)->is_running()) - { - return Result::Err(FWError::make(ErrorCode::TASK_ALREADY_RUNNING, "Error: task already running " + task_id + " ✗")); - } - FW_LOG("[DEBUG] Starting task_runner..."); - (*it)->start(); - FW_VERBOSE("[FLOWHOOK] Task started: " + task_id + " ✓"); - return Result::Ok(); - } - } - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +Result FlowHookCore::set_task_on_failure(const std::string &task_id, + const std::string &command) { + if (task_id.empty() || command.empty()) { + return Result::Err(FWError::make( + ErrorCode::EMPTY_VALUE, + "Error: task id and command cannot be empty " + task_id + " ✗ ")); + } + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + TEST((*it)->add_on_failure(command)); + config_manager->update_task((*it)->get_task()); + FW_LOG("[DEBUG] Task on failure set: " + task_id + " ✓"); + return Result::Ok(); } + } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +} - Result FlowHookCore::stop_task(const std::string &task_id) - { - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - string id = (*it)->get_task_id(); - if(id == task_id) - { - FW_LOG("[DEBUG] Stopping task: " + task_id + " ✗"); - if(!(*it)->is_running()) - { - return Result::Err(FWError::make(ErrorCode::TASK_NOT_RUNNING, "Error: task not running " + task_id + " ✗")); - } - (*it)->stop(); - FW_VERBOSE("[FLOWHOOK] Task stopped: " + task_id + " ✓"); - return Result::Ok(); - } - } - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +Result FlowHookCore::delete_task_on_failure(const std::string &task_id, + const std::string &command) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + TEST((*it)->delete_on_failure(command)); + config_manager->update_task((*it)->get_task()); + FW_LOG("[DEBUG] Task on failure deleted: " + task_id + " ✓"); + return Result::Ok(); } + } - Result FlowHookCore::start_all() - { - if(task_runners.empty()) - { - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: no tasks to start ✗")); - } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗ ")); +} - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - if((*it)->is_running()){ - return Result::Err(FWError::make(ErrorCode::TASK_ALREADY_RUNNING, "Error: task already running ✗")); - } - FW_LOG("[DEBUG] Starting task: " + (*it)->get_task_id() + " ..."); - TEST((*it)->start()); - } - FW_VERBOSE("[FLOWHOOK] All tasks started ✓"); - return Result::Ok(); +Result FlowHookCore::start_task(const std::string &task_id) { + FW_LOG("[DEBUG] Starting task: " + task_id + " ..."); + FW_LOG("[DEBUG] Looping through tasks to find the correct one ..."); + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + FW_LOG("[DEBUG] Task found: " + task_id + " ✓"); + if ((*it)->is_running()) { + return Result::Err( + FWError::make(ErrorCode::TASK_ALREADY_RUNNING, + "Error: task already running " + task_id + " ✗")); + } + FW_LOG("[DEBUG] Starting task_runner..."); + (*it)->start(); + FW_VERBOSE("[FLOWHOOK] Task started: " + task_id + " ✓"); + return Result::Ok(); } + } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +} - Result FlowHookCore::stop_all() - { - if(task_runners.empty()) - { - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: no tasks to stop ✗")); - } - - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - FW_LOG("[DEBUG] Stopping task " + (*it)->get_task_id() + " ..."); - TEST((*it)->stop()); - } - FW_VERBOSE("[FLOWHOOK] All tasks stopped ✓"); - return Result::Ok(); +Result FlowHookCore::stop_task(const std::string &task_id) { + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + string id = (*it)->get_task_id(); + if (id == task_id) { + FW_LOG("[DEBUG] Stopping task: " + task_id + " ✗"); + if (!(*it)->is_running()) { + return Result::Err( + FWError::make(ErrorCode::TASK_NOT_RUNNING, + "Error: task not running " + task_id + " ✗")); + } + (*it)->stop(); + FW_VERBOSE("[FLOWHOOK] Task stopped: " + task_id + " ✓"); + return Result::Ok(); } + } + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: task not found " + task_id + " ✗")); +} - Result FlowHookCore::start_active() - { - if(task_runners.empty()) - { - return Result::Err(FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: no tasks to start ✗")); - } +Result FlowHookCore::start_all() { + if (task_runners.empty()) { + return Result::Err( + FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: no tasks to start ✗")); + } - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - if((*it)->is_active()) - { - FW_LOG("[DEBUG] Starting task " + (*it)->get_task_id() + " ..."); - TEST((*it)->start()); - } - } - FW_VERBOSE("[FLOWHOOK] All active tasks started ✓"); - return Result::Ok(); + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + if ((*it)->is_running()) { + return Result::Err(FWError::make(ErrorCode::TASK_ALREADY_RUNNING, + "Error: task already running ✗")); } + FW_LOG("[DEBUG] Starting task: " + (*it)->get_task_id() + " ..."); + TEST((*it)->start()); + } + FW_VERBOSE("[FLOWHOOK] All tasks started ✓"); + return Result::Ok(); +} - std::vector FlowHookCore::get_tasks() const - { - vector tasks; - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - tasks.push_back((*it)->get_task()); - } - return tasks; - } +Result FlowHookCore::stop_all() { + if (task_runners.empty()) { + return Result::Err( + FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: no tasks to stop ✗")); + } + + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + FW_LOG("[DEBUG] Stopping task " + (*it)->get_task_id() + " ..."); + TEST((*it)->stop()); + } + FW_VERBOSE("[FLOWHOOK] All tasks stopped ✓"); + return Result::Ok(); +} - Result> FlowHookCore::get_watch_list(const std::string &task_id) - { - vector watched_paths; - for(auto it = task_runners.begin(); it != task_runners.end(); it++) - { - if((*it)->get_task_id() == task_id) - watched_paths = TRY((*it)->get_watch_list(), vector); - } - return Result>::Ok(watched_paths); +Result FlowHookCore::start_active() { + if (task_runners.empty()) { + return Result::Err( + FWError::make(ErrorCode::TASK_NOT_FOUND, "Error: no tasks to start ✗")); + } + + bool found_active = false; + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + if ((*it)->is_active()) { + found_active = true; + FW_LOG("[DEBUG] Starting task " + (*it)->get_task_id() + " ..."); + TEST((*it)->start()); } + } + if (!found_active) { + return Result::Err(FWError::make( + ErrorCode::TASK_NOT_FOUND, "Error: no active tasks to start ✗")); + } + FW_VERBOSE("[FLOWHOOK] All active tasks started ✓"); + return Result::Ok(); +} + +std::vector FlowHookCore::get_tasks() const { + vector tasks; + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + tasks.push_back((*it)->get_task()); + } + return tasks; +} + +Result> +FlowHookCore::get_watch_list(const std::string &task_id) { + vector watched_paths; + for (auto it = task_runners.begin(); it != task_runners.end(); it++) { + if ((*it)->get_task_id() == task_id) + watched_paths = TRY((*it)->get_watch_list(), vector); + } + return Result>::Ok(watched_paths); + + // activate } +} // namespace flowhook diff --git a/src/include/error/result.h b/src/include/error/result.h index ccf2204..eeabd1e 100644 --- a/src/include/error/result.h +++ b/src/include/error/result.h @@ -1,8 +1,6 @@ #pragma once -#include #include -#include #include #include diff --git a/src/include/filewatcher.h b/src/include/filewatcher.h index 92e2b19..0f13ea4 100644 --- a/src/include/filewatcher.h +++ b/src/include/filewatcher.h @@ -61,11 +61,8 @@ namespace flowhook bool is_running() const { return isWatching; } Result add_path(const std::string &arg); - Result add_path_internal(const std::string &arg, int MAX_DEPTH, int CURRENT_DEPTH); Result remove_path(const std::string &arg); - Result remove_path_internal(const std::string &arg); - bool isIgnored(const std::string &path); Result start(int timeout); Result stop(); diff --git a/src/include/flowhook_core.h b/src/include/flowhook_core.h index 0386bf2..d9adec4 100644 --- a/src/include/flowhook_core.h +++ b/src/include/flowhook_core.h @@ -27,8 +27,14 @@ namespace flowhook { ~FlowHookCore(); Result set_default_ignored(); + Result set_depth(const std::string &task_id, int depth); Result create_task(const std::string &task_name, const std::string &task_id); Result delete_task(const std::string &task_id); + bool is_task(const std::string &task_id); + + std::vector get_resolved_files(const std::string task_id); + Result is_task_active(const std::string &task_id); + Result get_task_depth(const std::string &task_id); Result start_task(const std::string &task_id); Result stop_task(const std::string &task_id); diff --git a/src/include/task_runner.h b/src/include/task_runner.h index a4202ec..b75b150 100644 --- a/src/include/task_runner.h +++ b/src/include/task_runner.h @@ -25,10 +25,12 @@ namespace flowhook bool flushed; bool is_init = false; int execution_id = 0; + std::vector resolved_files; TaskRunner() = default; Result init(const std::string &task_name, const std::string &working_directory); Result add_path_internal(const std::string &path, int MAX_DEPTH, int CURRENT_DEPTH); + bool check_path_existence(const std::string &path); public: static Result create(const std::string &task_name, const std::string &working_directory); ~TaskRunner(); @@ -36,6 +38,8 @@ namespace flowhook std::string get_task_name() const { return task.name; } std::string get_task_id() const { return task.id; } Task get_task() const { return task; } + std::vector get_resolved_files() const { return resolved_files; } + bool isIgnored(const std::string &path); @@ -56,7 +60,7 @@ namespace flowhook Result add_on_failure(const std::string &command); Result delete_on_failure(const std::string &command); - Result add_path(const std::string &path, int MAX_DEPTH = 1); + Result add_path(const std::string &path); Result delete_path(const std::string &path); Result add_ignored_path(const std::string &path); diff --git a/src/include/types.h b/src/include/types.h index 1be8cc4..d340f3a 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -66,9 +66,10 @@ namespace flowhook { std::string id = ""; // this is a name that will be used internally and always unique, not assigned by the user. Mostly the absolute path of the cwd std::string name = ""; // user assigned name can be duplicated, defaults to the filename of the cwd - int watching_depth = 1; + int watching_depth = 3; std::vector commands = {}; - std::vector paths = {}; + std::vector file_paths = {}; + std::vector dir_paths = {}; std::vector on_success = {}; std::vector on_failure = {}; @@ -79,8 +80,8 @@ namespace flowhook bool isRunning = false; bool isNull() const { - return id.empty() && name.empty() && commands.empty() && paths.empty() && - on_success.empty() && on_failure.empty() && !isActive && !isRunning && watching_depth == 1 && + return id.empty() && name.empty() && commands.empty() && file_paths.empty() && dir_paths.empty() && + on_success.empty() && on_failure.empty() && !isActive && !isRunning && watching_depth == 3 && ignored_paths.empty() && ignored_patterns.empty(); } }; diff --git a/src/session_logger.cpp b/src/session_logger.cpp index 83d872d..2c91500 100644 --- a/src/session_logger.cpp +++ b/src/session_logger.cpp @@ -47,6 +47,11 @@ namespace flowhook Result SessionLogger::start() { + if (is_running) + { + return Result::Ok(); // idempotent + } + FW_LOG("[DEBUG] Starting a new log session ..."); file.open(file_path, ios::out | ios::app); if (!file.is_open()) @@ -73,6 +78,11 @@ namespace flowhook Result SessionLogger::log_execution(const ExecutionResult &execution_result) { + if(!is_running) + { + return Result::Err(FWError::make( + ErrorCode::SESSION_LOGGER_NOT_RUNNING, "Error: session logger not initialized. ✗")); + } if (session.empty()) { return Result::Err(FWError::make( @@ -126,7 +136,7 @@ namespace flowhook { if (!is_running) { - return Result::Err(FWError::make(ErrorCode::SESSION_LOGGER_NOT_RUNNING, "Error: session logger not running. ✗")); + return Result::Ok(); // idempotent } if (!file.is_open()) diff --git a/src/task_runner.cpp b/src/task_runner.cpp index 75afae4..83d2574 100644 --- a/src/task_runner.cpp +++ b/src/task_runner.cpp @@ -38,7 +38,7 @@ Result TaskRunner::init(const string &task_name, working_directory + ". ✗")); } - string file_name = task_name + ".log"; + string file_name = task_name + "-flowhook.log"; fs::path _file_path = fs::path(working_directory) / file_name; sl = TRY(SessionLogger::create(_file_path.string()), void); @@ -63,17 +63,18 @@ TaskRunner::~TaskRunner() { } Result TaskRunner::set_depth(int num) { - if (num > 6) { - return Result::Err( - FWError::make(ErrorCode::INVALID_DEPTH, - "Error: invalid depth set - depth too much ✗")); - } else if (num < 1) { - return Result::Err( - FWError::make(ErrorCode::INVALID_DEPTH, - "Error: invalid depth set - depth set too low ✗")); + if (num > 10) { + return Result::Err(FWError::make( + ErrorCode::INVALID_DEPTH, "Error: invalid depth set - depth too much " + "✗, use a depth between 0 and 10")); + } else if (num < -1) { + return Result::Err(FWError::make( + ErrorCode::INVALID_DEPTH, "Error: invalid depth set - depth set too " + "low ✗, use a depth between 0 and 10")); } task.watching_depth = num; + FW_LOG("[DEBUG] Depth set to " + std::to_string(num)); return Result::Ok(); } @@ -117,15 +118,17 @@ TaskRunner::change_working_directory(const string &working_directory) { } bool TaskRunner::isIgnored(const string &path) { + string filename = fs::path(path).filename().string(); for (auto &p : task.ignored_paths) { - if (p == path) + if (p == path || p == filename) return true; } - string filename = fs::path(path).filename().string(); for (auto &p : task.ignored_patterns) { if (fnmatch(p.c_str(), filename.c_str(), 0) == 0) return true; + if (fnmatch(p.c_str(), path.c_str(), 0) == 0) + return true; } return false; } @@ -142,8 +145,7 @@ Result TaskRunner::add_ignored_path(const string &path) { } } task.ignored_paths.push_back(path); - FW_LOG("[DEBUG] Adding ignored path " + path + - " to Task completed. ✓"); + FW_LOG("[DEBUG] Adding ignored path " + path + " to Task completed. ✓"); return Result::Ok(); } @@ -159,8 +161,7 @@ Result TaskRunner::add_ignored_pattern(const string &pattern) { } } task.ignored_patterns.push_back(pattern); - FW_LOG("[DEBUG] Adding ignored pattern " + pattern + - " to Task completed. ✓"); + FW_LOG("[DEBUG] Adding ignored pattern " + pattern + " to Task completed. ✓"); return Result::Ok(); } @@ -222,18 +223,48 @@ Result TaskRunner::delete_command(const string &command) { FWError::make(ErrorCode::COMMAND_NOT_FOUND, "Error: command not found")); } -Result TaskRunner::add_path(const string &path, int MAX_DEPTH) { - TEST(add_path_internal(path, MAX_DEPTH, 0)); - if(fs::is_directory(path)) - { - FW_VERBOSE("[FLOWHOOK] Adding " + path + "'s children to task runner completed. ✓"); +bool TaskRunner::check_path_existence(const string &path) { + for (auto &p : task.file_paths) { + if (p == path) { + return true; + } + } + for (auto &p : task.dir_paths) { + if (p == path) { + return true; + } + } + return false; +} + +Result TaskRunner::add_path(const string &path) { + if (check_path_existence(path)) + return Result::Ok(); // idempotent + if (!fs::exists(path)) { + return Result::Err( + FWError::make(ErrorCode::PATH_NOT_FOUND, + "Error: provided path " + path + " does not exist! ✗ \n // use the `flowhook remove --path ` command to remove it from the task")); + } + if (isIgnored(path)) { + FW_LOG("[DEBUG] Path " + path + " matches ignored paths and patterns."); + return Result::Ok(); + } + + TEST(add_path_internal(path, task.watching_depth, 0)); + if (fs::is_directory(path)) { + task.dir_paths.push_back(path); + FW_VERBOSE("[FLOWHOOK] Adding " + path + + "'s children to task runner completed. ✓"); } else - FW_VERBOSE("[FLOWHOOK] Adding path " + path + " to task runner completed. ✓"); + task.file_paths.push_back(path); + FW_VERBOSE("[FLOWHOOK] Adding path " + path + " to task runner completed. ✓"); return Result::Ok(); } Result TaskRunner::add_path_internal(const string &path, int MAX_DEPTH, int CURRENT_DEPTH) { + if (check_path_existence(path)) + return Result::Ok(); // idempotent // if path is directory add each files inside it iteratively if (isIgnored(path)) { FW_LOG("[DEBUG] Path " + path + " matches ignored paths and patterns."); @@ -245,53 +276,71 @@ Result TaskRunner::add_path_internal(const string &path, int MAX_DEPTH, FW_LOG("[DEBUG] Path " + path + " is a directory adding child files recursively..."); for (auto &entry : fs::directory_iterator(path)) { + if (isIgnored(entry.path().string())) { + FW_LOG("[DEBUG] Path " + entry.path().string() + + " matches ignored patterns."); + FW_LOG("[DEBUG] Adding Path " + entry.path().string() + + " to task failed. ✗"); + continue; + } + if (entry.is_regular_file()) { FW_LOG("[DEBUG] Adding path " + entry.path().string() + " to filewatcher..."); - FW_LOG("[DEBUG] Checking if Path " + entry.path().string() + + FW_LOG("[DEBUG] Checking if resolved file path " + + entry.path().string() + " matches ignored paths and patterns ..."); - if (isIgnored(entry.path().string())) { - FW_LOG("[DEBUG] Path " + entry.path().string() + - " matches ignored patterns."); - FW_LOG("[DEBUG] Adding Path " + entry.path().string() + - " to task failed. ✗"); - continue; - } - task.paths.push_back(entry.path().string()); + resolved_files.push_back(entry.path().string()); fw->add_path(entry.path().string()); FW_LOG("[DEBUG] Adding path " + entry.path().string() + " to task completed. ✓"); } else if (entry.is_directory()) { - if (MAX_DEPTH > CURRENT_DEPTH) - TEST(add_path_internal(entry.path(), MAX_DEPTH, CURRENT_DEPTH + 1)); - else + if (MAX_DEPTH > CURRENT_DEPTH) { + resolved_files.push_back(entry.path().string()); + TEST(add_path_internal(entry.path().string(), MAX_DEPTH, + CURRENT_DEPTH + 1)); + } else { FW_LOG("[DEBUG] Path " + entry.path().string() + " is a directory. But MAX_DEPTH=" + to_string(MAX_DEPTH) + " have been reached. Child files won't be watched."); + } } } - } - else if (!fs::is_directory(path)) { + } else if (!fs::is_directory(path)) { FW_LOG("[DEBUG] Path " + path + " is a file. Adding to task..."); - task.paths.push_back(path); + resolved_files.push_back(path); fw->add_path(path); } return Result::Ok(); } - Result TaskRunner::delete_path(const string &path) { - for (auto it = task.paths.begin(); it != task.paths.end(); it++) { - if (*it == path) { - task.paths.erase(it); - return Result::Ok(); + if (!fs::exists(path)) { + return Result::Err( + FWError::make(ErrorCode::PATH_NOT_FOUND, + "Error: provided path " + path + " does not exist! ✗")); + } + + if (!fs::is_directory(path)) { + for (auto it = task.file_paths.begin(); it != task.file_paths.end(); it++) { + if (*it == path) { + task.file_paths.erase(it); + return Result::Ok(); + } + } + } else { + for (auto it = task.dir_paths.begin(); it != task.dir_paths.end(); it++) { + if (*it == path) { + task.dir_paths.erase(it); + return Result::Ok(); + } } } TEST(fw->remove_path(path)); return Result::Err( - FWError::make(ErrorCode::EVENT_NOT_FOUND, "Error: path not found")); + FWError::make(ErrorCode::PATH_NOT_FOUND, "Error: path not found")); } Result TaskRunner::add_on_success(const string &command) { @@ -472,8 +521,7 @@ Result TaskRunner::delete_callback(const WatchCallback &callback) { Result TaskRunner::start() { FW_LOG("[DEBUG] Starting TaskRunner..."); if (task.isRunning) { - return Result::Err(FWError::make( - ErrorCode::TASK_ALREADY_RUNNING, "Error: task runner already running")); + return Result::Ok(); // idempotent } task.isRunning = true; diff --git a/tests/integration/test_flowhook_core.cpp b/tests/integration/test_flowhook_core.cpp index 658ee30..d74fab6 100644 --- a/tests/integration/test_flowhook_core.cpp +++ b/tests/integration/test_flowhook_core.cpp @@ -10,6 +10,11 @@ using namespace std; using namespace flowhook; namespace fs = std::filesystem; +bool FLOWHOOK_DEBUG = false; +bool FLOWHOOK_VERBOSE = false; +bool FLOWHOOK_QUIET = false; + + struct FlowHookFixture { FlowHookCore* fh; @@ -18,6 +23,7 @@ struct FlowHookFixture UTEST_F_SETUP(FlowHookFixture) { fs::create_directories("/tmp/fh_test"); + fs::create_directories("/tmp/fh_test2"); fs::create_directories("/tmp/fh_test/task_test"); setenv("FLOWHOOK_CONFIG_DIR_TEST", "/tmp/fh_test", 1); @@ -57,7 +63,9 @@ UTEST_F(FlowHookFixture, create_task_with_invalid_path) UTEST_F(FlowHookFixture, delete_task) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task("test_task").isOk()); + auto r = utest_fixture->fh->delete_task("/tmp/fh_test"); + EXPECT_TRUE(r.isOk()); + if(r.isErr()) cout << r.getErrMessage() << endl; } UTEST_F(FlowHookFixture, delete_non_existent_task) { @@ -66,8 +74,8 @@ UTEST_F(FlowHookFixture, delete_non_existent_task) UTEST_F(FlowHookFixture, delete_task_twice) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task("test_task").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task("test_task").isErr()); + EXPECT_TRUE(utest_fixture->fh->delete_task("/tmp/fh_test").isOk()); + EXPECT_TRUE(utest_fixture->fh->delete_task("/tmp/fh_test").isErr()); } // ---------------------------------------------------------------------------------- @@ -77,46 +85,41 @@ UTEST_F(FlowHookFixture, delete_task_twice) UTEST_F(FlowHookFixture, set_task_path) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_path("test_task", "/tmp/fh_test/task_test").isOk()); + auto r = utest_fixture->fh->set_task_path("/tmp/fh_test", "/tmp/fh_test/task_test"); + EXPECT_TRUE(r.isOk()); } UTEST_F(FlowHookFixture, set_task_path_non_existent_task) { EXPECT_TRUE(utest_fixture->fh->set_task_path("non_existent_task", "/tmp/fh_test/task_test").isErr()); } -UTEST_F(FlowHookFixture, set_task_path_twice) -{ - ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->set_task_path("test_task", "/tmp/fh_test/task_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_path("test_task", "/tmp/fh_test/task_test").isErr()); -} UTEST_F(FlowHookFixture, set_task_path_invalid_path) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_path("test_task", "/invalid/path").isErr()); + EXPECT_TRUE(utest_fixture->fh->set_task_path("/tmp/fh_test", "/invalid/path").isErr()); } UTEST_F(FlowHookFixture, set_empty_task_path) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_path("test_task", "").isErr()); + EXPECT_TRUE(utest_fixture->fh->set_task_path("/tmp/fh_test", "").isErr()); } UTEST_F(FlowHookFixture, delete_task_path) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->set_task_path("test_task", "/tmp/fh_test/task_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_path("test_task", "/tmp/fh_test/task_test").isOk()); + ASSERT_TRUE(utest_fixture->fh->set_task_path("/tmp/fh_test", "/tmp/fh_test/task_test").isOk()); + EXPECT_TRUE(utest_fixture->fh->delete_task_path("/tmp/fh_test", "/tmp/fh_test/task_test").isOk()); } UTEST_F(FlowHookFixture, delete_non_existent_task_path) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_path("non_existent_task", "/tmp/fh_test/task_test").isErr()); + EXPECT_TRUE(utest_fixture->fh->delete_task_path("/non_existent_task", "/tmp/fh_test/task_test").isErr()); } UTEST_F(FlowHookFixture, delete_task_path_with_wrong_path) { - EXPECT_TRUE(utest_fixture->fh->delete_task_path("test_task", "/wrong/path").isErr()); + EXPECT_TRUE(utest_fixture->fh->delete_task_path("/tmp/fh_test", "/wrong/path").isErr()); } UTEST_F(FlowHookFixture, delete_empty_task_path) { - EXPECT_TRUE(utest_fixture->fh->delete_task_path("test_task", "").isErr()); + EXPECT_TRUE(utest_fixture->fh->delete_task_path("/tmp/fh_test", "").isErr()); } // ---------------------------------------------------------------------------------- @@ -126,7 +129,7 @@ UTEST_F(FlowHookFixture, delete_empty_task_path) UTEST_F(FlowHookFixture, set_task_command) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_command("test_task", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->set_task_command("/tmp/fh_test", "ls").isOk()); } UTEST_F(FlowHookFixture, set_task_command_non_existent_task) { @@ -135,31 +138,31 @@ UTEST_F(FlowHookFixture, set_task_command_non_existent_task) UTEST_F(FlowHookFixture, set_task_command_empty_command) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_command("test_task", "").isErr()); + EXPECT_TRUE(utest_fixture->fh->set_task_command("/tmp/fh_test", "").isErr()); } UTEST_F(FlowHookFixture, set_task_command_twice) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_command("test_task", "ls").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_command("test_task", "ls").isErr()); + EXPECT_TRUE(utest_fixture->fh->set_task_command("/tmp/fh_test", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->set_task_command("/tmp/fh_test", "ls").isErr()); } UTEST_F(FlowHookFixture, delete_task_command) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_command("test_task", "ls").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_command("test_task", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->set_task_command("/tmp/fh_test", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->delete_task_command("/tmp/fh_test", "ls").isOk()); } UTEST_F(FlowHookFixture, delete_task_command_twice) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_command("test_task", "ls").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_command("test_task", "ls").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_command("test_task", "ls").isErr()); + EXPECT_TRUE(utest_fixture->fh->set_task_command("/tmp/fh_test", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->delete_task_command("/tmp/fh_test", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->delete_task_command("/tmp/fh_test", "ls").isErr()); } UTEST_F(FlowHookFixture, delete_nonexistent_task_command) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_command("test_task", "ls").isErr()); + EXPECT_TRUE(utest_fixture->fh->delete_task_command("/tmp/fh_test", "ls").isErr()); } // ---------------------------------------------------------------------------------- @@ -169,8 +172,8 @@ UTEST_F(FlowHookFixture, delete_nonexistent_task_command) UTEST_F(FlowHookFixture, delete_task_on_success) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->set_task_on_success("test_task", "ls").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_on_success("test_task", "ls").isOk()); + ASSERT_TRUE(utest_fixture->fh->set_task_on_success("/tmp/fh_test", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->delete_task_on_success("/tmp/fh_test", "ls").isOk()); } UTEST_F(FlowHookFixture, delete_task_on_success_non_existent_task) { @@ -179,26 +182,26 @@ UTEST_F(FlowHookFixture, delete_task_on_success_non_existent_task) UTEST_F(FlowHookFixture, delete_task_on_success_non_existent_command) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_on_success("test_task", "non_existent_command").isErr()); + EXPECT_TRUE(utest_fixture->fh->delete_task_on_success("/tmp/fh_test", "non_existent_command").isErr()); } UTEST_F(FlowHookFixture, delete_task_on_success_twice) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->set_task_on_success("test_task", "ls").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_on_success("test_task", "ls").isOk()); + ASSERT_TRUE(utest_fixture->fh->set_task_on_success("/tmp/fh_test", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->delete_task_on_success("/tmp/fh_test", "ls").isOk()); EXPECT_TRUE(utest_fixture->fh->delete_task_on_success("test_task", "ls").isErr()); } UTEST_F(FlowHookFixture, delete_task_on_success_empty_command) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_on_success("test_task", "").isErr()); + EXPECT_TRUE(utest_fixture->fh->delete_task_on_success("/tmp/fh_test", "").isErr()); } UTEST_F(FlowHookFixture, delete_task_on_failure) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->set_task_on_failure("test_task", "ls").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_on_failure("test_task", "ls").isOk()); + ASSERT_TRUE(utest_fixture->fh->set_task_on_failure("/tmp/fh_test", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->delete_task_on_failure("/tmp/fh_test", "ls").isOk()); } UTEST_F(FlowHookFixture, delete_task_on_failure_non_existent_task) { @@ -207,19 +210,19 @@ UTEST_F(FlowHookFixture, delete_task_on_failure_non_existent_task) UTEST_F(FlowHookFixture, delete_task_on_failure_non_existent_command) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_on_failure("test_task", "non_existent_command").isErr()); + EXPECT_TRUE(utest_fixture->fh->delete_task_on_failure("/tmp/fh_test", "non_existent_command").isErr()); } UTEST_F(FlowHookFixture, delete_task_on_failure_twice) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->set_task_on_failure("test_task", "ls").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_on_failure("test_task", "ls").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_on_failure("test_task", "ls").isErr()); + ASSERT_TRUE(utest_fixture->fh->set_task_on_failure("/tmp/fh_test", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->delete_task_on_failure("/tmp/fh_test", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->delete_task_on_failure("/tmp/fh_test", "ls").isErr()); } UTEST_F(FlowHookFixture, delete_task_on_failure_empty_command) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->delete_task_on_failure("test_task", "").isErr()); + EXPECT_TRUE(utest_fixture->fh->delete_task_on_failure("/tmp/fh_test", "").isErr()); } // ---------------------------------------------------------------------------------- @@ -229,11 +232,11 @@ UTEST_F(FlowHookFixture, delete_task_on_failure_empty_command) UTEST_F(FlowHookFixture, start_task) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->start_task("test_task").isOk()); + EXPECT_TRUE(utest_fixture->fh->start_task("/tmp/fh_test").isOk()); // For now, we assume that the `start` call on TaskRunner succeeds. // In a real integration test, one might want to verify the task is actually running. // This is a minimal test to cover the FlowHookCore API. - utest_fixture->fh->stop_task("test_task").isOk(); // Clean up + utest_fixture->fh->stop_task("/tmp/fh_test").isOk(); // Clean up } UTEST_F(FlowHookFixture, start_task_non_existent) { @@ -242,15 +245,15 @@ UTEST_F(FlowHookFixture, start_task_non_existent) UTEST_F(FlowHookFixture, start_task_twice) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->start_task("test_task").isOk()); - EXPECT_TRUE(utest_fixture->fh->start_task("test_task").isErr()); - utest_fixture->fh->stop_task("test_task").isOk(); // Clean up + ASSERT_TRUE(utest_fixture->fh->start_task("/tmp/fh_test").isOk()); + EXPECT_TRUE(utest_fixture->fh->start_task("/tmp/fh_test").isErr()); + utest_fixture->fh->stop_task("/tmp/fh_test").isOk(); // Clean up } UTEST_F(FlowHookFixture, stop_task) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->start_task("test_task").isOk()); - EXPECT_TRUE(utest_fixture->fh->stop_task("test_task").isOk()); + ASSERT_TRUE(utest_fixture->fh->start_task("/tmp/fh_test").isOk()); + EXPECT_TRUE(utest_fixture->fh->stop_task("/tmp/fh_test").isOk()); } UTEST_F(FlowHookFixture, stop_task_non_existent) { @@ -259,9 +262,9 @@ UTEST_F(FlowHookFixture, stop_task_non_existent) UTEST_F(FlowHookFixture, stop_task_twice) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->start_task("test_task").isOk()); - ASSERT_TRUE(utest_fixture->fh->stop_task("test_task").isOk()); - EXPECT_TRUE(utest_fixture->fh->stop_task("test_task").isErr()); // TaskRunner::stop() might return error if not running + ASSERT_TRUE(utest_fixture->fh->start_task("/tmp/fh_test").isOk()); + ASSERT_TRUE(utest_fixture->fh->stop_task("/tmp/fh_test").isOk()); + EXPECT_TRUE(utest_fixture->fh->stop_task("/tmp/fh_test").isErr()); // TaskRunner::stop() might return error if not running } // ---------------------------------------------------------------------------------- @@ -271,7 +274,9 @@ UTEST_F(FlowHookFixture, stop_task_twice) UTEST_F(FlowHookFixture, activate_task) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->activate_task("test_task").isOk()); + auto r = utest_fixture->fh->activate_task("/tmp/fh_test"); + EXPECT_TRUE(r.isOk()); + if(r.isErr()) cout << r.getErrMessage() << endl; } UTEST_F(FlowHookFixture, activate_task_non_existent) { @@ -280,14 +285,14 @@ UTEST_F(FlowHookFixture, activate_task_non_existent) UTEST_F(FlowHookFixture, activate_task_twice) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->activate_task("test_task").isOk()); - EXPECT_TRUE(utest_fixture->fh->activate_task("test_task").isOk()); // TaskRunner::activate can be called multiple times + ASSERT_TRUE(utest_fixture->fh->activate_task("/tmp/fh_test").isOk()); + EXPECT_TRUE(utest_fixture->fh->activate_task("/tmp/fh_test").isOk()); // idempotent } UTEST_F(FlowHookFixture, deactivate_task) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->activate_task("test_task").isOk()); - EXPECT_TRUE(utest_fixture->fh->deactivate_task("test_task").isOk()); + ASSERT_TRUE(utest_fixture->fh->activate_task("/tmp/fh_test").isOk()); + EXPECT_TRUE(utest_fixture->fh->deactivate_task("/tmp/fh_test").isOk()); } UTEST_F(FlowHookFixture, deactivate_task_non_existent) { @@ -296,9 +301,9 @@ UTEST_F(FlowHookFixture, deactivate_task_non_existent) UTEST_F(FlowHookFixture, deactivate_task_twice) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->activate_task("test_task").isOk()); - ASSERT_TRUE(utest_fixture->fh->deactivate_task("test_task").isOk()); - EXPECT_TRUE(utest_fixture->fh->deactivate_task("test_task").isOk()); // TaskRunner::deactivate can be called multiple times + ASSERT_TRUE(utest_fixture->fh->activate_task("/tmp/fh_test").isOk()); + ASSERT_TRUE(utest_fixture->fh->deactivate_task("/tmp/fh_test").isOk()); + EXPECT_TRUE(utest_fixture->fh->deactivate_task("/tmp/fh_test").isOk()); // idempotent } // ---------------------------------------------------------------------------------- @@ -318,19 +323,10 @@ UTEST_F(FlowHookFixture, start_all_one_task) UTEST_F(FlowHookFixture, start_all_multiple_tasks) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task_1", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test").isOk()); + ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test2").isOk()); EXPECT_TRUE(utest_fixture->fh->start_all().isOk()); utest_fixture->fh->stop_all().isOk(); // Clean up } -UTEST_F(FlowHookFixture, start_all_some_already_running) -{ - ASSERT_TRUE(utest_fixture->fh->create_task("test_task_1", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->start_task("test_task_1").isOk()); - EXPECT_TRUE(utest_fixture->fh->start_all().isErr()); // Expect error because task_1 is already running - utest_fixture->fh->stop_all().isOk(); // Clean up -} - UTEST_F(FlowHookFixture, stop_all_no_tasks) { EXPECT_TRUE(utest_fixture->fh->stop_all().isErr()); @@ -338,53 +334,50 @@ UTEST_F(FlowHookFixture, stop_all_no_tasks) UTEST_F(FlowHookFixture, stop_all_one_task) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task_1", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->start_task("test_task_1").isOk()); + ASSERT_TRUE(utest_fixture->fh->start_task("/tmp/fh_test").isOk()); EXPECT_TRUE(utest_fixture->fh->stop_all().isOk()); } UTEST_F(FlowHookFixture, stop_all_multiple_tasks) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task_1", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test").isOk()); + ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test2").isOk()); ASSERT_TRUE(utest_fixture->fh->start_all().isOk()); EXPECT_TRUE(utest_fixture->fh->stop_all().isOk()); } UTEST_F(FlowHookFixture, start_active_no_tasks) { - EXPECT_TRUE(utest_fixture->fh->start_active().isErr()); + EXPECT_TRUE(utest_fixture->fh->start_active().isErr()); // idempotent } UTEST_F(FlowHookFixture, start_active_no_active_tasks) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task_1", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->start_active().isOk()); // No active tasks, so no error. - // The implementation of start_active in flowhook_core.cpp checks if tasks are active before starting. - // If no tasks are active, it will simply iterate and return Ok without starting anything. + EXPECT_TRUE(utest_fixture->fh->start_active().isErr()); } UTEST_F(FlowHookFixture, start_active_one_active_task) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task_1", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->activate_task("test_task_1").isOk()); + ASSERT_TRUE(utest_fixture->fh->activate_task("/tmp/fh_test").isOk()); EXPECT_TRUE(utest_fixture->fh->start_active().isOk()); - utest_fixture->fh->stop_task("test_task_1").isOk(); // Clean up + utest_fixture->fh->stop_task("/tmp/fh_test").isOk(); // Clean up } UTEST_F(FlowHookFixture, start_active_multiple_active_tasks) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task_1", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->activate_task("test_task_1").isOk()); - ASSERT_TRUE(utest_fixture->fh->activate_task("test_task_2").isOk()); + ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test2").isOk()); + ASSERT_TRUE(utest_fixture->fh->activate_task("/tmp/fh_test").isOk()); + ASSERT_TRUE(utest_fixture->fh->activate_task("/tmp/fh_test2").isOk()); EXPECT_TRUE(utest_fixture->fh->start_active().isOk()); utest_fixture->fh->stop_all().isOk(); // Clean up } UTEST_F(FlowHookFixture, start_active_some_active_some_inactive) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task_1", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->activate_task("test_task_1").isOk()); + ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test2").isOk()); + ASSERT_TRUE(utest_fixture->fh->activate_task("/tmp/fh_test").isOk()); // test_task_2 remains inactive EXPECT_TRUE(utest_fixture->fh->start_active().isOk()); - utest_fixture->fh->stop_task("test_task_1").isOk(); // Clean up + utest_fixture->fh->stop_task("/tmp/fh_test").isOk(); // Clean up } // ---------------------------------------------------------------------------------- @@ -408,22 +401,22 @@ UTEST_F(FlowHookFixture, get_tasks_one_task) UTEST_F(FlowHookFixture, get_tasks_multiple_tasks) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task_1", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test").isOk()); + ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test2").isOk()); std::vector tasks = utest_fixture->fh->get_tasks(); EXPECT_EQ(tasks.size(), 2); // Tasks should be in the order they were created or added to task_runners - EXPECT_EQ(tasks[0].name, "test_task_1"); - EXPECT_EQ(tasks[1].name, "test_task_2"); + EXPECT_EQ(tasks[0].id, "/tmp/fh_test"); + EXPECT_EQ(tasks[1].id, "/tmp/fh_test2"); } UTEST_F(FlowHookFixture, get_tasks_after_deletion) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task_1", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test").isOk()); - ASSERT_TRUE(utest_fixture->fh->delete_task("test_task_1").isOk()); + ASSERT_TRUE(utest_fixture->fh->create_task("test_task_2", "/tmp/fh_test2").isOk()); + ASSERT_TRUE(utest_fixture->fh->delete_task("/tmp/fh_test2").isOk()); std::vector tasks = utest_fixture->fh->get_tasks(); EXPECT_EQ(tasks.size(), 1); - EXPECT_EQ(tasks[0].name, "test_task_2"); + EXPECT_EQ(tasks[0].id, "/tmp/fh_test"); } @@ -434,34 +427,34 @@ UTEST_F(FlowHookFixture, get_tasks_after_deletion) UTEST_F(FlowHookFixture, set_task_on_success) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_on_success("test_task", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->set_task_on_success("/tmp/fh_test", "ls").isOk()); } UTEST_F(FlowHookFixture, set_task_on_failure) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_on_failure("test_task", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->set_task_on_failure("/tmp/fh_test", "ls").isOk()); } UTEST_F(FlowHookFixture, set_task_on_success_twice) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_on_success("test_task", "ls").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_on_success("test_task", "ls").isErr()); + EXPECT_TRUE(utest_fixture->fh->set_task_on_success("/tmp/fh_test", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->set_task_on_success("/tmp/fh_test", "ls").isErr()); } UTEST_F(FlowHookFixture, set_task_on_failure_twice) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_on_failure("test_task", "ls").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_on_failure("test_task", "ls").isErr()); + EXPECT_TRUE(utest_fixture->fh->set_task_on_failure("/tmp/fh_test", "ls").isOk()); + EXPECT_TRUE(utest_fixture->fh->set_task_on_failure("/tmp/fh_test", "ls").isErr()); } UTEST_F(FlowHookFixture, set_empty_task_on_success) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_on_success("test_task", "").isErr()); + EXPECT_TRUE(utest_fixture->fh->set_task_on_success("/tmp/fh_test", "").isErr()); } UTEST_F(FlowHookFixture, set_empty_task_on_failure) { ASSERT_TRUE(utest_fixture->fh->create_task("test_task", "/tmp/fh_test").isOk()); - EXPECT_TRUE(utest_fixture->fh->set_task_on_failure("test_task", "").isErr()); + EXPECT_TRUE(utest_fixture->fh->set_task_on_failure("/tmp/fh_test", "").isErr()); } UTEST_F(FlowHookFixture, set_task_on_success_nonexistent_task) { diff --git a/tests/unit/test_config_manager.cpp b/tests/unit/test_config_manager.cpp index 75bb392..b18c003 100644 --- a/tests/unit/test_config_manager.cpp +++ b/tests/unit/test_config_manager.cpp @@ -37,13 +37,13 @@ UTEST_F_SETUP(ConfigManagerFixture) fs::create_directories("/tmp/cm_test2"); utest_fixture->t = new Task("test_task", "/tmp/cm_test", 3, {"ls"}, - {"/tmp/cm_test_file.txt"}, vector{"ls"}, vector{"ls"}, {}, {}, true, true); + {"/tmp/cm_test_file.txt"}, {"/tmp/cm_test"}, vector{"ls"}, vector{"ls"}, {"*.o"}, {".git"}, true, true); utest_fixture->t2 = new Task("test_task2", "/tmp/cm_test2", 3, {"ls"}, - {"/tmp/cm_test_file.txt"}, vector{"ls"}, vector{"cd, ls"}, {}, {}, true, true); + {"/tmp/cm_test_file.txt"}, {"/tmp/cm_test"}, vector{"ls"}, vector{"cd, ls"}, {"*.o"}, {".git"}, true, true); - utest_fixture->t3 = new Task("test_task", "/tmp/cm_test3", 3, {"cd", "make"}, - {"/tmp/cm_test_file.txt"}, vector{"ls"}, vector{"cd, ls"}, {}, {}, true, true); + utest_fixture->t3 = new Task("test_task1", "/tmp/cm_test", 3, {"cd", "make"}, + {"/tmp/cm_test_file.txt"}, {"/tmp/cm_test"}, vector{"ls"}, vector{"cd, ls"}, {"*.o"}, {".git"}, true, true); } UTEST_F_TEARDOWN(ConfigManagerFixture) @@ -63,7 +63,7 @@ UTEST_F_TEARDOWN(ConfigManagerFixture) UTEST_F(ConfigManagerFixture, create) { ASSERT_NE(utest_fixture->cm, nullptr); - EXPECT_TRUE(fs::exists("/tmp/cm_test/tasks.json")); + EXPECT_TRUE(fs::exists("/tmp/cm_test/config.json")); EXPECT_TRUE(ConfigManagerTest::get_config(utest_fixture->cm)["version"] == FLOWHOOK_VERSION); } @@ -132,6 +132,10 @@ UTEST_F(ConfigManagerFixture, update_task) auto c2 = utest_fixture->cm->update_task(*utest_fixture->t3); EXPECT_TRUE(c2.isOk()); + if(c2.isErr()) + { + cout << c2.getErrMessage() << endl; + } } @@ -154,7 +158,7 @@ UTEST_F(ConfigManagerFixture, flush) } json test_json = utest_fixture->cm->getjson(); - ifstream file("/tmp/cm_test/tasks.json"); + ifstream file("/tmp/cm_test/config.json"); json content = json::parse(file); EXPECT_EQ(test_json.size(), content.size()); } @@ -171,7 +175,7 @@ UTEST_F(ConfigManagerFixture, purge_config) auto c2 = utest_fixture->cm->purge_config(); EXPECT_TRUE(c2.isOk()); - auto config_path = "/tmp/cm_test/tasks.json"; + auto config_path = "/tmp/cm_test/config.json"; EXPECT_TRUE(fs::file_size(config_path) == 0); } diff --git a/tests/unit/test_filewatcher.cpp b/tests/unit/test_filewatcher.cpp index 2b5b747..aeb3046 100644 --- a/tests/unit/test_filewatcher.cpp +++ b/tests/unit/test_filewatcher.cpp @@ -9,6 +9,11 @@ namespace fs = std::filesystem; using namespace flowhook; using namespace std; +bool FLOWHOOK_DEBUG = false; +bool FLOWHOOK_VERBOSE = false; +bool FLOWHOOK_QUIET = false; + + // --------------------------------------------------------------------------- // Callbacks // --------------------------------------------------------------------------- diff --git a/tests/unit/test_session_logger.cpp b/tests/unit/test_session_logger.cpp index c186356..c926bbb 100644 --- a/tests/unit/test_session_logger.cpp +++ b/tests/unit/test_session_logger.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -11,6 +12,10 @@ namespace fs = std::filesystem; using namespace flowhook; using namespace std; +bool FLOWHOOK_DEBUG = false; +bool FLOWHOOK_VERBOSE = false; +bool FLOWHOOK_QUIET = false; + // --------------------------------------------------------------------------- // Fixture // --------------------------------------------------------------------------- @@ -23,10 +28,17 @@ struct SessionLoggerFixture UTEST_F_SETUP(SessionLoggerFixture) { - WatchEvent e(IN_MODIFY, "file", "/tmp/sl_test_file.txt", 0); + WatchEvent e(0, "file", "/tmp/sl_test_file.txt", IN_MODIFY); utest_fixture->er = new ExecutionResult(1, 0, e, "log", vector{"ls"}); utest_fixture->er2 = new ExecutionResult(2, 0, e, "log", vector{"ls"}); - utest_fixture->sl = new SessionLogger(); + auto s = SessionLogger::create("/tmp/sl_test_file.txt"); + if(s.isErr()) + { + cout << s.getErrMessage() << endl; + exit(1); + } + + utest_fixture->sl = s.unwrap(); fs::create_directories("/tmp/sl_test"); } @@ -44,20 +56,12 @@ UTEST_F_TEARDOWN(SessionLoggerFixture) // --------------------------------------------------------------------------- UTEST_F(SessionLoggerFixture, start) { - EXPECT_TRUE(utest_fixture->sl->start("/tmp/sl_test").isOk()); + EXPECT_TRUE(utest_fixture->sl->start().isOk()); } UTEST_F(SessionLoggerFixture, start_twice) { - EXPECT_TRUE(utest_fixture->sl->start("/tmp/sl_test").isOk()); - EXPECT_TRUE(utest_fixture->sl->start("/tmp/sl_test").isErr()); -} -UTEST_F(SessionLoggerFixture, start_nonexistent) -{ - EXPECT_TRUE(utest_fixture->sl->start("/tmp/sl_nonexistent").isErr()); -} -UTEST_F(SessionLoggerFixture, start_empty) -{ - EXPECT_TRUE(utest_fixture->sl->start("").isErr()); + EXPECT_TRUE(utest_fixture->sl->start().isOk()); + EXPECT_TRUE(utest_fixture->sl->start().isOk()); // idempotent } // --------------------------------------------------------------------------- @@ -65,7 +69,7 @@ UTEST_F(SessionLoggerFixture, start_empty) // --------------------------------------------------------------------------- UTEST_F(SessionLoggerFixture, log_execution) { - ASSERT_TRUE(utest_fixture->sl->start("/tmp/sl_test").isOk()); + ASSERT_TRUE(utest_fixture->sl->start().isOk()); auto r = utest_fixture->sl->log_execution(*utest_fixture->er); EXPECT_TRUE(r.isOk()); } @@ -80,18 +84,20 @@ UTEST_F(SessionLoggerFixture, log_execution_no_running) // --------------------------------------------------------------------------- UTEST_F(SessionLoggerFixture, stop) { - ASSERT_TRUE(utest_fixture->sl->start("/tmp/sl_test").isOk()); + ASSERT_TRUE(utest_fixture->sl->start().isOk()); EXPECT_TRUE(utest_fixture->sl->stop().isOk()); } + UTEST_F(SessionLoggerFixture, stop_without_start) { - EXPECT_TRUE(utest_fixture->sl->stop().isErr()); + EXPECT_TRUE(utest_fixture->sl->stop().isOk()); // idempotent } + UTEST_F(SessionLoggerFixture, stop_twice) { - ASSERT_TRUE(utest_fixture->sl->start("/tmp/sl_test").isOk()); + ASSERT_TRUE(utest_fixture->sl->start().isOk()); EXPECT_TRUE(utest_fixture->sl->stop().isOk()); - EXPECT_TRUE(utest_fixture->sl->stop().isErr()); + EXPECT_TRUE(utest_fixture->sl->stop().isOk()); // idempotent } UTEST_MAIN() diff --git a/tests/unit/test_task_runner.cpp b/tests/unit/test_task_runner.cpp index 46882df..a783221 100644 --- a/tests/unit/test_task_runner.cpp +++ b/tests/unit/test_task_runner.cpp @@ -10,6 +10,11 @@ namespace fs = std::filesystem; using namespace flowhook; using namespace std; +bool FLOWHOOK_DEBUG = false; +bool FLOWHOOK_VERBOSE = false; +bool FLOWHOOK_QUIET = false; + + // --------------------------------------------------------------------------- // Callbacks // --------------------------------------------------------------------------- @@ -106,10 +111,6 @@ UTEST_F(TaskRunnerFixture, add_command) { auto r = utest_fixture->tr->add_command("ls"); EXPECT_TRUE(r.isOk()); - if(r.isErr()) - { - cerr << r.unwrapErr().message << endl; - } } UTEST_F(TaskRunnerFixture, add_command_twice) { @@ -152,7 +153,7 @@ UTEST_F(TaskRunnerFixture, add_path) UTEST_F(TaskRunnerFixture, add_path_twice) { ASSERT_TRUE(utest_fixture->tr->add_path("/tmp/tr_test").isOk()); - EXPECT_TRUE(utest_fixture->tr->add_path("/tmp/tr_test").isErr()); + EXPECT_TRUE(utest_fixture->tr->add_path("/tmp/tr_test").isOk()); // idempotent } UTEST_F(TaskRunnerFixture, add_path_empty) { @@ -175,7 +176,8 @@ UTEST_F(TaskRunnerFixture, add_path_file) UTEST_F(TaskRunnerFixture, delete_path) { ASSERT_TRUE(utest_fixture->tr->add_path("/tmp/tr_test").isOk()); - EXPECT_TRUE(utest_fixture->tr->delete_path("/tmp/tr_test").isOk()); + auto result = utest_fixture->tr->delete_path("/tmp/tr_test"); + EXPECT_TRUE(result.isOk()); } UTEST_F(TaskRunnerFixture, delete_path_twice) { @@ -313,6 +315,42 @@ UTEST_F(TaskRunnerFixture, delete_callback_empty) EXPECT_TRUE(utest_fixture->tr->delete_callback(WatchCallback()).isErr()); } +// --------------------------------------------------------------------------- +// Ignored Path/ Ignored Pattern +// --------------------------------------------------------------------------- + +UTEST_F(TaskRunnerFixture, ignored_path) +{ + ASSERT_TRUE(utest_fixture->tr->add_path("/tmp/tr_test").isOk()); + ofstream("/tmp/tr_test_file.txt").close(); + ASSERT_TRUE(utest_fixture->tr->add_ignored_path("/tmp/tr_test/tr_test_file.txt").isOk()); + EXPECT_TRUE(utest_fixture->tr->isIgnored("/tmp/tr_test/tr_test_file.txt")); +} + +UTEST_F(TaskRunnerFixture, ignored_pattern) +{ + ASSERT_TRUE(utest_fixture->tr->add_path("/tmp/tr_test").isOk()); + ASSERT_TRUE(utest_fixture->tr->add_ignored_pattern("*.txt").isOk()); + ofstream("/tmp/tr_test_file.txt").close(); + EXPECT_TRUE(utest_fixture->tr->isIgnored("*.txt")); +} + +UTEST_F(TaskRunnerFixture, remove_ignored) +{ + ASSERT_TRUE(utest_fixture->tr->add_path("/tmp/tr_test").isOk()); + ofstream("/tmp/tr_test_file.txt").close(); + ASSERT_TRUE(utest_fixture->tr->add_ignored_path("/tmp/tr_test/tr_test_file.txt").isOk()); + ASSERT_TRUE(utest_fixture->tr->isIgnored("/tmp/tr_test/tr_test_file.txt")); + ASSERT_TRUE(utest_fixture->tr->remove_ignored_path("/tmp/tr_test/tr_test_file.txt").isOk()); + EXPECT_FALSE(utest_fixture->tr->isIgnored("/tmp/tr_test/tr_test_file.txt")); + + ASSERT_TRUE(utest_fixture->tr->add_ignored_pattern("*.txt").isOk()); + ASSERT_TRUE(utest_fixture->tr->isIgnored("*.txt")); + ASSERT_TRUE(utest_fixture->tr->remove_ignored_pattern("*.txt").isOk()); + EXPECT_FALSE(utest_fixture->tr->isIgnored("*.txt")); +} + + // --------------------------------------------------------------------------- // Execute // --------------------------------------------------------------------------- @@ -326,6 +364,7 @@ UTEST_F(TaskRunnerFixture, execute) ASSERT_TRUE(utest_fixture->tr->add_on_success("ls").isOk()); ASSERT_TRUE(utest_fixture->tr->add_on_failure("ls").isOk()); ASSERT_TRUE(utest_fixture->tr->add_callback(utest_fixture->cb).isOk()); + ASSERT_TRUE(utest_fixture->tr->start().isOk()); WatchEvent e(2, "file", "/tmp/tr_test_file.txt", 0); auto r = utest_fixture->tr->execute(e); EXPECT_TRUE(r.isOk()); @@ -367,7 +406,7 @@ UTEST_F(TaskRunnerFixture, start_twice) ASSERT_TRUE(utest_fixture->tr->add_on_failure("ls").isOk()); ASSERT_TRUE(utest_fixture->tr->add_callback(utest_fixture->cb).isOk()); ASSERT_TRUE(utest_fixture->tr->start().isOk()); - EXPECT_TRUE(utest_fixture->tr->start().isErr()); + EXPECT_TRUE(utest_fixture->tr->start().isOk());// idempotent } // --------------------------------------------------------------------------- @@ -393,7 +432,7 @@ UTEST_F(TaskRunnerFixture, stop) } UTEST_F(TaskRunnerFixture, stop_without_start) { - EXPECT_TRUE(utest_fixture->tr->stop().isErr()); + EXPECT_TRUE(utest_fixture->tr->stop().isOk()); // idempotent } -UTEST_MAIN() \ No newline at end of file +UTEST_MAIN()