From 3a227c190faf90aad229f32228ffeec96630a300 Mon Sep 17 00:00:00 2001 From: Jari Sundell Date: Fri, 28 Mar 2025 11:21:25 +0100 Subject: [PATCH] Added system.files.advise_random.set command. --- src/command_download.cc | 2 +- src/command_local.cc | 40 ++--------- src/command_network.cc | 2 +- src/display/window_download_list.cc | 3 +- src/display/window_file_list.cc | 44 ++---------- test/Makefile.am | 34 +++++++-- test/helpers/progress_listener.cc | 64 +++++++++++++++++ test/helpers/progress_listener.h | 47 +++++++++++++ test/helpers/protectors.cc | 35 ++++++++++ test/helpers/protectors.h | 13 ++++ test/helpers/utils.h | 60 ++++++++++++++++ test/main.cc | 103 ++++++++++++++++++++++++---- test/rpc/xmlrpc_test.cc | 3 +- test/rpc/xmlrpc_test.h | 3 +- 14 files changed, 348 insertions(+), 105 deletions(-) create mode 100644 test/helpers/progress_listener.cc create mode 100644 test/helpers/progress_listener.h create mode 100644 test/helpers/protectors.cc create mode 100644 test/helpers/protectors.h create mode 100644 test/helpers/utils.h diff --git a/src/command_download.cc b/src/command_download.cc index 574a5dab..d0e0d632 100644 --- a/src/command_download.cc +++ b/src/command_download.cc @@ -370,7 +370,7 @@ f_multicall(core::Download* download, const torrent::Object::list_type& args) { for (torrent::Object::list_const_iterator cItr = ++args.begin(); cItr != args.end(); cItr++) { const std::string& cmd = cItr->as_string(); - row.push_back(rpc::parse_command(rpc::make_target(*itr), cmd.c_str(), cmd.c_str() + cmd.size()).first); + row.push_back(rpc::parse_command(rpc::make_target(itr->get()), cmd.c_str(), cmd.c_str() + cmd.size()).first); } } diff --git a/src/command_local.cc b/src/command_local.cc index 4e91c92f..c3b28623 100644 --- a/src/command_local.cc +++ b/src/command_local.cc @@ -1,39 +1,3 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, Jari Sundell -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// In addition, as a special exception, the copyright holders give -// permission to link the code of portions of this program with the -// OpenSSL library under certain conditions as described in each -// individual source file, and distribute linked combinations -// including the two. -// -// You must obey the GNU General Public License in all respects for -// all of the code used other than OpenSSL. If you modify file(s) -// with this exception, you may extend this exception to your version -// of the file(s), but you are not obligated to do so. If you do not -// wish to do so, delete this exception statement from your version. -// If you delete this exception statement from all source files in the -// program, then also delete it here. -// -// Contact: Jari Sundell -// -// Skomakerveien 33 -// 3185 Skoppum, NORWAY - #include "config.h" #include @@ -242,6 +206,7 @@ initialize_command_local() { CMD2_VAR_C_STRING("system.api_version", (int64_t)API_VERSION); CMD2_VAR_C_STRING("system.client_version", PACKAGE_VERSION); CMD2_VAR_C_STRING("system.library_version", torrent::version()); + CMD2_VAR_VALUE ("system.file.allocate", 0); CMD2_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30); CMD2_VAR_VALUE ("system.file.split_size", -1); @@ -255,6 +220,9 @@ initialize_command_local() { CMD2_VAR_LIST ("file.prioritize_toc.first"); CMD2_VAR_LIST ("file.prioritize_toc.last"); + CMD2_ANY ("system.files.advise_random", std::bind(&FM_t::advise_random, fileManager)); + CMD2_ANY_VALUE_V ("system.files.advise_random.set", std::bind(&FM_t::set_advise_random, fileManager, std::placeholders::_2)); + CMD2_ANY ("system.files.opened_counter", std::bind(&FM_t::files_opened_counter, fileManager)); CMD2_ANY ("system.files.closed_counter", std::bind(&FM_t::files_closed_counter, fileManager)); CMD2_ANY ("system.files.failed_counter", std::bind(&FM_t::files_failed_counter, fileManager)); diff --git a/src/command_network.cc b/src/command_network.cc index fba185a9..0c49ebf5 100644 --- a/src/command_network.cc +++ b/src/command_network.cc @@ -69,7 +69,7 @@ initialize_rpc() { if (index >= d->file_list()->size_files()) throw torrent::input_error("invalid parameters: index not found"); - return (*d->file_list())[index]; + return (*d->file_list())[index].get(); }; rpc::rpc.slot_find_tracker() = [](core::Download* d, uint32_t index) -> torrent::tracker::Tracker { if (index >= d->tracker_list()->size()) diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc index 981829a6..d73b86ff 100644 --- a/src/display/window_download_list.cc +++ b/src/display/window_download_list.cc @@ -22,7 +22,7 @@ WindowDownloadList::WindowDownloadList() : WindowDownloadList::~WindowDownloadList() { if (m_view != NULL) m_view->signal_changed().erase(m_changed_itr); - + m_view = NULL; } @@ -101,7 +101,6 @@ WindowDownloadList::redraw() { m_canvas->set_attr(0, 0, -1, RCOLOR_TITLE); - int layout_height; const std::string layout_name = rpc::call_command_string("ui.torrent_list.layout"); typedef std::pair Range; diff --git a/src/display/window_file_list.cc b/src/display/window_file_list.cc index b5504908..b374bc42 100644 --- a/src/display/window_file_list.cc +++ b/src/display/window_file_list.cc @@ -1,39 +1,3 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, Jari Sundell -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// In addition, as a special exception, the copyright holders give -// permission to link the code of portions of this program with the -// OpenSSL library under certain conditions as described in each -// individual source file, and distribute linked combinations -// including the two. -// -// You must obey the GNU General Public License in all respects for -// all of the code used other than OpenSSL. If you modify file(s) -// with this exception, you may extend this exception to your version -// of the file(s), but you are not obligated to do so. If you do not -// wish to do so, delete this exception statement from your version. -// If you delete this exception statement from all source files in the -// program, then also delete it here. -// -// Contact: Jari Sundell -// -// Skomakerveien 33 -// 3185 Skoppum, NORWAY - #include "config.h" #include @@ -83,7 +47,7 @@ wstring_width(const std::string& i_str, int width) { // Limit to width if it's too wide already. if (swidth == -1 || swidth > width) { length = swidth = 0; - + while (result[length]) { int next = ::wcwidth(result[length]); @@ -172,13 +136,13 @@ WindowFileList::redraw() { } else if (itr.is_entering()) { m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\', - itr.depth() < (*itr)->path()->size() ? wstring_width((*itr)->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN"); + itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN"); } else if (itr.is_leaving()) { m_canvas->print(0, pos, "%*c %-*s", 16 + (itr.depth() - 1), '/', filenameWidth - (itr.depth() - 1), ""); } else if (itr.is_file()) { - torrent::File* e = *itr; + torrent::File* e = itr.file(); const char* priority; @@ -203,7 +167,7 @@ WindowFileList::redraw() { m_canvas->print(8, pos, "%5.1f T", (double)val / (int64_t(1) << 40)); m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|', - itr.depth() < (*itr)->path()->size() ? wstring_width((*itr)->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN"); + itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN"); } else { m_canvas->print(0, pos, "BORK BORK"); diff --git a/test/Makefile.am b/test/Makefile.am index a87c5590..fff7df4d 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,10 +1,25 @@ -TESTS = rtorrentTest +TESTS = \ + rtorrent_Test_Rpc \ + rtorrent_Test_Src check_PROGRAMS = $(TESTS) -rtorrentTest_LDADD = ../src/libsub_root.a -rtorrentTest_SOURCES = \ +rtorrent_Test_LDADD = \ + ../src/libsub_root.a + +rtorrent_Test_Rpc_LDADD = $(rtorrent_Test_LDADD) +rtorrent_Test_Src_LDADD = $(rtorrent_Test_LDADD) + +rtorrent_Test_Common = \ + main.cc \ + \ helpers/assert.h \ + helpers/progress_listener.cc \ + helpers/progress_listener.h \ + helpers/protectors.cc \ + helpers/protectors.h + +rtorrent_Test_Rpc_SOURCES = $(rtorrent_Test_Common) \ rpc/command_test.cc \ rpc/command_test.h \ rpc/command_map_test.cc \ @@ -20,10 +35,15 @@ rtorrentTest_SOURCES = \ rpc/test_parse_options.cc \ rpc/test_parse_options.h \ src/command_dynamic_test.cc \ - src/command_dynamic_test.h \ - main.cc + src/command_dynamic_test.h -rtorrentTest_CXXFLAGS = $(CPPUNIT_CFLAGS) -rtorrentTest_LDFLAGS = $(CPPUNIT_LIBS) -ldl +rtorrent_Test_Src_SOURCES = $(rtorrent_Test_Common) \ + src/command_dynamic_test.cc \ + src/command_dynamic_test.h + +rtorrent_Test_Rpc_CXXFLAGS = $(CPPUNIT_CFLAGS) +rtorrent_Test_Rpc_LDFLAGS = $(CPPUNIT_LIBS) -ldl +rtorrent_Test_Src_CXXFLAGS = $(CPPUNIT_CFLAGS) +rtorrent_Test_Src_LDFLAGS = $(CPPUNIT_LIBS) -ldl AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/test/helpers/progress_listener.cc b/test/helpers/progress_listener.cc new file mode 100644 index 00000000..7a6ed047 --- /dev/null +++ b/test/helpers/progress_listener.cc @@ -0,0 +1,64 @@ +#include "config.h" + +#include "progress_listener.h" + +#include +#include +#include +#include +#include +#include "torrent/utils/log.h" +#include "torrent/utils/log_buffer.h" + +static std::string +get_test_path(const test_list_type& tl) { + if (tl.size() < 2) + return ""; + + return std::accumulate(std::next(tl.begin()), std::prev(tl.end()), std::string(), [](std::string result, CppUnit::Test* test) { + return std::move(result) + test->getName() + "::"; + }); +} + +void +progress_listener::startTest(CppUnit::Test *test) { + std::cout << get_test_path(m_test_path) << test->getName() << std::flush; + + torrent::log_cleanup(); + + m_last_test_failed = false; + m_current_log_buffer = torrent::log_open_log_buffer("test_output"); +} + +void +progress_listener::addFailure(const CppUnit::TestFailure &failure) { + // AddFailure is called for parent test suits, so only deal with leafs. + if (m_current_log_buffer == nullptr) + return; + + std::cout << " : " << (failure.isError() ? "error" : "assertion") << std::flush; + + m_last_test_failed = true; + m_failures.push_back(failure_type{ failure.failedTestName(), std::move(m_current_log_buffer) }); +} + +void +progress_listener::endTest(CppUnit::Test *test) { + std::cout << (m_last_test_failed ? "" : " : OK") << std::endl; + + m_current_log_buffer.reset(); + torrent::log_cleanup(); +} + +void +progress_listener::startSuite(CppUnit::Test *suite) { + m_test_path.push_back(suite); + + if (suite->countTestCases() > 0) + std::cout << std::endl << get_test_path(m_test_path) << suite->getName() << ":" << std::endl; +} + +void +progress_listener::endSuite(CppUnit::Test *suite) { + m_test_path.pop_back(); +} diff --git a/test/helpers/progress_listener.h b/test/helpers/progress_listener.h new file mode 100644 index 00000000..18fb8faa --- /dev/null +++ b/test/helpers/progress_listener.h @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include + +#include "torrent/utils/log_buffer.h" + +struct failure_type { + std::string name; + torrent::log_buffer_ptr log; +}; + +typedef std::unique_ptr test_failure_ptr; +typedef std::vector test_list_type; +typedef std::vector failure_list_type; + +class progress_listener : public CppUnit::TestListener { +public: + progress_listener() : m_last_test_failed(false) {} + + void startTest(CppUnit::Test *test) override; + void addFailure(const CppUnit::TestFailure &failure) override; + void endTest(CppUnit::Test *test) override; + + void startSuite(CppUnit::Test *suite) override; + void endSuite(CppUnit::Test *suite) override; + + //Called by a TestRunner before running the test. + // void startTestRun(CppUnit::Test *test, CppUnit::TestResult *event_manager) override; + + // Called by a TestRunner after running the test. + // void endTestRun(CppUnit::Test *test, CppUnit::TestResult *event_manager) override; + + const failure_list_type& failures() { return m_failures; } + failure_list_type&& move_failures() { return std::move(m_failures); } + +private: + progress_listener(const progress_listener& rhs) = delete; + void operator =(const progress_listener& rhs) = delete; + + test_list_type m_test_path; + failure_list_type m_failures; + bool m_last_test_failed; + + torrent::log_buffer_ptr m_current_log_buffer; +}; diff --git a/test/helpers/protectors.cc b/test/helpers/protectors.cc new file mode 100644 index 00000000..442b19dd --- /dev/null +++ b/test/helpers/protectors.cc @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include + +bool +ExceptionProtector::protect(const CppUnit::Functor& functor, const CppUnit::ProtectorContext& context) { + try { + return functor(); + + } catch (CppUnit::Exception &failure) { + reportFailure( context, failure ); + + } catch (torrent::base_error& e) { + std::string short_description("uncaught exception of base type torrent::base_error: " + std::string(typeid(e).name())); + + CppUnit::Message message(short_description, e.what()); + reportError(context, message); + + } catch ( std::exception &e ) { + std::string short_description("uncaught exception of type "); + + short_description += CppUnit::TypeInfoHelper::getClassName(typeid(e)); + + CppUnit::Message message(short_description, e.what()); + reportError(context, message); + + } catch ( ... ) { + reportError(context, CppUnit::Message("uncaught exception of unknown type")); + } + + return false; +} diff --git a/test/helpers/protectors.h b/test/helpers/protectors.h new file mode 100644 index 00000000..aed53871 --- /dev/null +++ b/test/helpers/protectors.h @@ -0,0 +1,13 @@ +#ifndef LIBTORRENT_HELPER_PROTECTORS_H +#define LIBTORRENT_HELPER_PROTECTORS_H + +#include +#include +#include + +class ExceptionProtector : public CppUnit::Protector { +public: + bool protect(const CppUnit::Functor &functor, const CppUnit::ProtectorContext &context) override; +}; + +#endif // LIBTORRENT_HELPER_PROTECTORS_H diff --git a/test/helpers/utils.h b/test/helpers/utils.h new file mode 100644 index 00000000..e81d22eb --- /dev/null +++ b/test/helpers/utils.h @@ -0,0 +1,60 @@ +#ifndef LIBTORRENT_HELPER_UTILS_H +#define LIBTORRENT_HELPER_UTILS_H + +#include +#include +#include +#include + +static void +dump_failure_log(const failure_type& failure) { + if (failure.log->empty()) + return; + + std::cout << std::endl << failure.name << std::endl; + + // Doesn't print dump messages as log_buffer drops them. + std::for_each(failure.log->begin(), failure.log->end(), [](const torrent::log_entry& entry) { + std::cout << entry.timestamp << ' ' << entry.message << '\n'; + }); + + std::cout << std::flush; +} + +static void +dump_failures(const failure_list_type& failures) { + if (failures.empty()) + return; + + std::cout << std::endl + << "=================" << std::endl + << "Failed Test Logs:" << std::endl + << "=================" << std::endl; + + std::for_each(failures.begin(), failures.end(), [](const failure_type& failure) { + dump_failure_log(failure); + }); + std::cout << std::endl; +} + +static +void add_tests(CppUnit::TextUi::TestRunner& runner, const char* c_test_names) { + if (c_test_names == NULL || std::string(c_test_names).empty()) { + runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest()); + return; + } + + const std::string& test_names(c_test_names); + + size_t pos = 0; + size_t next = 0; + + while ((next = test_names.find(',', pos)) < test_names.size()) { + runner.addTest(CppUnit::TestFactoryRegistry::getRegistry(test_names.substr(pos, next - pos)).makeTest()); + pos = next + 1; + } + + runner.addTest(CppUnit::TestFactoryRegistry::getRegistry(test_names.substr(pos)).makeTest()); +} + +#endif diff --git a/test/main.cc b/test/main.cc index 171a5788..e87fdc99 100644 --- a/test/main.cc +++ b/test/main.cc @@ -1,22 +1,97 @@ +#include "config.h" + +#include +#include +#include +#include +#include #include +#include +#include +#include #include #include -int main(int argc, char* argv[]) -{ - // Get the top level suite from the registry - CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest(); +#ifdef HAVE_BACKTRACE +#include +#endif - // Adds the test to the list of test to run - CppUnit::TextUi::TestRunner runner; - runner.addTest( suite ); +#include "helpers/progress_listener.h" +#include "helpers/protectors.h" +#include "helpers/utils.h" - // Change the default outputter to a compiler error format outputter - runner.setOutputter( new CppUnit::CompilerOutputter( &runner.result(), - std::cerr ) ); - // Run the tests. - bool wasSucessful = runner.run(); +CPPUNIT_REGISTRY_ADD_TO_DEFAULT("rpc"); +CPPUNIT_REGISTRY_ADD_TO_DEFAULT("src"); - // Return error code 1 if the one of test failed. - return wasSucessful ? 0 : 1; +void +do_test_panic(int signum) { + signal(signum, SIG_DFL); + + std::cout << std::endl << std::endl << "Caught " << strsignal(signum) << ", dumping stack:" << std::endl << std::endl; + +#ifdef HAVE_BACKTRACE + void* stackPtrs[20]; + + // Print the stack and exit. + int stackSize = backtrace(stackPtrs, 20); + char** stackStrings = backtrace_symbols(stackPtrs, stackSize); + + for (int i = 0; i < stackSize; ++i) + std::cout << stackStrings[i] << std::endl; + +#else + std::cout << "Stack dump not enabled." << std::endl; +#endif + + std::cout << std::endl; + torrent::log_cleanup(); + std::abort(); +} + +void +register_signal_handlers() { + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = &do_test_panic; + + if (sigaction(SIGSEGV, &sa, NULL) == -1) { + std::cout << "Could not register signal handlers." << std::endl; + exit(-1); + } +} + +int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) { + register_signal_handlers(); + + CppUnit::TestResult controller; + CppUnit::TestResultCollector result; + progress_listener progress; + + controller.addListener(&result); + controller.addListener(&progress); + + controller.popProtector(); + controller.pushProtector(new ExceptionProtector()); + + CppUnit::TextUi::TestRunner runner; + add_tests(runner, std::getenv("TEST_NAME")); + + try { + std::cout << "Running "; + runner.run( controller ); + + // TODO: Make outputter. + dump_failures(progress.failures()); + + // Print test in a compiler compatible format. + CppUnit::CompilerOutputter outputter( &result, std::cerr ); + outputter.write(); + + } catch ( std::invalid_argument &e ) { // Test path not resolved + std::cerr << std::endl << "ERROR: " << e.what() << std::endl; + return 1; + } + + return result.wasSuccessful() ? 0 : 1; } diff --git a/test/rpc/xmlrpc_test.cc b/test/rpc/xmlrpc_test.cc index be0dee19..1bfdeadc 100644 --- a/test/rpc/xmlrpc_test.cc +++ b/test/rpc/xmlrpc_test.cc @@ -10,7 +10,7 @@ CPPUNIT_TEST_SUITE_REGISTRATION(XmlrpcTest); -torrent::Object xmlrpc_cmd_test_reflect(rpc::target_type t, const torrent::Object& obj) { return obj; } +torrent::Object xmlrpc_cmd_test_reflect([[maybe_unused]] rpc::target_type t, const torrent::Object& obj) { return obj; } void initialize_command_dynamic(); @@ -100,7 +100,6 @@ std::vector> basic_requests = void XmlrpcTest::setUp() { - m_commandItr = m_commands; m_xmlrpc = rpc::XmlRpc(); m_xmlrpc.initialize(); setlocale(LC_ALL, ""); diff --git a/test/rpc/xmlrpc_test.h b/test/rpc/xmlrpc_test.h index 9f9312e0..df27d32f 100644 --- a/test/rpc/xmlrpc_test.h +++ b/test/rpc/xmlrpc_test.h @@ -22,9 +22,8 @@ public: private: rpc::XmlRpc m_xmlrpc; - + rpc::CommandMap m_map; rpc::command_base m_commands[cmd_size]; - rpc::command_base* m_commandItr; };