├── .gitignore ├── CMakeLists.txt ├── cpp ├── CMakeLists.txt ├── common │ ├── common.hpp │ └── regex.hpp ├── console │ ├── CMakeLists.txt │ ├── colorizer.cpp │ ├── colorizer.hpp │ ├── common.cpp │ ├── common.hpp │ ├── cupt.cpp │ ├── cupt.hpp │ ├── functionselectors.cpp │ ├── functionselectors.hpp │ ├── handlers.hpp │ ├── handlers │ │ ├── download.cpp │ │ ├── managepackages.cpp │ │ ├── misc.cpp │ │ ├── search.cpp │ │ ├── shell.cpp │ │ ├── snapshot.cpp │ │ └── why.cpp │ ├── misc.cpp │ ├── misc.hpp │ ├── selectors.cpp │ └── selectors.hpp ├── downloadmethods │ ├── CMakeLists.txt │ ├── curl.cpp │ ├── debdelta.cpp │ ├── file.cpp │ └── wget.cpp ├── flags.cmake ├── lib │ ├── CMakeLists.txt │ ├── include │ │ └── cupt │ │ │ ├── cache.hpp │ │ │ ├── cache │ │ │ ├── binarypackage.hpp │ │ │ ├── binaryversion.hpp │ │ │ ├── package.hpp │ │ │ ├── relation.hpp │ │ │ ├── releaseinfo.hpp │ │ │ ├── sourcepackage.hpp │ │ │ ├── sourceversion.hpp │ │ │ └── version.hpp │ │ │ ├── common.hpp │ │ │ ├── config.hpp │ │ │ ├── download │ │ │ ├── manager.hpp │ │ │ ├── method.hpp │ │ │ ├── methodfactory.hpp │ │ │ ├── progress.hpp │ │ │ ├── progresses │ │ │ │ └── console.hpp │ │ │ └── uri.hpp │ │ │ ├── file.hpp │ │ │ ├── format2.hpp │ │ │ ├── fwd.hpp │ │ │ ├── hashsums.hpp │ │ │ ├── packagename.hpp │ │ │ ├── range.hpp │ │ │ ├── stringrange.hpp │ │ │ ├── system │ │ │ ├── resolver.hpp │ │ │ ├── resolvers │ │ │ │ └── native.hpp │ │ │ ├── snapshots.hpp │ │ │ ├── state.hpp │ │ │ └── worker.hpp │ │ │ └── versionstring.hpp │ └── src │ │ ├── cache.cpp │ │ ├── cache │ │ ├── binarypackage.cpp │ │ ├── binaryversion.cpp │ │ ├── package.cpp │ │ ├── relation.cpp │ │ ├── sourcepackage.cpp │ │ ├── sourceversion.cpp │ │ └── version.cpp │ │ ├── common.cpp │ │ ├── common │ │ ├── compareversions.cpp │ │ └── consumers.cpp │ │ ├── config.cpp │ │ ├── download │ │ ├── manager.cpp │ │ ├── method.cpp │ │ ├── methodfactory.cpp │ │ ├── progress.cpp │ │ ├── progresses │ │ │ └── console.cpp │ │ └── uri.cpp │ │ ├── file.cpp │ │ ├── hashsums.cpp │ │ ├── internal │ │ ├── basepackageiterator.cpp │ │ ├── cachefiles.cpp │ │ ├── cachefiles.hpp │ │ ├── cacheimpl.cpp │ │ ├── cacheimpl.hpp │ │ ├── common.cpp │ │ ├── common.hpp │ │ ├── configparser.cpp │ │ ├── configparser.hpp │ │ ├── debdeltahelper.cpp │ │ ├── debdeltahelper.hpp │ │ ├── exceptionlessfuture.hpp │ │ ├── filesystem.cpp │ │ ├── filesystem.hpp │ │ ├── graph.hpp │ │ ├── indexofindex.cpp │ │ ├── indexofindex.hpp │ │ ├── lock.cpp │ │ ├── lock.hpp │ │ ├── logger.cpp │ │ ├── logger.hpp │ │ ├── nativeresolver │ │ │ ├── autoremovalpossibility.cpp │ │ │ ├── autoremovalpossibility.hpp │ │ │ ├── cowmap.hpp │ │ │ ├── cowmap.tpp │ │ │ ├── decisionfailtree.cpp │ │ │ ├── decisionfailtree.hpp │ │ │ ├── dependencygraph.cpp │ │ │ ├── dependencygraph.hpp │ │ │ ├── impl.cpp │ │ │ ├── impl.hpp │ │ │ ├── score.cpp │ │ │ ├── score.hpp │ │ │ ├── solution.cpp │ │ │ └── solution.hpp │ │ ├── parse.hpp │ │ ├── parse.tpp │ │ ├── pininfo.cpp │ │ ├── pininfo.hpp │ │ ├── pipe.cpp │ │ ├── pipe.hpp │ │ ├── regex.cpp │ │ ├── regex.hpp │ │ ├── tagparser.cpp │ │ ├── tagparser.hpp │ │ ├── versionparse.cpp │ │ ├── versionparse.hpp │ │ └── worker │ │ │ ├── archives.cpp │ │ │ ├── archives.hpp │ │ │ ├── base.cpp │ │ │ ├── base.hpp │ │ │ ├── dpkg.cpp │ │ │ ├── dpkg.hpp │ │ │ ├── metadata.cpp │ │ │ ├── metadata.hpp │ │ │ ├── packages.cpp │ │ │ ├── packages.hpp │ │ │ ├── setupandpreview.cpp │ │ │ ├── setupandpreview.hpp │ │ │ ├── snapshots.cpp │ │ │ ├── snapshots.hpp │ │ │ ├── temppath.cpp │ │ │ └── temppath.hpp │ │ ├── system │ │ ├── resolver.cpp │ │ ├── resolvers │ │ │ └── native.cpp │ │ ├── snapshots.cpp │ │ ├── state.cpp │ │ └── worker.cpp │ │ └── versionstring.cpp ├── platform-tests │ └── std-function-lambda-capture.cpp ├── version.cmake └── version.hpp.in ├── debian ├── .gitignore ├── NEWS ├── changelog ├── compat ├── control ├── copyright ├── cupt.bash-completion ├── cupt.docs ├── cupt.install ├── cupt.lintian-overrides ├── cupt.manpages ├── libcupt-common.dirs ├── libcupt-common.install ├── libcupt4-2-downloadmethod-curl.install ├── libcupt4-2-downloadmethod-wget.install ├── libcupt4-2.install ├── libcupt4-2.lintian-overrides ├── libcupt4-2.postinst ├── libcupt4-2.postrm ├── libcupt4-2.shlibs ├── libcupt4-dev.install ├── libcupt4-doc.install ├── rules ├── shlibs.local └── source │ └── format ├── doc ├── .gitignore ├── CMakeLists.txt ├── NEWS ├── cupt.1.pod ├── cupt.conf.5.pod ├── cupt_vs_apt.5.pod ├── examples │ ├── CMakeLists.txt │ ├── show-available │ │ ├── CMakeLists.txt │ │ └── show-available.cpp │ └── show-upgradeable │ │ ├── CMakeLists.txt │ │ └── show-upgradeable.cpp ├── functionalselectors.t2t ├── reference │ ├── .gitignore │ └── Doxyfile └── tutorial.t2t ├── po ├── CMakeLists.txt ├── da.po ├── de.po ├── fr.po ├── getpot ├── pl.po └── sk.po ├── scripts ├── bash_completion ├── logrotate └── run_coverage.sh └── test ├── CMakeLists.txt ├── CuptInteractive.pm ├── TestCupt.pm ├── fakes ├── dpkg-repack ├── dpkg-scanpackages └── dpkg-source ├── gpg ├── expired.gpg ├── mock1k-revoke.gpg ├── mock1k.gpg ├── mock4k.gpg └── secrets.asc ├── run.sh └── t ├── help.t ├── manage ├── preview │ ├── action-color.t │ ├── action-order.t │ ├── actions.pl │ ├── auto-status-indicators.t │ ├── choosing-specific-version.t │ ├── color-check.pl │ ├── color-detection.t │ ├── one-letter-package-suffixes.t │ ├── purge.t │ ├── reason-chain │ │ ├── alternatives.t │ │ ├── basic.t │ │ ├── common.pl │ │ ├── elaboration.t │ │ ├── implicit.t │ │ └── non-relational.t │ ├── reinstall │ │ ├── multiple-candidates.t │ │ └── yes-or-no.t │ ├── show-not-preferred.t │ └── warnings.t ├── resolver │ ├── autoremoval │ │ ├── global-setting.t │ │ ├── never-autoremove-setting.t │ │ └── no-if-rdepends-setting.t │ ├── build-dep.t │ ├── conflicts.t │ ├── depending-on-many-packages.t │ ├── depending-on-more-usual-packages-dont-give-more-score │ │ ├── long-chain.t │ │ └── short-chain.t │ ├── dist-upgrade │ │ ├── basic.t │ │ └── package-arguments.t │ ├── equal-solutions-offered-in-dependency-order.t │ ├── log-order │ │ ├── predecessors-of-problematic-element.t │ │ └── problematic-elements-of-equal-priority.t │ ├── multiarch │ │ ├── colon-any.t │ │ ├── colon-native.t │ │ └── colon-unknown.t │ ├── non-default-release │ │ ├── but-automatic-upgrades.t │ │ ├── normal.t │ │ └── not-automatic.t │ ├── optimise │ │ ├── common-hidden-dependency-problem.t │ │ ├── conflicting-with-not-installed-package.t │ │ └── versions-containing-same-problem.t │ ├── reinstall │ │ ├── as-version-change-variant.t │ │ └── required.t │ ├── relative-priority-order.t │ ├── remove │ │ ├── essential-penalty-when-non-essential-version-is-considered-first.t │ │ ├── no-remove-and-explicit-removals.t │ │ ├── no-remove-prevents-removal-of-other-packages.t │ │ └── on-flexible-removal-all-version-string-ids-are-proposed.t │ ├── same-original-version-string-but-different-relations.t │ ├── same-pin-versions │ │ ├── anti-relation-order.t │ │ ├── forward-relation-order.t │ │ ├── maximum-version-is-chosen-for-broken-version-fix.t │ │ ├── maximum-version-is-chosen-for-upgrade.t │ │ ├── relation-common.pl │ │ └── version-change-common.pl │ ├── score-tuning │ │ ├── common.pl │ │ ├── downgrade.t │ │ ├── hold.t │ │ ├── max-leaf-count.t │ │ ├── negative-version-factor.t │ │ ├── new.t │ │ ├── removal.t │ │ ├── upgrade.t │ │ └── version-factor.t │ ├── soft-dependencies.t │ ├── synchronize-by-source-versions │ │ ├── not-existing-packages.t │ │ └── types.t │ ├── unsatisfy-importance-is-not-grow-if-applied-to-multiple-package-versions.t │ ├── upgrade │ │ ├── dependee-of-considered-to-be-upgraded-package.t │ │ ├── first-problematic-and-many-simpler-packages.t │ │ ├── lock-step-dependency-vs-high-pinned-installed-version.t │ │ ├── low-pin-upgrade-with-new-dependency-vs-normal-pin-upgrade.t │ │ ├── normal-pin-upgrade-vs-pinned-new-package.t │ │ ├── same-origin-versions-not-proposed.t │ │ └── to-experimental.t │ └── versioned-provides.t └── worker │ ├── packages │ ├── common.pl │ ├── downloads │ │ └── uris │ │ │ ├── common.pl │ │ │ ├── debdelta │ │ │ ├── basic.t │ │ │ └── parsing-configuration-file.t │ │ │ ├── normal.t │ │ │ └── several-uris-for-same-version.t │ ├── dpkg │ │ ├── basic.t │ │ ├── chained.t │ │ ├── common.pl │ │ ├── dependency-of-unchanged-package.t │ │ ├── essential.t │ │ ├── middle-states.t │ │ ├── priorities.t │ │ ├── purge.t │ │ └── triggers.t │ ├── extended-states │ │ ├── autoinstalled-flag-changes.t │ │ ├── common.pl │ │ └── unknown-fields-are-preserved.t │ └── hooks │ │ └── pre-install-pkgs │ │ ├── feeding-input.t │ │ ├── v2.t │ │ ├── v2plus.pl │ │ └── v3.t │ └── snapshots │ ├── basic.t │ ├── common.pl │ ├── list.t │ ├── load │ ├── corrupted.t │ ├── install-remove.t │ └── no-changes.t │ ├── remove.t │ ├── rename.t │ └── save │ ├── basic.t │ └── name-acceptance.t ├── query ├── config │ ├── case-normalisation.t │ ├── common.pl │ ├── dir-ignore-files-silently.t │ ├── optional-patterns │ │ ├── acceptance.t │ │ └── normalisation.t │ └── translation.t ├── download │ ├── metadata │ │ ├── basic.t │ │ ├── common.pl │ │ ├── compare-with-existing │ │ │ ├── diffs.t │ │ │ └── stop-if-equal.t │ │ ├── easy-entry.t │ │ ├── many-releases.t │ │ ├── several-components.t │ │ ├── translations.t │ │ └── uncompress.t │ └── source │ │ ├── basic.t │ │ ├── common.pl │ │ └── selecting-files.t ├── include-exclude-releases.t ├── parsing │ ├── dpkg-status │ │ ├── duplicate-package-name.t │ │ ├── no-file.t │ │ ├── no-package-name.t │ │ ├── no-status.t │ │ └── no-version.t │ ├── extended-states │ │ └── malformed.t │ └── relation-expressions │ │ ├── colon-suffix.t │ │ ├── error-reporting.t │ │ ├── invalid-package-name.t │ │ ├── source │ │ ├── build-profiles.t │ │ ├── error-reporting.t │ │ └── valid-syntaxes.t │ │ └── valid-syntaxes.t ├── policy │ ├── pinning │ │ ├── by-binary-and-source-package-names.t │ │ ├── by-hostname.t │ │ ├── by-release-properties.t │ │ ├── by-version-string.t │ │ ├── combined-filters.t │ │ ├── installed-version-pinned-by-version-string.t │ │ ├── missing-extra-and-invalid-lines.t │ │ ├── multiple-pin-records.t │ │ └── pinning.pl │ ├── release-info-caching.t │ ├── sources-list-syntax │ │ ├── common.pl │ │ ├── main.t │ │ └── options.t │ └── valid-until.t ├── rdepends │ ├── alphabetic-package-name-order.t │ ├── basic.t │ └── multiple-package-versions.t ├── repo-signatures │ ├── common.pl │ ├── validation-errors.t │ └── yes-no.t ├── search │ ├── fse │ │ ├── FSE.pl │ │ ├── algebraic.t │ │ ├── binary-relations.t │ │ ├── binary-to-source.t │ │ ├── binary-version-properties.t │ │ ├── error-reporting │ │ │ ├── common.pl │ │ │ ├── function-specific.t │ │ │ ├── syntax.t │ │ │ └── unknown-functions.t │ │ ├── package-and-version-properties.t │ │ ├── quoting.t │ │ ├── release-properties.t │ │ ├── source-query.t │ │ ├── source-relations.t │ │ ├── source-to-binary.t │ │ └── source-version-properties.t │ └── simple.t ├── show │ ├── binary-as-source.t │ ├── extended-states-record-without-auto-installed-tag-is-valid.t │ ├── fields │ │ ├── binarypackages.t │ │ ├── build-relations.t │ │ ├── description.t │ │ ├── important.t │ │ ├── multiarch.t │ │ ├── provides.t │ │ └── source.t │ ├── nothing-selected.t │ ├── package-existance.t │ ├── status-of-installed-package.t │ └── version-merging │ │ ├── architecture.t │ │ └── version-hash-release.t ├── version-string │ ├── compare.t │ └── invalid.t └── why │ ├── basic.t │ ├── common.pl │ ├── endurance.t │ ├── from-specific-packages.t │ ├── loop.t │ ├── priorities.t │ ├── recommends-and-suggests.t │ ├── setup-from-links.pl │ └── traverse-order.t ├── shell ├── basic.t ├── cache-reload.t ├── common.pl ├── error-handling.t └── tokenising.t └── version.t /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeFiles/ 2 | CMakeCache.txt 3 | *.cmake 4 | Makefile 5 | !/doc/examples/show-upgradeable/Makefile 6 | 7 | *.gch 8 | *.so 9 | *.so.* 10 | *.bin 11 | install_manifest.txt 12 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(Cupt) 3 | 4 | add_subdirectory(cpp) 5 | add_subdirectory(doc) 6 | add_subdirectory(po) 7 | add_subdirectory(test) 8 | 9 | -------------------------------------------------------------------------------- /cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | IF(NOT EXISTS /usr/include/boost/xpressive/xpressive_dynamic.hpp) 2 | message(FATAL_ERROR "missing Boost.Xpressive library") 3 | ENDIF() 4 | 5 | IF(NOT EXISTS /usr/include/boost/program_options.hpp) 6 | message(FATAL_ERROR "missing Boost.ProgramOptions library") 7 | ENDIF() 8 | 9 | find_package(Boost 1.42.0) 10 | IF(Boost_FOUND) 11 | IF(Boost_VERSION LESS 104200) 12 | message(FATAL_ERROR "need Boost of version 1.42.0") 13 | ENDIF(Boost_VERSION LESS 104200) 14 | ELSE() 15 | message(FATAL_ERROR "missing Boost") 16 | ENDIF() 17 | 18 | IF(NOT EXISTS /usr/include/readline/readline.h) 19 | message(FATAL_ERROR "missing GNU Readline library") 20 | ENDIF() 21 | 22 | OPTION(LOCAL "is build local" ON) 23 | 24 | include(flags.cmake) 25 | include(version.cmake) 26 | 27 | include_directories(.) 28 | 29 | add_subdirectory(console) 30 | add_subdirectory(lib) 31 | add_subdirectory(downloadmethods) 32 | 33 | -------------------------------------------------------------------------------- /cpp/common/common.hpp: -------------------------------------------------------------------------------- 1 | #define FORIT(variableName, storage) for (auto variableName = (storage).begin(), variableName##End = (storage).end(); variableName != variableName##End; ++variableName) 2 | 3 | #include 4 | 5 | -------------------------------------------------------------------------------- /cpp/console/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../lib/include) 2 | add_executable(cupt.bin 3 | common.cpp 4 | common.hpp 5 | cupt.cpp 6 | cupt.hpp 7 | misc.cpp 8 | misc.hpp 9 | handlers.hpp 10 | handlers/search.cpp 11 | handlers/misc.cpp 12 | handlers/managepackages.cpp 13 | handlers/shell.cpp 14 | handlers/download.cpp 15 | handlers/snapshot.cpp 16 | handlers/why.cpp 17 | colorizer.cpp 18 | colorizer.hpp 19 | functionselectors.cpp 20 | functionselectors.hpp 21 | selectors.cpp 22 | selectors.hpp 23 | ) 24 | set_property(TARGET cupt.bin PROPERTY OUTPUT_NAME cupt) 25 | target_link_libraries(cupt.bin libcupt boost_program_options) 26 | 27 | install(TARGETS cupt.bin DESTINATION bin) 28 | -------------------------------------------------------------------------------- /cpp/console/colorizer.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2011 by Eugene V. Lyubimkin * 3 | * * 4 | * This program is free software; you can redistribute it and/or modify * 5 | * it under the terms of the GNU General Public License * 6 | * (version 3 or above) as published by the Free Software Foundation. * 7 | * * 8 | * This program is distributed in the hope that it will be useful, * 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 11 | * GNU General Public License for more details. * 12 | * * 13 | * You should have received a copy of the GNU GPL * 14 | * along with this program; if not, write to the * 15 | * Free Software Foundation, Inc., * 16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * 17 | **************************************************************************/ 18 | #ifndef COLORIZER_SEEN 19 | #define COLORIZER_SEEN 20 | 21 | #include "common.hpp" 22 | 23 | class Colorizer 24 | { 25 | bool __enabled; 26 | public: 27 | enum Color { Default = ' ', Red = '1', Green = '2', Blue = '4', Yellow = '3', Cyan = '6', Magenta = '5' }; 28 | Colorizer(const Config& config); 29 | string makeBold(const string& input) const; 30 | string colorize(const string& input, Color, bool bold) const; 31 | bool enabled() const; 32 | }; 33 | 34 | #endif 35 | 36 | -------------------------------------------------------------------------------- /cpp/console/cupt.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2010 by Eugene V. Lyubimkin * 3 | * * 4 | * This program is free software; you can redistribute it and/or modify * 5 | * it under the terms of the GNU General Public License * 6 | * (version 3 or above) as published by the Free Software Foundation. * 7 | * * 8 | * This program is distributed in the hope that it will be useful, * 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 11 | * GNU General Public License for more details. * 12 | * * 13 | * You should have received a copy of the GNU GPL * 14 | * along with this program; if not, write to the * 15 | * Free Software Foundation, Inc., * 16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * 17 | **************************************************************************/ 18 | 19 | #include "common.hpp" 20 | #include "misc.hpp" 21 | 22 | int mainEx(int argc, char* argv[], Context& context); 23 | 24 | -------------------------------------------------------------------------------- /cpp/downloadmethods/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../lib/include) 2 | 3 | add_library(downloadmethod-wget MODULE wget.cpp) 4 | set_property(TARGET downloadmethod-wget PROPERTY OUTPUT_NAME wget) 5 | target_link_libraries(downloadmethod-wget libcupt) 6 | 7 | add_library(downloadmethod-curl MODULE curl.cpp) 8 | set_property(TARGET downloadmethod-curl PROPERTY OUTPUT_NAME curl) 9 | target_link_libraries(downloadmethod-curl libcupt curl-gnutls) 10 | 11 | add_library(downloadmethod-debdelta MODULE debdelta.cpp) 12 | set_property(TARGET downloadmethod-debdelta PROPERTY OUTPUT_NAME debdelta) 13 | target_link_libraries(downloadmethod-debdelta libcupt) 14 | 15 | add_library(downloadmethod-file MODULE file.cpp) 16 | set_property(TARGET downloadmethod-file PROPERTY OUTPUT_NAME file) 17 | target_link_libraries(downloadmethod-file libcupt) 18 | 19 | install(TARGETS downloadmethod-file downloadmethod-wget downloadmethod-curl downloadmethod-debdelta 20 | DESTINATION ${CUPT_RELATIVE_DOWNLOADMETHODS_DIR}) 21 | -------------------------------------------------------------------------------- /cpp/flags.cmake: -------------------------------------------------------------------------------- 1 | try_run(LAMBDA_CAPTURE_OPT_RESULT LAMBDA_CAPTURE_COMPILES 2 | ${CMAKE_CURRENT_BINARY_DIR} 3 | ${CMAKE_CURRENT_SOURCE_DIR}/platform-tests/std-function-lambda-capture.cpp 4 | COMPILE_DEFINITIONS "-O2" 5 | COMPILE_OUTPUT_VARIABLE LAMBDA_COMPILE_OUTPUT) 6 | if (NOT LAMBDA_CAPTURE_COMPILES) 7 | message(FATAL_ERROR "Lambda platform test didn't compile: ${LAMBDA_COMPILE_OUTPUT}") 8 | endif() 9 | if (${LAMBDA_CAPTURE_OPT_RESULT} EQUAL 77) 10 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O1") 11 | message("Detected lambda capture misoptimisation, switching to -O1. See #838438.") 12 | endif() 13 | 14 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -Wall -Wextra -std=gnu++14 -fPIC -include common/common.hpp") 15 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} $ENV{CXXFLAGS}") 16 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-security") # yes, we should move to type-safe alternative eventually 17 | 18 | set(OUR_LINKER_FLAGS "-pthread -Wl,--as-needed") 19 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OUR_LINKER_FLAGS}") 20 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${OUR_LINKER_FLAGS}") 21 | 22 | -------------------------------------------------------------------------------- /cpp/lib/src/internal/lock.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2010 by Eugene V. Lyubimkin * 3 | * * 4 | * This program is free software; you can redistribute it and/or modify * 5 | * it under the terms of the GNU General Public License * 6 | * (version 3 or above) as published by the Free Software Foundation. * 7 | * * 8 | * This program is distributed in the hope that it will be useful, * 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 11 | * GNU General Public License for more details. * 12 | * * 13 | * You should have received a copy of the GNU GPL * 14 | * along with this program; if not, write to the * 15 | * Free Software Foundation, Inc., * 16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * 17 | **************************************************************************/ 18 | #ifndef CUPT_INTERNAL_LOCK_SEEN 19 | #define CUPT_INTERNAL_LOCK_SEEN 20 | 21 | #include 22 | #include 23 | 24 | namespace cupt { 25 | namespace internal { 26 | 27 | class Lock 28 | { 29 | string __path; 30 | File* __file_ptr; 31 | bool __simulating; 32 | bool __debugging; 33 | 34 | public: 35 | Lock(const Config& config, const string& path); 36 | ~Lock(); 37 | }; 38 | 39 | } 40 | } 41 | 42 | #endif 43 | 44 | -------------------------------------------------------------------------------- /cpp/lib/src/internal/parse.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2012 by Eugene V. Lyubimkin * 3 | * * 4 | * This program is free software; you can redistribute it and/or modify * 5 | * it under the terms of the GNU General Public License * 6 | * (version 3 or above) as published by the Free Software Foundation. * 7 | * * 8 | * This program is distributed in the hope that it will be useful, * 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 11 | * GNU General Public License for more details. * 12 | * * 13 | * You should have received a copy of the GNU GPL * 14 | * along with this program; if not, write to the * 15 | * Free Software Foundation, Inc., * 16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * 17 | **************************************************************************/ 18 | #ifndef CUPT_INTERNAL_PARSE_SEEN 19 | #define CUPT_INTERNAL_PARSE_SEEN 20 | 21 | namespace cupt { 22 | namespace internal { 23 | namespace parse { 24 | 25 | template < typename IterT, typename CallbackT > 26 | void processSpaceCharSpaceDelimitedStrings(IterT begin, IterT end, 27 | char delimiter, const CallbackT&); 28 | 29 | } 30 | } 31 | } 32 | 33 | #include 34 | 35 | #endif 36 | 37 | -------------------------------------------------------------------------------- /cpp/lib/src/internal/pipe.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2010-2012 by Eugene V. Lyubimkin * 3 | * * 4 | * This program is free software; you can redistribute it and/or modify * 5 | * it under the terms of the GNU General Public License * 6 | * (version 3 or above) as published by the Free Software Foundation. * 7 | * * 8 | * This program is distributed in the hope that it will be useful, * 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 11 | * GNU General Public License for more details. * 12 | * * 13 | * You should have received a copy of the GNU GPL * 14 | * along with this program; if not, write to the * 15 | * Free Software Foundation, Inc., * 16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * 17 | **************************************************************************/ 18 | #ifndef CUPT_PIPE_SEEN 19 | #define CUPT_PIPE_SEEN 20 | 21 | #include 22 | 23 | namespace cupt { 24 | namespace internal { 25 | 26 | struct PipeData; 27 | 28 | class CUPT_API Pipe 29 | { 30 | internal::PipeData* __data; 31 | Pipe(const Pipe&); 32 | public: 33 | Pipe(const string& name, bool leaveReaderOpenOnExec = false); 34 | virtual ~Pipe(); 35 | void useAsReader(); 36 | void useAsWriter(); 37 | int getReaderFd(); 38 | int getWriterFd(); 39 | }; 40 | 41 | } 42 | } 43 | 44 | #endif 45 | 46 | -------------------------------------------------------------------------------- /cpp/lib/src/versionstring.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2012 by Eugene V. Lyubimkin * 3 | * * 4 | * This program is free software; you can redistribute it and/or modify * 5 | * it under the terms of the GNU General Public License * 6 | * (version 3 or above) as published by the Free Software Foundation. * 7 | * * 8 | * This program is distributed in the hope that it will be useful, * 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 11 | * GNU General Public License for more details. * 12 | * * 13 | * You should have received a copy of the GNU GPL * 14 | * along with this program; if not, write to the * 15 | * Free Software Foundation, Inc., * 16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * 17 | **************************************************************************/ 18 | #include 19 | 20 | #include 21 | 22 | namespace cupt { 23 | 24 | StringRange getOriginalVersionString(const StringRange& s) 25 | { 26 | return { s.first, s.find(internal::idSuffixDelimiter) }; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /cpp/platform-tests/std-function-lambda-capture.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void* g = nullptr; 5 | 6 | struct C 7 | { 8 | void doCb() 9 | { 10 | size_t dummy_a = 1; 11 | 12 | std::cout << "Outside: " << this << std::endl; 13 | std::function f; 14 | f = [this, &dummy_a]() {}; 15 | f = [this]() { g = this; std::cout << "Inside: " << this << std::endl; }; 16 | f(); 17 | } 18 | }; 19 | 20 | int main() 21 | { 22 | C c; 23 | c.doCb(); 24 | return g == &c ? 0 : 77; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /cpp/version.cmake: -------------------------------------------------------------------------------- 1 | set(CUPT_API_VERSION 4) 2 | set(CUPT_SOVERSION 2) 3 | 4 | set(CUPT_RELATIVE_DOWNLOADMETHODS_DIR "lib/cupt${CUPT_API_VERSION}-${CUPT_SOVERSION}/downloadmethods") 5 | if (LOCAL) 6 | set(DOWNLOADMETHODS_DIR "${CMAKE_CURRENT_BINARY_DIR}/downloadmethods") 7 | else() 8 | set(DOWNLOADMETHODS_DIR "/usr/${CUPT_RELATIVE_DOWNLOADMETHODS_DIR}") 9 | endif() 10 | 11 | # detect version from debian/changelog 12 | execute_process( 13 | COMMAND "dpkg-parsechangelog" "-l${PROJECT_SOURCE_DIR}/debian/changelog" 14 | COMMAND "grep" "^Version" 15 | COMMAND "cut" "-d " "-f" "2" 16 | OUTPUT_VARIABLE CUPT_VERSION 17 | OUTPUT_STRIP_TRAILING_WHITESPACE 18 | ) 19 | 20 | IF(CUPT_VERSION) 21 | message(STATUS "Detected Cupt version: ${CUPT_VERSION}") 22 | ELSE() 23 | message(FATAL_ERROR "Unable to detect Cupt version.") 24 | ENDIF() 25 | 26 | configure_file(version.hpp.in version.hpp) 27 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 28 | 29 | -------------------------------------------------------------------------------- /cpp/version.hpp.in: -------------------------------------------------------------------------------- 1 | #cmakedefine CUPT_VERSION "${CUPT_VERSION}" 2 | #cmakedefine DOWNLOADMETHODS_DIR "${DOWNLOADMETHODS_DIR}" 3 | -------------------------------------------------------------------------------- /debian/.gitignore: -------------------------------------------------------------------------------- 1 | /files 2 | *.substvars 3 | *.debhelper.log 4 | *.debhelper 5 | /tmp/ 6 | /cupt/ 7 | /libcupt*/ 8 | -------------------------------------------------------------------------------- /debian/NEWS: -------------------------------------------------------------------------------- 1 | cupt (2.1.0) unstable; urgency=low 2 | 3 | Cupt now uses its own directory for index lists. APT-based package managers 4 | do not change Cupt repository cache anymore, and vice versa. 5 | 6 | -- Eugene V. Lyubimkin Fri, 06 May 2011 11:29:59 +0300 7 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format-Specification: 2 | http://wiki.debian.org/Proposals/CopyrightFormat?action=recall&rev=196 3 | Upstream-Maintainer: Eugene V. Lyubimkin 4 | Upstream-Name: Cupt 5 | 6 | Files: * 7 | Copyright: 2008-2013, Eugene V. Lyubimkin 8 | License: GPL-3+ 9 | 10 | Files: po/pl.po 11 | Copyright: 2012, Karol Kozłowski 12 | 13 | Files: po/da.po 14 | Copyright: 2012, Joe Hansen 15 | 16 | Files: po/sk.po 17 | Copyright: 2012, Ivan Masár 18 | 19 | Files: po/fr.po 20 | Copyright: 2012, Julien Patriarca 21 | 22 | Files: po/de.po 23 | Copyright: 2012, Chris Leick 24 | 25 | License: GPL-3+ 26 | This program is free software; you can redistribute it and/or modify 27 | it under the terms of the GNU General Public License as published by 28 | the Free Software Foundation; either version 3, or (at your option) 29 | any later version. 30 | On Debian GNU/Linux systems, the complete text of the GNU General 31 | Public License version 3 can be found in `/usr/share/common-licenses/GPL-3' 32 | -------------------------------------------------------------------------------- /debian/cupt.bash-completion: -------------------------------------------------------------------------------- 1 | scripts/bash_completion cupt 2 | -------------------------------------------------------------------------------- /debian/cupt.docs: -------------------------------------------------------------------------------- 1 | doc/NEWS 2 | -------------------------------------------------------------------------------- /debian/cupt.install: -------------------------------------------------------------------------------- 1 | usr/bin 2 | usr/share/doc/*.html usr/share/doc/cupt 3 | -------------------------------------------------------------------------------- /debian/cupt.lintian-overrides: -------------------------------------------------------------------------------- 1 | # Lintian calls man with MANWIDTH=80. There is now a Cupt configuration 2 | # option which is longer than 80 characters. It shouldn't be broken 3 | # into several lines. 4 | cupt binary: manpage-has-errors-from-man *cupt.conf*: can't break line 5 | -------------------------------------------------------------------------------- /debian/cupt.manpages: -------------------------------------------------------------------------------- 1 | debian/tmp/usr/share/doc/cupt_tutorial.7 2 | debian/tmp/usr/share/doc/cupt_functionalselectors.7 3 | debian/tmp/usr/share/doc/cupt.1 4 | debian/tmp/usr/share/doc/cupt.conf.5 5 | debian/tmp/usr/share/doc/cupt_vs_apt.5 6 | -------------------------------------------------------------------------------- /debian/libcupt-common.dirs: -------------------------------------------------------------------------------- 1 | etc/logrotate.d 2 | etc/cupt 3 | etc/cupt/cupt.conf.d 4 | -------------------------------------------------------------------------------- /debian/libcupt-common.install: -------------------------------------------------------------------------------- 1 | usr/share/locale/ 2 | -------------------------------------------------------------------------------- /debian/libcupt4-2-downloadmethod-curl.install: -------------------------------------------------------------------------------- 1 | usr/lib/cupt4-2/downloadmethods/libcurl.* 2 | -------------------------------------------------------------------------------- /debian/libcupt4-2-downloadmethod-wget.install: -------------------------------------------------------------------------------- 1 | usr/lib/cupt4-2/downloadmethods/libwget.* 2 | -------------------------------------------------------------------------------- /debian/libcupt4-2.install: -------------------------------------------------------------------------------- 1 | usr/lib/libcupt4.so.2 2 | usr/lib/cupt4-2/downloadmethods/libdebdelta* 3 | usr/lib/cupt4-2/downloadmethods/libfile* 4 | -------------------------------------------------------------------------------- /debian/libcupt4-2.lintian-overrides: -------------------------------------------------------------------------------- 1 | # Cupt reads package indices (including the dpkg status file) into memory every 2 | # time it starts. It's a first-above-dpkg level API. For speed and consistency, 3 | # scanning the files are done via read-only custom-buffered random access to 4 | # index files. 5 | binary: uses-dpkg-database-directly usr/lib/libcupt*so* 6 | -------------------------------------------------------------------------------- /debian/libcupt4-2.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ ! -e /var/lib/cupt ]; then 6 | mkdir /var/lib/cupt 7 | fi 8 | 9 | #DEBHELPER# 10 | 11 | -------------------------------------------------------------------------------- /debian/libcupt4-2.postrm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | case "$1" in 6 | purge) 7 | rm -rf /var/lib/cupt 8 | rm -f /var/log/cupt.log* 9 | ;; 10 | esac 11 | 12 | #DEBHELPER# 13 | -------------------------------------------------------------------------------- /debian/libcupt4-2.shlibs: -------------------------------------------------------------------------------- 1 | libcupt4 2 libcupt4-2 (>= 2.10.0~) 2 | -------------------------------------------------------------------------------- /debian/libcupt4-dev.install: -------------------------------------------------------------------------------- 1 | usr/lib/libcupt4.so 2 | usr/include 3 | -------------------------------------------------------------------------------- /debian/libcupt4-doc.install: -------------------------------------------------------------------------------- 1 | usr/share/doc/lib/* usr/share/doc/libcupt4-doc 2 | # usr/share | do not install man pages (at least for now) 3 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | ifeq (,$(filter noopt,$(DEB_BUILD_OPTIONS))) 4 | CMAKE_OPTIONS += -DCMAKE_BUILD_TYPE=RelWithDebInfo 5 | else 6 | CMAKE_OPTIONS += -DCMAKE_BUILD_TYPE=Debug 7 | endif 8 | 9 | BUILD_DIRECTORY=b/ 10 | 11 | override_dh_auto_configure: 12 | dh_auto_configure -B${BUILD_DIRECTORY} -- $(CMAKE_OPTIONS) 13 | 14 | override_dh_auto_install-arch: 15 | dh_auto_configure -B${BUILD_DIRECTORY} -- $(CMAKE_OPTIONS) -DLOCAL:bool=off 16 | dh_auto_install -B${BUILD_DIRECTORY} 17 | chrpath --delete $(CURDIR)/debian/tmp/usr/bin/cupt 18 | 19 | override_dh_auto_install-indep: 20 | dh_auto_install -B${BUILD_DIRECTORY} -i 21 | install -m644 scripts/logrotate $(CURDIR)/debian/libcupt-common/etc/logrotate.d/cupt 22 | 23 | override_dh_strip: 24 | dh_strip --dbg-package=cupt-dbg 25 | 26 | %: 27 | dh $@ -B${BUILD_DIRECTORY} --parallel --with bash-completion 28 | -------------------------------------------------------------------------------- /debian/shlibs.local: -------------------------------------------------------------------------------- 1 | libcupt4-0.shlibs -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | /doc-stamp 2 | -------------------------------------------------------------------------------- /doc/cupt_vs_apt.5.pod: -------------------------------------------------------------------------------- 1 | =pod 2 | 3 | =encoding utf8 4 | 5 | =head1 NAME 6 | 7 | cupt_vs_apt - overview of things Cupt and APT differ in 8 | 9 | =head1 FEATURE SETS 10 | 11 | See L. 12 | 13 | =head1 DIFFERENT BEHAVIOR 14 | 15 | =head2 Configuration 16 | 17 | =over 18 | 19 | =item 20 | 21 | 'apt::cache::allversions' defaults to 0, not 1 as in apt-cache 22 | 23 | =item 24 | 25 | 'gpgv::trustedkeyring' defaults to '/var/lib/cupt/trusted.gpg' 26 | 27 | =item 28 | 29 | to prioritize compression types, use option 'cupt::update::compression-types', 30 | not 'acquire::compression-types' 31 | 32 | =item 33 | 34 | to select repository indexes localizations, use option 35 | 'cupt::languages::indexes', not 'acquire::languages' 36 | 37 | =back 38 | 39 | =head2 Pinning 40 | 41 | =over 42 | 43 | =item 44 | 45 | the difference between 'specific' and 'general' pin types is not supported 46 | 47 | =item 48 | 49 | an additional pin priority penalty is applied by default to NotAutomatic 50 | releases (see 'cupt::cache::pin::addendums::not-automatic' option) 51 | 52 | =back 53 | -------------------------------------------------------------------------------- /doc/examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++0x") 2 | include_directories(../../cpp/lib/include) 3 | 4 | add_subdirectory(show-upgradeable) 5 | add_subdirectory(show-available) 6 | 7 | -------------------------------------------------------------------------------- /doc/examples/show-available/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(show-available show-available.cpp) 2 | target_link_libraries(show-available libcupt) 3 | -------------------------------------------------------------------------------- /doc/examples/show-upgradeable/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(show-upgradeable show-upgradeable.cpp) 2 | target_link_libraries(show-upgradeable libcupt) 3 | 4 | -------------------------------------------------------------------------------- /doc/reference/.gitignore: -------------------------------------------------------------------------------- 1 | /html/ 2 | /man/ 3 | -------------------------------------------------------------------------------- /po/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FindGettext) 2 | 3 | # existing targets don't suit: 4 | # GETTEXT_CREATE_TRANSLATIONS modifies .po-files during the build 5 | # GETTEXT_PROCESS_POT_FILE deletes .po-files in the 'clean' target 6 | # GETTEXT_PROCESS_PO_FILES is limited to a single language and cannot be issued 7 | # several times due to 'duplicate targets error' 8 | 9 | MACRO(CUPT_PROCESS_PO) 10 | FOREACH(_lang ${ARGN}) 11 | GET_FILENAME_COMPONENT(_absPoFile ${_lang}.po ABSOLUTE) 12 | SET(_moFile ${CMAKE_CURRENT_BINARY_DIR}/${_lang}.mo) 13 | 14 | ADD_CUSTOM_COMMAND( 15 | OUTPUT ${_moFile} 16 | COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_moFile} ${_absPoFile} 17 | DEPENDS ${_absPoFile} 18 | ) 19 | 20 | INSTALL(FILES ${_moFile} DESTINATION share/locale/${_lang}/LC_MESSAGES RENAME cupt.mo) 21 | SET(_moFiles ${_moFiles} ${_moFile}) 22 | ENDFOREACH(_lang) 23 | 24 | ADD_CUSTOM_TARGET(translations ALL DEPENDS ${_moFiles}) 25 | ENDMACRO(CUPT_PROCESS_PO) 26 | 27 | 28 | CUPT_PROCESS_PO(pl da sk fr de) 29 | -------------------------------------------------------------------------------- /po/getpot: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd ../cpp && \ 3 | xgettext -k__ -k__x -k__n:1,2 -k__nx:1,2 -k__xn:1,2 -kN__ \ 4 | --keyword=loggedFatal2:4 --from-code=UTF-8 \ 5 | --language='C++' --copyright-holder='Eugene V. Lyubimkin' \ 6 | --package-name='cupt' --msgid-bugs-address="cupt-devel@lists.alioth.debian.org" \ 7 | --output='../po/messages.pot' \ 8 | `find console downloadmethods lib -type f -name "*.cpp" -or -name "*.tpp" -or -name ".hpp"` 9 | 10 | -------------------------------------------------------------------------------- /scripts/logrotate: -------------------------------------------------------------------------------- 1 | /var/log/cupt.log { 2 | monthly 3 | rotate 24 4 | maxage 731 5 | compress 6 | delaycompress 7 | missingok 8 | notifempty 9 | nocreate 10 | } 11 | -------------------------------------------------------------------------------- /scripts/run_coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | OUTPUT=lcov.output.info 4 | 5 | set -e 6 | set -x 7 | 8 | ABSOLUTE_SOURCE_ROOT_PATH=`git rev-parse --show-toplevel`/cpp 9 | 10 | CAPTURE_OPTIONS="--directory . --base-directory "$ABSOLUTE_SOURCE_ROOT_PATH" --no-external" 11 | 12 | if [ -z "$ONLY_CAPTURE" ]; then 13 | # recompile 14 | CXXFLAGS=-coverage cmake ../.. 15 | make 16 | 17 | # clean leftovers from previous tests 18 | lcov --directory . --zerocounters 19 | 20 | # gather all source files 21 | lcov $CAPTURE_OPTIONS --capture --initial --output-file ${OUTPUT}.initial.base 22 | lcov --remove ${OUTPUT}.initial.base '*CMake*Compiler*' --output-file ${OUTPUT}.initial 23 | 24 | # test 25 | make test 26 | fi 27 | 28 | # capture 29 | lcov $CAPTURE_OPTIONS --capture --output-file ${OUTPUT}.tests 30 | 31 | # combine 32 | lcov -a ${OUTPUT}.initial -a ${OUTPUT}.tests -o $OUTPUT 33 | 34 | # visualise 35 | genhtml --output-directory html --prefix $ABSOLUTE_SOURCE_ROOT_PATH $OUTPUT --no-function-coverage 36 | 37 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SDIR ${CMAKE_CURRENT_SOURCE_DIR}) 2 | add_custom_target( 3 | test 4 | COMMAND ${SDIR}/run.sh ${SDIR} $ 5 | ) 6 | 7 | -------------------------------------------------------------------------------- /test/CuptInteractive.pm: -------------------------------------------------------------------------------- 1 | package CuptInteractive; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Expect::Simple; 7 | 8 | sub new { 9 | my ($class, $command, $prompt) = @_; 10 | my $o = {}; 11 | $o->{_impl} = Expect::Simple->new({ 12 | 'Cmd' => $command, 13 | 'Prompt' => $prompt//'cupt> ', 14 | 'DisconnectCmd' => 'q', 15 | 'Timeout' => '20', 16 | }); 17 | $o->{_error} = ''; 18 | my $self = bless($o, $class); 19 | 20 | $self->{_initial_output} = $self->_last_output(); 21 | 22 | return $self; 23 | } 24 | 25 | sub initial_output { 26 | my $self = shift; 27 | return $self->{_initial_output}; 28 | } 29 | 30 | sub execute { 31 | my ($self, $text) = @_; 32 | 33 | if ($self->{_error}) { 34 | return 'Previously: ' . $self->{_error}; 35 | } 36 | 37 | eval { 38 | $self->{_impl}->send($text); 39 | }; 40 | if ($@) { 41 | $self->{_error} = $self->{_impl}->before() . "\n\n" . $@; 42 | return $self->{_error}; 43 | } 44 | 45 | my $result = $self->_last_output(); 46 | $result =~ s/^\Q$text\E\n//; 47 | return $result; 48 | } 49 | 50 | sub _last_output { 51 | my $self = shift; 52 | 53 | my $result = $self->{_impl}->before(); 54 | $result =~ s/\r//g; 55 | return $result; 56 | } 57 | 58 | 1; 59 | 60 | -------------------------------------------------------------------------------- /test/fakes/dpkg-repack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | print "[fakes/dpkg-repack] @ARGV\n"; 7 | 8 | my $package = $ARGV[1]; 9 | system("touch ${package}_.deb"); 10 | 11 | -------------------------------------------------------------------------------- /test/fakes/dpkg-scanpackages: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use File::stat; 4 | 5 | use strict; 6 | use warnings; 7 | 8 | print STDERR "[fakes/dpkg-scanpackages] @ARGV\n"; 9 | 10 | my $dir = $ARGV[0]; 11 | 12 | foreach my $deb_path (glob("$dir/*.deb")) { 13 | my $st = stat($deb_path); 14 | 15 | my ($name, $version, $architecture) = ($deb_path =~ m@(.*)_(.*)_(.*?)\.deb$@); 16 | $name =~ s@.*/@@; 17 | my $size = $st->size; 18 | my ($sha1) = (`sha1sum $deb_path` =~ m/(.*?) /); 19 | 20 | print "Package: $name\n"; 21 | print "Version: $version\n"; 22 | print "Architecture: $architecture\n"; 23 | print "Size: $size\n"; 24 | print "SHA1: $sha1\n"; 25 | print "\n"; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /test/fakes/dpkg-source: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | print "[fakes/dpkg-source] @ARGV\n"; 7 | 8 | -------------------------------------------------------------------------------- /test/gpg/expired.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackyf/cupt/a6f61e2f11e0d1b14076b1e3458fc1ec26de20b6/test/gpg/expired.gpg -------------------------------------------------------------------------------- /test/gpg/mock1k-revoke.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackyf/cupt/a6f61e2f11e0d1b14076b1e3458fc1ec26de20b6/test/gpg/mock1k-revoke.gpg -------------------------------------------------------------------------------- /test/gpg/mock1k.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackyf/cupt/a6f61e2f11e0d1b14076b1e3458fc1ec26de20b6/test/gpg/mock1k.gpg -------------------------------------------------------------------------------- /test/gpg/mock4k.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackyf/cupt/a6f61e2f11e0d1b14076b1e3458fc1ec26de20b6/test/gpg/mock4k.gpg -------------------------------------------------------------------------------- /test/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | TESTS_PATH=$1 6 | RUNNER="perl -Mwarnings -Mstrict -I${TESTS_PATH} -MTestCupt" 7 | BINARY_UNDER_TEST_PATH=${PROVE_BINARY:-$2} 8 | 9 | ln -sTf $TESTS_PATH/t tt 10 | prove --exec "$RUNNER" -r --timer $PROVE_PARAMS tt/$PROVE_FILTER :: "$BINARY_UNDER_TEST_PATH" 11 | 12 | -------------------------------------------------------------------------------- /test/t/help.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 4; 2 | use IPC::Run3; 3 | 4 | my $cupt = setup(); 5 | 6 | foreach my $command ('help', '-h', '--help', '') { 7 | subtest "invoking via $command" => sub { 8 | run3("$cupt $command", \undef, \my $stdout, \my $stderr); 9 | is($?, 0, 'exit code'); 10 | like($stdout, qr/^Usage/, "prints usage"); 11 | is($stderr, '', "no errors/warnings"); 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /test/t/manage/preview/action-color.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 14; 2 | 3 | require(get_rinclude_path('actions')); 4 | require(get_rinclude_path('color-check')); 5 | 6 | sub test { 7 | my ($action, $action_phrase, $phrase_attrs, $package_attrs) = @_; 8 | 9 | my ($cupt, $combined_command) = setup_for_actions($action, ''); 10 | my $output = get_first_offer("$cupt $combined_command -o cupt::console::use-colors=yes"); 11 | 12 | subtest "color attributes for $action" => sub { 13 | check_color_part('name', $output, $action_phrase, $phrase_attrs); 14 | check_color_part('packages', $output, qr/\w+/, $package_attrs); 15 | }; 16 | } 17 | 18 | # for bold / not bold for packages we assume that the package is manually installed 19 | # unless the action implies otherwise 20 | 21 | test('installed', 'will be installed' => undef, 'bold cyan'); 22 | test('removed', 'will be removed' => 'bold', 'bold yellow'); 23 | test('upgraded', 'will be upgraded' => undef, 'bold green'); 24 | test('purged', 'will be purged' => 'bold', 'bold red'); 25 | test('downgraded', 'will be downgraded' => 'bold', 'bold magenta'); 26 | test('configured', 'will be configured' => undef, 'bold blue'); 27 | test('deconfigured', 'will be deconfigured' => 'bold', 'bold'); 28 | test('triggers', 'will have triggers processed' => undef, 'bold'); 29 | test('reinstalled', 'will be reinstalled', 'bold' => 'bold'); 30 | test('not preferred', 'will have a not preferred version' => 'bold', 'bold'); 31 | test('auto-removed', 'will be auto-removed' => undef, 'yellow'); 32 | test('auto-purged', 'will be auto-purged' => undef, 'red'); 33 | test('marked as auto', 'will be marked as automatically installed' => undef, undef ); 34 | test('marked as manual', 'will be marked as manually installed' => undef, undef); 35 | 36 | -------------------------------------------------------------------------------- /test/t/manage/preview/action-order.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 13; 2 | 3 | require(get_rinclude_path('actions')); 4 | 5 | sub test { 6 | my ($first, $second) = @_; 7 | 8 | my ($cupt, $combined_command) = setup_for_actions($first, $second); 9 | 10 | my $expected_regex = qr/ $first.*\n{2,}.* $second/s; 11 | my $output = get_first_offer("$cupt $combined_command"); 12 | like($output, $expected_regex, "relative order: $first -> $second ($combined_command)"); 13 | } 14 | 15 | test('marked as auto', 'marked as manual'); 16 | test('marked as manual', 'reinstalled'); 17 | test('reinstalled', 'installed'); 18 | test('installed', 'upgraded'); 19 | test('upgraded', 'removed'); 20 | test('removed', 'purged'); 21 | test('purged', 'downgraded'); 22 | test('downgraded', 'configured'); 23 | test('configured', 'triggers'); 24 | test('triggers', 'deconfigured'); 25 | test('deconfigured', 'not preferred'); 26 | test('not preferred', 'auto-removed'); 27 | test('not preferred', 'auto-purged'); 28 | 29 | -------------------------------------------------------------------------------- /test/t/manage/preview/choosing-specific-version.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3; 2 | 3 | my $cupt = setup( 4 | 'packages' => [ 5 | compose_package_record('pp', 5) . "Depends: not-existing\n" , 6 | compose_package_record('pp', 5, 'sha' => 'abc1') , 7 | compose_package_record('pp', 5, 'sha' => 'abc2') . "Recommends: not-existing\n" , 8 | compose_package_record('pp', '4.22-1~bpo8+1') , 9 | ], 10 | ); 11 | 12 | sub test { 13 | my ($comment, $request, $expected_version) = @_; 14 | 15 | my $output = get_first_offer("$cupt install --sf $request"); 16 | my $actual_version = get_offered_version($output, 'pp'); 17 | 18 | is($actual_version, $expected_version, $comment) 19 | or (diag($output), diag(stdall("$cupt policy pp"))); 20 | } 21 | 22 | test('best available dhs version', 'pp', '5^dhs0'); 23 | test('specifictly chosen dhs version', 'pp=5^dhs1', '5^dhs1'); 24 | 25 | test('regex characters are escaped', 'pp=4.22-1~bpo8+1', '4.22-1~bpo8+1'); 26 | 27 | -------------------------------------------------------------------------------- /test/t/manage/preview/color-check.pl: -------------------------------------------------------------------------------- 1 | use Term::ANSIColor; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | sub check_color_part { 7 | my ($desc, $output, $str, $attrs) = @_; 8 | 9 | my $color_prefix = defined($attrs) ? color($attrs) : ''; 10 | my $color_suffix = defined($attrs) ? color('reset') : ''; 11 | my $expected_regex = qr/\Q$color_prefix\E$str\Q$color_suffix\E/; 12 | my $printed_attrs = $attrs//''; 13 | 14 | like($output, $expected_regex, "$desc: '$printed_attrs'"); 15 | } 16 | 17 | 1; 18 | 19 | -------------------------------------------------------------------------------- /test/t/manage/preview/color-detection.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2+3+4+3+4; 2 | use CuptInteractive; 3 | 4 | require(get_rinclude_path('actions')); 5 | require(get_rinclude_path('color-check')); 6 | 7 | sub test { 8 | my ($option_value, $is_tty, $term, $got_colors) = @_; 9 | my $desc = "option value: $option_value, tty: $is_tty, term: $term -> $got_colors"; 10 | 11 | my ($cupt, $combined_command) = setup_for_actions('installed', ''); 12 | $cupt = "TERM=$term $cupt"; 13 | $combined_command .= " -o cupt::console::use-colors=$option_value -y -s"; 14 | 15 | my $output = $is_tty 16 | ? CuptInteractive->new("$cupt shell")->execute($combined_command) 17 | : stdall("$cupt $combined_command"); 18 | 19 | my $color_string = color('bold cyan'); 20 | my $checker = ($got_colors ? \&like : \&unlike); 21 | $checker->($output, qr/\Q$color_string\E/, $desc); 22 | } 23 | 24 | test('yes', 0, 'mainframe' => 1); 25 | test('yes', 1, 'bsd' => 1); 26 | 27 | test('no', 0, 'linux' => 0); 28 | test('no', 1, 'linux' => 0); 29 | test('no', 1, 'haiku' => 0); 30 | 31 | test('auto', 0, 'xterm' => 0); 32 | test('auto', 0, 'emacs' => 0); 33 | test('auto', 1, 'xterm' => 1); 34 | test('auto', 1, 'emacs' => 0); 35 | 36 | test('auto', 1, 'xt' => 0); 37 | test('auto', 1, 'linuzz' => 0); 38 | test('auto', 1, 'linux' => 1); 39 | 40 | test('auto', 1, 'xtermal' => 0); 41 | test('auto', 1, 'xterm-color' => 1); 42 | test('auto', 1, 'xterm-256color' => 1); 43 | test('auto', 1, 'xterm-qwerty' => 1); 44 | 45 | -------------------------------------------------------------------------------- /test/t/manage/preview/reason-chain/alternatives.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 1; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ 7 | compose_installed_record('subbroken1', '5') , 8 | compose_installed_record('subbroken2', '6') , 9 | compose_installed_record('broken', '4') . "Depends: subbroken1 | subbroken2\n" , 10 | ], 11 | 'packages' => [ 12 | compose_package_record('subalt', '7') . "Breaks: subbroken1, subbroken2\n" , 13 | ], 14 | ); 15 | 16 | my $answer = <<'END'; 17 | broken: broken 4^installed depends on 'subbroken3 | subbroken3' 18 | subbroken3: subalt 7 breaks 'subbroken3' 19 | subalt: user request: install subalt | for package 'subalt' 20 | subbroken3: subalt 7 breaks 'subbroken3' 21 | subalt: user request: install subalt | for package 'subalt' 22 | END 23 | my $deterministic_reason_chain = get_reason_chain($cupt, 'subalt' => 'broken'); 24 | $deterministic_reason_chain =~ s/[12]/3/g; 25 | 26 | chomp($answer); 27 | is($deterministic_reason_chain, $answer); 28 | 29 | -------------------------------------------------------------------------------- /test/t/manage/preview/reason-chain/basic.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 5; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = TestCupt::setup( 6 | 'dpkg_status' => [ 7 | compose_installed_record('unwanted', '4') , 8 | ], 9 | 'packages' => [ 10 | compose_package_record('top', '1') . "Depends: middle\n" , 11 | compose_package_record('middle', '2') . "Pre-Depends: bottom\n" , 12 | compose_package_record('bottom', '3') . "Breaks: unwanted\n" , 13 | ], 14 | ); 15 | 16 | my $answer = ''; 17 | 18 | sub add_answer_level { 19 | my ($line) = @_; 20 | 21 | $answer =~ s/^./ $&/gm; 22 | $answer = "$line\n$answer"; 23 | } 24 | 25 | 26 | $answer = "top: user request: install top | for package 'top'"; 27 | is(get_reason_chain($cupt, 'top' => 'top'), $answer); 28 | add_answer_level("middle: top 1 depends on 'middle'"); 29 | is(get_reason_chain($cupt, 'top' => 'middle'), $answer); 30 | add_answer_level("bottom: middle 2 pre-depends on 'bottom'"); 31 | is(get_reason_chain($cupt, 'top' => 'bottom'), $answer); 32 | add_answer_level("unwanted: bottom 3 breaks 'unwanted'"); 33 | is(get_reason_chain($cupt, 'top' => 'unwanted'), $answer); 34 | 35 | like(get_reason_chain($cupt, 'middle' => 'top'), qr/extraction failed/); 36 | 37 | -------------------------------------------------------------------------------- /test/t/manage/preview/reason-chain/common.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings FATAL => 'all'; 3 | 4 | sub get_reason_chain { 5 | my ($cupt, $package_to_install, $package_down_chain) = @_; 6 | 7 | my $input = "rc\n$package_down_chain\n"; 8 | my $output = `echo '$input' 2>&1 | $cupt -s install $package_to_install 2>&1`; 9 | 10 | my ($result) = ($output =~ m/to show reason chain.*?\n((?:$package_down_chain).+?)\n\n/s); 11 | 12 | if (not defined $result) { 13 | return "Reason chain extraction failed, full output:\n" . $output; 14 | } 15 | 16 | return $result; 17 | } 18 | 19 | 1; 20 | 21 | -------------------------------------------------------------------------------- /test/t/manage/preview/reason-chain/elaboration.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 4; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ 7 | compose_installed_record('aa', 1) . "Provides: foo\n" , 8 | compose_installed_record('bb', 2) . "Provides: foo\n" , 9 | ], 10 | ); 11 | 12 | # relation expression requests are not package-annotated 13 | my $request = '--unsatisfy foo'; 14 | is(get_reason_chain($cupt, $request, 'aa'), "aa: user request: unsatisfy 'foo'"); 15 | is(get_reason_chain($cupt, $request, 'bb'), "bb: user request: unsatisfy 'foo'"); 16 | 17 | # version requests are package-annotated 18 | $request = "--remove '*'"; 19 | is(get_reason_chain($cupt, $request, 'aa'), "aa: user request: remove * | for package 'aa'"); 20 | is(get_reason_chain($cupt, $request, 'bb'), "bb: user request: remove * | for package 'bb'"); 21 | 22 | -------------------------------------------------------------------------------- /test/t/manage/preview/reason-chain/implicit.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 5; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | sub test { 6 | my ($status, $reason_regex) = @_; 7 | 8 | my $record = compose_installed_record('abc', 1, 'status-line' => "install ok $status"); 9 | my $cupt = setup('dpkg_status' => [ $record ]); 10 | 11 | my $rc = get_reason_chain($cupt, '', 'abc'); 12 | like($rc, $reason_regex, "auto-configuring | $status"); 13 | } 14 | 15 | my $configure_reason = qr/^abc: implicit: configuring partially installed packages$/; 16 | my $no_reason = qr/extraction failed.*nothing to do/is; 17 | 18 | test('unpacked', $configure_reason); 19 | test('half-configured', $configure_reason); 20 | test('triggers-pending', $configure_reason); 21 | test('triggers-awaited', $no_reason); 22 | test('installed', $no_reason); 23 | 24 | -------------------------------------------------------------------------------- /test/t/manage/preview/reason-chain/non-relational.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 5; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ compose_installed_record('unneeded', '3.4-5') ], 7 | 'extended_states' => [ compose_autoinstalled_record('unneeded') ], 8 | ); 9 | 10 | is(get_reason_chain($cupt, '', 'unneeded'), 'unneeded: auto-removal'); 11 | 12 | 13 | $cupt = setup( 14 | 'packages' => [ compose_package_record('newpkg', '2.15') . "Provides: bar\n" ], 15 | ); 16 | 17 | is(get_reason_chain($cupt, 'newpkg', 'newpkg'), "newpkg: user request: install newpkg | for package 'newpkg'"); 18 | is(get_reason_chain($cupt, '--satisfy bar', 'newpkg'), "newpkg: user request: satisfy 'bar'"); 19 | 20 | $cupt = setup( 21 | 'dpkg_status' => [ 22 | compose_installed_record('libfoo1', '1') . "Source: foo\n" , 23 | compose_installed_record('libfoo-bin', '1') . "Source: foo\n" , 24 | ], 25 | 'packages' => [ 26 | compose_package_record('libfoo1', '4') . "Source: foo\n" , 27 | compose_package_record('libfoo-bin', '4') . "Source: foo\n" , 28 | ], 29 | 'sources' => [ 30 | compose_package_record('foo', 4) . "Binary: libfoo1, libfoo-bin\n" , 31 | ], 32 | ); 33 | 34 | my $upgrade_libfoo_request = 'libfoo1 -o cupt::resolver::synchronize-by-source-versions=hard'; 35 | my $libfoo1_reason = "libfoo1: user request: install libfoo1 | for package 'libfoo1'"; 36 | is(get_reason_chain($cupt, $upgrade_libfoo_request, 'libfoo1'), $libfoo1_reason); 37 | is(get_reason_chain($cupt, $upgrade_libfoo_request, 'libfoo-bin'), "libfoo-bin: libfoo-bin: synchronization with libfoo1 4\n $libfoo1_reason"); 38 | 39 | -------------------------------------------------------------------------------- /test/t/manage/preview/reinstall/multiple-candidates.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3; 2 | 3 | sub test { 4 | my ($text, $stanza, $expected_version) = @_; 5 | 6 | my $cupt = setup( 7 | 'dpkg_status' => [ compose_installed_record('pp', 3) ], 8 | 'packages' => [ 9 | compose_package_record('pp', 3, 'sha' => '5') . $stanza , 10 | compose_package_record('pp', 3, 'sha' => '7') , 11 | ] 12 | ); 13 | 14 | my $offer = get_first_offer("$cupt reinstall pp"); 15 | is(get_offered_version($offer, 'pp'), $expected_version, $text) or diag($offer); 16 | } 17 | 18 | test("equal candidates, first is chosen", "Suggests: xx\n" => '3'); 19 | test("first candidate is uninstallable, second is chosen", "Depends: xx\n" => '3^dhs0'); 20 | test("first candidate is worse, second is chosen", "Recommends: xx\n" => '3^dhs0'); 21 | 22 | -------------------------------------------------------------------------------- /test/t/manage/preview/reinstall/yes-or-no.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3+1+4; 2 | 3 | my $cupt = setup( 4 | 'dpkg_status' => [ 5 | compose_installed_record('pp', '4.5-1') , 6 | compose_installed_record('qq', '2') , 7 | compose_installed_record('rr', 0) , 8 | ], 9 | 'packages' => [ 10 | compose_package_record('pp', '4.5-1'), 11 | compose_package_record('qq', '3'), 12 | ], 13 | ); 14 | 15 | sub test { 16 | my ($comment, $arguments, $regex) = @_; 17 | 18 | my $offer = get_first_offer("$cupt -o cupt::console::actions-preview::show-versions=no reinstall $arguments"); 19 | 20 | like($offer, $regex, $comment); 21 | } 22 | 23 | sub not_avail_regex { 24 | my ($type, $name, $version) = @_; 25 | return qr/\Q$type: the package '$name' cannot be reinstalled because there is no corresponding version ($version) available in repositories\E/; 26 | } 27 | 28 | my $pp_yes_regex = qr/will be reinstalled:\n\npp\s*\n/; 29 | 30 | test('there is a reinstall candidate', 'pp', $pp_yes_regex); 31 | test('there are no reinstall candidates (some other version available)', 'qq', not_avail_regex('E', 'qq', 2)); 32 | test('there are no reinstall candidates (no other versions available)', 'rr', not_avail_regex('E', 'rr', 0)); 33 | 34 | test('wish of a available reinstall', '--wish pp', $pp_yes_regex); 35 | 36 | my $qq_soft_no_regex = not_avail_regex('W', 'qq', 2); 37 | test('wish of an unavailable reinstall', '--wish qq', $qq_soft_no_regex); 38 | test('try of an unavailable reinstall', '--try qq', $qq_soft_no_regex); 39 | test('big priority request of an unavailable reinstall', '--importance=1000000 qq', $qq_soft_no_regex); 40 | test('wish of a available and unavailable', '--wish pp qq', $pp_yes_regex); 41 | 42 | -------------------------------------------------------------------------------- /test/t/manage/preview/show-not-preferred.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $cupt = TestCupt::setup( 8 | 'dpkg_status' => 9 | entail(compose_installed_record('m', '1')) . 10 | entail(compose_installed_record('n', '11')) . 11 | entail(compose_installed_record('l', '21')) . 12 | entail(compose_installed_record('d', '0.99')), 13 | 'packages' => 14 | entail(compose_package_record('m', '2')) . 15 | entail(compose_package_record('m', '3.broken') . "Depends: m (= 2)\n") . 16 | entail(compose_package_record('n', '12')) . 17 | entail(compose_package_record('l', '22')) . 18 | entail(compose_package_record('l', '23')) . 19 | entail(compose_package_record('d', '0.98')), 20 | ); 21 | 22 | sub get_not_preferred_regex { 23 | my ($line) = @_; 24 | return qr/not preferred.*\n\n^$line\s*$/im 25 | } 26 | 27 | sub our_first_offer { 28 | my ($cupt, $arguments) = @_; 29 | return get_first_offer("$cupt -o cupt::console::actions-preview::show-versions=no $arguments"); 30 | } 31 | 32 | like(our_first_offer($cupt, "install --show-not-preferred"), get_not_preferred_regex('l m n'), 'm and n and l can be upgraded'); 33 | like(our_first_offer($cupt, "safe-upgrade"), get_not_preferred_regex('m'), 'm cannot be upgraded to the latest version'); 34 | 35 | -------------------------------------------------------------------------------- /test/t/manage/resolver/autoremoval/global-setting.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | my $cupt = TestCupt::setup( 5 | 'dpkg_status' => compose_installed_record('abc', '1'), 6 | 'extended_states' => entail(compose_autoinstalled_record('abc')) 7 | ); 8 | 9 | subtest "autoremoval of leaf package if not disabled" => sub { 10 | my $offer = get_first_offer("$cupt install"); 11 | like($offer, regex_offer(), "no-action install succeeded"); 12 | is(get_offered_version($offer, 'abc'), get_empty_version(), "'abc' is autoremoved") or 13 | diag($offer); 14 | }; 15 | 16 | subtest "no autoremoval of leaf package if disabled" => sub { 17 | my $offer = get_first_offer("$cupt install --no-auto-remove"); 18 | like($offer, regex_offer(), "no-action install succeeded"); 19 | is(get_offered_version($offer, 'abc'), get_unchanged_version(), "'abc' is unchanged"); 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /test/t/manage/resolver/autoremoval/never-autoremove-setting.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 3; 3 | 4 | my $cupt = TestCupt::setup( 5 | 'dpkg_status' => 6 | entail(compose_installed_record('abc', '1')) . 7 | entail(compose_installed_record('def', '2')), 8 | 'extended_states' => 9 | entail(compose_autoinstalled_record('abc')) . 10 | entail(compose_autoinstalled_record('def')) 11 | ); 12 | 13 | my $offer = get_first_offer("$cupt install -o apt::neverautoremove::=ab.*"); 14 | 15 | like($offer, regex_offer(), "no-action install succeeded"); 16 | is(get_offered_version($offer, 'abc'), get_unchanged_version(), "'abc' is not touched"); 17 | is(get_offered_version($offer, 'def'), get_empty_version(), "'def' is autoremoved"); 18 | 19 | -------------------------------------------------------------------------------- /test/t/manage/resolver/autoremoval/no-if-rdepends-setting.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 4; 3 | 4 | my $cupt = TestCupt::setup( 5 | 'dpkg_status' => 6 | entail(compose_installed_record('main', '1') . "Depends: slave1 | slave2 | slave3\n") . 7 | entail(compose_installed_record('slave1', '1')) . 8 | entail(compose_installed_record('slave2', '1')) . 9 | entail(compose_installed_record('slave3', '1')), 10 | 'extended_states' => 11 | entail(compose_autoinstalled_record('slave2')) . 12 | entail(compose_autoinstalled_record('slave3')) 13 | ); 14 | 15 | my $offer = get_first_offer("$cupt install -o cupt::resolver::no-autoremove-if-rdepends-exist::=slave2*"); 16 | 17 | like($offer, regex_offer(), "no-action install succeeded"); 18 | is(get_offered_version($offer, 'slave1'), get_unchanged_version(), "'slave1' is not touched (manually installed)"); 19 | is(get_offered_version($offer, 'slave2'), get_unchanged_version(), "'slave2' is not touched (since rdependency exists)"); 20 | is(get_offered_version($offer, 'slave3'), get_empty_version(), "'slave3' is autoremoved (none of the above)"); 21 | 22 | -------------------------------------------------------------------------------- /test/t/manage/resolver/conflicts.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $packages = < $packages); 23 | 24 | like(get_first_offer("$cupt install abc"), regex_offer(), "package doesn't conflict with itself"); 25 | like(get_first_offer("$cupt install abc def"), regex_no_solutions(), "conflicting packages cannot be installed together"); 26 | 27 | -------------------------------------------------------------------------------- /test/t/manage/resolver/depending-on-many-packages.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 19; 2 | 3 | sub test { 4 | my ($count, $command_priority, $release_is_default, $expected_result) = @_; 5 | 6 | my @many_package_list = map { "p$_" } (1..$count); 7 | 8 | my $archive = $release_is_default ? undef : 'other'; 9 | my $cupt = setup('releases' => [{ 10 | 'archive' => $archive, 11 | 'packages' => [ 12 | compose_package_record('big', '9000') . "Depends: " . join(',', @many_package_list) . "\n", 13 | (map { compose_package_record($_, 0) } @many_package_list) 14 | ], 15 | }]); 16 | 17 | my $expected_version = $expected_result ? '9000' : get_unchanged_version(); 18 | 19 | my $archive_comment = $archive // 'default'; 20 | my $comment = "$command_priority, $archive_comment archive, depends on $count packages --> $expected_result"; 21 | 22 | my $cupt_options = $release_is_default ? '' : '-o apt::default-release=xyz'; 23 | 24 | my $output = get_first_offer("$cupt --$command_priority $cupt_options install big"); 25 | is(get_offered_version($output, 'big'), $expected_version, $comment) or diag($output); 26 | } 27 | 28 | test(1, 'wish', 1 => 1); 29 | test(5, 'wish', 1 => 1); 30 | test(25, 'wish', 1 => 1); 31 | test(50, 'wish', 1 => 1); 32 | test(100, 'wish', 1 => 1); 33 | test(300, 'wish', 1 => 1); 34 | test(1000, 'wish', 1 => 1); 35 | 36 | test(1, 'wish', 0 => 1); 37 | test(3, 'wish', 0 => 1); 38 | test(6, 'wish', 0 => 1); 39 | test(10, 'wish', 0 => 0); 40 | test(20, 'wish', 0 => 0); 41 | 42 | test(1, 'try', 0 => 1); 43 | test(5, 'try', 0 => 1); 44 | test(25, 'try', 0 => 1); 45 | test(100, 'try', 0 => 1); 46 | test(200, 'try', 0 => 1); 47 | test(300, 'try', 0 => 0); 48 | test(500, 'try', 0 => 0); 49 | 50 | -------------------------------------------------------------------------------- /test/t/manage/resolver/depending-on-more-usual-packages-dont-give-more-score/long-chain.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 34; 2 | 3 | sub generate_package_chain_removing_i { 4 | my ($count) = @_; 5 | 6 | my @result; 7 | 8 | foreach my $index (1..$count-1) { 9 | my $current_package = "p$index"; 10 | my $next_package = 'p' . ($index+1); 11 | push @result, compose_package_record($current_package, '7') . "Depends: $next_package (>= 7)\n"; 12 | } 13 | push @result, compose_package_record("p$count", '8') . "Conflicts: i\n"; 14 | 15 | return @result; 16 | } 17 | 18 | my @lot_of_installed_packages = map { compose_installed_record("p$_", 5) } (1..32); 19 | 20 | sub test { 21 | my ($packages_were_installed, $count) = @_; 22 | 23 | my $cupt = TestCupt::setup( 24 | 'dpkg_status' => [ 25 | compose_installed_record('i', '1'), 26 | ($packages_were_installed ? @lot_of_installed_packages : ()), 27 | ], 28 | 'releases' => [ 29 | { 30 | 'archive' => 'kk', 31 | 'packages' => [ 32 | compose_package_record('a', '6') . "Depends: p1 (>= 7)\n", 33 | generate_package_chain_removing_i($count), 34 | ], 35 | }, 36 | { 37 | 'archive' => 'qq', 38 | 'packages' => [ compose_package_record('a', '4') ], 39 | }, 40 | ], 41 | ); 42 | 43 | my $offer = get_first_offer("$cupt install -t kk --sf a"); 44 | my $chain_type = ($packages_were_installed ? 'upgraded' : 'new'); 45 | is(get_offered_version($offer, 'a'), 4, "a 6 depends on the bad chain of $count $chain_type packages --> a 4 is offered"); 46 | } 47 | 48 | # new packages 49 | foreach (0..13) { 50 | test(0, 2**$_); 51 | } 52 | 53 | # upgraded packages 54 | foreach (1..20) { 55 | local $TODO = 'score of non-explicitly-requested-by-user upgrades could be smaller' if ($_ >= 8); 56 | test(1, $_); 57 | } 58 | 59 | -------------------------------------------------------------------------------- /test/t/manage/resolver/depending-on-more-usual-packages-dont-give-more-score/short-chain.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 26; 3 | 4 | my $cupt; 5 | my $pin_of_zzz; 6 | 7 | sub lsetup { 8 | $cupt = setup( 9 | 'packages' => 10 | entail(compose_package_record('mmm', '0') . "Depends: bb1 | bb2\n") . 11 | entail(compose_package_record('nnn', '0') . "Depends: bb2 | bb3\n") . 12 | entail(compose_package_record('bb1', '1') . "Recommends: zzz\n") . 13 | entail(compose_package_record('bb2', '2') . "Depends: ccc\n") . 14 | entail(compose_package_record('bb3', '3') . "Recommends: zzz\n") . 15 | entail(compose_package_record('ccc', '100') . "Recommends: zzz\n") . 16 | entail(compose_package_record('zzz', '8')), 17 | 'preferences' => 18 | compose_version_pin_record('bb1', '*', 700) . 19 | compose_version_pin_record('bb2', '*', 600) . 20 | compose_version_pin_record('bb3', '*', 500) . 21 | compose_version_pin_record('zzz', '*', $pin_of_zzz), 22 | ); 23 | } 24 | 25 | sub get_first_offer_for { 26 | my ($package) = @_; 27 | return get_first_offer("$cupt install $package"); 28 | } 29 | 30 | sub test { 31 | my ($package, $bb_version) = @_; 32 | 33 | subtest "$package, pin of zzz: $pin_of_zzz" => sub { 34 | my $output = get_first_offer_for($package); 35 | for my $version (1..3) { 36 | my $bb_package = "bb$version"; 37 | my $expected_version = ($bb_version eq $version) ? $version : get_unchanged_version(); 38 | is(get_offered_version($output, $bb_package), $expected_version) or diag($output); 39 | } 40 | }; 41 | } 42 | 43 | foreach (-5000, -2000, -1000, -500, -200, 0, 100, 300, 500, 700, 900, 1200, 2000) { 44 | $pin_of_zzz = $_; 45 | lsetup(); 46 | 47 | test('mmm', '1'); 48 | test('nnn', '2'); 49 | } 50 | 51 | -------------------------------------------------------------------------------- /test/t/manage/resolver/dist-upgrade/basic.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 4; 2 | use CuptInteractive; 3 | 4 | sub setup_with { 5 | my ($cupt_installed_version, $dpkg_installed_version) = @_; 6 | 7 | return setup( 8 | 'dpkg_status' => [ 9 | compose_installed_record('cupt', $cupt_installed_version) , 10 | compose_installed_record('dpkg', $dpkg_installed_version) , 11 | compose_installed_record('libvoo', '0.5') , 12 | compose_installed_record('qqq', '1.4.4') , 13 | ], 14 | 'packages' => [ 15 | compose_package_record('cupt', '3.0') , 16 | compose_package_record('dpkg', '1.50') , 17 | compose_package_record('libvoo', '0.6') , 18 | compose_package_record('voo', '0.6') , 19 | compose_package_record('qqq', '1.4.4') , 20 | ], 21 | ); 22 | } 23 | 24 | sub test { 25 | my ($text, $installed_versions, $expected_tools_upgrade_versions) = @_; 26 | my $cupt = setup_with(@$installed_versions); 27 | 28 | subtest $text => sub { 29 | my $process = CuptInteractive->new("$cupt -s dist-upgrade", "Do you want to continue? [y/N/q/a/rc/?]"); 30 | my $output = $process->initial_output(); 31 | if (scalar(keys %$expected_tools_upgrade_versions) > 0) { 32 | my $first_stage_versions = get_offered_versions($output); 33 | is_deeply($first_stage_versions, $expected_tools_upgrade_versions, 'first stage') or diag($output); 34 | $cupt = setup_with('3.0', '1.50'); 35 | $output = $process->execute('y'); 36 | } 37 | my $second_stage_versions = get_offered_versions($output); 38 | is_deeply($second_stage_versions, {'libvoo' => '0.6'}, 'second stage') or diag($output); 39 | }; 40 | } 41 | 42 | test('up-to-date', ['3.0', '1.50'], {}); 43 | test('newer cupt', ['2.15', '1.50'], {'cupt' => '3.0'}); 44 | test('newer dpkg', ['3.0', '1.48'], {'dpkg' => '1.50'}); 45 | test('newer both', ['2.11', '1.43'], {'dpkg' => '1.50', 'cupt' => '3.0'}); 46 | 47 | -------------------------------------------------------------------------------- /test/t/manage/resolver/dist-upgrade/package-arguments.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 9; 2 | 3 | my $cupt = setup( 4 | 'dpkg_status' => [ 5 | compose_installed_record('cupt', 0) , 6 | compose_installed_record('dpkg', 0) , 7 | compose_installed_record('def', 1) , 8 | compose_installed_record('pqr', 1) , 9 | compose_installed_record('abc', 1) , 10 | ], 11 | 'packages' => [ 12 | compose_package_record('def', 2) , 13 | compose_package_record('pqr', 3) , 14 | compose_package_record('xyz', 4) , 15 | ], 16 | ); 17 | 18 | sub test { 19 | my ($text, $arguments, $expected_versions) = @_; 20 | 21 | my $output = get_first_offer("$cupt dist-upgrade $arguments"); 22 | is_deeply(get_offered_versions($output), $expected_versions, $text) or diag($output); 23 | } 24 | 25 | test("empty --install", "--install", {'def' => 2, 'pqr' => 3}); 26 | test("empty --remove", "--remove", {'def' => 2, 'pqr' => 3}); 27 | test("keep one package", "--install def/installed", {'pqr' => 3}); 28 | test("keep everything", "--iii '*/installed'", {}); 29 | test("remove instead of upgrade", "--remove pqr", {'def' => 2, 'pqr' => get_empty_version()}); 30 | test("remove non-upgradeable", "--remove abc", {'def' => 2, 'pqr' => 3, 'abc' => get_empty_version()}); 31 | test("also install new package", "--install xyz", {'def' => 2, 'pqr' => 3, 'xyz' => 4}); 32 | test("also install new package (short syntax)", "xyz", {'def' => 2, 'pqr' => 3, 'xyz' => 4}); 33 | test("multiple changes", "--install xyz --install pqr/installed", {'def' => 2, 'xyz' => 4}); 34 | 35 | -------------------------------------------------------------------------------- /test/t/manage/resolver/equal-solutions-offered-in-dependency-order.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 3; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $packages = < $packages); 32 | 33 | my $output = `yes 'N' | $cupt -s install main 2>&1`; 34 | 35 | like($output, regex_offer(), "resolving succeeded"); 36 | like($output, qr/dep1.*dep2/s, "dep1 is offered before dep2"); 37 | like($output, qr/dep2.*dep3/s, "dep2 is offered before dep3"); 38 | 39 | -------------------------------------------------------------------------------- /test/t/manage/resolver/log-order/predecessors-of-problematic-element.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 1; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $problem_package = 'pr'; 8 | 9 | sub compose_rdepending_package { 10 | my ($package, $version) = @_; 11 | return entail(compose_installed_record($package, $version) . "Depends: $problem_package\n"); 12 | } 13 | 14 | my $cupt = TestCupt::setup( 15 | 'dpkg_status' => 16 | compose_rdepending_package('yyy', 5) . 17 | compose_rdepending_package('zzz', 3) . 18 | compose_rdepending_package('aaa', 4) . 19 | compose_rdepending_package('bbb', 2) . 20 | compose_rdepending_package('xxx', 9), 21 | ); 22 | 23 | my $log = get_first_offer("$cupt install -o debug::resolver=yes"); 24 | 25 | my @log_lines = split("\n", $log); 26 | my @packages_in_order = map { m/problem .*?: (.+?) .+?: depends '$problem_package'/ } @log_lines; 27 | 28 | is_deeply(\@packages_in_order, [ qw(zzz yyy xxx bbb aaa) ]) or diag($log); 29 | 30 | -------------------------------------------------------------------------------- /test/t/manage/resolver/log-order/problematic-elements-of-equal-priority.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 1; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my @dependee_packages; 8 | 9 | sub generate_r_record { 10 | my ($index) = @_; 11 | 12 | my @r_packages = ("r$index-3", "r$index-7", "r$index-5"); 13 | push @dependee_packages, @r_packages; 14 | 15 | my $depends_line = 'Depends: ' . join(', ', @r_packages) . "\n"; 16 | return entail(compose_installed_record(sprintf("a%02d", $index), 1) . $depends_line); 17 | } 18 | 19 | my $cupt = TestCupt::setup( 20 | 'dpkg_status' => 21 | join('', map { generate_r_record($_) } (1..42)), 22 | 'packages' => 23 | join('', map { entail(compose_package_record($_, 0)) } @dependee_packages), 24 | ); 25 | 26 | my $log = get_first_offer("$cupt install -o debug::resolver=yes"); 27 | my @log_lines = split("\n", $log); 28 | 29 | my @problems_in_order = map { m/problem .* depends '(.+?)'/ } @log_lines; 30 | 31 | is_deeply(\@problems_in_order, [ reverse @dependee_packages ]) or diag($log); 32 | 33 | -------------------------------------------------------------------------------- /test/t/manage/resolver/multiarch/colon-any.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $packages = 8 | entail(compose_package_record('abc', '1')) . 9 | entail(compose_package_record('abc', '2') . "Multi-Arch: allowed\n"); 10 | 11 | my $cupt = TestCupt::setup('packages' => $packages); 12 | 13 | subtest "':any' limits out non-Multi-Arch packages, subtest1" => sub { 14 | my $output = get_all_offers("$cupt satisfy 'abc:any'"); 15 | 16 | is(get_offer_count($output), 1, "only one offer") or 17 | return diag($output); 18 | is(get_offered_version($output, 'abc'), '2', "'abc 2' is offered") or 19 | diag($output); 20 | }; 21 | 22 | subtest "':any' limits out non-Multi-Arch packages, subtest2" => sub { 23 | my $output = get_first_offer("$cupt satisfy 'abc:any (<< 2)'"); 24 | 25 | like($output, regex_no_solutions(), "no solutions offered"); 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /test/t/manage/resolver/multiarch/colon-native.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6; 2 | 3 | sub test { 4 | my ($ma_value, $expected_count) = @_; 5 | 6 | my $ma_stanza = defined($ma_value) ? "Multi-Arch: $ma_value\n" : ''; 7 | my $cupt = setup('packages' => [ compose_package_record('abc', 0) . $ma_stanza ]); 8 | 9 | my $output = get_all_offers("$cupt satisfy 'abc:native'"); 10 | is(get_offer_count($output), $expected_count, $ma_stanza) or diag($output); 11 | } 12 | 13 | test(undef, 0); 14 | test('corruptedfoobla', 0); 15 | test('no', 0); 16 | test('same', 0); 17 | test('foreign', 1); 18 | test('allowed', 0); 19 | 20 | -------------------------------------------------------------------------------- /test/t/manage/resolver/multiarch/colon-unknown.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 6; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $packages = 8 | entail(compose_package_record('abc', '1')) . 9 | entail(compose_package_record('abc', '2') . "Multi-Arch: allowed\n"); 10 | 11 | my $cupt = TestCupt::setup('packages' => $packages); 12 | 13 | my $parse_failure = qr/^\QE: failed to parse\E/m; 14 | 15 | like(get_first_offer("$cupt satisfy 'abc:zzz'"), regex_no_solutions(), "unknown ':zzz'"); 16 | like(get_first_offer("$cupt satisfy 'abc:zzz (>= 1)'"), regex_no_solutions(), "unknown ':zzz' with version"); 17 | like(get_first_offer("$cupt satisfy 'abc:y'"), regex_no_solutions(), "unknown ':y'"); 18 | like(get_first_offer("$cupt satisfy 'abc:aaaaaaaaaaaaaaaaaaaaaaannnnnnnnnnnnnnnnnnnnnnn13245mmmmmmmmm___ajsdf'"), regex_no_solutions(), "unknown ':'"); 19 | like(get_first_offer("$cupt satisfy 'abc:'"), $parse_failure, "invalid ':'"); 20 | like(get_first_offer("$cupt satisfy 'abc: (<< 3)'"), $parse_failure, "invalid ':' with version"); 21 | 22 | -------------------------------------------------------------------------------- /test/t/manage/resolver/non-default-release/not-automatic.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6; 2 | 3 | sub generate_100_installed_packages { 4 | return map { compose_installed_record("p$_", 1) } (1..100); 5 | } 6 | 7 | sub generate_n_package_list { 8 | my ($count) = @_; 9 | return join(', ', map { "p$_" } (1..$count)); 10 | } 11 | 12 | sub setup_cupt { 13 | return setup( 14 | 'dpkg_status' => [ generate_100_installed_packages() ], 15 | 'releases' => [ 16 | { 17 | 'archive' => 'eexxpp', 18 | 'not-automatic' => 1, 19 | 'packages' => [ compose_package_record('aa', '66') ], 20 | }, 21 | { 22 | 'archive' => 'normal', 23 | 'packages' => [ 24 | compose_package_record('aa', '5') . 'Breaks: ' . generate_n_package_list(@_) . "\n", 25 | ], 26 | }, 27 | ], 28 | ); 29 | } 30 | 31 | sub test { 32 | my ($count, $expected_exp) = @_; 33 | 34 | my $cupt = setup_cupt($count); 35 | my $offer = get_first_offer("$cupt install --select=flexible aa"); 36 | 37 | my $expected_aa_version = $expected_exp ? 66 : 5; 38 | my $comment = "breaks $count packages, expecting package from eexxpp: $expected_exp"; 39 | is(get_offered_version($offer, 'aa'), $expected_aa_version, $comment) or diag($offer); 40 | } 41 | 42 | test(1 => 0); 43 | test(2 => 0); 44 | test(3 => 1); 45 | test(5 => 1); 46 | test(10 => 1); 47 | test(20 => 1); 48 | 49 | -------------------------------------------------------------------------------- /test/t/manage/resolver/optimise/conflicting-with-not-installed-package.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 1; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $cupt = TestCupt::setup( 8 | 'packages' => 9 | entail(compose_package_record('a', 2) . "Conflicts: b\n") . 10 | entail(compose_package_record('b', 1)), 11 | ); 12 | 13 | my $offer = get_first_offer("$cupt install a -o cupt::resolver::max-leaf-count=1"); 14 | like($offer, regex_offer()); 15 | 16 | -------------------------------------------------------------------------------- /test/t/manage/resolver/optimise/versions-containing-same-problem.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 6; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $cupt = TestCupt::setup( 8 | 'dpkg_status' => 9 | entail(compose_installed_record('pi', '1.2-3') . "Depends: nnn (>= 4)\n"), 10 | 'packages' => 11 | entail(compose_package_record('nnn', '3')) . 12 | entail(compose_package_record('pi', '1.4-1') . "Depends: nnn (>= 5)\n") . 13 | entail(compose_package_record('pi', '1.2-5') . "Depends: nnn (>= 4)\n") . 14 | entail(compose_package_record('pi', '1.2-6') . "Recommends: nnn (>= 4)\n") . 15 | entail(compose_package_record('pi', '1.2-2') . "Depends: nnn (>= 3)\n") . 16 | entail(compose_package_record('pi', '1.2-3') . "Depends: nnn (>= 2)\n") , 17 | ); 18 | 19 | my $output = get_all_offers("$cupt --no-remove install -o cupt::resolver::max-leaf-count=6"); 20 | 21 | like($output, regex_offer(), "resolving succeeded") or diag($output); 22 | 23 | my @offers = split_offers($output); 24 | my @offered_versions = map { get_offered_version($_, 'pi') } @offers; 25 | 26 | sub test_found { 27 | my ($version, $expected_result) = @_; 28 | 29 | my $result = grep { $_ eq $version } @offered_versions; 30 | is($result, $expected_result, "version $version is offered: $expected_result") or diag($output); 31 | } 32 | 33 | test_found('1.4-1' => 0); 34 | test_found('1.2-5' => 0); 35 | test_found('1.2-6' => 1); 36 | test_found('1.2-2' => 1); 37 | test_found('1.2-3' => 1); 38 | 39 | -------------------------------------------------------------------------------- /test/t/manage/resolver/reinstall/as-version-change-variant.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 4; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | sub setup_cupt { 8 | my ($version, $relation) = @_; 9 | 10 | return TestCupt::setup( 11 | 'dpkg_status' => 12 | entail(compose_installed_record('pp', 1) . "Depends: inv\n"), 13 | 'packages' => 14 | entail(compose_package_record('pp', $version) . $relation) . 15 | entail(compose_package_record('pp', 1)), 16 | 'preferences' => 17 | compose_version_pin_record('pp', '0.9', 1100) . 18 | compose_version_pin_record('pp', '1.3', 400) , 19 | ); 20 | } 21 | 22 | sub test 23 | { 24 | my ($version, $relation, $expected_result, $comment) = @_; 25 | 26 | my $cupt = setup_cupt($version, $relation); 27 | 28 | my $output = get_all_offers("$cupt install --no-remove"); 29 | my @offers = split_offers($output); 30 | 31 | my @offered_versions = map { get_offered_version($_, 'pp') } @offers; 32 | 33 | is_deeply(\@offered_versions, $expected_result, $comment) or diag($output); 34 | } 35 | 36 | test('0.9', '' => [ '1', '0.9' ], 'reinstall offered before good-pin downgrade'); 37 | test('1.1', '' => [ '1.1', '1' ], 'reinstall offered after normal-pin problemless upgrade'); 38 | test('1.3', '' => [ '1', '1.3' ], 'reinstall offered before middle-pin problemless upgrade'); 39 | test('2', "Recommends: inv2\n" => [ '1', '2' ], 'reinstall offered before unsatisfied-recommends upgrade'); 40 | 41 | -------------------------------------------------------------------------------- /test/t/manage/resolver/relative-priority-order.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 4; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | sub setup_cupt { 8 | my ($base_priority) = @_; 9 | 10 | return TestCupt::setup( 11 | 'dpkg_status' => 12 | entail(compose_installed_record('pp', 1)), 13 | 'packages' => 14 | entail(compose_package_record('pp', 2)) . 15 | entail(compose_package_record('pp', 3)) . 16 | entail(compose_package_record('pp', 4)), 17 | 'preferences' => 18 | compose_version_pin_record('pp', 1, $base_priority) . 19 | compose_version_pin_record('pp', 2, $base_priority+100) . 20 | compose_version_pin_record('pp', 3, $base_priority-100) . 21 | compose_version_pin_record('pp', 4, $base_priority) 22 | ); 23 | } 24 | 25 | sub test { 26 | my ($base_priority) = @_; 27 | 28 | my $cupt = setup_cupt($base_priority); 29 | my $output = get_all_offers("$cupt satisfy 'pp (>= 2)'"); 30 | 31 | my @offered_versions = map { get_offered_version($_, 'pp') } split_offers($output); 32 | 33 | is_deeply(\@offered_versions, [ 2, 4, 3 ], "base priority: $base_priority, priority order is preserved") 34 | or diag($output); 35 | } 36 | 37 | test(500); 38 | test(0); 39 | test(7000); 40 | test(-15000); 41 | 42 | -------------------------------------------------------------------------------- /test/t/manage/resolver/remove/essential-penalty-when-non-essential-version-is-considered-first.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $cupt = TestCupt::setup( 8 | 'dpkg_status' => 9 | entail(compose_installed_record('ie', 1) . "Essential: yes\n"), 10 | 'packages' => 11 | entail(compose_package_record('ie', 2)), 12 | ); 13 | 14 | sub test { 15 | my ($score, $expected_ie_version) = @_; 16 | 17 | my $cupt_options = "-o cupt::resolver::score::removal-of-essential=$score"; 18 | my $upgrade_request = '--importance=500 --install ie=2'; 19 | my $remove_request = '--importance=20000 --remove ie'; 20 | my $offer = get_first_offer("$cupt install $upgrade_request $remove_request $upgrade_request $cupt_options"); 21 | is(get_offered_version($offer, 'ie'), $expected_ie_version, "score: $score, expected version: $expected_ie_version") 22 | or diag($offer); 23 | } 24 | 25 | test(-15000 => get_empty_version()); 26 | test(-25000 => 2); 27 | 28 | -------------------------------------------------------------------------------- /test/t/manage/resolver/remove/no-remove-and-explicit-removals.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | sub setup_cupt { 8 | my ($is_auto) = @_; 9 | 10 | return TestCupt::setup( 11 | 'dpkg_status' => 12 | entail(compose_installed_record('abc', '1')), 13 | 'extended_states' => 14 | ($is_auto ? compose_autoinstalled_record('abc') : ''), 15 | ); 16 | } 17 | 18 | sub test { 19 | my ($is_auto, $regex, $comment) = @_; 20 | 21 | my $cupt = setup_cupt($is_auto); 22 | 23 | my $offer = get_first_offer("$cupt remove abc --no-remove"); 24 | like($offer, $regex, $comment); 25 | } 26 | 27 | test(0, regex_no_solutions(), "--no-remove does prevent explicit removals"); 28 | test(1, regex_offer(), "--no-remove doesn't prevent removals of automatically installed packages"); 29 | 30 | -------------------------------------------------------------------------------- /test/t/manage/resolver/remove/no-remove-prevents-removal-of-other-packages.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use warnings; 5 | use strict; 6 | 7 | my $cupt = TestCupt::setup( 8 | 'dpkg_status' => 9 | entail(compose_installed_record('xyz', 1)) . 10 | entail(compose_installed_record('abc', 20)) . 11 | entail(compose_installed_record('def', 30)), 12 | 'packages' => 13 | entail(compose_package_record('def', 31)) . 14 | entail(compose_package_record('xyz', 2) . "Breaks: abc, def\n"), 15 | ); 16 | 17 | sub test { 18 | my ($command) = @_; 19 | 20 | my $options = '-o cupt::resolver::score::removal=100000'; 21 | my $offer = get_first_offer("$cupt $command $options"); 22 | 23 | subtest "$command" => sub { 24 | like($offer, regex_offer(), "resolving succeeded"); 25 | is(get_offered_version($offer, 'abc'), get_unchanged_version(), "'abc' (not upgradeable) doesn't change"); 26 | is(get_offered_version($offer, 'def'), 31, "'def' is upgraded"); 27 | } or diag($offer) 28 | } 29 | 30 | test('install --no-remove --try xyz=2 --wish def=31'); 31 | test('safe-upgrade'); 32 | 33 | -------------------------------------------------------------------------------- /test/t/manage/resolver/remove/on-flexible-removal-all-version-string-ids-are-proposed.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $dpkg_status = < $dpkg_status, 30 | 'packages' => $packages, 31 | ); 32 | 33 | my $output = get_all_offers("$cupt remove --sf xyz/installed"); 34 | 35 | like($output, regex_offer(), "resolving succeeded"); 36 | is(get_offer_count($output), 3, "three solutions (removal + two of 'xyz 2') are offered") or 37 | diag $output; 38 | 39 | -------------------------------------------------------------------------------- /test/t/manage/resolver/same-original-version-string-but-different-relations.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 18; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $sha = 0; 8 | 9 | sub compose_ttt_record { 10 | my ($is_valid) = @_; 11 | return entail(compose_package_record('ttt', 4, 'sha' => ++$sha) . ($is_valid ? '' : "Depends: broken\n")); 12 | } 13 | 14 | sub setup_cupt { 15 | my $ttts = join('', map { compose_ttt_record($_) } @_); 16 | return TestCupt::setup( 17 | 'dpkg_status' => 18 | entail(compose_installed_record('ttt', 3)), 19 | 'packages' => 20 | entail(compose_package_record('nnn', 1) . "Depends: ttt (>= 4)\n") . 21 | $ttts , 22 | ); 23 | } 24 | 25 | my $command; 26 | 27 | sub test { 28 | my ($valid1, $valid2, $valid3, $expected_suffix) = @_; 29 | 30 | my $cupt = setup_cupt($valid1, $valid2, $valid3); 31 | 32 | my $output = get_first_offer("$cupt $command"); 33 | 34 | my $comment = "command: $command, 1st valid: $valid1, 2nd valid: $valid2, 3rd valid: $valid3, expected suffix: '$expected_suffix'"; 35 | is(get_offered_version($output, 'ttt'), "4$expected_suffix", $comment) 36 | or diag($output); 37 | } 38 | 39 | sub test_group { 40 | test(1, 1, 1, ''); 41 | test(1, 0, 1, ''); 42 | test(1, 0, 0, ''); 43 | 44 | test(0, 1, 1, '^dhs0'); 45 | test(0, 1, 0, '^dhs0'); 46 | 47 | test(0, 0, 1, '^dhs1'); 48 | } 49 | 50 | $command = 'install --sf "version(4.*)"'; 51 | test_group(); 52 | 53 | $command = 'full-upgrade'; 54 | test_group(); 55 | 56 | $command = 'install nnn'; 57 | test_group(); 58 | 59 | -------------------------------------------------------------------------------- /test/t/manage/resolver/same-pin-versions/anti-relation-order.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6; 2 | 3 | require(get_rinclude_path('relation-common')); 4 | 5 | init([ compose_installed_record('ppp', 0) ]); 6 | 7 | my $p = 'ppp'; 8 | my $ev = get_empty_version(); 9 | 10 | # "common command part" 11 | my $ccp = "satisfy '$p (= 0)-'"; 12 | 13 | # no sorting required 14 | test("$ccp $p-", [ $ev ], 'strict removal'); 15 | test("$ccp '$p (= 6)-'", [ 5, 4, 3, $ev ], 'version 6 disallowed by real relation'); 16 | test("$ccp v5or4-", [ 6, 3, $ev ], 'some versions disallowed by virtual relation'); 17 | test("$ccp v5or4- v6or4-", [ 3, $ev ], 'some version disallowed, crossed virtual relations'); 18 | # sorting required 19 | test("$ccp '$p (= 3)-'", [ 6, 5, 4, $ev ], 'version 3 disallowed by real relation'); 20 | test("$ccp vxorp3-", [ 6, 5, 4, $ev ], 'version 3 disallowed by virtual relation'); 21 | 22 | -------------------------------------------------------------------------------- /test/t/manage/resolver/same-pin-versions/forward-relation-order.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 15; 2 | 3 | require(get_rinclude_path('relation-common')); 4 | 5 | init(''); 6 | 7 | my $p = 'ppp'; 8 | my $q = 'qqq'; 9 | my $uv = get_unchanged_version(); 10 | 11 | # no sorting required 12 | test("satisfy '$p (= 3) | $p (= 4) | $p (= 5)'", [ 3, 4, 5 ], 'full order'); 13 | test("satisfy v5or4", [ 5, 4 ], 'virtual package, no sorting required'); 14 | test("satisfy '$p (= 3) | v5or4'", [ 3, 5, 4 ], 'no crossing, first relation (real package) has preference'); 15 | test("satisfy 'v5or4 | $p (= 6)'", [ 5, 4, 6 ], 'no crossing, first relation (virtual) has preference'); 16 | test("satisfy 'v5or4 | v6or4'", [ 5, 4, 6 ], 'partial crossing, first relation (virtual) has preference'); 17 | test("satisfy 'v5or4 | $p | $p (= 3)'", [ 5, 4, 6, 3 ], 'double crossing, relation order is preserved'); 18 | test("satisfy 'v5or4 | $q (= 7) | v6or4'", [ 5, 4, $uv, 6 ], 'partial crossing, other package in between'); 19 | 20 | # sorting required 21 | test("satisfy $p", [ 6, 5, 4, 3 ], 'no order'); 22 | test("satisfy v6or4", [ 6, 4 ], 'virtual package, sorting is required'); 23 | test("satisfy '$p (<< 5) | $p'", [ 4, 3, 6, 5 ], 'crossing, first relation (versioned) has preference'); 24 | test("satisfy '$p (= 3) | $p'", [ 3, 6, 5, 4 ], 'partial crossing, first relation (real) has preference'); 25 | 26 | # multi-package 27 | test("satisfy '$q (= 3) | $p (= 4) | $q (= 5) | $p (= 6) | $q (= 7)'", [ $uv, 4, $uv, 6, $uv ], 'multi-package real relations, version intermix'); 28 | test("satisfy mixed", [ 6, 3, $uv, $uv ], 'multi-package virtual relation, packages separately'); 29 | test("satisfy '$q | mixed | v6or4 | $p'", [ $uv, $uv, $uv, 6, 3, 4, 5 ], 'a bit of everything'); 30 | test("satisfy vxorp3", [ 3, $uv ], 'multi-package virtual relation, package order is alphabetic'); 31 | 32 | -------------------------------------------------------------------------------- /test/t/manage/resolver/same-pin-versions/maximum-version-is-chosen-for-broken-version-fix.t: -------------------------------------------------------------------------------- 1 | require(get_rinclude_path('version-change-common')); 2 | 3 | run_tests("Depends: fail\n", 'install'); 4 | 5 | -------------------------------------------------------------------------------- /test/t/manage/resolver/same-pin-versions/maximum-version-is-chosen-for-upgrade.t: -------------------------------------------------------------------------------- 1 | require(get_rinclude_path('version-change-common')); 2 | 3 | run_tests('', 'safe-upgrade'); 4 | 5 | -------------------------------------------------------------------------------- /test/t/manage/resolver/same-pin-versions/relation-common.pl: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | 3 | my $p = 'ppp'; 4 | my $q = 'qqq'; 5 | my $x = 'xxx'; 6 | 7 | my $cupt; 8 | 9 | sub init { 10 | my $installed = shift; 11 | 12 | $cupt = setup( 13 | 'dpkg_status' => $installed, 14 | 'packages' => [ 15 | compose_package_record($x, 8) . "Provides: vxorp3\n" , 16 | compose_package_record($p, 5) . "Provides: v5or4\n" , 17 | compose_package_record($p, 4) . "Provides: v5or4, v6or4\n" , 18 | compose_package_record($p, 6) . "Provides: v6or4, mixed\n" , 19 | compose_package_record($p, 3) . "Provides: mixed, vxorp3\n" , 20 | compose_package_record($q, 7) . "Provides: mixed\n" , 21 | compose_package_record($q, 5) . "Provides: mixed\n" , 22 | compose_package_record($q, 3) , 23 | ] 24 | ); 25 | } 26 | 27 | sub test { 28 | my ($command, $expected_versions, $comment) = @_; 29 | 30 | my $output = get_all_offers("$cupt $command"); 31 | my @offers = split_offers($output); 32 | 33 | my @proposed_versions = map { get_offered_version($_, $p) } @offers; 34 | 35 | is_deeply(\@proposed_versions, $expected_versions, $comment) or diag($output); 36 | } 37 | 38 | 1; 39 | 40 | -------------------------------------------------------------------------------- /test/t/manage/resolver/score-tuning/common.pl: -------------------------------------------------------------------------------- 1 | sub generate_n_installed_packages { 2 | return join('', map { entail(compose_installed_record("p$_", 1) . "Provides: p\n") } (1..$_[0])); 3 | } 4 | 5 | 1; 6 | 7 | -------------------------------------------------------------------------------- /test/t/manage/resolver/score-tuning/downgrade.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 12; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | sub setup_cupt { 6 | return setup( 7 | 'dpkg_status' => 8 | generate_n_installed_packages(@_) . 9 | entail(compose_installed_record('down', '1.0')), 10 | 'packages' => [ 11 | compose_package_record('down', '0.9') , 12 | compose_package_record('bb', 6) . "Depends: down (<< 1)\n" , 13 | compose_package_record('bb', 3) . "Breaks: p\n" , 14 | ], 15 | ); 16 | } 17 | 18 | sub test { 19 | my ($downgrade_score, $count, $downgrade_expected) = @_; 20 | 21 | my $cupt = setup_cupt($count); 22 | 23 | my $downgrade_option = defined $downgrade_score ? "-o cupt::resolver::score::downgrade=$downgrade_score" : ''; 24 | my $offer = get_first_offer("$cupt install --select=flexible bb $downgrade_option"); 25 | 26 | my $score_comment = ($downgrade_score // 'default'); 27 | my $comment = "downgrade score: $score_comment, breaks $count packages, downgrade expected: $downgrade_expected"; 28 | 29 | my $expected_bb_version = $downgrade_expected ? 6 : 3; 30 | is(get_offered_version($offer, 'bb'), $expected_bb_version, $comment) or diag($offer); 31 | } 32 | 33 | test(undef, 0 => 0); 34 | test(undef, 1 => 0); 35 | test(undef, 2 => 0); 36 | test(undef, 3 => 0); 37 | test(undef, 5 => 0); 38 | test(undef, 8 => 1); 39 | test(undef, 13 => 1); 40 | test(undef, 20 => 1); 41 | test(undef, 40 => 1); 42 | 43 | test(+8000, 1 => 0); 44 | test(+12000, 1 => 1); 45 | test(-100000, 40 => 0); 46 | 47 | -------------------------------------------------------------------------------- /test/t/manage/resolver/score-tuning/hold.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 9; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | sub setup_cupt { 6 | return setup( 7 | 'dpkg_status' => 8 | generate_n_installed_packages(@_) . 9 | entail(compose_installed_record('h', 1, 'on-hold'=>1)), 10 | 'packages' => [ 11 | compose_package_record('cc', 8) . "Depends: h (= 2)\n" , 12 | compose_package_record('cc', 4) . "Breaks: p\n" , 13 | compose_package_record('h', 2) , 14 | ], 15 | ); 16 | } 17 | 18 | sub test { 19 | my ($count, $upgrade_h_expected) = @_; 20 | 21 | my $cupt = setup_cupt($count); 22 | my $offer = get_first_offer("$cupt install --select=flexible cc"); 23 | 24 | my $expected_cc_version = $upgrade_h_expected ? 8 : 4; 25 | 26 | my $comment = "breaks $count packages, upgrade of h expected: $upgrade_h_expected"; 27 | is(get_offered_version($offer, 'cc'), $expected_cc_version, $comment) or diag($offer); 28 | } 29 | 30 | test(0 => 0); 31 | test(5 => 0); 32 | test(20 => 0); 33 | test(100 => 0); 34 | test(200 => 0); 35 | test(500 => 0); 36 | test(1000 => 1); 37 | test(2000 => 1); 38 | test(5000 => 1); 39 | 40 | -------------------------------------------------------------------------------- /test/t/manage/resolver/score-tuning/max-leaf-count.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 5; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $cupt = TestCupt::setup( 8 | 'packages' => 9 | entail(compose_package_record('l1', '1') . "Depends: l2\n") . 10 | entail(compose_package_record('l2', '2') . "Depends: l3\n") . 11 | entail(compose_package_record('l3', '3')), 12 | ); 13 | 14 | sub test { 15 | my ($limit, $expected_result) = @_; 16 | my $offer = get_first_offer("$cupt install l1 -o cupt::resolver::max-leaf-count=$limit"); 17 | my $result = ($offer =~ regex_offer()) || 0; 18 | is($result, $expected_result, "when limit is $limit, expect $expected_result") or diag($offer); 19 | } 20 | 21 | test(10 => 1); 22 | test(4 => 1); 23 | test(3 => 1); 24 | test(2 => 0); 25 | test(0 => 0); 26 | 27 | -------------------------------------------------------------------------------- /test/t/manage/resolver/score-tuning/negative-version-factor.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 10; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | sub generate_simple_packages { 8 | my ($count) = @_; 9 | 10 | my @list; 11 | my $content; 12 | 13 | foreach (1..$count) { 14 | my $name = "p$_"; 15 | push @list, $name; 16 | $content .= entail(compose_package_record($name, '2')); 17 | } 18 | 19 | return (\@list, $content); 20 | } 21 | 22 | sub setup_cupt { 23 | my ($count) = @_; 24 | 25 | my ($list, $content) = generate_simple_packages($count); 26 | 27 | return TestCupt::setup( 28 | 'dpkg_status' => 29 | entail(compose_installed_record('a', '1')), 30 | 'packages' => 31 | entail(compose_package_record('a', '2') . "Depends: " . join(',', @$list) . "\n") . 32 | $content, 33 | ); 34 | } 35 | 36 | sub get_first_offer_for { 37 | my ($cupt, $score) = @_; 38 | my $score_option = defined $score ? "-o cupt::resolver::score::version-factor::negative=$score" : ''; 39 | return get_first_offer("$cupt install -t na --importance=160 a=2 $score_option"); 40 | } 41 | 42 | sub test { 43 | my ($count, $score, $expect_a_upgraded) = @_; 44 | 45 | my $expected_a_version = $expect_a_upgraded ? '2' : get_unchanged_version(); 46 | my $comment = "count: $count, score: " . ($score//'default') . ", upgrade expected: $expect_a_upgraded"; 47 | 48 | my $cupt = setup_cupt($count); 49 | my $offer = get_first_offer_for($cupt, $score); 50 | 51 | is(get_offered_version($offer, 'a'), $expected_a_version, $comment) or diag($offer); 52 | } 53 | 54 | test(1, undef, 1); 55 | test(2, undef, 1); 56 | test(3, undef, 1); 57 | test(5, undef, 1); 58 | test(10, undef, 0); 59 | test(20, undef, 0); 60 | 61 | test(2, 120, 1); 62 | test(3, 120, 0); 63 | 64 | test(10, 24, 1); 65 | test(15, 24, 0); 66 | 67 | -------------------------------------------------------------------------------- /test/t/manage/resolver/score-tuning/new.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 3; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $cupt = TestCupt::setup( 8 | 'dpkg_status' => 9 | entail(compose_installed_record('i', 1)), 10 | 'packages' => 11 | entail(compose_package_record('n1', 3) . "Depends: p\n") . 12 | entail(compose_package_record('i', 2) . "Provides: p\n") . 13 | entail(compose_package_record('n2', 4) . "Provides: p\n"), 14 | ); 15 | 16 | sub test { 17 | my ($new_score, $expected_n1, $expected_n2) = @_; 18 | 19 | my $new_option = defined $new_score ? "-o cupt::resolver::score::new=$new_score" : ''; 20 | my $offer = get_first_offer("$cupt --importance=10 install n1 $new_option"); 21 | 22 | my $score_comment = ($new_score // 'default'); 23 | 24 | subtest "new score: $score_comment" => sub { 25 | is(get_offered_version($offer, 'n1'), ($expected_n1 ? '3' : get_unchanged_version()), "n1 expected: $expected_n1"); 26 | is(get_offered_version($offer, 'n2'), ($expected_n2 ? '4' : get_unchanged_version()), "n2 expected: $expected_n2"); 27 | } or diag($offer); 28 | } 29 | 30 | test(-20, 0, 0); 31 | test(undef, 1, 0); 32 | test(400, 1, 1); 33 | 34 | -------------------------------------------------------------------------------- /test/t/manage/resolver/score-tuning/upgrade.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 12; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | sub setup_cupt { 6 | return setup( 7 | 'dpkg_status' => 8 | generate_n_installed_packages(@_) . 9 | entail(compose_installed_record('up', '4')), 10 | 'packages' => [ 11 | compose_package_record('up', '5') , 12 | compose_package_record('bb', 7) . "Depends: up (= 5)\n" , 13 | compose_package_record('bb', 8) . "Breaks: p\n" , 14 | ], 15 | ); 16 | } 17 | 18 | sub test { 19 | my ($score, $count, $upgrade_expected) = @_; 20 | 21 | my $cupt = setup_cupt($count); 22 | 23 | my $upgrade_option = defined $score ? "-o cupt::resolver::score::upgrade=$score" : ''; 24 | my $offer = get_first_offer("$cupt install --select=flexible bb $upgrade_option"); 25 | 26 | my $score_comment = ($score // 'default'); 27 | my $comment = "upgrade score: $score_comment, breaks $count packages, upgrade expected: $upgrade_expected"; 28 | 29 | my $expected_bb_version = $upgrade_expected ? 7 : 8; 30 | is(get_offered_version($offer, 'bb'), $expected_bb_version, $comment) or diag($offer); 31 | } 32 | 33 | test(undef, 0 => 0); 34 | test(undef, 1 => 1); 35 | 36 | test(200, 0 => 0); 37 | test(200, 1 => 1); 38 | 39 | test(-500, 0 => 0); 40 | test(-500, 1 => 1); 41 | 42 | test(-10000, 1 => 0); 43 | test(-10000, 2 => 0); 44 | test(-10000, 4 => 0); 45 | test(-10000, 8 => 1); 46 | test(-10000, 15 => 1); 47 | test(-10000, 40 => 1); 48 | 49 | -------------------------------------------------------------------------------- /test/t/manage/resolver/score-tuning/version-factor.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 10; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $common_relations = "Provides: other_tt\nRecommends: missing-package\n"; 8 | 9 | my $cupt = TestCupt::setup( 10 | 'dpkg_status' => 11 | entail(compose_installed_record('tt', '1')), 12 | 'packages' => 13 | entail(compose_package_record('tt', '2') . $common_relations) . 14 | entail(compose_package_record('tt', '0') . $common_relations) , 15 | ); 16 | 17 | sub get_first_offer_for { 18 | my ($score) = @_; 19 | my $score_option = defined $score ? "-o cupt::resolver::score::version-factor::common=$score" : ''; 20 | return get_first_offer("$cupt install --importance=150 --satisfy other_tt $score_option"); 21 | } 22 | 23 | sub test { 24 | my ($score, $expected_tt_version) = @_; 25 | my $comment = 'score: ' . ($score//'default') . ", expecting $expected_tt_version"; 26 | my $offer = get_first_offer_for($score); 27 | is(get_offered_version($offer, 'tt'), $expected_tt_version, $comment) or diag($offer); 28 | } 29 | 30 | test(undef, 2); 31 | 32 | test(-500, 0); 33 | test(-100, 0); 34 | test(-30, 0); 35 | test(0, get_unchanged_version()); 36 | test(10, get_unchanged_version()); 37 | test(50, 2); 38 | test(100, 2); 39 | test(200, 2); 40 | test(500, 2); 41 | 42 | -------------------------------------------------------------------------------- /test/t/manage/resolver/synchronize-by-source-versions/not-existing-packages.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $source_line = "Source: xyz-source\n"; 8 | my $dpkg_status = entail(compose_installed_record('xyz1', '1') . $source_line); 9 | my $packages = entail(compose_package_record('xyz1', '2') . $source_line); 10 | my $sources = < $dpkg_status, 19 | 'packages' => $packages, 20 | 'sources' => $sources 21 | ); 22 | 23 | my $output = get_first_offer("$cupt install xyz1 -o cupt::resolver::synchronize-by-source-versions=hard"); 24 | 25 | like($output, regex_offer(), "resolving succeeded, not existing packages doesn't cause problems"); 26 | like($output, qr/xyz1 .* -> 2/, "'xyz1' package is updated"); 27 | 28 | -------------------------------------------------------------------------------- /test/t/manage/resolver/unsatisfy-importance-is-not-grow-if-applied-to-multiple-package-versions.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $packages = < $packages); 23 | 24 | my $output = get_first_offer("$cupt install --importance=3000 abc --importance=2500 --satisfy vp-"); 25 | 26 | like($output, regex_offer(), "resolving succeeded"); 27 | is(get_offered_version($output, 'abc'), '2', "abc is offered"); 28 | -------------------------------------------------------------------------------- /test/t/manage/resolver/upgrade/dependee-of-considered-to-be-upgraded-package.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 10; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $installed = entail(compose_installed_record('master', '10')); 8 | 9 | my $packages = entail(compose_package_record('master', '30') . "Depends: slave (>= 2)\n"); 10 | $packages .= entail(compose_package_record('slave', '2') . "Conflicts: master\n"); 11 | 12 | my $cupt; 13 | my $type; 14 | sub run_test { 15 | my ($removal_score) = @_; 16 | 17 | subtest "dependees are not ${type} if not needed in a final solution (removal score: $removal_score)" => sub { 18 | my $options = "-o cupt::resolver::score::removal=$removal_score -o cupt::resolver::score::upgrade=100000"; 19 | my $output = get_first_offer("$cupt full-upgrade $options"); 20 | like($output, regex_offer(), "there is an offer") or 21 | return; 22 | unlike($output, qr/slave/, "slave is not changed"); 23 | } 24 | } 25 | 26 | sub run_test_series { 27 | $cupt = TestCupt::setup( 28 | 'dpkg_status' => $installed, 29 | 'packages' => $packages, 30 | 'preferences' => "Package: slave\nPin: version 2\nPin-Priority: 50\n"); 31 | run_test(-5000); 32 | run_test(-500); 33 | run_test(0); 34 | run_test(500); 35 | run_test(5000); 36 | } 37 | 38 | $type = 'installed'; 39 | run_test_series(); 40 | 41 | $installed .= entail(compose_installed_record('slave', '1')); 42 | $type = 'upgraded'; 43 | run_test_series(); 44 | 45 | -------------------------------------------------------------------------------- /test/t/manage/resolver/upgrade/low-pin-upgrade-with-new-dependency-vs-normal-pin-upgrade.t: -------------------------------------------------------------------------------- 1 | # bug: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=754480 2 | 3 | use TestCupt; 4 | use Test::More tests => 5; 5 | 6 | use strict; 7 | use warnings; 8 | 9 | my $bad_relation = "Recommends: bad1, bad2\n"; 10 | 11 | sub test { 12 | my ($lower_pin) = @_; 13 | 14 | my $cupt = TestCupt::setup( 15 | 'dpkg_status' => 16 | entail(compose_installed_record('ppp', 1)), 17 | 'packages' => 18 | entail(compose_package_record('ppp', 2) . $bad_relation) . 19 | entail(compose_package_record('ppp', 5) . "Depends: newdep\n" . $bad_relation) . 20 | entail(compose_package_record('newdep', 3)), 21 | 'preferences' => 22 | compose_version_pin_record('ppp', 5, $lower_pin), 23 | ); 24 | 25 | my $offer = get_first_offer("$cupt full-upgrade"); 26 | 27 | my $comment = "bringing new normal-pin dependency doens't give order advantage to lower-pin ($lower_pin) upgrade"; 28 | is(get_offered_version($offer, 'ppp'), 2, $comment) or diag($offer); 29 | } 30 | 31 | test(120); 32 | test(200); 33 | test(300); 34 | test(400); 35 | test(495); 36 | 37 | -------------------------------------------------------------------------------- /test/t/manage/resolver/upgrade/normal-pin-upgrade-vs-pinned-new-package.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 11; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | sub setup_cupt { 8 | my ($new_pin) = @_; 9 | 10 | return TestCupt::setup( 11 | 'dpkg_status' => 12 | entail(compose_installed_record('ii', 1)), 13 | 'packages' => 14 | entail(compose_package_record('ii', 2)) . 15 | entail(compose_package_record('newp', 3)) . 16 | entail(compose_package_record('mm', 4) . "Depends: ii (>= 2) | newp\n"), 17 | 'preferences' => 18 | compose_version_pin_record('newp', '*', $new_pin), 19 | ); 20 | } 21 | 22 | sub test { 23 | my ($new_pin, $upgrade_expected) = @_; 24 | 25 | my $cupt = setup_cupt($new_pin); 26 | my $offer = get_first_offer("$cupt install mm"); 27 | 28 | my $comment = "new package of pin $new_pin vs upgrade of existing one, upgrade expected: $upgrade_expected"; 29 | 30 | is(get_offered_version($offer, 'ii'), ($upgrade_expected ? 2 : get_unchanged_version()), $comment) 31 | or diag($offer); 32 | } 33 | 34 | test(-1000 => 1); 35 | test(100 => 1); 36 | test(300 => 1); 37 | test(500 => 1); 38 | test(700 => 1); 39 | test(900 => 1); 40 | test(990 => 1); 41 | test(1000 => 1); 42 | test(1100 => 0); 43 | test(2000 => 0); 44 | test(5000 => 0); 45 | 46 | -------------------------------------------------------------------------------- /test/t/manage/resolver/upgrade/same-origin-versions-not-proposed.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 1; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $cupt = TestCupt::setup( 8 | 'dpkg_status' => 9 | entail(compose_installed_record('ip', '3.2')), 10 | 'packages' => 11 | entail(compose_package_record('ip', '3.1')) . 12 | entail(compose_package_record('ip', '3.2')) . 13 | entail(compose_package_record('ip', '3.2.2')) . 14 | entail(compose_package_record('ip', '3.3.4')) . 15 | entail(compose_package_record('ip', '3.2')) . 16 | entail(compose_package_record('ip', '3.5')) . 17 | entail(compose_package_record('ip', '4.0')), 18 | ); 19 | 20 | my $offers = get_all_offers("$cupt safe-upgrade"); 21 | my $expected_offer_count = 5; # 4 upgrade versions + 1 unsatisfied upgrade 22 | is(get_offer_count($offers), $expected_offer_count) or diag($offers); 23 | 24 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/common.pl: -------------------------------------------------------------------------------- 1 | sub get_worker_command { 2 | my ($cupt, $command, %params) = @_; 3 | 4 | my $options = '-y -o cupt::console::warnings::removal-of-essential=no'; 5 | if ($params{'simulate'}//1) { 6 | $options .= ' -s'; 7 | } 8 | 9 | return "$cupt $options $command"; 10 | } 11 | 12 | 1; 13 | 14 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/downloads/uris/common.pl: -------------------------------------------------------------------------------- 1 | require(get_rinclude_path('../../common')); 2 | 3 | sub test_uris { 4 | my ($comment, $cupt, $subcommand, $uris) = @_; 5 | 6 | my $output = stdall(get_worker_command($cupt, $subcommand)); 7 | 8 | my @downloads = ($output =~ m/^S: downloading: (.*)$/mg); 9 | 10 | is_deeply(\@downloads, $uris, $comment) 11 | or diag($output); 12 | } 13 | 14 | sub test_uris_for_bbb_and_ccc { 15 | my (%p) = @_; 16 | 17 | my $cupt = setup( 18 | 'dpkg_status' => [ 19 | compose_installed_record('aaa', 1) , 20 | compose_installed_record('bbb', 2) , 21 | ], 22 | 'releases' => $p{'releases'}, 23 | 'debdelta_conf' => $p{'debdelta_conf'}, 24 | 'debpatch' => $p{'debpatch'}, 25 | ); 26 | 27 | test_uris($p{'comment'}, $cupt, "install bbb ccc --remove aaa", 28 | [ $p{'expected_bbb'}, $p{'expected_ccc'} ]); 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/downloads/uris/debdelta/basic.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 4; 2 | 3 | require(get_rinclude_path('../common')); 4 | 5 | my $debdelta_conf = < $debdelta_conf, 18 | 'debpatch' => ($debpatch_present ? '' : undef), 19 | 'releases' => [ 20 | { 21 | 'packages' => [ 22 | compose_package_record('bbb', $version_of_bbb, 'architecture'=>$arch_of_bbb) . "Filename: pool/b/bbb5.deb\n" , 23 | compose_package_record('ccc', 4) . "Filename: pool/c/ccc4.deb\n" , 24 | compose_package_record('eee', 5) , 25 | ], 26 | 'scheme' => 'https', 27 | 'hostname' => 'debs.net', 28 | }, 29 | ], 30 | 'expected_bbb' => 'https://debs.net/pool/b/bbb5.deb' . $expected_bbb_addendum, 31 | 'expected_ccc' => 'https://debs.net/pool/c/ccc4.deb', 32 | ); 33 | 34 | $parameters{'comment'} = "debpatch is present: $debpatch_present, arch of bbb: $arch_of_bbb: version of bbb: $version_of_bbb"; 35 | 36 | test_uris_for_bbb_and_ccc(%parameters); 37 | } 38 | 39 | sub compose_bbb_debdelta_addendum { 40 | my ($filename_part) = @_; 41 | return " | debdelta:http://deltas.info/pub/pool/b/$filename_part.debdelta"; 42 | } 43 | 44 | test(0, 3, 'all', ''); 45 | test(1, 3, 'all', compose_bbb_debdelta_addendum('bbb_2_3_all')); 46 | test(1, '7:4.6-8', 'all', compose_bbb_debdelta_addendum('bbb_2_7%253a4.6-8_all')); 47 | test(1, 3, $architecture, compose_bbb_debdelta_addendum("bbb_2_3_$architecture")); 48 | 49 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/downloads/uris/normal.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 1; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | test_uris_for_bbb_and_ccc( 6 | 'comment' => 'planned downloads', 7 | 'releases' => [ 8 | { 9 | 'packages' => [ 10 | compose_package_record('bbb', 3) . "Filename: xxx/yyy.deb\n" , 11 | compose_package_record('ccc', 4) . "Filename: pool/aux/c/ccc/ccc_4_testarch.deb\n" , 12 | ], 13 | 'scheme' => 'http', 14 | 'hostname' => 'ftp.fi.debian.org/debian', 15 | }, 16 | ], 17 | 'expected_bbb' => 'http://ftp.fi.debian.org/debian/xxx/yyy.deb', 18 | 'expected_ccc' => 'http://ftp.fi.debian.org/debian/pool/aux/c/ccc/ccc_4_testarch.deb', 19 | ); 20 | 21 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/downloads/uris/several-uris-for-same-version.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'releases' => [ 7 | { 8 | 'packages' => [ 9 | compose_package_record('lll', 0) . "Filename: pool/0.deb\n" , 10 | compose_package_record('mmm', 3) . "Filename: pool/1.deb\n" , 11 | ], 12 | 'scheme' => 'https', 13 | 'hostname' => 'qq.uu', 14 | }, 15 | { 16 | 'packages' => [ 17 | compose_package_record('lll', 0) . "Filename: pool/0.deb\n" , 18 | compose_package_record('mmm', 3) . "Filename: pool/2.deb\n" , 19 | ], 20 | 'scheme' => 'ftp', 21 | 'hostname' => 'ftp.ret', 22 | }, 23 | { 24 | 'packages' => [ compose_package_record('mmm', 3, 'sha' => 'fed') . "Filename: pool/3.deb\n" ], 25 | 'scheme' => 'http', 26 | 'hostname' => 'blabla.uu', 27 | } 28 | ] 29 | ); 30 | 31 | test_uris('2 uris for lll 0, same filename', $cupt, 'install lll', [ 'https://qq.uu/pool/0.deb | ftp://ftp.ret/pool/0.deb' ]); 32 | test_uris('2 uris for mmm 3, different filenames', $cupt, 'install mmm', [ 'https://qq.uu/pool/1.deb | ftp://ftp.ret/pool/1.deb' ]); 33 | test_uris('1 uri for "another" mm 3', $cupt, 'install "Ru(.*blabla.*)"', [ 'http://blabla.uu/pool/3.deb' ]); 34 | 35 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/dpkg/basic.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ 7 | compose_installed_record('bb', 2) . "Depends: dd\n", 8 | compose_installed_record('dd', 6) , 9 | ], 10 | 'packages' => [ 11 | compose_package_record('aa', 1) , 12 | compose_package_record('bb', 3) . "Depends: dd\n" , 13 | compose_package_record('c2', 4) . "Depends: bb (>= 3)\n" , 14 | compose_package_record('c1', 5) . "Depends: c2\n" , 15 | ], 16 | ); 17 | 18 | test_dpkg_sequence($cupt, 'install aa' => ['--install', [], ['']]); 19 | test_dpkg_sequence($cupt, 'remove bb' => ['--remove', [], ['bb']]); 20 | test_dpkg_sequence($cupt, 'install bb' => ['--install', [], ['']]); 21 | 22 | test_dpkg_sequence($cupt, 'install c2' => 23 | ['--install', [], ['']], 24 | ['--install', [], ['']]); 25 | test_dpkg_sequence($cupt, 'install c1' => 26 | ['--install', [], ['']], 27 | ['--install', [], ['']], 28 | ['--install', [], ['']]); 29 | test_dpkg_sequence($cupt, 'remove dd' => 30 | ['--remove', [], ['bb']], 31 | ['--remove', [], ['dd']]); 32 | 33 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/dpkg/common.pl: -------------------------------------------------------------------------------- 1 | require(get_rinclude_path('../common')); 2 | 3 | my $skip_triggers = 1; 4 | sub set_parse_skip_triggers { 5 | $skip_triggers = shift; 6 | } 7 | 8 | sub parse_dpkg_commands { 9 | my $output = shift; 10 | 11 | my $skip_bad_path = 1; 12 | my $package_simplifier = sub { 13 | return (s!.*/((?:\w|-)+)_(\w+)_\w+\.deb$!<$1 $2>!r); 14 | }; 15 | 16 | my $dpkg = get_dpkg_path(); 17 | my @lines = ($output =~ m/^S: running command '$dpkg (.*)'$/mg); 18 | 19 | my $parse_line = sub { 20 | my @tokens = split(' '); 21 | 22 | my $subcommand = shift @tokens; 23 | if ($skip_triggers && ($subcommand =~ m/trigger/)) { 24 | return (); 25 | } 26 | 27 | my @options; 28 | my @packages; 29 | 30 | foreach (@tokens) { 31 | if ($skip_triggers && (m/trigger/)) { 32 | next; 33 | } 34 | if ($skip_bad_path && (m/--force-bad-path/)) { 35 | next; 36 | } 37 | 38 | my $target = (m/^-/ ? \@options : \@packages); 39 | push @$target, $_; 40 | } 41 | 42 | @packages = map(&$package_simplifier, @packages); 43 | 44 | return [$subcommand, \@options, \@packages]; 45 | }; 46 | 47 | return map(&$parse_line, @lines); 48 | } 49 | 50 | sub test_dpkg_sequence { 51 | my ($cupt, $user_command, @expected) = @_; 52 | 53 | my $comment = $user_command; 54 | $user_command =~ s/#.*//; 55 | 56 | my $output; 57 | subtest "$comment" => sub { 58 | $output = stdall(get_worker_command($cupt, $user_command)); 59 | is($?, 0, "command succeeded"); 60 | 61 | my @parsed_output = parse_dpkg_commands($output); 62 | is_deeply(\@parsed_output, \@expected, 'dpkg sequence') 63 | } or diag($output); 64 | } 65 | 66 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/dpkg/dependency-of-unchanged-package.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | sub test { 6 | my $strong_dependency = shift; 7 | 8 | my $dep = ($strong_dependency ? 'Depends' : 'Recommends'); 9 | my $expected_options = ($strong_dependency ? ['--force-depends'] : []); 10 | 11 | my $cupt = setup( 12 | 'dpkg_status' => [ 13 | compose_installed_record('unchanged', 1) . "$dep: mta\n" , 14 | compose_installed_record('exim4', 2) . "Provides: mta\nConflicts: mta\n" , 15 | ], 16 | 'packages' => [ 17 | compose_package_record('postfix', 3) . "Provides: mta\nConflicts: mta\n" , 18 | ], 19 | ); 20 | 21 | test_dpkg_sequence($cupt, "install postfix # $dep", 22 | ['--remove', $expected_options, ['exim4']], 23 | ['--install', $expected_options, ['']]); 24 | } 25 | 26 | test(0); 27 | test(1); 28 | 29 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/dpkg/middle-states.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 5*3; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | sub get_dpkg_sequence { 6 | my $action = shift; 7 | return () if not defined $action; 8 | return (["--$action", [], ['']]) if $action eq 'install'; 9 | return (["--$action", [], ['pp']]); 10 | } 11 | 12 | sub test_group { 13 | my ($state, $action_if_not_available, $action_if_available) = @_; 14 | 15 | my $cupt = setup( 16 | 'dpkg_status' => [ compose_status_record('pp', "install ok $state", 1) ], 17 | 'packages' => [ compose_package_record('pp', 1) ], 18 | ); 19 | 20 | test_dpkg_sequence($cupt, "remove pp # removing a package in state '$state'", 21 | get_dpkg_sequence('remove')); 22 | 23 | test_dpkg_sequence($cupt, "install --include-archives=nothing # install/finish a package in state '$state', original package not available", 24 | get_dpkg_sequence($action_if_not_available)); 25 | 26 | test_dpkg_sequence($cupt, "install # install/finish a package in state '$state'", 27 | get_dpkg_sequence($action_if_available)); 28 | } 29 | 30 | test_group('unpacked' => 'configure', 'configure'); 31 | test_group('half-installed' => 'remove', 'install'); 32 | test_group('half-configured' => 'configure', 'configure'); 33 | test_group('triggers-awaited' => undef, undef); 34 | test_group('triggers-pending' => undef, undef); 35 | 36 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/dpkg/priorities.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 4; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ 7 | compose_installed_record('aa', 1) , 8 | compose_installed_record('bb', 2) , 9 | ], 10 | 'packages' => [ 11 | compose_package_record('bb', 3) , 12 | compose_package_record('cc', 4) , 13 | ], 14 | ); 15 | test_dpkg_sequence($cupt, 'remove aa --install bb cc # action type priority', 16 | ['--install', [], ['']], 17 | ['--install', [], ['']], 18 | ['--remove', [], ['aa']]); 19 | 20 | $cupt = setup( 21 | 'packages' => 22 | entail(compose_package_record('dd', 4)) . 23 | entail(compose_package_record('aa', 1)) . 24 | entail(compose_package_record('ee', 0)) . 25 | entail(compose_package_record('cc', 3)) . 26 | entail(compose_package_record('bb', 2)) , 27 | ); 28 | for my $spelling ('"*"', 'ee dd cc bb aa', 'dd aa ee cc bb') { 29 | test_dpkg_sequence($cupt, "install $spelling # alphabetic package name priority", 30 | ['--install', [], ['']], 31 | ['--install', [], ['']], 32 | ['--install', [], ['']], 33 | ['--install', [], ['
']], 34 | ['--install', [], ['']]); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/dpkg/purge.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 5; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ 7 | compose_installed_record('aa', 1) , 8 | compose_removed_record('bb') , 9 | ], 10 | ); 11 | 12 | test_dpkg_sequence($cupt, 'remove aa # removing installed package', 13 | ['--remove', [], ['aa']]); 14 | test_dpkg_sequence($cupt, 'remove bb # trying to remove config-files package'); 15 | test_dpkg_sequence($cupt, 'purge aa # purging installed package', 16 | ['--purge', [], ['aa']]); 17 | test_dpkg_sequence($cupt, 'purge bb # purging config-files package', 18 | ['--purge', [], ['bb']]); 19 | test_dpkg_sequence($cupt, 'remove aa --purge bb # removing and purging different packages', 20 | ['--remove', [], ['aa']], 21 | ['--purge', [], ['bb']]); 22 | 23 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/dpkg/triggers.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 4; 2 | 3 | require(get_rinclude_path('common')); 4 | set_parse_skip_triggers(0); 5 | 6 | my $cupt = setup( 7 | 'packages' => [ compose_package_record('aa', 1) ], 8 | ); 9 | 10 | sub test { 11 | my ($params, $triggers_enabled, $name) = @_; 12 | 13 | # after-trigger command is given even when triggers are not deferred (see #766758) 14 | test_dpkg_sequence($cupt, "install aa $params", 15 | ['--triggers-only', ['-a'], []], 16 | ['--install', $triggers_enabled?['--no-triggers']:[], ['']], 17 | ['--triggers-only', ['--pending'], []]); 18 | } 19 | 20 | test('-o cupt::worker::defer-triggers=no' => 0); 21 | test('-o cupt::worker::defer-triggers=auto' => 1); 22 | test('' => 1); 23 | test('-o cupt::worker::defer-triggers=yes' => 1); 24 | 25 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/extended-states/common.pl: -------------------------------------------------------------------------------- 1 | require(get_rinclude_path('../common')); 2 | 3 | sub get_new_extended_states_content { 4 | open(my $fd, '<', get_extended_states_path()) or 5 | return ''; 6 | my $result = do { local $/; <$fd> }; 7 | return $result; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/extended-states/unknown-fields-are-preserved.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 1; 2 | 3 | my $initial_extended_states_content = < [ 24 | compose_installed_record('aa', 1) , 25 | compose_installed_record('bb', 2) , 26 | ], 27 | 'extended_states' => $initial_extended_states_content 28 | ); 29 | 30 | subtest "the test" => sub { 31 | my $output = stdall(get_worker_command($cupt, 'unmarkauto aa --no-auto-remove', 'simulate'=>0)); 32 | is($?, 0, 'unmarkauto succeeded') 33 | or diag($output); 34 | is(get_new_extended_states_content(), $expected_extended_states_content, 'unknown tags are preserved'); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/hooks/pre-install-pkgs/feeding-input.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2; 2 | use IPC::Run3; 3 | 4 | require(get_rinclude_path('../../common')); 5 | 6 | my $long_a = 'a' x 200; 7 | 8 | sub get_many_packages { 9 | my $get_package = sub { entail(compose_package_record("${long_a}xyz$_", "$_")) }; 10 | join('', map(&$get_package, (1..800))); 11 | } 12 | 13 | my $cupt = setup( 14 | 'releases' => [{ 15 | 'packages' => get_many_packages(), 16 | 'deb-caches' => 1, 17 | }] 18 | ); 19 | 20 | sub test { 21 | my ($comment, $hook_command, $checker) = @_; 22 | 23 | my $hook_options = "-o dpkg::pre-install-pkgs::='$hook_command' "; 24 | my $command = get_worker_command("timeout 90s $cupt", "install '*xyz*' $hook_options", 'simulate'=>0); 25 | 26 | my $output; 27 | subtest $comment => sub { 28 | run3($command, \undef, \$output, \$output); 29 | 30 | is($?, 0, 'exit code indicates success'); 31 | $checker->($output); 32 | unlike($output, qr/unable to close a part/, 'no fd closing warning messages'); 33 | } or diag($output); 34 | } 35 | 36 | test('input is given', 37 | 'cat', 38 | sub { 39 | my $output = shift; 40 | like($output, qr/xyz5_5_.*\.deb/, 'xyz line is present'); 41 | }); 42 | 43 | test('does not hang when a hook does not accept an input', 44 | '/mrr/nobinary --option1 || exit 0; exec abc', 45 | sub { 46 | pass('execution continues'); 47 | }); 48 | 49 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/hooks/pre-install-pkgs/v2.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2; 2 | 3 | require(get_rinclude_path('v2plus')); 4 | set_parameters(2, 5, 2); 5 | do_tests(); 6 | 7 | -------------------------------------------------------------------------------- /test/t/manage/worker/packages/hooks/pre-install-pkgs/v3.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2; 2 | 3 | require(get_rinclude_path('v2plus')); 4 | set_parameters(3, 9, 4); 5 | do_tests(); 6 | 7 | -------------------------------------------------------------------------------- /test/t/manage/worker/snapshots/basic.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup(); 6 | 7 | test_snapshot_list($cupt, '', 'there are no snapshots'); 8 | test_snapshot_command($cupt, 'unkn', qr/^E: unsupported action 'unkn'$/m, 'unknown snapshot subcommand'); 9 | test_snapshot_command($cupt, ' ', qr/^E: the action is not specified$/m, 'no subcommand'); 10 | 11 | -------------------------------------------------------------------------------- /test/t/manage/worker/snapshots/common.pl: -------------------------------------------------------------------------------- 1 | use IPC::Run3; 2 | 3 | sub test_snapshot_command { 4 | my ($cupt, $arguments, $expected_error, $description) = @_; 5 | 6 | my $output; 7 | subtest $description => sub { 8 | my $output; 9 | run3("$cupt snapshot $arguments", \undef, \$output, \$output); 10 | my $exit_code = $?; 11 | 12 | if (defined $expected_error) { 13 | isnt($exit_code, 0, 'negative exit code'); 14 | like($output, $expected_error, 'error message printed'); 15 | } else { 16 | is($exit_code, 0, 'positive exit code'); 17 | unlike($output, qr/^E: /m, "no errors"); 18 | } 19 | } or diag($output); 20 | } 21 | 22 | sub save_snapshot { 23 | my ($cupt, $name) = @_; 24 | test_snapshot_command($cupt, "save $name", undef, "saving snapshot $name"); 25 | } 26 | 27 | sub test_snapshot_list { 28 | my ($cupt, $expected_output, $description) = @_; 29 | 30 | my $list_command = "$cupt snapshot list"; 31 | 32 | my $output = stdall($list_command); 33 | is($output, $expected_output, $description); 34 | } 35 | 36 | 1; 37 | 38 | -------------------------------------------------------------------------------- /test/t/manage/worker/snapshots/list.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 4; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = TestCupt::setup( 6 | 'dpkg_status' => [ compose_installed_record('aa', 1) ] 7 | ); 8 | 9 | save_snapshot($cupt, 'xyz'); 10 | test_snapshot_list($cupt, "xyz\n", "listing one snapshot"); 11 | 12 | save_snapshot($cupt, 'klm'); 13 | test_snapshot_list($cupt, "klm\nxyz\n", "listing two snapshots"); 14 | 15 | -------------------------------------------------------------------------------- /test/t/manage/worker/snapshots/load/corrupted.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6; 2 | 3 | require(get_rinclude_path('../common')); 4 | 5 | 6 | my $snapshot_name = '201501'; 7 | my $snapshot_path = "var/lib/cupt/snapshots/$snapshot_name"; 8 | 9 | sub setup_cupt { 10 | return TestCupt::setup( 11 | 'dpkg_status' => [ 12 | compose_installed_record('ooo', 1) , 13 | compose_installed_record('ppp', 2) , 14 | ], 15 | ); 16 | } 17 | 18 | sub test { 19 | my ($corrupter, $error, $description) = @_; 20 | $description //= $error; 21 | 22 | my $cupt = setup_cupt(); 23 | 24 | subtest $description => sub { 25 | save_snapshot($cupt, $snapshot_name); 26 | $corrupter->(); 27 | test_snapshot_command($cupt, "load $snapshot_name", qr/^E:.* $error$/m, "error message '$error' printed"); 28 | } 29 | } 30 | 31 | test( 32 | sub { system("echo qqq >> $snapshot_path/installed_package_names"); }, 33 | "the package 'qqq' doesn't exist" 34 | ); 35 | 36 | test( 37 | sub { system("echo 2 > $snapshot_path/format"); }, 38 | "unsupported snapshot format '2'" 39 | ); 40 | 41 | test( 42 | sub { system("mv $snapshot_path ${snapshot_path}%"); }, 43 | "unable to find a snapshot named '$snapshot_name'" 44 | ); 45 | 46 | test( 47 | sub { 48 | my $pfile = glob("$snapshot_path/*Packages"); 49 | system("echo '' > $pfile"); 50 | }, 51 | "unable to find snapshot version for the package '.*'", 52 | 'broken Packages' 53 | ); 54 | 55 | test( 56 | sub { 57 | my $rfile = glob("$snapshot_path/*Release"); 58 | system("echo 'y17341732' > $rfile"); 59 | }, 60 | ".*", 61 | "broken Release" 62 | ); 63 | 64 | test( 65 | sub { system("echo 'deb fttps://corr $snapshot_name/' > $snapshot_path/source"); }, 66 | ".*", 67 | 'broken snapshot repository source file' 68 | ); 69 | 70 | -------------------------------------------------------------------------------- /test/t/manage/worker/snapshots/load/no-changes.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3; 2 | 3 | require(get_rinclude_path('../common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ compose_installed_record('abc', 3) ] 7 | ); 8 | 9 | save_snapshot($cupt, 'sn1'); 10 | 11 | my $offer = get_first_offer("$cupt snapshot load sn1"); 12 | like($offer, regex_offer(), 'load succeeded'); 13 | 14 | is_deeply(get_offered_versions($offer), {}, 'no changes proposed'); 15 | 16 | -------------------------------------------------------------------------------- /test/t/manage/worker/snapshots/remove.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup(); 6 | 7 | test_snapshot_command($cupt, "remove", qr/^E: no snapshot name specified$/m, 'no arguments'); 8 | test_snapshot_command($cupt, "remove xyz", qr/^E: unable to find a snapshot named 'xyz'$/m, 'snapshot does not exist'); 9 | 10 | save_snapshot($cupt, 'qpr'); 11 | save_snapshot($cupt, 'mnk'); 12 | 13 | test_snapshot_command($cupt, "remove mnk", undef, "snapshot existed"); 14 | test_snapshot_list($cupt, "qpr\n", "removal succeeded"); 15 | 16 | -------------------------------------------------------------------------------- /test/t/manage/worker/snapshots/rename.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup(); 6 | 7 | test_snapshot_command($cupt, "rename", qr/^E: no previous snapshot name specified$/m, 'no arguments supplied'); 8 | test_snapshot_command($cupt, "rename tyu", qr/^E: no new snapshot name specified$/m, 'one argument supplied'); 9 | test_snapshot_command($cupt, "rename tyu xyz", qr/^E: unable to find a snapshot named 'tyu'$/m, 'source snapshot does not exist'); 10 | 11 | save_snapshot($cupt, 'qpr'); 12 | test_snapshot_command($cupt, "rename qpr xyz", undef, 'source snapshot exists'); 13 | test_snapshot_list($cupt, "xyz\n", 'renaming succeeded'); 14 | 15 | -------------------------------------------------------------------------------- /test/t/manage/worker/snapshots/save/basic.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 9; 2 | 3 | require(get_rinclude_path('../common')); 4 | 5 | my $cupt = setup(); 6 | 7 | test_snapshot_command($cupt, 'save', qr/^E: no snapshot name/m, 'no arguments'); 8 | 9 | test_snapshot_command($cupt, 'save kkk', undef, 'saving first snapshot'); 10 | test_snapshot_command($cupt, 'save lll', undef, 'saving second snapshot'); 11 | 12 | test_snapshot_command($cupt, 'save lll', qr/^E: the system snapshot named 'lll' already exists$/m, 'second snapshot name already exists'); 13 | test_snapshot_command($cupt, 'remove lll', undef, 'removing second snapshot'); 14 | test_snapshot_command($cupt, 'save lll', undef, 'snapshot name is again free for saving after removal'); 15 | 16 | test_snapshot_command($cupt, 'save kkk', qr/^E: the system snapshot named 'kkk' already exists$/m, 'first snapshot name already exists'); 17 | test_snapshot_command($cupt, 'rename kkk mmm', undef, 'renaming first snapshot'); 18 | test_snapshot_command($cupt, 'save kkk', undef, 'snapshot name is again free for saving after rename'); 19 | 20 | -------------------------------------------------------------------------------- /test/t/manage/worker/snapshots/save/name-acceptance.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 22; 2 | 3 | require(get_rinclude_path('../common')); 4 | 5 | my $cupt = setup(); 6 | 7 | sub test_good_name { 8 | my ($name, $description) = @_; 9 | test_snapshot_command($cupt, "save '$name'", undef, "good: $description"); 10 | } 11 | 12 | test_good_name('abcdef', 'lower letters'); 13 | test_good_name('abc-def', 'dash'); 14 | test_good_name('abc_def', 'underscore'); 15 | test_good_name('ndjnvjodsnadsnvhansdvasodvnaosdhjqiuewohfqiuwe', 'long'); 16 | test_good_name('20091005', 'digits'); 17 | test_good_name('fgh4i', 'mixed lower letters and digits'); 18 | 19 | 20 | sub test_bad_name { 21 | my ($name, $description) = @_; 22 | my $snapshot_name_error_regex = qr/^E: the system snapshot name \Q'$name'\E cannot/m; 23 | test_snapshot_command($cupt, "save '$name'", $snapshot_name_error_regex, "bad: $description"); 24 | } 25 | 26 | test_bad_name('', 'empty'); 27 | test_bad_name('uuu www', 'space'); 28 | test_bad_name('.abc', 'dot at the beginning'); 29 | test_bad_name('../uuu', 'parent directory at the beginning'); 30 | test_bad_name('uuu/www', 'slash'); 31 | test_bad_name('uuu/../www', 'parent directory at the middle'); 32 | test_bad_name('xxx/..', 'parent directory at the end'); 33 | test_bad_name('uUu', 'big letter'); 34 | test_bad_name('uuu*www', 'asterisk'); 35 | test_bad_name('uuu\www', 'backslash'); 36 | test_bad_name('%s', 'percent'); 37 | test_bad_name('name!', 'exclamation mark'); 38 | test_bad_name('xyz?', 'question mark'); 39 | test_bad_name('xyz|uuu', 'pipe'); 40 | test_bad_name('qw;', 'semicolon'); 41 | test_bad_name('#$TY(', 'garbage characters'); 42 | 43 | -------------------------------------------------------------------------------- /test/t/query/config/case-normalisation.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 5; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup(); 6 | 7 | sub test { 8 | my ($option, $initial_present, $lowercase_present) = @_; 9 | 10 | my $value = 'good-value'; 11 | my $output = stdall("$cupt config-dump -o $option=$value"); 12 | 13 | subtest $option => sub { 14 | if ($initial_present) { 15 | test_option($output, $option, $value); 16 | } 17 | if ($lowercase_present) { 18 | test_option($output, lc($option), $value); 19 | } 20 | }; 21 | } 22 | 23 | test('APT::Architecture', 1, 1); 24 | test('Apt::default-release', 1, 1); 25 | test('Aptitude::Some-Other::Option', 1, 0); 26 | 27 | test('Cupt::WoRkEr::PURGE', 0, 1); 28 | test('CUPT::Cache::pin::addendums::hOLD', 0, 1); 29 | 30 | -------------------------------------------------------------------------------- /test/t/query/config/common.pl: -------------------------------------------------------------------------------- 1 | my $warning_regex = qr/^W:/m; 2 | 3 | sub test_option { 4 | my ($output, $option, $value) = @_; 5 | 6 | my $value_comment = ($value // ''); 7 | 8 | subtest "$option => $value_comment" => sub { 9 | if (defined $value) { 10 | like($output, qr/^\Q$option "$value"\E/m, 'option set successfully'); 11 | unlike($output, $warning_regex, 'no warnings'); 12 | } else { 13 | unlike($output, qr/^\Q$option\E/m, 'option not present'); 14 | like($output, $warning_regex, 'warning issued'); 15 | } 16 | }; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /test/t/query/config/dir-ignore-files-silently.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 9+4+4+5; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup(); 6 | my $the_option = 'cupt::console::use-colors'; # any would do 7 | my $default_value = 'no'; 8 | my $the_value = 'kippis'; 9 | 10 | sub test_half { 11 | my ($parts_dir, $filename, $expected_result) = @_; 12 | my $path = "$parts_dir/$filename"; 13 | generate_file($path, "$the_option \"$the_value\";\n"); 14 | my $expected_value = $expected_result ? $the_value : $default_value; 15 | my $output = stdall("$cupt config-dump"); 16 | test_option($output, $the_option, $expected_value); 17 | unlink($path); 18 | } 19 | 20 | sub test { 21 | my @params = @_; 22 | subtest "$_[0]" => sub { 23 | test_half('etc/apt/apt.conf.d', @params); 24 | test_half('etc/cupt/cupt.conf.d', @params); 25 | } 26 | } 27 | 28 | test('abc' => 1); 29 | test('xyz78' => 1); 30 | test('78xyz' => 1); 31 | test('abc.conf' => 1); 32 | test('a_b' => 1); 33 | test('a-b' => 1); 34 | test('~' => 0); 35 | test('abc~' => 0); 36 | test('abc~5' => 1); 37 | 38 | test('abc.disabled' => 0); 39 | test('abc_disabled' => 1); 40 | test('abc.disabledouch' => 1); 41 | test('23q.disable' => 1); 42 | 43 | test('xyz.bak' => 0); 44 | test('xyzbak' => 1); 45 | test('xyz.bak8' => 1); 46 | test('bakbakbar' => 1); 47 | 48 | test('qwe.dpkg-bak' => 0); 49 | test('qwe.dpkg-m' => 0); 50 | test('qwe.dpkg-uqoweqpw' => 0); 51 | test('qwe.d-m' => 1); 52 | test('qwe.dpkg' => 1); 53 | 54 | -------------------------------------------------------------------------------- /test/t/query/config/optional-patterns/acceptance.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 19; 2 | 3 | require(get_rinclude_path('../common')); 4 | 5 | my $cupt = setup(); 6 | 7 | sub test { 8 | my ($option, $result) = @_; 9 | 10 | my $value = '54'; 11 | my $output = stdall("$cupt config-dump -o $option=$value 2>&1 | egrep '$option|^W'"); 12 | 13 | test_option($output, $option, ($result ? $value : undef)); 14 | } 15 | 16 | test('acquire::aaa::proxy' => 1); 17 | test('acquire::bbb::ccc::proxy' => 1); 18 | test('acquire::m:n::proxy' => 1); 19 | test('acquire::ppp::qqq::rrr::proxy' => 1); 20 | 21 | my $dto = 'dpkg::tools::options'; 22 | test("${dto}::lll" => 1); 23 | test("${dto}::lll::mmm" => 1); 24 | test("${dto}::lll:mmm" => 1); 25 | test("${dto}::lll::mmm::nnn" => 1); 26 | test("${dto}::YOO___~1::version" => 1); 27 | test('dpkg::klb' => 1); 28 | test('pkg::klb' => 1); 29 | 30 | test('cupt::downloader::protocols::mmp::priority' => 1); 31 | test('upt::downloader::protocols::mmp::priority' => 1); 32 | test('cupt::downoader::protocols::mmp::priority' => 0); 33 | test('cupt::downloader:protocols::mmp::priority' => 0); 34 | test('cupt::downloader::protocols::mmp' => 0); 35 | test('cupt::downloader::protocols::::priority' => 1); 36 | test('cupt::downloader::protocols::mmp::prio' => 0); 37 | test('protocols::mmp::priority' => 1); 38 | 39 | -------------------------------------------------------------------------------- /test/t/query/config/optional-patterns/normalisation.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 14*3; 2 | 3 | require(get_rinclude_path('../common')); 4 | 5 | my $cupt = setup(); 6 | 7 | sub test_group { 8 | my ($template_option) = @_; 9 | 10 | my $value = '112'; 11 | for my $subst (qw(aux arbit POIUUUq531)) { 12 | (my $option = $template_option) =~ s/\Q*\E/$subst/g; 13 | 14 | my $uppercased_option = uc($option); 15 | my $output = stdall("$cupt config-dump -o $uppercased_option=$value"); 16 | 17 | test_option($output, lc($option), $value); 18 | } 19 | } 20 | 21 | test_group('acquiRE::*::*::proxy'); 22 | test_group('acquiRE::*::PROxy::*'); 23 | test_group('acquiRE::*::PROxy'); 24 | test_group('acquiRE::*::*::dl-limit'); 25 | test_group('acquiRE::*::DL-limit::*'); 26 | test_group('acquiRE::*::DL-limit'); 27 | test_group('acquiRE::*::*::timeout'); 28 | test_group('acquiRE::*::TIMeout::*'); 29 | test_group('acquiRE::*::TIMeout'); 30 | test_group('dpkg::TOOLS::OPtions::*'); 31 | test_group('dpkg::TOOLS::OPtions::*::*'); 32 | test_group('cupt::DOWNLOADEr::protocols::*::priority'); 33 | test_group('cupt::DOWNLOADEr::protocols::*::methods'); 34 | test_group('cupt::DOWNLOADEr::protocols::*::methods::*::priority'); 35 | 36 | -------------------------------------------------------------------------------- /test/t/query/config/translation.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 16; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup(); 6 | 7 | sub test { 8 | my ($from, $to, $value) = @_; 9 | 10 | my $output = stdall("$cupt config-dump -o $from=$value"); 11 | 12 | subtest "$from=$value --> $to=$value" => sub { 13 | test_option($output, $from, $value); 14 | test_option($output, $to, $value); 15 | }; 16 | } 17 | 18 | sub test_pair { 19 | test(@_, 'yes'); 20 | test(@_, 'no'); 21 | } 22 | 23 | sub test_group { 24 | my ($from, $to) = @_; 25 | test_pair($from, $to); 26 | test_pair(uc($from), $to); 27 | } 28 | 29 | test_group('apt::get::allowunauthenticated', 'cupt::console::allow-untrusted'); 30 | test_group('apt::get::assume-yes', 'cupt::console::assume-yes'); 31 | test_group('apt::get::automaticremove', 'cupt::resolver::auto-remove'); 32 | test_group('apt::get::purge', 'cupt::worker::purge'); 33 | 34 | -------------------------------------------------------------------------------- /test/t/query/download/metadata/basic.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 1; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | sub compose_release_dict { 6 | my ($packages, $sources) = @_; 7 | return { 8 | 'packages' => $packages, 9 | 'sources' => $sources, 10 | 'location' => 'remote', 11 | }; 12 | } 13 | 14 | my $cupt = setup( 15 | 'releases' => [ 16 | compose_release_dict([ 17 | compose_package_record('abc', 1), 18 | compose_package_record('def', 2) 19 | ], [ 20 | compose_package_record('rtg', 3), 21 | compose_package_record('bnm', 4) 22 | ]) 23 | ] 24 | ); 25 | 26 | subtest "simple update" => sub { 27 | check_exit_code("$cupt show abc", 0, 'no packages available at this point'); 28 | 29 | check_exit_code("$cupt update", 1, 'metadata update succeeded'); 30 | 31 | check_exit_code("$cupt show abc", 1, 'first binary package'); 32 | check_exit_code("$cupt show def", 1, 'second binary package'); 33 | check_exit_code("$cupt showsrc rtg", 1, 'first source package'); 34 | check_exit_code("$cupt showsrc bnm", 1, 'second binary package'); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /test/t/query/download/metadata/common.pl: -------------------------------------------------------------------------------- 1 | use IPC::Run3; 2 | use Test::Dir; 3 | 4 | sub check_exit_code { 5 | my ($command, $expected_success, $desc) = @_; 6 | my $checker = $expected_success ? \&is : \&isnt; 7 | 8 | my $output; 9 | run3($command, \undef, \$output, \$output); 10 | $checker->($?, 0, $desc) or diag($output); 11 | } 12 | 13 | sub check_no_partial_files { 14 | my $partial_dir = 'var/lib/cupt/lists/partial'; 15 | dir_exists_ok($partial_dir); 16 | dir_empty_ok($partial_dir); 17 | } 18 | 19 | 1; 20 | 21 | -------------------------------------------------------------------------------- /test/t/query/download/metadata/compare-with-existing/stop-if-equal.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 4; 2 | use Storable qw(dclone); 3 | 4 | require(get_rinclude_path('../common')); 5 | 6 | sub get_corrupter { 7 | my $kind_regex = shift; 8 | return sub { 9 | my (undef, $kind, undef, $content) = @_; 10 | return $content unless ($kind =~ $kind_regex); 11 | return '30urjak;sdas'; 12 | } 13 | } 14 | 15 | sub prepare { 16 | my $release = shift; 17 | $release->{location} = 'remote'; 18 | return setup('releases' => [ dclone($release) ]); 19 | } 20 | 21 | sub check { 22 | my ($cupt, $release, $corrupter, $expected_result, $desc) = @_; 23 | $release = dclone($release); 24 | 25 | $release->{hooks}->{diff}->{write} = $corrupter; 26 | update_remote_releases($release); 27 | 28 | my $output = stdall("$cupt update -o cupt::languages::indexes=de,nl"); 29 | my $checker = $expected_result ? \&unlike : \&like; 30 | $checker->($output, qr/W: failed to download/, $desc) or diag($output); 31 | } 32 | 33 | sub test { 34 | my ($release, $kind_regex, $desc) = @_; 35 | my $corrupter = get_corrupter($kind_regex); 36 | 37 | subtest $desc => sub { 38 | my $cupt = prepare($release); 39 | check($cupt, $release, $corrupter, 0, 'corrupted remote file, no local file'); 40 | check($cupt, $release, undef, 1, 'fine remote file, no local file'); 41 | check($cupt, $release, $corrupter, 1, 'corrupted remote file but local file present'); 42 | } 43 | } 44 | 45 | test({ 'packages' => [] }, qr/Packages/, 'packages'); 46 | test({ 'sources' => [] }, qr/Sources/, 'sources'); 47 | test({ 'packages' => [], 'translations' => { 'de' => [] } }, 48 | qr/Translation/, 'translations'); 49 | test({ 'packages' => [], 'translations' => { 'de' => [], 'nl' => [] } }, 50 | qr/Translation-de/, 'one of translations'); 51 | 52 | -------------------------------------------------------------------------------- /test/t/query/download/metadata/easy-entry.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3 + 3; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | sub init_cupt { 6 | my ($archive, $component, $line_hook) = @_; 7 | return setup('releases' => [{ 8 | 'archive' => $archive, 9 | 'components' => [{ 10 | 'component' => $component, 11 | 'packages' => [ compose_package_record('p3', 2) ], 12 | }], 13 | 'line-hook' => $line_hook, 14 | 'location' => 'remote', 15 | } 16 | ]); 17 | } 18 | 19 | sub get_desc { 20 | my ($archive, $component, undef) = @_; 21 | return "'$archive', '$component'"; 22 | } 23 | 24 | sub test_success { 25 | my @init_params = @_; 26 | my $desc = get_desc(@init_params); 27 | subtest "$desc => successful fetch" => sub { 28 | my $cupt = init_cupt(@init_params); 29 | check_exit_code("$cupt update", 1, 'update succeeded'); 30 | check_exit_code("$cupt show p3", 1, 'package existance (good)'); 31 | check_exit_code("$cupt show p5", 0, 'package existance (bad)'); 32 | }; 33 | } 34 | 35 | sub test_failure { 36 | my @init_params = @_; 37 | my $desc = get_desc(@init_params); 38 | subtest "$desc => not present" => sub { 39 | my $cupt = init_cupt(@init_params); 40 | check_exit_code("$cupt update", 0, 'update failed'); 41 | check_exit_code("$cupt show p3", 0, 'package existance'); 42 | }; 43 | } 44 | 45 | sub set_arc_and_comp_hook { 46 | my $repl = shift; 47 | return sub { 48 | my $line = shift; 49 | $line =~ s/arc9.*/$repl/; 50 | return $line; 51 | }; 52 | }; 53 | 54 | test_success('arc9', 'qwe', undef); 55 | test_failure('arc9', 'qwe', set_arc_and_comp_hook('arc9')); 56 | test_failure('arc9', 'qwe', set_arc_and_comp_hook('arc9/')); 57 | 58 | test_success('arc9/', '', undef); 59 | test_failure('arc9/', '', set_arc_and_comp_hook('arc9')); 60 | test_failure('arc9/', '', set_arc_and_comp_hook('arc9 qwe')); 61 | 62 | -------------------------------------------------------------------------------- /test/t/query/download/metadata/several-components.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3+2+4; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | sub compose_release { 6 | my @components = map { {'component' => $_, 'packages' => compose_package_record($_, 0)} } @_; 7 | return { 8 | 'location' => 'remote', 9 | 'components' => \@components, 10 | }; 11 | } 12 | 13 | sub check_packages_presence { 14 | my ($cupt, $packages) = @_; 15 | 16 | my $output = stdall("$cupt show '*'"); 17 | my @got_packages = ($output =~ m/^Package: (.*?)$/mg); 18 | is_deeply(\@got_packages, $packages, "expected packages: @$packages") 19 | or diag($output); 20 | } 21 | 22 | sub test { 23 | my ($local_components, $remote_components, $exp_update_result, $exp_components, $comment) = @_; 24 | subtest $comment => sub { 25 | my $cupt = setup( 26 | 'releases' => [ compose_release(split(/,/,$local_components)) ], 27 | ); 28 | update_remote_releases(compose_release(split(/,/,$remote_components))); 29 | 30 | check_exit_code("$cupt update", $exp_update_result, "update result is $exp_update_result"); 31 | check_no_partial_files(); 32 | check_packages_presence($cupt, [split(/,/,$exp_components)]); 33 | } 34 | } 35 | 36 | test('c1', 'c1' => 1, 'c1', 'one component locally and remotely'); 37 | test('c1,c2', 'c1,c2' => 1, 'c1,c2', 'two components locally and remotely'); 38 | test('ca,cb,cd,cz,ccc', 'ca,cb,cd,cz,ccc' => 1, 'ca,cb,ccc,cd,cz', 'five components locally and remotely'); 39 | 40 | test('c1', 'c1,c2' => 1, 'c1', 'one component locally, two remotely'); 41 | test('c1,c3', 'c1,c2,c3,c4' => 1, 'c1,c3', 'two components locally, four remotely'); 42 | 43 | test('c1', '' => 0, '', 'no components remotely'); 44 | test('c1', 'c2' => 0, '', 'remote component does not match'); 45 | test('c1,c2', 'c2,c3' => 0, 'c2', 'partial remote component match'); 46 | test('c1,c2,c3,c4', 'c1,c2,c4' => 0, 'c1,c2,c4' => 'all except one components match'); 47 | 48 | -------------------------------------------------------------------------------- /test/t/query/download/metadata/translations.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | sub get_translation_record_for_lang { 6 | my $lang = shift; 7 | return compose_translation_record('xyz', $lang, '789def', 'whatever') 8 | } 9 | 10 | sub fetch_pair_if_translation { 11 | my $tr_path_prefix = '_aaa_ccc_i18n_Translation'; 12 | 13 | return () if m/index0/; 14 | my ($lang) = m/$tr_path_prefix-(.*)/; 15 | return () unless defined($lang); 16 | 17 | return ($lang => $_); 18 | } 19 | 20 | sub test { 21 | my ($order, $expected_translations) = @_; 22 | my $cupt = setup( 23 | 'releases' => [{ 24 | 'location' => 'remote', 25 | 'archive' => 'aaa', 26 | 'component' => 'ccc', 27 | 'packages' => [], 28 | 'translations' => { 29 | 'en' => [ get_translation_record_for_lang('en') ], 30 | 'fi' => [ get_translation_record_for_lang('fi') ], 31 | 'de' => [ get_translation_record_for_lang('de') ], 32 | 'pt' => [ get_translation_record_for_lang('pt') ], 33 | 'pt_BR' => [ get_translation_record_for_lang('pt_BR') ], 34 | } 35 | }] 36 | ); 37 | 38 | my $dir = 'var/lib/cupt/lists'; 39 | 40 | subtest "order: $order" => sub { 41 | check_exit_code("$cupt update -o cupt::languages::indexes=$order", 1, 'metadata update succeeded'); 42 | check_no_partial_files(); 43 | 44 | my @got_files = glob("$dir/*"); 45 | my %got_translations = map(&fetch_pair_if_translation, @got_files); 46 | my @got_langs = sort keys %got_translations; 47 | is_deeply(\@got_langs, $expected_translations, 'downloaded right translation files'); 48 | while (my ($lang, $path) = each %got_translations) { 49 | is(`cat $path`, get_translation_record_for_lang($lang)."\n", "content is right for translation '$lang'"); 50 | } 51 | } 52 | } 53 | 54 | $ENV{LC_ALL} = "fi_FI.UTF-8"; 55 | test("en,environment", [qw(en fi)]); 56 | test("pt_BR,fi", [qw(fi pt pt_BR)]); 57 | 58 | -------------------------------------------------------------------------------- /test/t/query/download/source/basic.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $sp = get_default_source_package(); 6 | my $package = $sp->{'package'}; 7 | my $files = $sp->{'files'}; 8 | my ($dsc_file) = grep { m/dsc$/ } (map { $_->{'name'} } @$files); 9 | 10 | sub test { 11 | my ($option_line, $dpkg_source_call_expected) = @_; 12 | 13 | my $output; 14 | subtest "options: '$option_line'" => sub { 15 | my $cupt = prepare($sp); 16 | $output = stdall("$cupt source $option_line $package"); 17 | foreach (@$files) { 18 | check_file($_); 19 | } 20 | if ($dpkg_source_call_expected) { 21 | like($output, qr/\Q[fakes\/dpkg-source]\E -x $dsc_file$/, 'dpkg-source call'); 22 | } else { 23 | unlike($output, qr/dpkg-source/, 'no dpkg-source call'); 24 | } 25 | } or diag($output); 26 | } 27 | 28 | test("", 1); 29 | test("--download-only", 0); 30 | 31 | -------------------------------------------------------------------------------- /test/t/query/download/source/selecting-files.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 4; 2 | use List::Util qw(first); 3 | 4 | require(get_rinclude_path('common')); 5 | 6 | my $sp = get_default_source_package(); 7 | my $package = $sp->{'package'}; 8 | my $files = $sp->{'files'}; 9 | 10 | sub test { 11 | my ($option_line, $expected_file_names_sub) = @_; 12 | 13 | my $cupt = prepare($sp); 14 | my $ec = exitcode("$cupt source --download-only $option_line $package"); 15 | 16 | my (@expected_file_names) = grep(&$expected_file_names_sub, (map { $_->{'name'} } @$files)); 17 | my @got_file_names = glob("$package*"); 18 | 19 | subtest "options: '$option_line'" => sub { 20 | is($ec, 0, "operation was successful"); 21 | is_deeply(\@got_file_names, \@expected_file_names, "downloaded right selection of files"); 22 | foreach my $name (@got_file_names) { 23 | my $record = first { $_->{'name'} eq $name } @$files; 24 | check_file($record); 25 | } 26 | } 27 | } 28 | 29 | test("" => sub { 1 }); 30 | test("--dsc-only" => sub { m/\Q.dsc\E$/ }); 31 | test("--diff-only", => sub { m/\Q.diff.gz\E$/ }); 32 | test("--tar-only", => sub { m/\Q.tar.\E.*$/ }); 33 | 34 | -------------------------------------------------------------------------------- /test/t/query/parsing/dpkg-status/duplicate-package-name.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3; 2 | use IPC::Run3; 3 | 4 | sub setup_from { 5 | my @input = @_; 6 | my @records = map { entail(compose_installed_record(@$_)) } @input; 7 | return setup('dpkg_status' => join("", @records)); 8 | } 9 | 10 | sub test { 11 | my ($name, @installed_data) = @_; 12 | 13 | my $cupt = setup_from(@installed_data); 14 | my $package = $installed_data[0]->[0]; 15 | 16 | my $output; 17 | subtest $name => sub { 18 | run3("$cupt policy $package", \undef, \$output, \$output); 19 | is($?, 0, "exit code"); 20 | like($output, qr/^E: error while merging the version '.*?' for the package '$package'$/m, 'error with package name'); 21 | like($output, qr/^E: more than one installed version per package is not supported$/m, 'error explanation'); 22 | } or diag($output); 23 | } 24 | 25 | test("same version", ['abc', 1], ['abc', 1]); 26 | test("different versions", ['def', 3], ['def', 4]); 27 | test("non-contiguous", ['mnl', 5], ['xyz', 0], ['mnl', 5]); 28 | 29 | -------------------------------------------------------------------------------- /test/t/query/parsing/dpkg-status/no-file.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 1; 2 | use IPC::Run3; 3 | 4 | my $cupt = setup(); 5 | 6 | subtest "the" => sub { 7 | run3("$cupt policy xyz -o dir::state::status=/doesntexist", \undef, \my $stdout, \my $stderr); 8 | isnt($?, 0, 'exit code'); 9 | is($stdout, '', 'no result'); 10 | like($stderr, qr/^E: unable to open the dpkg status file '\/doesntexist': .+$/m, "error message"); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /test/t/query/parsing/dpkg-status/no-package-name.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 9; 2 | 3 | my $sample_record = compose_installed_record('def', 2); 4 | 5 | sub test { 6 | my ($line) = @_; 7 | 8 | my $record = $sample_record; 9 | $record =~ s/.*?\n/$line\n/; 10 | 11 | my $cupt = setup('dpkg_status' => entail($record)); 12 | 13 | $line =~ s/\n/{newline}/g; 14 | like(stdall("$cupt show def"), qr/^E: no package name in the record$/m, "line: '$line'") 15 | or diag($record); 16 | } 17 | 18 | test('Packge: def'); 19 | test('Package: '); 20 | test('Package:'); 21 | test('P: def'); 22 | test('Q: def'); 23 | test('package: def'); 24 | test('PaCKage: def'); 25 | test("\n"); 26 | test(''); 27 | 28 | -------------------------------------------------------------------------------- /test/t/query/parsing/dpkg-status/no-status.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 15*3; 2 | 3 | my $status = 'Status: install ok installed'; 4 | 5 | sub compose_dummy_record { 6 | my ($package) = @_; 7 | return "Package: $package\n$status\n"; 8 | } 9 | 10 | my $record_position; 11 | 12 | sub get_records { 13 | my ($main_record) = @_; 14 | my @dummy_records = map { entail(compose_dummy_record("dum$_")) } (1..$record_position); 15 | return join("", @dummy_records) . $main_record; 16 | } 17 | 18 | sub test { 19 | my ($record, $expected_result) = @_; 20 | 21 | my $installed = get_records(entail("Package: ppp\n$record\n")); 22 | my $cupt = setup('dpkg_status' => $installed); 23 | my $output = stdall("$cupt pkgnames"); 24 | 25 | $record =~ s/\n/{newline}/g; 26 | my $name = "dummy records: $record_position, record: '$record', expected result: $expected_result"; 27 | 28 | subtest $name => sub { 29 | if ($expected_result == 2) { 30 | is($output, "ppp\n"); 31 | } elsif ($expected_result) { 32 | is($output, ''); 33 | } else { 34 | like($output, qr/^E: no status line in the record$/m); 35 | } 36 | } or diag($installed); 37 | } 38 | 39 | my $version = 'Version: 1'; 40 | my $uuu = 'Uuu: www'; 41 | my $prov = 'Provides: qqq'; 42 | 43 | sub test_group() { 44 | test("$status" => 1); 45 | test('' => 1); 46 | test("$version" => 0); 47 | test("$uuu" => 1); 48 | test("$prov" => 1); 49 | test("$status\n$version" => 2); 50 | test("$version\n$status" => 2); 51 | test("$version\n$status\n$uuu" => 2); 52 | test("$version\n$uuu" => 0); 53 | test("$uuu\n$version" => 0); 54 | test("$uuu\n$version\n$status" => 2); 55 | test("$version\n$uuu\n$status" => 2); 56 | test("$prov\n$status" => 1); 57 | test("$prov\n$version" => 0); 58 | test("$status\n$prov\n$uuu" => 1); 59 | } 60 | 61 | $record_position = 0; 62 | test_group(); 63 | $record_position = 1; 64 | test_group(); 65 | $record_position = 44; 66 | test_group(); 67 | 68 | -------------------------------------------------------------------------------- /test/t/query/parsing/dpkg-status/no-version.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 10; 2 | 3 | my $status = 'Status: install ok installed'; 4 | 5 | sub test { 6 | my ($lines, $result) = @_; 7 | my $record = "Package: ppp\n$status\n$lines\n"; 8 | $record =~ s/\n\n/\n/; 9 | my $cupt = setup('dpkg_status' => entail($record)); 10 | 11 | $lines =~ s/\n/{newline}/g; 12 | my $expected_output = $result ? "ppp\n" : ''; 13 | 14 | my $output = stdall("$cupt pkgnames"); 15 | is($output, $expected_output, $lines); 16 | } 17 | 18 | test('Version: 11' => 1); 19 | test('Version: 118888888888888888888888888888888888' => 1); 20 | test('' => 0); 21 | test('Provides: abc' => 0); 22 | test("Version: 11\nProvides: abc" => 1); 23 | test("Provides: abc\nVersion: 11" => 1); 24 | test("SomeTag: some-value\nVersion: 11" => 1); 25 | test("Version: 11\nDepends: qwe" => 1); 26 | test("Enhances: bbb\nVersion: 11" => 1); 27 | test("UUU: yyy\nZzz: www\nVersion: 125\nNnn: ooo" => 1); 28 | 29 | -------------------------------------------------------------------------------- /test/t/query/parsing/extended-states/malformed.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6; 2 | use IPC::Run3; 3 | 4 | sub test { 5 | my ($record, $main_error) = @_; 6 | 7 | my $cupt = setup('extended_states' => $record); 8 | run3("$cupt show xyz", \undef, \undef, \my $errors); 9 | 10 | subtest to_one_line($record) => sub { 11 | like($errors, qr/^E: \Q$main_error\E$/m, 'specific error'); 12 | like($errors, qr/^E: unable to parse extended states$/m, 'general failure message'); 13 | } 14 | } 15 | 16 | sub get_bad_first_tag_error { 17 | my $tag = shift; 18 | return "wrong tag: expected 'Package', got '$tag'"; 19 | } 20 | test("Pkg: xyz\n", get_bad_first_tag_error('Pkg')); 21 | my $valid_ai = "Auto-Installed: 1\n"; 22 | test("Pkg: xyz\n$valid_ai", get_bad_first_tag_error('Pkg')); 23 | test("UYIOa: pm\n", get_bad_first_tag_error('UYIOa')); 24 | 25 | sub get_bad_value_error { 26 | my $value = shift; 27 | return "bad value '$value' (should be 0 or 1) for the package 'abc'"; 28 | } 29 | my $valid_start = "Package: abc\n"; 30 | test("${valid_start}Auto-Installed: 2", get_bad_value_error('2')); 31 | test("${valid_start}Auto-Installed: -5", get_bad_value_error('-5')); 32 | test("${valid_start}Auto-Installed: quwe", get_bad_value_error("quwe")); 33 | 34 | -------------------------------------------------------------------------------- /test/t/query/parsing/relation-expressions/colon-suffix.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 4; 2 | 3 | sub test { 4 | my ($text, $expected_dep) = @_; 5 | 6 | my $cupt = setup('packages' => 7 | [ compose_package_record('abc', 1) . "Depends: $expected_dep\n" ]); 8 | 9 | my $output = stdout("$cupt depends abc"); 10 | like($output, qr/Depends: \Q$expected_dep\E\n/, $text); 11 | } 12 | 13 | test('versionless dependency', 'def:xyz'); 14 | test('versioned dependency', "klm:aaa (>= 1.2.3)"); 15 | test(':any', 'def5:any'); 16 | test(':native', 'kyy13:native'); 17 | 18 | -------------------------------------------------------------------------------- /test/t/query/parsing/relation-expressions/invalid-package-name.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $packages = 8 | entail(compose_package_record('abc', '1') . "Depends: defAxyz\n") . 9 | entail(compose_package_record('abc', '2') . "Depends: klm35!#0er2"); 10 | 11 | my $cupt = TestCupt::setup('packages' => $packages); 12 | 13 | isnt(exitcode("$cupt show abc=1"), 0, "big letters not allowed"); 14 | isnt(exitcode("$cupt show abc=2"), 0, "garbage symbols not allowed"); 15 | 16 | -------------------------------------------------------------------------------- /test/t/query/parsing/relation-expressions/source/build-profiles.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 17; 2 | 3 | my $package = 'pkg'; 4 | 5 | sub compose_bd_record { 6 | my ($bd_line) = @_; 7 | return entail(compose_package_record($package, '0') . "Build-Depends: $bd_line\n"); 8 | } 9 | 10 | sub test { 11 | my ($bd, $bd_parsed, $desc) = @_; 12 | $bd_parsed //= $bd; 13 | 14 | my $cupt = setup('sources' => compose_bd_record($bd)); 15 | 16 | my $output = stdall("$cupt showsrc $package"); 17 | like($output, qr/^Build-Depends: \Q$bd_parsed\E/m, $desc); 18 | } 19 | 20 | test('aa ', undef, '1 positive'); 21 | test('aa ', undef, '1 negative'); 22 | test('aa (>= 2.3) ', undef, 'positive + relation'); 23 | test('aa (<< 2:5.2~pre4) ', undef, 'negative + relation'); 24 | test('aa ', undef, '2 positive'); 25 | test('aa ', 'aa ', '2 positive, whitespaces'); 26 | test('aa< stage2 stage3 > ', 'aa ', '2 positive, more whitespaces'); 27 | test('aa ', undef, '3 negative'); 28 | test('aa (>= 2.3) | bb ', undef, 'positive + complex relation'); 29 | test('aa (>= 2.3) | bb (= 12) ', undef, 'negative + complex relation'); 30 | test('aa [z1] ', undef, 'positive + architecture'); 31 | test('aa [z1 z2] ', undef, 'negative, 2 architectures'); 32 | test('aa [z1] | bb [!z2] ', undef, 'in non-last relation'); 33 | test('aa (= 256) [!z1] ', undef, 'position + relation + negative architecture'); 34 | test('aa ', undef, '2 lists'); 35 | test('aa ', undef, '4 lists'); 36 | test('aa | bb (<< 7:5.4-3) [z1 z2] ', 37 | undef, 'ultimate mix'); 38 | 39 | -------------------------------------------------------------------------------- /test/t/query/parsing/relation-expressions/source/error-reporting.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3 + 5; 2 | 3 | my $package = 'qqw'; 4 | 5 | sub compose_bd_record { 6 | my ($bd_line) = @_; 7 | return entail(compose_package_record($package, '0') . "Build-Depends: $bd_line\n"); 8 | } 9 | 10 | sub test { 11 | my ($bd, $area, $broken_part, $desc) = @_; 12 | 13 | my $cupt = setup('sources' => compose_bd_record($bd)); 14 | 15 | my $output = stdall("$cupt showsrc $package"); 16 | like($output, qr/^E: unable to parse $area \Q'$broken_part'\E/m, $desc); 17 | } 18 | 19 | sub test_af { 20 | my ($bd, $broken_part, $desc) = @_; 21 | test($bd, 'architecture filters', $broken_part, $desc); 22 | } 23 | 24 | sub test_bp { 25 | my ($bd, $broken_part, $desc) = @_; 26 | test($bd, 'build profiles', $broken_part, $desc); 27 | } 28 | 29 | test_af('sdf [armb ', '[armb ', 'unclosed list of architectures'); 30 | test_af('sdf [', '[', 'no architectures'); 31 | test_af('sdf [armb, ppq]', '[armb', 'wrong architecture delimiter'); 32 | 33 | test_bp('rrr ', ' 16; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $cupt = TestCupt::setup(); 8 | 9 | sub test { 10 | my ($relation) = @_; 11 | 12 | my $result = exitcode("$cupt -s satisfy '$relation-'"); 13 | 14 | is($result, 0, "relation '$relation'"); 15 | } 16 | 17 | test('abc'); 18 | test('xyz '); 19 | test('xyz '); 20 | test('abc (= 5)'); 21 | test('abc (<< 6.4)'); 22 | test('abc (>> 6.6)'); 23 | test('abc (> 6.7)'); 24 | test('abc (< 2.m)'); 25 | test('abc (>= 12)'); 26 | test('abc (<= 2.10.8)'); 27 | test('abc (>> 8) '); 28 | test('abc(=5)'); 29 | test('abc (>> 4)'); 30 | test('qw46 (>= 1)'); 31 | test('ww (>= 6)'); 32 | test('qq (= 7 )'); 33 | 34 | -------------------------------------------------------------------------------- /test/t/query/policy/pinning/by-binary-and-source-package-names.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 12; 2 | 3 | require(get_rinclude_path('pinning')); 4 | 5 | sub test { 6 | my ($first_line, $expected_result) = @_; 7 | 8 | test_pinning( 9 | { 10 | 'package' => 'def', 11 | 'version' => 1, 12 | 'package_content' => "Source: defsource\n", 13 | 'package_comment' => 'def/defsource', 14 | 'first_pin_line' => $first_line, 15 | 'pin_expression' => 'version *', 16 | }, 17 | $expected_result 18 | ); 19 | } 20 | 21 | test('Package: def' => 1); 22 | test('Package: d*' => 1); 23 | test('Package: fed' => 0); 24 | test('Packag: def' => -1); 25 | test('Packagedef' => -1); 26 | 27 | test('Source: defsource' => 1); 28 | test('Source: fedsource' => 0); 29 | test('Source: *' => 1); 30 | test('Source: /e/' => 1); 31 | test('Source: /g/' => 0); 32 | test('Souce: defsource' => -1); 33 | test('Source%&(&--^*NO CARRIER' => -1); 34 | 35 | -------------------------------------------------------------------------------- /test/t/query/policy/pinning/by-hostname.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 34; 2 | 3 | require(get_rinclude_path('pinning')); 4 | 5 | my $scheme; 6 | my $hostname; 7 | 8 | sub test { 9 | my ($pin_expression, $expected_result) = @_; 10 | 11 | test_pinning( 12 | { 13 | 'package' => 'qas', 14 | 'version' => 1, 15 | 'package_comment' => "scheme: $scheme, hostname: $hostname", 16 | 'first_pin_line' => 'Package: *', 17 | 'pin_expression' => $pin_expression, 18 | 'release_properties' => { 19 | 'scheme' => $scheme, 20 | 'hostname' => $hostname, 21 | }, 22 | }, 23 | $expected_result 24 | ); 25 | } 26 | 27 | $scheme = 'http'; 28 | foreach ('', '/packages', '/big/list/of/server/directories') { 29 | $hostname = 'ftp.eu.debian.org' . $_; 30 | test('origin "ftp.eu.debian.org"' => 1); 31 | test('origin "*.debian.org"' => 1); 32 | test('origin "*.knoppix.net"' => 0); 33 | test('origin "/org/"' => 1); 34 | test('origin "\'"' => 0); 35 | test('origin=*' => -1); 36 | test('origin abc' => 0); 37 | test('origi =abc' => -1); 38 | } 39 | 40 | $hostname = 'xx.yy.net/debian'; 41 | test('origin "/yy/"' => 1); 42 | test('origin "/debian/"' => 0); 43 | 44 | $hostname = 'home/user/repo'; 45 | foreach (qw(file copy)) { 46 | $scheme = $_; 47 | test('origin ""' => 1); 48 | test('origin "*home*"' => 0); 49 | test('origin "/./"' => 0); 50 | } 51 | 52 | $scheme = 'ftp'; 53 | test('origin ""' => 0); 54 | test('origin "*home*"' => 1); 55 | 56 | -------------------------------------------------------------------------------- /test/t/query/policy/pinning/by-release-properties.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 40; 2 | 3 | require(get_rinclude_path('pinning')); 4 | 5 | sub test { 6 | my ($property, $expected_result) = @_; 7 | 8 | test_pinning( 9 | { 10 | 'package' => 'zzz', 11 | 'version' => 3, 12 | 'release_properties' => { 13 | 'archive' => 'aaaaa', 14 | 'codename' => 'ccccc', 15 | 'label' => 'SuperSecret', 16 | 'component' => 'contrib', 17 | 'version' => '6.2', 18 | 'vendor' => 'Mint', 19 | }, 20 | 'package_content' => '', 21 | 'package_comment' => '', 22 | 'first_pin_line' => 'Package: *', 23 | 'pin_expression' => "release $property", 24 | }, 25 | $expected_result 26 | ); 27 | } 28 | 29 | test('a=aaaaa' => 1); 30 | test('a=aaaa' => 0); 31 | test('a=aaaaaa' => 0); 32 | test('aysid' => -1); 33 | 34 | test('n=ccccc' => 1); 35 | test('n=cccc1' => 0); 36 | test('n=XXX' => 0); 37 | test('nnjpi' => -1); 38 | 39 | test('l=SuperSecret' => 1); 40 | test('l=Secret' => 0); 41 | test('l=supersecret' => 0); 42 | test('l=/upe/' => 1); 43 | test('l=/upp/' => 0); 44 | test('l=*Secret*' => 1); 45 | test('l=Secret*' => 0); 46 | test('laskdfasdfl=%&^...*' => -1); 47 | 48 | test('c=contrib' => 1); 49 | test('c=main' => 0); 50 | test('c=non-free' => 0); 51 | test('c=garbagfzz' => 0); 52 | test('c=/main|contrib/' => 1); 53 | test('c=/+/' => -1); 54 | 55 | test('v=6.2' => 1); 56 | test('v=6.1' => 0); 57 | test('v=6' => 0); 58 | test('v=6.*' => 1); 59 | test('v=7.*' => 0); 60 | test('v=2*' => 0); 61 | test('v=/2/' => 1); 62 | test('v=/3/' => 0); 63 | test('vv=z' => -1); 64 | 65 | test('o=Mint' => 1); 66 | test('o=Debian' => 0); 67 | test('o=*int' => 1); 68 | test('o=/u/' => 0); 69 | test('ooo=ppp' => -1); 70 | 71 | test('=Mint' => -1); 72 | test('m=/u/' => -1); 73 | test('x=*' => -1); 74 | test('bb=f' => -1); 75 | 76 | -------------------------------------------------------------------------------- /test/t/query/policy/pinning/by-version-string.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 22; 2 | 3 | require(get_rinclude_path('pinning')); 4 | 5 | sub test { 6 | my ($version, $pin_expression, $match_expected) = @_; 7 | 8 | test_pinning( 9 | { 10 | 'package' => 'abc', 11 | 'version' => $version, 12 | 'package_content' => '', 13 | 'package_comment' => $version, 14 | 'first_pin_line' => 'Package: abc', 15 | 'pin_expression' => "version $pin_expression" 16 | }, 17 | $match_expected 18 | ); 19 | } 20 | 21 | test('2', '2', 1); 22 | test('2', '3', 0); 23 | test('20', '2', 0); 24 | test('20', '0', 0); 25 | 26 | test('20', '2*', 1); 27 | test('20', '*', 1); 28 | test('20', '3*', 0); 29 | test('20', '?0', 1); 30 | test('20', '?1', 0); 31 | test('20', '2?', 1); 32 | test('20', '??', 1); 33 | test('20', '?', 0); 34 | 35 | test('20', '/*/', -1); 36 | test('20', '/.*/', 1); 37 | test('20', '/./', 1); 38 | test('20', '/3./', 0); 39 | test('1.2.6', '/1\..\.6/', 1); 40 | test('1.2.6', '/1\..\.7/', 0); 41 | test('200', '/2+0/', 1); 42 | test('200', '/2+1/', 0); 43 | test('200', '/20+/', 1); 44 | test('200', '/20++/', -1); 45 | 46 | -------------------------------------------------------------------------------- /test/t/query/policy/pinning/combined-filters.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 14; 2 | 3 | require(get_rinclude_path('pinning')); 4 | 5 | sub test { 6 | my ($first_pin_line, $pin_expression, $expected_result) = @_; 7 | 8 | test_pinning( 9 | { 10 | 'package' => 'mno', 11 | 'version' => '3.4', 12 | 'package_comment' => '', 13 | 'release_properties' => { 14 | 'archive' => 'green', 15 | 'codename' => 'blue', 16 | }, 17 | 'first_pin_line' => $first_pin_line, 18 | 'pin_expression' => $pin_expression, 19 | }, 20 | $expected_result 21 | ); 22 | } 23 | 24 | test('Package: mno', 'version 3.4' => 1); 25 | test('Package: mnp', 'version 3.4' => 0); 26 | test('Package: mno', 'version 3.5' => 0); 27 | 28 | test('Package: *', 'release a=green, n=blue' => 1); 29 | test('Package: *', 'release a=red, n=blue' => 0); 30 | test('Package: *', 'release a=green, n=red' => 0); 31 | 32 | test('Package: mno', 'release a=green, n=blue' => 1); 33 | test('Package: xno', 'release a=green, n=blue' => 0); 34 | 35 | test('Package: xyz mno', 'version *' => 1); 36 | test('Package: mno xxx *', 'version *' => 1); 37 | test('Package: *m* *a*', 'version *' => 1); 38 | test('Package: *n* nop *o*', 'version *' => 1); 39 | test('Package: lll kkk', 'version *' => 0); 40 | test('Package: *x* *y* *p* *q*', 'version *' => 0); 41 | 42 | -------------------------------------------------------------------------------- /test/t/query/policy/pinning/installed-version-pinned-by-version-string.t: -------------------------------------------------------------------------------- 1 | # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=764238 2 | 3 | use TestCupt; 4 | use Test::More tests => 1; 5 | 6 | use warnings; 7 | use strict; 8 | 9 | my $cupt = TestCupt::setup( 10 | 'dpkg_status' => 11 | entail(compose_installed_record('pp', '1.2.3-4')), 12 | 'preferences' => 13 | compose_version_pin_record('pp', '1.2.3-4', 11782), 14 | ); 15 | 16 | my $output = stdout("$cupt policy pp"); 17 | 18 | is(get_version_priority($output, '1.2.3-4'), 11782) or diag($output); 19 | 20 | -------------------------------------------------------------------------------- /test/t/query/policy/pinning/multiple-pin-records.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 8; 2 | 3 | require(get_rinclude_path('pinning')); 4 | 5 | sub test { 6 | my ($pin_records, $expected_result) = @_; 7 | 8 | test_pinning_multiple_records( 9 | { 10 | 'package' => 'rrr', 11 | 'version' => '1.2-3', 12 | 'package_comment' => '', 13 | 'release_properties' => { 14 | 'label' => 'Latest and greatest', 15 | 'vendor' => 'Earth', 16 | }, 17 | }, 18 | $pin_records, 19 | $expected_result 20 | ); 21 | } 22 | 23 | my $any_record = [ 'Package: *', 'version *' ]; 24 | my $no_record = [ 'Package: uuu', 'version *' ]; 25 | my $broken_record = [ 'T&(', '%%%*' ]; 26 | 27 | test([ $any_record, $any_record ] => 1); 28 | test([ $no_record, $any_record ] => 2); 29 | test([ $no_record, $no_record, $any_record ] => 3); 30 | test([ $any_record, $broken_record, $any_record ] => -1); 31 | test([ $no_record, $no_record ] => 0); 32 | test([ $any_record, $no_record ] => 1); 33 | 34 | test( 35 | [ 36 | [ 'Package: rrr', 'version 1.1-3' ], 37 | [ 'Package: ppp', 'release o=Earth' ], 38 | [ 'Package: rrr', 'release o=Earth' ], 39 | ] 40 | => 3 41 | ); 42 | 43 | test( 44 | [ 45 | $no_record, 46 | [ 'Package: rrr', 'release l=*great*' ], 47 | $any_record 48 | ] 49 | => 2 50 | ); 51 | 52 | -------------------------------------------------------------------------------- /test/t/query/policy/sources-list-syntax/common.pl: -------------------------------------------------------------------------------- 1 | use IPC::Run3; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | sub run_case_raw { 7 | my ($desc, $hook, $success, $output_regex, $error_regex) = @_; 8 | 9 | my $source_list_lines = ""; 10 | my $wrapped_hook = sub { 11 | my $result = $hook->($_[0]); 12 | $source_list_lines .= $result; 13 | return $result; 14 | }; 15 | 16 | my $re = {'archive' => 'bozon', 'sources' => [], 'line-hook' => $wrapped_hook}; 17 | my $cupt = setup('releases' => [$re]); 18 | run3("$cupt policysrc", \undef, \my $stdout, \my $stderr); 19 | my $exitcode = $?; 20 | 21 | my $orig_stdall = $stdout . $stderr; 22 | $stdout =~ s/^Source files:$//m; # only keep release lines 23 | $stderr =~ s/^.*signature.*$//mg; # ignore a warning no longer suppressed by the default option 24 | 25 | subtest $desc => sub { 26 | if ($success) { 27 | is($exitcode, 0, 'success'); 28 | } else { 29 | isnt($exitcode, '0', 'failure'); 30 | } 31 | 32 | my $presence_regex = qr/bozon/; 33 | if (defined($output_regex)) { 34 | like($stdout, $presence_regex, 'output line is present'); 35 | like($stdout, $output_regex, 'output line is correct'); 36 | } else { 37 | unlike($stdout, $presence_regex, 'output line is not present'); 38 | } 39 | 40 | if (defined($error_regex)) { 41 | like($stderr, $error_regex, 'errors/warnings'); 42 | } else { 43 | like($stderr, qr/\n*/, 'no errors/warnings'); 44 | } 45 | } or diag("In:\n\n$source_list_lines\n\nOut:\n\n$orig_stdall"); 46 | } 47 | 48 | 1; 49 | 50 | -------------------------------------------------------------------------------- /test/t/query/policy/sources-list-syntax/options.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6 + 7 + 1; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | sub run_case { 6 | my ($opts, $success, $recognised, $error_regex) = @_; 7 | 8 | my $hook = sub { 9 | my $line = shift; 10 | $line =~ s/\[.*\]//; 11 | $line =~ s/ / $opts/; 12 | return $line; 13 | }; 14 | 15 | my $desc = "$opts => ($success, $recognised)"; 16 | my $output_regex = $recognised ? qr// : undef; 17 | run_case_raw($desc, $hook => $success, $output_regex, $error_regex); 18 | }; 19 | 20 | sub test_good { 21 | run_case(@_, 1, 1, undef); 22 | } 23 | 24 | sub test_unrecognised { 25 | run_case(@_, 1, 0, qr/^W: no release file present/m); 26 | } 27 | 28 | sub test_bad { 29 | my ($line, $message) = @_; 30 | my $regex = qr/^E: \Q$message\E\n.*\nE: unable to parse the sources list\n/s; 31 | run_case($line, 0, 0, $regex); 32 | } 33 | 34 | test_good(''); 35 | test_good('[ abc=xcv ]'); 36 | test_good('[ abc=def uio=a8 ]'); 37 | test_good('[ abc=aaa,bbb,7 ]'); 38 | test_good('[ YuP=nMp ]'); 39 | test_good('[abc=xcv]'); 40 | 41 | test_unrecognised('[]'); 42 | test_unrecognised('{ x=0 }'); 43 | test_unrecognised('{ x=0 ]'); 44 | test_unrecognised(']'); 45 | test_unrecognised('['); 46 | test_unrecognised('[ x=0 }'); 47 | test_unrecognised('[ xxx=w'); 48 | 49 | test_bad('[ abc ]', "no key-value separator ('=') in the option token 'abc'"); 50 | 51 | -------------------------------------------------------------------------------- /test/t/query/rdepends/alphabetic-package-name-order.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 4; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $packages = 8 | entail(compose_package_record('xyz', '100')) . 9 | entail(compose_package_record('zzz', '1') . "Depends: xyz\n") . 10 | entail(compose_package_record('xxx', '2') . "Depends: xyz\n") . 11 | entail(compose_package_record('yyy', '10') . "Depends: xyz\n"); 12 | 13 | my $cupt = TestCupt::setup('packages' => $packages); 14 | 15 | sub get_rdepends_lines { 16 | my ($output) = @_; 17 | my @lines = split(/\n/, $output); 18 | shift @lines; 19 | return @lines; 20 | } 21 | 22 | my $output = stdout("$cupt rdepends xyz"); 23 | my @lines = get_rdepends_lines($output); 24 | 25 | is(scalar @lines, 3, "3 answers") or 26 | return diag($output); 27 | like($lines[0], qr/xxx/, "first line is 'xxx'"); 28 | like($lines[1], qr/yyy/, "second line is 'yyy'"); 29 | like($lines[2], qr/zzz/, "third line is 'zzz'"); 30 | 31 | -------------------------------------------------------------------------------- /test/t/query/rdepends/basic.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $packages = 8 | entail(compose_package_record('abc', '1')) . 9 | entail(compose_package_record('abc', '2') . "Depends: xyz\n") . 10 | entail(compose_package_record('xyz', '3')); 11 | 12 | my $cupt = TestCupt::setup('packages' => $packages); 13 | 14 | my $output = stdout("$cupt rdepends xyz"); 15 | 16 | unlike($output, qr/abc 1/, "abc 1 doesn't depend on xyz"); 17 | like($output, qr/abc 2/, "abc 2 does depend on xyz"); 18 | 19 | -------------------------------------------------------------------------------- /test/t/query/rdepends/multiple-package-versions.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $packages = 8 | entail(compose_package_record('abc', '1') . "Depends: xyz\n") . 9 | entail(compose_package_record('abc', '2') . "Depends: xyz (>= 20)\n") . 10 | entail(compose_package_record('xyz', '10')) . 11 | entail(compose_package_record('xyz', '20')) . 12 | entail(compose_package_record('xyz', '30')); 13 | 14 | my $cupt = TestCupt::setup('packages' => $packages); 15 | 16 | sub get_output { 17 | my ($argument) = @_; 18 | return stdout("$cupt rdepends $argument"); 19 | } 20 | 21 | sub get_rdepends_lines { 22 | my ($output) = @_; 23 | my @lines = split(/\n/, $output); 24 | shift @lines; 25 | return @lines; 26 | } 27 | 28 | subtest "not satisfying versionful" => sub { 29 | my $output = get_output('xyz=10'); 30 | my @lines = get_rdepends_lines($output); 31 | 32 | is(scalar @lines, 1, "1 answer") or 33 | return diag($output); 34 | like($lines[0], qr/abc 1/, "abc 1 present"); 35 | }; 36 | 37 | subtest "satisfying versionful" => sub { 38 | my $output = get_output('xyz=30'); 39 | my @lines = get_rdepends_lines($output); 40 | 41 | is(scalar @lines, 2, "2 answers") or 42 | return diag($output); 43 | like($lines[0], qr/abc 1/, "first line is 'abc 1'"); 44 | like($lines[1], qr/abc 2/, "second line is 'abc 2'"); 45 | }; 46 | 47 | -------------------------------------------------------------------------------- /test/t/query/repo-signatures/common.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | my $apt_keyring_dir = 'etc/apt/trusted.gpg.d/'; 5 | my $apt_keyring_file = 'etc/apt/trusted.gpg'; 6 | 7 | sub bad_signer { 8 | return 'garbage'; 9 | } 10 | 11 | sub no_signer { 12 | return undef; 13 | } 14 | 15 | sub link_keyring { 16 | my ($source, $target) = @_; 17 | if (defined $source) { 18 | symlink($source => $target); 19 | } else { 20 | unlink($target); 21 | } 22 | } 23 | 24 | sub get_variant_filter_hook { 25 | my $variants = shift; 26 | return sub { 27 | my ($variant, undef, undef, $content) = @_; 28 | my $found = grep { $variant eq $_ } @$variants; 29 | return $found ? $content : undef; 30 | } 31 | } 32 | 33 | sub get_output { 34 | my %params = %{shift()}; 35 | 36 | my $keyrings = $params{'keyrings'}//[]; 37 | my $signer = $params{'signer'}; 38 | my $sign_variants = $params{'sign-variants'}//['orig','detached']; 39 | my $hooks = $params{'hooks'}//[]; 40 | my $trusted = $params{'trusted'}//'check'; 41 | 42 | my $cupt = setup( 43 | 'releases' => [ 44 | { 45 | 'packages' => [ compose_package_record('p', 1) ], 46 | 'trusted' => $trusted, 47 | 'hooks' => { 48 | 'sign' => { 49 | 'input' => get_variant_filter_hook($sign_variants), 50 | 'convert' => $signer, 51 | @$hooks, 52 | }, 53 | }, 54 | } 55 | ] 56 | ); 57 | 58 | mkdir $apt_keyring_dir or die; 59 | link_keyring($keyrings->[0] => $apt_keyring_file); 60 | link_keyring($keyrings->[1] => "$apt_keyring_dir/first.gpg"); 61 | link_keyring($keyrings->[2] => "$apt_keyring_dir/second.gpg"); 62 | 63 | return stdall("$cupt show 'trusted()'"); 64 | } 65 | 66 | -------------------------------------------------------------------------------- /test/t/query/search/fse/FSE.pl: -------------------------------------------------------------------------------- 1 | use IPC::Run3; 2 | 3 | use strict; 4 | use warnings FATAL => 'all'; 5 | 6 | sub get_binary_search_result { 7 | my ($cupt, $pattern) = @_; 8 | 9 | my $out = ''; 10 | run3("$cupt search --fse '$pattern'", \undef, \$out, \$out); 11 | my @sout = split(/\n/, $out); 12 | s/ -.*// for @sout; 13 | 14 | return ($out, @sout); 15 | } 16 | 17 | sub get_source_search_result { 18 | my ($cupt, $pattern) = @_; 19 | 20 | my $out = ''; 21 | run3("$cupt showsrc '$pattern'", \undef, \$out, \$out); 22 | my @sout = ($out =~ m/^Package: (.*)$/mg); 23 | 24 | return ($out, @sout); 25 | } 26 | 27 | sub test_fse_pattern { 28 | my ($cupt, $is_binary, $pattern, @expected) = @_; 29 | 30 | @expected = sort @expected; 31 | 32 | my $out; 33 | my @sout; 34 | if ($is_binary) { 35 | ($out, @sout) = get_binary_search_result($cupt, $pattern); 36 | } else { 37 | ($out, @sout) = get_source_search_result($cupt, $pattern); 38 | } 39 | 40 | is_deeply(\@sout, \@expected, "search of '$pattern' returns '@expected'") or 41 | diag($out); 42 | } 43 | 44 | sub eis { 45 | my $cupt = shift; 46 | test_fse_pattern($cupt, 1, @_); 47 | } 48 | 49 | sub eis_source { 50 | my $cupt = shift; 51 | test_fse_pattern($cupt, 0, @_); 52 | } 53 | 54 | sub pn { 55 | return 'package:name(' . $_[0] . ')'; 56 | } 57 | 58 | 1; 59 | 60 | -------------------------------------------------------------------------------- /test/t/query/search/fse/algebraic.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 17 + 4; 2 | 3 | require(get_rinclude_path('FSE')); 4 | 5 | my $installed = [ 6 | compose_installed_record('a', '1') , 7 | compose_installed_record('b', '2') , 8 | compose_installed_record('c', '3') , 9 | compose_installed_record('d', '4') , 10 | ]; 11 | my $cupt = TestCupt::setup('dpkg_status' => $installed); 12 | 13 | my $pa = pn('a'); 14 | my $pb = pn('b'); 15 | my $pc = pn('c'); 16 | my $pnone = pn('none'); 17 | 18 | eis($cupt, "and($pa)", qw(a)); 19 | eis($cupt, "and($pa,$pa)", qw(a)); 20 | eis($cupt, "$pa&$pb", ()); 21 | 22 | eis($cupt, "$pa|$pb", qw(a b)); 23 | eis($cupt, "or($pa)", qw(a)); 24 | eis($cupt, "or($pa,$pa)", qw(a)); 25 | 26 | eis($cupt, "not($pa)", qw(b c d)); 27 | eis($cupt, "not($pnone)", qw(a b c d)); 28 | eis($cupt, "not(package:name(.*))", ()); 29 | eis($cupt, "not($pa|$pb)", qw(c d)); 30 | 31 | eis($cupt, "xor($pa,$pb)", qw(a b)); 32 | eis($cupt, "xor($pa,$pa)", ()); 33 | eis($cupt, "xor($pa|$pb,$pb|$pc)", qw(a c)); 34 | 35 | eis($cupt, "or($pa|$pb) & or($pb|$pc)", qw(b)); 36 | eis($cupt, "or(or($pa,$pb), or($pb,$pc))", qw(a b c)); 37 | eis($cupt, "and(not($pa), not($pb), not($pc))", qw(d)); 38 | 39 | eis($cupt, "$pa & with(_v, $pa, _v)", qw(a)); 40 | 41 | 42 | eis($cupt, "$pa | $pb | $pc", qw(a b c)); 43 | eis($cupt, "$pa & $pb & $pc", qw()); 44 | eis($cupt, "$pa & $pb | $pc", qw(c)); 45 | eis($cupt, "$pa | $pb & $pc", qw(a)); 46 | 47 | -------------------------------------------------------------------------------- /test/t/query/search/fse/binary-to-source.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6; 2 | 3 | require(get_rinclude_path('FSE')); 4 | 5 | my $sources = [ 6 | compose_package_record('ss', '1') . "Build-Depends: bd1\n" , 7 | compose_package_record('ss', '2') . "Build-Depends: bd2\n" , 8 | ]; 9 | 10 | my $packages = [ 11 | compose_package_record('a', '1') . "Source: ss\n" , 12 | compose_package_record('b', '3') . "Source: ss\n" , 13 | compose_package_record('c', '4') . "Source: ss (2)\n" , 14 | compose_package_record('d', '1') . "Source: diffs\n" , 15 | compose_package_record('bd1', '0') , 16 | compose_package_record('bd2', '0') , 17 | ]; 18 | 19 | my $cupt = setup('packages' => $packages, 'sources' => $sources); 20 | 21 | sub bd_bts { 22 | return 'build-depends(binary-to-source(' . $_[0] . '))'; 23 | } 24 | 25 | eis($cupt, bd_bts('Pn(a)'), qw(bd1)); 26 | eis($cupt, bd_bts('Pn(b)'), ()); 27 | eis($cupt, bd_bts('Pn(c)'), qw(bd2)); 28 | eis($cupt, bd_bts('Pn(d)'), ()); 29 | eis($cupt, bd_bts('Pn(missing)'), ()); 30 | eis($cupt, bd_bts('Pn(a)|Pn(c)'), qw(bd1 bd2)); 31 | 32 | -------------------------------------------------------------------------------- /test/t/query/search/fse/binary-version-properties.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3+3+3+2+1+3+4; 2 | 3 | require(get_rinclude_path('FSE')); 4 | 5 | my @installed; 6 | push @installed, compose_installed_record('xx', '1') . < \@installed, 23 | 'packages' => [ compose_package_record('zz', '1') . "Important: yes\n" ], 24 | ); 25 | 26 | eis($cupt, 'installed', qw(xx yy)); 27 | eis($cupt, 'not(installed)', qw(zz)); 28 | eis($cupt, 'i', qw(xx yy)); 29 | 30 | eis($cupt, 'source-package(nn)', qw(xx)); 31 | eis($cupt, 'source-package(yy)', qw(yy)); 32 | eis($cupt, 'sp(yy)', qw(yy)); 33 | 34 | eis($cupt, 'source-version(.*8)', qw(xx)); 35 | eis($cupt, 'source-version(1)', qw(yy zz)); 36 | eis($cupt, 'sv(1)', qw(yy zz)); 37 | 38 | eis($cupt, 'essential', qw(yy)); 39 | eis($cupt, 'e', qw(yy)); 40 | 41 | eis($cupt, 'important', qw(zz)); 42 | 43 | eis($cupt, 'description(oqwo)', qw()); 44 | eis($cupt, 'description(.*oqwo.*)', qw(yy)); 45 | eis($cupt, 'd(.* Line.*)', qw(yy)); 46 | 47 | eis($cupt, 'provides(mm)', qw(yy)); 48 | eis($cupt, 'provides(pp)', qw(yy)); 49 | eis($cupt, 'o(pp)', qw(yy)); 50 | eis($cupt, 'provides(qq)', qw()); 51 | 52 | -------------------------------------------------------------------------------- /test/t/query/search/fse/error-reporting/common.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings FATAL => 'all'; 3 | 4 | require(get_rinclude_path('../FSE')); 5 | 6 | sub compose_error_messages { 7 | return join("", map { "E: $_\n" } @_); 8 | } 9 | 10 | sub upq { 11 | my $pattern = shift; 12 | return "unable to parse the query '$pattern'"; 13 | } 14 | 15 | sub test_binary { 16 | my ($cupt, $pattern, $expected_messages) = @_; 17 | my $expected_errors = compose_error_messages( 18 | @$expected_messages, 19 | upq($pattern), 20 | "error performing the command 'search'" 21 | ); 22 | 23 | (my $out, undef) = get_binary_search_result($cupt, $pattern); 24 | is($out, $expected_errors, $pattern); 25 | } 26 | 27 | sub test_source { 28 | my ($cupt, $pattern, $expected_messages) = @_; 29 | my $expected_errors = compose_error_messages( 30 | @$expected_messages, 31 | upq($pattern), 32 | "error performing the command 'showsrc'" 33 | ); 34 | 35 | (my $out, undef) = get_source_search_result($cupt, $pattern); 36 | is($out, $expected_errors, $pattern); 37 | } 38 | 39 | 1; 40 | 41 | -------------------------------------------------------------------------------- /test/t/query/search/fse/error-reporting/function-specific.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2+5+3+6+1; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup(); 6 | 7 | sub vnd { 8 | my $name = shift; 9 | return "the variable '$name' is not defined"; 10 | } 11 | 12 | sub narg { 13 | my ($n) = @_; 14 | return "the function requires exactly $n arguments"; 15 | } 16 | 17 | TODO: { 18 | local $TODO = 'make it a static check'; 19 | test_binary($cupt, 'and(_v, _s)' => [ vnd('_v') ]); 20 | test_binary($cupt, 'with(_x, Pn(abc), and(_y))' => [ vnd('_y') ]); 21 | } 22 | 23 | test_binary($cupt, 'best()' => [ narg(1) ]); 24 | test_binary($cupt, 'with(_x)' => [ narg(3) ]); 25 | test_binary($cupt, 'recursive()' => [ narg(3) ]); 26 | test_binary($cupt, 'fmap()' => [ "the function 'fmap' requires at least 2 arguments" ]); 27 | test_binary($cupt, '_x(foo)' => [ narg(0) ]); 28 | 29 | test_binary($cupt, 'not()' => [ narg(1) ]); 30 | test_binary($cupt, 'xor(Pn(abc))' => [ narg(2) ]); 31 | test_binary($cupt, 'and()' => [ 'the function should have at least one argument' ]); 32 | 33 | test_binary($cupt, 'priority(extra, optional)' => [ narg(1) ]); 34 | test_binary($cupt, 'essential(abc)' => [ narg(0) ]); 35 | test_binary($cupt, 'field(Python)' => [ narg(2) ]); 36 | test_binary($cupt, 'depends()' => [ narg(1) ]); 37 | test_binary($cupt, 'package:installed(x)' => [ narg(0) ]); 38 | test_binary($cupt, 'package:automatically-installed(x, y)' => [ narg(0) ]); 39 | 40 | test_binary($cupt, 'package-with-installed-dependencies()' => [ narg(1) ]); 41 | 42 | -------------------------------------------------------------------------------- /test/t/query/search/fse/error-reporting/unknown-functions.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 5+4+3+2; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup(); 6 | 7 | sub ubf { 8 | my $func = shift; 9 | return "unknown binary selector function '$func'"; 10 | } 11 | 12 | sub usf { 13 | my $func = shift; 14 | return "unknown source selector function '$func'"; 15 | } 16 | 17 | 18 | test_binary($cupt, '' => [ 'query cannot be empty' ]); 19 | test_binary($cupt, 'zag' => [ ubf('zag') ]); 20 | test_binary($cupt, 'zag()' => [ ubf('zag') ]); 21 | test_binary($cupt, 'zag(yyy)' => [ ubf('zag') ]); 22 | test_binary($cupt, 'zag($%%^^&, aBc, <>JIH)' => [ ubf('zag') ]); 23 | 24 | test_binary($cupt, 'zig(zag())' => [ ubf('zig') ]); 25 | test_binary($cupt, 'zig(Pn(abc))' => [ ubf('zig') ]); 26 | test_binary($cupt, 'and(zag())' => [ ubf('zag'), upq('zag()') ]); 27 | test_binary($cupt, 'or(or(zag(and(Pn(abc)))))' => [ ubf('zag'), upq('zag(and(Pn(abc)))'), upq('or(zag(and(Pn(abc))))') ]); 28 | 29 | test_source($cupt, 'zug()' => [ usf('zug') ]); 30 | test_source($cupt, 'zug(555, 777)' => [ usf('zug') ]); 31 | test_source($cupt, 'reverse-build-depends(zeg(zug()))' => [ ubf('zeg'), upq('zeg(zug())') ]); 32 | 33 | test_binary($cupt, 'source-to-binary(zug(555, 777))' => [ usf('zug'), upq('zug(555, 777)') ]); 34 | test_source($cupt, 'binary-to-source(qqq())' => [ ubf('qqq'), upq('qqq()') ]); 35 | 36 | -------------------------------------------------------------------------------- /test/t/query/search/fse/package-and-version-properties.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6 + 15; 2 | 3 | require(get_rinclude_path('FSE')); 4 | 5 | my @installed; 6 | push @installed, compose_installed_record('i', '1') . < 15 | END 16 | 17 | my $cupt = setup( 18 | 'dpkg_status' => \@installed, 19 | 'packages' => [ compose_package_record('p', '2') ], 20 | 'extended_states' => compose_autoinstalled_record('a') 21 | ); 22 | 23 | eis($cupt, 'package:name(a)', qw(a)); 24 | eis($cupt, 'Pn(a)', qw(a)); 25 | eis($cupt, 'package:installed()', qw(i a)); 26 | eis($cupt, 'Pi()', qw(i a)); 27 | eis($cupt, 'package:automatically-installed()', qw(a)); 28 | eis($cupt, 'Pai()', qw(a)); 29 | 30 | eis($cupt, 'version(3.*)', qw(a)); 31 | eis($cupt, 'v(3.*)', qw(a)); 32 | eis($cupt, 'trusted', qw(p)); 33 | eis($cupt, 't', qw(p)); 34 | eis($cupt, 'priority(extra)', qw(i p)); 35 | eis($cupt, 'p(extra)', qw(i p)); 36 | eis($cupt, 'priority(standard)', qw(a)); 37 | eis($cupt, 'maintainer(/One.*/)', qw(a i)); 38 | eis($cupt, 'm(/One.*/)', qw(a i)); 39 | eis($cupt, 'maintainer(.*mail.*)', qw(a)); 40 | eis($cupt, 'section(//)', qw(i p)); 41 | eis($cupt, 's(//)', qw(i p)); 42 | eis($cupt, 'section(doc)', qw(a)); 43 | eis($cupt, 'field(Special-property-field, /.+/)', qw(i)); 44 | eis($cupt, 'f(Special-property-field, /.+/)', qw(i)); 45 | 46 | -------------------------------------------------------------------------------- /test/t/query/search/fse/quoting.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6 + 3 + 2; 2 | 3 | require(get_rinclude_path('FSE')); 4 | 5 | my $installed = [ 6 | compose_installed_record('a', 1) . "X-Marks: xx, yy, ww\n", 7 | compose_installed_record('b', 2), 8 | compose_installed_record('c', 3) . "X-Marks: yy (zz)\n", 9 | ]; 10 | my $cupt = setup('dpkg_status' => $installed); 11 | 12 | eis($cupt, 'field(X-Marks, .*)', qw(a b c)); 13 | eis($cupt, 'field(X-Marks, /.*/)', qw(a b c)); 14 | eis($cupt, 'field(X-Marks, .+)', qw(a c)); 15 | eis($cupt, 'field(X-Marks, /.+/)', qw(a c)); 16 | eis($cupt, 'field(X-Marks, xx.*)', qw(a)); 17 | eis($cupt, 'field(X-Marks, /xx.*/)', qw(a)); 18 | 19 | eis($cupt, 'field(X-Marks, /xx, yy.*/)', qw(a)); 20 | eis($cupt, 'field(X-Marks, /.*yy, ww/)', qw(a)); 21 | eis($cupt, 'field(X-Marks, /.*xx, ww.*/)', qw()); 22 | 23 | eis($cupt, 'field(X-Marks, /yy (.*)/)', qw(c)); 24 | eis($cupt, 'field(X-Marks, /zz (.*)/)', qw()); 25 | 26 | -------------------------------------------------------------------------------- /test/t/query/search/fse/release-properties.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2*4 + 6 + 3; 2 | 3 | require(get_rinclude_path('FSE')); 4 | 5 | my $cupt = setup( 6 | 'releases' => [ 7 | { 8 | 'hostname' => 's1.some.org', 9 | 'archive' => 'a1', 10 | 'codename' => 'c1', 11 | 'version' => 'v1', 12 | 'vendor' => 'd1', 13 | 'components' => [ 14 | { 'component' => 'p1', 'packages' => [ compose_package_record('pkgx', 0) ] }, 15 | ], 16 | }, { 17 | 'hostname' => 's2.other.org/nnn', 18 | 'archive' => 'a2', 19 | 'codename' => 'c2', 20 | 'version' => 'v2', 21 | 'vendor' => 'd2', 22 | 'components' => [ 23 | { 'component' => 'p2', 'packages' => [ compose_package_record('pkgy', 1) ] }, 24 | { 'component' => 'p3', 'packages' => [ compose_package_record('pkgz', 2) ] }, 25 | ], 26 | } 27 | ] 28 | ); 29 | 30 | eis($cupt, 'release:archive(a1)' => qw(pkgx)); 31 | eis($cupt, 'Ra(a2)' => qw(pkgy pkgz)); 32 | 33 | eis($cupt, 'release:codename(c2)' => qw(pkgy pkgz)); 34 | eis($cupt, 'Rn(c1)' => qw(pkgx)); 35 | 36 | eis($cupt, 'release:version(v1)' => qw(pkgx)); 37 | eis($cupt, 'Rv(v2)' => qw(pkgy pkgz)); 38 | 39 | eis($cupt, 'release:vendor(d1)' => qw(pkgx)); 40 | eis($cupt, 'release:vendor(d.*)' => qw(pkgx pkgy pkgz)); 41 | 42 | eis($cupt, 'release:origin(other)' => qw()); 43 | eis($cupt, 'release:origin(.*other.*)' => qw(pkgy pkgz)); 44 | eis($cupt, 'release:origin(.*\.org.*)' => qw(pkgx pkgy pkgz)); 45 | eis($cupt, 'Ru(.*nnn)' => qw(pkgy pkgz)); 46 | eis($cupt, 'Ru(.*1.*)' => qw(pkgx)); 47 | eis($cupt, 'Ru(.*3.*)' => qw()); 48 | 49 | eis($cupt, 'release:component(p1)' => qw(pkgx)); 50 | eis($cupt, 'release:component(p2)' => qw(pkgy)); 51 | eis($cupt, 'Rc(p3)' => qw(pkgz)); 52 | 53 | -------------------------------------------------------------------------------- /test/t/query/search/fse/source-query.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2; 2 | 3 | require(get_rinclude_path('FSE')); 4 | 5 | my $cupt = setup( 6 | 'packages' => [ 7 | compose_package_record('aa', 1) , 8 | ], 9 | 'sources' => [ 10 | compose_package_record('sa', 0) . "Build-Depends: aa\n" , 11 | compose_package_record('sb', 0) . "Build-Depends: bb\n" , 12 | compose_package_record('sc', 0) . "Build-Depends: aa (>= 2)\n" , 13 | compose_package_record('sd', 0) . "Build-Depends: aa (<< 2)\n" , 14 | ], 15 | ); 16 | 17 | eis_source($cupt, 'reverse-build-depends(Pn(aa))', qw(sa sd)); 18 | eis_source($cupt, 'ZRbd(Pn(aa))', qw(sa sd)); 19 | 20 | -------------------------------------------------------------------------------- /test/t/query/search/fse/source-to-binary.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6; 2 | 3 | require(get_rinclude_path('FSE')); 4 | 5 | my $sources = [ 6 | compose_package_record('sm', '10') . "Binary: bm, bm2\n" , 7 | compose_package_record('sn', '2') , 8 | ]; 9 | 10 | my $packages = [ 11 | compose_package_record('bm', '1') , 12 | compose_package_record('bm', '10') , 13 | compose_package_record('bm', '100') , 14 | compose_package_record('bm', '11') . "Source: sm (10)\n" , 15 | compose_package_record('bm2', '10') , 16 | ]; 17 | 18 | my $cupt = setup('packages' => $packages, 'sources' => $sources); 19 | 20 | eis($cupt, "source-to-binary(Pn(sn))", ()); 21 | eis($cupt, "source-to-binary(Pn(sm))", qw(bm bm2)); 22 | eis($cupt, "source-to-binary(Pn(sm)) & version(1)", ()); 23 | eis($cupt, "source-to-binary(Pn(sm)) & version(10)", qw(bm bm2)); 24 | eis($cupt, "source-to-binary(Pn(sm)) & version(100)", ()); 25 | eis($cupt, "source-to-binary(Pn(sm)) & version(11)", qw(bm)); 26 | 27 | -------------------------------------------------------------------------------- /test/t/query/search/fse/source-version-properties.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 5; 2 | 3 | require(get_rinclude_path('FSE')); 4 | 5 | 6 | my $upl = 'Uploaders: Name Surname , justemail@server.domain'; 7 | 8 | my $cupt = setup( 9 | 'sources' => [ compose_package_record('abc', 0) . "$upl\n" ], 10 | ); 11 | 12 | eis_source($cupt, 'uploaders(.*w.*)' => qw()); 13 | eis_source($cupt, 'uploaders(.*v.*)' => qw(abc)); 14 | eis_source($cupt, 'u(.*justemail.*)' => qw(abc)); 15 | eis_source($cupt, 'u(.*Surname.*)' => qw(abc)); 16 | eis_source($cupt, 'u(/,/)' => qw()); 17 | 18 | -------------------------------------------------------------------------------- /test/t/query/show/binary-as-source.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3; 2 | 3 | my $cupt = setup( 4 | 'packages' => [ 5 | compose_package_record('ba', '1') . "Source: sa\n" , 6 | ], 7 | 'sources' => [ 8 | compose_package_record('sa', 1) . "Binary: ba\n" , 9 | ], 10 | ); 11 | 12 | my $sa = stdout("$cupt showsrc sa"); 13 | like($sa, qr/^Package: sa$/m, 'sa is parsed'); 14 | is(stdall("$cupt showsrc ba"), $sa, 'ba is converted to sa'); 15 | is(stdall("$cupt showsrc 'Pn(ba)'"), $sa, 'via binary FSE'); 16 | 17 | -------------------------------------------------------------------------------- /test/t/query/show/extended-states-record-without-auto-installed-tag-is-valid.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | sub test { 8 | my ($package, $description) = @_; 9 | 10 | my $cupt = TestCupt::setup( 11 | 'packages' => 12 | entail(compose_package_record('aa', 1)), 13 | 'extended_states' => 14 | "Package: $package\nMy-Good-Tag: 8\n\n", 15 | ); 16 | 17 | my $output = stdall("$cupt show aa"); 18 | 19 | like($output, qr/^Package: aa\n/, $description); 20 | } 21 | 22 | test('aa', 'known package'); 23 | test('xyz', 'unknown package'); 24 | 25 | -------------------------------------------------------------------------------- /test/t/query/show/fields/binarypackages.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2; 2 | 3 | my $sources = < $sources); 16 | 17 | like(stdout("$cupt showsrc abc"), qr/^Binary: b1, b2, bp5555\n/m, "one-line value"); 18 | like(stdout("$cupt showsrc def"), qr/^Binary: aaa, bbb, ccc, ddd\n/m, "multi-line value"); 19 | 20 | -------------------------------------------------------------------------------- /test/t/query/show/fields/build-relations.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6; 2 | 3 | my $orig = "aaa(<< 3.2~xyz+1 )"; 4 | my $parsed = "aaa (<< 3.2~xyz+1)"; 5 | 6 | sub test { 7 | my ($type) = @_; 8 | 9 | my $cupt = setup( 10 | 'sources' => [ 11 | compose_package_record('ee', 0) . "$type: $orig\n" , 12 | ], 13 | ); 14 | 15 | my $output = stdall("$cupt showsrc ee"); 16 | 17 | subtest $type => sub { 18 | like($output, qr/^$type: \Q$parsed\E$/m, 'parsed relation is printed'); 19 | 20 | my $count = () = $output =~ m/Build-/; 21 | is($count, 1, 'only one relation printed'); 22 | } 23 | } 24 | 25 | test('Build-Depends'); 26 | test('Build-Depends-Indep'); 27 | test('Build-Depends-Arch'); 28 | test('Build-Conflicts'); 29 | test('Build-Conflicts-Indep'); 30 | test('Build-Conflicts-Arch'); 31 | 32 | -------------------------------------------------------------------------------- /test/t/query/show/fields/important.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 5; 2 | 3 | sub test { 4 | my ($line, $expected_is_important) = @_; 5 | my $cupt = setup('packages' => [ compose_package_record('pp', 0) . "$line\n" ]); 6 | 7 | my $output = stdout("$cupt show pp"); 8 | my @found_important_values = ($output =~ m/^Important: (.*)$/mg); 9 | my @expected_important_values = map { 'yes' } (1..$expected_is_important); 10 | 11 | is_deeply(\@found_important_values, \@expected_important_values, $line); 12 | } 13 | 14 | test('VeryImportant: yes' => 0); 15 | test('Important: yes' => 1); 16 | test('Important: yes!' => 0); 17 | test('Important: no' => 0); 18 | test('Important: 781njdps' => 0); 19 | 20 | -------------------------------------------------------------------------------- /test/t/query/show/fields/multiarch.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 1; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $packages = entail(compose_package_record('abc', '1') . "Multi-Arch: allowed\n"); 8 | 9 | my $cupt = TestCupt::setup('packages' => $packages); 10 | 11 | like(stdout("$cupt show abc"), qr/^Multi-Arch: allowed$/m, "'Multi-Arch' field is shown"); 12 | -------------------------------------------------------------------------------- /test/t/query/show/fields/provides.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 2; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $aa_provides = "Provides: nn, ll\n"; 8 | my $bb_provides = "Provides: pp (= 3), ll, qq (= 2:1.3~4+bpo5)\n"; 9 | 10 | my $cupt = TestCupt::setup( 11 | 'packages' => 12 | entail(compose_package_record('aa', 1) . $aa_provides) . 13 | entail(compose_package_record('bb', 1) . $bb_provides) 14 | ); 15 | 16 | sub test { 17 | my ($package, $expected_provides_line) = @_; 18 | 19 | my $comment = "package: $package, expected provides line: $expected_provides_line"; 20 | 21 | my $output = stdout("$cupt show $package"); 22 | like($output, qr/^\Q$expected_provides_line\E/m, $comment); 23 | } 24 | 25 | test('aa', $aa_provides); 26 | test('bb', $bb_provides); 27 | 28 | -------------------------------------------------------------------------------- /test/t/query/show/fields/source.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 3; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $packages = 8 | entail(compose_package_record('xyz', '3')) . 9 | entail(compose_package_record('abc', '1') . "Source: abc-source\n") . 10 | entail(compose_package_record('abc', '2.1') . "Source: abc-source (4.5)\n"); 11 | 12 | my $cupt = TestCupt::setup('packages' => $packages); 13 | 14 | like(stdout("$cupt show xyz"), qr/^Source: xyz\n/m, "source package name is binary package name if omitted"); 15 | like(stdout("$cupt show abc=1"), qr/^Source: abc-source\n/m, "source version is binary version if omitted"); 16 | like(stdout("$cupt show abc=2.1"), qr/^Source: abc-source\nSource version: 4.5\n/m, "source version is parsed if present"); 17 | 18 | -------------------------------------------------------------------------------- /test/t/query/show/nothing-selected.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 7; 2 | 3 | my $cupt = setup( 4 | 'packages' => [ 5 | compose_package_record('bn', '2'), 6 | ], 7 | ); 8 | 9 | 10 | sub sn { 11 | my $cmd = shift; 12 | return qr/E: .* selected nothing\nE: error performing the command '$cmd'\n/; 13 | } 14 | 15 | like(stdall("$cupt show by"), sn('show'), 'invalid package'); 16 | like(stdall("$cupt show 'depends(Pn(bn))'"), sn('show'), 'empty FSE result'); 17 | 18 | like(stdall("$cupt showsrc blabla"), sn('showsrc'), 'invalid package'); 19 | like(stdall("$cupt showsrc 'priority(extra)'"), sn('showsrc'), 'empty FSE result'); 20 | like(stdall("$cupt showsrc bn"), sn('showsrc'), 'no source package for bn'); 21 | 22 | TODO: { 23 | local $TODO = 'should suppress parsing errors when a FSE cannot be treated as binary one'; 24 | like(stdall("$cupt showsrc 'reverse-build-depends(Pn(bn))'"), sn('showsrc'), 'source FSE, existing argument'); 25 | like(stdall("$cupt showsrc 'reverse-build-depends(Pn(bm))'"), sn('showsrc'), 'source FSE, empty argument'); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /test/t/query/show/package-existance.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 3; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my $config_files_record = < $dpkg_status); 19 | 20 | is(exitcode("$cupt show abc"), 0, "there is 'abc' binary package"); 21 | isnt(exitcode("$cupt show abd"), 0, "there is no 'abd' binary package"); 22 | isnt(exitcode("$cupt show libsvn-perl"), 0, "there is no 'svn-perl' binary package"); 23 | -------------------------------------------------------------------------------- /test/t/query/show/status-of-installed-package.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 21; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | sub test { 8 | my ($desired, $flag, $state, $error) = @_; 9 | 10 | my $inst_record = compose_status_record('pp', "$desired $flag $state", 1); 11 | my $cupt = TestCupt::setup('dpkg_status' => $inst_record); 12 | 13 | my $output = stdall("$cupt show pp"); 14 | 15 | my $line_to_search; 16 | if (length($error)) { 17 | $line_to_search = "E: malformed '$error' status indicator (for the package 'pp')"; 18 | } else { 19 | $line_to_search = "Status: $state" . ($desired eq 'hold' ? ' (on hold)' : ''); 20 | } 21 | 22 | my $comment = "status line: '$desired $flag $state', error expected: '$error'"; 23 | like($output, qr/^\Q$line_to_search\E$/m, $comment); 24 | } 25 | 26 | test('install', 'ok', 'installed' => ''); 27 | test('install', 'ok', 'half-installed' => ''); 28 | test('install', 'ok', 'unpacked' => ''); 29 | test('install', 'ok', 'half-configured' => ''); 30 | test('install', 'ok', 'triggers-awaited' => ''); 31 | test('install', 'ok', 'triggers-pending' => ''); 32 | 33 | test('install', 'ok', 'triggers' => 'status'); 34 | test('install', 'ok', 'triggers-ok' => 'status'); 35 | test('install', 'ok', 'install' => 'status'); 36 | test('install', 'ok', 'half' => 'status'); 37 | test('install', 'ok', '#*^^$' => 'status'); 38 | 39 | 40 | test('deinstall', 'ok', 'installed' => ''); 41 | test('hold', 'ok', 'installed' => ''); 42 | test('purge', 'ok', 'installed' => ''); 43 | 44 | test('installed', 'ok', 'installed' => 'desired'); 45 | test('ok', 'ok', 'installed' => 'desired'); 46 | test('@#@$&*((n', 'ok', 'installed' => 'desired'); 47 | 48 | 49 | test('install', 'reinstreq', 'installed' => ''); 50 | test('purge', 'reinstreq', 'half-configured' => ''); 51 | 52 | test('install', 'notok', 'installed' => 'error'); 53 | test('install', '000:=', 'installed' => 'error'); 54 | 55 | -------------------------------------------------------------------------------- /test/t/query/show/version-merging/architecture.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More tests => 6; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | 8 | my $package = 'abc'; 9 | my $version = '1.2.3'; 10 | 11 | my $record = < 29 | compose_record_with_arch($arch) 30 | ); 31 | } 32 | 33 | sub test { 34 | my ($is_installed, $arch, $expected_result) = @_; 35 | 36 | my $cupt = setup_cupt($is_installed, $arch); 37 | 38 | my $output = stdall("$cupt show $package"); 39 | 40 | my $comment_prefix = "(installed: $is_installed): arch $arch"; 41 | if ($expected_result) { 42 | like($output, qr/^\QVersion: $version\E/m, "$comment_prefix: merged"); 43 | } else { 44 | like($output, qr/^E:.* selected nothing/m, "$comment_prefix: not merged"); 45 | } 46 | } 47 | 48 | my $binary_arch = get_binary_architecture(); 49 | 50 | sub test_group { 51 | my ($is_installed) = @_; 52 | 53 | test($is_installed, $binary_arch => 1); 54 | test($is_installed, 'dubidu' => $is_installed); 55 | test($is_installed, 'all' => 1); 56 | } 57 | 58 | test_group(1); 59 | test_group(0); 60 | 61 | -------------------------------------------------------------------------------- /test/t/query/version-string/invalid.t: -------------------------------------------------------------------------------- 1 | use TestCupt; 2 | use Test::More; 3 | 4 | use strict; 5 | use warnings; 6 | 7 | my @incorrect_version_strings = ( 8 | 'ab:5', ':1.2.3', '2a5:1.2', # broken epoch 9 | '1.2.3-a:6', '1.2-:5', # broken revision 10 | '', '$', '2в.3.4', '5.2.5&', '%%', '()', '2.6.7!!!', # broken upstream version 11 | # 'abc' is excluded from here, people use it... :( 12 | ); 13 | 14 | plan tests => scalar @incorrect_version_strings; 15 | 16 | sub test { 17 | my $version = shift; 18 | 19 | my $cupt = TestCupt::setup( 20 | 'packages' => 21 | entail(compose_package_record('aa', $version)) 22 | ); 23 | 24 | my $output = stdall("$cupt show aa"); 25 | 26 | like($output, qr/^E: invalid version string '\Q$version\E'$/m, $version); 27 | } 28 | 29 | foreach (@incorrect_version_strings) { 30 | test($_); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /test/t/query/why/basic.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 8; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ 7 | compose_installed_record('aa', 0) . "Depends: xx, yy (>> 1), pp | qq\nPre-Depends: rr\n" , 8 | compose_installed_record('xx', 1) . "Depends: zz (<= 4)\n" , 9 | compose_installed_record('yy', 2) , 10 | compose_installed_record('zz', 3) , 11 | compose_installed_record('pp', 4) , 12 | compose_installed_record('qq', 5) , 13 | compose_installed_record('rr', 6) , 14 | ], 15 | 'extended_states' => [ 16 | compose_autoinstalled_record('xx') , 17 | compose_autoinstalled_record('yy') , 18 | compose_autoinstalled_record('zz') , 19 | compose_autoinstalled_record('pp') , 20 | compose_autoinstalled_record('qq') , 21 | compose_autoinstalled_record('rr') , 22 | ], 23 | ); 24 | 25 | sub test { 26 | my ($package, $expected_output) = @_; 27 | 28 | test_why($cupt, $package, '', $expected_output, ($package || '')); 29 | } 30 | 31 | test('aa' => ''); 32 | test('xx' => "aa 0: Depends: xx\n"); 33 | test('yy' => "aa 0: Depends: yy (>> 1)\n"); 34 | test('zz' => "aa 0: Depends: xx\nxx 1: Depends: zz (<= 4)\n"); 35 | test('pp' => "aa 0: Depends: pp | qq\n"); 36 | test('qq' => "aa 0: Depends: pp | qq\n"); 37 | test('rr' => "aa 0: Pre-Depends: rr\n"); 38 | 39 | test('' => "E: no binary package expressions specified\nE: error performing the command 'why'\n"); 40 | 41 | -------------------------------------------------------------------------------- /test/t/query/why/common.pl: -------------------------------------------------------------------------------- 1 | sub test_why_raw { 2 | my ($cupt, $package, $options, $main_check, $description) = @_; 3 | 4 | subtest $description => sub { 5 | my $command = "$cupt why $options $package"; 6 | my $output = stdall($command); 7 | my $exit_code = $?; 8 | if ($output !~ m/^E: /) { 9 | is($exit_code, 0, 'operation succeeded'); 10 | } else { 11 | isnt($exit_code, 0, 'operation failed'); 12 | } 13 | $main_check->($output); 14 | }; 15 | } 16 | 17 | sub test_why { 18 | my ($cupt, $package, $options, $expected_output, $description) = @_; 19 | 20 | $expected_output =~ s/(\w+) (\d):/$1 $2^installed:/g; 21 | 22 | my $main_check = sub { 23 | my $output = shift; 24 | is($output, $expected_output, 'output is correct'); 25 | }; 26 | test_why_raw($cupt, $package, $options, $main_check, $description); 27 | } 28 | 29 | sub test_why_regex { 30 | my ($cupt, $package, $options, $regex, $description) = @_; 31 | 32 | my $main_check = sub { 33 | my $output = shift; 34 | like($output, $regex, 'output is correct'); 35 | }; 36 | test_why_raw($cupt, $package, $options, $main_check, $description); 37 | } 38 | 39 | 1; 40 | 41 | -------------------------------------------------------------------------------- /test/t/query/why/endurance.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 9; 2 | 3 | require(get_rinclude_path('common')); 4 | require(get_rinclude_path('setup-from-links')); 5 | 6 | sub build_up_setup_data { 7 | my ($package, $level) = @_; 8 | 9 | return () if ($level == 0); 10 | 11 | my $unfold = sub { 12 | my $branch = $_; 13 | my $child = "$package$branch"; 14 | 15 | my $link = "$package D $child"; 16 | my @child_tree = build_up_setup_data($child, $level-1); 17 | 18 | return ($link, @child_tree); 19 | }; 20 | 21 | return map(&$unfold, qw(0 1 2)); 22 | } 23 | 24 | sub setup_cupt { 25 | my $level = shift; 26 | 27 | my @links = build_up_setup_data('zz', $level); 28 | return setup_cupt_from_links(\@links); 29 | } 30 | 31 | sub test { 32 | my $level = shift; 33 | my $cupt = setup_cupt($level); 34 | 35 | my $last_package = 'zz' . ('1' x $level); 36 | test_why_regex($cupt, $last_package, '', qr/^zz /, "level $level"); 37 | } 38 | 39 | foreach (1..9) { 40 | test($_); 41 | } 42 | 43 | -------------------------------------------------------------------------------- /test/t/query/why/from-specific-packages.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 13; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ 7 | compose_installed_record('aa', 7) , 8 | compose_installed_record('bb', 8) , 9 | compose_installed_record('mm', 6) . "Recommends: xxx\n" , 10 | compose_installed_record('xxx', 15) , 11 | ], 12 | 'packages' => [ 13 | compose_package_record('aa', 10) . "Recommends: xxx\n" , 14 | compose_package_record('bb', 11) . "Depends: xxx\n" , 15 | compose_package_record('bb', 12) . "Depends: xxx (>= 14)\n" , 16 | compose_package_record('bb', 13) . "Depends: xxx (>= 16)\n" , 17 | compose_package_record('cc', 14) , 18 | ], 19 | 'extended_states' => [ compose_autoinstalled_record('xxx') ], 20 | ); 21 | 22 | sub test { 23 | my ($from, $expected_result, $installed_only) = @_; 24 | 25 | $from = join(' ', map { "'$_'" } split(' ', $from)); 26 | 27 | my $options = ''; 28 | if ($installed_only//0) { 29 | $options .= '--installed-only'; 30 | } 31 | 32 | test_why($cupt, "$from xxx", $options, $expected_result, "from: [$from], options: [$options]"); 33 | } 34 | 35 | test('aa' => "aa 10: Recommends: xxx\n"); 36 | test('bb' => ''); 37 | test('bb=12' => "bb 12: Depends: xxx (>= 14)\n"); 38 | test('bb=11' => "bb 11: Depends: xxx\n"); 39 | test('cc' => ''); 40 | test('mm' => "mm 6: Recommends: xxx\n"); 41 | test('xxx' => ''); 42 | 43 | test('aa bb' => "aa 10: Recommends: xxx\n"); 44 | test('c*' => ''); 45 | test('??', "aa 10: Recommends: xxx\n"); 46 | 47 | test('??', "mm 6: Recommends: xxx\n", 1); 48 | test('aa', '', 1); 49 | test('aa bb', '', 1); 50 | 51 | -------------------------------------------------------------------------------- /test/t/query/why/loop.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ 7 | compose_installed_record('aa', 1) . "Depends: bb\n" , 8 | compose_installed_record('bb', 2) . "Depends: cc\n" , 9 | compose_installed_record('cc', 3) . "Depends: bb\n" , 10 | compose_installed_record('xx', 4) , 11 | ], 12 | 'extended_states' => [ 13 | compose_autoinstalled_record('bb') , 14 | compose_autoinstalled_record('cc') , 15 | compose_autoinstalled_record('xx') , 16 | ], 17 | ); 18 | 19 | sub test { 20 | my ($package, $expected_result) = @_; 21 | test_why($cupt, $package, '', $expected_result, $package); 22 | } 23 | 24 | test('bb', "aa 1: Depends: bb\n"); 25 | test('cc', "aa 1: Depends: bb\nbb 2: Depends: cc\n"); 26 | test('xx', ''); 27 | 28 | -------------------------------------------------------------------------------- /test/t/query/why/recommends-and-suggests.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 4; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ 7 | compose_installed_record('aa', 1) . "Recommends: zz\n" , 8 | compose_installed_record('bb', 1) . "Suggests: zz\n" , 9 | compose_installed_record('zz', 555) , 10 | ], 11 | 'extended_states' => [ compose_autoinstalled_record('zz') ], 12 | ); 13 | 14 | sub test { 15 | my ($recommends_is_on, $suggests_is_on, $expected_output) = @_; 16 | 17 | my $options = "-o cupt::resolver::keep-recommends=$recommends_is_on " . 18 | "-o cupt::resolver::keep-suggests=$suggests_is_on"; 19 | 20 | test_why($cupt, 'zz', $options, $expected_output, "recommends: $recommends_is_on, suggests: $suggests_is_on"); 21 | } 22 | 23 | test(0, 0, ''); 24 | my $recommends_output = "aa 1: Recommends: zz\n"; 25 | test(1, 0, $recommends_output); 26 | test(1, 1, $recommends_output); 27 | test(0, 1, "bb 1: Suggests: zz\n"); 28 | 29 | -------------------------------------------------------------------------------- /test/t/query/why/setup-from-links.pl: -------------------------------------------------------------------------------- 1 | my %dep_letters = ( 2 | 'D' => 'Depends', 3 | 'R' => 'Recommends', 4 | 'S' => 'Suggests', 5 | ); 6 | 7 | sub setup_cupt_from_links { 8 | my $links = shift; 9 | 10 | my %sorted_deps; 11 | my %autoflags; 12 | 13 | foreach my $link (@$links) { 14 | my ($from, $type, $tos) = split(' ', $link); 15 | 16 | my @tos = split('\|', $tos); 17 | foreach my $to (@tos) { 18 | if (length($to) > 2) { 19 | $autoflags{$to} = 1; 20 | } 21 | 22 | $sorted_deps{$to} //= {}; 23 | } 24 | 25 | push @{$sorted_deps{$from}->{$type}}, $tos; 26 | } 27 | 28 | my $installed_packages = ''; 29 | while (my ($package, $deplines) = each %sorted_deps) { 30 | $installed_packages .= compose_installed_record($package, 0); 31 | 32 | while (my ($dep_letter, $dependees) = each %$deplines) { 33 | $installed_packages .= $dep_letters{$dep_letter}; 34 | $installed_packages .= ': '; 35 | $installed_packages .= join(', ', @$dependees); 36 | $installed_packages .= "\n"; 37 | } 38 | 39 | $installed_packages .= "\n"; 40 | } 41 | 42 | my $extended_states = join('', map { entail(compose_autoinstalled_record($_)) } keys %autoflags); 43 | 44 | return TestCupt::setup('dpkg_status' => $installed_packages, 'extended_states' => $extended_states); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /test/t/query/why/traverse-order.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ 7 | compose_installed_record('bb', 1) . "Depends: cc, dd (>= 2), dd\n" , 8 | compose_installed_record('aa', 1) . "Depends: cc\n" , 9 | compose_installed_record('cc', 3) , 10 | compose_installed_record('dd', 4) , 11 | ], 12 | 'extended_states' => [ 13 | compose_autoinstalled_record('cc') , 14 | compose_autoinstalled_record('dd') , 15 | ] 16 | ); 17 | 18 | test_why($cupt, 'cc', '', "aa 1: Depends: cc\n", 'for packages -- alphabetic order'); 19 | test_why($cupt, 'dd', '', "bb 1: Depends: dd (>= 2)\n", 'for relation expressions -- left-to-right order'); 20 | 21 | -------------------------------------------------------------------------------- /test/t/shell/basic.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 2+2+5+3+3; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ 7 | compose_installed_record('aa', '0.2016'), 8 | compose_installed_record('bb', '2.1-4'), 9 | ], 10 | 'packages' => [ 11 | compose_package_record('aa', 3) . "Description: aa-desc\n", 12 | compose_package_record('dd', 4) . "Rtb: xy8pas\n", 13 | ], 14 | 'sources' => [ 15 | compose_package_record('cc', 5) . "Build-Depends: aa\n", 16 | compose_package_record('ee', 8) . "Wqq: uiop\n", 17 | ], 18 | ); 19 | my $cupt_shell = get_shell($cupt); 20 | 21 | sub test { 22 | my ($command, $extra_comment) = @_; 23 | $extra_comment = defined($extra_comment) ? " ($extra_comment)" : ""; 24 | 25 | subtest "'$command'$extra_comment" => sub { 26 | test_output_identical_with_non_shell($cupt, $cupt_shell, $command); 27 | } 28 | } 29 | 30 | sub test_manage { 31 | my @params = @_; 32 | $params[0] .= " -s -y -N"; 33 | test(@params); 34 | } 35 | 36 | test('help'); 37 | test('version'); 38 | 39 | test('policy'); 40 | test('policysrc'); 41 | 42 | test('show bb'); 43 | test('show aa'); 44 | TODO: { 45 | local $TODO = 'fix displaying extra fields'; 46 | test('showsrc cc'); 47 | test('show dd'); 48 | test('showsrc ee'); 49 | } 50 | 51 | test('show --installed-only bb'); 52 | test('show aa', 'non-installed package after --installed-only'); 53 | test('show --installed-only aa', '--installed-only after the cache is populated with also non-installed packages'); 54 | 55 | test_manage('install aa'); 56 | test_manage('remove bb'); 57 | test_manage('build-dep cc'); 58 | 59 | -------------------------------------------------------------------------------- /test/t/shell/cache-reload.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 14; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cache_version; 6 | sub our_setup { 7 | my $abc_version = $cache_version++; 8 | return setup('dpkg_status' => [compose_installed_record('abc', $abc_version)]); 9 | } 10 | 11 | sub test { 12 | my ($commands, $expected_cache_version) = @_; 13 | $cache_version = 0; 14 | 15 | my $cupt = our_setup(); 16 | my $cupt_shell = get_shell($cupt); 17 | 18 | foreach my $command (@$commands) { 19 | $cupt_shell->execute($command); 20 | $cupt = our_setup(); 21 | } 22 | 23 | my $abc_result = $cupt_shell->execute('show abc'); 24 | my $comment = join(' & ', map { to_one_line($_) } @$commands) . 25 | ": loaded cache version is $expected_cache_version"; 26 | 27 | like($abc_result, qr/^Version: $expected_cache_version/m, to_one_line($comment)); 28 | } 29 | 30 | test(['show abc'] => 0); 31 | test(['policy'] => 0); 32 | 33 | test(['remove -y abc'] => 1); 34 | test(['remove abc\nn'] => 0); 35 | test(['remove -y -s abc'] => 0); 36 | 37 | test(['show abc', 'show xyz'] => 0); 38 | test(['show abc', 'showsrc xyz'] => 1); 39 | test(['showsrc xyz'] => 0); 40 | 41 | test(['badcmd'] => 1); 42 | test(['show --installed-only xyz'] => 1); 43 | test(['showauto qwe'] => 1); 44 | 45 | test(['show abc', 'update'] => 2); 46 | test(['show abc', 'update -s'] => 0); 47 | 48 | test(['showsrc xyz', 'show abc', 'showsrc xyz'] => 0); 49 | 50 | -------------------------------------------------------------------------------- /test/t/shell/common.pl: -------------------------------------------------------------------------------- 1 | use CuptInteractive; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | sub get_shell { 7 | my $cupt = shift; 8 | return CuptInteractive->new("$cupt shell"); 9 | } 10 | 11 | sub test_output_identical_with_non_shell { 12 | my ($cupt, $cupt_shell, $command, $base_command) = @_; 13 | $base_command //= $command; 14 | 15 | my $output_normal = stdall("$cupt $base_command"); 16 | is($?, 0, 'command succeeded') 17 | or diag($output_normal); 18 | my $output_shell = $cupt_shell->execute($command); 19 | is($output_shell, $output_normal, "comparing output"); 20 | } 21 | 22 | 1; 23 | 24 | -------------------------------------------------------------------------------- /test/t/shell/error-handling.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 4+6; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup(); 6 | 7 | sub test { 8 | my $full_command = shift; 9 | my ($command) = ($full_command =~ m/(.*?)(?: |$)/); 10 | my $cupt_shell = get_shell($cupt); 11 | 12 | # several runs to check that bad commands do not cause early exit or behavioral changes 13 | subtest $full_command => sub { 14 | for my $run (1..3) { 15 | my $answer = $cupt_shell->execute($full_command); 16 | chomp($answer); 17 | is($answer, "E: unrecognized command '$command'", "run $run"); 18 | } 19 | } 20 | } 21 | 22 | test('installll'); 23 | test('dpkg'); 24 | test('dpkg -l'); 25 | test('ls --lat'); 26 | 27 | test('%abc def'); 28 | test('!abc def'); 29 | test('#br'); 30 | test('dpkg -l | grep geo'); 31 | test('$abc def'); 32 | test('(ls)'); 33 | 34 | -------------------------------------------------------------------------------- /test/t/shell/tokenising.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 6+1; 2 | 3 | require(get_rinclude_path('common')); 4 | 5 | my $cupt = setup( 6 | 'dpkg_status' => [ compose_installed_record('ii', 2) ], 7 | 'packages' => [ compose_package_record('pp', 1) ], 8 | ); 9 | my $base_command; 10 | 11 | sub test { 12 | my ($command, $comment) = @_; 13 | subtest $comment => sub { 14 | test_output_identical_with_non_shell($cupt, get_shell($cupt), $command, $base_command); 15 | } 16 | } 17 | 18 | $base_command = 'show pp'; 19 | test('show pp ', 'space after parameters'); 20 | test('show pp', 'space before parameters'); 21 | test(' show pp', 'space before command'); 22 | test("show 'pp'", 'single quotes around parameter'); 23 | test("'show' pp", 'single quotes around command'); 24 | test('show "pp"', 'double quotes'); 25 | 26 | $base_command = 'satisfy ii'; 27 | test('satisfy "ii, ii"', 'spaces within a parameter'); 28 | 29 | -------------------------------------------------------------------------------- /test/t/version.t: -------------------------------------------------------------------------------- 1 | use Test::More tests => 3; 2 | use IPC::Run3; 3 | 4 | my $sane_version_regex = qr/2\.\d+\.\d+(~|\w|\+)*/; 5 | my $cupt = setup(); 6 | 7 | foreach my $command (qw(version -v --version)) { 8 | subtest "invoking via $command" => sub { 9 | run3("$cupt $command", \undef, \my $stdout, \my $stderr); 10 | is($?, 0, 'exit code'); 11 | like($stdout, qr/^executable: $sane_version_regex$/m, 'executable version'); 12 | like($stdout, qr/^library: $sane_version_regex$/m, 'library version'); 13 | is($stderr, '', 'no warnings/errors'); 14 | } 15 | } 16 | 17 | --------------------------------------------------------------------------------