From 1f89fb0f08a8e2e8ed066e8b7f32c62806da264b Mon Sep 17 00:00:00 2001 From: Jaroslav Rohel Date: Mon, 28 Oct 2024 22:22:59 +0100 Subject: [PATCH 1/5] Make `BaseTestCase` for Perl unit test Unit tests for Python and Ruby implement the `BaseTestCase` class, which containsshared initialization code. This implements the `BaseTestCase` class for Perl unit tests. --- dnf5.spec | 1 + test/perl5/CMakeLists.txt | 2 +- test/perl5/libdnf5/BaseTestCase.pm | 110 +++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 test/perl5/libdnf5/BaseTestCase.pm diff --git a/dnf5.spec b/dnf5.spec index cb0265665..b87e52d5c 100644 --- a/dnf5.spec +++ b/dnf5.spec @@ -242,6 +242,7 @@ BuildRequires: perl(strict) BuildRequires: perl(Test::More) BuildRequires: perl(Test::Exception) BuildRequires: perl(warnings) +BuildRequires: perl(FindBin) %endif %endif diff --git a/test/perl5/CMakeLists.txt b/test/perl5/CMakeLists.txt index f07c757a4..923955d3d 100644 --- a/test/perl5/CMakeLists.txt +++ b/test/perl5/CMakeLists.txt @@ -4,7 +4,7 @@ endif() find_package(Perl REQUIRED) -foreach(MODULE "strict" "Test::More" "Test::Exception" "warnings") +foreach(MODULE "strict" "Test::More" "Test::Exception" "warnings" "FindBin") message(STATUS "Checking for ${MODULE} Perl module") execute_process( COMMAND "${PERL_EXECUTABLE}" -e "require ${MODULE}" diff --git a/test/perl5/libdnf5/BaseTestCase.pm b/test/perl5/libdnf5/BaseTestCase.pm new file mode 100644 index 000000000..95112fa2e --- /dev/null +++ b/test/perl5/libdnf5/BaseTestCase.pm @@ -0,0 +1,110 @@ +# Copyright Contributors to the libdnf project. +# +# This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ +# +# Libdnf 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. +# +# Libdnf 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 libdnf. If not, see . + +package BaseTestCase; + +use strict; +use warnings; + +use File::Temp qw(tempdir); +use File::Spec::Functions 'catfile'; + +use libdnf5::base; +use libdnf5::repo; + + +my $PROJECT_BINARY_DIR = $ENV{"PROJECT_BINARY_DIR"}; +my $PROJECT_SOURCE_DIR = $ENV{"PROJECT_SOURCE_DIR"}; + +sub new { + my $class = shift; + my $self = {}; + + $self->{base} = new libdnf5::base::Base(); + + $self->{tmpdir} = tempdir("libdnf5_perl5_unittest.XXXX", TMPDIR => 1, CLEANUP => 1); + + my $config = $self->{base}->get_config(); + $config->get_installroot_option()->set($libdnf5::conf::Option::Priority_RUNTIME, $self->{tmpdir}."/installroot"); + $config->get_cachedir_option()->set($libdnf5::conf::Option::Priority_RUNTIME, $self->{tmpdir}."/cache"); + $config->get_optional_metadata_types_option()->set($libdnf5::conf::Option::Priority_RUNTIME, $libdnf5::conf::OPTIONAL_METADATA_TYPES); + + # Prevent loading plugins from host + $config->get_plugins_option()->set(0); + + my $vars = $self->{base}->get_vars()->get(); + $vars->set("arch", "x86_64"); + + $self->{base}->setup(); + + $self->{repo_sack} = $self->{base}->get_repo_sack(); + $self->{package_sack} = $self->{base}->get_rpm_package_sack(); + + return bless ($self, $class); +} + +sub tearDown { + my $self = shift; + # shutil.rmtree(self.temp_dir) +} + +sub _add_repo { + # Add a repo from `repo_path`. + my $self = shift; + my $repoid = shift; + my $repo_path = shift; + my $load = shift // 1; # True is default + + my $repo = $self->{repo_sack}->create_repo($repoid); + $repo->get_config()->get_baseurl_option()->set($libdnf5::conf::Option::Priority_RUNTIME, "file://".$repo_path); + if ($load) { + $self->{repo_sack}->load_repos($libdnf5::repo::Repo::Type_AVAILABLE); + } + + return $repo +} + +sub add_repo_repomd { + # Add a repo from PROJECT_SOURCE_DIR/test/data/repos-repomd//repodata + my $self = shift; + my $repoid = shift; + my $load = shift // 1; # True is default + + my $repo_path = catfile($PROJECT_SOURCE_DIR, "/test/data/repos-repomd", $repoid); + return $self->_add_repo($repoid, $repo_path, $load) +} + +sub add_repo_rpm { + # Add a repo from PROJECT_BINARY_DIR/test/data/repos-rpm//repodata + my $self = shift; + my $repoid = shift; + my $load = shift // 1; # True is default + + my $repo_path = catfile($PROJECT_BINARY_DIR, "test/data/repos-rpm", $repoid); + return $self->_add_repo($repoid, $repo_path, $load) +} + +sub add_repo_solv { + # Add a repo from PROJECT_SOURCE_DIR/test/data/repos-solv/.repo + my $self = shift; + my $repoid = shift; + + my $repo_path = catfile($PROJECT_SOURCE_DIR, "/test/data/repos-solv", $repoid.".repo"); + return $self->{repo_sack}->create_repo_from_libsolv_testcase($repoid, $repo_path); +} + +1; From 515aa0f03516bc64aad560966e41c1efe13ea311 Mon Sep 17 00:00:00 2001 From: Jaroslav Rohel Date: Wed, 6 Nov 2024 12:45:08 +0100 Subject: [PATCH 2/5] Perl unit tests: Clean package_query tests, use BaseTestCase There is no need to write initialization code in the tests. Instead, we can use the `BaseTestCase` class, just like in Python and Ruby tests. --- test/perl5/libdnf5/rpm/test_package_query.t | 52 ++++----------------- 1 file changed, 10 insertions(+), 42 deletions(-) diff --git a/test/perl5/libdnf5/rpm/test_package_query.t b/test/perl5/libdnf5/rpm/test_package_query.t index c378a5b78..21f0c8305 100644 --- a/test/perl5/libdnf5/rpm/test_package_query.t +++ b/test/perl5/libdnf5/rpm/test_package_query.t @@ -17,60 +17,28 @@ use strict; use warnings; -no if $] >= 5.010, warnings => qw(experimental::smartmatch); use Test::More; -use Cwd qw(cwd); -use File::Temp qw(tempdir); -use File::Spec::Functions 'catfile'; +use FindBin; +use lib "$FindBin::Bin/.."; +use BaseTestCase; -use libdnf5::base; -my $base = new libdnf5::base::Base(); - -# Sets path to cache directory. -my $tmpdir = tempdir("libdnf5_perl5_unittest.XXXX", TMPDIR => 1, CLEANUP => 1); -$base->get_config()->get_installroot_option()->set($libdnf5::conf::Option::Priority_RUNTIME, $tmpdir."/installroot"); -$base->get_config()->get_cachedir_option()->set($libdnf5::conf::Option::Priority_RUNTIME, $tmpdir."/cache"); - -# Prevent loading plugins from host -$base->get_config()->get_plugins_option()->set("False"); - -# Sets base internals according to configuration -$base->setup(); - -my $repo_sack = $base->get_repo_sack(); - -# Creates new repositories in the repo_sack +# Create an instance of BaseTestCase, it will be shared for all tests +my $test = new BaseTestCase(); my $repoid = "repomd-repo1"; -my $repo = $repo_sack->create_repo($repoid); - -# Tunes repository configuration (baseurl is mandatory) -my $project_source_dir = $ENV{"PROJECT_SOURCE_DIR"}; -my $repo_path = catfile($project_source_dir, "/test/data/repos-repomd/repomd-repo1/"); -my $baseurl = "file://" . $repo_path; -my $repo_cfg = $repo->get_config(); -$repo_cfg->get_baseurl_option()->set($libdnf5::conf::Option::Priority_RUNTIME, $baseurl); +my $repo = $test->add_repo_repomd($repoid); -# fetch repo metadata and load it -$repo_sack->load_repos($libdnf5::repo::Repo::Type::AVAILABLE); - -#test_size() +# test_size() { - my $query = new libdnf5::rpm::PackageQuery($base); + my $query = new libdnf5::rpm::PackageQuery($test->{base}); is($query->size(), 3); } -my @nevras = ("CQRlib-1.1.1-4.fc29.src", "CQRlib-1.1.1-4.fc29.x86_64"); -my @nevras_contains = ("CQRlib-1.1.1-4.fc29.src", "CQRlib-1.1.1-4.fc29.x86_64", - "CQRlib-devel-1.1.2-16.fc29.src", "CQRlib-devel-1.1.2-16.fc29.x86_64"); -my @full_nevras = ("CQRlib-0:1.1.1-4.fc29.src", "CQRlib-0:1.1.1-4.fc29.x86_64", - "nodejs-1:5.12.1-1.fc29.src", "nodejs-1:5.12.1-1.fc29.x86_64"); - # Test QueryCmp::EQ { - my $query = new libdnf5::rpm::PackageQuery($base); + my $query = new libdnf5::rpm::PackageQuery($test->{base}); $query->filter_name(["pkg"]); is($query->size(), 1); @@ -88,7 +56,7 @@ my @full_nevras = ("CQRlib-0:1.1.1-4.fc29.src", "CQRlib-0:1.1.1-4.fc29.x86_64", # Test QueryCmp::GLOB { - my $query = new libdnf5::rpm::PackageQuery($base); + my $query = new libdnf5::rpm::PackageQuery($test->{base}); $query->filter_name(["pk*"], $libdnf5::common::QueryCmp_GLOB); is($query->size(), 2); From e663f4e6a19826db128e1c9391b78a446820ba81 Mon Sep 17 00:00:00 2001 From: Jaroslav Rohel Date: Wed, 6 Nov 2024 12:50:17 +0100 Subject: [PATCH 3/5] Basic Perl unit tests for DownloadCallbacks and RepoCallbacks --- .../libdnf5/repo/test_package_downloader.t | 88 ++++++++++++ test/perl5/libdnf5/repo/test_repo.t | 126 ++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 test/perl5/libdnf5/repo/test_package_downloader.t create mode 100644 test/perl5/libdnf5/repo/test_repo.t diff --git a/test/perl5/libdnf5/repo/test_package_downloader.t b/test/perl5/libdnf5/repo/test_package_downloader.t new file mode 100644 index 000000000..a9eb5af66 --- /dev/null +++ b/test/perl5/libdnf5/repo/test_package_downloader.t @@ -0,0 +1,88 @@ +# Copyright Contributors to the libdnf project. +# +# This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ +# +# Libdnf 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. +# +# Libdnf 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 libdnf. If not, see . + +use strict; +use warnings; + +use Test::More; + +use FindBin; +use lib "$FindBin::Bin/.."; +use BaseTestCase; + +{ + package PackageDownloadCallbacks; + use base qw(libdnf5::repo::DownloadCallbacks); + + sub new { + my $class = shift; + my $self = $class->SUPER::new(@_); + $self->{end_cnt} = 0; + $self->{progress_cnt} = 0; + $self->{mirror_failure_cnt} = 0; + return bless($self, $class); + } + + sub end { + my ($self, $user_cb_data, $status, $error_message) = @_; + $self->{end_cnt}++; + return 0; + } + + sub progress { + my ($self, $user_cb_data, $total_to_download, $downloaded) = @_; + $self->{progress_cnt}++; + return 0; + } + + sub mirror_failure { + my ($self, $user_cb_data, $msg, $url, $metadata) = @_; + my $self = shift; + $self->{mirror_failure_cnt}++; + return 0; + } +} + +my $test = new BaseTestCase(); + +my $repo = $test->add_repo_rpm("rpm-repo1"); + +my $query = new libdnf5::rpm::PackageQuery($test->{base}); +$query->filter_name(["one"]); +$query->filter_arch(["noarch"]); +is($query->size(), 2); + +my $downloader = new libdnf5::repo::PackageDownloader($test->{base}); + +my $cbs = new PackageDownloadCallbacks(); +$test->{base}->set_download_callbacks(new libdnf5::repo::DownloadCallbacksUniquePtr($cbs)); + +my $it = $query->begin(); +my $e = $query->end(); +while ($it != $e) { + $downloader->add($it->value()); + $it->next(); +} +$downloader->download(); + +$cbs = $test->{base}->get_download_callbacks(); + +is($cbs->{end_cnt}, 2); +ok($cbs->{progress_cnt} >= 2); +is($cbs->{mirror_failure_cnt}, 0); + +done_testing(); diff --git a/test/perl5/libdnf5/repo/test_repo.t b/test/perl5/libdnf5/repo/test_repo.t new file mode 100644 index 000000000..c165173d4 --- /dev/null +++ b/test/perl5/libdnf5/repo/test_repo.t @@ -0,0 +1,126 @@ +# Copyright Contributors to the libdnf project. +# +# This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ +# +# Libdnf 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. +# +# Libdnf 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 libdnf. If not, see . + +use strict; +use warnings; + +use Test::More; + +use FindBin; +use lib "$FindBin::Bin/.."; +use BaseTestCase; + +{ + package DownloadCallbacks; + use base qw(libdnf5::repo::DownloadCallbacks); + + sub new { + my $class = shift; + my $self = $class->SUPER::new(@_); + $self->{end_cnt} = 0; + $self->{end_error_message} = undef; + $self->{fastest_mirror_cnt} = 0; + $self->{handle_mirror_failure_cnt} = 0; + return bless($self, $class); + } + + sub end { + my ($self, $user_cb_data, $status, $error_message) = @_; + $self->{end_cnt}++; + $self->{end_error_message} = $error_message; + return 0; + } + + sub fastest_mirror { + my ($self, $user_cb_data, $stage, $ptr) = @_; + $self->{fastest_mirror_cnt}++; + } + + sub handle_mirror_failure { + my ($self, $user_cb_data, $msg, $url, $metadata) = @_; + $self->{handle_mirror_failure_cnt}++; + return 0; + } +} + +{ + package RepoCallbacks; + use base qw(libdnf5::repo::RepoCallbacks); + + sub new { + my $class = shift; + my $self = $class->SUPER::new(@_); + $self->{repokey_import_cnt} = 0; + return bless($self, $class); + } + + sub repokey_import { + my ($self, $id, $user_id, $fingerprint, $url, $timestamp) = @_; + $self->{repokey_import_cnt}++; + return 1; + } +} + +# test_load_repo +{ + my $test = new BaseTestCase(); + + my $repoid = "repomd-repo1"; + my $repo = $test->add_repo_repomd($repoid, 0); + + my $dl_cbs = new DownloadCallbacks(); + $test->{base}->set_download_callbacks(new libdnf5::repo::DownloadCallbacksUniquePtr($dl_cbs)); + $dl_cbs = $test->{base}->get_download_callbacks(); + + my $cbs = new RepoCallbacks(); + $repo->set_callbacks(new libdnf5::repo::RepoCallbacksUniquePtr($cbs)); + + $test->{repo_sack}->load_repos($libdnf5::repo::Repo::Type_AVAILABLE); + + is($dl_cbs->{end_cnt}, 1); + is($dl_cbs->{end_error_message}, undef); + + is($dl_cbs->{fastest_mirror_cnt}, 0); + is($dl_cbs->{handle_mirror_failure_cnt}, 0); + is($cbs->{repokey_import_cnt}, 0); +} + +# test_load_repo_overload +{ + my $test = new BaseTestCase(); + + my $repoid = "repomd-repo1"; + my $repo = $test->add_repo_repomd($repoid, 0); + + my $dl_cbs = new DownloadCallbacks(); + $test->{base}->set_download_callbacks(new libdnf5::repo::DownloadCallbacksUniquePtr($dl_cbs)); + $dl_cbs = $test->{base}->get_download_callbacks(); + + my $cbs = new RepoCallbacks(); + $repo->set_callbacks(new libdnf5::repo::RepoCallbacksUniquePtr($cbs)); + + $test->{repo_sack}->load_repos(); + + is($dl_cbs->{end_cnt}, 1); + is($dl_cbs->{end_error_message}, undef); + + is($dl_cbs->{fastest_mirror_cnt}, 0); + is($dl_cbs->{handle_mirror_failure_cnt}, 0); + is($cbs->{repokey_import_cnt}, 0); +} + +done_testing(); From 54d1a292e5671c1ba9d240514861d340d0a0d994 Mon Sep 17 00:00:00 2001 From: Jaroslav Rohel Date: Sat, 2 Nov 2024 19:08:19 +0100 Subject: [PATCH 4/5] SWIG bindings for `user_cb_data` in `repo::DownloadCallbacks` In the C++ API, the user can return a pointer (void *) to user data in the `add_new_download` method of the `repo::DownlodCallbacks` class. This pointer is then passed as an argument to `void * user_cb_data` in other methods of the `repo::DownloadCallbacks` class. There was a problem of how to use this mechanism in SWIG bindings. I implemented and tested several solutions. In the case of passing a pointer, there was a problem with the ownership of the data the pointer points to. Users of Python and some other languages are used to the gargabe collector and automatically holding ownership of the passed data. While I've solved this for `user_cb_data`, passing another void pointer will need to be solved (I'll do that later). And from there, the ownership will be more complicated. We need the solution to work reliably in Python, Ruby and Perl. And possibly be easy to use for other languages in the future. In the end, I chose the method of passing an integer instead of a pointer. When passing an integer, there is no need to deal with who owns the number. The integer is passed by value. And if the user needs to pass objects, for example, he can create an array of objects and pass the index (integer) to the array. The array also provides ownership of the objects. --- bindings/libdnf5/repo.i | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/bindings/libdnf5/repo.i b/bindings/libdnf5/repo.i index 27f4735ce..a1423643f 100644 --- a/bindings/libdnf5/repo.i +++ b/bindings/libdnf5/repo.i @@ -53,7 +53,40 @@ %include "libdnf5/repo/config_repo.hpp" %feature("director") DownloadCallbacks; + +%typemap(directorin, noblock=1) void * user_cb_data { + $input = SWIG_From_int(static_cast(reinterpret_cast($1))); +} + +%typemap(directorout, noblock=1) void * { + int swig_val; + int swig_res = SWIG_AsVal_int($1, &swig_val); + if (!SWIG_IsOK(swig_res)) { + Swig::DirectorTypeMismatchException::raise(SWIG_ErrorType(SWIG_ArgError(swig_res)), "in output value of type '""int""'"); + } + $result = reinterpret_cast(swig_val); +} + +%typemap(in, noblock=1) void * user_cb_data { + { + int swig_val; + int swig_res = SWIG_AsVal_int($input, &swig_val); + if (!SWIG_IsOK(swig_res)) { + Swig::DirectorTypeMismatchException::raise(SWIG_ErrorType(SWIG_ArgError(swig_res)), "in input value of type '""int""'"); + } + $1 = reinterpret_cast(swig_val); + } +} + +%typemap(out, noblock=1) void * { + $result = SWIG_From_int(static_cast(reinterpret_cast($1))); +} + %include "libdnf5/repo/download_callbacks.hpp" +%typemap(directorin) void *; +%typemap(directorout) void * user_cb_data; +%typemap(in) void * user_cb_data; +%typemap(out) void *; wrap_unique_ptr(DownloadCallbacksUniquePtr, libdnf5::repo::DownloadCallbacks); %ignore FileDownloadError; From 604e43f9a382f60604e41e50908366ae77c1654a Mon Sep 17 00:00:00 2001 From: Jaroslav Rohel Date: Thu, 7 Nov 2024 11:07:42 +0100 Subject: [PATCH 5/5] Extend unit tests with `user_cb_data` in `repo::DownloadCallbacks` The `test_package_downloader` unit tests for C++, Python, Ruby, Perl now test passing `user_cb_data`. In the `test_repo` tests, the implementation of the `add_new_download` and `progress` methods has been intentionally removed to test functionality in case the library user does not implement these methods. --- test/libdnf5/repo/test_package_downloader.cpp | 53 +++++++++---- .../libdnf5/repo/test_package_downloader.t | 37 ++++++++-- .../libdnf5/repo/test_package_downloader.py | 74 +++++++++++++------ test/python3/libdnf5/repo/test_repo.py | 21 ------ .../libdnf5/repo/test_package_downloader.rb | 58 ++++++++++----- test/ruby/libdnf5/repo/test_repo.rb | 38 +++++----- 6 files changed, 177 insertions(+), 104 deletions(-) diff --git a/test/libdnf5/repo/test_package_downloader.cpp b/test/libdnf5/repo/test_package_downloader.cpp index 178381b83..f2925095e 100644 --- a/test/libdnf5/repo/test_package_downloader.cpp +++ b/test/libdnf5/repo/test_package_downloader.cpp @@ -29,6 +29,7 @@ along with libdnf. If not, see . #include #include +#include #include CPPUNIT_TEST_SUITE_REGISTRATION(PackageDownloaderTest); @@ -41,13 +42,15 @@ class DownloadCallbacks : public libdnf5::repo::DownloadCallbacks { [[maybe_unused]] const char * description, [[maybe_unused]] double total_to_download) override { ++add_new_download_cnt; - return nullptr; + std::string user_cb_data = std::string("Package: ") + description; + return user_cb_data_container.emplace_back(std::move(user_cb_data)).data(); } int end([[maybe_unused]] void * user_cb_data, TransferStatus status, const char * msg) override { ++end_cnt; - end_status = status; - end_msg = libdnf5::utils::string::c_to_str(msg); + user_cb_data_array.emplace_back(static_cast(user_cb_data)); + end_status.emplace_back(status); + end_msg.emplace_back(libdnf5::utils::string::c_to_str(msg)); return 0; } @@ -68,13 +71,16 @@ class DownloadCallbacks : public libdnf5::repo::DownloadCallbacks { return 0; } - int add_new_download_cnt = 0; - int end_cnt = 0; - TransferStatus end_status = TransferStatus::ERROR; - std::string end_msg; + std::vector user_cb_data_container; + int add_new_download_cnt = 0; int progress_cnt = 0; int mirror_failure_cnt = 0; + int end_cnt = 0; + + std::vector user_cb_data_array; + std::vector end_status; + std::vector end_msg; }; void PackageDownloaderTest::test_package_downloader() { @@ -82,9 +88,8 @@ void PackageDownloaderTest::test_package_downloader() { libdnf5::rpm::PackageQuery query(base); query.filter_name("one"); - query.filter_version("2"); query.filter_arch("noarch"); - CPPUNIT_ASSERT_EQUAL((size_t)1, query.size()); + CPPUNIT_ASSERT_EQUAL((size_t)2, query.size()); auto downloader = libdnf5::repo::PackageDownloader(base); @@ -92,17 +97,33 @@ void PackageDownloaderTest::test_package_downloader() { auto cbs = cbs_unique_ptr.get(); base.set_download_callbacks(std::move(cbs_unique_ptr)); - downloader.add(*query.begin()); + for (const auto & package : query) { + downloader.add(package); + } downloader.download(); - CPPUNIT_ASSERT_EQUAL(1, cbs->add_new_download_cnt); - CPPUNIT_ASSERT_EQUAL(1, cbs->end_cnt); - CPPUNIT_ASSERT_EQUAL(DownloadCallbacks::TransferStatus::SUCCESSFUL, cbs->end_status); - CPPUNIT_ASSERT_EQUAL(std::string(""), cbs->end_msg); + std::sort(cbs->user_cb_data_container.begin(), cbs->user_cb_data_container.end()); + CPPUNIT_ASSERT_EQUAL( + (std::vector{"Package: one-0:1-1.noarch", "Package: one-0:2-1.noarch"}), + cbs->user_cb_data_container); - CPPUNIT_ASSERT_GREATEREQUAL(1, cbs->progress_cnt); + CPPUNIT_ASSERT_EQUAL(2, cbs->add_new_download_cnt); + CPPUNIT_ASSERT_EQUAL(2, cbs->end_cnt); + CPPUNIT_ASSERT_GREATEREQUAL(2, cbs->progress_cnt); CPPUNIT_ASSERT_EQUAL(0, cbs->mirror_failure_cnt); + + std::sort(cbs->user_cb_data_array.begin(), cbs->user_cb_data_array.end(), [](const char * a, const char * b) { + return std::string_view(a).compare(b) < 0; + }); + CPPUNIT_ASSERT_EQUAL(cbs->user_cb_data_container[0].c_str(), cbs->user_cb_data_array[0]); + CPPUNIT_ASSERT_EQUAL(cbs->user_cb_data_container[1].c_str(), cbs->user_cb_data_array[1]); + + CPPUNIT_ASSERT_EQUAL( + (std::vector{ + DownloadCallbacks::TransferStatus::SUCCESSFUL, DownloadCallbacks::TransferStatus::SUCCESSFUL}), + cbs->end_status); + CPPUNIT_ASSERT_EQUAL((std::vector{"", ""}), cbs->end_msg); } void PackageDownloaderTest::test_package_downloader_temp_files_memory() { @@ -136,6 +157,8 @@ void PackageDownloaderTest::test_package_downloader_temp_files_memory() { CPPUNIT_ASSERT_EQUAL(4, cbs->add_new_download_cnt); CPPUNIT_ASSERT_EQUAL(4, cbs->end_cnt); + CPPUNIT_ASSERT_GREATEREQUAL(4, cbs->progress_cnt); + CPPUNIT_ASSERT_EQUAL(0, cbs->mirror_failure_cnt); auto paths_prefix = std::filesystem::path(repo->get_cachedir()) / std::filesystem::path("packages"); const std::vector expected = { diff --git a/test/perl5/libdnf5/repo/test_package_downloader.t b/test/perl5/libdnf5/repo/test_package_downloader.t index a9eb5af66..15d8d3cd8 100644 --- a/test/perl5/libdnf5/repo/test_package_downloader.t +++ b/test/perl5/libdnf5/repo/test_package_downloader.t @@ -31,28 +31,46 @@ use BaseTestCase; sub new { my $class = shift; my $self = $class->SUPER::new(@_); - $self->{end_cnt} = 0; + $self->{user_cb_data_container} = []; + $self->{start_cnt} = 0; $self->{progress_cnt} = 0; $self->{mirror_failure_cnt} = 0; + $self->{end_cnt} = 0; + $self->{user_cb_data_array} = []; + $self->{end_status} = []; + $self->{end_msg} = []; return bless($self, $class); } + sub add_new_download { + my ($self, $user_data, $description, $total_to_download) = @_; + $self->{start_cnt}++; + my $user_cb_data = "Package: " . $description; + push(@{$self->{user_cb_data_container}}, $user_cb_data); + return scalar @{$self->{user_cb_data_container}} - 1; + } + sub end { my ($self, $user_cb_data, $status, $error_message) = @_; $self->{end_cnt}++; + ::ok($user_cb_data>=0 && $user_cb_data<=1, "end: user_cb_data"); + push @{$self->{user_cb_data_array}}, $user_cb_data; + push @{$self->{end_status}}, $status; + push @{$self->{end_msg}}, $error_message; return 0; } sub progress { my ($self, $user_cb_data, $total_to_download, $downloaded) = @_; $self->{progress_cnt}++; + ::ok($user_cb_data>=0 && $user_cb_data<=1, "progress: user_cb_data"); return 0; } sub mirror_failure { my ($self, $user_cb_data, $msg, $url, $metadata) = @_; - my $self = shift; $self->{mirror_failure_cnt}++; + ::ok($user_cb_data>=0 && $user_cb_data<=1, "mirror_failure: user_cb_data"); return 0; } } @@ -64,7 +82,7 @@ my $repo = $test->add_repo_rpm("rpm-repo1"); my $query = new libdnf5::rpm::PackageQuery($test->{base}); $query->filter_name(["one"]); $query->filter_arch(["noarch"]); -is($query->size(), 2); +is($query->size(), 2, "number of packages in query"); my $downloader = new libdnf5::repo::PackageDownloader($test->{base}); @@ -81,8 +99,15 @@ $downloader->download(); $cbs = $test->{base}->get_download_callbacks(); -is($cbs->{end_cnt}, 2); -ok($cbs->{progress_cnt} >= 2); -is($cbs->{mirror_failure_cnt}, 0); +is_deeply($cbs->{user_cb_data_container}, ["Package: one-0:1-1.noarch", "Package: one-0:2-1.noarch"]); + +is($cbs->{start_cnt}, 2, "start_cnt"); +is($cbs->{end_cnt}, 2, "end_cnt"); +ok($cbs->{progress_cnt} >= 2, "progress_cnt"); +is($cbs->{mirror_failure_cnt}, 0, "mirror_failure_cnt"); + +is_deeply($cbs->{user_cb_data_array}, [0, 1], "user_cb_data_array"); +is_deeply($cbs->{end_status}, [$libdnf5::repo::DownloadCallbacks::TransferStatus_SUCCESSFUL, $libdnf5::repo::DownloadCallbacks::TransferStatus_SUCCESSFUL]); +is_deeply($cbs->{end_status}, [0, 0]); done_testing(); diff --git a/test/python3/libdnf5/repo/test_package_downloader.py b/test/python3/libdnf5/repo/test_package_downloader.py index 6e338f874..b492da940 100644 --- a/test/python3/libdnf5/repo/test_package_downloader.py +++ b/test/python3/libdnf5/repo/test_package_downloader.py @@ -25,43 +25,61 @@ class TestPackageDownloader(base_test_case.BaseTestCase): def test_package_downloader(self): - repo = self.add_repo_rpm("rpm-repo1") - - query = libdnf5.rpm.PackageQuery(self.base) - query.filter_name(["one"]) - query.filter_version(["2"]) - query.filter_arch(["noarch"]) - self.assertEqual(query.size(), 1) - - downloader = libdnf5.repo.PackageDownloader(self.base) - class PackageDownloadCallbacks(libdnf5.repo.DownloadCallbacks): - end_cnt = 0 - end_status = None - end_msg = None - - progress_cnt = 0 - mirror_failure_cnt = 0 + test_instance = None + + def __init__(self): + super(PackageDownloadCallbacks, self).__init__() + self.user_cb_data_container = [] # Hold references to user_cb_data + self.start_cnt = 0 + self.progress_cnt = 0 + self.mirror_failure_cnt = 0 + self.end_cnt = 0 + self.user_cb_data_array = [] + self.end_status = [] + self.end_msg = [] + + def add_new_download(self, user_data, description, total_to_download): + self.start_cnt += 1 + user_cb_data_reference = "Package: " + description + self.user_cb_data_container.append(user_cb_data_reference) + return len(self.user_cb_data_container) - 1 def end(self, user_cb_data, status, msg): self.end_cnt += 1 - self.end_status = status - self.end_msg = msg + self.test_instance.assertIn(user_cb_data, [0, 1]) + self.user_cb_data_array.append(user_cb_data) + self.end_status.append(status) + self.end_msg.append(msg) return 0 def progress(self, user_cb_data, total_to_download, downloaded): self.progress_cnt += 1 + self.test_instance.assertIn(user_cb_data, [0, 1]) return 0 def mirror_failure(self, user_cb_data, msg, url): self.mirror_failure_cnt += 1 + self.test_instance.assertIn(user_cb_data, [0, 1]) return 0 + PackageDownloadCallbacks.test_instance = self + + repo = self.add_repo_rpm("rpm-repo1") + + query = libdnf5.rpm.PackageQuery(self.base) + query.filter_name(["one"]) + query.filter_arch(["noarch"]) + self.assertEqual(query.size(), 2) + + downloader = libdnf5.repo.PackageDownloader(self.base) + cbs = PackageDownloadCallbacks() self.base.set_download_callbacks( libdnf5.repo.DownloadCallbacksUniquePtr(cbs)) - downloader.add(query.begin().value()) + for package in query: + downloader.add(package) downloader.download() @@ -69,10 +87,18 @@ def mirror_failure(self, user_cb_data, msg, url): downloader = None gc.collect() - self.assertEqual(cbs.end_cnt, 1) - self.assertEqual( - cbs.end_status, PackageDownloadCallbacks.TransferStatus_SUCCESSFUL) - self.assertEqual(cbs.end_msg, None) + cbs.user_cb_data_container.sort() + self.assertEqual(cbs.user_cb_data_container, [ + "Package: one-0:1-1.noarch", "Package: one-0:2-1.noarch"]) - self.assertGreaterEqual(cbs.progress_cnt, 1) + self.assertEqual(cbs.start_cnt, 2) + self.assertGreaterEqual(cbs.progress_cnt, 2) self.assertEqual(cbs.mirror_failure_cnt, 0) + self.assertEqual(cbs.end_cnt, 2) + + cbs.user_cb_data_array.sort() + self.assertEqual(cbs.user_cb_data_array, [0, 1]) + self.assertEqual( + cbs.end_status, + [PackageDownloadCallbacks.TransferStatus_SUCCESSFUL, PackageDownloadCallbacks.TransferStatus_SUCCESSFUL]) + self.assertEqual(cbs.end_msg, [None, None]) diff --git a/test/python3/libdnf5/repo/test_repo.py b/test/python3/libdnf5/repo/test_repo.py index 1a38bbbe5..6c543c8b4 100644 --- a/test/python3/libdnf5/repo/test_repo.py +++ b/test/python3/libdnf5/repo/test_repo.py @@ -24,30 +24,17 @@ class TestRepo(base_test_case.BaseTestCase): class DownloadCallbacks(libdnf5.repo.DownloadCallbacks): - start_cnt = 0 - start_what = None - end_cnt = 0 end_error_message = None - progress_cnt = 0 fastest_mirror_cnt = 0 handle_mirror_failure_cnt = 0 - def add_new_download(self, user_data, descr, total): - self.start_cnt += 1 - self.start_what = descr - return None - def end(self, user_cb_data, status, error_message): self.end_cnt += 1 self.end_error_message = error_message return 0 - def progress(self, user_cb_data, total_to_download, downloaded): - self.progress_cnt += 1 - return 0 - def fastest_mirror(self, user_cb_data, stage, ptr): self.fastest_mirror_cnt += 1 @@ -77,13 +64,9 @@ def test_load_repo(self): self.repo_sack.load_repos(libdnf5.repo.Repo.Type_AVAILABLE) - self.assertEqual(dl_cbs.start_cnt, 1) - self.assertEqual(dl_cbs.start_what, repoid) - self.assertEqual(dl_cbs.end_cnt, 1) self.assertEqual(dl_cbs.end_error_message, None) - self.assertGreaterEqual(dl_cbs.progress_cnt, 1) self.assertEqual(dl_cbs.fastest_mirror_cnt, 0) self.assertEqual(dl_cbs.handle_mirror_failure_cnt, 0) self.assertEqual(cbs.repokey_import_cnt, 0) @@ -101,13 +84,9 @@ def test_load_repo_overload(self): self.repo_sack.load_repos() - self.assertEqual(dl_cbs.start_cnt, 1) - self.assertEqual(dl_cbs.start_what, repoid) - self.assertEqual(dl_cbs.end_cnt, 1) self.assertEqual(dl_cbs.end_error_message, None) - self.assertGreaterEqual(dl_cbs.progress_cnt, 1) self.assertEqual(dl_cbs.fastest_mirror_cnt, 0) self.assertEqual(dl_cbs.handle_mirror_failure_cnt, 0) self.assertEqual(cbs.repokey_import_cnt, 0) diff --git a/test/ruby/libdnf5/repo/test_package_downloader.rb b/test/ruby/libdnf5/repo/test_package_downloader.rb index 8744fbe6b..4d2878ee1 100644 --- a/test/ruby/libdnf5/repo/test_package_downloader.rb +++ b/test/ruby/libdnf5/repo/test_package_downloader.rb @@ -25,34 +25,47 @@ class TestPackageDownloader < BaseTestCase class PackageDownloadCallbacks < Repo::DownloadCallbacks - attr_accessor :end_cnt, :end_status, :end_msg - attr_accessor :progress_cnt, :mirror_failure_cnt + attr_accessor :user_cb_data_container + attr_accessor :start_cnt, :progress_cnt, :mirror_failure_cnt, :end_cnt + attr_accessor :user_cb_data_array, :end_status, :end_msg def initialize() super() - - @end_cnt = 0 - @end_status = nil - @end_msg = nil - + @user_cb_data_container = [] # Hold references to user_cb_data + @start_cnt = 0 @progress_cnt = 0 @mirror_failure_cnt = 0 + @end_cnt = 0 + @user_cb_data_array = [] + @end_status = [] + @end_msg = [] end - def end(user_data, status, msg) + def add_new_download(user_data, description, total_to_download) + @start_cnt += 1 + user_cb_data = "Package: " + description + @user_cb_data_container.push(user_cb_data) + return @user_cb_data_container.length() - 1 # Index of last added element + end + + def end(user_cb_data, status, msg) @end_cnt += 1 - @end_status = status - @end_msg = msg + assert(user_cb_data>=0 && user_cb_data <=1, "end: user_cb_data = #{user_cb_data.inspect}") + @user_cb_data_array.push(user_cb_data) + @end_status.push(status) + @end_msg.push(msg) return 0 end - def progress(user_data, total_to_download, downloaded) + def progress(user_cb_data, total_to_download, downloaded) @progress_cnt += 1 + assert(user_cb_data>=0 && user_cb_data <=1, "progress: user_cb_data = #{user_cb_data.inspect}") return 0 end - def mirror_failure(user_data, msg, url) + def mirror_failure(user_cb_data, msg, url) @mirror_failure_cnt += 1 + assert(user_cb_data>=0 && user_cb_data <=1, "mirror_failure: user_cb_data = #{user_cb_data.inspect}") return 0 end end @@ -62,16 +75,19 @@ def test_package_downloader() query = Rpm::PackageQuery.new(@base) query.filter_name(["one"]) - query.filter_version(["2"]) query.filter_arch(["noarch"]) - assert_equal(1, query.size()) + assert_equal(2, query.size()) downloader = Repo::PackageDownloader.new(@base) cbs = PackageDownloadCallbacks.new() @base.set_download_callbacks(Repo::DownloadCallbacksUniquePtr.new(cbs)) - downloader.add(query.begin().value) + it = query.begin() + while it != query.end() + downloader.add(it.value) + it.next() + end downloader.download() @@ -79,11 +95,15 @@ def test_package_downloader() downloader = nil GC.start - assert_equal(1, cbs.end_cnt) - assert_equal(PackageDownloadCallbacks::TransferStatus_SUCCESSFUL, cbs.end_status) - assert_equal(nil, cbs.end_msg) + assert_equal(["Package: one-0:1-1.noarch", "Package: one-0:2-1.noarch"], [cbs.user_cb_data_container[0], cbs.user_cb_data_container[1]].sort()) - assert_operator(1, :<=, cbs.progress_cnt) + assert_equal(2, cbs.start_cnt) + assert_operator(2, :<=, cbs.progress_cnt) assert_equal(0, cbs.mirror_failure_cnt) + assert_equal(2, cbs.end_cnt) + + assert_equal([0, 1], cbs.user_cb_data_array.sort()) + assert_equal([PackageDownloadCallbacks::TransferStatus_SUCCESSFUL, PackageDownloadCallbacks::TransferStatus_SUCCESSFUL], cbs.end_status) + assert_equal([nil, nil], cbs.end_msg) end end diff --git a/test/ruby/libdnf5/repo/test_repo.rb b/test/ruby/libdnf5/repo/test_repo.rb index 3dc96d224..589d3a9e3 100644 --- a/test/ruby/libdnf5/repo/test_repo.rb +++ b/test/ruby/libdnf5/repo/test_repo.rb @@ -32,35 +32,19 @@ class DownloadCallbacks < Repo::DownloadCallbacks def initialize() super() - - @start_cnt = 0 - @start_what = nil - @end_cnt = 0 @end_error_message = nil - @progress_cnt = 0 @fastest_mirror_cnt = 0 @handle_mirror_failure_cnt = 0 end - def add_new_download(user_data, descr, total) - @start_cnt += 1 - @start_what = descr - return nil - end - def end(user_cb_data, status, error_message) @end_cnt += 1 @end_error_message = error_message return 0 end - def progress(user_cb_data, total_to_download, downloaded) - @progress_cnt += 1 - return 0 - end - def fastest_mirror(user_cb_data, stage, ptr) @fastest_mirror_cnt += 1 end @@ -98,13 +82,29 @@ def test_load_repo() @repo_sack.load_repos(Repo::Repo::Type_AVAILABLE) - assert_equal(1, dl_cbs.start_cnt) - assert_equal(repoid, dl_cbs.start_what) + assert_equal(1, dl_cbs.end_cnt) + assert_equal(nil, dl_cbs.end_error_message) + + assert_equal(0, dl_cbs.fastest_mirror_cnt) + assert_equal(0, dl_cbs.handle_mirror_failure_cnt) + assert_equal(0, cbs.repokey_import_cnt) + end + + def test_load_repo_overload() + repoid = "repomd-repo1" + repo = add_repo_repomd(repoid, load=false) + + dl_cbs = DownloadCallbacks.new() + @base.set_download_callbacks(Repo::DownloadCallbacksUniquePtr.new(dl_cbs)) + + cbs = RepoCallbacks.new() + repo.set_callbacks(Repo::RepoCallbacksUniquePtr.new(cbs)) + + @repo_sack.load_repos() assert_equal(1, dl_cbs.end_cnt) assert_equal(nil, dl_cbs.end_error_message) - assert_operator(1, :<=, dl_cbs.progress_cnt) assert_equal(0, dl_cbs.fastest_mirror_cnt) assert_equal(0, dl_cbs.handle_mirror_failure_cnt) assert_equal(0, cbs.repokey_import_cnt)