summaryrefslogtreecommitdiffstats
path: root/test/libapt
diff options
context:
space:
mode:
Diffstat (limited to 'test/libapt')
-rw-r--r--test/libapt/CMakeLists.txt53
-rw-r--r--test/libapt/acqprogress_test.cc178
-rwxr-xr-xtest/libapt/apt-proxy-script9
-rw-r--r--test/libapt/authconf_test.cc261
-rw-r--r--test/libapt/cachefilter_test.cc119
-rw-r--r--test/libapt/cdrom_test.cc109
-rw-r--r--test/libapt/cdromfindpackages_test.cc125
-rw-r--r--test/libapt/commandline_test.cc238
-rw-r--r--test/libapt/compareversion_test.cc182
-rw-r--r--test/libapt/configuration_test.cc232
-rw-r--r--test/libapt/extracttar_test.cc52
-rw-r--r--test/libapt/file-helpers.cc85
-rw-r--r--test/libapt/file-helpers.h41
-rw-r--r--test/libapt/fileutl_test.cc399
-rw-r--r--test/libapt/getarchitectures_test.cc105
-rw-r--r--test/libapt/getlanguages_test.cc241
-rw-r--r--test/libapt/getlistoffilesindir_test.cc114
-rw-r--r--test/libapt/globalerror_test.cc169
-rw-r--r--test/libapt/gtest_runner.cc28
-rw-r--r--test/libapt/hashsums_test.cc305
-rw-r--r--test/libapt/indexcopytosourcelist_test.cc109
-rw-r--r--test/libapt/install_progress_test.cc25
-rw-r--r--test/libapt/json_test.cc69
-rw-r--r--test/libapt/openmaybeclearsignedfile_test.cc364
-rw-r--r--test/libapt/parsedepends_test.cc280
-rw-r--r--test/libapt/pattern_test.cc224
-rw-r--r--test/libapt/priority_test.cc16
-rw-r--r--test/libapt/sourcelist_test.cc33
-rw-r--r--test/libapt/srvrecs_test.cc85
-rw-r--r--test/libapt/stringview_test.cc95
-rw-r--r--test/libapt/strutil_test.cc446
-rw-r--r--test/libapt/tagfile_test.cc345
-rw-r--r--test/libapt/tagsection_test.cc232
-rw-r--r--test/libapt/teestream_test.cc39
-rw-r--r--test/libapt/uri_test.cc216
35 files changed, 5623 insertions, 0 deletions
diff --git a/test/libapt/CMakeLists.txt b/test/libapt/CMakeLists.txt
new file mode 100644
index 0000000..6bbe409
--- /dev/null
+++ b/test/libapt/CMakeLists.txt
@@ -0,0 +1,53 @@
+if (WITH_TESTS)
+ set(PROJECT_TEST_LIBRARIES apt-private)
+ find_path(GTEST_SRCPATH 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_SRCPATH})
+ include(ExternalProject)
+ ExternalProject_Add(gtest PREFIX ./gtest
+ SOURCE_DIR ${GTEST_SRCPATH}
+ 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_SRCPATH}/include)
+
+ message(STATUS "Found GTest at ${GTEST_SRCPATH}, 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..364971e
--- /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 <cstring>
+#include <string>
+#include <vector>
+
+#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..9e13c1d
--- /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 <cstddef>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#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..0fe32cf
--- /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 <cstdlib>
+#include <fstream>
+#include <string>
+#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..246aea7
--- /dev/null
+++ b/test/libapt/extracttar_test.cc
@@ -0,0 +1,52 @@
+#include <config.h>
+
+#include <apt-pkg/dirstream.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/extracttar.h>
+#include <cstdlib>
+#include <iostream>
+
+#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..bb7052b
--- /dev/null
+++ b/test/libapt/file-helpers.cc
@@ -0,0 +1,85 @@
+#include <config.h>
+
+#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..da772df
--- /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 <cstdlib>
+#include <cstring>
+#include <string>
+#include <vector>
+
+#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..4f76722
--- /dev/null
+++ b/test/libapt/getarchitectures_test.cc
@@ -0,0 +1,105 @@
+#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();
+}
+TEST(ArchitecturesTest,VeryForeign)
+{
+ _config->Clear();
+ _config->Set("APT::Architectures::", "i386");
+ _config->Set("APT::Architectures::", "amd64");
+ _config->Set("APT::Architectures::", "armel");
+
+ auto 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->Set("APT::BarbarianArchitectures::", "mipsel");
+ 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->Set("APT::BarbarianArchitectures::", "armel");
+ vec = APT::Configuration::getArchitectures(false);
+ ASSERT_EQ(2u, vec.size());
+ EXPECT_EQ("i386", vec[0]);
+ EXPECT_EQ("amd64", vec[1]);
+
+ _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..42e8355
--- /dev/null
+++ b/test/libapt/globalerror_test.cc
@@ -0,0 +1,169 @@
+#include <config.h>
+
+#include <apt-pkg/error.h>
+
+#include <cerrno>
+#include <cstddef>
+#include <cstring>
+#include <string>
+
+#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..09fc55d
--- /dev/null
+++ b/test/libapt/gtest_runner.cc
@@ -0,0 +1,28 @@
+#include <config.h>
+
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/pkgsystem.h>
+
+#include <gtest/gtest.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..901150e
--- /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 <cstdlib>
+#include <iostream>
+#include <string>
+
+#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..7dbe973
--- /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 <cstdio>
+#include <string>
+
+#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..f641c8c
--- /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 <cstring>
+#include <string>
+
+#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..f6a1a91
--- /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 <cstdlib>
+#include <cstring>
+#include <string>
+#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..51a574f
--- /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 <cstdlib>
+#include <cstring>
+#include <sstream>
+#include <string>
+#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 &section)
+{
+ 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 &section, 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");
+}