From 9ff0cd28d5fdb8e6c95d7b04c29601dd42c99395 Mon Sep 17 00:00:00 2001 From: xirvik Date: Sat, 15 Aug 2026 22:38:24 +0000 Subject: [PATCH] Add schedule.if_absent for clients that re-register periodic tasks. schedule restarts a re-registered task's countdown, if_absent keeps the existing entry. --- src/command_events.cc | 8 +++- test/Makefile.am | 2 + test/rpc/test_command_scheduler.cc | 65 ++++++++++++++++++++++++++++++ test/rpc/test_command_scheduler.h | 17 ++++++++ 4 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 test/rpc/test_command_scheduler.cc create mode 100644 test/rpc/test_command_scheduler.h diff --git a/src/command_events.cc b/src/command_events.cc index 8420cc5a..2afd600d 100644 --- a/src/command_events.cc +++ b/src/command_events.cc @@ -128,7 +128,7 @@ apply_remove_untied() { } torrent::Object -apply_schedule(const torrent::Object::list_type& args) { +apply_schedule(const torrent::Object::list_type& args, bool if_absent) { if (args.size() != 4) throw torrent::input_error("Wrong number of arguments."); @@ -138,6 +138,9 @@ apply_schedule(const torrent::Object::list_type& args) { auto& arg2 = (itr++)->as_string(); auto& arg3 = (itr++)->as_string(); + if (if_absent && control->command_scheduler()->find(arg1) != control->command_scheduler()->end()) + return torrent::Object(); + control->command_scheduler()->parse(arg1, arg2, arg3, *itr); return torrent::Object(); @@ -340,7 +343,8 @@ initialize_command_events() { CMD2_ANY ("close_untied", [](auto, auto) { return apply_close_untied(); }); CMD2_ANY ("remove_untied", [](auto, auto) { return apply_remove_untied(); }); - CMD2_ANY_LIST ("schedule", [](auto, auto& args) { return apply_schedule(args); }); + CMD2_ANY_LIST ("schedule", [](auto, auto& args) { return apply_schedule(args, false); }); + CMD2_ANY_LIST ("schedule.if_absent", [](auto, auto& args) { return apply_schedule(args, true); }); CMD2_ANY_STRING_V("schedule.remove", [](auto, auto& str) { return control->command_scheduler()->erase_str(str); }); CMD2_ANY_STRING_V("import", [](auto, auto& str) { return apply_import(str); }); diff --git a/test/Makefile.am b/test/Makefile.am index 599d1b3b..550051f8 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -35,6 +35,8 @@ rtorrent_Test_Rpc_SOURCES = $(rtorrent_Test_Common) \ rpc/test_command.h \ rpc/test_command_map.cc \ rpc/test_command_map.h \ + rpc/test_command_scheduler.cc \ + rpc/test_command_scheduler.h \ rpc/test_jsonrpc.cc \ rpc/test_jsonrpc.h \ rpc/test_xmlrpc.cc \ diff --git a/test/rpc/test_command_scheduler.cc b/test/rpc/test_command_scheduler.cc new file mode 100644 index 00000000..6ab4cb33 --- /dev/null +++ b/test/rpc/test_command_scheduler.cc @@ -0,0 +1,65 @@ +#include "config.h" + +#include "test/rpc/test_command_scheduler.h" + +#include + +#include "rpc/command_scheduler.h" +#include "rpc/command_scheduler_item.h" +#include "torrent/object.h" + +CPPUNIT_TEST_SUITE_REGISTRATION(TestCommandScheduler); + +namespace { + +const torrent::Object test_command = torrent::Object(std::string("print=scheduled")); + +std::chrono::microseconds +time_scheduled(rpc::CommandScheduler& scheduler, const std::string& key) { + auto itr = scheduler.find(key); + + CPPUNIT_ASSERT(itr != scheduler.end()); + + return (*itr)->time_scheduled(); +} + +} + +void +TestCommandScheduler::setUp() { + TestFixtureWithMainThread::setUp(); + + m_main_thread->test_set_cached_time(std::chrono::seconds(0)); +} + +void +TestCommandScheduler::tearDown() { + TestFixtureWithMainThread::tearDown(); +} + +void +TestCommandScheduler::test_parse_rearms_existing_key() { + rpc::CommandScheduler scheduler; + + scheduler.parse("key", "3600", "3600", test_command); + + auto first = time_scheduled(scheduler, "key"); + + m_main_thread->test_add_cached_time(std::chrono::seconds(600)); + scheduler.parse("key", "3600", "3600", test_command); + + CPPUNIT_ASSERT_EQUAL(size_t{1}, scheduler.size()); + CPPUNIT_ASSERT(time_scheduled(scheduler, "key") == first + std::chrono::seconds(600)); +} + +void +TestCommandScheduler::test_find_locates_a_scheduled_key() { + rpc::CommandScheduler scheduler; + + CPPUNIT_ASSERT(scheduler.find("key") == scheduler.end()); + + scheduler.parse("key", "3600", "3600", test_command); + + CPPUNIT_ASSERT(scheduler.find("key") != scheduler.end()); + CPPUNIT_ASSERT(scheduler.find("other") == scheduler.end()); +} diff --git a/test/rpc/test_command_scheduler.h b/test/rpc/test_command_scheduler.h new file mode 100644 index 00000000..c23cf938 --- /dev/null +++ b/test/rpc/test_command_scheduler.h @@ -0,0 +1,17 @@ +#include "test/helpers/test_main_thread.h" + +class TestCommandScheduler : public TestFixtureWithMainThread { + CPPUNIT_TEST_SUITE(TestCommandScheduler); + + CPPUNIT_TEST(test_parse_rearms_existing_key); + CPPUNIT_TEST(test_find_locates_a_scheduled_key); + + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + void tearDown(); + + void test_parse_rearms_existing_key(); + void test_find_locates_a_scheduled_key(); +};