diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 06:14:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 06:14:41 +0000 |
commit | 549a391d6438e828001eeeaf235b080c054a7bf3 (patch) | |
tree | 1bb6b1ea5987fa167a1d13abe82209cc882dd94b /test/libapt | |
parent | Initial commit. (diff) | |
download | apt-upstream.tar.xz apt-upstream.zip |
Adding upstream version 2.2.4.upstream/2.2.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/libapt')
35 files changed, 5589 insertions, 0 deletions
diff --git a/test/libapt/CMakeLists.txt b/test/libapt/CMakeLists.txt new file mode 100644 index 0000000..11d4d22 --- /dev/null +++ b/test/libapt/CMakeLists.txt @@ -0,0 +1,53 @@ +if (WITH_TESTS) + set(PROJECT_TEST_LIBRARIES apt-private) + find_path(GTEST_ROOT src/gtest.cc + /usr/src/googletest/googletest + /usr/src/googletest + /usr/src/gtest + ) + find_package(GTest) + set(GTEST_DEPENDENCIES) + + if(NOT GTEST_FOUND AND EXISTS ${GTEST_ROOT}) + include(ExternalProject) + ExternalProject_Add(gtest PREFIX ./gtest + SOURCE_DIR ${GTEST_ROOT} + INSTALL_COMMAND true) + + link_directories(${CMAKE_CURRENT_BINARY_DIR}/gtest/src/gtest-build) + link_directories(${CMAKE_CURRENT_BINARY_DIR}/gtest/src/gtest-build/lib) + + set(GTEST_LIBRARIES "-lgtest") + set(GTEST_DEPENDENCIES "gtest") + set(GTEST_FOUND TRUE) + find_path(GTEST_INCLUDE_DIRS NAMES gtest/gtest.h PATHS ${GTEST_ROOT}/include) + + message(STATUS "Found GTest at ${GTEST_ROOT}, headers at ${GTEST_INCLUDE_DIRS}") + endif() + + if (NOT GTEST_FOUND) + message(FATAL_ERROR "Could not find GTest") + endif() + + + # gtest produces some warnings with the set of warnings we activate, + # so disable the offending warnings while compiling tests for now + add_optional_compile_options(Wno-undef) + add_optional_compile_options(Wno-ctor-dtor-privacy) + # Do not force override for gtest, gtest is missing override specifiers + add_optional_compile_options(Wno-suggest-override) + + # Definition of the C++ files used to build the test binary - note that this + # is expanded at CMake time, so you have to rerun cmake if you add or remove + # a file (you can just run cmake . in the build directory) + file(GLOB files gtest_runner.cc *-helpers.cc *_test.cc) + add_executable(lib${PROJECT_NAME}_test ${files}) + target_include_directories(lib${PROJECT_NAME}_test PRIVATE ${GTEST_INCLUDE_DIRS}) + target_link_libraries(lib${PROJECT_NAME}_test ${GTEST_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${PROJECT_TEST_LIBRARIES}) + if (GTEST_DEPENDENCIES) + add_dependencies(lib${PROJECT_NAME}_test ${GTEST_DEPENDENCIES}) + endif() + add_test(NAME ${PROJECT_NAME}Tests + COMMAND lib${PROJECT_NAME}_test + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +endif() diff --git a/test/libapt/acqprogress_test.cc b/test/libapt/acqprogress_test.cc new file mode 100644 index 0000000..d4596c8 --- /dev/null +++ b/test/libapt/acqprogress_test.cc @@ -0,0 +1,178 @@ +#include <config.h> +#include <apt-pkg/acquire-item.h> +#include <apt-pkg/acquire.h> +#include <apt-pkg/configuration.h> +#include <apt-pkg/hashes.h> +#include <apt-private/acqprogress.h> +#include <gtest/gtest.h> +#include <sstream> +#include <string> + +class TestItem: public pkgAcquire::Item +{ +public: + explicit TestItem(pkgAcquire * const Acq) : pkgAcquire::Item(Acq) {} + + virtual std::string DescURI() const APT_OVERRIDE { return ""; } + virtual HashStringList GetExpectedHashes() const APT_OVERRIDE { return HashStringList(); } + +}; + +TEST(AcqProgress, IMSHit) +{ + std::ostringstream out; + unsigned int width = 80; + AcqTextStatus Stat(out, width, 0); + Stat.Start(); + + pkgAcquire Acq(&Stat); + pkgAcquire::ItemDesc hit; + hit.URI = "http://example.org/file"; + hit.Description = "Example File from example.org"; + hit.ShortDesc = "Example File"; + TestItem hitO(&Acq); + hit.Owner = &hitO; + + EXPECT_EQ("", out.str()); + Stat.IMSHit(hit); + EXPECT_EQ("Hit:1 Example File from example.org\n", out.str()); + Stat.IMSHit(hit); + EXPECT_EQ("Hit:1 Example File from example.org\n" + "Hit:1 Example File from example.org\n", out.str()); + Stat.Stop(); + EXPECT_EQ("Hit:1 Example File from example.org\n" + "Hit:1 Example File from example.org\n", out.str()); +} +TEST(AcqProgress, FetchNoFileSize) +{ + std::ostringstream out; + unsigned int width = 80; + AcqTextStatus Stat(out, width, 0); + Stat.Start(); + + pkgAcquire Acq(&Stat); + pkgAcquire::ItemDesc fetch; + fetch.URI = "http://example.org/file"; + fetch.Description = "Example File from example.org"; + fetch.ShortDesc = "Example File"; + TestItem fetchO(&Acq); + fetch.Owner = &fetchO; + + EXPECT_EQ("", out.str()); + Stat.Fetch(fetch); + EXPECT_EQ("Get:1 Example File from example.org\n", out.str()); + Stat.Fetch(fetch); + EXPECT_EQ("Get:1 Example File from example.org\n" + "Get:1 Example File from example.org\n", out.str()); + Stat.Stop(); + EXPECT_EQ("Get:1 Example File from example.org\n" + "Get:1 Example File from example.org\n", out.str()); +} +TEST(AcqProgress, FetchFileSize) +{ + std::ostringstream out; + unsigned int width = 80; + AcqTextStatus Stat(out, width, 0); + Stat.Start(); + + pkgAcquire Acq(&Stat); + pkgAcquire::ItemDesc fetch; + fetch.URI = "http://example.org/file"; + fetch.Description = "Example File from example.org"; + fetch.ShortDesc = "Example File"; + TestItem fetchO(&Acq); + fetchO.FileSize = 100; + fetch.Owner = &fetchO; + + EXPECT_EQ("", out.str()); + Stat.Fetch(fetch); + EXPECT_EQ("Get:1 Example File from example.org [100 B]\n", out.str()); + fetchO.FileSize = 42; + Stat.Fetch(fetch); + EXPECT_EQ("Get:1 Example File from example.org [100 B]\n" + "Get:1 Example File from example.org [42 B]\n", out.str()); + Stat.Stop(); + EXPECT_EQ("Get:1 Example File from example.org [100 B]\n" + "Get:1 Example File from example.org [42 B]\n", out.str()); +} +TEST(AcqProgress, Fail) +{ + std::ostringstream out; + unsigned int width = 80; + AcqTextStatus Stat(out, width, 0); + Stat.Start(); + + pkgAcquire Acq(&Stat); + pkgAcquire::ItemDesc fetch; + fetch.URI = "http://example.org/file"; + fetch.Description = "Example File from example.org"; + fetch.ShortDesc = "Example File"; + TestItem fetchO(&Acq); + fetchO.FileSize = 100; + fetchO.Status = pkgAcquire::Item::StatIdle; + fetch.Owner = &fetchO; + + EXPECT_EQ("", out.str()); + Stat.Fail(fetch); + EXPECT_EQ("Ign:1 Example File from example.org\n", out.str()); + fetchO.Status = pkgAcquire::Item::StatDone; + Stat.Fail(fetch); + EXPECT_EQ("Ign:1 Example File from example.org\n" + "Ign:1 Example File from example.org\n", out.str()); + fetchO.Status = pkgAcquire::Item::StatError; + fetchO.ErrorText = "An error test!"; + Stat.Fail(fetch); + EXPECT_EQ("Ign:1 Example File from example.org\n" + "Ign:1 Example File from example.org\n" + "Err:1 Example File from example.org\n" + " An error test!\n", out.str()); + _config->Set("Acquire::Progress::Ignore::ShowErrorText", true); + fetchO.Status = pkgAcquire::Item::StatDone; + Stat.Fail(fetch); + EXPECT_EQ("Ign:1 Example File from example.org\n" + "Ign:1 Example File from example.org\n" + "Err:1 Example File from example.org\n" + " An error test!\n" + "Ign:1 Example File from example.org\n" + " An error test!\n", out.str()); + _config->Set("Acquire::Progress::Ignore::ShowErrorText", true); + Stat.Stop(); + EXPECT_EQ("Ign:1 Example File from example.org\n" + "Ign:1 Example File from example.org\n" + "Err:1 Example File from example.org\n" + " An error test!\n" + "Ign:1 Example File from example.org\n" + " An error test!\n", out.str()); +} +TEST(AcqProgress, Pulse) +{ + std::ostringstream out; + unsigned int width = 80; + AcqTextStatus Stat(out, width, 0); + _config->Set("APT::Sandbox::User", ""); // ensure we aren't sandboxing + + pkgAcquire Acq(&Stat); + pkgAcquire::ItemDesc fetch; + fetch.URI = "http://example.org/file"; + fetch.Description = "Example File from example.org"; + fetch.ShortDesc = "Example File"; + TestItem fetchO(&Acq); + fetchO.FileSize = 100; + fetchO.Status = pkgAcquire::Item::StatFetching; + fetch.Owner = &fetchO; + + // make screen smaller and bigger again while running + EXPECT_TRUE(Stat.Pulse(&Acq)); + EXPECT_EQ("\r0% [Working]", out.str()); + width = 8; + EXPECT_TRUE(Stat.Pulse(&Acq)); + EXPECT_EQ("\r0% [Working]" + "\r " + "\r0% [Work", out.str()); + width = 80; + EXPECT_TRUE(Stat.Pulse(&Acq)); + EXPECT_EQ("\r0% [Working]" + "\r " + "\r0% [Work" + "\r0% [Working]", out.str()); +} diff --git a/test/libapt/apt-proxy-script b/test/libapt/apt-proxy-script new file mode 100755 index 0000000..41cfdc3 --- /dev/null +++ b/test/libapt/apt-proxy-script @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ $1 = "http://www.debian.org:90/temp/test" ]; then + echo "http://example.com" +fi +if [ $1 = "http://www.debian.org:91/temp/test" ]; then + echo "This works" >&2 + echo "http://example.com/foo" +fi diff --git a/test/libapt/authconf_test.cc b/test/libapt/authconf_test.cc new file mode 100644 index 0000000..3a7b149 --- /dev/null +++ b/test/libapt/authconf_test.cc @@ -0,0 +1,261 @@ +#include <config.h> + +#include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> +#include <apt-pkg/netrc.h> +#include <apt-pkg/strutl.h> + +#include <string> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +TEST(NetRCTest, Parsing) +{ + FileFd fd; + URI U("https://file.not/open"); + EXPECT_FALSE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + EXPECT_EQ("file.not", U.Host); + EXPECT_EQ("/open", U.Path); + + openTemporaryFile("doublesignedfile", fd, R"apt( +machine example.netter login bar password foo +machine example.net login foo password bar + +machine example.org:90 login apt password apt +machine example.org:8080 +login +example password foobar + +machine example.org +login anonymous +password pass + +machine example.com/foo login user1 unknown token password pass1 +machine example.com/bar password pass2 login user2 + unknown token +machine example.com/user login user +machine example.netter login unused password firstentry +machine socks5h://example.last/debian login debian password rules)apt"); + U = URI("https://example.net/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo", U.User); + EXPECT_EQ("bar", U.Password); + EXPECT_EQ("example.net", U.Host); + EXPECT_EQ("/foo", U.Path); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://user:pass@example.net/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("user", U.User); + EXPECT_EQ("pass", U.Password); + EXPECT_EQ("example.net", U.Host); + EXPECT_EQ("/foo", U.Path); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.org:90/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("apt", U.User); + EXPECT_EQ("apt", U.Password); + EXPECT_EQ("example.org", U.Host); + EXPECT_EQ(90u, U.Port); + EXPECT_EQ("/foo", U.Path); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.org:8080/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("example", U.User); + EXPECT_EQ("foobar", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.net:42/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo", U.User); + EXPECT_EQ("bar", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.org/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("anonymous", U.User); + EXPECT_EQ("pass", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.com/apt"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.com/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("user1", U.User); + EXPECT_EQ("pass1", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.com/fooo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("user1", U.User); + EXPECT_EQ("pass1", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.com/fo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.com/bar"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("user2", U.User); + EXPECT_EQ("pass2", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.com/user"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("user", U.User); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("socks5h://example.last/debian"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("debian", U.User); + EXPECT_EQ("rules", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("socks5h://example.debian/"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("socks5h://user:pass@example.debian/"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("user", U.User); + EXPECT_EQ("pass", U.Password); +} +TEST(NetRCTest, BadFileNoMachine) +{ + FileFd fd; + openTemporaryFile("doublesignedfile", fd, R"apt( +foo example.org login foo1 password bar +machin example.org login foo2 password bar +machine2 example.org login foo3 password bar +)apt"); + + URI U("https://example.org/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); +} +TEST(NetRCTest, BadFileEndsMachine) +{ + FileFd fd; + openTemporaryFile("doublesignedfile", fd, R"apt( +machine example.org login foo1 password bar +machine)apt"); + + URI U("https://example.org/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo1", U.User); + EXPECT_EQ("bar", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.net/foo"); + EXPECT_FALSE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://foo:bar@example.net/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo", U.User); + EXPECT_EQ("bar", U.Password); +} +TEST(NetRCTest, BadFileEndsLogin) +{ + FileFd fd; + openTemporaryFile("doublesignedfile", fd, R"apt( +machine example.org login foo1 password bar +machine example.net login)apt"); + + URI U("https://example.org/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo1", U.User); + EXPECT_EQ("bar", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.net/foo"); + EXPECT_FALSE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://foo:bar@example.net/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo", U.User); + EXPECT_EQ("bar", U.Password); +} +TEST(NetRCTest, BadFileEndsPassword) +{ + FileFd fd; + openTemporaryFile("doublesignedfile", fd, R"apt( +machine example.org login foo1 password bar +machine example.net password)apt"); + + URI U("https://example.org/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo1", U.User); + EXPECT_EQ("bar", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://example.net/foo"); + EXPECT_FALSE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://foo:bar@example.net/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo", U.User); + EXPECT_EQ("bar", U.Password); +} + +TEST(NetRCTest, MatchesOnlyHTTPS) +{ + FileFd fd; + openTemporaryFile("doublesignedfile", fd, R"apt( +machine https.example login foo1 password bar +machine http://http.example login foo1 password bar +)apt"); + + URI U("https://https.example/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo1", U.User); + EXPECT_EQ("bar", U.Password); + + _error->PushToStack(); + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://https.example/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + EXPECT_FALSE(_error->empty()); + EXPECT_TRUE(U.Password.empty()); + _error->RevertToStack(); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("http://http.example/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_EQ("foo1", U.User); + EXPECT_EQ("bar", U.Password); + + EXPECT_TRUE(fd.Seek(0)); + U = URI("https://http.example/foo"); + EXPECT_TRUE(MaybeAddAuth(fd, U)); + EXPECT_TRUE(U.User.empty()); + EXPECT_TRUE(U.Password.empty()); + +} diff --git a/test/libapt/cachefilter_test.cc b/test/libapt/cachefilter_test.cc new file mode 100644 index 0000000..08812e0 --- /dev/null +++ b/test/libapt/cachefilter_test.cc @@ -0,0 +1,119 @@ +#include <config.h> + +#include <apt-pkg/cachefilter.h> +#include <apt-pkg/fileutl.h> + +#include <string> + +#include <gtest/gtest.h> + +TEST(CacheFilterTest, ArchitectureSpecification) +{ + { + SCOPED_TRACE("Pattern is *"); + // * should be treated like any + APT::CacheFilter::PackageArchitectureMatchesSpecification ams("*"); + EXPECT_TRUE(ams("sparc")); + EXPECT_TRUE(ams("amd64")); + EXPECT_TRUE(ams("kfreebsd-amd64")); + } + { + SCOPED_TRACE("Pattern is any-i386"); + APT::CacheFilter::PackageArchitectureMatchesSpecification ams("any-i386"); + EXPECT_TRUE(ams("i386")); + EXPECT_FALSE(ams("amd64")); + EXPECT_TRUE(ams("linux-i386")); + EXPECT_FALSE(ams("linux-amd64")); + EXPECT_TRUE(ams("kfreebsd-i386")); + EXPECT_TRUE(ams("musl-linux-i386")); + } + { + SCOPED_TRACE("Pattern is linux-any"); + APT::CacheFilter::PackageArchitectureMatchesSpecification ams("linux-any"); + EXPECT_TRUE(ams("armhf")); + EXPECT_TRUE(ams("armel")); + EXPECT_TRUE(ams("linux-armhf")); + EXPECT_TRUE(ams("linux-armel")); + EXPECT_FALSE(ams("kfreebsd-armhf")); + EXPECT_TRUE(ams("musl-linux-armhf")); + } + if (FileExists(DPKG_DATADIR "/tupletable")) + { + SCOPED_TRACE("Pattern is gnu-any-any"); + APT::CacheFilter::PackageArchitectureMatchesSpecification ams("gnu-any-any"); //really? + EXPECT_TRUE(ams("armhf")); + EXPECT_TRUE(ams("armel")); + EXPECT_TRUE(ams("linux-armhf")); + EXPECT_TRUE(ams("linux-armel")); + EXPECT_TRUE(ams("kfreebsd-armhf")); + EXPECT_FALSE(ams("musl-linux-armhf")); + } + if (FileExists(DPKG_DATADIR "/triplettable")) + { + SCOPED_TRACE("Pattern is gnueabi-any-any"); + APT::CacheFilter::PackageArchitectureMatchesSpecification ams("gnueabi-any-any"); //really? + EXPECT_TRUE(ams("linux-armel")); + EXPECT_TRUE(ams("armel")); + EXPECT_FALSE(ams("armhf")); + EXPECT_FALSE(ams("linux-armhf")); + EXPECT_FALSE(ams("musleabihf-linux-armhf")); + } + if (FileExists(DPKG_DATADIR "/triplettable")) + { + SCOPED_TRACE("Pattern is gnueabihf-any-any"); + APT::CacheFilter::PackageArchitectureMatchesSpecification ams("gnueabihf-any-any"); //really? + EXPECT_FALSE(ams("linux-armel")); + EXPECT_FALSE(ams("armel")); + EXPECT_TRUE(ams("armhf")); + EXPECT_TRUE(ams("linux-armhf")); + EXPECT_FALSE(ams("musleabihf-linux-armhf")); + } + + // Weird ones - armhf's tuple is actually eabihf-gnu-linux-arm + // armel's tuple is actually eabi-gnu-linux-arm + // x32's tuple is actually x32-gnu-linux-amd64 + { + SCOPED_TRACE("Architecture is armhf"); + APT::CacheFilter::PackageArchitectureMatchesSpecification ams("armhf", false); + EXPECT_TRUE(ams("armhf")); + EXPECT_FALSE(ams("armel")); + EXPECT_TRUE(ams("linux-any")); + EXPECT_FALSE(ams("kfreebsd-any")); + EXPECT_TRUE(ams("any-arm")); + EXPECT_TRUE(ams("linux-armhf")); + EXPECT_FALSE(ams("kfreebsd-armhf")); + EXPECT_FALSE(ams("musl-linux-armhf")); + } + { + SCOPED_TRACE("Pattern is any-arm"); + APT::CacheFilter::PackageArchitectureMatchesSpecification ams("any-arm"); + EXPECT_TRUE(ams("armhf")); + EXPECT_TRUE(ams("armel")); + EXPECT_TRUE(ams("linux-armhf")); + EXPECT_TRUE(ams("linux-armel")); + EXPECT_TRUE(ams("musl-linux-armhf")); + EXPECT_TRUE(ams("uclibc-linux-armel")); + + EXPECT_FALSE(ams("arm64")); + EXPECT_FALSE(ams("linux-arm64")); + EXPECT_FALSE(ams("kfreebsd-arm64")); + EXPECT_FALSE(ams("musl-linux-arm64")); + } + { + SCOPED_TRACE("Pattern is any-amd64"); + APT::CacheFilter::PackageArchitectureMatchesSpecification ams("any-amd64"); + EXPECT_TRUE(ams("amd64")); + EXPECT_TRUE(ams("x32")); + EXPECT_TRUE(ams("linux-amd64")); + EXPECT_TRUE(ams("linux-x32")); + EXPECT_TRUE(ams("kfreebsd-amd64")); + EXPECT_TRUE(ams("musl-linux-amd64")); + EXPECT_TRUE(ams("uclibc-linux-amd64")); + + EXPECT_FALSE(ams("i386")); + EXPECT_FALSE(ams("linux-i386")); + EXPECT_FALSE(ams("kfreebsd-i386")); + EXPECT_FALSE(ams("musl-linux-i386")); + EXPECT_FALSE(ams("uclibc-linux-i386")); + } +} diff --git a/test/libapt/cdrom_test.cc b/test/libapt/cdrom_test.cc new file mode 100644 index 0000000..e2ec05a --- /dev/null +++ b/test/libapt/cdrom_test.cc @@ -0,0 +1,109 @@ +#include <config.h> + +#include <apt-pkg/cdrom.h> +#include <apt-pkg/cdromutl.h> +#include <apt-pkg/configuration.h> +#include <apt-pkg/fileutl.h> + +#include <string> +#include <vector> +#include <string.h> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +class Cdrom : public pkgCdrom { +public: + std::vector<std::string> ReduceSourcelist(std::string CD,std::vector<std::string> List) { + pkgCdrom::ReduceSourcelist(CD, List); + return List; + } +}; + +TEST(CDROMTest,ReduceSourcelist) +{ + Cdrom cd; + std::vector<std::string> List; + std::string CD("/media/cdrom/"); + + std::vector<std::string> R = cd.ReduceSourcelist(CD, List); + EXPECT_TRUE(R.empty()); + + List.push_back(" wheezy main"); + R = cd.ReduceSourcelist(CD, List); + ASSERT_EQ(1u, R.size()); + EXPECT_EQ(" wheezy main", R[0]); + + List.push_back(" wheezy main"); + R = cd.ReduceSourcelist(CD, List); + ASSERT_EQ(1u, R.size()); + EXPECT_EQ(" wheezy main", R[0]); + + List.push_back(" wheezy contrib"); + R = cd.ReduceSourcelist(CD, List); + ASSERT_EQ(1u, R.size()); + EXPECT_EQ(" wheezy contrib main", R[0]); + + List.push_back(" wheezy-update contrib"); + R = cd.ReduceSourcelist(CD, List); + ASSERT_EQ(2u, R.size()); + EXPECT_EQ(" wheezy contrib main", R[0]); + EXPECT_EQ(" wheezy-update contrib", R[1]); + + List.push_back(" wheezy-update contrib"); + R = cd.ReduceSourcelist(CD, List); + ASSERT_EQ(2u, R.size()); + EXPECT_EQ(" wheezy contrib main", R[0]); + EXPECT_EQ(" wheezy-update contrib", R[1]); + + List.push_back(" wheezy-update non-free"); + R = cd.ReduceSourcelist(CD, List); + ASSERT_EQ(2u, R.size()); + EXPECT_EQ(" wheezy contrib main", R[0]); + EXPECT_EQ(" wheezy-update contrib non-free", R[1]); + + List.push_back(" wheezy-update main"); + R = cd.ReduceSourcelist(CD, List); + ASSERT_EQ(2u, R.size()); + EXPECT_EQ(" wheezy contrib main", R[0]); + EXPECT_EQ(" wheezy-update contrib main non-free", R[1]); + + List.push_back(" wheezy non-free"); + R = cd.ReduceSourcelist(CD, List); + ASSERT_EQ(2u, R.size()); + EXPECT_EQ(" wheezy contrib main non-free", R[0]); + EXPECT_EQ(" wheezy-update contrib main non-free", R[1]); + + List.push_back(" sid main"); + R = cd.ReduceSourcelist(CD, List); + ASSERT_EQ(3u, R.size()); + EXPECT_EQ(" sid main", R[0]); + EXPECT_EQ(" wheezy contrib main non-free", R[1]); + EXPECT_EQ(" wheezy-update contrib main non-free", R[2]); + + List.push_back(" sid main-reduce"); + R = cd.ReduceSourcelist(CD, List); + ASSERT_EQ(3u, R.size()); + EXPECT_EQ(" sid main main-reduce", R[0]); + EXPECT_EQ(" wheezy contrib main non-free", R[1]); + EXPECT_EQ(" wheezy-update contrib main non-free", R[2]); +} +TEST(CDROMTest, FindMountPointForDevice) +{ + auto const file = createTemporaryFile("mountpoints", + "rootfs / rootfs rw 0 0\n" + "sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0\n" + "sysfs0 /sys0 sysfs rw,nosuid,nodev,noexec,relatime 0 0\n" + "/dev/disk/by-uuid/fadcbc52-6284-4874-aaaa-dcee1f05fe21 / ext4 rw,relatime,errors=remount-ro,data=ordered 0 0\n" + "/dev/sda1 /boot/efi vfat rw,nosuid,nodev,noexec,relatime,fmask=0000,dmask=0000,allow_utime=0022,codepage=437,iocharset=utf8,shortname=lower,quiet,utf8,errors=remount-ro,rw,nosuid,nodev,noexec,relatime,fmask=0000,dmask=0000,allow_utime=0022,codepage=437,iocharset=utf8,shortname=lower,quiet,utf8,errors=remount-ro,rw,nosuid,nodev,noexec,relatime,fmask=0000,dmask=0000,allow_utime=0022,codepage=437,iocharset=utf8,shortname=lower,quiet,utf8,errors=remount-ro,rw,nosuid,nodev,noexec,relatime,fmask=0000,dmask=0000,allow_utime=0022,codepage=437,iocharset=utf8,shortname=lower,quiet,utf8,errors=remount-ro 0 0\n" + "tmpfs /tmp tmpfs rw,nosuid,nodev,relatime 0 0\n"); + _config->Set("Dir::state::Mountpoints", file.Name()); + + EXPECT_EQ("/", FindMountPointForDevice("rootfs")); + EXPECT_EQ("/", FindMountPointForDevice("/dev/disk/by-uuid/fadcbc52-6284-4874-aaaa-dcee1f05fe21")); + EXPECT_EQ("/sys", FindMountPointForDevice("sysfs")); + EXPECT_EQ("/sys0", FindMountPointForDevice("sysfs0")); + EXPECT_EQ("/boot/efi", FindMountPointForDevice("/dev/sda1")); + EXPECT_EQ("/tmp", FindMountPointForDevice("tmpfs")); +} diff --git a/test/libapt/cdromfindpackages_test.cc b/test/libapt/cdromfindpackages_test.cc new file mode 100644 index 0000000..cffa7aa --- /dev/null +++ b/test/libapt/cdromfindpackages_test.cc @@ -0,0 +1,125 @@ +#include <config.h> + +#include <apt-pkg/cdrom.h> +#include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> + +#include <algorithm> +#include <iostream> +#include <string> +#include <vector> +#include <stddef.h> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +class Cdrom : public pkgCdrom { + public: + bool FindPackages(std::string const &CD, + std::vector<std::string> &List, + std::vector<std::string> &SList, + std::vector<std::string> &SigList, + std::vector<std::string> &TransList, + std::string &InfoDir) { + std::string const startdir = SafeGetCWD(); + EXPECT_FALSE(startdir.empty()); + EXPECT_TRUE(InfoDir.empty()); + bool const result = pkgCdrom::FindPackages(CD, List, SList, SigList, TransList, InfoDir, NULL, 0); + EXPECT_FALSE(InfoDir.empty()); + std::sort(List.begin(), List.end()); + std::sort(SList.begin(), SList.end()); + std::sort(SigList.begin(), SigList.end()); + std::sort(TransList.begin(), TransList.end()); + EXPECT_EQ(0, chdir(startdir.c_str())); + return result; + } + + using pkgCdrom::DropRepeats; +}; + +TEST(CDROMTest,FindPackages) +{ + std::string path; + createTemporaryDirectory("findpackage", path); + + createDirectory(path, ".disk"); + createDirectory(path, "pool"); + createDirectory(path, "dists/stable/main/binary-i386"); + createDirectory(path, "dists/stable/main/source"); + createDirectory(path, "dists/stable/contrib/binary-amd64"); + createDirectory(path, "dists/stable/non-free/binary-all"); + createDirectory(path, "dists/unstable/main/binary-i386"); + createDirectory(path, "dists/unstable/main/i18n"); + createDirectory(path, "dists/unstable/main/source"); + createDirectory(path, "dists/broken/non-free/source"); + createFile(path, "dists/broken/.aptignr"); + createFile(path, "dists/stable/main/binary-i386/Packages"); + createFile(path, "dists/stable/main/binary-i386/Packages.bz2"); + createFile(path, "dists/stable/main/source/Sources.xz"); + createFile(path, "dists/stable/contrib/binary-amd64/Packages"); + createFile(path, "dists/stable/contrib/binary-amd64/Packages.gz"); + createFile(path, "dists/stable/non-free/binary-all/Packages"); + createFile(path, "dists/unstable/main/binary-i386/Packages.xz"); + createFile(path, "dists/unstable/main/binary-i386/Packages.lzma"); + createFile(path, "dists/unstable/main/i18n/Translation-en"); + createFile(path, "dists/unstable/main/i18n/Translation-de.bz2"); + createFile(path, "dists/unstable/main/source/Sources.xz"); + createFile(path, "dists/broken/non-free/source/Sources.gz"); + createFile(path, "dists/stable/Release.gpg"); + createFile(path, "dists/stable/Release"); + createFile(path, "dists/unstable/InRelease"); + createFile(path, "dists/broken/Release.gpg"); + createLink(path, "dists/unstable", "dists/sid"); + + Cdrom cd; + std::vector<std::string> Packages, Sources, Signatur, Translation; + std::string InfoDir; + EXPECT_TRUE(cd.FindPackages(path, Packages, Sources, Signatur, Translation, InfoDir)); + EXPECT_EQ(5u, Packages.size()); + EXPECT_EQ(path + "/dists/sid/main/binary-i386/", Packages[0]); + EXPECT_EQ(path + "/dists/stable/contrib/binary-amd64/", Packages[1]); + EXPECT_EQ(path + "/dists/stable/main/binary-i386/", Packages[2]); + EXPECT_EQ(path + "/dists/stable/non-free/binary-all/", Packages[3]); + EXPECT_EQ(path + "/dists/unstable/main/binary-i386/", Packages[4]); + EXPECT_EQ(3u, Sources.size()); + EXPECT_EQ(path + "/dists/sid/main/source/", Sources[0]); + EXPECT_EQ(path + "/dists/stable/main/source/", Sources[1]); + EXPECT_EQ(path + "/dists/unstable/main/source/", Sources[2]); + EXPECT_EQ(3u, Signatur.size()); + EXPECT_EQ(path + "/dists/sid/", Signatur[0]); + EXPECT_EQ(path + "/dists/stable/", Signatur[1]); + EXPECT_EQ(path + "/dists/unstable/", Signatur[2]); + EXPECT_EQ(4u, Translation.size()); + EXPECT_EQ(path + "/dists/sid/main/i18n/Translation-de", Translation[0]); + EXPECT_EQ(path + "/dists/sid/main/i18n/Translation-en", Translation[1]); + EXPECT_EQ(path + "/dists/unstable/main/i18n/Translation-de", Translation[2]); + EXPECT_EQ(path + "/dists/unstable/main/i18n/Translation-en", Translation[3]); + EXPECT_EQ(path + "/.disk/", InfoDir); + + cd.DropRepeats(Packages, "Packages"); + cd.DropRepeats(Sources, "Sources"); + _error->PushToStack(); + cd.DropRepeats(Signatur, "InRelease"); + cd.DropRepeats(Signatur, "Release.gpg"); + _error->RevertToStack(); + _error->DumpErrors(); + cd.DropRepeats(Translation, ""); + + EXPECT_EQ(4u, Packages.size()); + EXPECT_EQ(path + "/dists/stable/contrib/binary-amd64/", Packages[0]); + EXPECT_EQ(path + "/dists/stable/main/binary-i386/", Packages[1]); + EXPECT_EQ(path + "/dists/stable/non-free/binary-all/", Packages[2]); + EXPECT_EQ(path + "/dists/unstable/main/binary-i386/", Packages[3]); + EXPECT_EQ(2u, Sources.size()); + EXPECT_EQ(path + "/dists/stable/main/source/", Sources[0]); + EXPECT_EQ(path + "/dists/unstable/main/source/", Sources[1]); + EXPECT_EQ(2u, Signatur.size()); + EXPECT_EQ(path + "/dists/stable/", Signatur[0]); + EXPECT_EQ(path + "/dists/unstable/", Signatur[1]); + EXPECT_EQ(2u, Translation.size()); + EXPECT_EQ(path + "/dists/unstable/main/i18n/Translation-de", Translation[0]); + EXPECT_EQ(path + "/dists/unstable/main/i18n/Translation-en", Translation[1]); + + removeDirectory(path); +} diff --git a/test/libapt/commandline_test.cc b/test/libapt/commandline_test.cc new file mode 100644 index 0000000..cde80b4 --- /dev/null +++ b/test/libapt/commandline_test.cc @@ -0,0 +1,238 @@ +#include <config.h> + +#include <apt-pkg/cmndline.h> +#include <apt-pkg/configuration.h> +#include <apt-private/private-cmndline.h> + +#include <gtest/gtest.h> + +class CLT: public CommandLine { + public: + std::string static AsString(const char * const * const argv, + unsigned int const argc) { + std::string const static conf = "Commandline::AsString"; + _config->Clear(conf); + SaveInConfig(argc, argv); + return _config->Find(conf); + } +}; + +TEST(CommandLineTest,SaveInConfig) +{ +#define APT_EXPECT_CMD(x, ...) { const char * const argv[] = { __VA_ARGS__ }; EXPECT_EQ(x, CLT::AsString(argv, sizeof(argv)/sizeof(argv[0]))); } + APT_EXPECT_CMD("apt-get install -sf", + "apt-get", "install", "-sf"); + APT_EXPECT_CMD("apt-cache -s apt -so Debug::test=Test", + "apt-cache", "-s", "apt", "-so", "Debug::test=Test"); + APT_EXPECT_CMD("apt-cache -s apt -so Debug::test='Das ist ein Test'", + "apt-cache", "-s", "apt", "-so", "Debug::test=Das ist ein Test"); + APT_EXPECT_CMD("apt-cache -s apt -so Debug::test='Das ist ein Test'", + "apt-cache", "-s", "apt", "-so", "Debug::test=\"Das ist ein Test\""); + APT_EXPECT_CMD("apt-cache -s apt -so Debug::test='Das ist ein Test' foo", + "apt-cache", "-s", "apt", "-so", "\"Debug::test=Das ist ein Test\"", "foo"); + APT_EXPECT_CMD("apt-cache -s apt -so Debug::test='Das ist ein Test' foo", + "apt-cache", "-s", "apt", "-so", "\'Debug::test=Das ist ein Test\'", "foo"); + APT_EXPECT_CMD("apt-cache -s apt -so Debug::test='That is crazy!' foo", + "apt-cache", "-s", "apt", "-so", "\'Debug::test=That \ris\n crazy!\'", "foo"); + APT_EXPECT_CMD("apt-cache -s apt --hallo test=1.0", + "apt-cache", "-s", "apt", "--hallo", "test=1.0"); +#undef APT_EXPECT_CMD +} +TEST(CommandLineTest,Parsing) +{ + CommandLine::Args Args[] = { + { 't', 0, "Test::Worked", 0 }, + { 'T', "testing", "Test::Worked", CommandLine::HasArg }, + { 'z', "zero", "Test::Zero", 0 }, + { 'o', "option", 0, CommandLine::ArbItem }, + {0,0,0,0} + }; + ::Configuration c; + CommandLine CmdL(Args, &c); + + char const * argv[] = { "test", "--zero", "-t" }; + CmdL.Parse(3 , argv); + EXPECT_TRUE(c.FindB("Test::Worked", false)); + EXPECT_TRUE(c.FindB("Test::Zero", false)); + + c.Clear("Test"); + EXPECT_FALSE(c.FindB("Test::Worked", false)); + EXPECT_FALSE(c.FindB("Test::Zero", false)); + + c.Set("Test::Zero", true); + EXPECT_TRUE(c.FindB("Test::Zero", false)); + + char const * argv2[] = { "test", "--no-zero", "-t" }; + CmdL.Parse(3 , argv2); + EXPECT_TRUE(c.FindB("Test::Worked", false)); + EXPECT_FALSE(c.FindB("Test::Zero", false)); + + c.Clear("Test"); + { + char const * argv[] = { "test", "-T", "yes" }; + CmdL.Parse(3 , argv); + EXPECT_TRUE(c.FindB("Test::Worked", false)); + EXPECT_EQ("yes", c.Find("Test::Worked", "no")); + EXPECT_EQ(0u, CmdL.FileSize()); + } + c.Clear("Test"); + { + char const * argv[] = { "test", "-T=yes" }; + CmdL.Parse(2 , argv); + EXPECT_TRUE(c.Exists("Test::Worked")); + EXPECT_EQ("yes", c.Find("Test::Worked", "no")); + EXPECT_EQ(0u, CmdL.FileSize()); + } + c.Clear("Test"); + { + char const * argv[] = { "test", "-T=", "yes" }; + CmdL.Parse(3 , argv); + EXPECT_TRUE(c.Exists("Test::Worked")); + EXPECT_EQ("no", c.Find("Test::Worked", "no")); + EXPECT_EQ(1u, CmdL.FileSize()); + } + + c.Clear("Test"); + { + char const * argv[] = { "test", "--testing", "yes" }; + CmdL.Parse(3 , argv); + EXPECT_TRUE(c.FindB("Test::Worked", false)); + EXPECT_EQ("yes", c.Find("Test::Worked", "no")); + EXPECT_EQ(0u, CmdL.FileSize()); + } + c.Clear("Test"); + { + char const * argv[] = { "test", "--testing=yes" }; + CmdL.Parse(2 , argv); + EXPECT_TRUE(c.Exists("Test::Worked")); + EXPECT_EQ("yes", c.Find("Test::Worked", "no")); + EXPECT_EQ(0u, CmdL.FileSize()); + } + c.Clear("Test"); + { + char const * argv[] = { "test", "--testing=", "yes" }; + CmdL.Parse(3 , argv); + EXPECT_TRUE(c.Exists("Test::Worked")); + EXPECT_EQ("no", c.Find("Test::Worked", "no")); + EXPECT_EQ(1u, CmdL.FileSize()); + } + + c.Clear("Test"); + { + char const * argv[] = { "test", "-o", "test::worked=yes" }; + CmdL.Parse(3 , argv); + EXPECT_TRUE(c.FindB("Test::Worked", false)); + EXPECT_EQ("yes", c.Find("Test::Worked", "no")); + } + c.Clear("Test"); + { + char const * argv[] = { "test", "-o", "test::worked=" }; + CmdL.Parse(3 , argv); + EXPECT_TRUE(c.Exists("Test::Worked")); + EXPECT_EQ("no", c.Find("Test::Worked", "no")); + } + c.Clear("Test"); + { + char const * argv[] = { "test", "-o", "test::worked=", "yes" }; + CmdL.Parse(4 , argv); + EXPECT_TRUE(c.Exists("Test::Worked")); + EXPECT_EQ("no", c.Find("Test::Worked", "no")); + } + c.Clear("Test"); +} + +TEST(CommandLineTest, BoolParsing) +{ + CommandLine::Args Args[] = { + { 't', 0, "Test::Worked", 0 }, + {0,0,0,0} + }; + ::Configuration c; + CommandLine CmdL(Args, &c); + + // the commandline parser used to use strtol() on the argument + // to check if the argument is a boolean expression - that + // stopped after the "0". this test ensures that we always check + // that the entire string was consumed by strtol + { + char const * argv[] = { "show", "-t", "0ad" }; + bool res = CmdL.Parse(sizeof(argv)/sizeof(char*), argv); + EXPECT_TRUE(res); + ASSERT_EQ(std::string(CmdL.FileList[0]), "0ad"); + } + + { + char const * argv[] = { "show", "-t", "0", "ad" }; + bool res = CmdL.Parse(sizeof(argv)/sizeof(char*), argv); + EXPECT_TRUE(res); + ASSERT_EQ(std::string(CmdL.FileList[0]), "ad"); + } + +} + +static bool DoVoid(CommandLine &) { return false; } + +TEST(CommandLineTest,GetCommand) +{ + CommandLine::Dispatch Cmds[] = { {"install",&DoVoid}, {"remove", &DoVoid}, {0,0} }; + { + char const * argv[] = { "apt-get", "-t", "unstable", "remove", "-d", "foo" }; + char const * com = CommandLine::GetCommand(Cmds, sizeof(argv)/sizeof(argv[0]), argv); + EXPECT_STREQ("remove", com); + std::vector<CommandLine::Args> Args = getCommandArgs(APT_CMD::APT_GET, com); + ::Configuration c; + CommandLine CmdL(Args.data(), &c); + ASSERT_TRUE(CmdL.Parse(sizeof(argv)/sizeof(argv[0]), argv)); + EXPECT_EQ(c.Find("APT::Default-Release"), "unstable"); + EXPECT_TRUE(c.FindB("APT::Get::Download-Only")); + ASSERT_EQ(2u, CmdL.FileSize()); + EXPECT_EQ(std::string(CmdL.FileList[0]), "remove"); + EXPECT_EQ(std::string(CmdL.FileList[1]), "foo"); + } + { + char const * argv[] = {"apt-get", "-t", "unstable", "remove", "--", "-d", "foo" }; + char const * com = CommandLine::GetCommand(Cmds, sizeof(argv)/sizeof(argv[0]), argv); + EXPECT_STREQ("remove", com); + std::vector<CommandLine::Args> Args = getCommandArgs(APT_CMD::APT_GET, com); + ::Configuration c; + CommandLine CmdL(Args.data(), &c); + ASSERT_TRUE(CmdL.Parse(sizeof(argv)/sizeof(argv[0]), argv)); + EXPECT_EQ(c.Find("APT::Default-Release"), "unstable"); + EXPECT_FALSE(c.FindB("APT::Get::Download-Only")); + ASSERT_EQ(3u, CmdL.FileSize()); + EXPECT_EQ(std::string(CmdL.FileList[0]), "remove"); + EXPECT_EQ(std::string(CmdL.FileList[1]), "-d"); + EXPECT_EQ(std::string(CmdL.FileList[2]), "foo"); + } + { + char const * argv[] = {"apt-get", "-t", "unstable", "--", "remove", "-d", "foo" }; + char const * com = CommandLine::GetCommand(Cmds, sizeof(argv)/sizeof(argv[0]), argv); + EXPECT_STREQ("remove", com); + std::vector<CommandLine::Args> Args = getCommandArgs(APT_CMD::APT_GET, com); + ::Configuration c; + CommandLine CmdL(Args.data(), &c); + ASSERT_TRUE(CmdL.Parse(sizeof(argv)/sizeof(argv[0]), argv)); + EXPECT_EQ(c.Find("APT::Default-Release"), "unstable"); + EXPECT_FALSE(c.FindB("APT::Get::Download-Only")); + ASSERT_EQ(3u, CmdL.FileSize()); + EXPECT_EQ(std::string(CmdL.FileList[0]), "remove"); + EXPECT_EQ(std::string(CmdL.FileList[1]), "-d"); + EXPECT_EQ(std::string(CmdL.FileList[2]), "foo"); + } + { + char const * argv[] = {"apt-get", "install", "-t", "unstable", "--", "remove", "-d", "foo" }; + char const * com = CommandLine::GetCommand(Cmds, sizeof(argv)/sizeof(argv[0]), argv); + EXPECT_STREQ("install", com); + std::vector<CommandLine::Args> Args = getCommandArgs(APT_CMD::APT_GET, com); + ::Configuration c; + CommandLine CmdL(Args.data(), &c); + ASSERT_TRUE(CmdL.Parse(sizeof(argv)/sizeof(argv[0]), argv)); + EXPECT_EQ(c.Find("APT::Default-Release"), "unstable"); + EXPECT_FALSE(c.FindB("APT::Get::Download-Only")); + ASSERT_EQ(4u, CmdL.FileSize()); + EXPECT_EQ(std::string(CmdL.FileList[0]), "install"); + EXPECT_EQ(std::string(CmdL.FileList[1]), "remove"); + EXPECT_EQ(std::string(CmdL.FileList[2]), "-d"); + EXPECT_EQ(std::string(CmdL.FileList[3]), "foo"); + } +} diff --git a/test/libapt/compareversion_test.cc b/test/libapt/compareversion_test.cc new file mode 100644 index 0000000..0129322 --- /dev/null +++ b/test/libapt/compareversion_test.cc @@ -0,0 +1,182 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +/* ###################################################################### + + Version Test - Simple program to run through a file and comare versions. + + Each version is compared and the result is checked against an expected + result in the file. The format of the file is + a b Res + Where Res is -1, 1, 0. dpkg -D=1 --compare-versions a "<" b can be + used to determine what Res should be. # at the start of the line + is a comment and blank lines are skipped + + The runner will also call dpkg --compare-versions to check if APT and + dpkg have (still) the same idea. + + ##################################################################### */ + /*}}}*/ +#include <config.h> + +#include <apt-pkg/debversion.h> +#include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> + +#include <fstream> +#include <string> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <gtest/gtest.h> + +using namespace std; + +static bool callDPKG(const char * const val, const char * const ref, char const * const op) { + pid_t Process = ExecFork(); + if (Process == 0) + { + const char * args[] = { + "dpkg", + "--compare-versions", + val, + op, + ref, + nullptr + }; + execvp(args[0], (char**) args); + exit(1); + } + int Ret; + waitpid(Process, &Ret, 0); + EXPECT_TRUE(WIFEXITED(Ret)); + return WEXITSTATUS(Ret) == 0; +} + + +#define EXPECT_VERSION_PART(A, compare, B) \ +{ \ + int Res = debVS.CmpVersion(A, B); \ + Res = (Res < 0) ? -1 : ( (Res > 0) ? 1 : Res); \ + EXPECT_EQ(compare, Res) << "APT: A: »" << A << "« B: »" << B << "«"; \ + EXPECT_PRED3(callDPKG, A, B, ((compare == 1) ? ">>" : ( (compare == 0) ? "=" : "<<"))); \ +} +#define EXPECT_VERSION(A, compare, B) \ + EXPECT_VERSION_PART(A, compare, B); \ + EXPECT_VERSION_PART(B, compare * -1, A) + +// History-Remark: The versions used to be specified in a versions.lst file + +enum CompareVersionType { LESS = -1, GREATER = 1, EQUAL = 0 }; + +TEST(CompareVersionTest,Basic) +{ + EXPECT_VERSION("7.6p2-4", GREATER, "7.6-0"); + EXPECT_VERSION("1.0.3-3", GREATER, "1.0-1"); + EXPECT_VERSION("1.3", GREATER, "1.2.2-2"); + EXPECT_VERSION("1.3", GREATER, "1.2.2"); + + /* disabled as dpkg doesn't like them… (versions have to start with a number) + EXPECT_VERSION("-", LESS, "."); + EXPECT_VERSION("p", LESS, "-"); + EXPECT_VERSION("a", LESS, "-"); + EXPECT_VERSION("z", LESS, "-"); + EXPECT_VERSION("a", LESS, "."); + EXPECT_VERSION("z", LESS, "."); + // */ + + /* disabled as dpkg doesn't like them… (versions have to start with a number) + EXPECT_VERSION("III-alpha9.8", LESS, "III-alpha9.8-1.5"); + // */ + + // Test some properties of text strings + EXPECT_VERSION("0-pre", EQUAL, "0-pre"); + EXPECT_VERSION("0-pre", LESS, "0-pree"); + + EXPECT_VERSION("1.1.6r2-2", GREATER, "1.1.6r-1"); + EXPECT_VERSION("2.6b2-1", GREATER, "2.6b-2"); + + EXPECT_VERSION("98.1p5-1", LESS, "98.1-pre2-b6-2"); + EXPECT_VERSION("0.4a6-2", GREATER, "0.4-1"); + + EXPECT_VERSION("1:3.0.5-2", LESS, "1:3.0.5.1"); +} +TEST(CompareVersionTest,Epochs) +{ + EXPECT_VERSION("1:0.4", GREATER, "10.3"); + EXPECT_VERSION("1:1.25-4", LESS, "1:1.25-8"); + EXPECT_VERSION("0:1.18.36", EQUAL, "1.18.36"); + + EXPECT_VERSION("1.18.36", GREATER, "1.18.35"); + EXPECT_VERSION("0:1.18.36", GREATER, "1.18.35"); +} +TEST(CompareVersionTest,Strangeness) +{ + // Funky, but allowed, characters in upstream version + EXPECT_VERSION("9:1.18.36:5.4-20", LESS, "10:0.5.1-22"); + EXPECT_VERSION("9:1.18.36:5.4-20", LESS, "9:1.18.36:5.5-1"); + EXPECT_VERSION("9:1.18.36:5.4-20", LESS, " 9:1.18.37:4.3-22"); + EXPECT_VERSION("1.18.36-0.17.35-18", GREATER, "1.18.36-19"); + + // Junk + EXPECT_VERSION("1:1.2.13-3", LESS, "1:1.2.13-3.1"); + EXPECT_VERSION("2.0.7pre1-4", LESS, "2.0.7r-1"); + + // if a version includes a dash, it should be the debrev dash - policy says so… + EXPECT_VERSION("0:0-0-0", GREATER, "0-0"); + + // do we like strange versions? Yes we like strange versions… + EXPECT_VERSION("0", EQUAL, "0"); + EXPECT_VERSION("0", EQUAL, "00"); +} +TEST(CompareVersionTest,DebianBug) +{ + // #205960 + EXPECT_VERSION("3.0~rc1-1", LESS, "3.0-1"); + // #573592 - debian policy 5.6.12 + EXPECT_VERSION("1.0", EQUAL, "1.0-0"); + EXPECT_VERSION("0.2", LESS, "1.0-0"); + EXPECT_VERSION("1.0", LESS, "1.0-0+b1"); + EXPECT_VERSION("1.0", GREATER, "1.0-0~"); +} +TEST(CompareVersionTest,CuptTests) +{ + // "steal" the testcases from (old perl) cupt + EXPECT_VERSION("1.2.3", EQUAL, "1.2.3"); // identical + EXPECT_VERSION("4.4.3-2", EQUAL, "4.4.3-2"); // identical + EXPECT_VERSION("1:2ab:5", EQUAL, "1:2ab:5"); // this is correct... + EXPECT_VERSION("7:1-a:b-5", EQUAL, "7:1-a:b-5"); // and this + EXPECT_VERSION("57:1.2.3abYZ+~-4-5", EQUAL, "57:1.2.3abYZ+~-4-5"); // and those too + EXPECT_VERSION("1.2.3", EQUAL, "0:1.2.3"); // zero epoch + EXPECT_VERSION("1.2.3", EQUAL, "1.2.3-0"); // zero revision + EXPECT_VERSION("009", EQUAL, "9"); // zeroes… + EXPECT_VERSION("009ab5", EQUAL, "9ab5"); // there as well + EXPECT_VERSION("1.2.3", LESS, "1.2.3-1"); // added non-zero revision + EXPECT_VERSION("1.2.3", LESS, "1.2.4"); // just bigger + EXPECT_VERSION("1.2.4", GREATER, "1.2.3"); // order doesn't matter + EXPECT_VERSION("1.2.24", GREATER, "1.2.3"); // bigger, eh? + EXPECT_VERSION("0.10.0", GREATER, "0.8.7"); // bigger, eh? + EXPECT_VERSION("3.2", GREATER, "2.3"); // major number rocks + EXPECT_VERSION("1.3.2a", GREATER, "1.3.2"); // letters rock + EXPECT_VERSION("0.5.0~git", LESS, "0.5.0~git2"); // numbers rock + EXPECT_VERSION("2a", LESS, "21"); // but not in all places + EXPECT_VERSION("1.3.2a", LESS, "1.3.2b"); // but there is another letter + EXPECT_VERSION("1:1.2.3", GREATER, "1.2.4"); // epoch rocks + EXPECT_VERSION("1:1.2.3", LESS, "1:1.2.4"); // bigger anyway + EXPECT_VERSION("1.2a+~bCd3", LESS, "1.2a++"); // tilde doesn't rock + EXPECT_VERSION("1.2a+~bCd3", GREATER, "1.2a+~"); // but first is longer! + EXPECT_VERSION("5:2", GREATER, "304-2"); // epoch rocks + EXPECT_VERSION("5:2", LESS, "304:2"); // so big epoch? + EXPECT_VERSION("25:2", GREATER, "3:2"); // 25 > 3, obviously + EXPECT_VERSION("1:2:123", LESS, "1:12:3"); // 12 > 2 + EXPECT_VERSION("1.2-5", LESS, "1.2-3-5"); // 1.2 < 1.2-3 + EXPECT_VERSION("5.10.0", GREATER, "5.005"); // preceding zeroes don't matters + EXPECT_VERSION("3a9.8", LESS, "3.10.2"); // letters are before all letter symbols + EXPECT_VERSION("3a9.8", GREATER, "3~10"); // but after the tilde + EXPECT_VERSION("1.4+OOo3.0.0~", LESS, "1.4+OOo3.0.0-4"); // another tilde check + EXPECT_VERSION("2.4.7-1", LESS, "2.4.7-z"); // revision comparing + EXPECT_VERSION("1.002-1+b2", GREATER, "1.00"); // whatever... + /* disabled as dpkg doesn't like them… (versions with illegal char) + EXPECT_VERSION("2.2.4-47978_Debian_lenny", EQUAL, "2.2.4-47978_Debian_lenny"); // and underscore... + // */ +} diff --git a/test/libapt/configuration_test.cc b/test/libapt/configuration_test.cc new file mode 100644 index 0000000..4d297a9 --- /dev/null +++ b/test/libapt/configuration_test.cc @@ -0,0 +1,232 @@ +#include <config.h> + +#include <apt-pkg/configuration.h> +#include <apt-pkg/fileutl.h> + +#include <string> +#include <vector> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +TEST(ConfigurationTest,Lists) +{ + Configuration Cnf; + + Cnf.Set("APT::Keep-Fds::",28); + Cnf.Set("APT::Keep-Fds::",17); + Cnf.Set("APT::Keep-Fds::2",47); + Cnf.Set("APT::Keep-Fds::","broken"); + std::vector<std::string> fds = Cnf.FindVector("APT::Keep-Fds"); + ASSERT_EQ(4u, fds.size()); + EXPECT_EQ("28", fds[0]); + EXPECT_EQ("17", fds[1]); + EXPECT_EQ("47", fds[2]); + EXPECT_EQ("broken", fds[3]); + + EXPECT_TRUE(Cnf.Exists("APT::Keep-Fds::2")); + EXPECT_EQ("47", Cnf.Find("APT::Keep-Fds::2")); + EXPECT_EQ(47, Cnf.FindI("APT::Keep-Fds::2")); + EXPECT_FALSE(Cnf.Exists("APT::Keep-Fds::3")); + EXPECT_EQ("", Cnf.Find("APT::Keep-Fds::3")); + EXPECT_EQ(56, Cnf.FindI("APT::Keep-Fds::3", 56)); + EXPECT_EQ("not-set", Cnf.Find("APT::Keep-Fds::3", "not-set")); + + Cnf.Clear("APT::Keep-Fds::2"); + EXPECT_TRUE(Cnf.Exists("APT::Keep-Fds::2")); + fds = Cnf.FindVector("APT::Keep-Fds"); + ASSERT_EQ(4u, fds.size()); + EXPECT_EQ("28", fds[0]); + EXPECT_EQ("17", fds[1]); + EXPECT_EQ("", fds[2]); + EXPECT_EQ("broken", fds[3]); + + Cnf.Clear("APT::Keep-Fds",28); + fds = Cnf.FindVector("APT::Keep-Fds"); + ASSERT_EQ(3u, fds.size()); + EXPECT_EQ("17", fds[0]); + EXPECT_EQ("", fds[1]); + EXPECT_EQ("broken", fds[2]); + + Cnf.Clear("APT::Keep-Fds",""); + EXPECT_FALSE(Cnf.Exists("APT::Keep-Fds::2")); + + Cnf.Clear("APT::Keep-Fds",17); + Cnf.Clear("APT::Keep-Fds","broken"); + fds = Cnf.FindVector("APT::Keep-Fds"); + EXPECT_TRUE(fds.empty()); + + Cnf.Set("APT::Keep-Fds::",21); + Cnf.Set("APT::Keep-Fds::",42); + fds = Cnf.FindVector("APT::Keep-Fds"); + ASSERT_EQ(2u, fds.size()); + EXPECT_EQ("21", fds[0]); + EXPECT_EQ("42", fds[1]); + + Cnf.Clear("APT::Keep-Fds"); + fds = Cnf.FindVector("APT::Keep-Fds"); + EXPECT_TRUE(fds.empty()); +} +TEST(ConfigurationTest,Integers) +{ + Configuration Cnf; + + Cnf.CndSet("APT::Version", 42); + Cnf.CndSet("APT::Version", "66"); + EXPECT_EQ("42", Cnf.Find("APT::Version")); + EXPECT_EQ(42, Cnf.FindI("APT::Version")); + EXPECT_EQ("42", Cnf.Find("APT::Version", "33")); + EXPECT_EQ(42, Cnf.FindI("APT::Version", 33)); + EXPECT_EQ("33", Cnf.Find("APT2::Version", "33")); + EXPECT_EQ(33, Cnf.FindI("APT2::Version", 33)); +} +TEST(ConfigurationTest,DirsAndFiles) +{ + Configuration Cnf; + + EXPECT_EQ("", Cnf.FindFile("Dir::State")); + EXPECT_EQ("", Cnf.FindFile("Dir::Aptitude::State")); + Cnf.Set("Dir", "/srv/sid"); + EXPECT_EQ("", Cnf.FindFile("Dir::State")); + Cnf.Set("Dir::State", "var/lib/apt"); + Cnf.Set("Dir::Aptitude::State", "var/lib/aptitude"); + EXPECT_EQ("/srv/sid/var/lib/apt", Cnf.FindFile("Dir::State")); + EXPECT_EQ("/srv/sid/var/lib/aptitude", Cnf.FindFile("Dir::Aptitude::State")); + + Cnf.Set("RootDir", "/"); + EXPECT_EQ("/srv/sid/var/lib/apt", Cnf.FindFile("Dir::State")); + EXPECT_EQ("/srv/sid/var/lib/aptitude", Cnf.FindFile("Dir::Aptitude::State")); + Cnf.Set("RootDir", "//./////.////"); + EXPECT_EQ("/srv/sid/var/lib/apt", Cnf.FindFile("Dir::State")); + EXPECT_EQ("/srv/sid/var/lib/aptitude", Cnf.FindFile("Dir::Aptitude::State")); + Cnf.Set("RootDir", "/rootdir"); + EXPECT_EQ("/rootdir/srv/sid/var/lib/apt", Cnf.FindFile("Dir::State")); + EXPECT_EQ("/rootdir/srv/sid/var/lib/aptitude", Cnf.FindFile("Dir::Aptitude::State")); + Cnf.Set("RootDir", "/rootdir/"); + EXPECT_EQ("/rootdir/srv/sid/var/lib/apt", Cnf.FindFile("Dir::State")); + EXPECT_EQ("/rootdir/srv/sid/var/lib/aptitude", Cnf.FindFile("Dir::Aptitude::State")); + + Cnf.Set("Dir::State", "/dev/null"); + Cnf.Set("Dir::State::lists", "lists/"); + EXPECT_EQ("/rootdir/dev/null", Cnf.FindDir("Dir::State")); + EXPECT_EQ("/rootdir/dev/null", Cnf.FindDir("Dir::State::lists")); +} +TEST(ConfigurationTest,DevNullInPaths) +{ + Configuration Cnf; + EXPECT_EQ("", Cnf.FindFile("Dir")); + EXPECT_EQ("", Cnf.FindFile("Dir::State")); + EXPECT_EQ("", Cnf.FindFile("Dir::State::status")); + Cnf.Set("Dir::State", "/dev/null"); + EXPECT_EQ("/dev/null", Cnf.FindFile("Dir::State")); + Cnf.Set("Dir", "/"); + Cnf.Set("Dir::State::status", "status"); + EXPECT_EQ("/dev/null", Cnf.FindFile("Dir::State")); + EXPECT_EQ("/dev/null", Cnf.FindFile("Dir::State::status")); + Cnf.Set("Dir::State", ""); + EXPECT_EQ("", Cnf.FindFile("Dir::State")); + EXPECT_EQ("/status", Cnf.FindFile("Dir::State::status")); + Cnf.Set("Dir", "/dev/null"); + EXPECT_EQ("/dev/null", Cnf.FindFile("Dir")); + EXPECT_EQ("", Cnf.FindFile("Dir::State")); + EXPECT_EQ("/dev/null", Cnf.FindFile("Dir::State::status")); + Cnf.Set("Dir", "/rootdir"); + EXPECT_EQ("/rootdir", Cnf.FindFile("Dir")); + EXPECT_EQ("", Cnf.FindFile("Dir::State")); + EXPECT_EQ("/rootdir/status", Cnf.FindFile("Dir::State::status")); + Cnf.Set("Dir::State::status", "/foo/status"); + EXPECT_EQ("/rootdir", Cnf.FindFile("Dir")); + EXPECT_EQ("", Cnf.FindFile("Dir::State")); + EXPECT_EQ("/foo/status", Cnf.FindFile("Dir::State::status")); +} +TEST(ConfigurationTest,Vector) +{ + Configuration Cnf; + + std::vector<std::string> vec = Cnf.FindVector("Test::Vector", ""); + EXPECT_EQ(0u, vec.size()); + vec = Cnf.FindVector("Test::Vector", "foo"); + ASSERT_EQ(1u, vec.size()); + EXPECT_EQ("foo", vec[0]); + vec = Cnf.FindVector("Test::Vector", "foo,bar"); + EXPECT_EQ(2u, vec.size()); + EXPECT_EQ("foo", vec[0]); + EXPECT_EQ("bar", vec[1]); + Cnf.Set("Test::Vector::", "baz"); + Cnf.Set("Test::Vector::", "bob"); + Cnf.Set("Test::Vector::", "dob"); + vec = Cnf.FindVector("Test::Vector"); + ASSERT_EQ(3u, vec.size()); + EXPECT_EQ("baz", vec[0]); + EXPECT_EQ("bob", vec[1]); + EXPECT_EQ("dob", vec[2]); + vec = Cnf.FindVector("Test::Vector", "foo,bar"); + ASSERT_EQ(3u, vec.size()); + EXPECT_EQ("baz", vec[0]); + EXPECT_EQ("bob", vec[1]); + EXPECT_EQ("dob", vec[2]); + Cnf.Set("Test::Vector", "abel,bravo"); + vec = Cnf.FindVector("Test::Vector", "foo,bar"); + ASSERT_EQ(2u, vec.size()); + EXPECT_EQ("abel", vec[0]); + EXPECT_EQ("bravo", vec[1]); +} +TEST(ConfigurationTest,Merge) +{ + Configuration Cnf; + Cnf.Set("Binary::apt::option::foo", "bar"); + Cnf.Set("Binary::apt::option::empty", ""); + Cnf.Set("option::foo", "foo"); + + Cnf.MoveSubTree("Binary::apt", "Binary::apt2"); + EXPECT_FALSE(Cnf.Exists("Binary::apt::option")); + EXPECT_TRUE(Cnf.Exists("option")); + EXPECT_EQ("foo", Cnf.Find("option::foo")); + EXPECT_EQ("bar", Cnf.Find("Binary::apt2::option::foo")); + + EXPECT_FALSE(Cnf.Exists("option::empty")); + EXPECT_TRUE(Cnf.Exists("Binary::apt2::option::empty")); + Cnf.Set("option::empty", "not"); + + Cnf.MoveSubTree("Binary::apt2", NULL); + EXPECT_FALSE(Cnf.Exists("Binary::apt2::option")); + EXPECT_TRUE(Cnf.Exists("option")); + EXPECT_EQ("bar", Cnf.Find("option::foo")); + EXPECT_EQ("", Cnf.Find("option::empty")); +} +TEST(ConfigurationTest, Parsing) +{ + Configuration Cnf; + { + auto const file = createTemporaryFile("doublesignedfile", R"apt( +SimpleOption "true"; +/* SimpleOption "false"; */ +Answer::Simple "42"; +# This is a comment +List::Option { "In"; "One"; "Line"; }; +// this a comment as well +List::Option2 { "Multi"; +"Line"; # inline comment + "Options"; +}; Trailing "true"; +/* Commented::Out "true"; */ +)apt"); + EXPECT_TRUE(ReadConfigFile(Cnf, file.Name())); + } + EXPECT_TRUE(Cnf.FindB("SimpleOption")); + EXPECT_EQ(42, Cnf.FindI("Answer::Simple")); + EXPECT_TRUE(Cnf.Exists("List::Option")); + auto const singleline = Cnf.FindVector("List::Option"); + EXPECT_EQ(3u, singleline.size()); + EXPECT_EQ("In", singleline[0]); + EXPECT_EQ("One", singleline[1]); + EXPECT_EQ("Line", singleline[2]); + auto const multiline = Cnf.FindVector("List::Option2"); + EXPECT_EQ(3u, multiline.size()); + EXPECT_EQ("Multi", multiline[0]); + EXPECT_EQ("Line", multiline[1]); + EXPECT_EQ("Options", multiline[2]); + EXPECT_TRUE(Cnf.FindB("Trailing")); + EXPECT_FALSE(Cnf.Exists("Commented::Out")); +} diff --git a/test/libapt/extracttar_test.cc b/test/libapt/extracttar_test.cc new file mode 100644 index 0000000..ae74341 --- /dev/null +++ b/test/libapt/extracttar_test.cc @@ -0,0 +1,50 @@ +#include <apt-pkg/error.h> +#include <apt-pkg/dirstream.h> +#include <apt-pkg/extracttar.h> +#include <iostream> +#include <stdlib.h> + +#include "assert.h" +#include <gtest/gtest.h> + +class Stream : public pkgDirStream +{ + public: + int count; + Stream () { count = 0; } + bool DoItem(Item &Itm,int &Fd) APT_OVERRIDE { (void)Itm; (void)Fd; count++; return true; } + bool Fail(Item &Itm,int Fd) APT_OVERRIDE { (void)Itm; (void)Fd; return true; } + bool FinishedFile(Item &Itm,int Fd) APT_OVERRIDE { (void)Itm; (void)Fd; return true; } + ~Stream() {} +}; + +TEST(ExtractTar, ExtractTar) +{ + FileFd tgz; + ASSERT_NE(nullptr, GetTempFile("extracttar", false, &tgz)); + ASSERT_TRUE(tgz.Close()); + ASSERT_FALSE(tgz.Name().empty()); + // FIXME: We should do the right thing… but its a test and nobody will ever… + // Proposal: The first one who sees this assert fail will have to write a patch. + ASSERT_EQ(std::string::npos, tgz.Name().find('\'')); + EXPECT_EQ(0, system(("tar c /etc/passwd 2>/dev/null | gzip > " + tgz.Name()).c_str())); + + FileFd fd(tgz.Name(), FileFd::ReadOnly); + RemoveFile("ExtractTarTest", tgz.Name()); + ASSERT_TRUE(fd.IsOpen()); + ExtractTar tar(fd, -1, "gzip"); + + // Run multiple times, because we want to check not only that extraction + // works, but also that it works multiple times (important for python-apt) + for (int i = 0; i < 5; i++) { + SCOPED_TRACE(i); + Stream stream; + fd.Seek(0); + tar.Go(stream); + if (_error->PendingError()) { + _error->DumpErrors(); + EXPECT_FALSE(true); + } + EXPECT_EQ(stream.count, 1); + } +} diff --git a/test/libapt/file-helpers.cc b/test/libapt/file-helpers.cc new file mode 100644 index 0000000..85fc683 --- /dev/null +++ b/test/libapt/file-helpers.cc @@ -0,0 +1,83 @@ +#include <apt-pkg/fileutl.h> + +#include <string> + +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +void helperCreateTemporaryDirectory(std::string const &id, std::string &dir) +{ + std::string const strtempdir = GetTempDir().append("/apt-tests-").append(id).append(".XXXXXX"); + char * tempdir = strdup(strtempdir.c_str()); + ASSERT_STREQ(tempdir, mkdtemp(tempdir)); + dir = tempdir; + free(tempdir); +} +void helperRemoveDirectory(std::string const &dir) +{ + // basic sanity check to avoid removing random directories based on earlier failures + if (dir.find("/apt-tests-") == std::string::npos || dir.find_first_of("*?") != std::string::npos) + FAIL() << "Directory '" << dir << "' seems invalid. It is therefore not removed!"; + else + ASSERT_EQ(0, system(std::string("rm -rf ").append(dir).c_str())); +} +void helperCreateFile(std::string const &dir, std::string const &name) +{ + std::string file = dir; + file.append("/"); + file.append(name); + int const fd = creat(file.c_str(), 0600); + ASSERT_NE(-1, fd); + close(fd); +} +void helperCreateDirectory(std::string const &dir, std::string const &name) +{ + std::string file = dir; + file.append("/"); + file.append(name); + ASSERT_TRUE(CreateDirectory(dir, file)); +} +void helperCreateLink(std::string const &dir, std::string const &targetname, std::string const &linkname) +{ + std::string target = dir; + target.append("/"); + target.append(targetname); + std::string link = dir; + link.append("/"); + link.append(linkname); + ASSERT_EQ(0, symlink(target.c_str(), link.c_str())); +} + +void openTemporaryFile(std::string const &id, FileFd &fd, char const * const content, bool const ImmediateUnlink) +{ + EXPECT_NE(nullptr, GetTempFile("apt-" + id, ImmediateUnlink, &fd)); + EXPECT_TRUE(ImmediateUnlink || not fd.Name().empty()); + if (content != nullptr) + { + EXPECT_TRUE(fd.Write(content, strlen(content))); + EXPECT_TRUE(fd.Sync()); + fd.Seek(0); + } +} +ScopedFileDeleter::ScopedFileDeleter(std::string const &filename) : _filename{filename} {} +ScopedFileDeleter::ScopedFileDeleter(ScopedFileDeleter &&sfd) = default; +ScopedFileDeleter& ScopedFileDeleter::operator=(ScopedFileDeleter &&sfd) = default; +ScopedFileDeleter::~ScopedFileDeleter() { + if (not _filename.empty()) + RemoveFile("ScopedFileDeleter", _filename.c_str()); +} +[[nodiscard]] ScopedFileDeleter createTemporaryFile(std::string const &id, char const * const content) +{ + FileFd fd; + openTemporaryFile(id, fd, content, false); + EXPECT_TRUE(fd.IsOpen()); + EXPECT_TRUE(fd.Close()); + EXPECT_FALSE(fd.Name().empty()); + return ScopedFileDeleter{fd.Name()}; +} diff --git a/test/libapt/file-helpers.h b/test/libapt/file-helpers.h new file mode 100644 index 0000000..4ce0fb2 --- /dev/null +++ b/test/libapt/file-helpers.h @@ -0,0 +1,41 @@ +#ifndef APT_TESTS_FILE_HELPERS +#define APT_TESTS_FILE_HELPERS + +#include <string> + +#include <gtest/gtest.h> + +class FileFd; + +#define createTemporaryDirectory(id, dir) \ + ASSERT_NO_FATAL_FAILURE(helperCreateTemporaryDirectory(id, dir)) +void helperCreateTemporaryDirectory(std::string const &id, std::string &dir); +#define removeDirectory(dir) \ + ASSERT_NO_FATAL_FAILURE(helperRemoveDirectory(dir)) +void helperRemoveDirectory(std::string const &dir); +#define createFile(dir, name) \ + ASSERT_NO_FATAL_FAILURE(helperCreateFile(dir, name)) +void helperCreateFile(std::string const &dir, std::string const &name); +#define createDirectory(dir, name) \ + ASSERT_NO_FATAL_FAILURE(helperCreateDirectory(dir, name)) +void helperCreateDirectory(std::string const &dir, std::string const &name); +#define createLink(dir, targetname, linkname) \ + ASSERT_NO_FATAL_FAILURE(helperCreateLink(dir, targetname, linkname)) +void helperCreateLink(std::string const &dir, std::string const &targetname, std::string const &linkname); + +class ScopedFileDeleter { + std::string _filename; +public: + ScopedFileDeleter(std::string const &filename); + ScopedFileDeleter(ScopedFileDeleter const &) = delete; + ScopedFileDeleter(ScopedFileDeleter &&); + ScopedFileDeleter& operator=(ScopedFileDeleter const &) = delete; + ScopedFileDeleter& operator=(ScopedFileDeleter &&); + ~ScopedFileDeleter(); + + std::string Name() const { return _filename; } +}; +void openTemporaryFile(std::string const &id, FileFd &fd, char const * const content = nullptr, bool const ImmediateUnlink = true); +ScopedFileDeleter createTemporaryFile(std::string const &id, char const * const content = nullptr); + +#endif diff --git a/test/libapt/fileutl_test.cc b/test/libapt/fileutl_test.cc new file mode 100644 index 0000000..ecab2eb --- /dev/null +++ b/test/libapt/fileutl_test.cc @@ -0,0 +1,399 @@ +#include <config.h> + +#include <apt-pkg/aptconfiguration.h> +#include <apt-pkg/configuration.h> +#include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> +#include <apt-pkg/strutl.h> + +#include <algorithm> +#include <string> +#include <vector> +#include <stdlib.h> +#include <string.h> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +static void TestFileFd(mode_t const a_umask, mode_t const ExpectedFilePermission, + unsigned int const filemode, APT::Configuration::Compressor const &compressor) +{ + std::string trace; + strprintf(trace, "TestFileFd: Compressor: %s umask: %#o permission: %#o mode: %d", compressor.Name.c_str(), a_umask, ExpectedFilePermission, filemode); + SCOPED_TRACE(trace); + + auto const file = createTemporaryFile("filefd-test"); + EXPECT_TRUE(RemoveFile("TestFileFd", file.Name())); + + FileFd f; + umask(a_umask); + EXPECT_TRUE(f.Open(file.Name(), filemode, compressor)); + EXPECT_TRUE(f.IsOpen()); + EXPECT_FALSE(f.Failed()); + EXPECT_EQ(umask(a_umask), a_umask); + + std::string test = "This is a test!\n"; + EXPECT_TRUE(f.Write(test.c_str(), test.size())); + EXPECT_TRUE(f.IsOpen()); + EXPECT_FALSE(f.Failed()); + + f.Close(); + EXPECT_FALSE(f.IsOpen()); + EXPECT_FALSE(f.Failed()); + + EXPECT_TRUE(f.Open(file.Name(), FileFd::ReadOnly, compressor)); + EXPECT_TRUE(f.IsOpen()); + EXPECT_FALSE(f.Failed()); + EXPECT_FALSE(f.Eof()); + EXPECT_NE(0u, f.FileSize()); + EXPECT_FALSE(f.Failed()); + EXPECT_NE(0, f.ModificationTime()); + EXPECT_FALSE(f.Failed()); + + // ensure the memory is as predictably messed up +#define APT_INIT_READBACK \ + char readback[20]; \ + memset(readback, 'D', sizeof(readback)*sizeof(readback[0])); \ + readback[19] = '\0'; +#define EXPECT_N_STR(expect, actual) \ + EXPECT_EQ(0, strncmp(expect, actual, strlen(expect))); + { + APT_INIT_READBACK + char const * const expect = "DDDDDDDDDDDDDDDDDDD"; + EXPECT_STREQ(expect,readback); + EXPECT_N_STR(expect, readback); + } + { + APT_INIT_READBACK + char const * const expect = "This"; + EXPECT_TRUE(f.Read(readback, strlen(expect))); + EXPECT_FALSE(f.Failed()); + EXPECT_FALSE(f.Eof()); + EXPECT_N_STR(expect, readback); + EXPECT_EQ(strlen(expect), f.Tell()); + } + { + APT_INIT_READBACK + char const * const expect = "test!\n"; + EXPECT_TRUE(f.Skip((test.size() - f.Tell()) - strlen(expect))); + EXPECT_TRUE(f.Read(readback, strlen(expect))); + EXPECT_FALSE(f.Failed()); + EXPECT_FALSE(f.Eof()); + EXPECT_N_STR(expect, readback); + EXPECT_EQ(test.size(), f.Tell()); + } + // Non-zero backwards seek + { + APT_INIT_READBACK + char const * const expect = "is"; + EXPECT_EQ(test.size(), f.Tell()); + EXPECT_TRUE(f.Seek(5)); + EXPECT_TRUE(f.Read(readback, strlen(expect))); + EXPECT_FALSE(f.Failed()); + EXPECT_FALSE(f.Eof()); + EXPECT_N_STR(expect, readback); + EXPECT_EQ(7u, f.Tell()); + } + { + APT_INIT_READBACK + EXPECT_TRUE(f.Seek(0)); + EXPECT_FALSE(f.Eof()); + EXPECT_TRUE(f.Read(readback, 20, true)); + EXPECT_FALSE(f.Failed()); + EXPECT_TRUE(f.Eof()); + EXPECT_N_STR(test.c_str(), readback); + EXPECT_EQ(f.Size(), f.Tell()); + } + { + APT_INIT_READBACK + EXPECT_TRUE(f.Seek(0)); + EXPECT_FALSE(f.Eof()); + EXPECT_TRUE(f.Read(readback, test.size(), true)); + EXPECT_FALSE(f.Failed()); + EXPECT_FALSE(f.Eof()); + EXPECT_N_STR(test.c_str(), readback); + EXPECT_EQ(f.Size(), f.Tell()); + } + { + APT_INIT_READBACK + EXPECT_TRUE(f.Seek(0)); + EXPECT_FALSE(f.Eof()); + unsigned long long actual; + EXPECT_TRUE(f.Read(readback, 20, &actual)); + EXPECT_FALSE(f.Failed()); + EXPECT_TRUE(f.Eof()); + EXPECT_EQ(test.size(), actual); + EXPECT_N_STR(test.c_str(), readback); + EXPECT_EQ(f.Size(), f.Tell()); + } + { + APT_INIT_READBACK + EXPECT_TRUE(f.Seek(0)); + EXPECT_FALSE(f.Eof()); + f.ReadLine(readback, 20); + EXPECT_FALSE(f.Failed()); + EXPECT_FALSE(f.Eof()); + EXPECT_EQ(test, readback); + EXPECT_EQ(f.Size(), f.Tell()); + } + { + APT_INIT_READBACK + EXPECT_TRUE(f.Seek(0)); + EXPECT_FALSE(f.Eof()); + char const * const expect = "This"; + f.ReadLine(readback, strlen(expect) + 1); + EXPECT_FALSE(f.Failed()); + EXPECT_FALSE(f.Eof()); + EXPECT_N_STR(expect, readback); + EXPECT_EQ(strlen(expect), f.Tell()); + } +#undef APT_INIT_READBACK + { + test.erase(test.length() - 1); + EXPECT_TRUE(f.Seek(0)); + EXPECT_FALSE(f.Eof()); + std::string line; + EXPECT_TRUE(f.ReadLine(line)); + EXPECT_FALSE(f.Failed()); + EXPECT_FALSE(f.Eof()); + EXPECT_EQ(line.length(), test.length()); + EXPECT_EQ(line.length() + 1, f.Tell()); + EXPECT_EQ(f.Size(), f.Tell()); + EXPECT_EQ(line, test); + } + + f.Close(); + EXPECT_FALSE(f.IsOpen()); + EXPECT_FALSE(f.Failed()); + + // regression test for permission bug LP: #1304657 + struct stat buf; + EXPECT_EQ(0, stat(file.Name().c_str(), &buf)); + EXPECT_EQ(ExpectedFilePermission, buf.st_mode & 0777); +} + +static void TestFileFd(unsigned int const filemode) +{ + auto const compressors = APT::Configuration::getCompressors(); + EXPECT_EQ(8u, compressors.size()); + bool atLeastOneWasTested = false; + for (auto const &c: compressors) + { + if ((filemode & FileFd::ReadWrite) == FileFd::ReadWrite && + (c.Name.empty() != true && c.Binary.empty() != true)) + continue; + atLeastOneWasTested = true; + TestFileFd(0002, 0664, filemode, c); + TestFileFd(0022, 0644, filemode, c); + TestFileFd(0077, 0600, filemode, c); + TestFileFd(0026, 0640, filemode, c); + } + EXPECT_TRUE(atLeastOneWasTested); +} + +TEST(FileUtlTest, FileFD) +{ + // testing the (un)compress via pipe, as the 'real' compressors are usually built in via libraries + _config->Set("APT::Compressor::rev::Name", "rev"); + _config->Set("APT::Compressor::rev::Extension", ".reversed"); + _config->Set("APT::Compressor::rev::Binary", "rev"); + _config->Set("APT::Compressor::rev::Cost", 10); + auto const compressors = APT::Configuration::getCompressors(false); + EXPECT_EQ(8u, compressors.size()); + EXPECT_TRUE(std::any_of(compressors.begin(), compressors.end(), [](APT::Configuration::Compressor const &c) { return c.Name == "rev"; })); + + std::string const startdir = SafeGetCWD(); + EXPECT_FALSE(startdir.empty()); + std::string tempdir; + createTemporaryDirectory("filefd", tempdir); + EXPECT_EQ(0, chdir(tempdir.c_str())); + + TestFileFd(FileFd::WriteOnly | FileFd::Create); + TestFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Empty); + TestFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive); + TestFileFd(FileFd::WriteOnly | FileFd::Atomic); + TestFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Atomic); + // short-hands for ReadWrite with these modes + TestFileFd(FileFd::WriteEmpty); + TestFileFd(FileFd::WriteAny); + TestFileFd(FileFd::WriteTemp); + TestFileFd(FileFd::WriteAtomic); + + EXPECT_EQ(0, chdir(startdir.c_str())); + removeDirectory(tempdir); +} +TEST(FileUtlTest, Glob) +{ + std::vector<std::string> files; + // normal match + files = Glob("*MakeLists.txt"); + EXPECT_EQ(1u, files.size()); + + // not there + files = Glob("xxxyyyzzz"); + EXPECT_TRUE(files.empty()); + EXPECT_FALSE(_error->PendingError()); + + // many matches (number is a bit random) + files = Glob("*.cc"); + EXPECT_LT(10u, files.size()); +} +TEST(FileUtlTest, GetTempDir) +{ + char const * const envtmp = getenv("TMPDIR"); + std::string old_tmpdir; + if (envtmp != NULL) + old_tmpdir = envtmp; + + unsetenv("TMPDIR"); + EXPECT_EQ("/tmp", GetTempDir()); + + setenv("TMPDIR", "", 1); + EXPECT_EQ("/tmp", GetTempDir()); + + setenv("TMPDIR", "/not-there-no-really-not", 1); + EXPECT_EQ("/tmp", GetTempDir()); + + // root can access everything, so /usr will be accepted + if (geteuid() != 0) + { + // here but not accessible for non-roots + setenv("TMPDIR", "/usr", 1); + EXPECT_EQ("/tmp", GetTempDir()); + } + + // files are no good for tmpdirs, too + setenv("TMPDIR", "/dev/null", 1); + EXPECT_EQ("/tmp", GetTempDir()); + + setenv("TMPDIR", "/var/tmp", 1); + EXPECT_EQ("/var/tmp", GetTempDir()); + + unsetenv("TMPDIR"); + if (old_tmpdir.empty() == false) + setenv("TMPDIR", old_tmpdir.c_str(), 1); +} +TEST(FileUtlTest, Popen) +{ + FileFd Fd; + pid_t Child; + char buf[1024]; + std::string s; + unsigned long long n = 0; + std::vector<std::string> OpenFds; + + // count Fds to ensure we don't have a resource leak + if(FileExists("/proc/self/fd")) + OpenFds = Glob("/proc/self/fd/*"); + + // output something + const char* Args[10] = {"/bin/echo", "meepmeep", NULL}; + EXPECT_TRUE(Popen(Args, Fd, Child, FileFd::ReadOnly)); + EXPECT_TRUE(Fd.Read(buf, sizeof(buf)-1, &n)); + buf[n] = 0; + EXPECT_NE(n, 0u); + EXPECT_STREQ(buf, "meepmeep\n"); + + // wait for the child to exit and cleanup + EXPECT_TRUE(ExecWait(Child, "PopenRead")); + EXPECT_TRUE(Fd.Close()); + + // ensure that after a close all is good again + if(FileExists("/proc/self/fd")) + { + EXPECT_EQ(Glob("/proc/self/fd/*").size(), OpenFds.size()); + } + + // ReadWrite is not supported + _error->PushToStack(); + EXPECT_FALSE(Popen(Args, Fd, Child, FileFd::ReadWrite)); + EXPECT_FALSE(Fd.IsOpen()); + EXPECT_FALSE(Fd.Failed()); + EXPECT_TRUE(_error->PendingError()); + _error->RevertToStack(); + + // write something + Args[0] = "/bin/bash"; + Args[1] = "-c"; + Args[2] = "read"; + Args[3] = NULL; + EXPECT_TRUE(Popen(Args, Fd, Child, FileFd::WriteOnly)); + s = "\n"; + EXPECT_TRUE(Fd.Write(s.c_str(), s.length())); + EXPECT_TRUE(Fd.Close()); + EXPECT_FALSE(Fd.IsOpen()); + EXPECT_FALSE(Fd.Failed()); + EXPECT_TRUE(ExecWait(Child, "PopenWrite")); +} +TEST(FileUtlTest, flAbsPath) +{ + std::string cwd = SafeGetCWD(); + int res = chdir("/etc/"); + EXPECT_EQ(res, 0); + std::string p = flAbsPath("passwd"); + EXPECT_EQ(p, "/etc/passwd"); + + res = chdir(cwd.c_str()); + EXPECT_EQ(res, 0); +} + +static void TestDevNullFileFd(unsigned int const filemode) +{ + SCOPED_TRACE(filemode); + FileFd f("/dev/null", filemode); + EXPECT_FALSE(f.Failed()); + EXPECT_TRUE(f.IsOpen()); + EXPECT_TRUE(f.IsOpen()); + + std::string test = "This is a test!\n"; + EXPECT_TRUE(f.Write(test.c_str(), test.size())); + EXPECT_TRUE(f.IsOpen()); + EXPECT_FALSE(f.Failed()); + + f.Close(); + EXPECT_FALSE(f.IsOpen()); + EXPECT_FALSE(f.Failed()); +} +TEST(FileUtlTest, WorkingWithDevNull) +{ + TestDevNullFileFd(FileFd::WriteOnly | FileFd::Create); + TestDevNullFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Empty); + TestDevNullFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive); + TestDevNullFileFd(FileFd::WriteOnly | FileFd::Atomic); + TestDevNullFileFd(FileFd::WriteOnly | FileFd::Create | FileFd::Atomic); + // short-hands for ReadWrite with these modes + TestDevNullFileFd(FileFd::WriteEmpty); + TestDevNullFileFd(FileFd::WriteAny); + TestDevNullFileFd(FileFd::WriteTemp); + TestDevNullFileFd(FileFd::WriteAtomic); +} +constexpr char const * const TESTSTRING = "This is a test"; +static void TestFailingAtomicKeepsFile(char const * const label, std::string const &filename) +{ + SCOPED_TRACE(label); + EXPECT_TRUE(FileExists(filename)); + FileFd fd; + EXPECT_TRUE(fd.Open(filename, FileFd::ReadOnly)); + char buffer[50]; + EXPECT_NE(nullptr, fd.ReadLine(buffer, sizeof(buffer))); + EXPECT_STREQ(TESTSTRING, buffer); +} +TEST(FileUtlTest, FailingAtomic) +{ + auto const file = createTemporaryFile("failingatomic", TESTSTRING); + TestFailingAtomicKeepsFile("init", file.Name()); + + FileFd f; + EXPECT_TRUE(f.Open(file.Name(), FileFd::ReadWrite | FileFd::Atomic)); + f.EraseOnFailure(); + EXPECT_FALSE(f.Failed()); + EXPECT_TRUE(f.IsOpen()); + TestFailingAtomicKeepsFile("before-fail", file.Name()); + EXPECT_TRUE(f.Write("Bad file write", 10)); + f.OpFail(); + EXPECT_TRUE(f.Failed()); + TestFailingAtomicKeepsFile("after-fail", file.Name()); + EXPECT_TRUE(f.Close()); + TestFailingAtomicKeepsFile("closed", file.Name()); +} diff --git a/test/libapt/getarchitectures_test.cc b/test/libapt/getarchitectures_test.cc new file mode 100644 index 0000000..57e9a5f --- /dev/null +++ b/test/libapt/getarchitectures_test.cc @@ -0,0 +1,77 @@ +#include <config.h> + +#include <apt-pkg/aptconfiguration.h> +#include <apt-pkg/configuration.h> + +#include <string> +#include <vector> + +#include <gtest/gtest.h> + +TEST(ArchitecturesTest,SimpleLists) +{ + _config->Clear(); + std::vector<std::string> vec; + + _config->Set("APT::Architectures::1", "i386"); + _config->Set("APT::Architectures::2", "amd64"); + vec = APT::Configuration::getArchitectures(false); + ASSERT_EQ(2u, vec.size()); + EXPECT_EQ("i386", vec[0]); + EXPECT_EQ("amd64", vec[1]); + + _config->Set("APT::Architecture", "i386"); + vec = APT::Configuration::getArchitectures(false); + ASSERT_EQ(2u, vec.size()); + EXPECT_EQ("i386", vec[0]); + EXPECT_EQ("amd64", vec[1]); + + _config->Set("APT::Architectures::2", ""); + vec = APT::Configuration::getArchitectures(false); + ASSERT_EQ(1u, vec.size()); + EXPECT_EQ("i386", vec[0]); + + _config->Set("APT::Architecture", "armel"); + vec = APT::Configuration::getArchitectures(false); + ASSERT_EQ(2u, vec.size()); + EXPECT_EQ("armel", vec[0]); + EXPECT_EQ("i386", vec[1]); + + _config->Set("APT::Architectures::2", "armel"); + vec = APT::Configuration::getArchitectures(false); + ASSERT_EQ(2u, vec.size()); + EXPECT_EQ("i386", vec[0]); + EXPECT_EQ("armel", vec[1]); + + _config->Set("APT::Architectures", "armel,armhf"); + vec = APT::Configuration::getArchitectures(false); + ASSERT_EQ(2u, vec.size()); + EXPECT_EQ("armel", vec[0]); + EXPECT_EQ("armhf", vec[1]); + _config->Clear(); +} +TEST(ArchitecturesTest,Duplicates) +{ + _config->Clear(); + + _config->Set("APT::Architecture", "armel"); + _config->Set("APT::Architectures::", "i386"); + _config->Set("APT::Architectures::", "amd64"); + _config->Set("APT::Architectures::", "i386"); + _config->Set("APT::Architectures::", "armel"); + _config->Set("APT::Architectures::", "i386"); + _config->Set("APT::Architectures::", "amd64"); + _config->Set("APT::Architectures::", "armel"); + _config->Set("APT::Architectures::", "armel"); + _config->Set("APT::Architectures::", "amd64"); + _config->Set("APT::Architectures::", "amd64"); + std::vector<std::string> vec = _config->FindVector("APT::Architectures"); + ASSERT_EQ(10u, vec.size()); + vec = APT::Configuration::getArchitectures(false); + ASSERT_EQ(3u, vec.size()); + EXPECT_EQ("i386", vec[0]); + EXPECT_EQ("amd64", vec[1]); + EXPECT_EQ("armel", vec[2]); + + _config->Clear(); +} diff --git a/test/libapt/getlanguages_test.cc b/test/libapt/getlanguages_test.cc new file mode 100644 index 0000000..7146c5a --- /dev/null +++ b/test/libapt/getlanguages_test.cc @@ -0,0 +1,241 @@ +#include <config.h> + +#include <apt-pkg/aptconfiguration.h> +#include <apt-pkg/configuration.h> +#include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> + +#include <algorithm> +#include <iostream> +#include <string> +#include <vector> + +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +TEST(LanguagesTest,Environment) +{ + _config->Clear(); + + char const* env[2]; + env[0] = "de_DE.UTF-8"; + env[1] = ""; + + std::vector<std::string> vec = APT::Configuration::getLanguages(false, false, env); + ASSERT_EQ(3u, vec.size()); + EXPECT_EQ("de_DE", vec[0]); + EXPECT_EQ("de", vec[1]); + EXPECT_EQ("en", vec[2]); + + // Special: Check if the cache is actually in use + env[0] = "en_GB.UTF-8"; + vec = APT::Configuration::getLanguages(false, true, env); + ASSERT_EQ(3u, vec.size()); + EXPECT_EQ("de_DE", vec[0]); + EXPECT_EQ("de", vec[1]); + EXPECT_EQ("en", vec[2]); + + env[0] = "en_GB.UTF-8"; + vec = APT::Configuration::getLanguages(false, false, env); + ASSERT_EQ(2u, vec.size()); + EXPECT_EQ("en_GB", vec[0]); + EXPECT_EQ("en", vec[1]); + + // esperanto + env[0] = "eo.UTF-8"; + vec = APT::Configuration::getLanguages(false, false, env); + ASSERT_EQ(2u, vec.size()); + EXPECT_EQ("eo", vec[0]); + EXPECT_EQ("en", vec[1]); + + env[0] = "tr_DE@euro"; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_EQ(3u, vec.size()); + EXPECT_EQ("tr_DE", vec[0]); + EXPECT_EQ("tr", vec[1]); + EXPECT_EQ("en", vec[2]); + + env[0] = "de_NO"; + env[1] = "de_NO:en_GB:nb_NO:nb:no_NO:no:nn_NO:nn:da:sv:en"; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_EQ(6u, vec.size()); + EXPECT_EQ("de_NO", vec[0]); + EXPECT_EQ("de", vec[1]); + EXPECT_EQ("en_GB", vec[2]); + EXPECT_EQ("nb_NO", vec[3]); + EXPECT_EQ("nb", vec[4]); + EXPECT_EQ("en", vec[5]); + + env[0] = "pt_PR.UTF-8"; + env[1] = ""; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_EQ(3u, vec.size()); + EXPECT_EQ("pt_PR", vec[0]); + EXPECT_EQ("pt", vec[1]); + EXPECT_EQ("en", vec[2]); + + env[0] = "ast_DE.UTF-8"; + vec = APT::Configuration::getLanguages(false, false, env); // bogus, but syntactical correct + EXPECT_EQ(3u, vec.size()); + EXPECT_EQ("ast_DE", vec[0]); + EXPECT_EQ("ast", vec[1]); + EXPECT_EQ("en", vec[2]); + + env[0] = "C"; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_EQ(1u, vec.size()); + EXPECT_EQ("en", vec[0]); + + _config->Set("Acquire::Languages", "none"); + env[0] = "C"; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_TRUE(vec.empty()); + + _config->Set("Acquire::Languages", "environment"); + env[0] = "C"; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_EQ(1u, vec.size()); + EXPECT_EQ("en", vec[0]); + + _config->Set("Acquire::Languages", "de"); + env[0] = "C"; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_EQ(1u, vec.size()); + EXPECT_EQ("de", vec[0]); + + _config->Set("Acquire::Languages", "fr"); + env[0] = "ast_DE.UTF-8"; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_EQ(1u, vec.size()); + EXPECT_EQ("fr", vec[0]); + + _config->Set("Acquire::Languages", "environment,en"); + env[0] = "de_DE.UTF-8"; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_EQ(3u, vec.size()); + EXPECT_EQ("de_DE", vec[0]); + EXPECT_EQ("de", vec[1]); + EXPECT_EQ("en", vec[2]); + _config->Set("Acquire::Languages", ""); + + _config->Set("Acquire::Languages::1", "environment"); + _config->Set("Acquire::Languages::2", "en"); + env[0] = "de_DE.UTF-8"; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_EQ(3u, vec.size()); + EXPECT_EQ("de_DE", vec[0]); + EXPECT_EQ("de", vec[1]); + EXPECT_EQ("en", vec[2]); + + _config->Set("Acquire::Languages::3", "de"); + env[0] = "de_DE.UTF-8"; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_EQ(3u, vec.size()); + EXPECT_EQ("de_DE", vec[0]); + EXPECT_EQ("de", vec[1]); + EXPECT_EQ("en", vec[2]); + + _config->Clear(); +} + +TEST(LanguagesTest,TranslationFiles) +{ + _config->Clear(); + _config->Set("Acquire::Languages::1", "environment"); + _config->Set("Acquire::Languages::2", "en"); + _config->Set("Acquire::Languages::3", "de"); + + char const* env[2]; + env[0] = "de_DE.UTF-8"; + env[1] = ""; + + std::string tempdir; + createTemporaryDirectory("languages", tempdir); + +#define createTranslation(lang) \ + createFile(tempdir, std::string("/ftp.de.debian.org_debian_dists_sid_main_i18n_Translation-").append(lang)); + + createTranslation("tr"); + createTranslation("pt"); + createTranslation("se~"); + createTranslation("st.bak"); + createTranslation("ast_DE"); + createTranslation("tlh%5fDE"); + + _config->Set("Dir::State::lists", tempdir); + std::vector<std::string> vec = APT::Configuration::getLanguages(true, false, env); + EXPECT_EQ(8u, vec.size()); + EXPECT_EQ("de_DE", vec[0]); + EXPECT_EQ("de", vec[1]); + EXPECT_EQ("en", vec[2]); + EXPECT_EQ("none", vec[3]); + EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(), "pt")); + EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(), "tr")); + EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(), "ast_DE")); + EXPECT_NE(vec.end(), std::find(vec.begin(), vec.end(), "tlh_DE")); + EXPECT_NE(vec[4], vec[5]); + EXPECT_NE(vec[4], vec[6]); + EXPECT_NE(vec[4], vec[7]); + EXPECT_NE(vec[5], vec[6]); + EXPECT_NE(vec[5], vec[7]); + EXPECT_NE(vec[6], vec[7]); + + _config->Set("Acquire::Languages", "none"); + vec = APT::Configuration::getLanguages(true, false, env); + EXPECT_EQ(1u, vec.size()); + EXPECT_EQ("none", vec[0]); + _config->Set("Acquire::Languages", ""); + + _config->Set("Dir::State::lists", "/non-existing-dir"); + _config->Set("Acquire::Languages::1", "none"); + env[0] = "de_DE.UTF-8"; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_TRUE(vec.empty()); + env[0] = "de_DE.UTF-8"; + vec = APT::Configuration::getLanguages(true, false, env); + EXPECT_EQ(2u, vec.size()); + EXPECT_EQ("en", vec[0]); + EXPECT_EQ("de", vec[1]); + + _config->Set("Acquire::Languages::1", "fr"); + _config->Set("Acquire::Languages", "de_DE"); + env[0] = "de_DE.UTF-8"; + vec = APT::Configuration::getLanguages(false, false, env); + EXPECT_EQ(1u, vec.size()); + EXPECT_EQ("de_DE", vec[0]); + + _config->Set("Acquire::Languages", "none"); + env[0] = "de_DE.UTF-8"; + vec = APT::Configuration::getLanguages(true, false, env); + EXPECT_EQ(1u, vec.size()); + EXPECT_EQ("none", vec[0]); + + _error->PushToStack(); + _config->Set("Acquire::Languages", ""); + //FIXME: Remove support for this deprecated setting + _config->Set("APT::Acquire::Translation", "ast_DE"); + env[0] = "de_DE.UTF-8"; + vec = APT::Configuration::getLanguages(true, false, env); + EXPECT_EQ(2u, vec.size()); + EXPECT_EQ("ast_DE", vec[0]); + EXPECT_EQ("en", vec[1]); + _config->Set("APT::Acquire::Translation", "none"); + env[0] = "de_DE.UTF-8"; + vec = APT::Configuration::getLanguages(true, false, env); + EXPECT_EQ(1u, vec.size()); + EXPECT_EQ("en", vec[0]); + + // discard the deprecation warning for APT::Acquire::Translation + if (_error->PendingError()) + _error->MergeWithStack(); + else + _error->RevertToStack(); + + EXPECT_EQ(0, system(std::string("rm -rf ").append(tempdir).c_str())); + _config->Clear(); +} diff --git a/test/libapt/getlistoffilesindir_test.cc b/test/libapt/getlistoffilesindir_test.cc new file mode 100644 index 0000000..f002355 --- /dev/null +++ b/test/libapt/getlistoffilesindir_test.cc @@ -0,0 +1,114 @@ +#include <config.h> + +#include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> + +#include <iostream> +#include <string> +#include <vector> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +#define P(x) tempdir + "/" + x + +TEST(FileUtlTest,GetListOfFilesInDir) +{ + std::string tempdir; + createTemporaryDirectory("getlistoffiles", tempdir); + + createFile(tempdir, "anormalfile"); + createFile(tempdir, "01yet-anothernormalfile"); + createFile(tempdir, "anormalapt.conf"); + createFile(tempdir, "01yet-anotherapt.conf"); + createFile(tempdir, "anormalapt.list"); + createFile(tempdir, "01yet-anotherapt.list"); + createFile(tempdir, "wrongextension.wron"); + createFile(tempdir, "wrong-extension.wron"); + createFile(tempdir, "strangefile."); + createFile(tempdir, "s.t.r.a.n.g.e.f.i.l.e"); + createFile(tempdir, ".hiddenfile"); + createFile(tempdir, ".hiddenfile.conf"); + createFile(tempdir, ".hiddenfile.list"); + createFile(tempdir, "multi..dot"); + createFile(tempdir, "multi.dot.conf"); + createFile(tempdir, "multi.dot.list"); + createFile(tempdir, "disabledfile.disabled"); + createFile(tempdir, "disabledfile.conf.disabled"); + createFile(tempdir, "disabledfile.list.disabled"); + createFile(tempdir, "invälid.conf"); + createFile(tempdir, "invalíd"); + createFile(tempdir, "01invalíd"); + createDirectory(tempdir, "invaliddir"); + createDirectory(tempdir, "directory.conf"); + createDirectory(tempdir, "directory.list"); + createDirectory(tempdir, "directory.wron"); + createDirectory(tempdir, "directory.list.disabled"); + createLink(tempdir, "anormalfile", "linkedfile.list"); + createLink(tempdir, "invaliddir", "linkeddir.list"); + createLink(tempdir, "non-existing-file", "brokenlink.list"); + + // Files with no extension + _error->PushToStack(); + std::vector<std::string> files = GetListOfFilesInDir(tempdir, "", true); + ASSERT_EQ(2u, files.size()); + EXPECT_EQ(P("01yet-anothernormalfile"), files[0]); + EXPECT_EQ(P("anormalfile"), files[1]); + + // Files with no extension - should be the same as above + files = GetListOfFilesInDir(tempdir, "", true, true); + ASSERT_EQ(2u, files.size()); + EXPECT_EQ(P("01yet-anothernormalfile"), files[0]); + EXPECT_EQ(P("anormalfile"), files[1]); + + // Files with impossible extension + files = GetListOfFilesInDir(tempdir, "impossible", true); + EXPECT_TRUE(files.empty()); + + // Files with impossible or no extension + files = GetListOfFilesInDir(tempdir, "impossible", true, true); + ASSERT_EQ(2u, files.size()); + EXPECT_EQ(P("01yet-anothernormalfile"), files[0]); + EXPECT_EQ(P("anormalfile"), files[1]); + + // Files with list extension - nothing more + files = GetListOfFilesInDir(tempdir, "list", true); + ASSERT_EQ(4u, files.size()); + EXPECT_EQ(P("01yet-anotherapt.list"), files[0]); + EXPECT_EQ(P("anormalapt.list"), files[1]); + EXPECT_EQ(P("linkedfile.list"), files[2]); + EXPECT_EQ(P("multi.dot.list"), files[3]); + + // Files with conf or no extension + files = GetListOfFilesInDir(tempdir, "conf", true, true); + ASSERT_EQ(5u, files.size()); + EXPECT_EQ(P("01yet-anotherapt.conf"), files[0]); + EXPECT_EQ(P("01yet-anothernormalfile"), files[1]); + EXPECT_EQ(P("anormalapt.conf"), files[2]); + EXPECT_EQ(P("anormalfile"), files[3]); + EXPECT_EQ(P("multi.dot.conf"), files[4]); + + // Files with disabled extension - nothing more + files = GetListOfFilesInDir(tempdir, "disabled", true); + ASSERT_EQ(3u, files.size()); + EXPECT_EQ(P("disabledfile.conf.disabled"), files[0]); + EXPECT_EQ(P("disabledfile.disabled"), files[1]); + EXPECT_EQ(P("disabledfile.list.disabled"), files[2]); + + // Files with disabled or no extension + files = GetListOfFilesInDir(tempdir, "disabled", true, true); + ASSERT_EQ(5u, files.size()); + EXPECT_EQ(P("01yet-anothernormalfile"), files[0]); + EXPECT_EQ(P("anormalfile"), files[1]); + EXPECT_EQ(P("disabledfile.conf.disabled"), files[2]); + EXPECT_EQ(P("disabledfile.disabled"), files[3]); + EXPECT_EQ(P("disabledfile.list.disabled"), files[4]); + + // discard the unknown file extension messages + if (_error->PendingError()) + _error->MergeWithStack(); + else + _error->RevertToStack(); + removeDirectory(tempdir); +} diff --git a/test/libapt/globalerror_test.cc b/test/libapt/globalerror_test.cc new file mode 100644 index 0000000..ff14d46 --- /dev/null +++ b/test/libapt/globalerror_test.cc @@ -0,0 +1,169 @@ +#include <config.h> + +#include <apt-pkg/error.h> + +#include <string> +#include <errno.h> +#include <stddef.h> +#include <string.h> + +#include <gtest/gtest.h> + +TEST(GlobalErrorTest,BasicDiscard) +{ + GlobalError e; + EXPECT_TRUE(e.empty()); + EXPECT_FALSE(e.PendingError()); + EXPECT_FALSE(e.Notice("%s Notice", "A")); + EXPECT_TRUE(e.empty()); + EXPECT_FALSE(e.empty(GlobalError::DEBUG)); + EXPECT_FALSE(e.PendingError()); + EXPECT_FALSE(e.Error("%s horrible %s %d times", "Something", "happened", 2)); + EXPECT_TRUE(e.PendingError()); + + std::string text; + EXPECT_FALSE(e.PopMessage(text)); + EXPECT_TRUE(e.PendingError()); + EXPECT_EQ("A Notice", text); + EXPECT_TRUE(e.PopMessage(text)); + EXPECT_EQ("Something horrible happened 2 times", text); + EXPECT_TRUE(e.empty(GlobalError::DEBUG)); + EXPECT_FALSE(e.PendingError()); + EXPECT_FALSE(e.Error("%s horrible %s %d times", "Something", "happened", 2)); + EXPECT_TRUE(e.PendingError()); + EXPECT_FALSE(e.empty(GlobalError::FATAL)); + e.Discard(); + + EXPECT_TRUE(e.empty()); + EXPECT_FALSE(e.PendingError()); +} +TEST(GlobalErrorTest,StackPushing) +{ + GlobalError e; + EXPECT_FALSE(e.Notice("%s Notice", "A")); + EXPECT_FALSE(e.Error("%s horrible %s %d times", "Something", "happened", 2)); + EXPECT_TRUE(e.PendingError()); + EXPECT_FALSE(e.empty(GlobalError::NOTICE)); + e.PushToStack(); + EXPECT_TRUE(e.empty(GlobalError::NOTICE)); + EXPECT_FALSE(e.PendingError()); + EXPECT_FALSE(e.Warning("%s Warning", "A")); + EXPECT_TRUE(e.empty(GlobalError::ERROR)); + EXPECT_FALSE(e.PendingError()); + e.RevertToStack(); + EXPECT_FALSE(e.empty(GlobalError::ERROR)); + EXPECT_TRUE(e.PendingError()); + + std::string text; + EXPECT_FALSE(e.PopMessage(text)); + EXPECT_TRUE(e.PendingError()); + EXPECT_EQ("A Notice", text); + EXPECT_TRUE(e.PopMessage(text)); + EXPECT_EQ("Something horrible happened 2 times", text); + EXPECT_FALSE(e.PendingError()); + EXPECT_TRUE(e.empty()); + + EXPECT_FALSE(e.Notice("%s Notice", "A")); + EXPECT_FALSE(e.Error("%s horrible %s %d times", "Something", "happened", 2)); + EXPECT_TRUE(e.PendingError()); + EXPECT_FALSE(e.empty(GlobalError::NOTICE)); + e.PushToStack(); + EXPECT_TRUE(e.empty(GlobalError::NOTICE)); + EXPECT_FALSE(e.PendingError()); + EXPECT_FALSE(e.Warning("%s Warning", "A")); + EXPECT_TRUE(e.empty(GlobalError::ERROR)); + EXPECT_FALSE(e.PendingError()); + e.MergeWithStack(); + EXPECT_FALSE(e.empty(GlobalError::ERROR)); + EXPECT_TRUE(e.PendingError()); + EXPECT_FALSE(e.PopMessage(text)); + EXPECT_TRUE(e.PendingError()); + EXPECT_EQ("A Notice", text); + EXPECT_TRUE(e.PopMessage(text)); + EXPECT_EQ("Something horrible happened 2 times", text); + EXPECT_FALSE(e.PendingError()); + EXPECT_FALSE(e.empty()); + EXPECT_FALSE(e.PopMessage(text)); + EXPECT_EQ("A Warning", text); + EXPECT_TRUE(e.empty()); +} +TEST(GlobalErrorTest,Errno) +{ + GlobalError e; + std::string const textOfErrnoZero(strerror(0)); + errno = 0; + EXPECT_FALSE(e.Errno("errno", "%s horrible %s %d times", "Something", "happened", 2)); + EXPECT_FALSE(e.empty()); + EXPECT_TRUE(e.PendingError()); + std::string text; + EXPECT_TRUE(e.PopMessage(text)); + EXPECT_FALSE(e.PendingError()); + EXPECT_EQ(std::string("Something horrible happened 2 times - errno (0: ").append(textOfErrnoZero).append(")"), text); + EXPECT_TRUE(e.empty()); +} +TEST(GlobalErrorTest,LongMessage) +{ + GlobalError e; + std::string const textOfErrnoZero(strerror(0)); + errno = 0; + std::string text, longText; + for (size_t i = 0; i < 500; ++i) + longText.append("a"); + EXPECT_FALSE(e.Error("%s horrible %s %d times", longText.c_str(), "happened", 2)); + EXPECT_TRUE(e.PopMessage(text)); + EXPECT_EQ(longText + " horrible happened 2 times", text); + + EXPECT_FALSE(e.Errno("errno", "%s horrible %s %d times", longText.c_str(), "happened", 2)); + EXPECT_TRUE(e.PopMessage(text)); + EXPECT_EQ(longText + " horrible happened 2 times - errno (0: " + textOfErrnoZero + ")", text); + + EXPECT_FALSE(e.Error("%s horrible %s %d times", longText.c_str(), "happened", 2)); + std::ostringstream out; + e.DumpErrors(out); + EXPECT_EQ(std::string("E: ").append(longText).append(" horrible happened 2 times\n"), out.str()); + + EXPECT_FALSE(e.Errno("errno", "%s horrible %s %d times", longText.c_str(), "happened", 2)); + std::ostringstream out2; + e.DumpErrors(out2); + EXPECT_EQ(std::string("E: ").append(longText).append(" horrible happened 2 times - errno (0: ").append(textOfErrnoZero).append(")\n"), out2.str()); +} +TEST(GlobalErrorTest,UTF8Message) +{ + GlobalError e; + std::string text; + + EXPECT_FALSE(e.Warning("Репозиторий не обновлён и будут %d %s", 4, "test")); + EXPECT_FALSE(e.PopMessage(text)); + EXPECT_EQ("Репозиторий не обновлён и будут 4 test", text); + + EXPECT_FALSE(e.Warning("Репозиторий не обновлён и будут %d %s", 4, "test")); + std::ostringstream out; + e.DumpErrors(out); + EXPECT_EQ("W: Репозиторий не обновлён и будут 4 test\n", out.str()); + + std::string longText; + for (size_t i = 0; i < 50; ++i) + longText.append("РезийбёбAZ"); + EXPECT_FALSE(e.Warning("%s", longText.c_str())); + EXPECT_FALSE(e.PopMessage(text)); + EXPECT_EQ(longText, text); +} +TEST(GlobalErrorTest,MultiLineMessage) +{ + GlobalError e; + std::string text; + + EXPECT_FALSE(e.Warning("Sometimes one line isn't enough.\nYou do know what I mean, right?\r\n%s?\rGood because I don't.", "Right")); + EXPECT_FALSE(e.PopMessage(text)); + EXPECT_EQ("Sometimes one line isn't enough.\nYou do know what I mean, right?\r\nRight?\rGood because I don't.", text); + + EXPECT_FALSE(e.Warning("Sometimes one line isn't enough.\nYou do know what I mean, right?\r\n%s?\rGood because I don't.", "Right")); + std::ostringstream out; + e.DumpErrors(out); + EXPECT_EQ("W: Sometimes one line isn't enough.\n You do know what I mean, right?\n Right?\n Good because I don't.\n", out.str()); + + EXPECT_FALSE(e.Warning("Sometimes one line isn't enough.\nYou do know what I mean, right?\r\n%s?\rGood because I don't.\n", "Right")); + std::ostringstream out2; + e.DumpErrors(out2); + EXPECT_EQ("W: Sometimes one line isn't enough.\n You do know what I mean, right?\n Right?\n Good because I don't.\n", out2.str()); +} diff --git a/test/libapt/gtest_runner.cc b/test/libapt/gtest_runner.cc new file mode 100644 index 0000000..5620aa0 --- /dev/null +++ b/test/libapt/gtest_runner.cc @@ -0,0 +1,26 @@ +#include <gtest/gtest.h> + +#include <apt-pkg/configuration.h> +#include <apt-pkg/error.h> +#include <apt-pkg/init.h> +#include <apt-pkg/pkgsystem.h> + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + if (pkgInitSystem(*_config, _system) == false) + return 42; + int const result = RUN_ALL_TESTS(); + if (_error->empty() == false) + { + std::cerr << "The test generated the following global messages:" << std::endl; + _error->DumpErrors(std::cerr); + // messages on the stack can't be right, error out + // even if we have no idea where this message came from + if (result == 0) + { + std::cerr << "All tests successful, but messages were generated, so still a failure!" << std::endl; + return 29; + } + } + return result; +} diff --git a/test/libapt/hashsums_test.cc b/test/libapt/hashsums_test.cc new file mode 100644 index 0000000..2d8079e --- /dev/null +++ b/test/libapt/hashsums_test.cc @@ -0,0 +1,305 @@ +#include <config.h> + +#include <apt-pkg/configuration.h> +#include <apt-pkg/fileutl.h> +#include <apt-pkg/hashes.h> +#include <apt-pkg/strutl.h> + +#include <iostream> +#include <string> +#include <stdlib.h> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +template <class T> void Test(const char *In,const char *Out) +{ + T Sum; + Sum.Add(In); + equals(Sum.Result().Value(), Out); +} + + + +static void getSummationString(char const * const type, std::string &sum) +{ + /* to compare our result with an independent source we call the specific binaries + and read their result back. We do this with a little trick by claiming that the + summation is a compressor – and open the 'compressed' file later on directly to + read out the summation sum calculated by it */ + APT::Configuration::Compressor compress(type, ".ext", type, NULL, NULL, 99); + + FileFd fd; + auto const file = createTemporaryFile("hashsums"); + ASSERT_TRUE(fd.Open(file.Name(), FileFd::WriteOnly | FileFd::Empty, compress)); + ASSERT_TRUE(fd.IsOpen()); + FileFd input("/etc/os-release", FileFd::ReadOnly); + ASSERT_TRUE(input.IsOpen()); + ASSERT_NE(0u, input.FileSize()); + ASSERT_TRUE(CopyFile(input, fd)); + ASSERT_TRUE(input.IsOpen()); + ASSERT_TRUE(fd.IsOpen()); + ASSERT_FALSE(fd.Failed()); + input.Close(); + fd.Close(); + ASSERT_TRUE(fd.Open(file.Name(), FileFd::ReadOnly, FileFd::None)); + ASSERT_TRUE(fd.IsOpen()); + ASSERT_NE(0u, fd.FileSize()); + ASSERT_FALSE(fd.Failed()); + char readback[2000]; + unsigned long long actual; + ASSERT_TRUE(fd.Read(readback, sizeof(readback)/sizeof(readback[0]), &actual)); + actual -= 4; + readback[actual] = '\0'; + sum = readback; +} +TEST(HashSumsTest, FileBased) +{ + std::string summation; + + getSummationString("md5sum", summation); + HashString md5("MD5Sum", summation); + EXPECT_EQ(md5.HashValue(), summation); + + getSummationString("sha1sum", summation); + HashString sha1("SHA1", summation); + EXPECT_EQ(sha1.HashValue(), summation); + + getSummationString("sha256sum", summation); + HashString sha256("SHA256", summation); + EXPECT_EQ(sha256.HashValue(), summation); + + getSummationString("sha512sum", summation); + HashString sha512("SHA512", summation); + EXPECT_EQ(sha512.HashValue(), summation); + + FileFd fd("/etc/os-release", FileFd::ReadOnly); + EXPECT_TRUE(fd.IsOpen()); + std::string FileSize; + strprintf(FileSize, "%llu", fd.FileSize()); + + { + Hashes hashes; + hashes.AddFD(fd.Fd()); + HashStringList list = hashes.GetHashStringList(); + EXPECT_FALSE(list.empty()); + EXPECT_EQ(5u, list.size()); + EXPECT_EQ(md5.HashValue(), list.find("MD5Sum")->HashValue()); + EXPECT_EQ(sha1.HashValue(), list.find("SHA1")->HashValue()); + EXPECT_EQ(sha256.HashValue(), list.find("SHA256")->HashValue()); + EXPECT_EQ(sha512.HashValue(), list.find("SHA512")->HashValue()); + EXPECT_EQ(FileSize, list.find("Checksum-FileSize")->HashValue()); + } + unsigned long long sz = fd.FileSize(); + fd.Seek(0); + { + Hashes hashes; + hashes.AddFD(fd.Fd(), sz); + HashStringList list = hashes.GetHashStringList(); + EXPECT_FALSE(list.empty()); + EXPECT_EQ(5u, list.size()); + EXPECT_EQ(md5.HashValue(), list.find("MD5Sum")->HashValue()); + EXPECT_EQ(sha1.HashValue(), list.find("SHA1")->HashValue()); + EXPECT_EQ(sha256.HashValue(), list.find("SHA256")->HashValue()); + EXPECT_EQ(sha512.HashValue(), list.find("SHA512")->HashValue()); + EXPECT_EQ(FileSize, list.find("Checksum-FileSize")->HashValue()); + } + fd.Seek(0); + { + Hashes hashes(Hashes::MD5SUM | Hashes::SHA512SUM); + hashes.AddFD(fd); + HashStringList list = hashes.GetHashStringList(); + EXPECT_FALSE(list.empty()); + EXPECT_EQ(3u, list.size()); + EXPECT_EQ(md5.HashValue(), list.find("MD5Sum")->HashValue()); + EXPECT_EQ(NULL, list.find("SHA1")); + EXPECT_EQ(NULL, list.find("SHA256")); + EXPECT_EQ(sha512.HashValue(), list.find("SHA512")->HashValue()); + EXPECT_EQ(FileSize, list.find("Checksum-FileSize")->HashValue()); + fd.Seek(0); + Hashes hashes2(list); + hashes2.AddFD(fd); + list = hashes2.GetHashStringList(); + EXPECT_FALSE(list.empty()); + EXPECT_EQ(3u, list.size()); + EXPECT_EQ(md5.HashValue(), list.find("MD5Sum")->HashValue()); + EXPECT_EQ(NULL, list.find("SHA1")); + EXPECT_EQ(NULL, list.find("SHA256")); + EXPECT_EQ(sha512.HashValue(), list.find("SHA512")->HashValue()); + EXPECT_EQ(FileSize, list.find("Checksum-FileSize")->HashValue()); + } + fd.Seek(0); + { + Hashes MD5(Hashes::MD5SUM); + MD5.AddFD(fd.Fd()); + EXPECT_EQ(md5, MD5.GetHashString(Hashes::MD5SUM)); + } + fd.Seek(0); + { + Hashes SHA1(Hashes::SHA1SUM); + SHA1.AddFD(fd.Fd()); + EXPECT_EQ(sha1, SHA1.GetHashString(Hashes::SHA1SUM)); + } + fd.Seek(0); + { + Hashes SHA2(Hashes::SHA256SUM); + SHA2.AddFD(fd.Fd()); + EXPECT_EQ(sha256, SHA2.GetHashString(Hashes::SHA256SUM)); + } + fd.Seek(0); + { + Hashes SHA2(Hashes::SHA512SUM); + SHA2.AddFD(fd.Fd()); + EXPECT_EQ(sha512, SHA2.GetHashString(Hashes::SHA512SUM)); + } + fd.Close(); + + HashString sha2file("SHA512", sha512.HashValue()); + EXPECT_TRUE(sha2file.VerifyFile("/etc/os-release")); + HashString sha2wrong("SHA512", "00000000000"); + EXPECT_FALSE(sha2wrong.VerifyFile("/etc/os-release")); + EXPECT_EQ(sha2file, sha2file); + EXPECT_TRUE(sha2file == sha2file); + EXPECT_NE(sha2file, sha2wrong); + EXPECT_TRUE(sha2file != sha2wrong); + + HashString sha2big("SHA256", sha256.HashValue()); + EXPECT_TRUE(sha2big.VerifyFile("/etc/os-release")); + HashString sha2small("sha256:" + sha256.HashValue()); + EXPECT_TRUE(sha2small.VerifyFile("/etc/os-release")); + EXPECT_EQ(sha2big, sha2small); + EXPECT_TRUE(sha2big == sha2small); + EXPECT_FALSE(sha2big != sha2small); + + HashStringList hashes; + EXPECT_TRUE(hashes.empty()); + EXPECT_TRUE(hashes.push_back(sha2file)); + EXPECT_FALSE(hashes.empty()); + EXPECT_EQ(1u, hashes.size()); + + HashStringList wrong; + EXPECT_TRUE(wrong.push_back(sha2wrong)); + EXPECT_NE(wrong, hashes); + EXPECT_FALSE(wrong == hashes); + EXPECT_TRUE(wrong != hashes); + + HashStringList similar; + EXPECT_TRUE(similar.push_back(sha2big)); + EXPECT_NE(similar, hashes); + EXPECT_FALSE(similar == hashes); + EXPECT_TRUE(similar != hashes); + + EXPECT_TRUE(hashes.push_back(sha2big)); + EXPECT_EQ(2u, hashes.size()); + EXPECT_TRUE(hashes.push_back(sha2small)); + EXPECT_EQ(2u, hashes.size()); + EXPECT_FALSE(hashes.push_back(sha2wrong)); + EXPECT_EQ(2u, hashes.size()); + EXPECT_TRUE(hashes.VerifyFile("/etc/os-release")); + + EXPECT_EQ(similar, hashes); + EXPECT_TRUE(similar == hashes); + EXPECT_FALSE(similar != hashes); + similar.clear(); + EXPECT_TRUE(similar.empty()); + EXPECT_EQ(0u, similar.size()); + EXPECT_NE(similar, hashes); + EXPECT_FALSE(similar == hashes); + EXPECT_TRUE(similar != hashes); +} +TEST(HashSumsTest, HashStringList) +{ + _config->Clear("Acquire::ForceHash"); + + HashStringList list; + EXPECT_TRUE(list.empty()); + EXPECT_FALSE(list.usable()); + EXPECT_EQ(0u, list.size()); + EXPECT_EQ(NULL, list.find(NULL)); + EXPECT_EQ(NULL, list.find("")); + EXPECT_EQ(NULL, list.find("MD5Sum")); + EXPECT_EQ(NULL, list.find("ROT26")); + EXPECT_EQ(NULL, list.find("SHA1")); + EXPECT_EQ(0u, list.FileSize()); + + // empty lists aren't equal + HashStringList list2; + EXPECT_FALSE(list == list2); + EXPECT_TRUE(list != list2); + + // some hashes don't really contribute to usability + list.push_back(HashString("Checksum-FileSize", "29")); + EXPECT_FALSE(list.empty()); + EXPECT_FALSE(list.usable()); + EXPECT_EQ(1u, list.size()); + EXPECT_EQ(29u, list.FileSize()); + list.push_back(HashString("MD5Sum", "d41d8cd98f00b204e9800998ecf8427e")); + EXPECT_FALSE(list.empty()); + EXPECT_FALSE(list.usable()); + EXPECT_EQ(2u, list.size()); + EXPECT_EQ(29u, list.FileSize()); + EXPECT_TRUE(NULL != list.find("MD5Sum")); + list.push_back(HashString("SHA1", "cacecbd74968bc90ea3342767e6b94f46ddbcafc")); + EXPECT_FALSE(list.usable()); + EXPECT_EQ(3u, list.size()); + EXPECT_EQ(29u, list.FileSize()); + EXPECT_TRUE(NULL != list.find("MD5Sum")); + EXPECT_TRUE(NULL != list.find("SHA1")); + list.push_back(HashString("SHA256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); + EXPECT_TRUE(list.usable()); + EXPECT_EQ(4u, list.size()); + EXPECT_EQ(29u, list.FileSize()); + EXPECT_TRUE(NULL != list.find("MD5Sum")); + EXPECT_TRUE(NULL != list.find("SHA1")); + EXPECT_TRUE(NULL != list.find("SHA256")); + + Hashes hashes; + hashes.Add("The quick brown fox jumps over the lazy dog"); + list = hashes.GetHashStringList(); + EXPECT_FALSE(list.empty()); + EXPECT_TRUE(list.usable()); + EXPECT_EQ(5u, list.size()); + EXPECT_TRUE(NULL != list.find(NULL)); + EXPECT_TRUE(NULL != list.find("")); + EXPECT_TRUE(NULL != list.find("MD5Sum")); + EXPECT_TRUE(NULL != list.find("Checksum-FileSize")); + EXPECT_TRUE(NULL == list.find("ROT26")); + + _config->Set("Acquire::ForceHash", "MD5Sum"); + EXPECT_FALSE(list.empty()); + EXPECT_TRUE(list.usable()); + EXPECT_EQ(5u, list.size()); + EXPECT_TRUE(NULL != list.find(NULL)); + EXPECT_TRUE(NULL != list.find("")); + EXPECT_TRUE(NULL != list.find("MD5Sum")); + EXPECT_TRUE(NULL != list.find("Checksum-FileSize")); + EXPECT_TRUE(NULL == list.find("ROT26")); + + _config->Set("Acquire::ForceHash", "ROT26"); + EXPECT_FALSE(list.empty()); + EXPECT_FALSE(list.usable()); + EXPECT_EQ(5u, list.size()); + EXPECT_TRUE(NULL == list.find(NULL)); + EXPECT_TRUE(NULL == list.find("")); + EXPECT_TRUE(NULL != list.find("MD5Sum")); + EXPECT_TRUE(NULL != list.find("Checksum-FileSize")); + EXPECT_TRUE(NULL == list.find("ROT26")); + + _config->Clear("Acquire::ForceHash"); + + list2.push_back(*list.find("MD5Sum")); + EXPECT_TRUE(list == list2); + EXPECT_FALSE(list != list2); + + // introduce a mismatch to the list + list2.push_back(HashString("SHA1", "cacecbd74968bc90ea3342767e6b94f46ddbcafc")); + EXPECT_FALSE(list == list2); + EXPECT_TRUE(list != list2); + + _config->Set("Acquire::ForceHash", "MD5Sum"); + EXPECT_TRUE(list == list2); + EXPECT_FALSE(list != list2); + + _config->Clear("Acquire::ForceHash"); +} diff --git a/test/libapt/indexcopytosourcelist_test.cc b/test/libapt/indexcopytosourcelist_test.cc new file mode 100644 index 0000000..b0bfeb4 --- /dev/null +++ b/test/libapt/indexcopytosourcelist_test.cc @@ -0,0 +1,109 @@ +#include <config.h> + +#include <apt-pkg/aptconfiguration.h> +#include <apt-pkg/configuration.h> +#include <apt-pkg/indexcopy.h> + +#include <string> +#include <stdio.h> + +#include <gtest/gtest.h> + +class NoCopy : private IndexCopy { + public: + std::string ConvertToSourceList(std::string const &CD,std::string &&Path) { + IndexCopy::ConvertToSourceList(CD, Path); + return Path; + } + bool GetFile(std::string &/*Filename*/, unsigned long long &/*Size*/) APT_OVERRIDE { return false; } + bool RewriteEntry(FileFd & /*Target*/, std::string const &/*File*/) APT_OVERRIDE { return false; } + const char *GetFileName() APT_OVERRIDE { return NULL; } + const char *Type() APT_OVERRIDE { return NULL; } + +}; + +TEST(IndexCopyTest, ConvertToSourceList) +{ + NoCopy ic; + std::string const CD("/media/cdrom/"); + + char const * Releases[] = { "unstable", "wheezy-updates", NULL }; + char const * Components[] = { "main", "non-free", NULL }; + + for (char const ** Release = Releases; *Release != NULL; ++Release) + { + SCOPED_TRACE(std::string("Release ") + *Release); + for (char const ** Component = Components; *Component != NULL; ++Component) + { + SCOPED_TRACE(std::string("Component ") + *Component); + std::string const Path = std::string("dists/") + *Release + "/" + *Component + "/"; + std::string const Binary = Path + "binary-"; + std::string const A = Binary + "armel/"; + std::string const B = Binary + "mips/"; + std::string const C = Binary + "kfreebsd-mips/"; + std::string const S = Path + "source/"; + std::string const List = std::string(*Release) + " " + *Component; + + { + SCOPED_TRACE("no archs configured"); + _config->Clear("APT"); + _config->Set("APT::Architecture", "all"); + _config->Set("APT::Architectures::", "all"); + APT::Configuration::getArchitectures(false); + EXPECT_EQ(A, ic.ConvertToSourceList("/media/cdrom/", CD + A)); + EXPECT_EQ(B, ic.ConvertToSourceList("/media/cdrom/", CD + B)); + EXPECT_EQ(C, ic.ConvertToSourceList("/media/cdrom/", CD + C)); + EXPECT_EQ(List, ic.ConvertToSourceList("/media/cdrom/", CD + S)); + } + + { + SCOPED_TRACE("mips configured"); + _config->Clear("APT"); + _config->Set("APT::Architecture", "mips"); + _config->Set("APT::Architectures::", "mips"); + APT::Configuration::getArchitectures(false); + EXPECT_EQ(A, ic.ConvertToSourceList("/media/cdrom/", CD + A)); + EXPECT_EQ(List, ic.ConvertToSourceList("/media/cdrom/", CD + B)); + EXPECT_EQ(C, ic.ConvertToSourceList("/media/cdrom/", CD + C)); + EXPECT_EQ(List, ic.ConvertToSourceList("/media/cdrom/", CD + S)); + } + + { + SCOPED_TRACE("kfreebsd-mips configured"); + _config->Clear("APT"); + _config->Set("APT::Architecture", "kfreebsd-mips"); + _config->Set("APT::Architectures::", "kfreebsd-mips"); + APT::Configuration::getArchitectures(false); + EXPECT_EQ(A, ic.ConvertToSourceList("/media/cdrom/", CD + A)); + EXPECT_EQ(B, ic.ConvertToSourceList("/media/cdrom/", CD + B)); + EXPECT_EQ(List, ic.ConvertToSourceList("/media/cdrom/", CD + C)); + EXPECT_EQ(List, ic.ConvertToSourceList("/media/cdrom/", CD + S)); + } + + { + SCOPED_TRACE("armel configured"); + _config->Clear("APT"); + _config->Set("APT::Architecture", "armel"); + _config->Set("APT::Architectures::", "armel"); + APT::Configuration::getArchitectures(false); + EXPECT_EQ(List, ic.ConvertToSourceList("/media/cdrom/", CD + A)); + EXPECT_EQ(B, ic.ConvertToSourceList("/media/cdrom/", CD + B)); + EXPECT_EQ(C, ic.ConvertToSourceList("/media/cdrom/", CD + C)); + EXPECT_EQ(List, ic.ConvertToSourceList("/media/cdrom/", CD + S)); + } + + { + SCOPED_TRACE("armel+mips configured"); + _config->Clear("APT"); + _config->Set("APT::Architecture", "armel"); + _config->Set("APT::Architectures::", "armel"); + _config->Set("APT::Architectures::", "mips"); + APT::Configuration::getArchitectures(false); + EXPECT_EQ(List, ic.ConvertToSourceList("/media/cdrom/", CD + A)); + EXPECT_EQ(List, ic.ConvertToSourceList("/media/cdrom/", CD + B)); + EXPECT_EQ(C, ic.ConvertToSourceList("/media/cdrom/", CD + C)); + EXPECT_EQ(List, ic.ConvertToSourceList("/media/cdrom/", CD + S)); + } + } + } +} diff --git a/test/libapt/install_progress_test.cc b/test/libapt/install_progress_test.cc new file mode 100644 index 0000000..68101af --- /dev/null +++ b/test/libapt/install_progress_test.cc @@ -0,0 +1,25 @@ +#include <config.h> + +#include <apt-pkg/install-progress.h> + +#include <string> + +#include <gtest/gtest.h> + +TEST(InstallProgressTest, FancyGetTextProgressStr) +{ + APT::Progress::PackageManagerFancy p; + + EXPECT_EQ(60u, p.GetTextProgressStr(0.5, 60).size()); + EXPECT_EQ("[#.]", p.GetTextProgressStr(0.5, 4)); + EXPECT_EQ("[..........]", p.GetTextProgressStr(0.0, 12)); + EXPECT_EQ("[#.........]", p.GetTextProgressStr(0.1, 12)); + EXPECT_EQ("[####......]", p.GetTextProgressStr(0.4999, 12)); + EXPECT_EQ("[#####.....]", p.GetTextProgressStr(0.5001, 12)); + EXPECT_EQ("[#########.]", p.GetTextProgressStr(0.9001, 12)); + EXPECT_EQ("[##########]", p.GetTextProgressStr(1.0, 12)); + + // deal with incorrect inputs gracefully (or should we die instead?) + EXPECT_EQ("[..........]", p.GetTextProgressStr(-1.0, 12)); + EXPECT_EQ("[##########]", p.GetTextProgressStr(2.0, 12)); +} diff --git a/test/libapt/json_test.cc b/test/libapt/json_test.cc new file mode 100644 index 0000000..ee8f3ce --- /dev/null +++ b/test/libapt/json_test.cc @@ -0,0 +1,69 @@ +#include <config.h> +#include "../../apt-private/private-cachefile.cc" +#include "../../apt-private/private-json-hooks.cc" +#include <gtest/gtest.h> +#include <string> + +TEST(JsonTest, JsonString) +{ + std::ostringstream os; + + // Check for escaping backslash and quotation marks, and ensure that we do not change number formatting + JsonWriter(os).value("H al\"l\\o").value(17); + + EXPECT_EQ("\"H al\\u0022l\\u005Co\"17", os.str()); + + for (int i = 0; i <= 0x1F; i++) + { + os.str(""); + + JsonWriter(os).encodeString(os, std::string("X") + char(i) + "Z"); + + std::string exp; + strprintf(exp, "\"X\\u%04XZ\"", i); + + EXPECT_EQ(exp, os.str()); + } +} + +TEST(JsonTest, JsonObject) +{ + std::ostringstream os; + + JsonWriter(os).beginObject().name("key").value("value").endObject(); + + EXPECT_EQ("{\"key\":\"value\"}", os.str()); +} + +TEST(JsonTest, JsonArrayAndValues) +{ + std::ostringstream os; + + JsonWriter(os).beginArray().value(0).value("value").value(1).value(true).endArray(); + + EXPECT_EQ("[0,\"value\",1,true]", os.str()); +} +TEST(JsonTest, JsonStackRegression) +{ + std::ostringstream os; + + JsonWriter w(os); + + // Nest those things deeply such that we transition states: + // object -> array -> object; -> array -> object + // Older versions never popped back and got stuck on array state. + w.beginObject(); + w.name("a").beginArray().beginObject().endObject().endArray(); + w.name("b").beginArray().beginObject().endObject().endArray(); + w.endObject(); + + EXPECT_EQ("{\"a\":[{}],\"b\":[{}]}", os.str()); +} +TEST(JsonTest, JsonNull) +{ + std::ostringstream os; + + JsonWriter(os).value(nullptr); + + EXPECT_EQ("null", os.str()); +} diff --git a/test/libapt/openmaybeclearsignedfile_test.cc b/test/libapt/openmaybeclearsignedfile_test.cc new file mode 100644 index 0000000..4db8967 --- /dev/null +++ b/test/libapt/openmaybeclearsignedfile_test.cc @@ -0,0 +1,364 @@ +#include <config.h> + +#include <apt-pkg/error.h> +#include <apt-pkg/fileutl.h> +#include <apt-pkg/gpgv.h> + +#include <string> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +/* The test files are created with the 'Joe Sixpack' and 'Marvin Paranoid' + test key included in the integration testing framework */ + +static void EXPECT_SUCCESSFUL_PARSE(std::string const &tempfile) +{ + FileFd fd; + EXPECT_TRUE(OpenMaybeClearSignedFile(tempfile, fd)); + EXPECT_TRUE(fd.IsOpen()); + char buffer[100]; + EXPECT_TRUE(fd.ReadLine(buffer, sizeof(buffer))); + EXPECT_STREQ(buffer, "Test"); + EXPECT_TRUE(fd.Eof()); +} + +TEST(OpenMaybeClearSignedFileTest,SimpleSignedFile) +{ + // Using c++11 raw-strings would be nifty, but travis doesn't support it… + auto const file = createTemporaryFile("simplesignedfile", "-----BEGIN PGP SIGNED MESSAGE-----\n" +"Hash: SHA512\n" +"\n" +"Test\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt\n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l\n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg\n" +"X/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k\n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx\n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns\n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq\n" +"=TB1F\n" +"-----END PGP SIGNATURE-----\n"); + EXPECT_TRUE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_SUCCESSFUL_PARSE(file.Name()); +} + +TEST(OpenMaybeClearSignedFileTest,WhitespaceSignedFile) +{ + // no raw-string here to protect the whitespace from cleanup + auto const file = createTemporaryFile("simplesignedfile", "-----BEGIN PGP SIGNED MESSAGE----- \t \n" +"Hash: SHA512 \n" +" \n" +"Test \n" +"-----BEGIN PGP SIGNATURE----- \n" +" \n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt \n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l \n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg \n" +"X/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k \n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx \n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns \n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq \n" +"=TB1F \n" +"-----END PGP SIGNATURE-----"); + EXPECT_TRUE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_SUCCESSFUL_PARSE(file.Name()); +} + +TEST(OpenMaybeClearSignedFileTest,SignedFileWithContentHeaders) +{ + auto const file = createTemporaryFile("headerssignedfile", "-----BEGIN PGP SIGNED MESSAGE-----\n" +"Version: 0.8.15~exp1\n" +"Hash: SHA512\n" +"Comment: I love you!\n" +"X-Expires: never\n" +"Multilines: no\n" +" yes\n" +" maybe\n" +"\n" +"Test\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt\n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l\n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg\n" +"X/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k\n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx\n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns\n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq\n" +"=TB1F\n" +"-----END PGP SIGNATURE-----\n"); + EXPECT_TRUE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_SUCCESSFUL_PARSE(file.Name()); +} + +TEST(OpenMaybeClearSignedFileTest,SignedFileWithTwoSignatures) +{ + auto const file = createTemporaryFile("doublesignedfile", "-----BEGIN PGP SIGNED MESSAGE-----\n" +"Hash: SHA512\n" +"\n" +"Test\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt\n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l\n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg\n" +"X/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k\n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx\n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns\n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq\n" +"=TB1F\n" +"-----END PGP SIGNATURE-----\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFHBAEBCgAxFiEE3mauypFRr6GHfsMd6FJdR1KBROIFAlhT/yYTHG1hcnZpbkBl\n" +"eGFtcGxlLm9yZwAKCRDoUl1HUoFE4qq3B/459MSk3xCW30wc5+ul5ZxTSg6eLYPJ\n" +"tfVNYi90/ZxRrYQAN+EWozEIZcxoMYp8Ans3++irkjPbHs4NsesmFKt2W5meFl4V\n" +"oUzYrOh5y5GlDeF7ok5g9atQe8BojjBics+g1IBYcnaMU+ywONmlixa03IPGfxV5\n" +"oTx02Xvlns20i6HRc0WFtft5q1hXo4EIlVc9O0u902SVEEkeuHF3+bCcXrNLPBJA\n" +"+8dxmH5+i89f/kVqURrdHdEuA1tsTNyb2C+lvRONh21H8QRRTU/iUQSzV6vZvof5\n" +"ASc9hsAZRG0xHuRU0F94V/XrkWw8QYAobJ/yxvs4L0EuA4optbSqawDB\n" +"=CP8j\n" +"-----END PGP SIGNATURE-----\n"); + EXPECT_TRUE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_SUCCESSFUL_PARSE(file.Name()); +} + + +static void EXPECT_FAILED_PARSE(std::string const &tempfile, std::string const &error) +{ + EXPECT_TRUE(_error->empty()); + FileFd fd; + EXPECT_FALSE(OpenMaybeClearSignedFile(tempfile, fd)); + EXPECT_FALSE(_error->empty()); + EXPECT_FALSE(fd.IsOpen()); + ASSERT_TRUE(_error->PendingError()); + + std::string msg; + EXPECT_TRUE(_error->PopMessage(msg)); + EXPECT_EQ(msg, error); +} + +TEST(OpenMaybeClearSignedFileTest,TwoSimpleSignedFile) +{ + // read only the first message + auto const file = createTemporaryFile("twosimplesignedfile", "-----BEGIN PGP SIGNED MESSAGE-----\n" +"Hash: SHA512\n" +"\n" +"Test\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt\n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l\n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg\n" +"X/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k\n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx\n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns\n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq\n" +"=TB1F\n" +"-----END PGP SIGNATURE-----\n" +"-----BEGIN PGP SIGNED MESSAGE-----\n" +"Hash: SHA512\n" +"\n" +"Test\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt\n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l\n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg\n" +"X/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k\n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx\n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns\n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq\n" +"=TB1F\n" +"-----END PGP SIGNATURE-----"); + EXPECT_TRUE(_error->empty()); + EXPECT_TRUE(StartsWithGPGClearTextSignature(file.Name())); + // technically they are signed, but we just want one message + EXPECT_FAILED_PARSE(file.Name(), "Clearsigned file '" + file.Name() + "' contains unsigned lines."); +} + +TEST(OpenMaybeClearSignedFileTest,UnsignedFile) +{ + auto const file = createTemporaryFile("unsignedfile", "Test"); + EXPECT_FALSE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_SUCCESSFUL_PARSE(file.Name()); +} + +TEST(OpenMaybeClearSignedFileTest,GarbageTop) +{ + auto const file = createTemporaryFile("garbagetop", "Garbage\n" +"-----BEGIN PGP SIGNED MESSAGE-----\n" +"Hash: SHA512\n" +"\n" +"Test\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt\n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l\n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg\n" +"X/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k\n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx\n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns\n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq\n" +"=TB1F\n" +"-----END PGP SIGNATURE-----\n"); + EXPECT_FALSE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_FAILED_PARSE(file.Name(), "Clearsigned file '" + file.Name() + "' does not start with a signed message block."); +} + +TEST(OpenMaybeClearSignedFileTest,GarbageHeader) +{ + auto const file = createTemporaryFile("garbageheader", "-----BEGIN PGP SIGNED MESSAGE----- Garbage\n" +"Hash: SHA512\n" +"\n" +"Test\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt\n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l\n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg\n" +"X/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k\n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx\n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns\n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq\n" +"=TB1F\n" +"-----END PGP SIGNATURE-----\n"); + EXPECT_FALSE(StartsWithGPGClearTextSignature(file.Name())); + // beware: the file will be successfully opened as unsigned file + FileFd fd; + EXPECT_TRUE(OpenMaybeClearSignedFile(file.Name(), fd)); + EXPECT_TRUE(fd.IsOpen()); + char buffer[100]; + EXPECT_TRUE(fd.ReadLine(buffer, sizeof(buffer))); + EXPECT_STREQ(buffer, "-----BEGIN PGP SIGNED MESSAGE----- Garbage\n"); + EXPECT_FALSE(fd.Eof()); +} + +TEST(OpenMaybeClearSignedFileTest,GarbageBottom) +{ + auto const file = createTemporaryFile("garbagebottom", "-----BEGIN PGP SIGNED MESSAGE-----\n" +"Hash: SHA512\n" +"\n" +"Test\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt\n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l\n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg\n" +"X/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k\n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx\n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns\n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq\n" +"=TB1F\n" +"-----END PGP SIGNATURE-----\n" +"Garbage"); + EXPECT_TRUE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_FAILED_PARSE(file.Name(), "Clearsigned file '" + file.Name() + "' contains unsigned lines."); +} + +TEST(OpenMaybeClearSignedFileTest,BogusNoSig) +{ + auto const file = createTemporaryFile("bogusnosig", "-----BEGIN PGP SIGNED MESSAGE-----\n" +"Hash: SHA512\n" +"\n" +"Test"); + EXPECT_TRUE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_FAILED_PARSE(file.Name(), "Splitting of clearsigned file " + file.Name() + " failed as it doesn't contain all expected parts"); +} + +TEST(OpenMaybeClearSignedFileTest,BogusSigStart) +{ + auto const file = createTemporaryFile("bogusnosig", "-----BEGIN PGP SIGNED MESSAGE-----\n" +"Hash: SHA512\n" +"\n" +"Test\n" +"-----BEGIN PGP SIGNATURE-----"); + EXPECT_TRUE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_FAILED_PARSE(file.Name(), "Signature in file " + file.Name() + " wasn't closed"); +} + +TEST(OpenMaybeClearSignedFileTest,DashedSignedFile) +{ + auto const file = createTemporaryFile("dashedsignedfile", "-----BEGIN PGP SIGNED MESSAGE-----\n" +"Hash: SHA512\n" +"\n" +"- Test\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt\n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l\n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg\n" +"X/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k\n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx\n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns\n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq\n" +"=TB1F\n" +"-----END PGP SIGNATURE-----\n"); + EXPECT_TRUE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_SUCCESSFUL_PARSE(file.Name()); +} +TEST(OpenMaybeClearSignedFileTest,StrangeDashArmorFile) +{ + auto const file = createTemporaryFile("strangedashfile", "-----BEGIN PGP SIGNED MESSAGE-----\n" +"Hash: SHA512\n" +"-Hash: SHA512\n" +"\n" +"Test\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt\n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l\n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg\n" +"X/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k\n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx\n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns\n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq\n" +"=TB1F\n" +"-----END PGP SIGNATURE-----\n"); + EXPECT_TRUE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_FAILED_PARSE(file.Name(), "Clearsigned file '" + file.Name() + "' contains unexpected line starting with a dash (armor)"); +} +TEST(OpenMaybeClearSignedFileTest,StrangeDashMsgFile) +{ + auto const file = createTemporaryFile("strangedashfile", "-----BEGIN PGP SIGNED MESSAGE-----\n" +"Hash: SHA512\n" +"\n" +"-Test\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt\n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l\n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg\n" +"X/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k\n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx\n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns\n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq\n" +"=TB1F\n" +"-----END PGP SIGNATURE-----\n"); + EXPECT_TRUE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_FAILED_PARSE(file.Name(), "Clearsigned file '" + file.Name() + "' contains unexpected line starting with a dash (msg)"); +} +TEST(OpenMaybeClearSignedFileTest,StrangeDashSigFile) +{ + auto const file = createTemporaryFile("strangedashfile", "-----BEGIN PGP SIGNED MESSAGE-----\n" +"Hash: SHA512\n" +"\n" +"Test\n" +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iQFEBAEBCgAuFiEENKjp0Y2zIPNn6OqgWpDRQdusja4FAlhT7+kQHGpvZUBleGFt\n" +"cGxlLm9yZwAKCRBakNFB26yNrjvEB/9/e3jA1l0fvPafx9LEXcH8CLpUFQK7ra9l\n" +"3M4YAH4JKQlTG1be7ixruBRlCTh3YiSs66fKMeJeUYoxA2HPhvbGFEjQFAxunEYg\n" +"-/LBKv1mQWa+Q34P5GBjK8kQdLCN+yJAiUErmWNQG3GPninrxsC9tY5jcWvHeP1k\n" +"V7N3MLnNqzXaCJM24mnKidC5IDadUdQ8qC8c3rjUexQ8vBz0eucH56jbqV5oOcvx\n" +"pjlW965dCPIf3OI8q6J7bIOjyY+u/PTcVlqPq3TUz/ti6RkVbKpLH0D4ll3lUTns\n" +"JQt/+gJCPxHUJphy8sccBKhW29CLELJIIafvU30E1nWn9szh2Xjq\n" +"=TB1F\n" +"-----END PGP SIGNATURE-----\n"); + EXPECT_TRUE(StartsWithGPGClearTextSignature(file.Name())); + EXPECT_FAILED_PARSE(file.Name(), "Clearsigned file '" + file.Name() + "' contains unexpected line starting with a dash (sig)"); +} diff --git a/test/libapt/parsedepends_test.cc b/test/libapt/parsedepends_test.cc new file mode 100644 index 0000000..ed849f7 --- /dev/null +++ b/test/libapt/parsedepends_test.cc @@ -0,0 +1,280 @@ +#include <config.h> + +#include <apt-pkg/configuration.h> +#include <apt-pkg/deblistparser.h> +#include <apt-pkg/pkgcache.h> + +#include <string> +#include <string.h> + +#include <gtest/gtest.h> + +static void parseDependency(bool const StripMultiArch, bool const ParseArchFlags, bool const ParseRestrictionsList, std::string Arch) +{ + std::string Package; + std::string Version; + unsigned int Op = 5; + unsigned int Null = 0; + // The tests are made for amd64. Specify a different arch here to check if + // they still work. + _config->Set("APT::Architecture",Arch); + _config->Set("APT::Build-Profiles","stage1"); + + const char* Depends = + "debhelper:any (>= 5.0), " + "libdb-dev:any, " + "gettext:native (<= 0.12), " + "libcurl4-gnutls-dev:native | libcurl3-gnutls-dev (>> 7.15.5), " + "docbook-xml, " + "apt (>= 0.7.25), " + "not-for-me [ !amd64 ], " + "only-for-me [ amd64 ], " + "any-for-me [ any ], " + "not-for-darwin [ !darwin-any ], " + "cpu-for-me [ any-amd64 ], " + "os-for-me [ linux-any ], " + "libc-for-me [ gnu-linux-any ], " + "libc-not-for-me [ musl-linux-any ], " + "cpu-not-for-me [ any-armel ], " + "os-not-for-me [ kfreebsd-any ], " + "not-in-stage1 <!stage1>, " + "not-stage1-and-not-nodoc <!nodoc !stage1>, " + "not-stage1-or-not-nodoc <!nodoc> <!stage1>, " + "unknown-profile <unknown stage1>, " + "overlord-dev:any (= 7.15.3~) | overlord-dev:native (>> 7.15.5), " + ; + + // Stripping MultiArch is currently the default setting to not confuse + // non-MultiArch capable users of the library with "strange" extensions. + const char* Start = Depends; + const char* End = Depends + strlen(Depends); + + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + if (StripMultiArch == true) + EXPECT_EQ("debhelper", Package); + else + EXPECT_EQ("debhelper:any", Package); + EXPECT_EQ("5.0", Version); + EXPECT_EQ(Null | pkgCache::Dep::GreaterEq, Op); + + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + if (StripMultiArch == true) + EXPECT_EQ("libdb-dev", Package); + else + EXPECT_EQ("libdb-dev:any", Package); + EXPECT_EQ("", Version); + EXPECT_EQ(Null | pkgCache::Dep::NoOp, Op); + + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + if (StripMultiArch == true) + EXPECT_EQ("gettext", Package); + else + EXPECT_EQ("gettext:native", Package); + EXPECT_EQ("0.12", Version); + EXPECT_EQ(Null | pkgCache::Dep::LessEq, Op); + + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + if (StripMultiArch == true) + EXPECT_EQ("libcurl4-gnutls-dev", Package); + else + EXPECT_EQ("libcurl4-gnutls-dev:native", Package); + EXPECT_EQ("", Version); + EXPECT_EQ(Null | pkgCache::Dep::Or, Op); + + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("libcurl3-gnutls-dev", Package); + EXPECT_EQ("7.15.5", Version); + EXPECT_EQ(Null | pkgCache::Dep::Greater, Op); + + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("docbook-xml", Package); + EXPECT_EQ("", Version); + EXPECT_EQ(Null | pkgCache::Dep::NoOp, Op); + + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("apt", Package); + EXPECT_EQ("0.7.25", Version); + EXPECT_EQ(Null | pkgCache::Dep::GreaterEq, Op); + + if (ParseArchFlags == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("", Package); // not-for-me + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseArchFlags == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("only-for-me", Package); + EXPECT_EQ("", Version); + EXPECT_EQ(Null | pkgCache::Dep::NoOp, Op); + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseArchFlags == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("any-for-me", Package); + EXPECT_EQ("", Version); + EXPECT_EQ(Null | pkgCache::Dep::NoOp, Op); + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseArchFlags == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("not-for-darwin", Package); + EXPECT_EQ("", Version); + EXPECT_EQ(Null | pkgCache::Dep::NoOp, Op); + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseArchFlags == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("cpu-for-me", Package); + EXPECT_EQ("", Version); + EXPECT_EQ(Null | pkgCache::Dep::NoOp, Op); + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseArchFlags == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("os-for-me", Package); + EXPECT_EQ("", Version); + EXPECT_EQ(Null | pkgCache::Dep::NoOp, Op); + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseArchFlags == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("libc-for-me", Package); + EXPECT_EQ("", Version); + EXPECT_EQ(Null | pkgCache::Dep::NoOp, Op); + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseArchFlags == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("", Package); // libc-not-for-me + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseArchFlags == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("", Package); // cpu-not-for-me + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseArchFlags == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("", Package); // os-not-for-me + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseRestrictionsList == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("", Package); // not-in-stage1 + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseRestrictionsList == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("", Package); // not-stage1-and-not-nodoc + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseRestrictionsList == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("not-stage1-or-not-nodoc", Package); + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + if (ParseRestrictionsList == true) { + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + EXPECT_EQ("", Package); // unknown-profile + } else { + EXPECT_EQ(true, 0 == debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64")); + Start = strstr(Start, ","); + Start++; + } + + Start = debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + if (StripMultiArch == true) + EXPECT_EQ("overlord-dev", Package); + else + EXPECT_EQ("overlord-dev:any", Package); + EXPECT_EQ("7.15.3~", Version); + EXPECT_EQ(Null | pkgCache::Dep::Equals | pkgCache::Dep::Or, Op); + + debListParser::ParseDepends(Start, End, Package, Version, Op, ParseArchFlags, StripMultiArch, ParseRestrictionsList, "amd64"); + if (StripMultiArch == true) + EXPECT_EQ("overlord-dev", Package); + else + EXPECT_EQ("overlord-dev:native", Package); + EXPECT_EQ("7.15.5", Version); + EXPECT_EQ(Null | pkgCache::Dep::Greater, Op); +} + +// FIXME: This testcase is too big/complex +TEST(ParseDependsTest, Everything) +{ + bool StripMultiArch = true; + bool ParseArchFlags = false; + bool ParseRestrictionsList = false; + unsigned short runner = 0; + +test: + { + SCOPED_TRACE(std::string("StripMultiArch: ") + (StripMultiArch ? "true" : "false")); + SCOPED_TRACE(std::string("ParseArchFlags: ") + (ParseArchFlags ? "true" : "false")); + SCOPED_TRACE(std::string("ParseRestrictionsList: ") + (ParseRestrictionsList ? "true" : "false")); + parseDependency(StripMultiArch, ParseArchFlags, ParseRestrictionsList, "kfreebsd-i386"); + parseDependency(StripMultiArch, ParseArchFlags, ParseRestrictionsList, "amd64"); + } + if (StripMultiArch == false) { + if (ParseArchFlags == false) + ParseRestrictionsList = !ParseRestrictionsList; + ParseArchFlags = !ParseArchFlags; + } + StripMultiArch = !StripMultiArch; + + runner++; + if (runner < 8) + goto test; // this is the prove: tests are really evil ;) +} diff --git a/test/libapt/pattern_test.cc b/test/libapt/pattern_test.cc new file mode 100644 index 0000000..55bc4bd --- /dev/null +++ b/test/libapt/pattern_test.cc @@ -0,0 +1,224 @@ +/* + * cachefilter-patterns.h - Pattern parser and additional patterns as matchers + * + * Copyright (c) 2019 Canonical Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <apt-pkg/cachefilter-patterns.h> +#include <apt-pkg/cachefilter.h> + +#include <gtest/gtest.h> + +using namespace APT::Internal; + +#define EXPECT_EXCEPTION(exp, exc, msg) \ + caught = false; \ + try \ + { \ + exp; \ + } \ + catch (exc & e) \ + { \ + caught = true; \ + EXPECT_TRUE(e.message.find(msg) != std::string::npos) << msg << " not in " << e.message; \ + }; \ + EXPECT_TRUE(caught) << #exp "should have thrown an exception" + +TEST(TreeParserTest, ParseInvalid) +{ + bool caught = false; + + // Not a valid pattern: Reject + EXPECT_EXCEPTION(PatternTreeParser("?").parse(), PatternTreeParser::Error, "Pattern must have a term"); + EXPECT_EXCEPTION(PatternTreeParser("?AB?").parse(), PatternTreeParser::Error, "Pattern must have a term"); + EXPECT_EXCEPTION(PatternTreeParser("~").parse(), PatternTreeParser::Error, "Unknown short pattern"); + + // Not a pattern at all: Report nullptr + EXPECT_EQ(PatternTreeParser("A?").parse(), nullptr); +} + +TEST(TreeParserTest, ParseWord) +{ + auto node = PatternTreeParser("?word(word)").parseTop(); + auto patternNode = dynamic_cast<PatternTreeParser::PatternNode *>(node.get()); + + ASSERT_EQ(patternNode->arguments.size(), 1u); + auto wordNode = dynamic_cast<PatternTreeParser::WordNode *>(patternNode->arguments[0].get()); + + EXPECT_EQ(patternNode->arguments[0].get(), wordNode); + EXPECT_EQ(wordNode->word, "word"); +} + +TEST(TreeParserTest, ParseQuotedWord) +{ + auto node = PatternTreeParser("?word(\"a word\")").parseTop(); + auto patternNode = dynamic_cast<PatternTreeParser::PatternNode *>(node.get()); + + ASSERT_EQ(patternNode->arguments.size(), 1u); + auto wordNode = dynamic_cast<PatternTreeParser::WordNode *>(patternNode->arguments[0].get()); + + EXPECT_EQ(patternNode->arguments[0].get(), wordNode); + EXPECT_EQ(wordNode->word, "a word"); +} + +TEST(TreeParserTest, ParsePattern) +{ + auto node = PatternTreeParser("?hello").parseTop(); + auto patternNode = dynamic_cast<PatternTreeParser::PatternNode *>(node.get()); + + EXPECT_EQ(node.get(), patternNode); + EXPECT_EQ(patternNode->term, "?hello"); + EXPECT_TRUE(patternNode->arguments.empty()); + EXPECT_FALSE(patternNode->haveArgumentList); +} + +TEST(TreeParserTest, ParseWithEmptyArgs) +{ + auto node = PatternTreeParser("?hello()").parseTop(); + auto patternNode = dynamic_cast<PatternTreeParser::PatternNode *>(node.get()); + + EXPECT_EQ(node.get(), patternNode); + EXPECT_EQ(patternNode->term, "?hello"); + EXPECT_TRUE(patternNode->arguments.empty()); + EXPECT_TRUE(patternNode->haveArgumentList); +} + +TEST(TreeParserTest, ParseWithOneArgs) +{ + auto node = PatternTreeParser("?hello(foo)").parseTop(); + auto patternNode = dynamic_cast<PatternTreeParser::PatternNode *>(node.get()); + + EXPECT_EQ(node.get(), patternNode); + EXPECT_EQ(patternNode->term, "?hello"); + EXPECT_EQ(1u, patternNode->arguments.size()); +} + +TEST(TreeParserTest, ParseWithManyArgs) +{ + auto node = PatternTreeParser("?hello(foo,bar)").parseTop(); + auto patternNode = dynamic_cast<PatternTreeParser::PatternNode *>(node.get()); + + EXPECT_EQ(node.get(), patternNode); + EXPECT_EQ(patternNode->term, "?hello"); + EXPECT_EQ(2u, patternNode->arguments.size()); +} + +TEST(TreeParserTest, ParseWithManyArgsWithSpaces) +{ + auto node = PatternTreeParser("?hello (foo, bar)").parseTop(); + auto patternNode = dynamic_cast<PatternTreeParser::PatternNode *>(node.get()); + + EXPECT_EQ(node.get(), patternNode); + EXPECT_EQ(patternNode->term, "?hello"); + EXPECT_EQ(2u, patternNode->arguments.size()); +} + +TEST(TreeParserTest, ParseWithManyArgsWithSpacesWithTrailingComma) +{ + auto node = PatternTreeParser("?hello (foo, bar,)").parseTop(); + auto patternNode = dynamic_cast<PatternTreeParser::PatternNode *>(node.get()); + + EXPECT_EQ(node.get(), patternNode); + EXPECT_EQ(patternNode->term, "?hello"); + EXPECT_EQ(2u, patternNode->arguments.size()); +} + +// Helper +static bool samePattern(const std::unique_ptr<PatternTreeParser::Node> &a, const std::unique_ptr<PatternTreeParser::Node> &b) +{ + auto pa = dynamic_cast<const PatternTreeParser::PatternNode *>(a.get()); + auto pb = dynamic_cast<const PatternTreeParser::PatternNode *>(b.get()); + + if (pa && pb) + { + if (pa->term != pb->term || pa->haveArgumentList != pb->haveArgumentList || pa->arguments.size() != pb->arguments.size()) + return false; + + for (size_t i = 0; i < pa->arguments.size(); i++) + { + if (!samePattern(pa->arguments[i], pb->arguments[i])) + return false; + } + return true; + } + + auto wa = dynamic_cast<const PatternTreeParser::WordNode *>(a.get()); + auto wb = dynamic_cast<const PatternTreeParser::WordNode *>(b.get()); + if (wa && wb) + return wa->word == wb->word && wa->quoted == wb->quoted; + + return false; +} + +#define EXPECT_PATTERN_EQ(shrt, lng) \ + EXPECT_TRUE(samePattern(PatternTreeParser(shrt).parseTop(), PatternTreeParser(lng).parseTop())) +#define EXPECT_PATTERN_EQ_ATOMIC(shrt, lng) \ + EXPECT_TRUE(PatternTreeParser(shrt).parseTop()); \ + caught = false; \ + try \ + { \ + PatternTreeParser(shrt "XXX").parseTop(); \ + } \ + catch (PatternTreeParser::Error & e) \ + { \ + caught = true; \ + }; \ + EXPECT_TRUE(caught) << shrt "XXX should have thrown an exception"; \ + EXPECT_PATTERN_EQ(shrt, lng) + +TEST(TreeParserTest, ParseShortPattern) +{ + bool caught; + EXPECT_PATTERN_EQ("~ramd64", "?architecture(amd64)"); + EXPECT_PATTERN_EQ("~AanArchive", "?archive(anArchive)"); + EXPECT_PATTERN_EQ_ATOMIC("~M", "?automatic"); + EXPECT_PATTERN_EQ_ATOMIC("~b", "?broken"); + EXPECT_PATTERN_EQ_ATOMIC("~c", "?config-files"); + EXPECT_PATTERN_EQ_ATOMIC("~E", "?essential"); + EXPECT_PATTERN_EQ_ATOMIC("~F", "?false"); + EXPECT_PATTERN_EQ_ATOMIC("~g", "?garbage"); + EXPECT_PATTERN_EQ_ATOMIC("~i", "?installed"); + EXPECT_PATTERN_EQ("~napt", "?name(apt)"); + EXPECT_PATTERN_EQ_ATOMIC("~o", "?obsolete"); + EXPECT_PATTERN_EQ("~Obar", "?origin(bar)"); + EXPECT_PATTERN_EQ("~sfoo", "?section(foo)"); + EXPECT_PATTERN_EQ("~esourcename", "?source-package(sourcename)"); + EXPECT_PATTERN_EQ_ATOMIC("~T", "?true"); + EXPECT_PATTERN_EQ_ATOMIC("~U", "?upgradable"); + EXPECT_PATTERN_EQ("~Vverstr", "?version(verstr)"); + EXPECT_PATTERN_EQ_ATOMIC("~v", "?virtual"); + EXPECT_PATTERN_EQ("!?foo", "?not(?foo)"); + + caught = false; + try + { + PatternTreeParser("!x").parseTop(); + } + catch (PatternTreeParser::Error &e) + { + caught = true; + }; + EXPECT_TRUE(caught) << "!X should have thrown an exception"; + + EXPECT_PATTERN_EQ("?a?b", "?and(?a, ?b)"); + EXPECT_PATTERN_EQ("~T~F", "?and(?true, ?false)"); + EXPECT_PATTERN_EQ("~T ~F", "?and(?true, ?false)"); + EXPECT_PATTERN_EQ("~T !~F", "?and(?true, ?not(?false))"); + EXPECT_PATTERN_EQ("!~F ~T", "?and(?not(?false), ?true)"); + EXPECT_PATTERN_EQ("!~F~T", "?and(?not(?false), ?true)"); + + EXPECT_PATTERN_EQ("!~F~T | ~T", "?or(?and(?not(?false), ?true), ?true)"); + EXPECT_PATTERN_EQ("~ramd64|~rall", "?or(?architecture(amd64), ?architecture(all))"); + EXPECT_PATTERN_EQ("~ramd64 | ~rall", "?or(?architecture(amd64), ?architecture(all))"); + EXPECT_PATTERN_EQ("~ramd64?name(foo)", "?and(?architecture(amd64), ?name(foo))"); + + EXPECT_PATTERN_EQ("(?A|?B)?C", "?and(?or(?A, ?B), ?C)"); + EXPECT_PATTERN_EQ("?A|?B?C", "?or(?A, ?and(?B, ?C))"); + EXPECT_PATTERN_EQ("?A|(?B?C)", "?or(?A, ?and(?B, ?C))"); + EXPECT_PATTERN_EQ("(?B?C)|?A", "?or(?and(?B, ?C), ?A)"); + EXPECT_PATTERN_EQ("~napt~nfoo", "?and(?name(apt),?name(foo))"); + EXPECT_PATTERN_EQ("~napt!~nfoo", "?and(?name(apt),?not(?name(foo)))"); +} diff --git a/test/libapt/priority_test.cc b/test/libapt/priority_test.cc new file mode 100644 index 0000000..af7932a --- /dev/null +++ b/test/libapt/priority_test.cc @@ -0,0 +1,16 @@ +#include <config.h> +#include <apt-pkg/pkgcache.h> +#include <gtest/gtest.h> +#include <string> + +using std::string; + +// Tests for Bug#807523 +TEST(PriorityTest, PriorityPrinting) +{ + EXPECT_EQ("required", string(pkgCache::Priority(pkgCache::State::Required))); + EXPECT_EQ("important", string(pkgCache::Priority(pkgCache::State::Important))); + EXPECT_EQ("standard", string(pkgCache::Priority(pkgCache::State::Standard))); + EXPECT_EQ("optional", string(pkgCache::Priority(pkgCache::State::Optional))); + EXPECT_EQ("extra", string(pkgCache::Priority(pkgCache::State::Extra))); +} diff --git a/test/libapt/sourcelist_test.cc b/test/libapt/sourcelist_test.cc new file mode 100644 index 0000000..42fab65 --- /dev/null +++ b/test/libapt/sourcelist_test.cc @@ -0,0 +1,33 @@ +#include <config.h> + +#include <apt-pkg/fileutl.h> +#include <apt-pkg/sourcelist.h> + +#include <string> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +TEST(SourceListTest,ParseFileDeb822) +{ + auto const file = createTemporaryFile("parsefiledeb822.XXXXXX.sources", + "Types: deb\n" + "URIs: http://ftp.debian.org/debian\n" + "Suites: stable\n" + "Components: main\n" + "Description: short\n" + " long description that can be very long\n" + "\n" + "Types: deb\n" + "URIs: http://ftp.debian.org/debian\n" + "Suites: unstable\n" + "Components: main non-free\n"); + + pkgSourceList sources; + EXPECT_TRUE(sources.Read(file.Name())); + EXPECT_EQ(2u, sources.size()); +} diff --git a/test/libapt/srvrecs_test.cc b/test/libapt/srvrecs_test.cc new file mode 100644 index 0000000..f042579 --- /dev/null +++ b/test/libapt/srvrecs_test.cc @@ -0,0 +1,85 @@ +#include <config.h> + +#include <apt-pkg/srvrec.h> +#include <apt-pkg/strutl.h> + +#include <algorithm> +#include <iostream> +#include <string> + +#include <gtest/gtest.h> + +TEST(SrvRecTest, PopFromSrvRecs) +{ + std::vector<SrvRec> Meep; + Meep.emplace_back("foo", 20, 0, 80); + Meep.emplace_back("bar", 20, 0, 80); + Meep.emplace_back("baz", 30, 0, 80); + + EXPECT_EQ(Meep.size(), 3u); + SrvRec const result = PopFromSrvRecs(Meep); + // ensure that pop removed one element + EXPECT_EQ(Meep.size(), 2u); + EXPECT_NE(result.target, "baz"); + + SrvRec const result2 = PopFromSrvRecs(Meep); + EXPECT_NE(result.target, result2.target); + EXPECT_NE(result2.target, "baz"); + EXPECT_EQ(Meep.size(), 1u); + + SrvRec const result3 = PopFromSrvRecs(Meep); + EXPECT_EQ(result3.target, "baz"); + EXPECT_TRUE(Meep.empty()); +} + +TEST(SrvRecTest,Randomness) +{ + constexpr unsigned int testLength = 100; + std::vector<SrvRec> base1; + std::vector<SrvRec> base2; + std::vector<SrvRec> base3; + for (unsigned int i = 0; i < testLength; ++i) + { + std::string name; + strprintf(name, "foo%d", i); + base1.emplace_back(name, 20, 0, 80); + base2.emplace_back(name, 20, 0, 80); + base3.emplace_back(name, 30, 0, 80); + } + EXPECT_EQ(testLength, base1.size()); + EXPECT_EQ(testLength, base2.size()); + EXPECT_EQ(testLength, base3.size()); + std::move(base3.begin(), base3.end(), std::back_inserter(base2)); + EXPECT_EQ(testLength*2, base2.size()); + + std::vector<SrvRec> first_pull; + auto const startingClock = clock(); + for (unsigned int i = 0; i < testLength; ++i) + first_pull.push_back(PopFromSrvRecs(base1)); + EXPECT_TRUE(base1.empty()); + EXPECT_FALSE(first_pull.empty()); + EXPECT_EQ(testLength, first_pull.size()); + + // busy-wait for a cpu-clock change as we use it as "random" value + if (startingClock != -1) + for (int i = 0; i < 100000; ++i) + if (startingClock != clock()) + break; + + std::vector<SrvRec> second_pull; + for (unsigned int i = 0; i < testLength; ++i) + second_pull.push_back(PopFromSrvRecs(base2)); + EXPECT_FALSE(base2.empty()); + EXPECT_FALSE(second_pull.empty()); + EXPECT_EQ(testLength, second_pull.size()); + + EXPECT_EQ(first_pull.size(), second_pull.size()); + EXPECT_TRUE(std::all_of(first_pull.begin(), first_pull.end(), [](SrvRec const &R) { return R.priority == 20; })); + EXPECT_TRUE(std::all_of(second_pull.begin(), second_pull.end(), [](SrvRec const &R) { return R.priority == 20; })); + if (startingClock != -1 && startingClock != clock()) + { + EXPECT_FALSE(std::equal(first_pull.begin(), first_pull.end(), second_pull.begin())); + } + + EXPECT_TRUE(std::all_of(base2.begin(), base2.end(), [](SrvRec const &R) { return R.priority == 30; })); +} diff --git a/test/libapt/stringview_test.cc b/test/libapt/stringview_test.cc new file mode 100644 index 0000000..9cfaa3b --- /dev/null +++ b/test/libapt/stringview_test.cc @@ -0,0 +1,95 @@ + +#include <config.h> +#include <apt-pkg/string_view.h> +#include <string> + +#include <type_traits> + +#include <gtest/gtest.h> + +TEST(StringViewTest,EmptyString) +{ + constexpr APT::StringView defString; + static_assert( 0 == defString.length(), "def right size"); + + APT::StringView strString{std::string{}}; + EXPECT_EQ(0u, strString.length()); + + constexpr char const * const charp = ""; + constexpr APT::StringView charpString{charp, 0}; + static_assert( 0 == charpString.length(), "charp right size"); + + APT::StringView charp2String{charp}; + EXPECT_EQ(0u, strString.length()); + + const APT::StringView charaString{""}; + EXPECT_EQ(0u, charaString.length()); + + EXPECT_TRUE(APT::StringView("") == ""); + EXPECT_FALSE(APT::StringView("") != ""); +} + +TEST(StringViewTest,FooString) +{ + constexpr APT::StringView defString("fooGARBAGE", 3); + static_assert( 3 == defString.length(), "def right size"); + EXPECT_EQ(0, defString.to_string().compare(0, defString.length(), defString.data(), 3)); + + APT::StringView strString{std::string{"foo"}}; + EXPECT_EQ(3u, strString.length()); + EXPECT_EQ(0, strString.to_string().compare(0, strString.length(), strString.data(), 3)); + + constexpr char const * const charp = "fooGARBAGE"; + constexpr APT::StringView charpString{charp, 3}; + EXPECT_EQ(3u, charpString.length()); + EXPECT_EQ(0, charpString.to_string().compare(0, charpString.length(), charpString.data(), 3)); + + char * charp2 = strdup("foo"); + APT::StringView charp2String{charp2}; + EXPECT_EQ(3u, charp2String.length()); + EXPECT_EQ(0, charp2String.to_string().compare(0, charp2String.length(), charp2String.data(), 3)); + free(charp2); + + const APT::StringView charaString{"foo"}; + EXPECT_EQ(3u, charaString.length()); + EXPECT_EQ(0, charaString.to_string().compare(0, charaString.length(), charaString.data(), 3)); + + EXPECT_TRUE(APT::StringView("foo") == "foo"); + EXPECT_FALSE(APT::StringView("foo") != "foo"); +} + +TEST(StringViewTest,SubStr) +{ + const APT::StringView defString("Hello World!"); + EXPECT_EQ(defString.to_string().substr(6), defString.substr(6).to_string()); + EXPECT_EQ(defString.to_string().substr(0,5), defString.substr(0,5).to_string()); + EXPECT_EQ(defString.to_string().substr(6,5), defString.substr(6,5).to_string()); +} + +TEST(StringViewTest,Find) +{ + const APT::StringView defString("Hello World!"); + EXPECT_EQ(defString.to_string().find('l'), defString.find('l')); + EXPECT_EQ(defString.to_string().find('X'), defString.find('X')); + EXPECT_EQ(defString.to_string().find('e',3), defString.find('e',3)); + EXPECT_EQ(defString.to_string().find('l',6), defString.find('l',6)); + EXPECT_EQ(defString.to_string().find('l',11), defString.find('l',11)); + + EXPECT_EQ(defString.to_string().find("l"), defString.find("l")); + EXPECT_EQ(defString.to_string().find("ll"), defString.find("ll")); + EXPECT_EQ(defString.to_string().find("lo"), defString.find("lo")); + EXPECT_EQ(defString.to_string().find("ll", 1), defString.find("ll", 1)); + EXPECT_EQ(defString.to_string().find("ll", 6), defString.find("ll", 6)); + EXPECT_EQ(defString.to_string().find("or"), defString.find("or")); + EXPECT_EQ(defString.to_string().find("od"), defString.find("od")); +} + +TEST(StringViewTest,RFind) +{ + const APT::StringView defString("Hello World!"); + EXPECT_EQ(defString.to_string().rfind('l'), defString.rfind('l')); + EXPECT_EQ(defString.to_string().rfind('X'), defString.rfind('X')); + EXPECT_EQ(defString.to_string().rfind('e',3), defString.rfind('e',3)); + EXPECT_EQ(defString.to_string().rfind('l',6), defString.rfind('l',6)); + EXPECT_EQ(defString.to_string().rfind('l',11), defString.rfind('l',11)); +} diff --git a/test/libapt/strutil_test.cc b/test/libapt/strutil_test.cc new file mode 100644 index 0000000..469de44 --- /dev/null +++ b/test/libapt/strutil_test.cc @@ -0,0 +1,446 @@ +#include <config.h> +#include <apt-pkg/fileutl.h> +#include <apt-pkg/string_view.h> +#include <apt-pkg/strutl.h> +#include <limits> +#include <string> +#include <vector> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +TEST(StrUtilTest,DeEscapeString) +{ + // nothing special + EXPECT_EQ("", DeEscapeString("")); + EXPECT_EQ("foobar", DeEscapeString("foobar")); + // hex and octal + EXPECT_EQ("foo bar\nbaz", DeEscapeString("foo\\040bar\\x0abaz")); + EXPECT_EQ("foo ", DeEscapeString("foo\\040")); + EXPECT_EQ("\nbaz", DeEscapeString("\\x0abaz")); + EXPECT_EQ("/media/Ubuntu 11.04 amd64", DeEscapeString("/media/Ubuntu\\04011.04\\040amd64")); + // double slashes + EXPECT_EQ("foo\\ x", DeEscapeString("foo\\\\ x")); + EXPECT_EQ("\\foo\\", DeEscapeString("\\\\foo\\\\")); + + // FIXME: the input is bad, the output as well, but we have no indicator for it + EXPECT_EQ("aa", DeEscapeString("aa\\x")); + EXPECT_EQ("aa0", DeEscapeString("aa\\x0")); + EXPECT_EQ("aa", DeEscapeString("aa\\0")); + EXPECT_EQ("aaa", DeEscapeString("aa\\0a")); +} +TEST(StrUtilTest,StringStrip) +{ + EXPECT_EQ("", APT::String::Strip("")); + EXPECT_EQ("foobar", APT::String::Strip("foobar")); + EXPECT_EQ("foo bar", APT::String::Strip("foo bar")); + + EXPECT_EQ("", APT::String::Strip(" ")); + EXPECT_EQ("", APT::String::Strip(" \r\n \t ")); + + EXPECT_EQ("foo bar", APT::String::Strip("foo bar ")); + EXPECT_EQ("foo bar", APT::String::Strip("foo bar \r\n \t ")); + EXPECT_EQ("foo bar", APT::String::Strip("\r\n \t foo bar")); + EXPECT_EQ("bar foo", APT::String::Strip("\r\n \t bar foo \r\n \t ")); + EXPECT_EQ("bar \t\r\n foo", APT::String::Strip("\r\n \t bar \t\r\n foo \r\n \t ")); +} +TEST(StrUtilTest,StringSplitBasic) +{ + std::vector<std::string> result = StringSplit("", ""); + EXPECT_TRUE(result.empty()); + + result = StringSplit("abc", ""); + EXPECT_TRUE(result.empty()); + + result = StringSplit("", "abc"); + EXPECT_EQ(result.size(), 1u); + + result = StringSplit("abc", "b"); + ASSERT_EQ(result.size(), 2u); + EXPECT_EQ(result[0], "a"); + EXPECT_EQ(result[1], "c"); + + result = StringSplit("abc", "abc"); + ASSERT_EQ(result.size(), 2u); + EXPECT_EQ(result[0], ""); + EXPECT_EQ(result[1], ""); +} +TEST(StrUtilTest,StringSplitDpkgStatus) +{ + std::string const input = "status: libnet1:amd64: unpacked"; + std::vector<std::string> result = StringSplit(input, "xxx"); + ASSERT_EQ(result.size(), 1u); + EXPECT_EQ(result[0], input); + + result = StringSplit(input, ""); + EXPECT_TRUE(result.empty()); + + result = StringSplit(input, ": "); + ASSERT_EQ(result.size(), 3u); + EXPECT_EQ(result[0], "status"); + EXPECT_EQ(result[1], "libnet1:amd64"); + EXPECT_EQ(result[2], "unpacked"); + + result = StringSplit("x:y:z", ":", 2); + ASSERT_EQ(result.size(), 2u); + EXPECT_EQ(result[0], "x"); + EXPECT_EQ(result[1], "y:z"); +} +TEST(StrUtilTest,EndsWith) +{ + using APT::String::Endswith; + EXPECT_TRUE(Endswith("abcd", "d")); + EXPECT_TRUE(Endswith("abcd", "cd")); + EXPECT_TRUE(Endswith("abcd", "abcd")); + EXPECT_FALSE(Endswith("abcd", "x")); + EXPECT_FALSE(Endswith("abcd", "abcndefg")); +} +TEST(StrUtilTest,StartsWith) +{ + using APT::String::Startswith; + EXPECT_TRUE(Startswith("abcd", "a")); + EXPECT_TRUE(Startswith("abcd", "ab")); + EXPECT_TRUE(Startswith("abcd", "abcd")); + EXPECT_FALSE(Startswith("abcd", "x")); + EXPECT_FALSE(Startswith("abcd", "abcndefg")); +} +TEST(StrUtilTest,TimeToStr) +{ + EXPECT_EQ("0s", TimeToStr(0)); + EXPECT_EQ("42s", TimeToStr(42)); + EXPECT_EQ("9min 21s", TimeToStr((9*60) + 21)); + EXPECT_EQ("20min 42s", TimeToStr((20*60) + 42)); + EXPECT_EQ("10h 42min 21s", TimeToStr((10*3600) + (42*60) + 21)); + EXPECT_EQ("10h 42min 21s", TimeToStr((10*3600) + (42*60) + 21)); + EXPECT_EQ("1988d 3h 29min 7s", TimeToStr((1988*86400) + (3*3600) + (29*60) + 7)); + + EXPECT_EQ("59s", TimeToStr(59)); + EXPECT_EQ("60s", TimeToStr(60)); + EXPECT_EQ("1min 1s", TimeToStr(61)); + EXPECT_EQ("59min 59s", TimeToStr(3599)); + EXPECT_EQ("60min 0s", TimeToStr(3600)); + EXPECT_EQ("1h 0min 1s", TimeToStr(3601)); + EXPECT_EQ("1h 1min 0s", TimeToStr(3660)); + EXPECT_EQ("23h 59min 59s", TimeToStr(86399)); + EXPECT_EQ("24h 0min 0s", TimeToStr(86400)); + EXPECT_EQ("1d 0h 0min 1s", TimeToStr(86401)); + EXPECT_EQ("1d 0h 1min 0s", TimeToStr(86460)); +} +TEST(StrUtilTest,SubstVar) +{ + EXPECT_EQ("", SubstVar("", "fails", "passes")); + EXPECT_EQ("test ", SubstVar("test fails", "fails", "")); + EXPECT_EQ("test passes", SubstVar("test passes", "", "fails")); + + EXPECT_EQ("test passes", SubstVar("test passes", "fails", "passes")); + EXPECT_EQ("test passes", SubstVar("test fails", "fails", "passes")); + + EXPECT_EQ("starts with", SubstVar("beginnt with", "beginnt", "starts")); + EXPECT_EQ("beginnt with", SubstVar("starts with", "starts", "beginnt")); + EXPECT_EQ("is in middle", SubstVar("is in der middle", "in der", "in")); + EXPECT_EQ("is in der middle", SubstVar("is in middle", "in", "in der")); + EXPECT_EQ("does end", SubstVar("does enden", "enden", "end")); + EXPECT_EQ("does enden", SubstVar("does end", "end", "enden")); + + EXPECT_EQ("abc", SubstVar("abc", "d", "a")); + EXPECT_EQ("abc", SubstVar("abd", "d", "c")); + EXPECT_EQ("abc", SubstVar("adc", "d", "b")); + EXPECT_EQ("abc", SubstVar("dbc", "d", "a")); + + EXPECT_EQ("b", SubstVar("b", "aa", "a")); + EXPECT_EQ("bb", SubstVar("bb", "aa", "a")); + EXPECT_EQ("bbb", SubstVar("bbb", "aa", "a")); + + EXPECT_EQ("aa", SubstVar("aaaa", "aa", "a")); + EXPECT_EQ("aaaa", SubstVar("aa", "a", "aa")); + EXPECT_EQ("aaaa", SubstVar("aaaa", "a", "a")); + EXPECT_EQ("a a a a ", SubstVar("aaaa", "a", "a ")); + + EXPECT_EQ(" bb bb bb bb ", SubstVar(" a a a a ", "a", "bb")); + EXPECT_EQ(" bb bb bb bb ", SubstVar(" aaa aaa aaa aaa ", "aaa", "bb")); + EXPECT_EQ(" bb a bb a bb a bb ", SubstVar(" aaa a aaa a aaa a aaa ", "aaa", "bb")); + +} +TEST(StrUtilTest,Base64Encode) +{ + EXPECT_EQ("QWxhZGRpbjpvcGVuIHNlc2FtZQ==", Base64Encode("Aladdin:open sesame")); + EXPECT_EQ("cGxlYXN1cmUu", Base64Encode("pleasure.")); + EXPECT_EQ("bGVhc3VyZS4=", Base64Encode("leasure.")); + EXPECT_EQ("ZWFzdXJlLg==", Base64Encode("easure.")); + EXPECT_EQ("YXN1cmUu", Base64Encode("asure.")); + EXPECT_EQ("c3VyZS4=", Base64Encode("sure.")); + EXPECT_EQ("dXJlLg==", Base64Encode("ure.")); + EXPECT_EQ("cmUu", Base64Encode("re.")); + EXPECT_EQ("ZS4=", Base64Encode("e.")); + EXPECT_EQ("Lg==", Base64Encode(".")); + EXPECT_EQ("", Base64Encode("")); + EXPECT_EQ("IA==", Base64Encode("\x20")); + EXPECT_EQ("/w==", Base64Encode("\xff")); + EXPECT_EQ("/A==", Base64Encode("\xfc")); + EXPECT_EQ("//8=", Base64Encode("\xff\xff")); +} +static void ReadMessagesTestWithNewLine(char const * const nl, char const * const ab) +{ + SCOPED_TRACE(SubstVar(SubstVar(nl, "\n", "n"), "\r", "r") + " # " + ab); + FileFd fd; + std::string pkgA = "Package: pkgA\n" + "Version: 1\n" + "Size: 100\n" + "Description: aaa\n" + " aaa"; + std::string pkgB = "Package: pkgB\n" + "Version: 1\n" + "Flag: no\n" + "Description: bbb"; + std::string pkgC = "Package: pkgC\n" + "Version: 2\n" + "Flag: yes\n" + "Description:\n" + " ccc"; + + openTemporaryFile("readmessage", fd, (pkgA + nl + pkgB + nl + pkgC + nl).c_str()); + std::vector<std::string> list; + EXPECT_TRUE(ReadMessages(fd.Fd(), list)); + EXPECT_EQ(3u, list.size()); + EXPECT_EQ(pkgA, list[0]); + EXPECT_EQ(pkgB, list[1]); + EXPECT_EQ(pkgC, list[2]); + + size_t const msgsize = 63990; + openTemporaryFile("readmessage", fd); + for (size_t j = 0; j < msgsize; ++j) + fd.Write(ab, strlen(ab)); + for (size_t i = 0; i < 21; ++i) + { + std::string msg; + strprintf(msg, "msgsize=%zu i=%zu", msgsize, i); + SCOPED_TRACE(msg); + fd.Seek((msgsize + (i - 1)) * strlen(ab)); + fd.Write(ab, strlen(ab)); + fd.Write(nl, strlen(nl)); + fd.Seek(0); + list.clear(); + EXPECT_TRUE(ReadMessages(fd.Fd(), list)); + EXPECT_EQ(1u, list.size()); + EXPECT_EQ((msgsize + i) * strlen(ab), list[0].length()); + EXPECT_EQ(std::string::npos, list[0].find_first_not_of(ab)); + } + + list.clear(); + fd.Write(pkgA.c_str(), pkgA.length()); + fd.Write(nl, strlen(nl)); + fd.Seek(0); + EXPECT_TRUE(ReadMessages(fd.Fd(), list)); + EXPECT_EQ(2u, list.size()); + EXPECT_EQ((msgsize + 20) * strlen(ab), list[0].length()); + EXPECT_EQ(std::string::npos, list[0].find_first_not_of(ab)); + EXPECT_EQ(pkgA, list[1]); + + + fd.Close(); +} +TEST(StrUtilTest,ReadMessages) +{ + ReadMessagesTestWithNewLine("\n\n", "a"); + ReadMessagesTestWithNewLine("\r\n\r\n", "a"); + ReadMessagesTestWithNewLine("\n\n", "ab"); + ReadMessagesTestWithNewLine("\r\n\r\n", "ab"); +} +TEST(StrUtilTest,QuoteString) +{ + EXPECT_EQ("", QuoteString("", "")); + EXPECT_EQ("K%c3%b6ln", QuoteString("Köln", "")); + EXPECT_EQ("Köln", DeQuoteString(QuoteString("Köln", ""))); + EXPECT_EQ("Köln", DeQuoteString(DeQuoteString(QuoteString(QuoteString("Köln", ""), "")))); + EXPECT_EQ("~-_$#|u%c3%a4%c3%b6%c5%a6%e2%84%a2%e2%85%9e%c2%b1%c3%86%e1%ba%9e%c2%aa%c3%9f", QuoteString("~-_$#|uäöŦ™⅞±Æẞªß", "")); + EXPECT_EQ("~-_$#|uäöŦ™⅞±Æẞªß", DeQuoteString(QuoteString("~-_$#|uäöŦ™⅞±Æẞªß", ""))); + EXPECT_EQ("%45ltvill%65%2d%45rbach", QuoteString("Eltville-Erbach", "E-Ae")); + EXPECT_EQ("Eltville-Erbach", DeQuoteString(QuoteString("Eltville-Erbach", ""))); +} + +static void EXPECT_STRTONUM(APT::StringView const str, bool const success, unsigned long const expected, unsigned const base) +{ + SCOPED_TRACE(std::string(str.data(), str.length())); + SCOPED_TRACE(base); + unsigned long N1 = 1000; + unsigned long long N2 = 1000; + if (not success) + { + EXPECT_FALSE(StrToNum(str.data(), N1, str.length(), base)); + EXPECT_FALSE(StrToNum(str.data(), N2, str.length(), base)); + return; + } + EXPECT_TRUE(StrToNum(str.data(), N1, str.length(), base)); + EXPECT_EQ(expected, N1); + + EXPECT_TRUE(StrToNum(str.data(), N2, str.length(), base)); + EXPECT_EQ(expected, N2); +} +TEST(StrUtilTest,StrToNum) +{ + EXPECT_STRTONUM("", true, 0, 10); + EXPECT_STRTONUM(" ", true, 0, 10); + EXPECT_STRTONUM("0", true, 0, 10); + EXPECT_STRTONUM("1", true, 1, 10); + EXPECT_STRTONUM(" 1 ", true, 1, 10); + EXPECT_STRTONUM("1", true, 1, 8); + EXPECT_STRTONUM("10", true, 10, 10); + EXPECT_STRTONUM("10", true, 8, 8); + EXPECT_STRTONUM("010", true, 8, 8); + EXPECT_STRTONUM(" 010 ", true, 8, 8); + EXPECT_STRTONUM("-1", false, 0, 10); + EXPECT_STRTONUM(" -1 ", false, 0, 10); + EXPECT_STRTONUM("11", true, 3, 2); + + unsigned long long bigN = 0; + unsigned long smallN = 0; + auto bigLimit = std::to_string(std::numeric_limits<unsigned long long>::max()); + if (std::numeric_limits<unsigned long>::max() < std::numeric_limits<unsigned long long>::max()) + { + EXPECT_TRUE(StrToNum(bigLimit.c_str(), bigN, bigLimit.length(), 10)); + EXPECT_EQ(std::numeric_limits<unsigned long long>::max(), bigN); + EXPECT_FALSE(StrToNum(bigLimit.c_str(), smallN, bigLimit.length(), 10)); + } + bigLimit.append("0"); + EXPECT_FALSE(StrToNum(bigLimit.c_str(), bigN, bigLimit.length(), 10)); + EXPECT_FALSE(StrToNum(bigLimit.c_str(), smallN, bigLimit.length(), 10)); + + auto const smallLimit = std::to_string(std::numeric_limits<unsigned long>::max()); + EXPECT_TRUE(StrToNum(smallLimit.c_str(), bigN, smallLimit.length(), 10)); + EXPECT_EQ(std::numeric_limits<unsigned long>::max(), bigN); + EXPECT_TRUE(StrToNum(smallLimit.c_str(), smallN, smallLimit.length(), 10)); + EXPECT_EQ(std::numeric_limits<unsigned long>::max(), smallN); +} + +TEST(StrUtilTest,RFC1123StrToTime) +{ + { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sun, 06 Nov 1994 08:49:37 GMT", t)); + EXPECT_EQ(784111777, t); + } { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sun, 6 Nov 1994 08:49:37 UTC", t)); + EXPECT_EQ(784111777, t); + } { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sun, 6 Nov 1994 08:49:37 UTC", t)); + EXPECT_EQ(784111777, t); + } { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sun, 06 Nov 1994 8:49:37 UTC", t)); + EXPECT_EQ(784111777, t); + } { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sun, 06 Nov 1994 08:49:37 UTC", t)); + EXPECT_EQ(784111777, t); + } { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sun, 06 Nov 1994 08:49:37 -0000", t)); + EXPECT_EQ(784111777, t); + } { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sun, 06 Nov 1994 08:49:37 +0000", t)); + EXPECT_EQ(784111777, t); + } { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sunday, 06-Nov-94 08:49:37 GMT", t)); + EXPECT_EQ(784111777, t); + } { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sunday, 6-Nov-94 08:49:37 GMT", t)); + EXPECT_EQ(784111777, t); + } { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sunday, 06-Nov-94 8:49:37 GMT", t)); + EXPECT_EQ(784111777, t); + } { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sun Nov 6 08:49:37 1994", t)); + EXPECT_EQ(784111777, t); + } { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sun Nov 06 08:49:37 1994", t)); + EXPECT_EQ(784111777, t); + } { + time_t t; + EXPECT_TRUE(RFC1123StrToTime("Sun Nov 6 8:49:37 1994", t)); + EXPECT_EQ(784111777, t); + } + time_t t; + EXPECT_FALSE(RFC1123StrToTime("So, 06 Nov 1994 08:49:37 UTC", t)); + EXPECT_FALSE(RFC1123StrToTime(", 06 Nov 1994 08:49:37 UTC", t)); + EXPECT_FALSE(RFC1123StrToTime("Son, 06 Nov 1994 08:49:37 UTC", t)); + EXPECT_FALSE(RFC1123StrToTime("Sun: 06 Nov 1994 08:49:37 UTC", t)); + EXPECT_FALSE(RFC1123StrToTime("Sun, 06 Nov 1994 08:49:37", t)); + EXPECT_FALSE(RFC1123StrToTime("Sun, 06 Nov 1994 08:49:37 GMT+1", t)); + EXPECT_FALSE(RFC1123StrToTime("Sun, 06 Nov 1994 GMT", t)); + EXPECT_FALSE(RFC1123StrToTime("Sunday, 06 Nov 1994 GMT", t)); + EXPECT_FALSE(RFC1123StrToTime("Sonntag, 06 Nov 1994 08:49:37 GMT", t)); + EXPECT_FALSE(RFC1123StrToTime("domingo Nov 6 08:49:37 1994", t)); + EXPECT_FALSE(RFC1123StrToTime("Sunday: 06-Nov-94 08:49:37 GMT", t)); + EXPECT_FALSE(RFC1123StrToTime("Sunday, 06-Nov-94 08:49:37 GMT+1", t)); + EXPECT_FALSE(RFC1123StrToTime("Sunday, 06-Nov-94 08:49:37 EDT", t)); + EXPECT_FALSE(RFC1123StrToTime("Sunday, 06-Nov-94 08:49:37 -0100", t)); + EXPECT_FALSE(RFC1123StrToTime("Sunday, 06-Nov-94 08:49:37 -0.1", t)); +} +TEST(StrUtilTest, LookupTag) +{ + EXPECT_EQ("", LookupTag("", "Field", "")); + EXPECT_EQ("", LookupTag("", "Field", nullptr)); + EXPECT_EQ("default", LookupTag("", "Field", "default")); + EXPECT_EQ("default", LookupTag("Field1: yes", "Field", "default")); + EXPECT_EQ("default", LookupTag("Fiel: yes", "Field", "default")); + EXPECT_EQ("default", LookupTag("Fiel d: yes", "Field", "default")); + EXPECT_EQ("foo", LookupTag("Field: foo", "Field", "default")); + EXPECT_EQ("foo", LookupTag("Field: foo\n", "Field", "default")); + EXPECT_EQ("foo", LookupTag("\nField: foo\n", "Field", "default")); + EXPECT_EQ("foo", LookupTag("Field:foo", "Field", "default")); + EXPECT_EQ("foo", LookupTag("Field:foo\n", "Field", "default")); + EXPECT_EQ("foo", LookupTag("\nField:foo\n", "Field", "default")); + EXPECT_EQ("foo", LookupTag("Field:\tfoo\n", "Field", "default")); + EXPECT_EQ("foo", LookupTag("Field: foo \t", "Field", "default")); + EXPECT_EQ("foo", LookupTag("Field: foo \t\n", "Field", "default")); + EXPECT_EQ("Field : yes", LookupTag("Field: Field : yes \t\n", "Field", "default")); + EXPECT_EQ("Field : yes", LookupTag("Field:\n Field : yes \t\n", "Field", "default")); + EXPECT_EQ("Field : yes", LookupTag("Foo: bar\nField: Field : yes \t\n", "Field", "default")); + EXPECT_EQ("line1\nline2", LookupTag("Multi: line1\n line2", "Multi", "default")); + EXPECT_EQ("line1\nline2", LookupTag("Multi: line1\n line2\n", "Multi", "default")); + EXPECT_EQ("line1\nline2", LookupTag("Multi:\n line1\n line2\n", "Multi", "default")); + EXPECT_EQ("line1\n\nline2", LookupTag("Multi:\n line1\n .\n line2\n", "Multi", "default")); + EXPECT_EQ("line1\na\nline2", LookupTag("Multi:\n line1\n a\n line2\n", "Multi", "default")); + EXPECT_EQ("line1\nfoo\nline2", LookupTag("Multi:\n line1\n foo\n line2\n", "Multi", "default")); + EXPECT_EQ("line1\n line2", LookupTag("Multi: line1\n line2", "Multi", "default")); + EXPECT_EQ(" line1\n \t line2", LookupTag("Multi:\t \n line1\n \t line2\n", "Multi", "default")); + EXPECT_EQ(" line1\n\n\n \t line2", LookupTag("Multi:\t \n line1\n .\n . \n \t line2\n", "Multi", "default")); + + std::string const msg = + "Field1: Value1\nField2:Value2\nField3:\t Value3\n" + "Multi-Field1: Line1\n Line2\nMulti-Field2:\n Line1\n Line2\n" + "Field4: Value4\nField5:Value5"; + EXPECT_EQ("Value1", LookupTag(msg, "Field1", "")); + EXPECT_EQ("Value2", LookupTag(msg, "Field2", "")); + EXPECT_EQ("Value3", LookupTag(msg, "Field3", "")); + EXPECT_EQ("Line1\nLine2", LookupTag(msg, "Multi-Field1", "")); + EXPECT_EQ("Line1\nLine2", LookupTag(msg, "Multi-Field2", "")); + EXPECT_EQ("Value4", LookupTag(msg, "Field4", "")); + EXPECT_EQ("Value5", LookupTag(msg, "Field5", "")); +} + +TEST(StrUtilTest, DisplayLength) +{ + EXPECT_EQ(0, APT::String::DisplayLength("")); + EXPECT_EQ(1, APT::String::DisplayLength("a")); + EXPECT_EQ(3, APT::String::DisplayLength("apt")); + EXPECT_EQ(1, APT::String::DisplayLength("@")); + EXPECT_EQ(3, APT::String::DisplayLength("き")); + + EXPECT_EQ(1, APT::String::DisplayLength("$")); + EXPECT_EQ(2, APT::String::DisplayLength("¢")); + EXPECT_EQ(3, APT::String::DisplayLength("ह")); + EXPECT_EQ(3, APT::String::DisplayLength("€")); + EXPECT_EQ(3, APT::String::DisplayLength("한")); + EXPECT_EQ(4, APT::String::DisplayLength("𐍈")); + EXPECT_EQ(16, APT::String::DisplayLength("𐍈한€ह¢$")); +} diff --git a/test/libapt/tagfile_test.cc b/test/libapt/tagfile_test.cc new file mode 100644 index 0000000..06ea01c --- /dev/null +++ b/test/libapt/tagfile_test.cc @@ -0,0 +1,345 @@ +#include <config.h> + +#include <apt-pkg/fileutl.h> +#include <apt-pkg/tagfile.h> + +#include <sstream> +#include <string> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +TEST(TagFileTest,SingleField) +{ + FileFd fd; + openTemporaryFile("singlefield", fd, "FieldA-12345678: the value of the field"); + + pkgTagFile tfile(&fd); + pkgTagSection section; + ASSERT_TRUE(tfile.Step(section)); + + // It has one field + EXPECT_EQ(1u, section.Count()); + // ... and it is called FieldA-12345678 + EXPECT_TRUE(section.Exists("FieldA-12345678")); + // its value is correct + EXPECT_EQ("the value of the field", section.FindS("FieldA-12345678")); + // A non-existent field has an empty string as value + EXPECT_EQ("", section.FindS("FieldB-12345678")); + // ... and Exists does not lie about missing fields... + EXPECT_FALSE(section.Exists("FieldB-12345678")); + // There is only one section in this tag file + EXPECT_FALSE(tfile.Step(section)); + + // Now we scan an empty section to test reset + ASSERT_TRUE(section.Scan("\n\n", 2, true)); + EXPECT_EQ(0u, section.Count()); + EXPECT_FALSE(section.Exists("FieldA-12345678")); + EXPECT_FALSE(section.Exists("FieldB-12345678")); + + openTemporaryFile("emptyfile", fd); + ASSERT_FALSE(tfile.Step(section)); + EXPECT_EQ(0u, section.Count()); +} + +TEST(TagFileTest,MultipleSections) +{ + FileFd fd; + openTemporaryFile("bigsection", fd, "Package: pkgA\n" + "Version: 1\n" + "Size: 100\n" + "Description: aaa\n" + " aaa\n" + "\n" + "Package: pkgB\n" + "Version: 1\n" + "Flag: no\n" + "Description: bbb\n" + "\n" + "Package: pkgC\n" + "Version: 2\n" + "Flag: yes\n" + "Description:\n" + " ccc\n" + ); + + pkgTagFile tfile(&fd); + pkgTagSection section; + EXPECT_FALSE(section.Exists("Version")); + + EXPECT_TRUE(tfile.Step(section)); + EXPECT_EQ(4u, section.Count()); + EXPECT_TRUE(section.Exists("Version")); + EXPECT_TRUE(section.Exists("Package")); + EXPECT_TRUE(section.Exists("Size")); + EXPECT_FALSE(section.Exists("Flag")); + EXPECT_TRUE(section.Exists("Description")); + EXPECT_EQ("pkgA", section.FindS("Package")); + EXPECT_EQ("1", section.FindS("Version")); + EXPECT_EQ(1u, section.FindULL("Version")); + EXPECT_EQ(100u, section.FindULL("Size")); + unsigned long Flags = 1; + EXPECT_TRUE(section.FindFlag("Flag", Flags, 1)); + EXPECT_EQ(1u, Flags); + Flags = 0; + EXPECT_TRUE(section.FindFlag("Flag", Flags, 1)); + EXPECT_EQ(0u, Flags); + EXPECT_EQ("aaa\n aaa", section.FindS("Description")); + + + EXPECT_TRUE(tfile.Step(section)); + EXPECT_EQ(4u, section.Count()); + EXPECT_TRUE(section.Exists("Version")); + EXPECT_TRUE(section.Exists("Package")); + EXPECT_FALSE(section.Exists("Size")); + EXPECT_TRUE(section.Exists("Flag")); + EXPECT_TRUE(section.Exists("Description")); + EXPECT_EQ("pkgB", section.FindS("Package")); + EXPECT_EQ("1", section.FindS("Version")); + EXPECT_EQ(1u, section.FindULL("Version")); + EXPECT_EQ(0u, section.FindULL("Size")); + Flags = 1; + EXPECT_TRUE(section.FindFlag("Flag", Flags, 1)); + EXPECT_EQ(0u, Flags); + Flags = 0; + EXPECT_TRUE(section.FindFlag("Flag", Flags, 1)); + EXPECT_EQ(0u, Flags); + EXPECT_EQ("bbb", section.FindS("Description")); + + EXPECT_TRUE(tfile.Step(section)); + EXPECT_EQ(4u, section.Count()); + EXPECT_TRUE(section.Exists("Version")); + EXPECT_TRUE(section.Exists("Package")); + EXPECT_FALSE(section.Exists("Size")); + EXPECT_TRUE(section.Exists("Flag")); + EXPECT_TRUE(section.Exists("Description")); + EXPECT_EQ("pkgC", section.FindS("Package")); + EXPECT_EQ("2", section.FindS("Version")); + EXPECT_EQ(2u, section.FindULL("Version")); + Flags = 0; + EXPECT_TRUE(section.FindFlag("Flag", Flags, 1)); + EXPECT_EQ(1u, Flags); + Flags = 1; + EXPECT_TRUE(section.FindFlag("Flag", Flags, 1)); + EXPECT_EQ(1u, Flags); + EXPECT_EQ("ccc", section.FindS("Description")); + + // There is no section left in this tag file + EXPECT_FALSE(tfile.Step(section)); +} + +TEST(TagFileTest,BigSection) +{ + size_t const count = 500; + std::stringstream content; + for (size_t i = 0; i < count; ++i) + content << "Field-" << i << ": " << (2000 + i) << std::endl; + + FileFd fd; + openTemporaryFile("bigsection", fd, content.str().c_str()); + + pkgTagFile tfile(&fd); + pkgTagSection section; + EXPECT_TRUE(tfile.Step(section)); + + EXPECT_EQ(count, section.Count()); + for (size_t i = 0; i < count; ++i) + { + std::stringstream name; + name << "Field-" << i; + EXPECT_TRUE(section.Exists(name.str().c_str())) << name.str() << " does not exist"; + EXPECT_EQ((i + 2000), section.FindULL(name.str().c_str())); + } + + // There is only one section in this tag file + EXPECT_FALSE(tfile.Step(section)); +} + +TEST(TagFileTest, PickedUpFromPreviousCall) +{ + size_t const count = 500; + std::stringstream contentstream; + for (size_t i = 0; i < count; ++i) + contentstream << "Field-" << i << ": " << (2000 + i) << std::endl; + contentstream << std::endl << std::endl; + std::string content = contentstream.str(); + + pkgTagSection section; + EXPECT_FALSE(section.Scan(content.c_str(), content.size()/2)); + EXPECT_NE(0u, section.Count()); + EXPECT_NE(count, section.Count()); + EXPECT_TRUE(section.Scan(content.c_str(), content.size(), false)); + EXPECT_EQ(count, section.Count()); + + for (size_t i = 0; i < count; ++i) + { + std::stringstream name; + name << "Field-" << i; + EXPECT_TRUE(section.Exists(name.str().c_str())) << name.str() << " does not exist"; + EXPECT_EQ((i + 2000), section.FindULL(name.str().c_str())); + } +} + +TEST(TagFileTest, SpacesEverywhere) +{ + std::string content = + "Package: pkgA\n" + "Package: pkgB\n" + "NoSpaces:yes\n" + "NoValue:\n" + "TagSpaces\t :yes\n" + "ValueSpaces: \tyes\n" + "BothSpaces \t:\t yes\n" + "TrailingSpaces: yes\t \n" + "Naming Space: yes\n" + "Naming Spaces: yes\n" + "Package : pkgC \n" + "Multi-Colon::yes:\n" + "\n\n"; + + pkgTagSection section; + EXPECT_TRUE(section.Scan(content.c_str(), content.size())); + EXPECT_TRUE(section.Exists("Package")); + EXPECT_TRUE(section.Exists("NoSpaces")); + EXPECT_TRUE(section.Exists("NoValue")); + EXPECT_TRUE(section.Exists("TagSpaces")); + EXPECT_TRUE(section.Exists("ValueSpaces")); + EXPECT_TRUE(section.Exists("BothSpaces")); + EXPECT_TRUE(section.Exists("TrailingSpaces")); + EXPECT_TRUE(section.Exists("Naming Space")); + EXPECT_TRUE(section.Exists("Naming Spaces")); + EXPECT_TRUE(section.Exists("Multi-Colon")); + EXPECT_EQ("pkgC", section.FindS("Package")); + EXPECT_EQ("yes", section.FindS("NoSpaces")); + EXPECT_EQ("", section.FindS("NoValue")); + EXPECT_EQ("yes", section.FindS("TagSpaces")); + EXPECT_EQ("yes", section.FindS("ValueSpaces")); + EXPECT_EQ("yes", section.FindS("BothSpaces")); + EXPECT_EQ("yes", section.FindS("TrailingSpaces")); + EXPECT_EQ("yes", section.FindS("Naming Space")); + EXPECT_EQ("yes", section.FindS("Naming Spaces")); + EXPECT_EQ(":yes:", section.FindS("Multi-Colon")); + // overridden values are still present, but not really accessible + EXPECT_EQ(12u, section.Count()); +} + +TEST(TagFileTest, Comments) +{ + FileFd fd; + openTemporaryFile("commentfile", fd, "# Leading comments should be ignored.\n" +"\n" +"# A wild second comment appears!\n" +"\n" +"Source: foo\n" +"#Package: foo\n" +"Section: bar\n" +"#Section: overridden\n" +"Priority: optional\n" +"Build-Depends: debhelper,\n" +"# apt-utils, (temporarily disabled)\n" +" apt\n" +"\n" +"# Comments in the middle shouldn't result in extra blank paragraphs either.\n" +"\n" +"# Ditto.\n" +"\n" +"# A comment at the top of a paragraph should be ignored.\n" +"Package: foo\n" +"Architecture: any\n" +"Description: An awesome package\n" +" # This should still appear in the result.\n" +"# this one shouldn't\n" +" Blah, blah, blah. # but this again.\n" +"# A comment at the end of a paragraph should be ignored.\n" +"\n" +"# Trailing comments shouldn't cause extra blank paragraphs." + ); + + pkgTagFile tfile(&fd, pkgTagFile::SUPPORT_COMMENTS, 1); + pkgTagSection section; + EXPECT_TRUE(tfile.Step(section)); + EXPECT_FALSE(section.Exists("Package")); + EXPECT_TRUE(section.Exists("Source")); + EXPECT_EQ("foo", section.FindS("Source")); + EXPECT_TRUE(section.Exists("Section")); + EXPECT_EQ("bar", section.FindS("Section")); + EXPECT_TRUE(section.Exists("Priority")); + EXPECT_EQ("optional", section.FindS("Priority")); + EXPECT_TRUE(section.Exists("Build-Depends")); + EXPECT_EQ("debhelper,\n apt", section.FindS("Build-Depends")); + + EXPECT_TRUE(tfile.Step(section)); + EXPECT_FALSE(section.Exists("Source")); + EXPECT_TRUE(section.Exists("Package")); + EXPECT_EQ("foo", section.FindS("Package")); + EXPECT_FALSE(section.Exists("Section")); + EXPECT_TRUE(section.Exists("Architecture")); + EXPECT_EQ("any", section.FindS("Architecture")); + EXPECT_FALSE(section.Exists("Build-Depends")); + EXPECT_TRUE(section.Exists("Description")); + EXPECT_EQ("An awesome package\n # This should still appear in the result.\n Blah, blah, blah. # but this again.", section.FindS("Description")); + + EXPECT_FALSE(tfile.Step(section)); +} + +TEST(TagFileTest, EmptyTagName) +{ + FileFd fd; + openTemporaryFile("emptytagname", fd, "0:\n" +"PACKAGE:0\n" +"\n" +":\n" +"PACKAGE:\n" +"\n" +"PACKAGE:\n" +":\n" +"\n" +"PACKAGE:\n" +":\n" +"Version:1\n" +"\n" +"PACKAGE::\n" + ); + pkgTagFile tfile(&fd); + pkgTagSection section; + ASSERT_TRUE(tfile.Step(section)); + EXPECT_EQ(2u, section.Count()); + EXPECT_TRUE(section.Exists("PACKAGE")); + EXPECT_EQ("0", section.FindS("PACKAGE")); + EXPECT_TRUE(section.Exists("0")); + EXPECT_EQ("", section.FindS("0")); + + ASSERT_TRUE(tfile.Step(section)); + EXPECT_EQ(2u, section.Count()); + EXPECT_TRUE(section.Exists("PACKAGE")); + EXPECT_EQ("", section.FindS("PACKAGE")); + EXPECT_TRUE(section.Exists("")); + EXPECT_EQ("", section.FindS("")); + + ASSERT_TRUE(tfile.Step(section)); + EXPECT_EQ(2u, section.Count()); + EXPECT_TRUE(section.Exists("PACKAGE")); + EXPECT_EQ("", section.FindS("PACKAGE")); + EXPECT_TRUE(section.Exists("")); + EXPECT_EQ("", section.FindS("")); + + ASSERT_TRUE(tfile.Step(section)); + EXPECT_EQ(3u, section.Count()); + EXPECT_TRUE(section.Exists("PACKAGE")); + EXPECT_EQ("", section.FindS("PACKAGE")); + EXPECT_TRUE(section.Exists("")); + EXPECT_EQ("", section.FindS("")); + EXPECT_TRUE(section.Exists("Version")); + EXPECT_EQ("1", section.FindS("Version")); + + ASSERT_TRUE(tfile.Step(section)); + EXPECT_EQ(1u, section.Count()); + EXPECT_TRUE(section.Exists("PACKAGE")); + EXPECT_EQ(":", section.FindS("PACKAGE")); + + EXPECT_FALSE(tfile.Step(section)); +} diff --git a/test/libapt/tagsection_test.cc b/test/libapt/tagsection_test.cc new file mode 100644 index 0000000..80cecca --- /dev/null +++ b/test/libapt/tagsection_test.cc @@ -0,0 +1,232 @@ +#include <config.h> + +#include <apt-pkg/fileutl.h> +#include <apt-pkg/tagfile.h> + +#include <sstream> +#include <string> + +#include <gtest/gtest.h> + +#include "file-helpers.h" + +std::string packageValue = "aaaa"; +std::string typoValue = "aa\n" + " .\n" + " cc"; +std::string typoRawValue = "\n " + typoValue; +std::string overrideValue = "1"; + +static void EXPECT_SECTION_WITH_ALL_CONTENT(pkgTagSection const §ion) +{ + EXPECT_TRUE(section.Exists("Package")); + EXPECT_TRUE(section.Exists("TypoA")); + EXPECT_TRUE(section.Exists("Override")); + EXPECT_TRUE(section.Exists("Override-Backup")); + EXPECT_FALSE(section.Exists("TypoB")); + EXPECT_EQ(packageValue, section.FindS("Package")); + EXPECT_EQ(typoValue, section.FindS("TypoA")); + EXPECT_EQ(typoRawValue, section.FindRawS("TypoA")); + EXPECT_EQ(1, section.FindI("Override")); + EXPECT_EQ(1, section.FindI("Override-Backup")); + EXPECT_EQ(4u, section.Count()); +} + +static void setupTestcaseStart(FileFd &fd, pkgTagSection §ion, std::string &content) +{ + openTemporaryFile("writesection", fd); + content = "Package: " + packageValue + "\n" + "TypoA:\n " + typoValue + "\n" + "Override: " + overrideValue + "\n" + "Override-Backup: " + overrideValue + "\n" + "\n"; + EXPECT_TRUE(section.Scan(content.c_str(), content.length(), true)); + EXPECT_SECTION_WITH_ALL_CONTENT(section); +} +TEST(TagSectionTest,WriteUnmodified) +{ + FileFd fd; + pkgTagSection section; + std::string content; + setupTestcaseStart(fd, section, content); + EXPECT_TRUE(section.Write(fd)); + EXPECT_TRUE(fd.Seek(0)); + pkgTagFile tfile(&fd); + ASSERT_TRUE(tfile.Step(section)); + EXPECT_SECTION_WITH_ALL_CONTENT(section); +} +TEST(TagSectionTest,WriteUnmodifiedOrder) +{ + FileFd fd; + pkgTagSection section; + std::string content; + setupTestcaseStart(fd, section, content); + char const * const order[] = { "Package", "TypoA", "Override", NULL }; + EXPECT_TRUE(section.Write(fd, order)); + EXPECT_TRUE(fd.Seek(0)); + pkgTagFile tfile(&fd); + ASSERT_TRUE(tfile.Step(section)); + EXPECT_SECTION_WITH_ALL_CONTENT(section); +} +TEST(TagSectionTest,WriteUnmodifiedOrderReversed) +{ + FileFd fd; + pkgTagSection section; + std::string content; + setupTestcaseStart(fd, section, content); + char const * const order[] = { "Override", "TypoA", "Package", NULL }; + EXPECT_TRUE(section.Write(fd, order)); + EXPECT_TRUE(fd.Seek(0)); + pkgTagFile tfile(&fd); + ASSERT_TRUE(tfile.Step(section)); + EXPECT_SECTION_WITH_ALL_CONTENT(section); +} +TEST(TagSectionTest,WriteUnmodifiedOrderNotAll) +{ + FileFd fd; + pkgTagSection section; + std::string content; + setupTestcaseStart(fd, section, content); + char const * const order[] = { "Override", NULL }; + EXPECT_TRUE(section.Write(fd, order)); + EXPECT_TRUE(fd.Seek(0)); + pkgTagFile tfile(&fd); + ASSERT_TRUE(tfile.Step(section)); + EXPECT_SECTION_WITH_ALL_CONTENT(section); +} +TEST(TagSectionTest,WriteNoOrderRename) +{ + FileFd fd; + pkgTagSection section; + std::string content; + setupTestcaseStart(fd, section, content); + std::vector<pkgTagSection::Tag> rewrite; + rewrite.push_back(pkgTagSection::Tag::Rename("TypoA", "TypoB")); + EXPECT_TRUE(section.Write(fd, NULL, rewrite)); + EXPECT_TRUE(fd.Seek(0)); + pkgTagFile tfile(&fd); + ASSERT_TRUE(tfile.Step(section)); + EXPECT_TRUE(section.Exists("Package")); + EXPECT_FALSE(section.Exists("TypoA")); + EXPECT_TRUE(section.Exists("TypoB")); + EXPECT_TRUE(section.Exists("Override")); + EXPECT_TRUE(section.Exists("Override-Backup")); + EXPECT_EQ(packageValue, section.FindS("Package")); + EXPECT_EQ(typoValue, section.FindS("TypoB")); + EXPECT_EQ(1, section.FindI("Override")); + EXPECT_EQ(1, section.FindI("Override-Backup")); + EXPECT_EQ(4u, section.Count()); +} +TEST(TagSectionTest,WriteNoOrderRemove) +{ + FileFd fd; + pkgTagSection section; + std::string content; + setupTestcaseStart(fd, section, content); + std::vector<pkgTagSection::Tag> rewrite; + rewrite.push_back(pkgTagSection::Tag::Remove("TypoA")); + rewrite.push_back(pkgTagSection::Tag::Rewrite("Override", "")); + EXPECT_TRUE(section.Write(fd, NULL, rewrite)); + EXPECT_TRUE(fd.Seek(0)); + pkgTagFile tfile(&fd); + ASSERT_TRUE(tfile.Step(section)); + EXPECT_TRUE(section.Exists("Package")); + EXPECT_FALSE(section.Exists("TypoA")); + EXPECT_FALSE(section.Exists("TypoB")); + EXPECT_FALSE(section.Exists("Override")); + EXPECT_TRUE(section.Exists("Override-Backup")); + EXPECT_EQ(packageValue, section.FindS("Package")); + EXPECT_EQ(2u, section.Count()); +} +TEST(TagSectionTest,WriteNoOrderRewrite) +{ + FileFd fd; + pkgTagSection section; + std::string content; + setupTestcaseStart(fd, section, content); + std::vector<pkgTagSection::Tag> rewrite; + rewrite.push_back(pkgTagSection::Tag::Rewrite("Override", "42")); + EXPECT_TRUE(section.Write(fd, NULL, rewrite)); + EXPECT_TRUE(fd.Seek(0)); + pkgTagFile tfile(&fd); + ASSERT_TRUE(tfile.Step(section)); + EXPECT_TRUE(section.Exists("Package")); + EXPECT_TRUE(section.Exists("TypoA")); + EXPECT_FALSE(section.Exists("TypoB")); + EXPECT_TRUE(section.Exists("Override")); + EXPECT_TRUE(section.Exists("Override-Backup")); + EXPECT_EQ(packageValue, section.FindS("Package")); + EXPECT_EQ(42, section.FindI("Override")); + EXPECT_EQ(1, section.FindI("Override-Backup")); + EXPECT_EQ(4u, section.Count()); +} +TEST(TagSectionTest,WriteOrderRename) +{ + FileFd fd; + pkgTagSection section; + std::string content; + setupTestcaseStart(fd, section, content); + std::vector<pkgTagSection::Tag> rewrite; + rewrite.push_back(pkgTagSection::Tag::Rename("TypoA", "TypoB")); + char const * const order[] = { "Package", "TypoA", "Override", NULL }; + EXPECT_TRUE(section.Write(fd, order, rewrite)); + EXPECT_TRUE(fd.Seek(0)); + pkgTagFile tfile(&fd); + ASSERT_TRUE(tfile.Step(section)); + EXPECT_TRUE(section.Exists("Package")); + EXPECT_FALSE(section.Exists("TypoA")); + EXPECT_TRUE(section.Exists("TypoB")); + EXPECT_TRUE(section.Exists("Override")); + EXPECT_TRUE(section.Exists("Override-Backup")); + EXPECT_EQ(packageValue, section.FindS("Package")); + EXPECT_EQ(typoValue, section.FindS("TypoB")); + EXPECT_EQ(1, section.FindI("Override")); + EXPECT_EQ(1, section.FindI("Override-Backup")); + EXPECT_EQ(4u, section.Count()); +} +TEST(TagSectionTest,WriteOrderRemove) +{ + FileFd fd; + pkgTagSection section; + std::string content; + setupTestcaseStart(fd, section, content); + std::vector<pkgTagSection::Tag> rewrite; + rewrite.push_back(pkgTagSection::Tag::Remove("TypoA")); + rewrite.push_back(pkgTagSection::Tag::Rewrite("Override", "")); + char const * const order[] = { "Package", "TypoA", "Override", NULL }; + EXPECT_TRUE(section.Write(fd, order, rewrite)); + EXPECT_TRUE(fd.Seek(0)); + pkgTagFile tfile(&fd); + ASSERT_TRUE(tfile.Step(section)); + EXPECT_TRUE(section.Exists("Package")); + EXPECT_FALSE(section.Exists("TypoA")); + EXPECT_FALSE(section.Exists("TypoB")); + EXPECT_FALSE(section.Exists("Override")); + EXPECT_TRUE(section.Exists("Override-Backup")); + EXPECT_EQ(packageValue, section.FindS("Package")); + EXPECT_EQ(1, section.FindI("Override-Backup")); + EXPECT_EQ(2u, section.Count()); +} +TEST(TagSectionTest,WriteOrderRewrite) +{ + FileFd fd; + pkgTagSection section; + std::string content; + setupTestcaseStart(fd, section, content); + std::vector<pkgTagSection::Tag> rewrite; + rewrite.push_back(pkgTagSection::Tag::Rewrite("Override", "42")); + char const * const order[] = { "Package", "TypoA", "Override", NULL }; + EXPECT_TRUE(section.Write(fd, order, rewrite)); + EXPECT_TRUE(fd.Seek(0)); + pkgTagFile tfile(&fd); + ASSERT_TRUE(tfile.Step(section)); + EXPECT_TRUE(section.Exists("Package")); + EXPECT_TRUE(section.Exists("TypoA")); + EXPECT_FALSE(section.Exists("TypoB")); + EXPECT_TRUE(section.Exists("Override")); + EXPECT_TRUE(section.Exists("Override-Backup")); + EXPECT_EQ(packageValue, section.FindS("Package")); + EXPECT_EQ(42, section.FindI("Override")); + EXPECT_EQ(1, section.FindI("Override-Backup")); + EXPECT_EQ(4u, section.Count()); +} diff --git a/test/libapt/teestream_test.cc b/test/libapt/teestream_test.cc new file mode 100644 index 0000000..a897e08 --- /dev/null +++ b/test/libapt/teestream_test.cc @@ -0,0 +1,39 @@ +#include <config.h> + +#include "../interactive-helper/teestream.h" +#include <fstream> +#include <sstream> +#include <string> + +#include <gtest/gtest.h> + +TEST(TeeStreamTest,TwoStringSinks) +{ + std::ostringstream one, two; + basic_teeostream<char> tee(one, two); + tee << "This is the " << 1 << '.' << " Test, we expect: " << std::boolalpha << true << "\n"; + std::string okay("This is the 1. Test, we expect: true\n"); + EXPECT_EQ(okay, one.str()); + EXPECT_EQ(okay, two.str()); + EXPECT_EQ(one.str(), two.str()); +} + +TEST(TeeStreamTest,DevNullSink1) +{ + std::ostringstream one; + std::fstream two("/dev/null"); + basic_teeostream<char> tee(one, two); + tee << "This is the " << 2 << '.' << " Test, we expect: " << std::boolalpha << false << "\n"; + std::string okay("This is the 2. Test, we expect: false\n"); + EXPECT_EQ(okay, one.str()); +} + +TEST(TeeStreamTest,DevNullSink2) +{ + std::ostringstream one; + std::fstream two("/dev/null"); + basic_teeostream<char> tee(two, one); + tee << "This is the " << 3 << '.' << " Test, we expect: " << std::boolalpha << false << "\n"; + std::string okay("This is the 3. Test, we expect: false\n"); + EXPECT_EQ(okay, one.str()); +} diff --git a/test/libapt/uri_test.cc b/test/libapt/uri_test.cc new file mode 100644 index 0000000..519de49 --- /dev/null +++ b/test/libapt/uri_test.cc @@ -0,0 +1,216 @@ +#include <config.h> +#include <apt-pkg/configuration.h> +#include <apt-pkg/proxy.h> +#include <apt-pkg/strutl.h> +#include <gtest/gtest.h> +#include <string> + +TEST(URITest, BasicHTTP) +{ + URI U("http://www.debian.org:90/temp/test"); + EXPECT_EQ("http", U.Access); + EXPECT_EQ("", U.User); + EXPECT_EQ("", U.Password); + EXPECT_EQ(90u, U.Port); + EXPECT_EQ("www.debian.org", U.Host); + EXPECT_EQ("/temp/test", U.Path); + EXPECT_EQ("http://www.debian.org:90/temp/test", (std::string)U); + EXPECT_EQ("http://www.debian.org:90", URI::SiteOnly(U)); + EXPECT_EQ("http://www.debian.org:90/temp/test", URI::ArchiveOnly(U)); + EXPECT_EQ("http://www.debian.org:90/temp/test", URI::NoUserPassword(U)); + // Login data + U = URI("http://jgg:foo@ualberta.ca/blah"); + EXPECT_EQ("http", U.Access); + EXPECT_EQ("jgg", U.User); + EXPECT_EQ("foo", U.Password); + EXPECT_EQ(0u, U.Port); + EXPECT_EQ("ualberta.ca", U.Host); + EXPECT_EQ("/blah", U.Path); + EXPECT_EQ("http://jgg:foo@ualberta.ca/blah", (std::string)U); + EXPECT_EQ("http://ualberta.ca", URI::SiteOnly(U)); + EXPECT_EQ("http://ualberta.ca/blah", URI::ArchiveOnly(U)); + EXPECT_EQ("http://ualberta.ca/blah", URI::NoUserPassword(U)); + // just a user + U = URI("https://apt@example.org/blah"); + EXPECT_EQ("https", U.Access); + EXPECT_EQ("apt", U.User); + EXPECT_EQ("", U.Password); + EXPECT_EQ(0u, U.Port); + EXPECT_EQ("example.org", U.Host); + EXPECT_EQ("/blah", U.Path); + EXPECT_EQ("https://apt@example.org/blah", (std::string)U); + EXPECT_EQ("https://example.org", URI::SiteOnly(U)); + EXPECT_EQ("https://example.org/blah", URI::ArchiveOnly(U)); + EXPECT_EQ("https://example.org/blah", URI::NoUserPassword(U)); +} +TEST(URITest, SingeSlashFile) +{ + URI U("file:/usr/bin/foo"); + EXPECT_EQ("file", U.Access); + EXPECT_EQ("", U.User); + EXPECT_EQ("", U.Password); + EXPECT_EQ(0u, U.Port); + EXPECT_EQ("", U.Host); + EXPECT_EQ("/usr/bin/foo", U.Path); + EXPECT_EQ("file:/usr/bin/foo", (std::string)U); + EXPECT_EQ("file:", URI::SiteOnly(U)); + EXPECT_EQ("file:/usr/bin/foo", URI::ArchiveOnly(U)); + EXPECT_EQ("file:/usr/bin/foo", URI::NoUserPassword(U)); +} +TEST(URITest, BasicCDROM) +{ + URI U("cdrom:Moo Cow Rom:/debian"); + EXPECT_EQ("cdrom", U.Access); + EXPECT_EQ("", U.User); + EXPECT_EQ("", U.Password); + EXPECT_EQ(0u, U.Port); + EXPECT_EQ("Moo Cow Rom", U.Host); + EXPECT_EQ("/debian", U.Path); + EXPECT_EQ("cdrom://Moo Cow Rom/debian", (std::string)U); + EXPECT_EQ("cdrom://Moo Cow Rom", URI::SiteOnly(U)); + EXPECT_EQ("cdrom://Moo Cow Rom/debian", URI::ArchiveOnly(U)); + EXPECT_EQ("cdrom://Moo Cow Rom/debian", URI::NoUserPassword(U)); +} +TEST(URITest, RelativeGzip) +{ + URI U("gzip:./bar/cow"); + EXPECT_EQ("gzip", U.Access); + EXPECT_EQ("", U.User); + EXPECT_EQ("", U.Password); + EXPECT_EQ(0u, U.Port); + EXPECT_EQ(".", U.Host); + EXPECT_EQ("/bar/cow", U.Path); + EXPECT_EQ("gzip://./bar/cow", (std::string)U); + EXPECT_EQ("gzip://.", URI::SiteOnly(U)); + EXPECT_EQ("gzip://./bar/cow", URI::ArchiveOnly(U)); + EXPECT_EQ("gzip://./bar/cow", URI::NoUserPassword(U)); +} +TEST(URITest, NoSlashFTP) +{ + URI U("ftp:ftp.fr.debian.org/debian/pool/main/x/xtel/xtel_3.2.1-15_i386.deb"); + EXPECT_EQ("ftp", U.Access); + EXPECT_EQ("", U.User); + EXPECT_EQ("", U.Password); + EXPECT_EQ(0u, U.Port); + EXPECT_EQ("ftp.fr.debian.org", U.Host); + EXPECT_EQ("/debian/pool/main/x/xtel/xtel_3.2.1-15_i386.deb", U.Path); + EXPECT_EQ("ftp://ftp.fr.debian.org/debian/pool/main/x/xtel/xtel_3.2.1-15_i386.deb", (std::string)U); + EXPECT_EQ("ftp://ftp.fr.debian.org", URI::SiteOnly(U)); + EXPECT_EQ("ftp://ftp.fr.debian.org/debian/pool/main/x/xtel/xtel_3.2.1-15_i386.deb", URI::ArchiveOnly(U)); + EXPECT_EQ("ftp://ftp.fr.debian.org/debian/pool/main/x/xtel/xtel_3.2.1-15_i386.deb", URI::NoUserPassword(U)); +} +TEST(URITest, RFC2732) +{ + URI U("http://[1080::8:800:200C:417A]/foo"); + EXPECT_EQ("http", U.Access); + EXPECT_EQ("", U.User); + EXPECT_EQ("", U.Password); + EXPECT_EQ(0u, U.Port); + EXPECT_EQ("1080::8:800:200C:417A", U.Host); + EXPECT_EQ("/foo", U.Path); + EXPECT_EQ("http://[1080::8:800:200C:417A]/foo", (std::string)U); + EXPECT_EQ("http://[1080::8:800:200C:417A]", URI::SiteOnly(U)); + EXPECT_EQ("http://[1080::8:800:200C:417A]/foo", URI::ArchiveOnly(U)); + EXPECT_EQ("http://[1080::8:800:200C:417A]/foo", URI::NoUserPassword(U)); + // with port + U = URI("http://[::FFFF:129.144.52.38]:80/index.html"); + EXPECT_EQ("http", U.Access); + EXPECT_EQ("", U.User); + EXPECT_EQ("", U.Password); + EXPECT_EQ(80u, U.Port); + EXPECT_EQ("::FFFF:129.144.52.38", U.Host); + EXPECT_EQ("/index.html", U.Path); + EXPECT_EQ("http://[::FFFF:129.144.52.38]:80/index.html", (std::string)U); + EXPECT_EQ("http://[::FFFF:129.144.52.38]:80", URI::SiteOnly(U)); + EXPECT_EQ("http://[::FFFF:129.144.52.38]:80/index.html", URI::ArchiveOnly(U)); + EXPECT_EQ("http://[::FFFF:129.144.52.38]:80/index.html", URI::NoUserPassword(U)); + // extra colon + U = URI("http://[::FFFF:129.144.52.38:]:80/index.html"); + EXPECT_EQ("http", U.Access); + EXPECT_EQ("", U.User); + EXPECT_EQ("", U.Password); + EXPECT_EQ(80u, U.Port); + EXPECT_EQ("::FFFF:129.144.52.38:", U.Host); + EXPECT_EQ("/index.html", U.Path); + EXPECT_EQ("http://[::FFFF:129.144.52.38:]:80/index.html", (std::string)U); + EXPECT_EQ("http://[::FFFF:129.144.52.38:]:80", URI::SiteOnly(U)); + EXPECT_EQ("http://[::FFFF:129.144.52.38:]:80/index.html", URI::ArchiveOnly(U)); + EXPECT_EQ("http://[::FFFF:129.144.52.38:]:80/index.html", URI::NoUserPassword(U)); + // extra colon port + U = URI("http://[::FFFF:129.144.52.38:]/index.html"); + EXPECT_EQ("http", U.Access); + EXPECT_EQ("", U.User); + EXPECT_EQ("", U.Password); + EXPECT_EQ(0u, U.Port); + EXPECT_EQ("::FFFF:129.144.52.38:", U.Host); + EXPECT_EQ("/index.html", U.Path); + EXPECT_EQ("http://[::FFFF:129.144.52.38:]/index.html", (std::string)U); + EXPECT_EQ("http://[::FFFF:129.144.52.38:]", URI::SiteOnly(U)); + EXPECT_EQ("http://[::FFFF:129.144.52.38:]/index.html", URI::ArchiveOnly(U)); + EXPECT_EQ("http://[::FFFF:129.144.52.38:]/index.html", URI::NoUserPassword(U)); + // My Evil Corruption of RFC 2732 to handle CDROM names! + // Fun for the whole family! */ + U = URI("cdrom:[The Debian 1.2 disk, 1/2 R1:6]/debian/"); + EXPECT_EQ("cdrom", U.Access); + EXPECT_EQ("", U.User); + EXPECT_EQ("", U.Password); + EXPECT_EQ(0u, U.Port); + EXPECT_EQ("The Debian 1.2 disk, 1/2 R1:6", U.Host); + EXPECT_EQ("/debian/", U.Path); + EXPECT_EQ("cdrom://[The Debian 1.2 disk, 1/2 R1:6]/debian/", (std::string)U); + EXPECT_EQ("cdrom://[The Debian 1.2 disk, 1/2 R1:6]", URI::SiteOnly(U)); + EXPECT_EQ("cdrom://[The Debian 1.2 disk, 1/2 R1:6]/debian", URI::ArchiveOnly(U)); + EXPECT_EQ("cdrom://[The Debian 1.2 disk, 1/2 R1:6]/debian/", URI::NoUserPassword(U)); + // no brackets + U = URI("cdrom:Foo Bar Cow/debian/"); + EXPECT_EQ("cdrom", U.Access); + EXPECT_EQ("", U.User); + EXPECT_EQ("", U.Password); + EXPECT_EQ(0u, U.Port); + EXPECT_EQ("Foo Bar Cow", U.Host); + EXPECT_EQ("/debian/", U.Path); + EXPECT_EQ("cdrom://Foo Bar Cow/debian/", (std::string)U); + EXPECT_EQ("cdrom://Foo Bar Cow", URI::SiteOnly(U)); + EXPECT_EQ("cdrom://Foo Bar Cow/debian", URI::ArchiveOnly(U)); + EXPECT_EQ("cdrom://Foo Bar Cow/debian/", URI::NoUserPassword(U)); + // percent encoded password + U = URI("ftp://foo:b%40r@example.org"); + EXPECT_EQ("foo", U.User); + EXPECT_EQ("b@r", U.Password); + EXPECT_EQ("ftp://foo:b%40r@example.org/", (std::string) U); + EXPECT_EQ("ftp://example.org", URI::SiteOnly(U)); + EXPECT_EQ("ftp://example.org", URI::ArchiveOnly(U)); + EXPECT_EQ("ftp://example.org/", URI::NoUserPassword(U)); + // percent encoded user + U = URI("ftp://f%40o:bar@example.org"); + EXPECT_EQ("f@o", U.User); + EXPECT_EQ("bar", U.Password); + EXPECT_EQ("ftp://f%40o:bar@example.org/", (std::string) U); + EXPECT_EQ("ftp://example.org", URI::SiteOnly(U)); + EXPECT_EQ("ftp://example.org", URI::ArchiveOnly(U)); + EXPECT_EQ("ftp://example.org/", URI::NoUserPassword(U)); +} +TEST(URITest, AutoProxyTest) +{ + URI u0("http://www.debian.org:90/temp/test"); + URI u1("http://www.debian.org:91/temp/test"); + + _config->Set("Acquire::http::Proxy-Auto-Detect", "./apt-proxy-script"); + + // Scenario 0: Autodetecting a simple proxy + AutoDetectProxy(u0); + EXPECT_EQ(_config->Find("Acquire::http::proxy::www.debian.org", ""), "http://example.com"); + + // Scenario 1: Proxy stays the same if it is already set + AutoDetectProxy(u1); + EXPECT_EQ(_config->Find("Acquire::http::proxy::www.debian.org", ""), "http://example.com"); + + // Scenario 2: Reading with stderr output works fine + _config->Clear("Acquire::http::proxy::www.debian.org"); + AutoDetectProxy(u1); + EXPECT_EQ(_config->Find("Acquire::http::proxy::www.debian.org", ""), "http://example.com/foo"); + + // Scenario 1 again: Proxy stays the same if it is already set + AutoDetectProxy(u0); + EXPECT_EQ(_config->Find("Acquire::http::proxy::www.debian.org", ""), "http://example.com/foo"); +} |