From 5857e2c680fde9e37abc8f799f8d5509dd47ed62 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Mon, 8 Jul 2019 16:45:27 -0700 Subject: initial remove-in-parallel doesn't actually do parallel remove yet --- toolsrc/src/vcpkg/base/files.cpp | 68 ++++++++++++++++++++++++++++++---------- toolsrc/src/vcpkg/base/rng.cpp | 14 +++++++++ 2 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 toolsrc/src/vcpkg/base/rng.cpp (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index 5099795e9..d0926bb4c 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include +#include #include #include #include @@ -256,26 +257,61 @@ namespace vcpkg::Files virtual bool remove(const fs::path& path, std::error_code& ec) override { return fs::stdfs::remove(path, ec); } virtual std::uintmax_t remove_all(const fs::path& path, std::error_code& ec) override { - // Working around the currently buggy remove_all() - std::uintmax_t out = fs::stdfs::remove_all(path, ec); + /* + does not use the std::filesystem call since it is buggy, and can + have spurious errors before VS 2017 update 6, and on later versions + (as well as on macOS and Linux), this is just as fast and will have + fewer spurious errors due to locks. + */ + struct recursive { + const fs::path& tmp_directory; + std::error_code& ec; + xoshiro256ss_engine& rng; + + void operator()(const fs::path& current) const { + const auto type = fs::stdfs::symlink_status(current, ec).type(); + if (ec) return; + + const auto tmp_name = Strings::b64url_encode(rng()); + const auto tmp_path = tmp_directory / tmp_name; + + switch (type) { + case fs::file_type::directory: { + fs::stdfs::rename(current, tmp_path, ec); + if (ec) return; + for (const auto& entry : fs::stdfs::directory_iterator(tmp_path)) { + (*this)(entry); + } + fs::stdfs::remove(tmp_path, ec); + } break; + case fs::file_type::symlink: + case fs::file_type::regular: { + fs::stdfs::rename(current, tmp_path, ec); + fs::stdfs::remove(current, ec); + } break; + case fs::file_type::not_found: return; + case fs::file_type::none: { + Checks::exit_with_message(VCPKG_LINE_INFO, "Error occurred when evaluating file type of file: %s", current); + } + default: { + Checks::exit_with_message(VCPKG_LINE_INFO, "Attempted to delete special file: %s", current); + } + } + } + }; - for (int i = 0; i < 5 && this->exists(path); i++) - { - using namespace std::chrono_literals; - std::this_thread::sleep_for(i * 100ms); - out += fs::stdfs::remove_all(path, ec); - } + auto const real_path = fs::stdfs::absolute(path); - if (this->exists(path)) - { - System::print2( - System::Color::warning, - "Some files in ", - path.u8string(), - " were unable to be removed. Close any editors operating in this directory and retry.\n"); + if (! real_path.has_parent_path()) { + Checks::exit_with_message(VCPKG_LINE_INFO, "Attempted to remove_all the base directory"); } - return out; + // thoughts: is this fine? or should we do something different? + // maybe a temporary directory? + auto const base_path = real_path.parent_path(); + + xoshiro256ss_engine rng{}; + recursive{base_path, ec, rng}(real_path); } virtual bool exists(const fs::path& path) const override { return fs::stdfs::exists(path); } virtual bool is_directory(const fs::path& path) const override { return fs::stdfs::is_directory(path); } diff --git a/toolsrc/src/vcpkg/base/rng.cpp b/toolsrc/src/vcpkg/base/rng.cpp new file mode 100644 index 000000000..9fe2ea3b4 --- /dev/null +++ b/toolsrc/src/vcpkg/base/rng.cpp @@ -0,0 +1,14 @@ +#include + +namespace vcpkg { + namespace { + std::random_device system_entropy{}; + } + + splitmix64_engine::splitmix64_engine() { + std::uint64_t top_half = system_entropy(); + std::uint64_t bottom_half = system_entropy(); + + state = (top_half << 32) | bottom_half; + } +} -- cgit v1.2.3 From 2d6df16849ebcf237d17c919727756d90974daba Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Wed, 10 Jul 2019 14:35:10 -0700 Subject: remove_all parallelized, and fix the issues with symlink --- toolsrc/src/vcpkg/base/files.cpp | 196 +++++++++++++++++++++++++++++++-------- toolsrc/src/vcpkg/base/rng.cpp | 4 +- 2 files changed, 157 insertions(+), 43 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index d0926bb4c..e89c531be 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #if defined(__linux__) || defined(__APPLE__) @@ -21,6 +22,45 @@ #include #endif +namespace fs { + file_status decltype(symlink_status)::operator()(const path& p, std::error_code& ec) const noexcept { +#if defined(_WIN32) + /* + do not find the permissions of the file -- it's unnecessary for the + things that vcpkg does. + if one were to add support for this in the future, one should look + into GetFileSecurityW + */ + perms permissions = perms::unknown; + + WIN32_FILE_ATTRIBUTE_DATA file_attributes; + file_type ft = file_type::unknown; + if (!GetFileAttributesExW(p.c_str(), GetFileExInfoStandard, &file_attributes)) { + ft = file_type::not_found; + } else if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + // check for reparse point -- if yes, then symlink + ft = file_type::symlink; + } else if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + ft = file_type::directory; + } else { + // otherwise, the file is a regular file + ft = file_type::regular; + } + + return file_status(ft, permissions); + +#else + return stdfs::symlink_status(p, ec); +#endif + } + + file_status decltype(symlink_status)::operator()(const path& p) const noexcept { + std::error_code ec; + auto result = symlink_status(p, ec); + if (ec) vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "error getting status of path %s: %s", p, ec.message()); + } +} + namespace vcpkg::Files { static const std::regex FILESYSTEM_INVALID_CHARACTERS_REGEX = std::regex(R"([\/:*?"<>|])"); @@ -263,55 +303,129 @@ namespace vcpkg::Files (as well as on macOS and Linux), this is just as fast and will have fewer spurious errors due to locks. */ - struct recursive { - const fs::path& tmp_directory; - std::error_code& ec; - xoshiro256ss_engine& rng; - - void operator()(const fs::path& current) const { - const auto type = fs::stdfs::symlink_status(current, ec).type(); - if (ec) return; - - const auto tmp_name = Strings::b64url_encode(rng()); - const auto tmp_path = tmp_directory / tmp_name; - - switch (type) { - case fs::file_type::directory: { - fs::stdfs::rename(current, tmp_path, ec); - if (ec) return; - for (const auto& entry : fs::stdfs::directory_iterator(tmp_path)) { - (*this)(entry); + + /* + `remove` doesn't actually remove anything -- it simply moves the + files into a parent directory (which ends up being at `path`), + and then inserts `actually_remove{current_path}` into the work + queue. + */ + struct remove { + struct tld { + const fs::path& tmp_directory; + std::uint64_t index; + + std::atomic& files_deleted; + + std::mutex& ec_mutex; + std::error_code& ec; + }; + + struct actually_remove; + using queue = WorkQueue; + + /* + if `current_path` is a directory, first `remove`s all + elements of the directory, then calls remove. + + else, just calls remove. + */ + struct actually_remove { + fs::path current_path; + + void operator()(tld& info, const queue& queue) const { + std::error_code ec; + const auto path_type = fs::symlink_status(current_path, ec).type(); + + if (check_ec(ec, info, queue)) return; + + if (path_type == fs::file_type::directory) { + for (const auto& entry : fs::stdfs::directory_iterator(current_path)) { + remove{}(entry, info, queue); + } + } + + if (fs::stdfs::remove(current_path, ec)) { + info.files_deleted.fetch_add(1, std::memory_order_relaxed); + } else { + check_ec(ec, info, queue); } - fs::stdfs::remove(tmp_path, ec); - } break; - case fs::file_type::symlink: - case fs::file_type::regular: { - fs::stdfs::rename(current, tmp_path, ec); - fs::stdfs::remove(current, ec); - } break; - case fs::file_type::not_found: return; - case fs::file_type::none: { - Checks::exit_with_message(VCPKG_LINE_INFO, "Error occurred when evaluating file type of file: %s", current); - } - default: { - Checks::exit_with_message(VCPKG_LINE_INFO, "Attempted to delete special file: %s", current); } + }; + + static bool check_ec(const std::error_code& ec, tld& info, const queue& queue) { + if (ec) { + queue.terminate(); + + auto lck = std::unique_lock(info.ec_mutex); + if (!info.ec) { + info.ec = ec; + } + + return true; + } else { + return false; } } + + void operator()(const fs::path& current_path, tld& info, const queue& queue) const { + std::error_code ec; + + const auto type = fs::symlink_status(current_path, ec).type(); + if (check_ec(ec, info, queue)) return; + + const auto tmp_name = Strings::b64url_encode(info.index++); + const auto tmp_path = info.tmp_directory / tmp_name; + + fs::stdfs::rename(current_path, tmp_path, ec); + if (check_ec(ec, info, queue)) return; + + queue.enqueue_action(actually_remove{std::move(tmp_path)}); + } }; - auto const real_path = fs::stdfs::absolute(path); + const auto path_type = fs::symlink_status(path, ec).type(); - if (! real_path.has_parent_path()) { - Checks::exit_with_message(VCPKG_LINE_INFO, "Attempted to remove_all the base directory"); + std::atomic files_deleted = 0; + + if (path_type == fs::file_type::directory) { + std::uint64_t index = 0; + std::mutex ec_mutex; + + auto queue = remove::queue([&] { + index += 1 << 32; + return remove::tld{path, index, files_deleted, ec_mutex, ec}; + }); + + index += 1 << 32; + auto main_tld = remove::tld{path, index, files_deleted, ec_mutex, ec}; + for (const auto& entry : fs::stdfs::directory_iterator(path)) { + remove{}(entry, main_tld, queue); + } + + queue.join(); } - // thoughts: is this fine? or should we do something different? - // maybe a temporary directory? - auto const base_path = real_path.parent_path(); + /* + we need to do backoff on the removal of the top level directory, + since we need to place all moved files into that top level + directory, and so we can only delete the directory after all the + lower levels have been deleted. + */ + for (int backoff = 0; backoff < 5; ++backoff) { + if (backoff) { + using namespace std::chrono_literals; + auto backoff_time = 100ms * backoff; + std::this_thread::sleep_for(backoff_time); + } + + if (fs::stdfs::remove(path, ec)) { + files_deleted.fetch_add(1, std::memory_order_relaxed); + break; + } + } - xoshiro256ss_engine rng{}; - recursive{base_path, ec, rng}(real_path); + return files_deleted; } virtual bool exists(const fs::path& path) const override { return fs::stdfs::exists(path); } virtual bool is_directory(const fs::path& path) const override { return fs::stdfs::is_directory(path); } @@ -343,11 +457,11 @@ namespace vcpkg::Files virtual fs::file_status status(const fs::path& path, std::error_code& ec) const override { - return fs::stdfs::status(path, ec); + return fs::status(path, ec); } virtual fs::file_status symlink_status(const fs::path& path, std::error_code& ec) const override { - return fs::stdfs::symlink_status(path, ec); + return fs::symlink_status(path, ec); } virtual void write_contents(const fs::path& file_path, const std::string& data, std::error_code& ec) override { diff --git a/toolsrc/src/vcpkg/base/rng.cpp b/toolsrc/src/vcpkg/base/rng.cpp index 9fe2ea3b4..40ff646b7 100644 --- a/toolsrc/src/vcpkg/base/rng.cpp +++ b/toolsrc/src/vcpkg/base/rng.cpp @@ -1,11 +1,11 @@ #include -namespace vcpkg { +namespace vcpkg::Rng { namespace { std::random_device system_entropy{}; } - splitmix64_engine::splitmix64_engine() { + splitmix::splitmix() { std::uint64_t top_half = system_entropy(); std::uint64_t bottom_half = system_entropy(); -- cgit v1.2.3 From 43493b56df7c8f7aab02256ab7f65135d4dd1d4c Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Wed, 10 Jul 2019 15:18:44 -0700 Subject: delete the random number generator --- toolsrc/src/vcpkg/base/rng.cpp | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 toolsrc/src/vcpkg/base/rng.cpp (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg/base/rng.cpp b/toolsrc/src/vcpkg/base/rng.cpp deleted file mode 100644 index 40ff646b7..000000000 --- a/toolsrc/src/vcpkg/base/rng.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include - -namespace vcpkg::Rng { - namespace { - std::random_device system_entropy{}; - } - - splitmix::splitmix() { - std::uint64_t top_half = system_entropy(); - std::uint64_t bottom_half = system_entropy(); - - state = (top_half << 32) | bottom_half; - } -} -- cgit v1.2.3 From 3b6d6b3465e0e79999e5995f0104a6e8c021088c Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Wed, 10 Jul 2019 15:42:13 -0700 Subject: actually get the code compiling --- toolsrc/src/vcpkg/base/files.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index e89c531be..d8a982164 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -1,7 +1,6 @@ #include "pch.h" #include -#include #include #include #include @@ -57,7 +56,9 @@ namespace fs { file_status decltype(symlink_status)::operator()(const path& p) const noexcept { std::error_code ec; auto result = symlink_status(p, ec); - if (ec) vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "error getting status of path %s: %s", p, ec.message()); + if (ec) vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "error getting status of path %s: %s", p.string(), ec.message()); + + return result; } } @@ -393,11 +394,11 @@ namespace vcpkg::Files std::mutex ec_mutex; auto queue = remove::queue([&] { - index += 1 << 32; + index += static_cast(1) << 32; return remove::tld{path, index, files_deleted, ec_mutex, ec}; }); - index += 1 << 32; + index += static_cast(1) << 32; auto main_tld = remove::tld{path, index, files_deleted, ec_mutex, ec}; for (const auto& entry : fs::stdfs::directory_iterator(path)) { remove{}(entry, main_tld, queue); -- cgit v1.2.3 From 5b76f24f35976739991941d3b6289fb78fd93648 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Wed, 10 Jul 2019 16:28:56 -0700 Subject: make this compile on macos --- toolsrc/src/vcpkg/base/files.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index d8a982164..f4c2106d4 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -21,8 +21,8 @@ #include #endif -namespace fs { - file_status decltype(symlink_status)::operator()(const path& p, std::error_code& ec) const noexcept { +namespace fs::detail { + file_status symlink_status_t::operator()(const path& p, std::error_code& ec) const noexcept { #if defined(_WIN32) /* do not find the permissions of the file -- it's unnecessary for the @@ -53,7 +53,7 @@ namespace fs { #endif } - file_status decltype(symlink_status)::operator()(const path& p) const noexcept { + file_status symlink_status_t::operator()(const path& p) const noexcept { std::error_code ec; auto result = symlink_status(p, ec); if (ec) vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "error getting status of path %s: %s", p.string(), ec.message()); -- cgit v1.2.3 From bb579072077153fabfa74acec852bce222265357 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Wed, 10 Jul 2019 17:39:04 -0700 Subject: make it compile on macos under g++6 --- toolsrc/src/vcpkg/base/files.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index f4c2106d4..8bc37819a 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -129,7 +129,7 @@ namespace vcpkg::Files file_stream.read(&output[0], length); file_stream.close(); - return std::move(output); + return output; } virtual Expected> read_lines(const fs::path& file_path) const override { @@ -147,7 +147,7 @@ namespace vcpkg::Files } file_stream.close(); - return std::move(output); + return output; } virtual fs::path find_file_recursively_up(const fs::path& starting_dir, const std::string& filename) const override @@ -372,9 +372,6 @@ namespace vcpkg::Files void operator()(const fs::path& current_path, tld& info, const queue& queue) const { std::error_code ec; - const auto type = fs::symlink_status(current_path, ec).type(); - if (check_ec(ec, info, queue)) return; - const auto tmp_name = Strings::b64url_encode(info.index++); const auto tmp_path = info.tmp_directory / tmp_name; @@ -387,16 +384,16 @@ namespace vcpkg::Files const auto path_type = fs::symlink_status(path, ec).type(); - std::atomic files_deleted = 0; + std::atomic files_deleted{0}; if (path_type == fs::file_type::directory) { std::uint64_t index = 0; std::mutex ec_mutex; - auto queue = remove::queue([&] { + remove::queue queue{[&] { index += static_cast(1) << 32; return remove::tld{path, index, files_deleted, ec_mutex, ec}; - }); + }}; index += static_cast(1) << 32; auto main_tld = remove::tld{path, index, files_deleted, ec_mutex, ec}; -- cgit v1.2.3 From 510b0c5cc0233311b6993b89cd5ce488218ed78d Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Thu, 11 Jul 2019 15:01:29 -0700 Subject: fix more comments --- toolsrc/src/vcpkg/archives.cpp | 5 +- toolsrc/src/vcpkg/base/files.cpp | 133 ++++++++++++++++++++++--------- toolsrc/src/vcpkg/base/strings.cpp | 42 ++++++++++ toolsrc/src/vcpkg/build.cpp | 12 +-- toolsrc/src/vcpkg/commands.exportifw.cpp | 16 ++-- toolsrc/src/vcpkg/commands.portsdiff.cpp | 2 +- toolsrc/src/vcpkg/export.cpp | 6 +- toolsrc/src/vcpkg/install.cpp | 3 +- toolsrc/src/vcpkg/remove.cpp | 3 +- 9 files changed, 164 insertions(+), 58 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg/archives.cpp b/toolsrc/src/vcpkg/archives.cpp index 69a916828..d22e841de 100644 --- a/toolsrc/src/vcpkg/archives.cpp +++ b/toolsrc/src/vcpkg/archives.cpp @@ -15,9 +15,10 @@ namespace vcpkg::Archives #endif ; + fs.remove_all(to_path, VCPKG_LINE_INFO); + fs.remove_all(to_path_partial, VCPKG_LINE_INFO); + // TODO: check this error code std::error_code ec; - fs.remove_all(to_path, ec); - fs.remove_all(to_path_partial, ec); fs.create_directories(to_path_partial, ec); const auto ext = archive.extension(); #if defined(_WIN32) diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index 8bc37819a..a4e67a142 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -5,8 +5,8 @@ #include #include #include -#include #include +#include #if defined(__linux__) || defined(__APPLE__) #include @@ -21,8 +21,10 @@ #include #endif -namespace fs::detail { - file_status symlink_status_t::operator()(const path& p, std::error_code& ec) const noexcept { +namespace fs::detail +{ + file_status symlink_status_t::operator()(const path& p, std::error_code& ec) const noexcept + { #if defined(_WIN32) /* do not find the permissions of the file -- it's unnecessary for the @@ -34,14 +36,21 @@ namespace fs::detail { WIN32_FILE_ATTRIBUTE_DATA file_attributes; file_type ft = file_type::unknown; - if (!GetFileAttributesExW(p.c_str(), GetFileExInfoStandard, &file_attributes)) { + if (!GetFileAttributesExW(p.c_str(), GetFileExInfoStandard, &file_attributes)) + { ft = file_type::not_found; - } else if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + } + else if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + { // check for reparse point -- if yes, then symlink ft = file_type::symlink; - } else if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + } + else if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { ft = file_type::directory; - } else { + } + else + { // otherwise, the file is a regular file ft = file_type::regular; } @@ -53,12 +62,13 @@ namespace fs::detail { #endif } - file_status symlink_status_t::operator()(const path& p) const noexcept { + file_status symlink_status_t::operator()(const path& p, vcpkg::LineInfo li) const noexcept + { std::error_code ec; auto result = symlink_status(p, ec); - if (ec) vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "error getting status of path %s: %s", p.string(), ec.message()); + if (ec) vcpkg::Checks::exit_with_message(li, "error getting status of path %s: %s", p.string(), ec.message()); - return result; + return result; } } @@ -105,6 +115,25 @@ namespace vcpkg::Files if (ec) Checks::exit_with_message(linfo, "error writing lines: %s: %s", path.u8string(), ec.message()); } + std::uintmax_t Filesystem::remove_all(const fs::path& path, LineInfo li) + { + std::error_code ec; + fs::path failure_point; + + const auto result = this->remove_all(path, ec, failure_point); + + if (ec) + { + Checks::exit_with_message(li, + "Failure to remove_all(%s) due to file %s: %s", + path.string(), + failure_point.string(), + ec.message()); + } + + return result; + } + struct RealFilesystem final : Filesystem { virtual Expected read_contents(const fs::path& file_path) const override @@ -296,7 +325,7 @@ namespace vcpkg::Files #endif } virtual bool remove(const fs::path& path, std::error_code& ec) override { return fs::stdfs::remove(path, ec); } - virtual std::uintmax_t remove_all(const fs::path& path, std::error_code& ec) override + virtual std::uintmax_t remove_all(const fs::path& path, std::error_code& ec, fs::path& failure_point) override { /* does not use the std::filesystem call since it is buggy, and can @@ -311,8 +340,10 @@ namespace vcpkg::Files and then inserts `actually_remove{current_path}` into the work queue. */ - struct remove { - struct tld { + struct remove + { + struct tld + { const fs::path& tmp_directory; std::uint64_t index; @@ -320,6 +351,7 @@ namespace vcpkg::Files std::mutex& ec_mutex; std::error_code& ec; + fs::path& failure_point; }; struct actually_remove; @@ -331,52 +363,68 @@ namespace vcpkg::Files else, just calls remove. */ - struct actually_remove { + struct actually_remove + { fs::path current_path; - void operator()(tld& info, const queue& queue) const { + void operator()(tld& info, const queue& queue) const + { std::error_code ec; const auto path_type = fs::symlink_status(current_path, ec).type(); - if (check_ec(ec, info, queue)) return; + if (check_ec(ec, info, queue, current_path)) return; - if (path_type == fs::file_type::directory) { - for (const auto& entry : fs::stdfs::directory_iterator(current_path)) { + if (path_type == fs::file_type::directory) + { + for (const auto& entry : fs::stdfs::directory_iterator(current_path)) + { remove{}(entry, info, queue); } } - if (fs::stdfs::remove(current_path, ec)) { + if (fs::stdfs::remove(current_path, ec)) + { info.files_deleted.fetch_add(1, std::memory_order_relaxed); - } else { - check_ec(ec, info, queue); + } + else + { + check_ec(ec, info, queue, current_path); } } }; - static bool check_ec(const std::error_code& ec, tld& info, const queue& queue) { - if (ec) { + static bool check_ec(const std::error_code& ec, + tld& info, + const queue& queue, + const fs::path& failure_point) + { + if (ec) + { queue.terminate(); auto lck = std::unique_lock(info.ec_mutex); - if (!info.ec) { + if (!info.ec) + { info.ec = ec; } return true; - } else { + } + else + { return false; } } - void operator()(const fs::path& current_path, tld& info, const queue& queue) const { + void operator()(const fs::path& current_path, tld& info, const queue& queue) const + { std::error_code ec; const auto tmp_name = Strings::b64url_encode(info.index++); const auto tmp_path = info.tmp_directory / tmp_name; fs::stdfs::rename(current_path, tmp_path, ec); - if (check_ec(ec, info, queue)) return; + if (check_ec(ec, info, queue, current_path)) return; queue.enqueue_action(actually_remove{std::move(tmp_path)}); } @@ -386,22 +434,28 @@ namespace vcpkg::Files std::atomic files_deleted{0}; - if (path_type == fs::file_type::directory) { + if (path_type == fs::file_type::directory) + { std::uint64_t index = 0; std::mutex ec_mutex; - remove::queue queue{[&] { + auto const tld_gen = [&] { index += static_cast(1) << 32; - return remove::tld{path, index, files_deleted, ec_mutex, ec}; - }}; + return remove::tld{path, index, files_deleted, ec_mutex, ec, failure_point}; + }; - index += static_cast(1) << 32; - auto main_tld = remove::tld{path, index, files_deleted, ec_mutex, ec}; - for (const auto& entry : fs::stdfs::directory_iterator(path)) { + remove::queue queue{4, VCPKG_LINE_INFO, tld_gen}; + + // note: we don't actually start the queue running until the + // `join()`. This allows us to rename all the top-level files in + // peace, so that we don't get collisions. + auto main_tld = tld_gen(); + for (const auto& entry : fs::stdfs::directory_iterator(path)) + { remove{}(entry, main_tld, queue); } - queue.join(); + queue.join(VCPKG_LINE_INFO); } /* @@ -410,14 +464,17 @@ namespace vcpkg::Files directory, and so we can only delete the directory after all the lower levels have been deleted. */ - for (int backoff = 0; backoff < 5; ++backoff) { - if (backoff) { + for (int backoff = 0; backoff < 5; ++backoff) + { + if (backoff) + { using namespace std::chrono_literals; auto backoff_time = 100ms * backoff; std::this_thread::sleep_for(backoff_time); } - if (fs::stdfs::remove(path, ec)) { + if (fs::stdfs::remove(path, ec)) + { files_deleted.fetch_add(1, std::memory_order_relaxed); break; } diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp index 54a74a7a1..16543046e 100644 --- a/toolsrc/src/vcpkg/base/strings.cpp +++ b/toolsrc/src/vcpkg/base/strings.cpp @@ -288,3 +288,45 @@ bool Strings::contains(StringView haystack, StringView needle) { return Strings::search(haystack, needle) != haystack.end(); } + +namespace vcpkg::Strings::detail { + + template + std::string b64url_encode_implementation(Integral x) { + static_assert(std::is_integral::value, "b64url_encode must take an integer type"); + using Unsigned = std::make_unsigned_t; + auto value = static_cast(x); + + // 64 values, plus the implicit \0 + constexpr static char map[0x41] = + /* 0123456789ABCDEF */ + /*0*/ "ABCDEFGHIJKLMNOP" + /*1*/ "QRSTUVWXYZabcdef" + /*2*/ "ghijklmnopqrstuv" + /*3*/ "wxyz0123456789-_" + ; + + constexpr static int shift = 5; + constexpr static auto mask = (static_cast(1) << shift) - 1; + + std::string result; + // reserve ceiling(number of bits / 3) + result.resize((sizeof(value) * 8 + 2) / 3, map[0]); + + for (char& c: result) { + if (value == 0) { + break; + } + c = map[value & mask]; + value >>= shift; + } + + return result; + } + + std::string b64url_encode_t::operator()(std::uint64_t x) const noexcept{ + return b64url_encode_implementation(x); + } + +} + diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp index 68df1f965..c50acce7e 100644 --- a/toolsrc/src/vcpkg/build.cpp +++ b/toolsrc/src/vcpkg/build.cpp @@ -363,7 +363,8 @@ namespace vcpkg::Build const Triplet& triplet = spec.triplet(); const auto& triplet_file_path = paths.get_triplet_file_path(spec.triplet()).u8string(); - if (!Strings::case_insensitive_ascii_starts_with(triplet_file_path, paths.triplets.u8string())) + if (!Strings::case_insensitive_ascii_starts_with(triplet_file_path, + paths.triplets.u8string())) { System::printf("-- Loading triplet configuration from: %s\n", triplet_file_path); } @@ -495,7 +496,8 @@ namespace vcpkg::Build if (fs.is_directory(file)) // Will only keep the logs { std::error_code ec; - fs.remove_all(file, ec); + fs::path failure_point; + fs.remove_all(file, ec, failure_point); } } } @@ -610,8 +612,8 @@ namespace vcpkg::Build auto& fs = paths.get_filesystem(); auto pkg_path = paths.package_dir(spec); + fs.remove_all(pkg_path, VCPKG_LINE_INFO); std::error_code ec; - fs.remove_all(pkg_path, ec); fs.create_directories(pkg_path, ec); auto files = fs.get_files_non_recursive(pkg_path); Checks::check_exit(VCPKG_LINE_INFO, files.empty(), "unable to clear path: %s", pkg_path.u8string()); @@ -794,7 +796,7 @@ namespace vcpkg::Build fs.rename_or_copy(tmp_failure_zip, archive_tombstone_path, ".tmp", ec); // clean up temporary directory - fs.remove_all(tmp_log_path, ec); + fs.remove_all(tmp_log_path, VCPKG_LINE_INFO); } } @@ -1018,7 +1020,7 @@ namespace vcpkg::Build hash += "-"; hash += Hash::get_file_hash(fs, *p, "SHA1"); } - else if (pre_build_info.cmake_system_name.empty() || + else if (pre_build_info.cmake_system_name.empty() || pre_build_info.cmake_system_name == "WindowsStore") { hash += "-"; diff --git a/toolsrc/src/vcpkg/commands.exportifw.cpp b/toolsrc/src/vcpkg/commands.exportifw.cpp index f0946110c..3d963a297 100644 --- a/toolsrc/src/vcpkg/commands.exportifw.cpp +++ b/toolsrc/src/vcpkg/commands.exportifw.cpp @@ -352,13 +352,15 @@ namespace vcpkg::Export::IFW System::print2("Generating repository ", repository_dir.generic_u8string(), "...\n"); std::error_code ec; + fs::path failure_point; Files::Filesystem& fs = paths.get_filesystem(); - fs.remove_all(repository_dir, ec); + fs.remove_all(repository_dir, ec, failure_point); Checks::check_exit(VCPKG_LINE_INFO, !ec, - "Could not remove outdated repository directory %s", - repository_dir.generic_u8string()); + "Could not remove outdated repository directory %s due to file %s", + repository_dir.generic_u8string(), + failure_point.string()); const auto cmd_line = Strings::format(R"("%s" --packages "%s" "%s" > nul)", repogen_exe.u8string(), @@ -414,16 +416,18 @@ namespace vcpkg::Export::IFW const VcpkgPaths& paths) { std::error_code ec; + fs::path failure_point; Files::Filesystem& fs = paths.get_filesystem(); // Prepare packages directory const fs::path ifw_packages_dir_path = get_packages_dir_path(export_id, ifw_options, paths); - fs.remove_all(ifw_packages_dir_path, ec); + fs.remove_all(ifw_packages_dir_path, ec, failure_point); Checks::check_exit(VCPKG_LINE_INFO, !ec, - "Could not remove outdated packages directory %s", - ifw_packages_dir_path.generic_u8string()); + "Could not remove outdated packages directory %s due to file %s", + ifw_packages_dir_path.generic_u8string(), + failure_point.string()); fs.create_directory(ifw_packages_dir_path, ec); Checks::check_exit( diff --git a/toolsrc/src/vcpkg/commands.portsdiff.cpp b/toolsrc/src/vcpkg/commands.portsdiff.cpp index b30c38f43..cddc274b8 100644 --- a/toolsrc/src/vcpkg/commands.portsdiff.cpp +++ b/toolsrc/src/vcpkg/commands.portsdiff.cpp @@ -105,7 +105,7 @@ namespace vcpkg::Commands::PortsDiff std::map names_and_versions; for (auto&& port : all_ports) names_and_versions.emplace(port->core_paragraph->name, port->core_paragraph->version); - fs.remove_all(temp_checkout_path, ec); + fs.remove_all(temp_checkout_path, VCPKG_LINE_INFO); return names_and_versions; } diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp index 88c1526c5..f306bf4e6 100644 --- a/toolsrc/src/vcpkg/export.cpp +++ b/toolsrc/src/vcpkg/export.cpp @@ -400,8 +400,10 @@ namespace vcpkg::Export Files::Filesystem& fs = paths.get_filesystem(); const fs::path export_to_path = paths.root; const fs::path raw_exported_dir_path = export_to_path / export_id; + fs.remove_all(raw_exported_dir_path, VCPKG_LINE_INFO); + + // TODO: error handling std::error_code ec; - fs.remove_all(raw_exported_dir_path, ec); fs.create_directory(raw_exported_dir_path, ec); // execute the plan @@ -476,7 +478,7 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console if (!opts.raw) { - fs.remove_all(raw_exported_dir_path, ec); + fs.remove_all(raw_exported_dir_path, VCPKG_LINE_INFO); } } diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp index de19c360a..981a9233f 100644 --- a/toolsrc/src/vcpkg/install.cpp +++ b/toolsrc/src/vcpkg/install.cpp @@ -355,8 +355,7 @@ namespace vcpkg::Install { auto& fs = paths.get_filesystem(); const fs::path package_dir = paths.package_dir(action.spec); - std::error_code ec; - fs.remove_all(package_dir, ec); + fs.remove_all(package_dir, VCPKG_LINE_INFO); } if (action.build_options.clean_downloads == Build::CleanDownloads::YES) diff --git a/toolsrc/src/vcpkg/remove.cpp b/toolsrc/src/vcpkg/remove.cpp index a40b27bd7..84ec6c981 100644 --- a/toolsrc/src/vcpkg/remove.cpp +++ b/toolsrc/src/vcpkg/remove.cpp @@ -179,8 +179,7 @@ namespace vcpkg::Remove { System::printf("Purging package %s...\n", display_name); Files::Filesystem& fs = paths.get_filesystem(); - std::error_code ec; - fs.remove_all(paths.packages / action.spec.dir(), ec); + fs.remove_all(paths.packages / action.spec.dir(), VCPKG_LINE_INFO); System::printf(System::Color::success, "Purging package %s... done\n", display_name); } } -- cgit v1.2.3 From a0fe40ea5842006c0da901a99ae07d7a25531175 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Thu, 11 Jul 2019 18:16:10 -0700 Subject: add tests! Also, fix all the bugs I found when I wrote the tests! --- toolsrc/src/tests.files.cpp | 80 ++++++++++++++++++++++++++++++++++++++ toolsrc/src/tests.strings.cpp | 38 ++++++++++++++++++ toolsrc/src/vcpkg/base/files.cpp | 3 +- toolsrc/src/vcpkg/base/strings.cpp | 72 +++++++++++++++++----------------- 4 files changed, 157 insertions(+), 36 deletions(-) create mode 100644 toolsrc/src/tests.files.cpp create mode 100644 toolsrc/src/tests.strings.cpp (limited to 'toolsrc/src') diff --git a/toolsrc/src/tests.files.cpp b/toolsrc/src/tests.files.cpp new file mode 100644 index 000000000..73c7eb5bc --- /dev/null +++ b/toolsrc/src/tests.files.cpp @@ -0,0 +1,80 @@ +#include "tests.pch.h" + +#include +#include + +#include +#include + +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace UnitTest1 { + class FilesTest : public TestClass { + using uid = std::uniform_int_distribution; + + std::string get_random_filename() + { + std::random_device rd; + return vcpkg::Strings::b64url_encode(uid{}(rd)); + } + + void create_directory_tree( + vcpkg::Files::Filesystem& fs, + std::uint64_t depth, + const fs::path& base) + { + std::random_device rd; + constexpr auto max_depth = std::uint64_t(3); + const auto width = depth ? uid{0, (max_depth - depth) * 3 / 2}(rd) : 5; + + std::error_code ec; + if (width == 0) { + fs.write_contents(base, "", ec); + Assert::IsFalse(bool(ec)); + + return; + } + + fs.create_directory(base, ec); + Assert::IsFalse(bool(ec)); + + for (int i = 0; i < width; ++i) { + create_directory_tree(fs, depth + 1, base / get_random_filename()); + } + } + + TEST_METHOD(remove_all) { + fs::path temp_dir; + + { + wchar_t* tmp = static_cast(calloc(32'767, 2)); + + if (!GetEnvironmentVariableW(L"TEMP", tmp, 32'767)) { + Assert::Fail(L"GetEnvironmentVariable(\"TEMP\") failed"); + } + + temp_dir = tmp; + + std::string dir_name = "vcpkg-tmp-dir-"; + dir_name += get_random_filename(); + + temp_dir /= dir_name; + } + + auto& fs = vcpkg::Files::get_real_filesystem(); + + std::cout << "temp dir is: " << temp_dir << '\n'; + + std::error_code ec; + create_directory_tree(fs, 0, temp_dir); + + fs::path fp; + fs.remove_all(temp_dir, ec, fp); + Assert::IsFalse(bool(ec)); + + Assert::IsFalse(fs.exists(temp_dir)); + } + }; +} diff --git a/toolsrc/src/tests.strings.cpp b/toolsrc/src/tests.strings.cpp new file mode 100644 index 000000000..14b449e86 --- /dev/null +++ b/toolsrc/src/tests.strings.cpp @@ -0,0 +1,38 @@ +#include "tests.pch.h" + +#include + +#include +#include +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace UnitTest1 { + class StringsTest : public TestClass { + TEST_METHOD(b64url_encode) + { + using u64 = std::uint64_t; + + std::vector> map; + + map.emplace_back(0, "AAAAAAAAAAA"); + map.emplace_back(1, "BAAAAAAAAAA"); + + map.emplace_back(u64(1) << 32, "AAAAAEAAAAA"); + map.emplace_back((u64(1) << 32) + 1, "BAAAAEAAAAA"); + + map.emplace_back(0xE4D0'1065'D11E'0229, "pIgHRXGEQTO"); + map.emplace_back(0xA626'FE45'B135'07FF, "_fQNxWk_mYK"); + map.emplace_back(0xEE36'D228'0C31'D405, "FQdMMgi024O"); + map.emplace_back(0x1405'64E7'FE7E'A88C, "Miqf-fOZFQB"); + map.emplace_back(0xFFFF'FFFF'FFFF'FFFF, "__________P"); + + std::string result; + for (const auto& pr : map) { + result = vcpkg::Strings::b64url_encode(pr.first); + Assert::AreEqual(result, pr.second); + } + } + }; +} diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index a4e67a142..4e36fe990 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -23,7 +23,7 @@ namespace fs::detail { - file_status symlink_status_t::operator()(const path& p, std::error_code& ec) const noexcept + file_status symlink_status_t::operator()(const path& p, std::error_code&) const noexcept { #if defined(_WIN32) /* @@ -406,6 +406,7 @@ namespace vcpkg::Files if (!info.ec) { info.ec = ec; + info.failure_point = failure_point; } return true; diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp index 16543046e..ade4384a9 100644 --- a/toolsrc/src/vcpkg/base/strings.cpp +++ b/toolsrc/src/vcpkg/base/strings.cpp @@ -289,44 +289,46 @@ bool Strings::contains(StringView haystack, StringView needle) return Strings::search(haystack, needle) != haystack.end(); } -namespace vcpkg::Strings::detail { - - template - std::string b64url_encode_implementation(Integral x) { - static_assert(std::is_integral::value, "b64url_encode must take an integer type"); - using Unsigned = std::make_unsigned_t; - auto value = static_cast(x); - - // 64 values, plus the implicit \0 - constexpr static char map[0x41] = - /* 0123456789ABCDEF */ - /*0*/ "ABCDEFGHIJKLMNOP" - /*1*/ "QRSTUVWXYZabcdef" - /*2*/ "ghijklmnopqrstuv" - /*3*/ "wxyz0123456789-_" - ; - - constexpr static int shift = 5; - constexpr static auto mask = (static_cast(1) << shift) - 1; - - std::string result; - // reserve ceiling(number of bits / 3) - result.resize((sizeof(value) * 8 + 2) / 3, map[0]); - - for (char& c: result) { - if (value == 0) { - break; +namespace vcpkg::Strings +{ + namespace + { + template + std::string b64url_encode_implementation(Integral x) + { + static_assert(std::is_integral::value, "b64url_encode must take an integer type"); + using Unsigned = std::make_unsigned_t; + auto value = static_cast(x); + + // 64 values, plus the implicit \0 + constexpr static char map[65] = + /* 0123456789ABCDEF */ + /*0*/ "ABCDEFGHIJKLMNOP" + /*1*/ "QRSTUVWXYZabcdef" + /*2*/ "ghijklmnopqrstuv" + /*3*/ "wxyz0123456789-_"; + + // log2(64) + constexpr static int shift = 6; + // 64 - 1 + constexpr static auto mask = 63; + + // ceiling(bitsize(Integral) / log2(64)) + constexpr static auto result_size = (sizeof(value) * 8 + shift - 1) / shift; + + std::string result; + result.reserve(result_size); + + for (std::size_t i = 0; i < result_size; ++i) + { + result.push_back(map[value & mask]); + value >>= shift; } - c = map[value & mask]; - value >>= shift; - } - return result; + return result; + } } - std::string b64url_encode_t::operator()(std::uint64_t x) const noexcept{ - return b64url_encode_implementation(x); - } + std::string b64url_encode(std::uint64_t x) noexcept { return b64url_encode_implementation(x); } } - -- cgit v1.2.3 From 771e23c665f1960ef4e7fd9816e0d21c943a7903 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Thu, 11 Jul 2019 18:26:42 -0700 Subject: forgot to test on macos >.< --- toolsrc/src/vcpkg/base/files.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index 4e36fe990..e40822705 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -23,9 +23,11 @@ namespace fs::detail { - file_status symlink_status_t::operator()(const path& p, std::error_code&) const noexcept + file_status symlink_status_t::operator()(const path& p, std::error_code& ec) const noexcept { #if defined(_WIN32) + static_cast(ec); + /* do not find the permissions of the file -- it's unnecessary for the things that vcpkg does. -- cgit v1.2.3 From 02c977186e890e4090d91c171b42d20729e668af Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Mon, 15 Jul 2019 16:43:55 -0700 Subject: modify files test to include symlinks --- toolsrc/src/tests.files.cpp | 121 +++++++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 34 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/tests.files.cpp b/toolsrc/src/tests.files.cpp index 73c7eb5bc..c99dbb286 100644 --- a/toolsrc/src/tests.files.cpp +++ b/toolsrc/src/tests.files.cpp @@ -10,65 +10,60 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; -namespace UnitTest1 { - class FilesTest : public TestClass { +namespace UnitTest1 +{ + class FilesTest : public TestClass + { using uid = std::uniform_int_distribution; - - std::string get_random_filename() - { - std::random_device rd; - return vcpkg::Strings::b64url_encode(uid{}(rd)); - } - - void create_directory_tree( - vcpkg::Files::Filesystem& fs, - std::uint64_t depth, - const fs::path& base) - { - std::random_device rd; - constexpr auto max_depth = std::uint64_t(3); - const auto width = depth ? uid{0, (max_depth - depth) * 3 / 2}(rd) : 5; - std::error_code ec; - if (width == 0) { - fs.write_contents(base, "", ec); - Assert::IsFalse(bool(ec)); + public: + FilesTest() + { + HKEY key; + const auto status = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock)", 0, KEY_READ, &key); - return; + if (!status) + { + ALLOW_SYMLINKS = false; + std::clog << "Symlinks are not allowed on this system\n"; } - - fs.create_directory(base, ec); - Assert::IsFalse(bool(ec)); - - for (int i = 0; i < width; ++i) { - create_directory_tree(fs, depth + 1, base / get_random_filename()); + else + { + ALLOW_SYMLINKS = true; + RegCloseKey(key); } } - - TEST_METHOD(remove_all) { + + private: + TEST_METHOD(remove_all) + { + auto urbg = get_urbg(0); + fs::path temp_dir; { wchar_t* tmp = static_cast(calloc(32'767, 2)); - if (!GetEnvironmentVariableW(L"TEMP", tmp, 32'767)) { + if (!GetEnvironmentVariableW(L"TEMP", tmp, 32'767)) + { Assert::Fail(L"GetEnvironmentVariable(\"TEMP\") failed"); } temp_dir = tmp; std::string dir_name = "vcpkg-tmp-dir-"; - dir_name += get_random_filename(); + dir_name += get_random_filename(urbg); temp_dir /= dir_name; } auto& fs = vcpkg::Files::get_real_filesystem(); - std::cout << "temp dir is: " << temp_dir << '\n'; + std::clog << "temp dir is: " << temp_dir << '\n'; std::error_code ec; - create_directory_tree(fs, 0, temp_dir); + create_directory_tree(urbg, fs, 0, temp_dir); fs::path fp; fs.remove_all(temp_dir, ec, fp); @@ -76,5 +71,63 @@ namespace UnitTest1 { Assert::IsFalse(fs.exists(temp_dir)); } + + bool ALLOW_SYMLINKS; + + std::mt19937_64 get_urbg(std::uint64_t index) + { + // smallest prime > 2**63 - 1 + return std::mt19937_64{index + 9223372036854775837}; + } + + std::string get_random_filename(std::mt19937_64& urbg) { return vcpkg::Strings::b64url_encode(uid{}(urbg)); } + + void create_directory_tree(std::mt19937_64& urbg, + vcpkg::Files::Filesystem& fs, + std::uint64_t depth, + const fs::path& base) + { + std::random_device rd; + constexpr auto max_depth = std::uint64_t(3); + const auto width = depth ? uid{0, (max_depth - depth) * 3 / 2}(urbg) : 5; + + std::error_code ec; + if (width == 0) + { + // I don't want to move urbg forward conditionally + const auto type = uid{0, 3}(urbg); + if (type == 0 || !ALLOW_SYMLINKS) + { + // 0 is a regular file + fs.write_contents(base, "", ec); + } + else if (type == 1) + { + // 1 is a regular symlink + fs.write_contents(base, "", ec); + Assert::IsFalse(bool(ec)); + fs::path base_link = base; + base_link.append("-link"); + fs::stdfs::create_symlink(base, base_link, ec); + } + else + { + // 2 is a directory symlink + fs::stdfs::create_directory_symlink(".", base, ec); + } + + Assert::IsFalse(bool(ec)); + + return; + } + + fs.create_directory(base, ec); + Assert::IsFalse(bool(ec)); + + for (int i = 0; i < width; ++i) + { + create_directory_tree(urbg, fs, depth + 1, base / get_random_filename(urbg)); + } + } }; } -- cgit v1.2.3 From 65d34c5e55ef30a6572dfb3b79d212196fbadc0d Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Mon, 15 Jul 2019 18:51:03 -0700 Subject: wheeeee more fixes --- toolsrc/src/tests.files.cpp | 64 +++++++++++++++++++++++--------------- toolsrc/src/tests.strings.cpp | 20 ++++++------ toolsrc/src/vcpkg/base/files.cpp | 2 +- toolsrc/src/vcpkg/base/strings.cpp | 23 ++++++-------- 4 files changed, 59 insertions(+), 50 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/tests.files.cpp b/toolsrc/src/tests.files.cpp index c99dbb286..56b0ceac6 100644 --- a/toolsrc/src/tests.files.cpp +++ b/toolsrc/src/tests.files.cpp @@ -4,6 +4,7 @@ #include #include +#include // required for filesystem::create_{directory_}symlink #include #include @@ -21,18 +22,20 @@ namespace UnitTest1 { HKEY key; const auto status = RegOpenKeyExW( - HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock)", 0, KEY_READ, &key); + HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock)", 0, 0, &key); - if (!status) + if (status == ERROR_FILE_NOT_FOUND) { ALLOW_SYMLINKS = false; std::clog << "Symlinks are not allowed on this system\n"; } else { + // if we get a permissions error, we still know that we're in developer mode ALLOW_SYMLINKS = true; - RegCloseKey(key); } + + if (status == ERROR_SUCCESS) RegCloseKey(key); } private: @@ -62,9 +65,9 @@ namespace UnitTest1 std::clog << "temp dir is: " << temp_dir << '\n'; - std::error_code ec; create_directory_tree(urbg, fs, 0, temp_dir); + std::error_code ec; fs::path fp; fs.remove_all(temp_dir, ec, fp); Assert::IsFalse(bool(ec)); @@ -80,7 +83,7 @@ namespace UnitTest1 return std::mt19937_64{index + 9223372036854775837}; } - std::string get_random_filename(std::mt19937_64& urbg) { return vcpkg::Strings::b64url_encode(uid{}(urbg)); } + std::string get_random_filename(std::mt19937_64& urbg) { return vcpkg::Strings::b32_encode(uid{}(urbg)); } void create_directory_tree(std::mt19937_64& urbg, vcpkg::Files::Filesystem& fs, @@ -88,46 +91,57 @@ namespace UnitTest1 const fs::path& base) { std::random_device rd; - constexpr auto max_depth = std::uint64_t(3); - const auto width = depth ? uid{0, (max_depth - depth) * 3 / 2}(urbg) : 5; + constexpr std::uint64_t max_depth = 5; + constexpr std::uint64_t width = 5; + const auto type = depth < max_depth ? uid{0, 9}(urbg) : uid{7, 9}(urbg); + + // 0 <= type < 7 : directory + // 7 = type : regular + // 8 = type : regular symlink (regular file if !ALLOW_SYMLINKS) + // 9 = type : directory symlink (^^) std::error_code ec; - if (width == 0) + if (type >= 7) { // I don't want to move urbg forward conditionally - const auto type = uid{0, 3}(urbg); - if (type == 0 || !ALLOW_SYMLINKS) + if (type == 7 || !ALLOW_SYMLINKS) { - // 0 is a regular file + // regular file fs.write_contents(base, "", ec); } - else if (type == 1) + else if (type == 8) { - // 1 is a regular symlink + // regular symlink fs.write_contents(base, "", ec); Assert::IsFalse(bool(ec)); - fs::path base_link = base; - base_link.append("-link"); - fs::stdfs::create_symlink(base, base_link, ec); + const std::filesystem::path basep = base.native(); + auto basep_link = basep; + basep_link.replace_filename(basep.filename().native() + L"-link"); + std::filesystem::create_symlink(basep, basep_link, ec); } else { - // 2 is a directory symlink - fs::stdfs::create_directory_symlink(".", base, ec); + // directory symlink + std::filesystem::path basep = base.native(); + std::filesystem::create_directory_symlink(basep / "..", basep, ec); } Assert::IsFalse(bool(ec)); - return; } - - fs.create_directory(base, ec); - Assert::IsFalse(bool(ec)); - - for (int i = 0; i < width; ++i) + else { - create_directory_tree(urbg, fs, depth + 1, base / get_random_filename(urbg)); + // directory + fs.create_directory(base, ec); + Assert::IsFalse(bool(ec)); + + for (int i = 0; i < width; ++i) + { + create_directory_tree(urbg, fs, depth + 1, base / get_random_filename(urbg)); + } + } + } }; } diff --git a/toolsrc/src/tests.strings.cpp b/toolsrc/src/tests.strings.cpp index 14b449e86..6301ea05d 100644 --- a/toolsrc/src/tests.strings.cpp +++ b/toolsrc/src/tests.strings.cpp @@ -16,21 +16,21 @@ namespace UnitTest1 { std::vector> map; - map.emplace_back(0, "AAAAAAAAAAA"); - map.emplace_back(1, "BAAAAAAAAAA"); + map.emplace_back(0, "AAAAAAAAAAAAA"); + map.emplace_back(1, "BAAAAAAAAAAAA"); - map.emplace_back(u64(1) << 32, "AAAAAEAAAAA"); - map.emplace_back((u64(1) << 32) + 1, "BAAAAEAAAAA"); + map.emplace_back(u64(1) << 32, "AAAAAAEAAAAAA"); + map.emplace_back((u64(1) << 32) + 1, "BAAAAAEAAAAAA"); - map.emplace_back(0xE4D0'1065'D11E'0229, "pIgHRXGEQTO"); - map.emplace_back(0xA626'FE45'B135'07FF, "_fQNxWk_mYK"); - map.emplace_back(0xEE36'D228'0C31'D405, "FQdMMgi024O"); - map.emplace_back(0x1405'64E7'FE7E'A88C, "Miqf-fOZFQB"); - map.emplace_back(0xFFFF'FFFF'FFFF'FFFF, "__________P"); + map.emplace_back(0xE4D0'1065'D11E'0229, "JRA4RIXMQAUJO"); + map.emplace_back(0xA626'FE45'B135'07FF, "77BKTYWI6XJMK"); + map.emplace_back(0xEE36'D228'0C31'D405, "FAVDDGAFSWN4O"); + map.emplace_back(0x1405'64E7'FE7E'A88C, "MEK5H774ELBIB"); + map.emplace_back(0xFFFF'FFFF'FFFF'FFFF, "777777777777P"); std::string result; for (const auto& pr : map) { - result = vcpkg::Strings::b64url_encode(pr.first); + result = vcpkg::Strings::b32_encode(pr.first); Assert::AreEqual(result, pr.second); } } diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index e40822705..6c6945e44 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -423,7 +423,7 @@ namespace vcpkg::Files { std::error_code ec; - const auto tmp_name = Strings::b64url_encode(info.index++); + const auto tmp_name = Strings::b32_encode(info.index++); const auto tmp_path = info.tmp_directory / tmp_name; fs::stdfs::rename(current_path, tmp_path, ec); diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp index ade4384a9..7970e1b46 100644 --- a/toolsrc/src/vcpkg/base/strings.cpp +++ b/toolsrc/src/vcpkg/base/strings.cpp @@ -294,26 +294,21 @@ namespace vcpkg::Strings namespace { template - std::string b64url_encode_implementation(Integral x) + std::string b32_encode_implementation(Integral x) { static_assert(std::is_integral::value, "b64url_encode must take an integer type"); using Unsigned = std::make_unsigned_t; auto value = static_cast(x); - // 64 values, plus the implicit \0 - constexpr static char map[65] = - /* 0123456789ABCDEF */ - /*0*/ "ABCDEFGHIJKLMNOP" - /*1*/ "QRSTUVWXYZabcdef" - /*2*/ "ghijklmnopqrstuv" - /*3*/ "wxyz0123456789-_"; + // 32 values, plus the implicit \0 + constexpr static char map[33] = "ABCDEFGHIJKLMNOP" "QRSTUVWXYZ234567"; - // log2(64) - constexpr static int shift = 6; - // 64 - 1 - constexpr static auto mask = 63; + // log2(32) + constexpr static int shift = 5; + // 32 - 1 + constexpr static auto mask = 31; - // ceiling(bitsize(Integral) / log2(64)) + // ceiling(bitsize(Integral) / log2(32)) constexpr static auto result_size = (sizeof(value) * 8 + shift - 1) / shift; std::string result; @@ -329,6 +324,6 @@ namespace vcpkg::Strings } } - std::string b64url_encode(std::uint64_t x) noexcept { return b64url_encode_implementation(x); } + std::string b32_encode(std::uint64_t x) noexcept { return b32_encode_implementation(x); } } -- cgit v1.2.3 From f599f19bad8fd97b60f41063537be2e4ecef3ca7 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Wed, 17 Jul 2019 18:58:23 -0700 Subject: tests.files.cpp:create_directory_tree -- change magic numbers to names --- toolsrc/src/tests.files.cpp | 79 ++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 36 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/tests.files.cpp b/toolsrc/src/tests.files.cpp index 56b0ceac6..e60662fd9 100644 --- a/toolsrc/src/tests.files.cpp +++ b/toolsrc/src/tests.files.cpp @@ -93,45 +93,31 @@ namespace UnitTest1 std::random_device rd; constexpr std::uint64_t max_depth = 5; constexpr std::uint64_t width = 5; - const auto type = depth < max_depth ? uid{0, 9}(urbg) : uid{7, 9}(urbg); - // 0 <= type < 7 : directory - // 7 = type : regular - // 8 = type : regular symlink (regular file if !ALLOW_SYMLINKS) - // 9 = type : directory symlink (^^) - - std::error_code ec; - if (type >= 7) - { - // I don't want to move urbg forward conditionally - if (type == 7 || !ALLOW_SYMLINKS) - { - // regular file - fs.write_contents(base, "", ec); - } - else if (type == 8) - { - // regular symlink - fs.write_contents(base, "", ec); - Assert::IsFalse(bool(ec)); - const std::filesystem::path basep = base.native(); - auto basep_link = basep; - basep_link.replace_filename(basep.filename().native() + L"-link"); - std::filesystem::create_symlink(basep, basep_link, ec); - } - else - { - // directory symlink - std::filesystem::path basep = base.native(); - std::filesystem::create_directory_symlink(basep / "..", basep, ec); - } - - Assert::IsFalse(bool(ec)); + // we want ~70% of our "files" to be directories, and then a third + // each of the remaining ~30% to be regular files, directory symlinks, + // and regular symlinks + constexpr std::uint64_t directory_min_tag = 0; + constexpr std::uint64_t directory_max_tag = 6; + constexpr std::uint64_t regular_file_tag = 7; + constexpr std::uint64_t regular_symlink_tag = 8; + constexpr std::uint64_t directory_symlink_tag = 9; + + // if we're at the max depth, we only want to build non-directories + std::uint64_t file_type; + if (depth < max_depth) { + file_type = uid{directory_min_tag, regular_symlink_tag}(urbg); + } else { + file_type = uid{regular_file_tag, regular_symlink_tag}(urbg); + } + if (!ALLOW_SYMLINKS && file_type > regular_file_tag) { + file_type = regular_file_tag; } - else + + std::error_code ec; + if (type <= directory_max_tag) { - // directory fs.create_directory(base, ec); Assert::IsFalse(bool(ec)); @@ -139,9 +125,30 @@ namespace UnitTest1 { create_directory_tree(urbg, fs, depth + 1, base / get_random_filename(urbg)); } - + } + else if (type == regular_file_tag) + { + // regular file + fs.write_contents(base, "", ec); + } + else if (type == regular_symlink_tag) + { + // regular symlink + fs.write_contents(base, "", ec); + Assert::IsFalse(bool(ec)); + const std::filesystem::path basep = base.native(); + auto basep_link = basep; + basep_link.replace_filename(basep.filename().native() + L"-link"); + std::filesystem::create_symlink(basep, basep_link, ec); + } + else // type == directory_symlink_tag + { + // directory symlink + std::filesystem::path basep = base.native(); + std::filesystem::create_directory_symlink(basep / "..", basep, ec); } + Assert::IsFalse(bool(ec)); } }; } -- cgit v1.2.3 From fddebb75da034752fb267ba121497ba58157bb79 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Thu, 18 Jul 2019 16:40:52 -0700 Subject: clang-format all the things --- toolsrc/src/tests.files.cpp | 16 ++++++++++------ toolsrc/src/tests.strings.cpp | 9 ++++++--- toolsrc/src/vcpkg/base/strings.cpp | 3 ++- toolsrc/src/vcpkg/build.cpp | 15 ++++++--------- 4 files changed, 24 insertions(+), 19 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/tests.files.cpp b/toolsrc/src/tests.files.cpp index e60662fd9..482675c34 100644 --- a/toolsrc/src/tests.files.cpp +++ b/toolsrc/src/tests.files.cpp @@ -3,8 +3,8 @@ #include #include -#include #include // required for filesystem::create_{directory_}symlink +#include #include #include @@ -31,11 +31,11 @@ namespace UnitTest1 } else { - // if we get a permissions error, we still know that we're in developer mode + // if we get a permissions error, we still know that we're in developer mode ALLOW_SYMLINKS = true; } - if (status == ERROR_SUCCESS) RegCloseKey(key); + if (status == ERROR_SUCCESS) RegCloseKey(key); } private: @@ -105,13 +105,17 @@ namespace UnitTest1 // if we're at the max depth, we only want to build non-directories std::uint64_t file_type; - if (depth < max_depth) { + if (depth < max_depth) + { file_type = uid{directory_min_tag, regular_symlink_tag}(urbg); - } else { + } + else + { file_type = uid{regular_file_tag, regular_symlink_tag}(urbg); } - if (!ALLOW_SYMLINKS && file_type > regular_file_tag) { + if (!ALLOW_SYMLINKS && file_type > regular_file_tag) + { file_type = regular_file_tag; } diff --git a/toolsrc/src/tests.strings.cpp b/toolsrc/src/tests.strings.cpp index 6301ea05d..f541a203d 100644 --- a/toolsrc/src/tests.strings.cpp +++ b/toolsrc/src/tests.strings.cpp @@ -8,8 +8,10 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; -namespace UnitTest1 { - class StringsTest : public TestClass { +namespace UnitTest1 +{ + class StringsTest : public TestClass + { TEST_METHOD(b64url_encode) { using u64 = std::uint64_t; @@ -29,7 +31,8 @@ namespace UnitTest1 { map.emplace_back(0xFFFF'FFFF'FFFF'FFFF, "777777777777P"); std::string result; - for (const auto& pr : map) { + for (const auto& pr : map) + { result = vcpkg::Strings::b32_encode(pr.first); Assert::AreEqual(result, pr.second); } diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp index 7970e1b46..46e78a363 100644 --- a/toolsrc/src/vcpkg/base/strings.cpp +++ b/toolsrc/src/vcpkg/base/strings.cpp @@ -301,7 +301,8 @@ namespace vcpkg::Strings auto value = static_cast(x); // 32 values, plus the implicit \0 - constexpr static char map[33] = "ABCDEFGHIJKLMNOP" "QRSTUVWXYZ234567"; + constexpr static char map[33] = "ABCDEFGHIJKLMNOP" + "QRSTUVWXYZ234567"; // log2(32) constexpr static int shift = 5; diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp index c50acce7e..66d8dae2c 100644 --- a/toolsrc/src/vcpkg/build.cpp +++ b/toolsrc/src/vcpkg/build.cpp @@ -363,8 +363,7 @@ namespace vcpkg::Build const Triplet& triplet = spec.triplet(); const auto& triplet_file_path = paths.get_triplet_file_path(spec.triplet()).u8string(); - if (!Strings::case_insensitive_ascii_starts_with(triplet_file_path, - paths.triplets.u8string())) + if (!Strings::case_insensitive_ascii_starts_with(triplet_file_path, paths.triplets.u8string())) { System::printf("-- Loading triplet configuration from: %s\n", triplet_file_path); } @@ -431,12 +430,11 @@ namespace vcpkg::Build } command.append(cmd_launch_cmake); const auto timer = Chrono::ElapsedTimer::create_started(); - const int return_code = System::cmd_execute_clean( - command, - {} + const int return_code = System::cmd_execute_clean(command, + {} #ifdef _WIN32 - , - powershell_exe_path.parent_path().u8string() + ";" + , + powershell_exe_path.parent_path().u8string() + ";" #endif ); const auto buildtimeus = timer.microseconds(); @@ -1020,8 +1018,7 @@ namespace vcpkg::Build hash += "-"; hash += Hash::get_file_hash(fs, *p, "SHA1"); } - else if (pre_build_info.cmake_system_name.empty() || - pre_build_info.cmake_system_name == "WindowsStore") + else if (pre_build_info.cmake_system_name.empty() || pre_build_info.cmake_system_name == "WindowsStore") { hash += "-"; hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "windows.cmake", "SHA1"); -- cgit v1.2.3 From c55ea0a0d5229b9dd79aa8ea888f6c0408f9e5dd Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Fri, 19 Jul 2019 12:56:24 -0700 Subject: switch to new test framework --- toolsrc/src/tests.files.cpp | 158 ------------------------------------ toolsrc/src/tests.strings.cpp | 41 ---------- toolsrc/src/vcpkg-tests/files.cpp | 124 ++++++++++++++++++++++++++++ toolsrc/src/vcpkg-tests/strings.cpp | 33 ++++++++ toolsrc/src/vcpkg-tests/util.cpp | 54 ++++++++++++ toolsrc/src/vcpkg/build.cpp | 48 ----------- 6 files changed, 211 insertions(+), 247 deletions(-) delete mode 100644 toolsrc/src/tests.files.cpp delete mode 100644 toolsrc/src/tests.strings.cpp create mode 100644 toolsrc/src/vcpkg-tests/files.cpp create mode 100644 toolsrc/src/vcpkg-tests/strings.cpp (limited to 'toolsrc/src') diff --git a/toolsrc/src/tests.files.cpp b/toolsrc/src/tests.files.cpp deleted file mode 100644 index 482675c34..000000000 --- a/toolsrc/src/tests.files.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include "tests.pch.h" - -#include -#include - -#include // required for filesystem::create_{directory_}symlink -#include -#include - -#include - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -namespace UnitTest1 -{ - class FilesTest : public TestClass - { - using uid = std::uniform_int_distribution; - - public: - FilesTest() - { - HKEY key; - const auto status = RegOpenKeyExW( - HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock)", 0, 0, &key); - - if (status == ERROR_FILE_NOT_FOUND) - { - ALLOW_SYMLINKS = false; - std::clog << "Symlinks are not allowed on this system\n"; - } - else - { - // if we get a permissions error, we still know that we're in developer mode - ALLOW_SYMLINKS = true; - } - - if (status == ERROR_SUCCESS) RegCloseKey(key); - } - - private: - TEST_METHOD(remove_all) - { - auto urbg = get_urbg(0); - - fs::path temp_dir; - - { - wchar_t* tmp = static_cast(calloc(32'767, 2)); - - if (!GetEnvironmentVariableW(L"TEMP", tmp, 32'767)) - { - Assert::Fail(L"GetEnvironmentVariable(\"TEMP\") failed"); - } - - temp_dir = tmp; - - std::string dir_name = "vcpkg-tmp-dir-"; - dir_name += get_random_filename(urbg); - - temp_dir /= dir_name; - } - - auto& fs = vcpkg::Files::get_real_filesystem(); - - std::clog << "temp dir is: " << temp_dir << '\n'; - - create_directory_tree(urbg, fs, 0, temp_dir); - - std::error_code ec; - fs::path fp; - fs.remove_all(temp_dir, ec, fp); - Assert::IsFalse(bool(ec)); - - Assert::IsFalse(fs.exists(temp_dir)); - } - - bool ALLOW_SYMLINKS; - - std::mt19937_64 get_urbg(std::uint64_t index) - { - // smallest prime > 2**63 - 1 - return std::mt19937_64{index + 9223372036854775837}; - } - - std::string get_random_filename(std::mt19937_64& urbg) { return vcpkg::Strings::b32_encode(uid{}(urbg)); } - - void create_directory_tree(std::mt19937_64& urbg, - vcpkg::Files::Filesystem& fs, - std::uint64_t depth, - const fs::path& base) - { - std::random_device rd; - constexpr std::uint64_t max_depth = 5; - constexpr std::uint64_t width = 5; - - // we want ~70% of our "files" to be directories, and then a third - // each of the remaining ~30% to be regular files, directory symlinks, - // and regular symlinks - constexpr std::uint64_t directory_min_tag = 0; - constexpr std::uint64_t directory_max_tag = 6; - constexpr std::uint64_t regular_file_tag = 7; - constexpr std::uint64_t regular_symlink_tag = 8; - constexpr std::uint64_t directory_symlink_tag = 9; - - // if we're at the max depth, we only want to build non-directories - std::uint64_t file_type; - if (depth < max_depth) - { - file_type = uid{directory_min_tag, regular_symlink_tag}(urbg); - } - else - { - file_type = uid{regular_file_tag, regular_symlink_tag}(urbg); - } - - if (!ALLOW_SYMLINKS && file_type > regular_file_tag) - { - file_type = regular_file_tag; - } - - std::error_code ec; - if (type <= directory_max_tag) - { - fs.create_directory(base, ec); - Assert::IsFalse(bool(ec)); - - for (int i = 0; i < width; ++i) - { - create_directory_tree(urbg, fs, depth + 1, base / get_random_filename(urbg)); - } - } - else if (type == regular_file_tag) - { - // regular file - fs.write_contents(base, "", ec); - } - else if (type == regular_symlink_tag) - { - // regular symlink - fs.write_contents(base, "", ec); - Assert::IsFalse(bool(ec)); - const std::filesystem::path basep = base.native(); - auto basep_link = basep; - basep_link.replace_filename(basep.filename().native() + L"-link"); - std::filesystem::create_symlink(basep, basep_link, ec); - } - else // type == directory_symlink_tag - { - // directory symlink - std::filesystem::path basep = base.native(); - std::filesystem::create_directory_symlink(basep / "..", basep, ec); - } - - Assert::IsFalse(bool(ec)); - } - }; -} diff --git a/toolsrc/src/tests.strings.cpp b/toolsrc/src/tests.strings.cpp deleted file mode 100644 index f541a203d..000000000 --- a/toolsrc/src/tests.strings.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "tests.pch.h" - -#include - -#include -#include -#include - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -namespace UnitTest1 -{ - class StringsTest : public TestClass - { - TEST_METHOD(b64url_encode) - { - using u64 = std::uint64_t; - - std::vector> map; - - map.emplace_back(0, "AAAAAAAAAAAAA"); - map.emplace_back(1, "BAAAAAAAAAAAA"); - - map.emplace_back(u64(1) << 32, "AAAAAAEAAAAAA"); - map.emplace_back((u64(1) << 32) + 1, "BAAAAAEAAAAAA"); - - map.emplace_back(0xE4D0'1065'D11E'0229, "JRA4RIXMQAUJO"); - map.emplace_back(0xA626'FE45'B135'07FF, "77BKTYWI6XJMK"); - map.emplace_back(0xEE36'D228'0C31'D405, "FAVDDGAFSWN4O"); - map.emplace_back(0x1405'64E7'FE7E'A88C, "MEK5H774ELBIB"); - map.emplace_back(0xFFFF'FFFF'FFFF'FFFF, "777777777777P"); - - std::string result; - for (const auto& pr : map) - { - result = vcpkg::Strings::b32_encode(pr.first); - Assert::AreEqual(result, pr.second); - } - } - }; -} diff --git a/toolsrc/src/vcpkg-tests/files.cpp b/toolsrc/src/vcpkg-tests/files.cpp new file mode 100644 index 000000000..d2edffcff --- /dev/null +++ b/toolsrc/src/vcpkg-tests/files.cpp @@ -0,0 +1,124 @@ +#include +#include + +#include +#include + +#include // required for filesystem::create_{directory_}symlink +#include +#include + +#include + +using vcpkg::Test::SYMLINKS_ALLOWED; +using vcpkg::Test::TEMPORARY_DIRECTORY; + +namespace +{ + using uid = std::uniform_int_distribution; + + std::mt19937_64 get_urbg(std::uint64_t index) + { + // smallest prime > 2**63 - 1 + return std::mt19937_64{index + 9223372036854775837}; + } + + std::string get_random_filename(std::mt19937_64& urbg) { return vcpkg::Strings::b32_encode(uid{}(urbg)); } + + void create_directory_tree(std::mt19937_64& urbg, + vcpkg::Files::Filesystem& fs, + std::uint64_t depth, + const fs::path& base) + { + std::random_device rd; + constexpr std::uint64_t max_depth = 5; + constexpr std::uint64_t width = 5; + + // we want ~70% of our "files" to be directories, and then a third + // each of the remaining ~30% to be regular files, directory symlinks, + // and regular symlinks + constexpr std::uint64_t directory_min_tag = 0; + constexpr std::uint64_t directory_max_tag = 6; + constexpr std::uint64_t regular_file_tag = 7; + constexpr std::uint64_t regular_symlink_tag = 8; + constexpr std::uint64_t directory_symlink_tag = 9; + + // if we're at the max depth, we only want to build non-directories + std::uint64_t file_type; + if (depth < max_depth) + { + file_type = uid{directory_min_tag, regular_symlink_tag}(urbg); + } + else + { + file_type = uid{regular_file_tag, regular_symlink_tag}(urbg); + } + + if (!SYMLINKS_ALLOWED && file_type > regular_file_tag) + { + file_type = regular_file_tag; + } + + std::error_code ec; + if (file_type <= directory_max_tag) + { + fs.create_directory(base, ec); + if (ec) { + INFO("File that failed: " << base); + REQUIRE_FALSE(ec); + } + + for (int i = 0; i < width; ++i) + { + create_directory_tree(urbg, fs, depth + 1, base / get_random_filename(urbg)); + } + } + else if (file_type == regular_file_tag) + { + // regular file + fs.write_contents(base, "", ec); + } + else if (file_type == regular_symlink_tag) + { + // regular symlink + fs.write_contents(base, "", ec); + REQUIRE_FALSE(ec); + const std::filesystem::path basep = base.native(); + auto basep_link = basep; + basep_link.replace_filename(basep.filename().native() + L"-link"); + std::filesystem::create_symlink(basep, basep_link, ec); + } + else // type == directory_symlink_tag + { + // directory symlink + std::filesystem::path basep = base.native(); + std::filesystem::create_directory_symlink(basep / "..", basep, ec); + } + + REQUIRE_FALSE(ec); + } +} + +TEST_CASE ("remove all", "[files]") +{ + auto urbg = get_urbg(0); + + fs::path temp_dir = TEMPORARY_DIRECTORY / get_random_filename(urbg); + + auto& fs = vcpkg::Files::get_real_filesystem(); + + std::error_code ec; + fs.create_directory(TEMPORARY_DIRECTORY, ec); + + REQUIRE_FALSE(ec); + + INFO("temp dir is: " << temp_dir); + + create_directory_tree(urbg, fs, 0, temp_dir); + + fs::path fp; + fs.remove_all(temp_dir, ec, fp); + REQUIRE_FALSE(ec); + + REQUIRE_FALSE(fs.exists(temp_dir)); +} diff --git a/toolsrc/src/vcpkg-tests/strings.cpp b/toolsrc/src/vcpkg-tests/strings.cpp new file mode 100644 index 000000000..3168a7c95 --- /dev/null +++ b/toolsrc/src/vcpkg-tests/strings.cpp @@ -0,0 +1,33 @@ +#include + +#include + +#include +#include +#include + +TEST_CASE ("b32 encoding", "[strings]") +{ + using u64 = std::uint64_t; + + std::vector> map; + + map.emplace_back(0, "AAAAAAAAAAAAA"); + map.emplace_back(1, "BAAAAAAAAAAAA"); + + map.emplace_back(u64(1) << 32, "AAAAAAEAAAAAA"); + map.emplace_back((u64(1) << 32) + 1, "BAAAAAEAAAAAA"); + + map.emplace_back(0xE4D0'1065'D11E'0229, "JRA4RIXMQAUJO"); + map.emplace_back(0xA626'FE45'B135'07FF, "77BKTYWI6XJMK"); + map.emplace_back(0xEE36'D228'0C31'D405, "FAVDDGAFSWN4O"); + map.emplace_back(0x1405'64E7'FE7E'A88C, "MEK5H774ELBIB"); + map.emplace_back(0xFFFF'FFFF'FFFF'FFFF, "777777777777P"); + + std::string result; + for (const auto& pr : map) + { + result = vcpkg::Strings::b32_encode(pr.first); + REQUIRE(vcpkg::Strings::b32_encode(pr.first) == pr.second); + } +} diff --git a/toolsrc/src/vcpkg-tests/util.cpp b/toolsrc/src/vcpkg-tests/util.cpp index 54102f1aa..f14628379 100644 --- a/toolsrc/src/vcpkg-tests/util.cpp +++ b/toolsrc/src/vcpkg-tests/util.cpp @@ -1,10 +1,16 @@ #include #include +#include #include +#include #include +#if defined(_WIN32) +#include +#endif + namespace vcpkg::Test { std::unique_ptr make_status_pgh(const char* name, @@ -44,4 +50,52 @@ namespace vcpkg::Test return m_ret.value_or_exit(VCPKG_LINE_INFO); } + + #if defined(_WIN32) + + static bool system_allows_symlinks() { + HKEY key; + bool allow_symlinks = true; + + const auto status = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock)", 0, 0, &key); + + if (status == ERROR_FILE_NOT_FOUND) + { + allow_symlinks = false; + std::clog << "Symlinks are not allowed on this system\n"; + } + + if (status == ERROR_SUCCESS) RegCloseKey(key); + + return allow_symlinks; + } + + static fs::path internal_temporary_directory() { + wchar_t* tmp = static_cast(std::calloc(32'767, 2)); + + if (!GetEnvironmentVariableW(L"TEMP", tmp, 32'767)) { + std::cerr << "No temporary directory found.\n"; + std::abort(); + } + + fs::path result = tmp; + std::free(tmp); + + return result / L"vcpkg-test"; + } + + #else + + constexpr static bool system_allows_symlinks() { + return true; + } + static fs::path internal_temporary_directory() { + return "/tmp/vcpkg-test"; + } + + #endif + + const bool SYMLINKS_ALLOWED = system_allows_symlinks(); + const fs::path TEMPORARY_DIRECTORY = internal_temporary_directory(); } diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp index d9305b271..235adb819 100644 --- a/toolsrc/src/vcpkg/build.cpp +++ b/toolsrc/src/vcpkg/build.cpp @@ -1097,56 +1097,8 @@ namespace vcpkg::Build } } -<<<<<<< HEAD - pre_build_info.triplet_abi_tag = [&]() { - const auto& fs = paths.get_filesystem(); - static std::map s_hash_cache; - - auto it_hash = s_hash_cache.find(triplet_file_path); - if (it_hash != s_hash_cache.end()) - { - return it_hash->second; - } - auto hash = Hash::get_file_hash(fs, triplet_file_path, "SHA1"); - - if (auto p = pre_build_info.external_toolchain_file.get()) - { - hash += "-"; - hash += Hash::get_file_hash(fs, *p, "SHA1"); - } - else if (pre_build_info.cmake_system_name.empty() || pre_build_info.cmake_system_name == "WindowsStore") - { - hash += "-"; - hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "windows.cmake", "SHA1"); - } - else if (pre_build_info.cmake_system_name == "Linux") - { - hash += "-"; - hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "linux.cmake", "SHA1"); - } - else if (pre_build_info.cmake_system_name == "Darwin") - { - hash += "-"; - hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "osx.cmake", "SHA1"); - } - else if (pre_build_info.cmake_system_name == "FreeBSD") - { - hash += "-"; - hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "freebsd.cmake", "SHA1"); - } - else if (pre_build_info.cmake_system_name == "Android") - { - hash += "-"; - hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "android.cmake", "SHA1"); - } - - s_hash_cache.emplace(triplet_file_path, hash); - return hash; - }(); -======= pre_build_info.triplet_abi_tag = get_triplet_abi(paths, pre_build_info, triplet); ->>>>>>> trunk return pre_build_info; } -- cgit v1.2.3 From 0d8bba52e4c0a861b25f9d32006bfde9b749e09f Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Fri, 19 Jul 2019 23:20:28 -0700 Subject: allow tests to run on older standard libraries --- toolsrc/src/vcpkg-test/arguments.cpp | 109 +++ toolsrc/src/vcpkg-test/catch.cpp | 11 + toolsrc/src/vcpkg-test/chrono.cpp | 34 + toolsrc/src/vcpkg-test/dependencies.cpp | 28 + toolsrc/src/vcpkg-test/files.cpp | 121 +++ toolsrc/src/vcpkg-test/paragraph.cpp | 445 +++++++++ toolsrc/src/vcpkg-test/plan.cpp | 1241 ++++++++++++++++++++++++++ toolsrc/src/vcpkg-test/specifier.cpp | 134 +++ toolsrc/src/vcpkg-test/statusparagraphs.cpp | 110 +++ toolsrc/src/vcpkg-test/strings.cpp | 33 + toolsrc/src/vcpkg-test/supports.cpp | 79 ++ toolsrc/src/vcpkg-test/update.cpp | 102 +++ toolsrc/src/vcpkg-test/util.cpp | 158 ++++ toolsrc/src/vcpkg-tests/arguments.cpp | 109 --- toolsrc/src/vcpkg-tests/catch.cpp | 11 - toolsrc/src/vcpkg-tests/chrono.cpp | 34 - toolsrc/src/vcpkg-tests/dependencies.cpp | 28 - toolsrc/src/vcpkg-tests/files.cpp | 124 --- toolsrc/src/vcpkg-tests/paragraph.cpp | 445 --------- toolsrc/src/vcpkg-tests/plan.cpp | 1241 -------------------------- toolsrc/src/vcpkg-tests/specifier.cpp | 134 --- toolsrc/src/vcpkg-tests/statusparagraphs.cpp | 110 --- toolsrc/src/vcpkg-tests/strings.cpp | 33 - toolsrc/src/vcpkg-tests/supports.cpp | 79 -- toolsrc/src/vcpkg-tests/update.cpp | 102 --- toolsrc/src/vcpkg-tests/util.cpp | 101 --- 26 files changed, 2605 insertions(+), 2551 deletions(-) create mode 100644 toolsrc/src/vcpkg-test/arguments.cpp create mode 100644 toolsrc/src/vcpkg-test/catch.cpp create mode 100644 toolsrc/src/vcpkg-test/chrono.cpp create mode 100644 toolsrc/src/vcpkg-test/dependencies.cpp create mode 100644 toolsrc/src/vcpkg-test/files.cpp create mode 100644 toolsrc/src/vcpkg-test/paragraph.cpp create mode 100644 toolsrc/src/vcpkg-test/plan.cpp create mode 100644 toolsrc/src/vcpkg-test/specifier.cpp create mode 100644 toolsrc/src/vcpkg-test/statusparagraphs.cpp create mode 100644 toolsrc/src/vcpkg-test/strings.cpp create mode 100644 toolsrc/src/vcpkg-test/supports.cpp create mode 100644 toolsrc/src/vcpkg-test/update.cpp create mode 100644 toolsrc/src/vcpkg-test/util.cpp delete mode 100644 toolsrc/src/vcpkg-tests/arguments.cpp delete mode 100644 toolsrc/src/vcpkg-tests/catch.cpp delete mode 100644 toolsrc/src/vcpkg-tests/chrono.cpp delete mode 100644 toolsrc/src/vcpkg-tests/dependencies.cpp delete mode 100644 toolsrc/src/vcpkg-tests/files.cpp delete mode 100644 toolsrc/src/vcpkg-tests/paragraph.cpp delete mode 100644 toolsrc/src/vcpkg-tests/plan.cpp delete mode 100644 toolsrc/src/vcpkg-tests/specifier.cpp delete mode 100644 toolsrc/src/vcpkg-tests/statusparagraphs.cpp delete mode 100644 toolsrc/src/vcpkg-tests/strings.cpp delete mode 100644 toolsrc/src/vcpkg-tests/supports.cpp delete mode 100644 toolsrc/src/vcpkg-tests/update.cpp delete mode 100644 toolsrc/src/vcpkg-tests/util.cpp (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg-test/arguments.cpp b/toolsrc/src/vcpkg-test/arguments.cpp new file mode 100644 index 000000000..3fe5fa420 --- /dev/null +++ b/toolsrc/src/vcpkg-test/arguments.cpp @@ -0,0 +1,109 @@ +#include + +#include + +#include + +using vcpkg::CommandSetting; +using vcpkg::CommandStructure; +using vcpkg::CommandSwitch; +using vcpkg::VcpkgCmdArguments; + +TEST_CASE ("VcpkgCmdArguments from lowercase argument sequence", "[arguments]") +{ + std::vector t = {"--vcpkg-root", + "C:\\vcpkg", + "--scripts-root=C:\\scripts", + "--debug", + "--sendmetrics", + "--printmetrics", + "--overlay-ports=C:\\ports1", + "--overlay-ports=C:\\ports2", + "--overlay-triplets=C:\\tripletsA", + "--overlay-triplets=C:\\tripletsB"}; + auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); + + REQUIRE(*v.vcpkg_root_dir == "C:\\vcpkg"); + REQUIRE(*v.scripts_root_dir == "C:\\scripts"); + REQUIRE(v.debug); + REQUIRE(*v.debug.get()); + REQUIRE(v.sendmetrics); + REQUIRE(*v.sendmetrics.get()); + REQUIRE(v.printmetrics); + REQUIRE(*v.printmetrics.get()); + + REQUIRE(v.overlay_ports->size() == 2); + REQUIRE(v.overlay_ports->at(0) == "C:\\ports1"); + REQUIRE(v.overlay_ports->at(1) == "C:\\ports2"); + + REQUIRE(v.overlay_triplets->size() == 2); + REQUIRE(v.overlay_triplets->at(0) == "C:\\tripletsA"); + REQUIRE(v.overlay_triplets->at(1) == "C:\\tripletsB"); +} + +TEST_CASE ("VcpkgCmdArguments from uppercase argument sequence", "[arguments]") +{ + std::vector t = {"--VCPKG-ROOT", + "C:\\vcpkg", + "--SCRIPTS-ROOT=C:\\scripts", + "--DEBUG", + "--SENDMETRICS", + "--PRINTMETRICS", + "--OVERLAY-PORTS=C:\\ports1", + "--OVERLAY-PORTS=C:\\ports2", + "--OVERLAY-TRIPLETS=C:\\tripletsA", + "--OVERLAY-TRIPLETS=C:\\tripletsB"}; + auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); + + REQUIRE(*v.vcpkg_root_dir == "C:\\vcpkg"); + REQUIRE(*v.scripts_root_dir == "C:\\scripts"); + REQUIRE(v.debug); + REQUIRE(*v.debug.get()); + REQUIRE(v.sendmetrics); + REQUIRE(*v.sendmetrics.get()); + REQUIRE(v.printmetrics); + REQUIRE(*v.printmetrics.get()); + + REQUIRE(v.overlay_ports->size() == 2); + REQUIRE(v.overlay_ports->at(0) == "C:\\ports1"); + REQUIRE(v.overlay_ports->at(1) == "C:\\ports2"); + + REQUIRE(v.overlay_triplets->size() == 2); + REQUIRE(v.overlay_triplets->at(0) == "C:\\tripletsA"); + REQUIRE(v.overlay_triplets->at(1) == "C:\\tripletsB"); +} + +TEST_CASE ("VcpkgCmdArguments from argument sequence with valued options", "[arguments]") +{ + SECTION ("case 1") + { + std::array settings = {{{"--a", ""}}}; + CommandStructure cmdstruct = {"", 0, SIZE_MAX, {{}, settings}, nullptr}; + + std::vector t = {"--a=b", "command", "argument"}; + auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); + auto opts = v.parse_arguments(cmdstruct); + + REQUIRE(opts.settings["--a"] == "b"); + REQUIRE(v.command_arguments.size() == 1); + REQUIRE(v.command_arguments[0] == "argument"); + REQUIRE(v.command == "command"); + } + + SECTION ("case 2") + { + std::array switches = {{{"--a", ""}, {"--c", ""}}}; + std::array settings = {{{"--b", ""}, {"--d", ""}}}; + CommandStructure cmdstruct = {"", 0, SIZE_MAX, {switches, settings}, nullptr}; + + std::vector t = {"--a", "--b=c"}; + auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); + auto opts = v.parse_arguments(cmdstruct); + + REQUIRE(opts.settings["--b"] == "c"); + REQUIRE(opts.settings.find("--d") == opts.settings.end()); + REQUIRE(opts.switches.find("--a") != opts.switches.end()); + REQUIRE(opts.settings.find("--c") == opts.settings.end()); + REQUIRE(v.command_arguments.size() == 0); + } +} diff --git a/toolsrc/src/vcpkg-test/catch.cpp b/toolsrc/src/vcpkg-test/catch.cpp new file mode 100644 index 000000000..8b5d1aa15 --- /dev/null +++ b/toolsrc/src/vcpkg-test/catch.cpp @@ -0,0 +1,11 @@ +#define CATCH_CONFIG_RUNNER +#include + +#include + +int main(int argc, char** argv) +{ + vcpkg::Debug::g_debugging = true; + + return Catch::Session().run(argc, argv); +} diff --git a/toolsrc/src/vcpkg-test/chrono.cpp b/toolsrc/src/vcpkg-test/chrono.cpp new file mode 100644 index 000000000..306217ad0 --- /dev/null +++ b/toolsrc/src/vcpkg-test/chrono.cpp @@ -0,0 +1,34 @@ +#include + +#include + +namespace Chrono = vcpkg::Chrono; + +TEST_CASE ("parse time", "[chrono]") +{ + auto timestring = "1990-02-03T04:05:06.0Z"; + auto maybe_time = Chrono::CTime::parse(timestring); + + REQUIRE(maybe_time.has_value()); + REQUIRE(maybe_time.get()->to_string() == timestring); +} + +TEST_CASE ("parse blank time", "[chrono]") +{ + auto maybe_time = Chrono::CTime::parse(""); + + REQUIRE_FALSE(maybe_time.has_value()); +} + +TEST_CASE ("difference of times", "[chrono]") +{ + auto maybe_time1 = Chrono::CTime::parse("1990-02-03T04:05:06.0Z"); + auto maybe_time2 = Chrono::CTime::parse("1990-02-10T04:05:06.0Z"); + + REQUIRE(maybe_time1.has_value()); + REQUIRE(maybe_time2.has_value()); + + auto delta = maybe_time2.get()->to_time_point() - maybe_time1.get()->to_time_point(); + + REQUIRE(std::chrono::duration_cast(delta).count() == 24 * 7); +} diff --git a/toolsrc/src/vcpkg-test/dependencies.cpp b/toolsrc/src/vcpkg-test/dependencies.cpp new file mode 100644 index 000000000..5ed05cc07 --- /dev/null +++ b/toolsrc/src/vcpkg-test/dependencies.cpp @@ -0,0 +1,28 @@ +#include + +#include + +using namespace vcpkg; +using Parse::parse_comma_list; + +TEST_CASE ("parse depends", "[dependencies]") +{ + auto v = expand_qualified_dependencies(parse_comma_list("libA (windows)")); + REQUIRE(v.size() == 1); + REQUIRE(v.at(0).depend.name == "libA"); + REQUIRE(v.at(0).qualifier == "windows"); +} + +TEST_CASE ("filter depends", "[dependencies]") +{ + auto deps = expand_qualified_dependencies(parse_comma_list("libA (windows), libB, libC (uwp)")); + auto v = filter_dependencies(deps, Triplet::X64_WINDOWS); + REQUIRE(v.size() == 2); + REQUIRE(v.at(0) == "libA"); + REQUIRE(v.at(1) == "libB"); + + auto v2 = filter_dependencies(deps, Triplet::ARM_UWP); + REQUIRE(v.size() == 2); + REQUIRE(v2.at(0) == "libB"); + REQUIRE(v2.at(1) == "libC"); +} diff --git a/toolsrc/src/vcpkg-test/files.cpp b/toolsrc/src/vcpkg-test/files.cpp new file mode 100644 index 000000000..9e14cec0c --- /dev/null +++ b/toolsrc/src/vcpkg-test/files.cpp @@ -0,0 +1,121 @@ +#include +#include + +#include +#include + +#include +#include + +#include + +using vcpkg::Test::SYMLINKS_ALLOWED; +using vcpkg::Test::TEMPORARY_DIRECTORY; + +namespace +{ + using uid = std::uniform_int_distribution; + + std::mt19937_64 get_urbg(std::uint64_t index) + { + // smallest prime > 2**63 - 1 + return std::mt19937_64{index + 9223372036854775837ULL}; + } + + std::string get_random_filename(std::mt19937_64& urbg) { return vcpkg::Strings::b32_encode(uid{}(urbg)); } + + void create_directory_tree(std::mt19937_64& urbg, + vcpkg::Files::Filesystem& fs, + std::uint64_t depth, + const fs::path& base) + { + std::random_device rd; + constexpr std::uint64_t max_depth = 5; + constexpr std::uint64_t width = 5; + + // we want ~70% of our "files" to be directories, and then a third + // each of the remaining ~30% to be regular files, directory symlinks, + // and regular symlinks + constexpr std::uint64_t directory_min_tag = 0; + constexpr std::uint64_t directory_max_tag = 6; + constexpr std::uint64_t regular_file_tag = 7; + constexpr std::uint64_t regular_symlink_tag = 8; + constexpr std::uint64_t directory_symlink_tag = 9; + + // if we're at the max depth, we only want to build non-directories + std::uint64_t file_type; + if (depth < max_depth) + { + file_type = uid{directory_min_tag, regular_symlink_tag}(urbg); + } + else + { + file_type = uid{regular_file_tag, regular_symlink_tag}(urbg); + } + + if (!SYMLINKS_ALLOWED && file_type > regular_file_tag) + { + file_type = regular_file_tag; + } + + std::error_code ec; + if (file_type <= directory_max_tag) + { + fs.create_directory(base, ec); + if (ec) { + INFO("File that failed: " << base); + REQUIRE_FALSE(ec); + } + + for (int i = 0; i < width; ++i) + { + create_directory_tree(urbg, fs, depth + 1, base / get_random_filename(urbg)); + } + } + else if (file_type == regular_file_tag) + { + // regular file + fs.write_contents(base, "", ec); + } + else if (file_type == regular_symlink_tag) + { + // regular symlink + fs.write_contents(base, "", ec); + REQUIRE_FALSE(ec); + auto base_link = base; + base_link.replace_filename(base.filename().u8string() + "-link"); + vcpkg::Test::create_symlink(base, base_link, ec); + } + else // type == directory_symlink_tag + { + // directory symlink + vcpkg::Test::create_directory_symlink(base / "..", base, ec); + } + + REQUIRE_FALSE(ec); + } +} + +TEST_CASE ("remove all", "[files]") +{ + auto urbg = get_urbg(0); + + fs::path temp_dir = TEMPORARY_DIRECTORY / get_random_filename(urbg); + + auto& fs = vcpkg::Files::get_real_filesystem(); + + std::error_code ec; + fs.create_directory(TEMPORARY_DIRECTORY, ec); + + REQUIRE_FALSE(ec); + + INFO("temp dir is: " << temp_dir); + + create_directory_tree(urbg, fs, 0, temp_dir); + + fs::path fp; + fs.remove_all(temp_dir, ec, fp); + REQUIRE_FALSE(ec); + + REQUIRE_FALSE(fs.exists(temp_dir)); +} diff --git a/toolsrc/src/vcpkg-test/paragraph.cpp b/toolsrc/src/vcpkg-test/paragraph.cpp new file mode 100644 index 000000000..a95879cfa --- /dev/null +++ b/toolsrc/src/vcpkg-test/paragraph.cpp @@ -0,0 +1,445 @@ +#include +#include + +#include + +#include + +namespace Strings = vcpkg::Strings; + +TEST_CASE ("SourceParagraph construct minimum", "[paragraph]") +{ + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "zlib"}, + {"Version", "1.2.8"}, + }}); + + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->name == "zlib"); + REQUIRE(pgh.core_paragraph->version == "1.2.8"); + REQUIRE(pgh.core_paragraph->maintainer == ""); + REQUIRE(pgh.core_paragraph->description == ""); + REQUIRE(pgh.core_paragraph->depends.size() == 0); +} + +TEST_CASE ("SourceParagraph construct maximum", "[paragraph]") +{ + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "s"}, + {"Version", "v"}, + {"Maintainer", "m"}, + {"Description", "d"}, + {"Build-Depends", "bd"}, + {"Default-Features", "df"}, + {"Supports", "x64"}, + }}); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->name == "s"); + REQUIRE(pgh.core_paragraph->version == "v"); + REQUIRE(pgh.core_paragraph->maintainer == "m"); + REQUIRE(pgh.core_paragraph->description == "d"); + REQUIRE(pgh.core_paragraph->depends.size() == 1); + REQUIRE(pgh.core_paragraph->depends[0].name() == "bd"); + REQUIRE(pgh.core_paragraph->default_features.size() == 1); + REQUIRE(pgh.core_paragraph->default_features[0] == "df"); + REQUIRE(pgh.core_paragraph->supports.size() == 1); + REQUIRE(pgh.core_paragraph->supports[0] == "x64"); +} + +TEST_CASE ("SourceParagraph two depends", "[paragraph]") +{ + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "zlib"}, + {"Version", "1.2.8"}, + {"Build-Depends", "z, openssl"}, + }}); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->depends.size() == 2); + REQUIRE(pgh.core_paragraph->depends[0].name() == "z"); + REQUIRE(pgh.core_paragraph->depends[1].name() == "openssl"); +} + +TEST_CASE ("SourceParagraph three depends", "[paragraph]") +{ + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "zlib"}, + {"Version", "1.2.8"}, + {"Build-Depends", "z, openssl, xyz"}, + }}); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->depends.size() == 3); + REQUIRE(pgh.core_paragraph->depends[0].name() == "z"); + REQUIRE(pgh.core_paragraph->depends[1].name() == "openssl"); + REQUIRE(pgh.core_paragraph->depends[2].name() == "xyz"); +} + +TEST_CASE ("SourceParagraph three supports", "[paragraph]") +{ + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "zlib"}, + {"Version", "1.2.8"}, + {"Supports", "x64, windows, uwp"}, + }}); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->supports.size() == 3); + REQUIRE(pgh.core_paragraph->supports[0] == "x64"); + REQUIRE(pgh.core_paragraph->supports[1] == "windows"); + REQUIRE(pgh.core_paragraph->supports[2] == "uwp"); +} + +TEST_CASE ("SourceParagraph construct qualified depends", "[paragraph]") +{ + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "zlib"}, + {"Version", "1.2.8"}, + {"Build-Depends", "libA (windows), libB (uwp)"}, + }}); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->name == "zlib"); + REQUIRE(pgh.core_paragraph->version == "1.2.8"); + REQUIRE(pgh.core_paragraph->maintainer == ""); + REQUIRE(pgh.core_paragraph->description == ""); + REQUIRE(pgh.core_paragraph->depends.size() == 2); + REQUIRE(pgh.core_paragraph->depends[0].name() == "libA"); + REQUIRE(pgh.core_paragraph->depends[0].qualifier == "windows"); + REQUIRE(pgh.core_paragraph->depends[1].name() == "libB"); + REQUIRE(pgh.core_paragraph->depends[1].qualifier == "uwp"); +} + +TEST_CASE ("SourceParagraph default features", "[paragraph]") +{ + auto m_pgh = + vcpkg::SourceControlFile::parse_control_file(std::vector>{{ + {"Source", "a"}, + {"Version", "1.0"}, + {"Default-Features", "a1"}, + }}); + REQUIRE(m_pgh.has_value()); + auto& pgh = **m_pgh.get(); + + REQUIRE(pgh.core_paragraph->default_features.size() == 1); + REQUIRE(pgh.core_paragraph->default_features[0] == "a1"); +} + +TEST_CASE ("BinaryParagraph construct minimum", "[paragraph]") +{ + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + }); + + REQUIRE(pgh.spec.name() == "zlib"); + REQUIRE(pgh.version == "1.2.8"); + REQUIRE(pgh.maintainer == ""); + REQUIRE(pgh.description == ""); + REQUIRE(pgh.spec.triplet().canonical_name() == "x86-windows"); + REQUIRE(pgh.depends.size() == 0); +} + +TEST_CASE ("BinaryParagraph construct maximum", "[paragraph]") +{ + vcpkg::BinaryParagraph pgh({ + {"Package", "s"}, + {"Version", "v"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Maintainer", "m"}, + {"Description", "d"}, + {"Depends", "bd"}, + }); + + REQUIRE(pgh.spec.name() == "s"); + REQUIRE(pgh.version == "v"); + REQUIRE(pgh.maintainer == "m"); + REQUIRE(pgh.description == "d"); + REQUIRE(pgh.depends.size() == 1); + REQUIRE(pgh.depends[0] == "bd"); +} + +TEST_CASE ("BinaryParagraph three depends", "[paragraph]") +{ + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Depends", "a, b, c"}, + }); + + REQUIRE(pgh.depends.size() == 3); + REQUIRE(pgh.depends[0] == "a"); + REQUIRE(pgh.depends[1] == "b"); + REQUIRE(pgh.depends[2] == "c"); +} + +TEST_CASE ("BinaryParagraph abi", "[paragraph]") +{ + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Abi", "abcd123"}, + }); + + REQUIRE(pgh.depends.size() == 0); + REQUIRE(pgh.abi == "abcd123"); +} + +TEST_CASE ("BinaryParagraph default features", "[paragraph]") +{ + vcpkg::BinaryParagraph pgh({ + {"Package", "a"}, + {"Version", "1.0"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Default-Features", "a1"}, + }); + + REQUIRE(pgh.depends.size() == 0); + REQUIRE(pgh.default_features.size() == 1); + REQUIRE(pgh.default_features[0] == "a1"); +} + +TEST_CASE ("parse paragraphs empty", "[paragraph]") +{ + const char* str = ""; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + REQUIRE(pghs.empty()); +} + +TEST_CASE ("parse paragraphs one field", "[paragraph]") +{ + const char* str = "f1: v1"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + REQUIRE(pghs.size() == 1); + REQUIRE(pghs[0].size() == 1); + REQUIRE(pghs[0]["f1"] == "v1"); +} + +TEST_CASE ("parse paragraphs one pgh", "[paragraph]") +{ + const char* str = "f1: v1\n" + "f2: v2"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + REQUIRE(pghs.size() == 1); + REQUIRE(pghs[0].size() == 2); + REQUIRE(pghs[0]["f1"] == "v1"); + REQUIRE(pghs[0]["f2"] == "v2"); +} + +TEST_CASE ("parse paragraphs two pgh", "[paragraph]") +{ + const char* str = "f1: v1\n" + "f2: v2\n" + "\n" + "f3: v3\n" + "f4: v4"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + + REQUIRE(pghs.size() == 2); + REQUIRE(pghs[0].size() == 2); + REQUIRE(pghs[0]["f1"] == "v1"); + REQUIRE(pghs[0]["f2"] == "v2"); + REQUIRE(pghs[1].size() == 2); + REQUIRE(pghs[1]["f3"] == "v3"); + REQUIRE(pghs[1]["f4"] == "v4"); +} + +TEST_CASE ("parse paragraphs field names", "[paragraph]") +{ + const char* str = "1:\n" + "f:\n" + "F:\n" + "0:\n" + "F-2:\n"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + + REQUIRE(pghs.size() == 1); + REQUIRE(pghs[0].size() == 5); +} + +TEST_CASE ("parse paragraphs multiple blank lines", "[paragraph]") +{ + const char* str = "f1: v1\n" + "f2: v2\n" + "\n" + "\n" + "f3: v3\n" + "f4: v4"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + + REQUIRE(pghs.size() == 2); +} + +TEST_CASE ("parse paragraphs empty fields", "[paragraph]") +{ + const char* str = "f1:\n" + "f2: "; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + + REQUIRE(pghs.size() == 1); + REQUIRE(pghs[0].size() == 2); + REQUIRE(pghs[0]["f1"] == ""); + REQUIRE(pghs[0]["f2"] == ""); + REQUIRE(pghs[0].size() == 2); +} + +TEST_CASE ("parse paragraphs multiline fields", "[paragraph]") +{ + const char* str = "f1: simple\n" + " f1\r\n" + "f2:\r\n" + " f2\r\n" + " continue\r\n"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + + REQUIRE(pghs.size() == 1); + REQUIRE(pghs[0]["f1"] == "simple\n f1"); + REQUIRE(pghs[0]["f2"] == "\n f2\n continue"); +} + +TEST_CASE ("parse paragraphs crlfs", "[paragraph]") +{ + const char* str = "f1: v1\r\n" + "f2: v2\r\n" + "\r\n" + "f3: v3\r\n" + "f4: v4"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + + REQUIRE(pghs.size() == 2); + REQUIRE(pghs[0].size() == 2); + REQUIRE(pghs[0]["f1"] == "v1"); + REQUIRE(pghs[0]["f2"] == "v2"); + REQUIRE(pghs[1].size() == 2); + REQUIRE(pghs[1]["f3"] == "v3"); + REQUIRE(pghs[1]["f4"] == "v4"); +} + +TEST_CASE ("parse paragraphs comment", "[paragraph]") +{ + const char* str = "f1: v1\r\n" + "#comment\r\n" + "f2: v2\r\n" + "#comment\r\n" + "\r\n" + "#comment\r\n" + "f3: v3\r\n" + "#comment\r\n" + "f4: v4"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + + REQUIRE(pghs.size() == 2); + REQUIRE(pghs[0].size() == 2); + REQUIRE(pghs[0]["f1"] == "v1"); + REQUIRE(pghs[0]["f2"] == "v2"); + REQUIRE(pghs[1].size()); + REQUIRE(pghs[1]["f3"] == "v3"); + REQUIRE(pghs[1]["f4"] == "v4"); +} + +TEST_CASE ("parse comment before single line feed", "[paragraph]") +{ + const char* str = "f1: v1\r\n" + "#comment\n"; + auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); + REQUIRE(pghs[0].size() == 1); + REQUIRE(pghs[0]["f1"] == "v1"); +} + +TEST_CASE ("BinaryParagraph serialize min", "[paragraph]") +{ + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + }); + std::string ss = Strings::serialize(pgh); + auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); + + REQUIRE(pghs.size() == 1); + REQUIRE(pghs[0].size() == 4); + REQUIRE(pghs[0]["Package"] == "zlib"); + REQUIRE(pghs[0]["Version"] == "1.2.8"); + REQUIRE(pghs[0]["Architecture"] == "x86-windows"); + REQUIRE(pghs[0]["Multi-Arch"] == "same"); +} + +TEST_CASE ("BinaryParagraph serialize max", "[paragraph]") +{ + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Description", "first line\n second line"}, + {"Maintainer", "abc "}, + {"Depends", "dep"}, + {"Multi-Arch", "same"}, + }); + std::string ss = Strings::serialize(pgh); + auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); + + REQUIRE(pghs.size() == 1); + REQUIRE(pghs[0].size() == 7); + REQUIRE(pghs[0]["Package"] == "zlib"); + REQUIRE(pghs[0]["Version"] == "1.2.8"); + REQUIRE(pghs[0]["Architecture"] == "x86-windows"); + REQUIRE(pghs[0]["Multi-Arch"] == "same"); + REQUIRE(pghs[0]["Description"] == "first line\n second line"); + REQUIRE(pghs[0]["Depends"] == "dep"); +} + +TEST_CASE ("BinaryParagraph serialize multiple deps", "[paragraph]") +{ + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Depends", "a, b, c"}, + }); + std::string ss = Strings::serialize(pgh); + auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); + + REQUIRE(pghs.size() == 1); + REQUIRE(pghs[0]["Depends"] == "a, b, c"); +} + +TEST_CASE ("BinaryParagraph serialize abi", "[paragraph]") +{ + vcpkg::BinaryParagraph pgh({ + {"Package", "zlib"}, + {"Version", "1.2.8"}, + {"Architecture", "x86-windows"}, + {"Multi-Arch", "same"}, + {"Depends", "a, b, c"}, + {"Abi", "123abc"}, + }); + std::string ss = Strings::serialize(pgh); + auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); + + REQUIRE(pghs.size() == 1); + REQUIRE(pghs[0]["Abi"] == "123abc"); +} diff --git a/toolsrc/src/vcpkg-test/plan.cpp b/toolsrc/src/vcpkg-test/plan.cpp new file mode 100644 index 000000000..049ef2066 --- /dev/null +++ b/toolsrc/src/vcpkg-test/plan.cpp @@ -0,0 +1,1241 @@ +#include +#include + +#include +#include +#include + +#include +#include +#include + +using namespace vcpkg; + +using Test::make_status_feature_pgh; +using Test::make_status_pgh; +using Test::unsafe_pspec; + +static std::unique_ptr make_control_file( + const char* name, + const char* depends, + const std::vector>& features = {}, + const std::vector& default_features = {}) +{ + using Pgh = std::unordered_map; + std::vector scf_pghs; + scf_pghs.push_back(Pgh{{"Source", name}, + {"Version", "0"}, + {"Build-Depends", depends}, + {"Default-Features", Strings::join(", ", default_features)}}); + for (auto&& feature : features) + { + scf_pghs.push_back(Pgh{ + {"Feature", feature.first}, + {"Description", "feature"}, + {"Build-Depends", feature.second}, + }); + } + auto m_pgh = vcpkg::SourceControlFile::parse_control_file(std::move(scf_pghs)); + REQUIRE(m_pgh.has_value()); + return std::move(*m_pgh.get()); +} + +/// +/// Assert that the given action an install of given features from given package. +/// +static void features_check(Dependencies::AnyAction& install_action, + std::string pkg_name, + std::vector vec, + const Triplet& triplet = Triplet::X86_WINDOWS) +{ + REQUIRE(install_action.install_action.has_value()); + const auto& plan = install_action.install_action.value_or_exit(VCPKG_LINE_INFO); + const auto& feature_list = plan.feature_list; + + REQUIRE(plan.spec.triplet().to_string() == triplet.to_string()); + + auto& scfl = *plan.source_control_file_location.get(); + REQUIRE(pkg_name == scfl.source_control_file->core_paragraph->name); + REQUIRE(feature_list.size() == vec.size()); + + for (auto&& feature_name : vec) + { + // TODO: see if this can be simplified + if (feature_name == "core" || feature_name == "") + { + REQUIRE((Util::find(feature_list, "core") != feature_list.end() || + Util::find(feature_list, "") != feature_list.end())); + continue; + } + REQUIRE(Util::find(feature_list, feature_name) != feature_list.end()); + } +} + +/// +/// Assert that the given action is a remove of given package. +/// +static void remove_plan_check(Dependencies::AnyAction& remove_action, + std::string pkg_name, + const Triplet& triplet = Triplet::X86_WINDOWS) +{ + const auto& plan = remove_action.remove_action.value_or_exit(VCPKG_LINE_INFO); + REQUIRE(plan.spec.triplet().to_string() == triplet.to_string()); + REQUIRE(pkg_name == plan.spec.name()); +} + +/// +/// Map of source control files by their package name. +/// +struct PackageSpecMap +{ + std::unordered_map map; + Triplet triplet; + PackageSpecMap(const Triplet& t = Triplet::X86_WINDOWS) noexcept { triplet = t; } + + PackageSpec emplace(const char* name, + const char* depends = "", + const std::vector>& features = {}, + const std::vector& default_features = {}) + { + auto scfl = SourceControlFileLocation{make_control_file(name, depends, features, default_features), ""}; + return emplace(std::move(scfl)); + } + + PackageSpec emplace(vcpkg::SourceControlFileLocation&& scfl) + { + auto spec = PackageSpec::from_name_and_triplet(scfl.source_control_file->core_paragraph->name, triplet); + REQUIRE(spec.has_value()); + map.emplace(scfl.source_control_file->core_paragraph->name, std::move(scfl)); + return PackageSpec{*spec.get()}; + } +}; + +TEST_CASE ("basic install scheme", "[plan]") +{ + std::vector> status_paragraphs; + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a", "b"); + auto spec_b = spec_map.emplace("b", "c"); + auto spec_c = spec_map.emplace("c"); + + Dependencies::MapPortFileProvider map_port(spec_map.map); + auto install_plan = Dependencies::create_feature_install_plan( + map_port, {FeatureSpec{spec_a, ""}}, StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 3); + REQUIRE(install_plan.at(0).spec().name() == "c"); + REQUIRE(install_plan.at(1).spec().name() == "b"); + REQUIRE(install_plan.at(2).spec().name() == "a"); +} + +TEST_CASE ("multiple install scheme", "[plan]") +{ + std::vector> status_paragraphs; + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a", "d"); + auto spec_b = spec_map.emplace("b", "d, e"); + auto spec_c = spec_map.emplace("c", "e, h"); + auto spec_d = spec_map.emplace("d", "f, g, h"); + auto spec_e = spec_map.emplace("e", "g"); + auto spec_f = spec_map.emplace("f"); + auto spec_g = spec_map.emplace("g"); + auto spec_h = spec_map.emplace("h"); + + Dependencies::MapPortFileProvider map_port(spec_map.map); + auto install_plan = Dependencies::create_feature_install_plan( + map_port, + {FeatureSpec{spec_a, ""}, FeatureSpec{spec_b, ""}, FeatureSpec{spec_c, ""}}, + StatusParagraphs(std::move(status_paragraphs))); + + auto iterator_pos = [&](const PackageSpec& spec) { + auto it = + std::find_if(install_plan.begin(), install_plan.end(), [&](auto& action) { return action.spec() == spec; }); + REQUIRE(it != install_plan.end()); + return it - install_plan.begin(); + }; + + const auto a_pos = iterator_pos(spec_a); + const auto b_pos = iterator_pos(spec_b); + const auto c_pos = iterator_pos(spec_c); + const auto d_pos = iterator_pos(spec_d); + const auto e_pos = iterator_pos(spec_e); + const auto f_pos = iterator_pos(spec_f); + const auto g_pos = iterator_pos(spec_g); + const auto h_pos = iterator_pos(spec_h); + + REQUIRE(a_pos > d_pos); + REQUIRE(b_pos > e_pos); + REQUIRE(b_pos > d_pos); + REQUIRE(c_pos > e_pos); + REQUIRE(c_pos > h_pos); + REQUIRE(d_pos > f_pos); + REQUIRE(d_pos > g_pos); + REQUIRE(d_pos > h_pos); + REQUIRE(e_pos > g_pos); +} + +TEST_CASE ("existing package scheme", "[plan]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(vcpkg::Test::make_status_pgh("a")); + + PackageSpecMap spec_map; + auto spec_a = FullPackageSpec{spec_map.emplace("a")}; + + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 1); + const auto p = install_plan.at(0).install_action.get(); + REQUIRE(p); + REQUIRE(p->spec.name() == "a"); + REQUIRE(p->plan_type == Dependencies::InstallPlanType::ALREADY_INSTALLED); + REQUIRE(p->request_type == Dependencies::RequestType::USER_REQUESTED); +} + +TEST_CASE ("user requested package scheme", "[plan]") +{ + std::vector> status_paragraphs; + + PackageSpecMap spec_map; + const auto spec_a = FullPackageSpec{spec_map.emplace("a", "b")}; + const auto spec_b = FullPackageSpec{spec_map.emplace("b")}; + + const auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 2); + const auto p = install_plan.at(0).install_action.get(); + REQUIRE(p); + REQUIRE(p->spec.name() == "b"); + REQUIRE(p->plan_type == Dependencies::InstallPlanType::BUILD_AND_INSTALL); + REQUIRE(p->request_type == Dependencies::RequestType::AUTO_SELECTED); + + const auto p2 = install_plan.at(1).install_action.get(); + REQUIRE(p2); + REQUIRE(p2->spec.name() == "a"); + REQUIRE(p2->plan_type == Dependencies::InstallPlanType::BUILD_AND_INSTALL); + REQUIRE(p2->request_type == Dependencies::RequestType::USER_REQUESTED); +} + +TEST_CASE ("long install scheme", "[plan]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("j", "k")); + status_paragraphs.push_back(make_status_pgh("k")); + + PackageSpecMap spec_map; + + auto spec_a = spec_map.emplace("a", "b, c, d, e, f, g, h, j, k"); + auto spec_b = spec_map.emplace("b", "c, d, e, f, g, h, j, k"); + auto spec_c = spec_map.emplace("c", "d, e, f, g, h, j, k"); + auto spec_d = spec_map.emplace("d", "e, f, g, h, j, k"); + auto spec_e = spec_map.emplace("e", "f, g, h, j, k"); + auto spec_f = spec_map.emplace("f", "g, h, j, k"); + auto spec_g = spec_map.emplace("g", "h, j, k"); + auto spec_h = spec_map.emplace("h", "j, k"); + auto spec_j = spec_map.emplace("j", "k"); + auto spec_k = spec_map.emplace("k"); + + Dependencies::MapPortFileProvider map_port(spec_map.map); + auto install_plan = Dependencies::create_feature_install_plan( + map_port, {FeatureSpec{spec_a, ""}}, StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 8); + REQUIRE(install_plan.at(0).spec().name() == "h"); + REQUIRE(install_plan.at(1).spec().name() == "g"); + REQUIRE(install_plan.at(2).spec().name() == "f"); + REQUIRE(install_plan.at(3).spec().name() == "e"); + REQUIRE(install_plan.at(4).spec().name() == "d"); + REQUIRE(install_plan.at(5).spec().name() == "c"); + REQUIRE(install_plan.at(6).spec().name() == "b"); + REQUIRE(install_plan.at(7).spec().name() == "a"); +} + +TEST_CASE ("basic feature test 1", "[plan]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a", "b, b[b1]")); + status_paragraphs.push_back(make_status_pgh("b")); + status_paragraphs.push_back(make_status_feature_pgh("b", "b1")); + + PackageSpecMap spec_map; + auto spec_a = FullPackageSpec{spec_map.emplace("a", "b, b[b1]", {{"a1", "b[b2]"}}), {"a1"}}; + auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}, {"b3", ""}})}; + + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 4); + remove_plan_check(install_plan.at(0), "a"); + remove_plan_check(install_plan.at(1), "b"); + features_check(install_plan.at(2), "b", {"b1", "core", "b1"}); + features_check(install_plan.at(3), "a", {"a1", "core"}); +} + +TEST_CASE ("basic feature test 2", "[plan]") +{ + std::vector> status_paragraphs; + + PackageSpecMap spec_map; + + auto spec_a = FullPackageSpec{spec_map.emplace("a", "b[b1]", {{"a1", "b[b2]"}}), {"a1"}}; + auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}, {"b3", ""}})}; + + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 2); + features_check(install_plan.at(0), "b", {"b1", "b2", "core"}); + features_check(install_plan.at(1), "a", {"a1", "core"}); +} + +TEST_CASE ("basic feature test 3", "[plan]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a")); + + PackageSpecMap spec_map; + + auto spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}}; + auto spec_b = FullPackageSpec{spec_map.emplace("b")}; + auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; + + auto install_plan = Dependencies::create_feature_install_plan(spec_map.map, + FullPackageSpec::to_feature_specs({spec_c, spec_a}), + StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 4); + remove_plan_check(install_plan.at(0), "a"); + features_check(install_plan.at(1), "b", {"core"}); + features_check(install_plan.at(2), "a", {"a1", "core"}); + features_check(install_plan.at(3), "c", {"core"}); +} + +TEST_CASE ("basic feature test 4", "[plan]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a")); + status_paragraphs.push_back(make_status_feature_pgh("a", "a1", "")); + + PackageSpecMap spec_map; + + auto spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}})}; + auto spec_b = FullPackageSpec{spec_map.emplace("b")}; + auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; + + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, FullPackageSpec::to_feature_specs({spec_c}), StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 1); + features_check(install_plan.at(0), "c", {"core"}); +} + +TEST_CASE ("basic feature test 5", "[plan]") +{ + std::vector> status_paragraphs; + + PackageSpecMap spec_map; + + auto spec_a = + FullPackageSpec{spec_map.emplace("a", "", {{"a1", "b[b1]"}, {"a2", "b[b2]"}, {"a3", "a[a2]"}}), {"a3"}}; + auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}})}; + + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 2); + features_check(install_plan.at(0), "b", {"core", "b2"}); + features_check(install_plan.at(1), "a", {"core", "a3", "a2"}); +} + +TEST_CASE ("basic feature test 6", "[plan]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("b")); + + PackageSpecMap spec_map; + auto spec_a = FullPackageSpec{spec_map.emplace("a", "b[core]"), {"core"}}; + auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {"b1"}}; + + auto install_plan = Dependencies::create_feature_install_plan(spec_map.map, + FullPackageSpec::to_feature_specs({spec_a, spec_b}), + StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 3); + remove_plan_check(install_plan.at(0), "b"); + features_check(install_plan.at(1), "b", {"core", "b1"}); + features_check(install_plan.at(2), "a", {"core"}); +} + +TEST_CASE ("basic feature test 7", "[plan]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("x", "b")); + status_paragraphs.push_back(make_status_pgh("b")); + + PackageSpecMap spec_map; + + auto spec_a = FullPackageSpec{spec_map.emplace("a")}; + auto spec_x = FullPackageSpec{spec_map.emplace("x", "a"), {"core"}}; + auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {"b1"}}; + + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, FullPackageSpec::to_feature_specs({spec_b}), StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 5); + remove_plan_check(install_plan.at(0), "x"); + remove_plan_check(install_plan.at(1), "b"); + + // TODO: order here may change but A < X, and B anywhere + features_check(install_plan.at(2), "b", {"core", "b1"}); + features_check(install_plan.at(3), "a", {"core"}); + features_check(install_plan.at(4), "x", {"core"}); +} + +TEST_CASE ("basic feature test 8", "[plan][!mayfail]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a")); + status_paragraphs.push_back(make_status_pgh("a")); + status_paragraphs.back()->package.spec = + PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); + + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}}; + auto spec_b_64 = FullPackageSpec{spec_map.emplace("b")}; + auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; + + spec_map.triplet = Triplet::X86_WINDOWS; + auto spec_a_86 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}}; + auto spec_b_86 = FullPackageSpec{spec_map.emplace("b")}; + auto spec_c_86 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; + + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({spec_c_64, spec_a_86, spec_a_64, spec_c_86}), + StatusParagraphs(std::move(status_paragraphs))); + + remove_plan_check(install_plan.at(0), "a", Triplet::X64_WINDOWS); + remove_plan_check(install_plan.at(1), "a"); + features_check(install_plan.at(2), "b", {"core"}, Triplet::X64_WINDOWS); + features_check(install_plan.at(3), "a", {"a1", "core"}, Triplet::X64_WINDOWS); + features_check(install_plan.at(4), "c", {"core"}, Triplet::X64_WINDOWS); + features_check(install_plan.at(5), "b", {"core"}); + features_check(install_plan.at(6), "a", {"a1", "core"}); + features_check(install_plan.at(7), "c", {"core"}); +} + +TEST_CASE ("install all features test", "[plan]") +{ + std::vector> status_paragraphs; + + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "", {{"0", ""}, {"1", ""}}), {"core"}}; + + auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS); + REQUIRE(install_specs.has_value()); + if (!install_specs.has_value()) return; + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 1); + features_check(install_plan.at(0), "a", {"0", "1", "core"}, Triplet::X64_WINDOWS); +} + +TEST_CASE ("install default features test 1", "[plan]") +{ + std::vector> status_paragraphs; + + // Add a port "a" with default features "1" and features "0" and "1". + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + spec_map.emplace("a", "", {{"0", ""}, {"1", ""}}, {"1"}); + + // Install "a" (without explicit feature specification) + auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + // Expect the default feature "1" to be installed, but not "0" + REQUIRE(install_plan.size() == 1); + features_check(install_plan.at(0), "a", {"1", "core"}, Triplet::X64_WINDOWS); +} + +TEST_CASE ("install default features test 2", "[plan]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a")); + status_paragraphs.back()->package.spec = + PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); + + // Add a port "a" of which "core" is already installed, but we will + // install the default features "explicitly" + // "a" has two features, of which "a1" is default. + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + spec_map.emplace("a", "", {{"a0", ""}, {"a1", ""}}, {"a1"}); + + // Install "a" (without explicit feature specification) + auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + // Expect "a" to get removed for rebuild and then installed with default + // features. + REQUIRE(install_plan.size() == 2); + remove_plan_check(install_plan.at(0), "a", Triplet::X64_WINDOWS); + features_check(install_plan.at(1), "a", {"a1", "core"}, Triplet::X64_WINDOWS); +} + +TEST_CASE ("install default features test 3", "[plan]") +{ + std::vector> status_paragraphs; + + // "a" has two features, of which "a1" is default. + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + spec_map.emplace("a", "", {{"a0", ""}, {"a1", ""}}, {"a1"}); + + // Explicitly install "a" without default features + auto install_specs = FullPackageSpec::from_string("a[core]", Triplet::X64_WINDOWS); + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + // Expect the default feature not to get installed. + REQUIRE(install_plan.size() == 1); + features_check(install_plan.at(0), "a", {"core"}, Triplet::X64_WINDOWS); +} + +TEST_CASE ("install default features of dependency test 1", "[plan]") +{ + std::vector> status_paragraphs; + + // Add a port "a" which depends on the core of "b" + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + spec_map.emplace("a", "b[core]"); + // "b" has two features, of which "b1" is default. + spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b1"}); + + // Install "a" (without explicit feature specification) + auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + // Expect "a" to get installed and defaults of "b" through the dependency, + // as no explicit features of "b" are installed by the user. + REQUIRE(install_plan.size() == 2); + features_check(install_plan.at(0), "b", {"b1", "core"}, Triplet::X64_WINDOWS); + features_check(install_plan.at(1), "a", {"core"}, Triplet::X64_WINDOWS); +} + +TEST_CASE ("do not install default features of existing dependency", "[plan]") +{ + // Add a port "a" which depends on the core of "b" + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + spec_map.emplace("a", "b[core]"); + // "b" has two features, of which "b1" is default. + spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b1"}); + + std::vector> status_paragraphs; + // "b[core]" is already installed + status_paragraphs.push_back(make_status_pgh("b")); + status_paragraphs.back()->package.spec = + PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); + + // Install "a" (without explicit feature specification) + auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + // Expect "a" to get installed, but not require rebuilding "b" + REQUIRE(install_plan.size() == 1); + features_check(install_plan.at(0), "a", {"core"}, Triplet::X64_WINDOWS); +} + +TEST_CASE ("install default features of dependency test 2", "[plan]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("b")); + status_paragraphs.back()->package.spec = + PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); + + // Add a port "a" which depends on the core of "b", which was already + // installed explicitly + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + spec_map.emplace("a", "b[core]"); + // "b" has two features, of which "b1" is default. + spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b1"}); + + // Install "a" (without explicit feature specification) + auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + // Expect "a" to get installed, not the defaults of "b", as the required + // dependencies are already there, installed explicitly by the user. + REQUIRE(install_plan.size() == 1); + features_check(install_plan.at(0), "a", {"core"}, Triplet::X64_WINDOWS); +} + +TEST_CASE ("install plan action dependencies", "[plan]") +{ + std::vector> status_paragraphs; + + // Add a port "a" which depends on the core of "b", which was already + // installed explicitly + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + auto spec_c = spec_map.emplace("c"); + auto spec_b = spec_map.emplace("b", "c"); + spec_map.emplace("a", "b"); + + // Install "a" (without explicit feature specification) + auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 3); + features_check(install_plan.at(0), "c", {"core"}, Triplet::X64_WINDOWS); + + features_check(install_plan.at(1), "b", {"core"}, Triplet::X64_WINDOWS); + REQUIRE(install_plan.at(1).install_action.get()->computed_dependencies == std::vector{spec_c}); + + features_check(install_plan.at(2), "a", {"core"}, Triplet::X64_WINDOWS); + REQUIRE(install_plan.at(2).install_action.get()->computed_dependencies == std::vector{spec_b}); +} + +TEST_CASE ("install plan action dependencies 2", "[plan]") +{ + std::vector> status_paragraphs; + + // Add a port "a" which depends on the core of "b", which was already + // installed explicitly + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + auto spec_c = spec_map.emplace("c"); + auto spec_b = spec_map.emplace("b", "c"); + spec_map.emplace("a", "c, b"); + + // Install "a" (without explicit feature specification) + auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 3); + features_check(install_plan.at(0), "c", {"core"}, Triplet::X64_WINDOWS); + + features_check(install_plan.at(1), "b", {"core"}, Triplet::X64_WINDOWS); + REQUIRE(install_plan.at(1).install_action.get()->computed_dependencies == std::vector{spec_c}); + + features_check(install_plan.at(2), "a", {"core"}, Triplet::X64_WINDOWS); + REQUIRE(install_plan.at(2).install_action.get()->computed_dependencies == std::vector{spec_b, spec_c}); +} + +TEST_CASE ("install plan action dependencies 3", "[plan]") +{ + std::vector> status_paragraphs; + + // Add a port "a" which depends on the core of "b", which was already + // installed explicitly + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + spec_map.emplace("a", "", {{"0", ""}, {"1", "a[0]"}}, {"1"}); + + // Install "a" (without explicit feature specification) + auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 1); + features_check(install_plan.at(0), "a", {"1", "0", "core"}, Triplet::X64_WINDOWS); + REQUIRE(install_plan.at(0).install_action.get()->computed_dependencies == std::vector{}); +} + +TEST_CASE ("install with default features", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a", "")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map; + auto b_spec = spec_map.emplace("b", "", {{"0", ""}}, {"0"}); + auto a_spec = spec_map.emplace("a", "b[core]", {{"0", ""}}); + + // Install "a" and indicate that "b" should not install default features + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, {FeatureSpec{a_spec, "0"}, FeatureSpec{b_spec, "core"}}, status_db); + + REQUIRE(install_plan.size() == 3); + remove_plan_check(install_plan.at(0), "a"); + features_check(install_plan.at(1), "b", {"core"}); + features_check(install_plan.at(2), "a", {"0", "core"}); +} + +TEST_CASE ("upgrade with default features 1", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a", "", "1")); + pghs.push_back(make_status_feature_pgh("a", "0")); + StatusParagraphs status_db(std::move(pghs)); + + // Add a port "a" of which "core" and "0" are already installed. + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a", "", {{"0", ""}, {"1", ""}}, {"1"}); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + auto plan = graph.serialize(); + + // The upgrade should not install the default feature + REQUIRE(plan.size() == 2); + + REQUIRE(plan.at(0).spec().name() == "a"); + remove_plan_check(plan.at(0), "a"); + features_check(plan.at(1), "a", {"core", "0"}); +} + +TEST_CASE ("upgrade with default features 2", "[plan]") +{ + std::vector> pghs; + // B is currently installed _without_ default feature b0 + pghs.push_back(make_status_pgh("b", "", "b0", "x64-windows")); + pghs.push_back(make_status_pgh("a", "b[core]", "", "x64-windows")); + + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + auto spec_a = spec_map.emplace("a", "b[core]"); + auto spec_b = spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b0", "b1"}); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + graph.upgrade(spec_b); + auto plan = graph.serialize(); + + // The upgrade should install the new default feature b1 but not b0 + REQUIRE(plan.size() == 4); + remove_plan_check(plan.at(0), "a", Triplet::X64_WINDOWS); + remove_plan_check(plan.at(1), "b", Triplet::X64_WINDOWS); + features_check(plan.at(2), "b", {"core", "b1"}, Triplet::X64_WINDOWS); + features_check(plan.at(3), "a", {"core"}, Triplet::X64_WINDOWS); +} + +TEST_CASE ("upgrade with default features 3", "[plan]") +{ + std::vector> pghs; + // note: unrelated package due to x86 triplet + pghs.push_back(make_status_pgh("b", "", "", "x86-windows")); + pghs.push_back(make_status_pgh("a", "", "", "x64-windows")); + + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + auto spec_a = spec_map.emplace("a", "b[core]"); + spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b0"}); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + auto plan = graph.serialize(); + + // The upgrade should install the default feature + REQUIRE(plan.size() == 3); + remove_plan_check(plan.at(0), "a", Triplet::X64_WINDOWS); + features_check(plan.at(1), "b", {"b0", "core"}, Triplet::X64_WINDOWS); + features_check(plan.at(2), "a", {"core"}, Triplet::X64_WINDOWS); +} + +TEST_CASE ("upgrade with new default feature", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a", "", "0", "x86-windows")); + + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a", "", {{"0", ""}, {"1", ""}, {"2", ""}}, {"0", "1"}); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + auto plan = graph.serialize(); + + // The upgrade should install the new default feature but not the old default feature 0 + REQUIRE(plan.size() == 2); + remove_plan_check(plan.at(0), "a", Triplet::X86_WINDOWS); + features_check(plan.at(1), "a", {"core", "1"}, Triplet::X86_WINDOWS); +} + +TEST_CASE ("transitive features test", "[plan]") +{ + std::vector> status_paragraphs; + + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"0", "b[0]"}}), {"core"}}; + auto spec_b_64 = FullPackageSpec{spec_map.emplace("b", "c", {{"0", "c[0]"}}), {"core"}}; + auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "", {{"0", ""}}), {"core"}}; + + auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS); + REQUIRE(install_specs.has_value()); + if (!install_specs.has_value()) return; + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 3); + features_check(install_plan.at(0), "c", {"0", "core"}, Triplet::X64_WINDOWS); + features_check(install_plan.at(1), "b", {"0", "core"}, Triplet::X64_WINDOWS); + features_check(install_plan.at(2), "a", {"0", "core"}, Triplet::X64_WINDOWS); +} + +TEST_CASE ("no transitive features test", "[plan]") +{ + std::vector> status_paragraphs; + + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"0", ""}}), {"core"}}; + auto spec_b_64 = FullPackageSpec{spec_map.emplace("b", "c", {{"0", ""}}), {"core"}}; + auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "", {{"0", ""}}), {"core"}}; + + auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS); + REQUIRE(install_specs.has_value()); + if (!install_specs.has_value()) return; + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 3); + features_check(install_plan.at(0), "c", {"core"}, Triplet::X64_WINDOWS); + features_check(install_plan.at(1), "b", {"core"}, Triplet::X64_WINDOWS); + features_check(install_plan.at(2), "a", {"0", "core"}, Triplet::X64_WINDOWS); +} + +TEST_CASE ("only transitive features test", "[plan]") +{ + std::vector> status_paragraphs; + + PackageSpecMap spec_map(Triplet::X64_WINDOWS); + auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "", {{"0", "b[0]"}}), {"core"}}; + auto spec_b_64 = FullPackageSpec{spec_map.emplace("b", "", {{"0", "c[0]"}}), {"core"}}; + auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "", {{"0", ""}}), {"core"}}; + + auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS); + REQUIRE(install_specs.has_value()); + if (!install_specs.has_value()) return; + auto install_plan = Dependencies::create_feature_install_plan( + spec_map.map, + FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), + StatusParagraphs(std::move(status_paragraphs))); + + REQUIRE(install_plan.size() == 3); + features_check(install_plan.at(0), "c", {"0", "core"}, Triplet::X64_WINDOWS); + features_check(install_plan.at(1), "b", {"0", "core"}, Triplet::X64_WINDOWS); + features_check(install_plan.at(2), "a", {"0", "core"}, Triplet::X64_WINDOWS); +} + +TEST_CASE ("basic remove scheme", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + StatusParagraphs status_db(std::move(pghs)); + + auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("a")}, status_db); + + REQUIRE(remove_plan.size() == 1); + REQUIRE(remove_plan.at(0).spec.name() == "a"); +} + +TEST_CASE ("recurse remove scheme", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_pgh("b", "a")); + StatusParagraphs status_db(std::move(pghs)); + + auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("a")}, status_db); + + REQUIRE(remove_plan.size() == 2); + REQUIRE(remove_plan.at(0).spec.name() == "b"); + REQUIRE(remove_plan.at(1).spec.name() == "a"); +} + +TEST_CASE ("features depend remove scheme", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_pgh("b")); + pghs.push_back(make_status_feature_pgh("b", "0", "a")); + StatusParagraphs status_db(std::move(pghs)); + + auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("a")}, status_db); + + REQUIRE(remove_plan.size() == 2); + REQUIRE(remove_plan.at(0).spec.name() == "b"); + REQUIRE(remove_plan.at(1).spec.name() == "a"); +} + +TEST_CASE ("features depend remove scheme once removed", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("expat")); + pghs.push_back(make_status_pgh("vtk", "expat")); + pghs.push_back(make_status_pgh("opencv")); + pghs.push_back(make_status_feature_pgh("opencv", "vtk", "vtk")); + StatusParagraphs status_db(std::move(pghs)); + + auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("expat")}, status_db); + + REQUIRE(remove_plan.size() == 3); + REQUIRE(remove_plan.at(0).spec.name() == "opencv"); + REQUIRE(remove_plan.at(1).spec.name() == "vtk"); + REQUIRE(remove_plan.at(2).spec.name() == "expat"); +} + +TEST_CASE ("features depend remove scheme once removed x64", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("expat", "", "", "x64")); + pghs.push_back(make_status_pgh("vtk", "expat", "", "x64")); + pghs.push_back(make_status_pgh("opencv", "", "", "x64")); + pghs.push_back(make_status_feature_pgh("opencv", "vtk", "vtk", "x64")); + StatusParagraphs status_db(std::move(pghs)); + + auto remove_plan = + Dependencies::create_remove_plan({unsafe_pspec("expat", Triplet::from_canonical_name("x64"))}, status_db); + + REQUIRE(remove_plan.size() == 3); + REQUIRE(remove_plan.at(0).spec.name() == "opencv"); + REQUIRE(remove_plan.at(1).spec.name() == "vtk"); + REQUIRE(remove_plan.at(2).spec.name() == "expat"); +} + +TEST_CASE ("features depend core remove scheme", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("curl", "", "", "x64")); + pghs.push_back(make_status_pgh("cpr", "curl[core]", "", "x64")); + StatusParagraphs status_db(std::move(pghs)); + + auto remove_plan = + Dependencies::create_remove_plan({unsafe_pspec("curl", Triplet::from_canonical_name("x64"))}, status_db); + + REQUIRE(remove_plan.size() == 2); + REQUIRE(remove_plan.at(0).spec.name() == "cpr"); + REQUIRE(remove_plan.at(1).spec.name() == "curl"); +} + +TEST_CASE ("features depend core remove scheme 2", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("curl", "", "", "x64")); + pghs.push_back(make_status_feature_pgh("curl", "a", "", "x64")); + pghs.push_back(make_status_feature_pgh("curl", "b", "curl[a]", "x64")); + StatusParagraphs status_db(std::move(pghs)); + + auto remove_plan = + Dependencies::create_remove_plan({unsafe_pspec("curl", Triplet::from_canonical_name("x64"))}, status_db); + + REQUIRE(remove_plan.size() == 1); + REQUIRE(remove_plan.at(0).spec.name() == "curl"); +} + +TEST_CASE ("basic upgrade scheme", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a"); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + REQUIRE(plan.size() == 2); + REQUIRE(plan.at(0).spec().name() == "a"); + REQUIRE(plan.at(0).remove_action.has_value()); + REQUIRE(plan.at(1).spec().name() == "a"); + REQUIRE(plan.at(1).install_action.has_value()); +} + +TEST_CASE ("basic upgrade scheme with recurse", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_pgh("b", "a")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a"); + spec_map.emplace("b", "a"); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + REQUIRE(plan.size() == 4); + REQUIRE(plan.at(0).spec().name() == "b"); + REQUIRE(plan.at(0).remove_action.has_value()); + + REQUIRE(plan.at(1).spec().name() == "a"); + REQUIRE(plan.at(1).remove_action.has_value()); + + REQUIRE(plan.at(2).spec().name() == "a"); + REQUIRE(plan.at(2).install_action.has_value()); + + REQUIRE(plan.at(3).spec().name() == "b"); + REQUIRE(plan.at(3).install_action.has_value()); +} + +TEST_CASE ("basic upgrade scheme with bystander", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_pgh("b")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a"); + spec_map.emplace("b", "a"); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + REQUIRE(plan.size() == 2); + REQUIRE(plan.at(0).spec().name() == "a"); + REQUIRE(plan.at(0).remove_action.has_value()); + REQUIRE(plan.at(1).spec().name() == "a"); + REQUIRE(plan.at(1).install_action.has_value()); +} + +TEST_CASE ("basic upgrade scheme with new dep", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a", "b"); + spec_map.emplace("b"); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + REQUIRE(plan.size() == 3); + REQUIRE(plan.at(0).spec().name() == "a"); + REQUIRE(plan.at(0).remove_action.has_value()); + REQUIRE(plan.at(1).spec().name() == "b"); + REQUIRE(plan.at(1).install_action.has_value()); + REQUIRE(plan.at(2).spec().name() == "a"); + REQUIRE(plan.at(2).install_action.has_value()); +} + +TEST_CASE ("basic upgrade scheme with features", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_feature_pgh("a", "a1")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a", "", {{"a1", ""}}); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + REQUIRE(plan.size() == 2); + + REQUIRE(plan.at(0).spec().name() == "a"); + REQUIRE(plan.at(0).remove_action.has_value()); + + features_check(plan.at(1), "a", {"core", "a1"}); +} + +TEST_CASE ("basic upgrade scheme with new default feature", "[plan]") +{ + // only core of package "a" is installed + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + StatusParagraphs status_db(std::move(pghs)); + + // a1 was added as a default feature and should be installed in upgrade + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a", "", {{"a1", ""}}, {"a1"}); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + REQUIRE(plan.size() == 2); + + REQUIRE(plan.at(0).spec().name() == "a"); + REQUIRE(plan.at(0).remove_action.has_value()); + + features_check(plan.at(1), "a", {"core", "a1"}); +} + +TEST_CASE ("basic upgrade scheme with self features", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_feature_pgh("a", "a1", "")); + pghs.push_back(make_status_feature_pgh("a", "a2", "a[a1]")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a", "", {{"a1", ""}, {"a2", "a[a1]"}}); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + REQUIRE(plan.size() == 2); + + REQUIRE(plan.at(0).spec().name() == "a"); + REQUIRE(plan.at(0).remove_action.has_value()); + + REQUIRE(plan.at(1).spec().name() == "a"); + REQUIRE(plan.at(1).install_action.has_value()); + REQUIRE(plan.at(1).install_action.get()->feature_list == std::set{"core", "a1", "a2"}); +} + +TEST_CASE ("basic export scheme", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a"); + + auto plan = Dependencies::create_export_plan({spec_a}, status_db); + + REQUIRE(plan.size() == 1); + REQUIRE(plan.at(0).spec.name() == "a"); + REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT); +} + +TEST_CASE ("basic export scheme with recurse", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_pgh("b", "a")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a"); + auto spec_b = spec_map.emplace("b", "a"); + + auto plan = Dependencies::create_export_plan({spec_b}, status_db); + + REQUIRE(plan.size() == 2); + REQUIRE(plan.at(0).spec.name() == "a"); + REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT); + + REQUIRE(plan.at(1).spec.name() == "b"); + REQUIRE(plan.at(1).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT); +} + +TEST_CASE ("basic export scheme with bystander", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_pgh("b")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a"); + auto spec_b = spec_map.emplace("b", "a"); + + auto plan = Dependencies::create_export_plan({spec_a}, status_db); + + REQUIRE(plan.size() == 1); + REQUIRE(plan.at(0).spec.name() == "a"); + REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT); +} + +TEST_CASE ("basic export scheme with missing", "[plan]") +{ + StatusParagraphs status_db; + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a"); + + auto plan = Dependencies::create_export_plan({spec_a}, status_db); + + REQUIRE(plan.size() == 1); + REQUIRE(plan.at(0).spec.name() == "a"); + REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::NOT_BUILT); +} + +TEST_CASE ("basic export scheme with features", "[plan]") +{ + std::vector> pghs; + pghs.push_back(make_status_pgh("b")); + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_feature_pgh("a", "a1", "b[core]")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map; + auto spec_a = spec_map.emplace("a", "", {{"a1", ""}}); + + auto plan = Dependencies::create_export_plan({spec_a}, status_db); + + REQUIRE(plan.size() == 2); + + REQUIRE(plan.at(0).spec.name() == "b"); + REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT); + + REQUIRE(plan.at(1).spec.name() == "a"); + REQUIRE(plan.at(1).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT); +} diff --git a/toolsrc/src/vcpkg-test/specifier.cpp b/toolsrc/src/vcpkg-test/specifier.cpp new file mode 100644 index 000000000..330a96d78 --- /dev/null +++ b/toolsrc/src/vcpkg-test/specifier.cpp @@ -0,0 +1,134 @@ +#include + +#include +#include + +using namespace vcpkg; + +TEST_CASE ("specifier conversion", "[specifier]") +{ + SECTION ("full package spec to feature specs") + { + constexpr std::size_t SPEC_SIZE = 6; + + auto a_spec = PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); + auto b_spec = PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); + + auto fspecs = FullPackageSpec::to_feature_specs({{a_spec, {"0", "1"}}, {b_spec, {"2", "3"}}}); + + REQUIRE(fspecs.size() == SPEC_SIZE); + + std::array features = {"", "0", "1", "", "2", "3"}; + std::array specs = {&a_spec, &a_spec, &a_spec, &b_spec, &b_spec, &b_spec}; + + for (std::size_t i = 0; i < SPEC_SIZE; ++i) + { + REQUIRE(features.at(i) == fspecs.at(i).feature()); + REQUIRE(*specs.at(i) == fspecs.at(i).spec()); + } + } +} + +TEST_CASE ("specifier parsing", "[specifier]") +{ + SECTION ("parsed specifier from string") + { + auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib"); + REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS); + + auto& spec = *maybe_spec.get(); + REQUIRE(spec.name == "zlib"); + REQUIRE(spec.features.size() == 0); + REQUIRE(spec.triplet == ""); + } + + SECTION ("parsed specifier from string with triplet") + { + auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib:x64-uwp"); + REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS); + + auto& spec = *maybe_spec.get(); + REQUIRE(spec.name == "zlib"); + REQUIRE(spec.triplet == "x64-uwp"); + } + + SECTION ("parsed specifier from string with colons") + { + auto ec = vcpkg::ParsedSpecifier::from_string("zlib:x86-uwp:").error(); + REQUIRE(ec == vcpkg::PackageSpecParseResult::TOO_MANY_COLONS); + } + + SECTION ("parsed specifier from string with feature") + { + auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[feature]:x64-uwp"); + REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS); + + auto& spec = *maybe_spec.get(); + REQUIRE(spec.name == "zlib"); + REQUIRE(spec.features.size() == 1); + REQUIRE(spec.features.at(0) == "feature"); + REQUIRE(spec.triplet == "x64-uwp"); + } + + SECTION ("parsed specifier from string with many features") + { + auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[0, 1,2]"); + REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS); + + auto& spec = *maybe_spec.get(); + REQUIRE(spec.name == "zlib"); + REQUIRE(spec.features.size() == 3); + REQUIRE(spec.features.at(0) == "0"); + REQUIRE(spec.features.at(1) == "1"); + REQUIRE(spec.features.at(2) == "2"); + REQUIRE(spec.triplet == ""); + } + + SECTION ("parsed specifier wildcard feature") + { + auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[*]"); + REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS); + + auto& spec = *maybe_spec.get(); + REQUIRE(spec.name == "zlib"); + REQUIRE(spec.features.size() == 1); + REQUIRE(spec.features.at(0) == "*"); + REQUIRE(spec.triplet == ""); + } + + SECTION ("expand wildcards") + { + auto zlib = vcpkg::FullPackageSpec::from_string("zlib[0,1]", Triplet::X86_UWP).value_or_exit(VCPKG_LINE_INFO); + auto openssl = + vcpkg::FullPackageSpec::from_string("openssl[*]", Triplet::X86_UWP).value_or_exit(VCPKG_LINE_INFO); + auto specs = FullPackageSpec::to_feature_specs({zlib, openssl}); + Util::sort(specs); + auto spectargets = FeatureSpec::from_strings_and_triplet( + { + "openssl", + "zlib", + "openssl[*]", + "zlib[0]", + "zlib[1]", + }, + Triplet::X86_UWP); + Util::sort(spectargets); + + REQUIRE(specs.size() == spectargets.size()); + REQUIRE(Util::all_equal(specs, spectargets)); + } + +#if defined(_WIN32) + SECTION ("ASCII to utf16") + { + auto str = vcpkg::Strings::to_utf16("abc"); + REQUIRE(str == L"abc"); + } + + SECTION ("ASCII to utf16 with whitespace") + { + auto str = vcpkg::Strings::to_utf16("abc -x86-windows"); + REQUIRE(str == L"abc -x86-windows"); + } +#endif +}; diff --git a/toolsrc/src/vcpkg-test/statusparagraphs.cpp b/toolsrc/src/vcpkg-test/statusparagraphs.cpp new file mode 100644 index 000000000..c0833e8ba --- /dev/null +++ b/toolsrc/src/vcpkg-test/statusparagraphs.cpp @@ -0,0 +1,110 @@ +#include +#include + +#include +#include +#include + +using namespace vcpkg; +using namespace vcpkg::Paragraphs; +using namespace vcpkg::Test; + +TEST_CASE ("find installed", "[statusparagraphs]") +{ + auto pghs = parse_paragraphs(R"( +Package: ffmpeg +Version: 3.3.3 +Architecture: x64-windows +Multi-Arch: same +Description: +Status: install ok installed +)"); + + REQUIRE(pghs); + + StatusParagraphs status_db( + Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique(std::move(rpgh)); })); + + auto it = status_db.find_installed(unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS)); + REQUIRE(it != status_db.end()); +} + +TEST_CASE ("find not installed", "[statusparagraphs]") +{ + auto pghs = parse_paragraphs(R"( +Package: ffmpeg +Version: 3.3.3 +Architecture: x64-windows +Multi-Arch: same +Description: +Status: purge ok not-installed +)"); + + REQUIRE(pghs); + + StatusParagraphs status_db( + Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique(std::move(rpgh)); })); + + auto it = status_db.find_installed(unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS)); + REQUIRE(it == status_db.end()); +} + +TEST_CASE ("find with feature packages", "[statusparagraphs]") +{ + auto pghs = parse_paragraphs(R"( +Package: ffmpeg +Version: 3.3.3 +Architecture: x64-windows +Multi-Arch: same +Description: +Status: install ok installed + +Package: ffmpeg +Feature: openssl +Depends: openssl +Architecture: x64-windows +Multi-Arch: same +Description: +Status: purge ok not-installed +)"); + + REQUIRE(pghs); + + StatusParagraphs status_db( + Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique(std::move(rpgh)); })); + + auto it = status_db.find_installed(unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS)); + REQUIRE(it != status_db.end()); + + // Feature "openssl" is not installed and should not be found + auto it1 = status_db.find_installed({unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS), "openssl"}); + REQUIRE(it1 == status_db.end()); +} + +TEST_CASE ("find for feature packages", "[statusparagraphs]") +{ + auto pghs = parse_paragraphs(R"( +Package: ffmpeg +Version: 3.3.3 +Architecture: x64-windows +Multi-Arch: same +Description: +Status: install ok installed + +Package: ffmpeg +Feature: openssl +Depends: openssl +Architecture: x64-windows +Multi-Arch: same +Description: +Status: install ok installed +)"); + REQUIRE(pghs); + + StatusParagraphs status_db( + Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique(std::move(rpgh)); })); + + // Feature "openssl" is installed and should therefore be found + auto it = status_db.find_installed({unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS), "openssl"}); + REQUIRE(it != status_db.end()); +} diff --git a/toolsrc/src/vcpkg-test/strings.cpp b/toolsrc/src/vcpkg-test/strings.cpp new file mode 100644 index 000000000..6b744eee6 --- /dev/null +++ b/toolsrc/src/vcpkg-test/strings.cpp @@ -0,0 +1,33 @@ +#include + +#include + +#include +#include +#include + +TEST_CASE ("b32 encoding", "[strings]") +{ + using u64 = std::uint64_t; + + std::vector> map; + + map.emplace_back(0, "AAAAAAAAAAAAA"); + map.emplace_back(1, "BAAAAAAAAAAAA"); + + map.emplace_back(u64(1) << 32, "AAAAAAEAAAAAA"); + map.emplace_back((u64(1) << 32) + 1, "BAAAAAEAAAAAA"); + + map.emplace_back(0xE4D0'1065'D11E'0229, "JRA4RIXMQAUJO"); + map.emplace_back(0xA626'FE45'B135'07FF, "77BKTYWI6XJMK"); + map.emplace_back(0xEE36'D228'0C31'D405, "FAVDDGAFSWN4O"); + map.emplace_back(0x1405'64E7'FE7E'A88C, "MEK5H774ELBIB"); + map.emplace_back(0xFFFF'FFFF'FFFF'FFFF, "777777777777P"); + + std::string result; + for (const auto& pr : map) + { + result = vcpkg::Strings::b32_encode(pr.first); + REQUIRE(vcpkg::Strings::b32_encode(pr.first) == pr.second); + } +} diff --git a/toolsrc/src/vcpkg-test/supports.cpp b/toolsrc/src/vcpkg-test/supports.cpp new file mode 100644 index 000000000..8bd386da0 --- /dev/null +++ b/toolsrc/src/vcpkg-test/supports.cpp @@ -0,0 +1,79 @@ +#include + +#include + +using namespace vcpkg; +using Parse::parse_comma_list; + +TEST_CASE ("parse supports all", "[supports]") +{ + auto v = Supports::parse({ + "x64", + "x86", + "arm", + "windows", + "uwp", + "v140", + "v141", + "crt-static", + "crt-dynamic", + }); + + REQUIRE(v.has_value()); + + REQUIRE(v.get()->is_supported(System::CPUArchitecture::X64, + Supports::Platform::UWP, + Supports::Linkage::DYNAMIC, + Supports::ToolsetVersion::V140)); + REQUIRE(v.get()->is_supported(System::CPUArchitecture::ARM, + Supports::Platform::WINDOWS, + Supports::Linkage::STATIC, + Supports::ToolsetVersion::V141)); +} + +TEST_CASE ("parse supports invalid", "[supports]") +{ + auto v = Supports::parse({"arm64"}); + + REQUIRE_FALSE(v.has_value()); + + REQUIRE(v.error().size() == 1); + REQUIRE(v.error().at(0) == "arm64"); +} + +TEST_CASE ("parse supports case sensitive", "[supports]") +{ + auto v = Supports::parse({"Windows"}); + + REQUIRE_FALSE(v.has_value()); + REQUIRE(v.error().size() == 1); + REQUIRE(v.error().at(0) == "Windows"); +} + +TEST_CASE ("parse supports some", "[supports]") +{ + auto v = Supports::parse({ + "x64", + "x86", + "windows", + }); + + REQUIRE(v.has_value()); + + REQUIRE(v.get()->is_supported(System::CPUArchitecture::X64, + Supports::Platform::WINDOWS, + Supports::Linkage::DYNAMIC, + Supports::ToolsetVersion::V140)); + REQUIRE_FALSE(v.get()->is_supported(System::CPUArchitecture::ARM, + Supports::Platform::WINDOWS, + Supports::Linkage::DYNAMIC, + Supports::ToolsetVersion::V140)); + REQUIRE_FALSE(v.get()->is_supported(System::CPUArchitecture::X64, + Supports::Platform::UWP, + Supports::Linkage::DYNAMIC, + Supports::ToolsetVersion::V140)); + REQUIRE(v.get()->is_supported(System::CPUArchitecture::X64, + Supports::Platform::WINDOWS, + Supports::Linkage::STATIC, + Supports::ToolsetVersion::V141)); +} diff --git a/toolsrc/src/vcpkg-test/update.cpp b/toolsrc/src/vcpkg-test/update.cpp new file mode 100644 index 000000000..70b2f04c1 --- /dev/null +++ b/toolsrc/src/vcpkg-test/update.cpp @@ -0,0 +1,102 @@ +#include +#include + +#include + +#include + +using namespace vcpkg; +using namespace vcpkg::Update; +using namespace vcpkg::Test; + +using Pgh = std::vector>; + +TEST_CASE ("find outdated packages basic", "[update]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a")); + status_paragraphs.back()->package.version = "2"; + + StatusParagraphs status_db(std::move(status_paragraphs)); + + std::unordered_map map; + auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}})); + map.emplace("a", SourceControlFileLocation{std::move(scf), ""}); + Dependencies::MapPortFileProvider provider(map); + + auto pkgs = SortedVector(Update::find_outdated_packages(provider, status_db), + &OutdatedPackage::compare_by_name); + + REQUIRE(pkgs.size() == 1); + REQUIRE(pkgs[0].version_diff.left.to_string() == "2"); + REQUIRE(pkgs[0].version_diff.right.to_string() == "0"); +} + +TEST_CASE ("find outdated packages features", "[update]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a")); + status_paragraphs.back()->package.version = "2"; + + status_paragraphs.push_back(make_status_feature_pgh("a", "b")); + status_paragraphs.back()->package.version = "2"; + + StatusParagraphs status_db(std::move(status_paragraphs)); + + std::unordered_map map; + auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}})); + map.emplace("a", SourceControlFileLocation{std::move(scf), ""}); + Dependencies::MapPortFileProvider provider(map); + + auto pkgs = SortedVector(Update::find_outdated_packages(provider, status_db), + &OutdatedPackage::compare_by_name); + + REQUIRE(pkgs.size() == 1); + REQUIRE(pkgs[0].version_diff.left.to_string() == "2"); + REQUIRE(pkgs[0].version_diff.right.to_string() == "0"); +} + +TEST_CASE ("find outdated packages features 2", "[update]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a")); + status_paragraphs.back()->package.version = "2"; + + status_paragraphs.push_back(make_status_feature_pgh("a", "b")); + status_paragraphs.back()->package.version = "0"; + status_paragraphs.back()->state = InstallState::NOT_INSTALLED; + status_paragraphs.back()->want = Want::PURGE; + + StatusParagraphs status_db(std::move(status_paragraphs)); + + std::unordered_map map; + auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}})); + map.emplace("a", SourceControlFileLocation{std::move(scf), ""}); + Dependencies::MapPortFileProvider provider(map); + + auto pkgs = SortedVector(Update::find_outdated_packages(provider, status_db), + &OutdatedPackage::compare_by_name); + + REQUIRE(pkgs.size() == 1); + REQUIRE(pkgs[0].version_diff.left.to_string() == "2"); + REQUIRE(pkgs[0].version_diff.right.to_string() == "0"); +} + +TEST_CASE ("find outdated packages none", "[update]") +{ + std::vector> status_paragraphs; + status_paragraphs.push_back(make_status_pgh("a")); + status_paragraphs.back()->package.version = "2"; + + StatusParagraphs status_db(std::move(status_paragraphs)); + + std::unordered_map map; + auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "2"}}})); + map.emplace("a", SourceControlFileLocation{std::move(scf), ""}); + Dependencies::MapPortFileProvider provider(map); + + auto pkgs = SortedVector(Update::find_outdated_packages(provider, status_db), + &OutdatedPackage::compare_by_name); + + REQUIRE(pkgs.size() == 0); +} diff --git a/toolsrc/src/vcpkg-test/util.cpp b/toolsrc/src/vcpkg-test/util.cpp new file mode 100644 index 000000000..19f8f355e --- /dev/null +++ b/toolsrc/src/vcpkg-test/util.cpp @@ -0,0 +1,158 @@ +#include +#include + +#include +#include +#include + +#include +#include + +#if defined(_WIN32) +#include +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1914 + +#define USE_STD_FILESYSTEM + +#include // required for filesystem::create_{directory_}symlink + +#elif !defined(_MSC_VER) + +#include + +#endif + +namespace vcpkg::Test +{ + std::unique_ptr make_status_pgh(const char* name, + const char* depends, + const char* default_features, + const char* triplet) + { + using Pgh = std::unordered_map; + return std::make_unique(Pgh{{"Package", name}, + {"Version", "1"}, + {"Architecture", triplet}, + {"Multi-Arch", "same"}, + {"Depends", depends}, + {"Default-Features", default_features}, + {"Status", "install ok installed"}}); + } + + std::unique_ptr make_status_feature_pgh(const char* name, + const char* feature, + const char* depends, + const char* triplet) + { + using Pgh = std::unordered_map; + return std::make_unique(Pgh{{"Package", name}, + {"Version", "1"}, + {"Feature", feature}, + {"Architecture", triplet}, + {"Multi-Arch", "same"}, + {"Depends", depends}, + {"Status", "install ok installed"}}); + } + + PackageSpec unsafe_pspec(std::string name, Triplet t) + { + auto m_ret = PackageSpec::from_name_and_triplet(name, t); + REQUIRE(m_ret.has_value()); + return m_ret.value_or_exit(VCPKG_LINE_INFO); + } + + + + // I am so sorry for this awful mix of macros + + static bool system_allows_symlinks() { +#if defined(_WIN32) + #if !defined(USE_STD_FILESYSTEM) + return false; + #else + HKEY key; + bool allow_symlinks = true; + + const auto status = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock)", 0, 0, &key); + + if (status == ERROR_FILE_NOT_FOUND) + { + allow_symlinks = false; + std::clog << "Symlinks are not allowed on this system\n"; + } + + if (status == ERROR_SUCCESS) RegCloseKey(key); + + return allow_symlinks; + #endif +#else + return true; +#endif + } + + static fs::path internal_temporary_directory() { +#if defined(_WIN32) + wchar_t* tmp = static_cast(std::calloc(32'767, 2)); + + if (!GetEnvironmentVariableW(L"TEMP", tmp, 32'767)) { + std::cerr << "No temporary directory found.\n"; + std::abort(); + } + + fs::path result = tmp; + std::free(tmp); + + return result / L"vcpkg-test"; +#else + return "/tmp/vcpkg-test"; +#endif + } + + const bool SYMLINKS_ALLOWED = system_allows_symlinks(); + const fs::path TEMPORARY_DIRECTORY = internal_temporary_directory(); + + void create_symlink(const fs::path& target, const fs::path& file, std::error_code& ec) { +#if defined(_MSC_VER) + #if defined(USE_STD_FILESYSTEM) + if (SYMLINKS_ALLOWED) + { + std::filesystem::path targetp = target.native(); + std::filesystem::path filep = file.native(); + + std::filesystem::create_symlink(targetp, filep); + } + else + #endif + { + vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "Symlinks are not allowed on this system"); + } +#else + if(symlink(target.c_str(), file.c_str()) != 0) { + ec.assign(errno, std::system_category()); + } +#endif + } + + void create_directory_symlink(const fs::path& target, const fs::path& file, std::error_code& ec) { +#if defined(_MSC_VER) + #if defined(USE_STD_FILESYSTEM) + if (SYMLINKS_ALLOWED) + { + std::filesystem::path targetp = target.native(); + std::filesystem::path filep = file.native(); + + std::filesystem::create_symlink(targetp, filep); + } + else + #endif + { + vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "Symlinks are not allowed on this system"); + } +#else + ::vcpkg::Test::create_symlink(target, file, ec); +#endif + } +} diff --git a/toolsrc/src/vcpkg-tests/arguments.cpp b/toolsrc/src/vcpkg-tests/arguments.cpp deleted file mode 100644 index 8c625be0f..000000000 --- a/toolsrc/src/vcpkg-tests/arguments.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include - -#include - -#include - -using vcpkg::CommandSetting; -using vcpkg::CommandStructure; -using vcpkg::CommandSwitch; -using vcpkg::VcpkgCmdArguments; - -TEST_CASE ("VcpkgCmdArguments from lowercase argument sequence", "[arguments]") -{ - std::vector t = {"--vcpkg-root", - "C:\\vcpkg", - "--scripts-root=C:\\scripts", - "--debug", - "--sendmetrics", - "--printmetrics", - "--overlay-ports=C:\\ports1", - "--overlay-ports=C:\\ports2", - "--overlay-triplets=C:\\tripletsA", - "--overlay-triplets=C:\\tripletsB"}; - auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); - - REQUIRE(*v.vcpkg_root_dir == "C:\\vcpkg"); - REQUIRE(*v.scripts_root_dir == "C:\\scripts"); - REQUIRE(v.debug); - REQUIRE(*v.debug.get()); - REQUIRE(v.sendmetrics); - REQUIRE(*v.sendmetrics.get()); - REQUIRE(v.printmetrics); - REQUIRE(*v.printmetrics.get()); - - REQUIRE(v.overlay_ports->size() == 2); - REQUIRE(v.overlay_ports->at(0) == "C:\\ports1"); - REQUIRE(v.overlay_ports->at(1) == "C:\\ports2"); - - REQUIRE(v.overlay_triplets->size() == 2); - REQUIRE(v.overlay_triplets->at(0) == "C:\\tripletsA"); - REQUIRE(v.overlay_triplets->at(1) == "C:\\tripletsB"); -} - -TEST_CASE ("VcpkgCmdArguments from uppercase argument sequence", "[arguments]") -{ - std::vector t = {"--VCPKG-ROOT", - "C:\\vcpkg", - "--SCRIPTS-ROOT=C:\\scripts", - "--DEBUG", - "--SENDMETRICS", - "--PRINTMETRICS", - "--OVERLAY-PORTS=C:\\ports1", - "--OVERLAY-PORTS=C:\\ports2", - "--OVERLAY-TRIPLETS=C:\\tripletsA", - "--OVERLAY-TRIPLETS=C:\\tripletsB"}; - auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); - - REQUIRE(*v.vcpkg_root_dir == "C:\\vcpkg"); - REQUIRE(*v.scripts_root_dir == "C:\\scripts"); - REQUIRE(v.debug); - REQUIRE(*v.debug.get()); - REQUIRE(v.sendmetrics); - REQUIRE(*v.sendmetrics.get()); - REQUIRE(v.printmetrics); - REQUIRE(*v.printmetrics.get()); - - REQUIRE(v.overlay_ports->size() == 2); - REQUIRE(v.overlay_ports->at(0) == "C:\\ports1"); - REQUIRE(v.overlay_ports->at(1) == "C:\\ports2"); - - REQUIRE(v.overlay_triplets->size() == 2); - REQUIRE(v.overlay_triplets->at(0) == "C:\\tripletsA"); - REQUIRE(v.overlay_triplets->at(1) == "C:\\tripletsB"); -} - -TEST_CASE ("VcpkgCmdArguments from argument sequence with valued options", "[arguments]") -{ - SECTION ("case 1") - { - std::array settings = {{{"--a", ""}}}; - CommandStructure cmdstruct = {"", 0, SIZE_MAX, {{}, settings}, nullptr}; - - std::vector t = {"--a=b", "command", "argument"}; - auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); - auto opts = v.parse_arguments(cmdstruct); - - REQUIRE(opts.settings["--a"] == "b"); - REQUIRE(v.command_arguments.size() == 1); - REQUIRE(v.command_arguments[0] == "argument"); - REQUIRE(v.command == "command"); - } - - SECTION ("case 2") - { - std::array switches = {{{"--a", ""}, {"--c", ""}}}; - std::array settings = {{{"--b", ""}, {"--d", ""}}}; - CommandStructure cmdstruct = {"", 0, SIZE_MAX, {switches, settings}, nullptr}; - - std::vector t = {"--a", "--b=c"}; - auto v = VcpkgCmdArguments::create_from_arg_sequence(t.data(), t.data() + t.size()); - auto opts = v.parse_arguments(cmdstruct); - - REQUIRE(opts.settings["--b"] == "c"); - REQUIRE(opts.settings.find("--d") == opts.settings.end()); - REQUIRE(opts.switches.find("--a") != opts.switches.end()); - REQUIRE(opts.settings.find("--c") == opts.settings.end()); - REQUIRE(v.command_arguments.size() == 0); - } -} diff --git a/toolsrc/src/vcpkg-tests/catch.cpp b/toolsrc/src/vcpkg-tests/catch.cpp deleted file mode 100644 index 701dcb39a..000000000 --- a/toolsrc/src/vcpkg-tests/catch.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#define CATCH_CONFIG_RUNNER -#include - -#include - -int main(int argc, char** argv) -{ - vcpkg::Debug::g_debugging = true; - - return Catch::Session().run(argc, argv); -} diff --git a/toolsrc/src/vcpkg-tests/chrono.cpp b/toolsrc/src/vcpkg-tests/chrono.cpp deleted file mode 100644 index c164753f9..000000000 --- a/toolsrc/src/vcpkg-tests/chrono.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include - -#include - -namespace Chrono = vcpkg::Chrono; - -TEST_CASE ("parse time", "[chrono]") -{ - auto timestring = "1990-02-03T04:05:06.0Z"; - auto maybe_time = Chrono::CTime::parse(timestring); - - REQUIRE(maybe_time.has_value()); - REQUIRE(maybe_time.get()->to_string() == timestring); -} - -TEST_CASE ("parse blank time", "[chrono]") -{ - auto maybe_time = Chrono::CTime::parse(""); - - REQUIRE_FALSE(maybe_time.has_value()); -} - -TEST_CASE ("difference of times", "[chrono]") -{ - auto maybe_time1 = Chrono::CTime::parse("1990-02-03T04:05:06.0Z"); - auto maybe_time2 = Chrono::CTime::parse("1990-02-10T04:05:06.0Z"); - - REQUIRE(maybe_time1.has_value()); - REQUIRE(maybe_time2.has_value()); - - auto delta = maybe_time2.get()->to_time_point() - maybe_time1.get()->to_time_point(); - - REQUIRE(std::chrono::duration_cast(delta).count() == 24 * 7); -} diff --git a/toolsrc/src/vcpkg-tests/dependencies.cpp b/toolsrc/src/vcpkg-tests/dependencies.cpp deleted file mode 100644 index 0dee6f296..000000000 --- a/toolsrc/src/vcpkg-tests/dependencies.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include - -#include - -using namespace vcpkg; -using Parse::parse_comma_list; - -TEST_CASE ("parse depends", "[dependencies]") -{ - auto v = expand_qualified_dependencies(parse_comma_list("libA (windows)")); - REQUIRE(v.size() == 1); - REQUIRE(v.at(0).depend.name == "libA"); - REQUIRE(v.at(0).qualifier == "windows"); -} - -TEST_CASE ("filter depends", "[dependencies]") -{ - auto deps = expand_qualified_dependencies(parse_comma_list("libA (windows), libB, libC (uwp)")); - auto v = filter_dependencies(deps, Triplet::X64_WINDOWS); - REQUIRE(v.size() == 2); - REQUIRE(v.at(0) == "libA"); - REQUIRE(v.at(1) == "libB"); - - auto v2 = filter_dependencies(deps, Triplet::ARM_UWP); - REQUIRE(v.size() == 2); - REQUIRE(v2.at(0) == "libB"); - REQUIRE(v2.at(1) == "libC"); -} diff --git a/toolsrc/src/vcpkg-tests/files.cpp b/toolsrc/src/vcpkg-tests/files.cpp deleted file mode 100644 index d2edffcff..000000000 --- a/toolsrc/src/vcpkg-tests/files.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include -#include - -#include -#include - -#include // required for filesystem::create_{directory_}symlink -#include -#include - -#include - -using vcpkg::Test::SYMLINKS_ALLOWED; -using vcpkg::Test::TEMPORARY_DIRECTORY; - -namespace -{ - using uid = std::uniform_int_distribution; - - std::mt19937_64 get_urbg(std::uint64_t index) - { - // smallest prime > 2**63 - 1 - return std::mt19937_64{index + 9223372036854775837}; - } - - std::string get_random_filename(std::mt19937_64& urbg) { return vcpkg::Strings::b32_encode(uid{}(urbg)); } - - void create_directory_tree(std::mt19937_64& urbg, - vcpkg::Files::Filesystem& fs, - std::uint64_t depth, - const fs::path& base) - { - std::random_device rd; - constexpr std::uint64_t max_depth = 5; - constexpr std::uint64_t width = 5; - - // we want ~70% of our "files" to be directories, and then a third - // each of the remaining ~30% to be regular files, directory symlinks, - // and regular symlinks - constexpr std::uint64_t directory_min_tag = 0; - constexpr std::uint64_t directory_max_tag = 6; - constexpr std::uint64_t regular_file_tag = 7; - constexpr std::uint64_t regular_symlink_tag = 8; - constexpr std::uint64_t directory_symlink_tag = 9; - - // if we're at the max depth, we only want to build non-directories - std::uint64_t file_type; - if (depth < max_depth) - { - file_type = uid{directory_min_tag, regular_symlink_tag}(urbg); - } - else - { - file_type = uid{regular_file_tag, regular_symlink_tag}(urbg); - } - - if (!SYMLINKS_ALLOWED && file_type > regular_file_tag) - { - file_type = regular_file_tag; - } - - std::error_code ec; - if (file_type <= directory_max_tag) - { - fs.create_directory(base, ec); - if (ec) { - INFO("File that failed: " << base); - REQUIRE_FALSE(ec); - } - - for (int i = 0; i < width; ++i) - { - create_directory_tree(urbg, fs, depth + 1, base / get_random_filename(urbg)); - } - } - else if (file_type == regular_file_tag) - { - // regular file - fs.write_contents(base, "", ec); - } - else if (file_type == regular_symlink_tag) - { - // regular symlink - fs.write_contents(base, "", ec); - REQUIRE_FALSE(ec); - const std::filesystem::path basep = base.native(); - auto basep_link = basep; - basep_link.replace_filename(basep.filename().native() + L"-link"); - std::filesystem::create_symlink(basep, basep_link, ec); - } - else // type == directory_symlink_tag - { - // directory symlink - std::filesystem::path basep = base.native(); - std::filesystem::create_directory_symlink(basep / "..", basep, ec); - } - - REQUIRE_FALSE(ec); - } -} - -TEST_CASE ("remove all", "[files]") -{ - auto urbg = get_urbg(0); - - fs::path temp_dir = TEMPORARY_DIRECTORY / get_random_filename(urbg); - - auto& fs = vcpkg::Files::get_real_filesystem(); - - std::error_code ec; - fs.create_directory(TEMPORARY_DIRECTORY, ec); - - REQUIRE_FALSE(ec); - - INFO("temp dir is: " << temp_dir); - - create_directory_tree(urbg, fs, 0, temp_dir); - - fs::path fp; - fs.remove_all(temp_dir, ec, fp); - REQUIRE_FALSE(ec); - - REQUIRE_FALSE(fs.exists(temp_dir)); -} diff --git a/toolsrc/src/vcpkg-tests/paragraph.cpp b/toolsrc/src/vcpkg-tests/paragraph.cpp deleted file mode 100644 index 0fb85ec69..000000000 --- a/toolsrc/src/vcpkg-tests/paragraph.cpp +++ /dev/null @@ -1,445 +0,0 @@ -#include -#include - -#include - -#include - -namespace Strings = vcpkg::Strings; - -TEST_CASE ("SourceParagraph construct minimum", "[paragraph]") -{ - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "zlib"}, - {"Version", "1.2.8"}, - }}); - - REQUIRE(m_pgh.has_value()); - auto& pgh = **m_pgh.get(); - - REQUIRE(pgh.core_paragraph->name == "zlib"); - REQUIRE(pgh.core_paragraph->version == "1.2.8"); - REQUIRE(pgh.core_paragraph->maintainer == ""); - REQUIRE(pgh.core_paragraph->description == ""); - REQUIRE(pgh.core_paragraph->depends.size() == 0); -} - -TEST_CASE ("SourceParagraph construct maximum", "[paragraph]") -{ - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "s"}, - {"Version", "v"}, - {"Maintainer", "m"}, - {"Description", "d"}, - {"Build-Depends", "bd"}, - {"Default-Features", "df"}, - {"Supports", "x64"}, - }}); - REQUIRE(m_pgh.has_value()); - auto& pgh = **m_pgh.get(); - - REQUIRE(pgh.core_paragraph->name == "s"); - REQUIRE(pgh.core_paragraph->version == "v"); - REQUIRE(pgh.core_paragraph->maintainer == "m"); - REQUIRE(pgh.core_paragraph->description == "d"); - REQUIRE(pgh.core_paragraph->depends.size() == 1); - REQUIRE(pgh.core_paragraph->depends[0].name() == "bd"); - REQUIRE(pgh.core_paragraph->default_features.size() == 1); - REQUIRE(pgh.core_paragraph->default_features[0] == "df"); - REQUIRE(pgh.core_paragraph->supports.size() == 1); - REQUIRE(pgh.core_paragraph->supports[0] == "x64"); -} - -TEST_CASE ("SourceParagraph two depends", "[paragraph]") -{ - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "zlib"}, - {"Version", "1.2.8"}, - {"Build-Depends", "z, openssl"}, - }}); - REQUIRE(m_pgh.has_value()); - auto& pgh = **m_pgh.get(); - - REQUIRE(pgh.core_paragraph->depends.size() == 2); - REQUIRE(pgh.core_paragraph->depends[0].name() == "z"); - REQUIRE(pgh.core_paragraph->depends[1].name() == "openssl"); -} - -TEST_CASE ("SourceParagraph three depends", "[paragraph]") -{ - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "zlib"}, - {"Version", "1.2.8"}, - {"Build-Depends", "z, openssl, xyz"}, - }}); - REQUIRE(m_pgh.has_value()); - auto& pgh = **m_pgh.get(); - - REQUIRE(pgh.core_paragraph->depends.size() == 3); - REQUIRE(pgh.core_paragraph->depends[0].name() == "z"); - REQUIRE(pgh.core_paragraph->depends[1].name() == "openssl"); - REQUIRE(pgh.core_paragraph->depends[2].name() == "xyz"); -} - -TEST_CASE ("SourceParagraph three supports", "[paragraph]") -{ - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "zlib"}, - {"Version", "1.2.8"}, - {"Supports", "x64, windows, uwp"}, - }}); - REQUIRE(m_pgh.has_value()); - auto& pgh = **m_pgh.get(); - - REQUIRE(pgh.core_paragraph->supports.size() == 3); - REQUIRE(pgh.core_paragraph->supports[0] == "x64"); - REQUIRE(pgh.core_paragraph->supports[1] == "windows"); - REQUIRE(pgh.core_paragraph->supports[2] == "uwp"); -} - -TEST_CASE ("SourceParagraph construct qualified depends", "[paragraph]") -{ - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "zlib"}, - {"Version", "1.2.8"}, - {"Build-Depends", "libA (windows), libB (uwp)"}, - }}); - REQUIRE(m_pgh.has_value()); - auto& pgh = **m_pgh.get(); - - REQUIRE(pgh.core_paragraph->name == "zlib"); - REQUIRE(pgh.core_paragraph->version == "1.2.8"); - REQUIRE(pgh.core_paragraph->maintainer == ""); - REQUIRE(pgh.core_paragraph->description == ""); - REQUIRE(pgh.core_paragraph->depends.size() == 2); - REQUIRE(pgh.core_paragraph->depends[0].name() == "libA"); - REQUIRE(pgh.core_paragraph->depends[0].qualifier == "windows"); - REQUIRE(pgh.core_paragraph->depends[1].name() == "libB"); - REQUIRE(pgh.core_paragraph->depends[1].qualifier == "uwp"); -} - -TEST_CASE ("SourceParagraph default features", "[paragraph]") -{ - auto m_pgh = - vcpkg::SourceControlFile::parse_control_file(std::vector>{{ - {"Source", "a"}, - {"Version", "1.0"}, - {"Default-Features", "a1"}, - }}); - REQUIRE(m_pgh.has_value()); - auto& pgh = **m_pgh.get(); - - REQUIRE(pgh.core_paragraph->default_features.size() == 1); - REQUIRE(pgh.core_paragraph->default_features[0] == "a1"); -} - -TEST_CASE ("BinaryParagraph construct minimum", "[paragraph]") -{ - vcpkg::BinaryParagraph pgh({ - {"Package", "zlib"}, - {"Version", "1.2.8"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - }); - - REQUIRE(pgh.spec.name() == "zlib"); - REQUIRE(pgh.version == "1.2.8"); - REQUIRE(pgh.maintainer == ""); - REQUIRE(pgh.description == ""); - REQUIRE(pgh.spec.triplet().canonical_name() == "x86-windows"); - REQUIRE(pgh.depends.size() == 0); -} - -TEST_CASE ("BinaryParagraph construct maximum", "[paragraph]") -{ - vcpkg::BinaryParagraph pgh({ - {"Package", "s"}, - {"Version", "v"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - {"Maintainer", "m"}, - {"Description", "d"}, - {"Depends", "bd"}, - }); - - REQUIRE(pgh.spec.name() == "s"); - REQUIRE(pgh.version == "v"); - REQUIRE(pgh.maintainer == "m"); - REQUIRE(pgh.description == "d"); - REQUIRE(pgh.depends.size() == 1); - REQUIRE(pgh.depends[0] == "bd"); -} - -TEST_CASE ("BinaryParagraph three depends", "[paragraph]") -{ - vcpkg::BinaryParagraph pgh({ - {"Package", "zlib"}, - {"Version", "1.2.8"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - {"Depends", "a, b, c"}, - }); - - REQUIRE(pgh.depends.size() == 3); - REQUIRE(pgh.depends[0] == "a"); - REQUIRE(pgh.depends[1] == "b"); - REQUIRE(pgh.depends[2] == "c"); -} - -TEST_CASE ("BinaryParagraph abi", "[paragraph]") -{ - vcpkg::BinaryParagraph pgh({ - {"Package", "zlib"}, - {"Version", "1.2.8"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - {"Abi", "abcd123"}, - }); - - REQUIRE(pgh.depends.size() == 0); - REQUIRE(pgh.abi == "abcd123"); -} - -TEST_CASE ("BinaryParagraph default features", "[paragraph]") -{ - vcpkg::BinaryParagraph pgh({ - {"Package", "a"}, - {"Version", "1.0"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - {"Default-Features", "a1"}, - }); - - REQUIRE(pgh.depends.size() == 0); - REQUIRE(pgh.default_features.size() == 1); - REQUIRE(pgh.default_features[0] == "a1"); -} - -TEST_CASE ("parse paragraphs empty", "[paragraph]") -{ - const char* str = ""; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - REQUIRE(pghs.empty()); -} - -TEST_CASE ("parse paragraphs one field", "[paragraph]") -{ - const char* str = "f1: v1"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - REQUIRE(pghs.size() == 1); - REQUIRE(pghs[0].size() == 1); - REQUIRE(pghs[0]["f1"] == "v1"); -} - -TEST_CASE ("parse paragraphs one pgh", "[paragraph]") -{ - const char* str = "f1: v1\n" - "f2: v2"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - REQUIRE(pghs.size() == 1); - REQUIRE(pghs[0].size() == 2); - REQUIRE(pghs[0]["f1"] == "v1"); - REQUIRE(pghs[0]["f2"] == "v2"); -} - -TEST_CASE ("parse paragraphs two pgh", "[paragraph]") -{ - const char* str = "f1: v1\n" - "f2: v2\n" - "\n" - "f3: v3\n" - "f4: v4"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - - REQUIRE(pghs.size() == 2); - REQUIRE(pghs[0].size() == 2); - REQUIRE(pghs[0]["f1"] == "v1"); - REQUIRE(pghs[0]["f2"] == "v2"); - REQUIRE(pghs[1].size() == 2); - REQUIRE(pghs[1]["f3"] == "v3"); - REQUIRE(pghs[1]["f4"] == "v4"); -} - -TEST_CASE ("parse paragraphs field names", "[paragraph]") -{ - const char* str = "1:\n" - "f:\n" - "F:\n" - "0:\n" - "F-2:\n"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - - REQUIRE(pghs.size() == 1); - REQUIRE(pghs[0].size() == 5); -} - -TEST_CASE ("parse paragraphs multiple blank lines", "[paragraph]") -{ - const char* str = "f1: v1\n" - "f2: v2\n" - "\n" - "\n" - "f3: v3\n" - "f4: v4"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - - REQUIRE(pghs.size() == 2); -} - -TEST_CASE ("parse paragraphs empty fields", "[paragraph]") -{ - const char* str = "f1:\n" - "f2: "; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - - REQUIRE(pghs.size() == 1); - REQUIRE(pghs[0].size() == 2); - REQUIRE(pghs[0]["f1"] == ""); - REQUIRE(pghs[0]["f2"] == ""); - REQUIRE(pghs[0].size() == 2); -} - -TEST_CASE ("parse paragraphs multiline fields", "[paragraph]") -{ - const char* str = "f1: simple\n" - " f1\r\n" - "f2:\r\n" - " f2\r\n" - " continue\r\n"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - - REQUIRE(pghs.size() == 1); - REQUIRE(pghs[0]["f1"] == "simple\n f1"); - REQUIRE(pghs[0]["f2"] == "\n f2\n continue"); -} - -TEST_CASE ("parse paragraphs crlfs", "[paragraph]") -{ - const char* str = "f1: v1\r\n" - "f2: v2\r\n" - "\r\n" - "f3: v3\r\n" - "f4: v4"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - - REQUIRE(pghs.size() == 2); - REQUIRE(pghs[0].size() == 2); - REQUIRE(pghs[0]["f1"] == "v1"); - REQUIRE(pghs[0]["f2"] == "v2"); - REQUIRE(pghs[1].size() == 2); - REQUIRE(pghs[1]["f3"] == "v3"); - REQUIRE(pghs[1]["f4"] == "v4"); -} - -TEST_CASE ("parse paragraphs comment", "[paragraph]") -{ - const char* str = "f1: v1\r\n" - "#comment\r\n" - "f2: v2\r\n" - "#comment\r\n" - "\r\n" - "#comment\r\n" - "f3: v3\r\n" - "#comment\r\n" - "f4: v4"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - - REQUIRE(pghs.size() == 2); - REQUIRE(pghs[0].size() == 2); - REQUIRE(pghs[0]["f1"] == "v1"); - REQUIRE(pghs[0]["f2"] == "v2"); - REQUIRE(pghs[1].size()); - REQUIRE(pghs[1]["f3"] == "v3"); - REQUIRE(pghs[1]["f4"] == "v4"); -} - -TEST_CASE ("parse comment before single line feed", "[paragraph]") -{ - const char* str = "f1: v1\r\n" - "#comment\n"; - auto pghs = vcpkg::Paragraphs::parse_paragraphs(str).value_or_exit(VCPKG_LINE_INFO); - REQUIRE(pghs[0].size() == 1); - REQUIRE(pghs[0]["f1"] == "v1"); -} - -TEST_CASE ("BinaryParagraph serialize min", "[paragraph]") -{ - vcpkg::BinaryParagraph pgh({ - {"Package", "zlib"}, - {"Version", "1.2.8"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - }); - std::string ss = Strings::serialize(pgh); - auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); - - REQUIRE(pghs.size() == 1); - REQUIRE(pghs[0].size() == 4); - REQUIRE(pghs[0]["Package"] == "zlib"); - REQUIRE(pghs[0]["Version"] == "1.2.8"); - REQUIRE(pghs[0]["Architecture"] == "x86-windows"); - REQUIRE(pghs[0]["Multi-Arch"] == "same"); -} - -TEST_CASE ("BinaryParagraph serialize max", "[paragraph]") -{ - vcpkg::BinaryParagraph pgh({ - {"Package", "zlib"}, - {"Version", "1.2.8"}, - {"Architecture", "x86-windows"}, - {"Description", "first line\n second line"}, - {"Maintainer", "abc "}, - {"Depends", "dep"}, - {"Multi-Arch", "same"}, - }); - std::string ss = Strings::serialize(pgh); - auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); - - REQUIRE(pghs.size() == 1); - REQUIRE(pghs[0].size() == 7); - REQUIRE(pghs[0]["Package"] == "zlib"); - REQUIRE(pghs[0]["Version"] == "1.2.8"); - REQUIRE(pghs[0]["Architecture"] == "x86-windows"); - REQUIRE(pghs[0]["Multi-Arch"] == "same"); - REQUIRE(pghs[0]["Description"] == "first line\n second line"); - REQUIRE(pghs[0]["Depends"] == "dep"); -} - -TEST_CASE ("BinaryParagraph serialize multiple deps", "[paragraph]") -{ - vcpkg::BinaryParagraph pgh({ - {"Package", "zlib"}, - {"Version", "1.2.8"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - {"Depends", "a, b, c"}, - }); - std::string ss = Strings::serialize(pgh); - auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); - - REQUIRE(pghs.size() == 1); - REQUIRE(pghs[0]["Depends"] == "a, b, c"); -} - -TEST_CASE ("BinaryParagraph serialize abi", "[paragraph]") -{ - vcpkg::BinaryParagraph pgh({ - {"Package", "zlib"}, - {"Version", "1.2.8"}, - {"Architecture", "x86-windows"}, - {"Multi-Arch", "same"}, - {"Depends", "a, b, c"}, - {"Abi", "123abc"}, - }); - std::string ss = Strings::serialize(pgh); - auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss).value_or_exit(VCPKG_LINE_INFO); - - REQUIRE(pghs.size() == 1); - REQUIRE(pghs[0]["Abi"] == "123abc"); -} diff --git a/toolsrc/src/vcpkg-tests/plan.cpp b/toolsrc/src/vcpkg-tests/plan.cpp deleted file mode 100644 index 7ecab460b..000000000 --- a/toolsrc/src/vcpkg-tests/plan.cpp +++ /dev/null @@ -1,1241 +0,0 @@ -#include -#include - -#include -#include -#include - -#include -#include -#include - -using namespace vcpkg; - -using Test::make_status_feature_pgh; -using Test::make_status_pgh; -using Test::unsafe_pspec; - -static std::unique_ptr make_control_file( - const char* name, - const char* depends, - const std::vector>& features = {}, - const std::vector& default_features = {}) -{ - using Pgh = std::unordered_map; - std::vector scf_pghs; - scf_pghs.push_back(Pgh{{"Source", name}, - {"Version", "0"}, - {"Build-Depends", depends}, - {"Default-Features", Strings::join(", ", default_features)}}); - for (auto&& feature : features) - { - scf_pghs.push_back(Pgh{ - {"Feature", feature.first}, - {"Description", "feature"}, - {"Build-Depends", feature.second}, - }); - } - auto m_pgh = vcpkg::SourceControlFile::parse_control_file(std::move(scf_pghs)); - REQUIRE(m_pgh.has_value()); - return std::move(*m_pgh.get()); -} - -/// -/// Assert that the given action an install of given features from given package. -/// -static void features_check(Dependencies::AnyAction& install_action, - std::string pkg_name, - std::vector vec, - const Triplet& triplet = Triplet::X86_WINDOWS) -{ - REQUIRE(install_action.install_action.has_value()); - const auto& plan = install_action.install_action.value_or_exit(VCPKG_LINE_INFO); - const auto& feature_list = plan.feature_list; - - REQUIRE(plan.spec.triplet().to_string() == triplet.to_string()); - - auto& scfl = *plan.source_control_file_location.get(); - REQUIRE(pkg_name == scfl.source_control_file->core_paragraph->name); - REQUIRE(feature_list.size() == vec.size()); - - for (auto&& feature_name : vec) - { - // TODO: see if this can be simplified - if (feature_name == "core" || feature_name == "") - { - REQUIRE((Util::find(feature_list, "core") != feature_list.end() || - Util::find(feature_list, "") != feature_list.end())); - continue; - } - REQUIRE(Util::find(feature_list, feature_name) != feature_list.end()); - } -} - -/// -/// Assert that the given action is a remove of given package. -/// -static void remove_plan_check(Dependencies::AnyAction& remove_action, - std::string pkg_name, - const Triplet& triplet = Triplet::X86_WINDOWS) -{ - const auto& plan = remove_action.remove_action.value_or_exit(VCPKG_LINE_INFO); - REQUIRE(plan.spec.triplet().to_string() == triplet.to_string()); - REQUIRE(pkg_name == plan.spec.name()); -} - -/// -/// Map of source control files by their package name. -/// -struct PackageSpecMap -{ - std::unordered_map map; - Triplet triplet; - PackageSpecMap(const Triplet& t = Triplet::X86_WINDOWS) noexcept { triplet = t; } - - PackageSpec emplace(const char* name, - const char* depends = "", - const std::vector>& features = {}, - const std::vector& default_features = {}) - { - auto scfl = SourceControlFileLocation{make_control_file(name, depends, features, default_features), ""}; - return emplace(std::move(scfl)); - } - - PackageSpec emplace(vcpkg::SourceControlFileLocation&& scfl) - { - auto spec = PackageSpec::from_name_and_triplet(scfl.source_control_file->core_paragraph->name, triplet); - REQUIRE(spec.has_value()); - map.emplace(scfl.source_control_file->core_paragraph->name, std::move(scfl)); - return PackageSpec{*spec.get()}; - } -}; - -TEST_CASE ("basic install scheme", "[plan]") -{ - std::vector> status_paragraphs; - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a", "b"); - auto spec_b = spec_map.emplace("b", "c"); - auto spec_c = spec_map.emplace("c"); - - Dependencies::MapPortFileProvider map_port(spec_map.map); - auto install_plan = Dependencies::create_feature_install_plan( - map_port, {FeatureSpec{spec_a, ""}}, StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 3); - REQUIRE(install_plan.at(0).spec().name() == "c"); - REQUIRE(install_plan.at(1).spec().name() == "b"); - REQUIRE(install_plan.at(2).spec().name() == "a"); -} - -TEST_CASE ("multiple install scheme", "[plan]") -{ - std::vector> status_paragraphs; - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a", "d"); - auto spec_b = spec_map.emplace("b", "d, e"); - auto spec_c = spec_map.emplace("c", "e, h"); - auto spec_d = spec_map.emplace("d", "f, g, h"); - auto spec_e = spec_map.emplace("e", "g"); - auto spec_f = spec_map.emplace("f"); - auto spec_g = spec_map.emplace("g"); - auto spec_h = spec_map.emplace("h"); - - Dependencies::MapPortFileProvider map_port(spec_map.map); - auto install_plan = Dependencies::create_feature_install_plan( - map_port, - {FeatureSpec{spec_a, ""}, FeatureSpec{spec_b, ""}, FeatureSpec{spec_c, ""}}, - StatusParagraphs(std::move(status_paragraphs))); - - auto iterator_pos = [&](const PackageSpec& spec) { - auto it = - std::find_if(install_plan.begin(), install_plan.end(), [&](auto& action) { return action.spec() == spec; }); - REQUIRE(it != install_plan.end()); - return it - install_plan.begin(); - }; - - const auto a_pos = iterator_pos(spec_a); - const auto b_pos = iterator_pos(spec_b); - const auto c_pos = iterator_pos(spec_c); - const auto d_pos = iterator_pos(spec_d); - const auto e_pos = iterator_pos(spec_e); - const auto f_pos = iterator_pos(spec_f); - const auto g_pos = iterator_pos(spec_g); - const auto h_pos = iterator_pos(spec_h); - - REQUIRE(a_pos > d_pos); - REQUIRE(b_pos > e_pos); - REQUIRE(b_pos > d_pos); - REQUIRE(c_pos > e_pos); - REQUIRE(c_pos > h_pos); - REQUIRE(d_pos > f_pos); - REQUIRE(d_pos > g_pos); - REQUIRE(d_pos > h_pos); - REQUIRE(e_pos > g_pos); -} - -TEST_CASE ("existing package scheme", "[plan]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(vcpkg::Test::make_status_pgh("a")); - - PackageSpecMap spec_map; - auto spec_a = FullPackageSpec{spec_map.emplace("a")}; - - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 1); - const auto p = install_plan.at(0).install_action.get(); - REQUIRE(p); - REQUIRE(p->spec.name() == "a"); - REQUIRE(p->plan_type == Dependencies::InstallPlanType::ALREADY_INSTALLED); - REQUIRE(p->request_type == Dependencies::RequestType::USER_REQUESTED); -} - -TEST_CASE ("user requested package scheme", "[plan]") -{ - std::vector> status_paragraphs; - - PackageSpecMap spec_map; - const auto spec_a = FullPackageSpec{spec_map.emplace("a", "b")}; - const auto spec_b = FullPackageSpec{spec_map.emplace("b")}; - - const auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 2); - const auto p = install_plan.at(0).install_action.get(); - REQUIRE(p); - REQUIRE(p->spec.name() == "b"); - REQUIRE(p->plan_type == Dependencies::InstallPlanType::BUILD_AND_INSTALL); - REQUIRE(p->request_type == Dependencies::RequestType::AUTO_SELECTED); - - const auto p2 = install_plan.at(1).install_action.get(); - REQUIRE(p2); - REQUIRE(p2->spec.name() == "a"); - REQUIRE(p2->plan_type == Dependencies::InstallPlanType::BUILD_AND_INSTALL); - REQUIRE(p2->request_type == Dependencies::RequestType::USER_REQUESTED); -} - -TEST_CASE ("long install scheme", "[plan]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("j", "k")); - status_paragraphs.push_back(make_status_pgh("k")); - - PackageSpecMap spec_map; - - auto spec_a = spec_map.emplace("a", "b, c, d, e, f, g, h, j, k"); - auto spec_b = spec_map.emplace("b", "c, d, e, f, g, h, j, k"); - auto spec_c = spec_map.emplace("c", "d, e, f, g, h, j, k"); - auto spec_d = spec_map.emplace("d", "e, f, g, h, j, k"); - auto spec_e = spec_map.emplace("e", "f, g, h, j, k"); - auto spec_f = spec_map.emplace("f", "g, h, j, k"); - auto spec_g = spec_map.emplace("g", "h, j, k"); - auto spec_h = spec_map.emplace("h", "j, k"); - auto spec_j = spec_map.emplace("j", "k"); - auto spec_k = spec_map.emplace("k"); - - Dependencies::MapPortFileProvider map_port(spec_map.map); - auto install_plan = Dependencies::create_feature_install_plan( - map_port, {FeatureSpec{spec_a, ""}}, StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 8); - REQUIRE(install_plan.at(0).spec().name() == "h"); - REQUIRE(install_plan.at(1).spec().name() == "g"); - REQUIRE(install_plan.at(2).spec().name() == "f"); - REQUIRE(install_plan.at(3).spec().name() == "e"); - REQUIRE(install_plan.at(4).spec().name() == "d"); - REQUIRE(install_plan.at(5).spec().name() == "c"); - REQUIRE(install_plan.at(6).spec().name() == "b"); - REQUIRE(install_plan.at(7).spec().name() == "a"); -} - -TEST_CASE ("basic feature test 1", "[plan]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a", "b, b[b1]")); - status_paragraphs.push_back(make_status_pgh("b")); - status_paragraphs.push_back(make_status_feature_pgh("b", "b1")); - - PackageSpecMap spec_map; - auto spec_a = FullPackageSpec{spec_map.emplace("a", "b, b[b1]", {{"a1", "b[b2]"}}), {"a1"}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}, {"b3", ""}})}; - - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 4); - remove_plan_check(install_plan.at(0), "a"); - remove_plan_check(install_plan.at(1), "b"); - features_check(install_plan.at(2), "b", {"b1", "core", "b1"}); - features_check(install_plan.at(3), "a", {"a1", "core"}); -} - -TEST_CASE ("basic feature test 2", "[plan]") -{ - std::vector> status_paragraphs; - - PackageSpecMap spec_map; - - auto spec_a = FullPackageSpec{spec_map.emplace("a", "b[b1]", {{"a1", "b[b2]"}}), {"a1"}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}, {"b3", ""}})}; - - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 2); - features_check(install_plan.at(0), "b", {"b1", "b2", "core"}); - features_check(install_plan.at(1), "a", {"a1", "core"}); -} - -TEST_CASE ("basic feature test 3", "[plan]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a")); - - PackageSpecMap spec_map; - - auto spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b")}; - auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; - - auto install_plan = Dependencies::create_feature_install_plan(spec_map.map, - FullPackageSpec::to_feature_specs({spec_c, spec_a}), - StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 4); - remove_plan_check(install_plan.at(0), "a"); - features_check(install_plan.at(1), "b", {"core"}); - features_check(install_plan.at(2), "a", {"a1", "core"}); - features_check(install_plan.at(3), "c", {"core"}); -} - -TEST_CASE ("basic feature test 4", "[plan]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a")); - status_paragraphs.push_back(make_status_feature_pgh("a", "a1", "")); - - PackageSpecMap spec_map; - - auto spec_a = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}})}; - auto spec_b = FullPackageSpec{spec_map.emplace("b")}; - auto spec_c = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; - - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, FullPackageSpec::to_feature_specs({spec_c}), StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 1); - features_check(install_plan.at(0), "c", {"core"}); -} - -TEST_CASE ("basic feature test 5", "[plan]") -{ - std::vector> status_paragraphs; - - PackageSpecMap spec_map; - - auto spec_a = - FullPackageSpec{spec_map.emplace("a", "", {{"a1", "b[b1]"}, {"a2", "b[b2]"}, {"a3", "a[a2]"}}), {"a3"}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}, {"b2", ""}})}; - - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, FullPackageSpec::to_feature_specs({spec_a}), StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 2); - features_check(install_plan.at(0), "b", {"core", "b2"}); - features_check(install_plan.at(1), "a", {"core", "a3", "a2"}); -} - -TEST_CASE ("basic feature test 6", "[plan]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("b")); - - PackageSpecMap spec_map; - auto spec_a = FullPackageSpec{spec_map.emplace("a", "b[core]"), {"core"}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {"b1"}}; - - auto install_plan = Dependencies::create_feature_install_plan(spec_map.map, - FullPackageSpec::to_feature_specs({spec_a, spec_b}), - StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 3); - remove_plan_check(install_plan.at(0), "b"); - features_check(install_plan.at(1), "b", {"core", "b1"}); - features_check(install_plan.at(2), "a", {"core"}); -} - -TEST_CASE ("basic feature test 7", "[plan]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("x", "b")); - status_paragraphs.push_back(make_status_pgh("b")); - - PackageSpecMap spec_map; - - auto spec_a = FullPackageSpec{spec_map.emplace("a")}; - auto spec_x = FullPackageSpec{spec_map.emplace("x", "a"), {"core"}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {"b1"}}; - - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, FullPackageSpec::to_feature_specs({spec_b}), StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 5); - remove_plan_check(install_plan.at(0), "x"); - remove_plan_check(install_plan.at(1), "b"); - - // TODO: order here may change but A < X, and B anywhere - features_check(install_plan.at(2), "b", {"core", "b1"}); - features_check(install_plan.at(3), "a", {"core"}); - features_check(install_plan.at(4), "x", {"core"}); -} - -TEST_CASE ("basic feature test 8", "[plan][!mayfail]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a")); - status_paragraphs.push_back(make_status_pgh("a")); - status_paragraphs.back()->package.spec = - PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); - - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}}; - auto spec_b_64 = FullPackageSpec{spec_map.emplace("b")}; - auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; - - spec_map.triplet = Triplet::X86_WINDOWS; - auto spec_a_86 = FullPackageSpec{spec_map.emplace("a", "b", {{"a1", ""}}), {"core"}}; - auto spec_b_86 = FullPackageSpec{spec_map.emplace("b")}; - auto spec_c_86 = FullPackageSpec{spec_map.emplace("c", "a[a1]"), {"core"}}; - - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({spec_c_64, spec_a_86, spec_a_64, spec_c_86}), - StatusParagraphs(std::move(status_paragraphs))); - - remove_plan_check(install_plan.at(0), "a", Triplet::X64_WINDOWS); - remove_plan_check(install_plan.at(1), "a"); - features_check(install_plan.at(2), "b", {"core"}, Triplet::X64_WINDOWS); - features_check(install_plan.at(3), "a", {"a1", "core"}, Triplet::X64_WINDOWS); - features_check(install_plan.at(4), "c", {"core"}, Triplet::X64_WINDOWS); - features_check(install_plan.at(5), "b", {"core"}); - features_check(install_plan.at(6), "a", {"a1", "core"}); - features_check(install_plan.at(7), "c", {"core"}); -} - -TEST_CASE ("install all features test", "[plan]") -{ - std::vector> status_paragraphs; - - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "", {{"0", ""}, {"1", ""}}), {"core"}}; - - auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS); - REQUIRE(install_specs.has_value()); - if (!install_specs.has_value()) return; - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 1); - features_check(install_plan.at(0), "a", {"0", "1", "core"}, Triplet::X64_WINDOWS); -} - -TEST_CASE ("install default features test 1", "[plan]") -{ - std::vector> status_paragraphs; - - // Add a port "a" with default features "1" and features "0" and "1". - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - spec_map.emplace("a", "", {{"0", ""}, {"1", ""}}, {"1"}); - - // Install "a" (without explicit feature specification) - auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - // Expect the default feature "1" to be installed, but not "0" - REQUIRE(install_plan.size() == 1); - features_check(install_plan.at(0), "a", {"1", "core"}, Triplet::X64_WINDOWS); -} - -TEST_CASE ("install default features test 2", "[plan]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a")); - status_paragraphs.back()->package.spec = - PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); - - // Add a port "a" of which "core" is already installed, but we will - // install the default features "explicitly" - // "a" has two features, of which "a1" is default. - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - spec_map.emplace("a", "", {{"a0", ""}, {"a1", ""}}, {"a1"}); - - // Install "a" (without explicit feature specification) - auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - // Expect "a" to get removed for rebuild and then installed with default - // features. - REQUIRE(install_plan.size() == 2); - remove_plan_check(install_plan.at(0), "a", Triplet::X64_WINDOWS); - features_check(install_plan.at(1), "a", {"a1", "core"}, Triplet::X64_WINDOWS); -} - -TEST_CASE ("install default features test 3", "[plan]") -{ - std::vector> status_paragraphs; - - // "a" has two features, of which "a1" is default. - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - spec_map.emplace("a", "", {{"a0", ""}, {"a1", ""}}, {"a1"}); - - // Explicitly install "a" without default features - auto install_specs = FullPackageSpec::from_string("a[core]", Triplet::X64_WINDOWS); - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - // Expect the default feature not to get installed. - REQUIRE(install_plan.size() == 1); - features_check(install_plan.at(0), "a", {"core"}, Triplet::X64_WINDOWS); -} - -TEST_CASE ("install default features of dependency test 1", "[plan]") -{ - std::vector> status_paragraphs; - - // Add a port "a" which depends on the core of "b" - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - spec_map.emplace("a", "b[core]"); - // "b" has two features, of which "b1" is default. - spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b1"}); - - // Install "a" (without explicit feature specification) - auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - // Expect "a" to get installed and defaults of "b" through the dependency, - // as no explicit features of "b" are installed by the user. - REQUIRE(install_plan.size() == 2); - features_check(install_plan.at(0), "b", {"b1", "core"}, Triplet::X64_WINDOWS); - features_check(install_plan.at(1), "a", {"core"}, Triplet::X64_WINDOWS); -} - -TEST_CASE ("do not install default features of existing dependency", "[plan]") -{ - // Add a port "a" which depends on the core of "b" - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - spec_map.emplace("a", "b[core]"); - // "b" has two features, of which "b1" is default. - spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b1"}); - - std::vector> status_paragraphs; - // "b[core]" is already installed - status_paragraphs.push_back(make_status_pgh("b")); - status_paragraphs.back()->package.spec = - PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); - - // Install "a" (without explicit feature specification) - auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - // Expect "a" to get installed, but not require rebuilding "b" - REQUIRE(install_plan.size() == 1); - features_check(install_plan.at(0), "a", {"core"}, Triplet::X64_WINDOWS); -} - -TEST_CASE ("install default features of dependency test 2", "[plan]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("b")); - status_paragraphs.back()->package.spec = - PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); - - // Add a port "a" which depends on the core of "b", which was already - // installed explicitly - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - spec_map.emplace("a", "b[core]"); - // "b" has two features, of which "b1" is default. - spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b1"}); - - // Install "a" (without explicit feature specification) - auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - // Expect "a" to get installed, not the defaults of "b", as the required - // dependencies are already there, installed explicitly by the user. - REQUIRE(install_plan.size() == 1); - features_check(install_plan.at(0), "a", {"core"}, Triplet::X64_WINDOWS); -} - -TEST_CASE ("install plan action dependencies", "[plan]") -{ - std::vector> status_paragraphs; - - // Add a port "a" which depends on the core of "b", which was already - // installed explicitly - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - auto spec_c = spec_map.emplace("c"); - auto spec_b = spec_map.emplace("b", "c"); - spec_map.emplace("a", "b"); - - // Install "a" (without explicit feature specification) - auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 3); - features_check(install_plan.at(0), "c", {"core"}, Triplet::X64_WINDOWS); - - features_check(install_plan.at(1), "b", {"core"}, Triplet::X64_WINDOWS); - REQUIRE(install_plan.at(1).install_action.get()->computed_dependencies == std::vector{spec_c}); - - features_check(install_plan.at(2), "a", {"core"}, Triplet::X64_WINDOWS); - REQUIRE(install_plan.at(2).install_action.get()->computed_dependencies == std::vector{spec_b}); -} - -TEST_CASE ("install plan action dependencies 2", "[plan]") -{ - std::vector> status_paragraphs; - - // Add a port "a" which depends on the core of "b", which was already - // installed explicitly - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - auto spec_c = spec_map.emplace("c"); - auto spec_b = spec_map.emplace("b", "c"); - spec_map.emplace("a", "c, b"); - - // Install "a" (without explicit feature specification) - auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 3); - features_check(install_plan.at(0), "c", {"core"}, Triplet::X64_WINDOWS); - - features_check(install_plan.at(1), "b", {"core"}, Triplet::X64_WINDOWS); - REQUIRE(install_plan.at(1).install_action.get()->computed_dependencies == std::vector{spec_c}); - - features_check(install_plan.at(2), "a", {"core"}, Triplet::X64_WINDOWS); - REQUIRE(install_plan.at(2).install_action.get()->computed_dependencies == std::vector{spec_b, spec_c}); -} - -TEST_CASE ("install plan action dependencies 3", "[plan]") -{ - std::vector> status_paragraphs; - - // Add a port "a" which depends on the core of "b", which was already - // installed explicitly - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - spec_map.emplace("a", "", {{"0", ""}, {"1", "a[0]"}}, {"1"}); - - // Install "a" (without explicit feature specification) - auto install_specs = FullPackageSpec::from_string("a", Triplet::X64_WINDOWS); - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 1); - features_check(install_plan.at(0), "a", {"1", "0", "core"}, Triplet::X64_WINDOWS); - REQUIRE(install_plan.at(0).install_action.get()->computed_dependencies == std::vector{}); -} - -TEST_CASE ("install with default features", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a", "")); - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map; - auto b_spec = spec_map.emplace("b", "", {{"0", ""}}, {"0"}); - auto a_spec = spec_map.emplace("a", "b[core]", {{"0", ""}}); - - // Install "a" and indicate that "b" should not install default features - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, {FeatureSpec{a_spec, "0"}, FeatureSpec{b_spec, "core"}}, status_db); - - REQUIRE(install_plan.size() == 3); - remove_plan_check(install_plan.at(0), "a"); - features_check(install_plan.at(1), "b", {"core"}); - features_check(install_plan.at(2), "a", {"0", "core"}); -} - -TEST_CASE ("upgrade with default features 1", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a", "", "1")); - pghs.push_back(make_status_feature_pgh("a", "0")); - StatusParagraphs status_db(std::move(pghs)); - - // Add a port "a" of which "core" and "0" are already installed. - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a", "", {{"0", ""}, {"1", ""}}, {"1"}); - - Dependencies::MapPortFileProvider provider(spec_map.map); - Dependencies::PackageGraph graph(provider, status_db); - - graph.upgrade(spec_a); - auto plan = graph.serialize(); - - // The upgrade should not install the default feature - REQUIRE(plan.size() == 2); - - REQUIRE(plan.at(0).spec().name() == "a"); - remove_plan_check(plan.at(0), "a"); - features_check(plan.at(1), "a", {"core", "0"}); -} - -TEST_CASE ("upgrade with default features 2", "[plan]") -{ - std::vector> pghs; - // B is currently installed _without_ default feature b0 - pghs.push_back(make_status_pgh("b", "", "b0", "x64-windows")); - pghs.push_back(make_status_pgh("a", "b[core]", "", "x64-windows")); - - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - auto spec_a = spec_map.emplace("a", "b[core]"); - auto spec_b = spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b0", "b1"}); - - Dependencies::MapPortFileProvider provider(spec_map.map); - Dependencies::PackageGraph graph(provider, status_db); - - graph.upgrade(spec_a); - graph.upgrade(spec_b); - auto plan = graph.serialize(); - - // The upgrade should install the new default feature b1 but not b0 - REQUIRE(plan.size() == 4); - remove_plan_check(plan.at(0), "a", Triplet::X64_WINDOWS); - remove_plan_check(plan.at(1), "b", Triplet::X64_WINDOWS); - features_check(plan.at(2), "b", {"core", "b1"}, Triplet::X64_WINDOWS); - features_check(plan.at(3), "a", {"core"}, Triplet::X64_WINDOWS); -} - -TEST_CASE ("upgrade with default features 3", "[plan]") -{ - std::vector> pghs; - // note: unrelated package due to x86 triplet - pghs.push_back(make_status_pgh("b", "", "", "x86-windows")); - pghs.push_back(make_status_pgh("a", "", "", "x64-windows")); - - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - auto spec_a = spec_map.emplace("a", "b[core]"); - spec_map.emplace("b", "", {{"b0", ""}, {"b1", ""}}, {"b0"}); - - Dependencies::MapPortFileProvider provider(spec_map.map); - Dependencies::PackageGraph graph(provider, status_db); - - graph.upgrade(spec_a); - auto plan = graph.serialize(); - - // The upgrade should install the default feature - REQUIRE(plan.size() == 3); - remove_plan_check(plan.at(0), "a", Triplet::X64_WINDOWS); - features_check(plan.at(1), "b", {"b0", "core"}, Triplet::X64_WINDOWS); - features_check(plan.at(2), "a", {"core"}, Triplet::X64_WINDOWS); -} - -TEST_CASE ("upgrade with new default feature", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a", "", "0", "x86-windows")); - - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a", "", {{"0", ""}, {"1", ""}, {"2", ""}}, {"0", "1"}); - - Dependencies::MapPortFileProvider provider(spec_map.map); - Dependencies::PackageGraph graph(provider, status_db); - - graph.upgrade(spec_a); - auto plan = graph.serialize(); - - // The upgrade should install the new default feature but not the old default feature 0 - REQUIRE(plan.size() == 2); - remove_plan_check(plan.at(0), "a", Triplet::X86_WINDOWS); - features_check(plan.at(1), "a", {"core", "1"}, Triplet::X86_WINDOWS); -} - -TEST_CASE ("transitive features test", "[plan]") -{ - std::vector> status_paragraphs; - - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"0", "b[0]"}}), {"core"}}; - auto spec_b_64 = FullPackageSpec{spec_map.emplace("b", "c", {{"0", "c[0]"}}), {"core"}}; - auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "", {{"0", ""}}), {"core"}}; - - auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS); - REQUIRE(install_specs.has_value()); - if (!install_specs.has_value()) return; - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 3); - features_check(install_plan.at(0), "c", {"0", "core"}, Triplet::X64_WINDOWS); - features_check(install_plan.at(1), "b", {"0", "core"}, Triplet::X64_WINDOWS); - features_check(install_plan.at(2), "a", {"0", "core"}, Triplet::X64_WINDOWS); -} - -TEST_CASE ("no transitive features test", "[plan]") -{ - std::vector> status_paragraphs; - - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "b", {{"0", ""}}), {"core"}}; - auto spec_b_64 = FullPackageSpec{spec_map.emplace("b", "c", {{"0", ""}}), {"core"}}; - auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "", {{"0", ""}}), {"core"}}; - - auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS); - REQUIRE(install_specs.has_value()); - if (!install_specs.has_value()) return; - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 3); - features_check(install_plan.at(0), "c", {"core"}, Triplet::X64_WINDOWS); - features_check(install_plan.at(1), "b", {"core"}, Triplet::X64_WINDOWS); - features_check(install_plan.at(2), "a", {"0", "core"}, Triplet::X64_WINDOWS); -} - -TEST_CASE ("only transitive features test", "[plan]") -{ - std::vector> status_paragraphs; - - PackageSpecMap spec_map(Triplet::X64_WINDOWS); - auto spec_a_64 = FullPackageSpec{spec_map.emplace("a", "", {{"0", "b[0]"}}), {"core"}}; - auto spec_b_64 = FullPackageSpec{spec_map.emplace("b", "", {{"0", "c[0]"}}), {"core"}}; - auto spec_c_64 = FullPackageSpec{spec_map.emplace("c", "", {{"0", ""}}), {"core"}}; - - auto install_specs = FullPackageSpec::from_string("a[*]", Triplet::X64_WINDOWS); - REQUIRE(install_specs.has_value()); - if (!install_specs.has_value()) return; - auto install_plan = Dependencies::create_feature_install_plan( - spec_map.map, - FullPackageSpec::to_feature_specs({install_specs.value_or_exit(VCPKG_LINE_INFO)}), - StatusParagraphs(std::move(status_paragraphs))); - - REQUIRE(install_plan.size() == 3); - features_check(install_plan.at(0), "c", {"0", "core"}, Triplet::X64_WINDOWS); - features_check(install_plan.at(1), "b", {"0", "core"}, Triplet::X64_WINDOWS); - features_check(install_plan.at(2), "a", {"0", "core"}, Triplet::X64_WINDOWS); -} - -TEST_CASE ("basic remove scheme", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - StatusParagraphs status_db(std::move(pghs)); - - auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("a")}, status_db); - - REQUIRE(remove_plan.size() == 1); - REQUIRE(remove_plan.at(0).spec.name() == "a"); -} - -TEST_CASE ("recurse remove scheme", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - pghs.push_back(make_status_pgh("b", "a")); - StatusParagraphs status_db(std::move(pghs)); - - auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("a")}, status_db); - - REQUIRE(remove_plan.size() == 2); - REQUIRE(remove_plan.at(0).spec.name() == "b"); - REQUIRE(remove_plan.at(1).spec.name() == "a"); -} - -TEST_CASE ("features depend remove scheme", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - pghs.push_back(make_status_pgh("b")); - pghs.push_back(make_status_feature_pgh("b", "0", "a")); - StatusParagraphs status_db(std::move(pghs)); - - auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("a")}, status_db); - - REQUIRE(remove_plan.size() == 2); - REQUIRE(remove_plan.at(0).spec.name() == "b"); - REQUIRE(remove_plan.at(1).spec.name() == "a"); -} - -TEST_CASE ("features depend remove scheme once removed", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("expat")); - pghs.push_back(make_status_pgh("vtk", "expat")); - pghs.push_back(make_status_pgh("opencv")); - pghs.push_back(make_status_feature_pgh("opencv", "vtk", "vtk")); - StatusParagraphs status_db(std::move(pghs)); - - auto remove_plan = Dependencies::create_remove_plan({unsafe_pspec("expat")}, status_db); - - REQUIRE(remove_plan.size() == 3); - REQUIRE(remove_plan.at(0).spec.name() == "opencv"); - REQUIRE(remove_plan.at(1).spec.name() == "vtk"); - REQUIRE(remove_plan.at(2).spec.name() == "expat"); -} - -TEST_CASE ("features depend remove scheme once removed x64", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("expat", "", "", "x64")); - pghs.push_back(make_status_pgh("vtk", "expat", "", "x64")); - pghs.push_back(make_status_pgh("opencv", "", "", "x64")); - pghs.push_back(make_status_feature_pgh("opencv", "vtk", "vtk", "x64")); - StatusParagraphs status_db(std::move(pghs)); - - auto remove_plan = - Dependencies::create_remove_plan({unsafe_pspec("expat", Triplet::from_canonical_name("x64"))}, status_db); - - REQUIRE(remove_plan.size() == 3); - REQUIRE(remove_plan.at(0).spec.name() == "opencv"); - REQUIRE(remove_plan.at(1).spec.name() == "vtk"); - REQUIRE(remove_plan.at(2).spec.name() == "expat"); -} - -TEST_CASE ("features depend core remove scheme", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("curl", "", "", "x64")); - pghs.push_back(make_status_pgh("cpr", "curl[core]", "", "x64")); - StatusParagraphs status_db(std::move(pghs)); - - auto remove_plan = - Dependencies::create_remove_plan({unsafe_pspec("curl", Triplet::from_canonical_name("x64"))}, status_db); - - REQUIRE(remove_plan.size() == 2); - REQUIRE(remove_plan.at(0).spec.name() == "cpr"); - REQUIRE(remove_plan.at(1).spec.name() == "curl"); -} - -TEST_CASE ("features depend core remove scheme 2", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("curl", "", "", "x64")); - pghs.push_back(make_status_feature_pgh("curl", "a", "", "x64")); - pghs.push_back(make_status_feature_pgh("curl", "b", "curl[a]", "x64")); - StatusParagraphs status_db(std::move(pghs)); - - auto remove_plan = - Dependencies::create_remove_plan({unsafe_pspec("curl", Triplet::from_canonical_name("x64"))}, status_db); - - REQUIRE(remove_plan.size() == 1); - REQUIRE(remove_plan.at(0).spec.name() == "curl"); -} - -TEST_CASE ("basic upgrade scheme", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a"); - - Dependencies::MapPortFileProvider provider(spec_map.map); - Dependencies::PackageGraph graph(provider, status_db); - - graph.upgrade(spec_a); - - auto plan = graph.serialize(); - - REQUIRE(plan.size() == 2); - REQUIRE(plan.at(0).spec().name() == "a"); - REQUIRE(plan.at(0).remove_action.has_value()); - REQUIRE(plan.at(1).spec().name() == "a"); - REQUIRE(plan.at(1).install_action.has_value()); -} - -TEST_CASE ("basic upgrade scheme with recurse", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - pghs.push_back(make_status_pgh("b", "a")); - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a"); - spec_map.emplace("b", "a"); - - Dependencies::MapPortFileProvider provider(spec_map.map); - Dependencies::PackageGraph graph(provider, status_db); - - graph.upgrade(spec_a); - - auto plan = graph.serialize(); - - REQUIRE(plan.size() == 4); - REQUIRE(plan.at(0).spec().name() == "b"); - REQUIRE(plan.at(0).remove_action.has_value()); - - REQUIRE(plan.at(1).spec().name() == "a"); - REQUIRE(plan.at(1).remove_action.has_value()); - - REQUIRE(plan.at(2).spec().name() == "a"); - REQUIRE(plan.at(2).install_action.has_value()); - - REQUIRE(plan.at(3).spec().name() == "b"); - REQUIRE(plan.at(3).install_action.has_value()); -} - -TEST_CASE ("basic upgrade scheme with bystander", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - pghs.push_back(make_status_pgh("b")); - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a"); - spec_map.emplace("b", "a"); - - Dependencies::MapPortFileProvider provider(spec_map.map); - Dependencies::PackageGraph graph(provider, status_db); - - graph.upgrade(spec_a); - - auto plan = graph.serialize(); - - REQUIRE(plan.size() == 2); - REQUIRE(plan.at(0).spec().name() == "a"); - REQUIRE(plan.at(0).remove_action.has_value()); - REQUIRE(plan.at(1).spec().name() == "a"); - REQUIRE(plan.at(1).install_action.has_value()); -} - -TEST_CASE ("basic upgrade scheme with new dep", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a", "b"); - spec_map.emplace("b"); - - Dependencies::MapPortFileProvider provider(spec_map.map); - Dependencies::PackageGraph graph(provider, status_db); - - graph.upgrade(spec_a); - - auto plan = graph.serialize(); - - REQUIRE(plan.size() == 3); - REQUIRE(plan.at(0).spec().name() == "a"); - REQUIRE(plan.at(0).remove_action.has_value()); - REQUIRE(plan.at(1).spec().name() == "b"); - REQUIRE(plan.at(1).install_action.has_value()); - REQUIRE(plan.at(2).spec().name() == "a"); - REQUIRE(plan.at(2).install_action.has_value()); -} - -TEST_CASE ("basic upgrade scheme with features", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - pghs.push_back(make_status_feature_pgh("a", "a1")); - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a", "", {{"a1", ""}}); - - Dependencies::MapPortFileProvider provider(spec_map.map); - Dependencies::PackageGraph graph(provider, status_db); - - graph.upgrade(spec_a); - - auto plan = graph.serialize(); - - REQUIRE(plan.size() == 2); - - REQUIRE(plan.at(0).spec().name() == "a"); - REQUIRE(plan.at(0).remove_action.has_value()); - - features_check(plan.at(1), "a", {"core", "a1"}); -} - -TEST_CASE ("basic upgrade scheme with new default feature", "[plan]") -{ - // only core of package "a" is installed - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - StatusParagraphs status_db(std::move(pghs)); - - // a1 was added as a default feature and should be installed in upgrade - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a", "", {{"a1", ""}}, {"a1"}); - - Dependencies::MapPortFileProvider provider(spec_map.map); - Dependencies::PackageGraph graph(provider, status_db); - - graph.upgrade(spec_a); - - auto plan = graph.serialize(); - - REQUIRE(plan.size() == 2); - - REQUIRE(plan.at(0).spec().name() == "a"); - REQUIRE(plan.at(0).remove_action.has_value()); - - features_check(plan.at(1), "a", {"core", "a1"}); -} - -TEST_CASE ("basic upgrade scheme with self features", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - pghs.push_back(make_status_feature_pgh("a", "a1", "")); - pghs.push_back(make_status_feature_pgh("a", "a2", "a[a1]")); - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a", "", {{"a1", ""}, {"a2", "a[a1]"}}); - - Dependencies::MapPortFileProvider provider(spec_map.map); - Dependencies::PackageGraph graph(provider, status_db); - - graph.upgrade(spec_a); - - auto plan = graph.serialize(); - - REQUIRE(plan.size() == 2); - - REQUIRE(plan.at(0).spec().name() == "a"); - REQUIRE(plan.at(0).remove_action.has_value()); - - REQUIRE(plan.at(1).spec().name() == "a"); - REQUIRE(plan.at(1).install_action.has_value()); - REQUIRE(plan.at(1).install_action.get()->feature_list == std::set{"core", "a1", "a2"}); -} - -TEST_CASE ("basic export scheme", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a"); - - auto plan = Dependencies::create_export_plan({spec_a}, status_db); - - REQUIRE(plan.size() == 1); - REQUIRE(plan.at(0).spec.name() == "a"); - REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT); -} - -TEST_CASE ("basic export scheme with recurse", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - pghs.push_back(make_status_pgh("b", "a")); - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a"); - auto spec_b = spec_map.emplace("b", "a"); - - auto plan = Dependencies::create_export_plan({spec_b}, status_db); - - REQUIRE(plan.size() == 2); - REQUIRE(plan.at(0).spec.name() == "a"); - REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT); - - REQUIRE(plan.at(1).spec.name() == "b"); - REQUIRE(plan.at(1).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT); -} - -TEST_CASE ("basic export scheme with bystander", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("a")); - pghs.push_back(make_status_pgh("b")); - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a"); - auto spec_b = spec_map.emplace("b", "a"); - - auto plan = Dependencies::create_export_plan({spec_a}, status_db); - - REQUIRE(plan.size() == 1); - REQUIRE(plan.at(0).spec.name() == "a"); - REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT); -} - -TEST_CASE ("basic export scheme with missing", "[plan]") -{ - StatusParagraphs status_db; - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a"); - - auto plan = Dependencies::create_export_plan({spec_a}, status_db); - - REQUIRE(plan.size() == 1); - REQUIRE(plan.at(0).spec.name() == "a"); - REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::NOT_BUILT); -} - -TEST_CASE ("basic export scheme with features", "[plan]") -{ - std::vector> pghs; - pghs.push_back(make_status_pgh("b")); - pghs.push_back(make_status_pgh("a")); - pghs.push_back(make_status_feature_pgh("a", "a1", "b[core]")); - StatusParagraphs status_db(std::move(pghs)); - - PackageSpecMap spec_map; - auto spec_a = spec_map.emplace("a", "", {{"a1", ""}}); - - auto plan = Dependencies::create_export_plan({spec_a}, status_db); - - REQUIRE(plan.size() == 2); - - REQUIRE(plan.at(0).spec.name() == "b"); - REQUIRE(plan.at(0).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT); - - REQUIRE(plan.at(1).spec.name() == "a"); - REQUIRE(plan.at(1).plan_type == Dependencies::ExportPlanType::ALREADY_BUILT); -} diff --git a/toolsrc/src/vcpkg-tests/specifier.cpp b/toolsrc/src/vcpkg-tests/specifier.cpp deleted file mode 100644 index 52ef044e7..000000000 --- a/toolsrc/src/vcpkg-tests/specifier.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include - -#include -#include - -using namespace vcpkg; - -TEST_CASE ("specifier conversion", "[specifier]") -{ - SECTION ("full package spec to feature specs") - { - constexpr std::size_t SPEC_SIZE = 6; - - auto a_spec = PackageSpec::from_name_and_triplet("a", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); - auto b_spec = PackageSpec::from_name_and_triplet("b", Triplet::X64_WINDOWS).value_or_exit(VCPKG_LINE_INFO); - - auto fspecs = FullPackageSpec::to_feature_specs({{a_spec, {"0", "1"}}, {b_spec, {"2", "3"}}}); - - REQUIRE(fspecs.size() == SPEC_SIZE); - - std::array features = {"", "0", "1", "", "2", "3"}; - std::array specs = {&a_spec, &a_spec, &a_spec, &b_spec, &b_spec, &b_spec}; - - for (std::size_t i = 0; i < SPEC_SIZE; ++i) - { - REQUIRE(features.at(i) == fspecs.at(i).feature()); - REQUIRE(*specs.at(i) == fspecs.at(i).spec()); - } - } -} - -TEST_CASE ("specifier parsing", "[specifier]") -{ - SECTION ("parsed specifier from string") - { - auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib"); - REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS); - - auto& spec = *maybe_spec.get(); - REQUIRE(spec.name == "zlib"); - REQUIRE(spec.features.size() == 0); - REQUIRE(spec.triplet == ""); - } - - SECTION ("parsed specifier from string with triplet") - { - auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib:x64-uwp"); - REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS); - - auto& spec = *maybe_spec.get(); - REQUIRE(spec.name == "zlib"); - REQUIRE(spec.triplet == "x64-uwp"); - } - - SECTION ("parsed specifier from string with colons") - { - auto ec = vcpkg::ParsedSpecifier::from_string("zlib:x86-uwp:").error(); - REQUIRE(ec == vcpkg::PackageSpecParseResult::TOO_MANY_COLONS); - } - - SECTION ("parsed specifier from string with feature") - { - auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[feature]:x64-uwp"); - REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS); - - auto& spec = *maybe_spec.get(); - REQUIRE(spec.name == "zlib"); - REQUIRE(spec.features.size() == 1); - REQUIRE(spec.features.at(0) == "feature"); - REQUIRE(spec.triplet == "x64-uwp"); - } - - SECTION ("parsed specifier from string with many features") - { - auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[0, 1,2]"); - REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS); - - auto& spec = *maybe_spec.get(); - REQUIRE(spec.name == "zlib"); - REQUIRE(spec.features.size() == 3); - REQUIRE(spec.features.at(0) == "0"); - REQUIRE(spec.features.at(1) == "1"); - REQUIRE(spec.features.at(2) == "2"); - REQUIRE(spec.triplet == ""); - } - - SECTION ("parsed specifier wildcard feature") - { - auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[*]"); - REQUIRE(maybe_spec.error() == vcpkg::PackageSpecParseResult::SUCCESS); - - auto& spec = *maybe_spec.get(); - REQUIRE(spec.name == "zlib"); - REQUIRE(spec.features.size() == 1); - REQUIRE(spec.features.at(0) == "*"); - REQUIRE(spec.triplet == ""); - } - - SECTION ("expand wildcards") - { - auto zlib = vcpkg::FullPackageSpec::from_string("zlib[0,1]", Triplet::X86_UWP).value_or_exit(VCPKG_LINE_INFO); - auto openssl = - vcpkg::FullPackageSpec::from_string("openssl[*]", Triplet::X86_UWP).value_or_exit(VCPKG_LINE_INFO); - auto specs = FullPackageSpec::to_feature_specs({zlib, openssl}); - Util::sort(specs); - auto spectargets = FeatureSpec::from_strings_and_triplet( - { - "openssl", - "zlib", - "openssl[*]", - "zlib[0]", - "zlib[1]", - }, - Triplet::X86_UWP); - Util::sort(spectargets); - - REQUIRE(specs.size() == spectargets.size()); - REQUIRE(Util::all_equal(specs, spectargets)); - } - -#if defined(_WIN32) - SECTION ("ASCII to utf16") - { - auto str = vcpkg::Strings::to_utf16("abc"); - REQUIRE(str == L"abc"); - } - - SECTION ("ASCII to utf16 with whitespace") - { - auto str = vcpkg::Strings::to_utf16("abc -x86-windows"); - REQUIRE(str == L"abc -x86-windows"); - } -#endif -}; diff --git a/toolsrc/src/vcpkg-tests/statusparagraphs.cpp b/toolsrc/src/vcpkg-tests/statusparagraphs.cpp deleted file mode 100644 index df52ccb87..000000000 --- a/toolsrc/src/vcpkg-tests/statusparagraphs.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include -#include - -#include -#include -#include - -using namespace vcpkg; -using namespace vcpkg::Paragraphs; -using namespace vcpkg::Test; - -TEST_CASE ("find installed", "[statusparagraphs]") -{ - auto pghs = parse_paragraphs(R"( -Package: ffmpeg -Version: 3.3.3 -Architecture: x64-windows -Multi-Arch: same -Description: -Status: install ok installed -)"); - - REQUIRE(pghs); - - StatusParagraphs status_db( - Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique(std::move(rpgh)); })); - - auto it = status_db.find_installed(unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS)); - REQUIRE(it != status_db.end()); -} - -TEST_CASE ("find not installed", "[statusparagraphs]") -{ - auto pghs = parse_paragraphs(R"( -Package: ffmpeg -Version: 3.3.3 -Architecture: x64-windows -Multi-Arch: same -Description: -Status: purge ok not-installed -)"); - - REQUIRE(pghs); - - StatusParagraphs status_db( - Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique(std::move(rpgh)); })); - - auto it = status_db.find_installed(unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS)); - REQUIRE(it == status_db.end()); -} - -TEST_CASE ("find with feature packages", "[statusparagraphs]") -{ - auto pghs = parse_paragraphs(R"( -Package: ffmpeg -Version: 3.3.3 -Architecture: x64-windows -Multi-Arch: same -Description: -Status: install ok installed - -Package: ffmpeg -Feature: openssl -Depends: openssl -Architecture: x64-windows -Multi-Arch: same -Description: -Status: purge ok not-installed -)"); - - REQUIRE(pghs); - - StatusParagraphs status_db( - Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique(std::move(rpgh)); })); - - auto it = status_db.find_installed(unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS)); - REQUIRE(it != status_db.end()); - - // Feature "openssl" is not installed and should not be found - auto it1 = status_db.find_installed({unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS), "openssl"}); - REQUIRE(it1 == status_db.end()); -} - -TEST_CASE ("find for feature packages", "[statusparagraphs]") -{ - auto pghs = parse_paragraphs(R"( -Package: ffmpeg -Version: 3.3.3 -Architecture: x64-windows -Multi-Arch: same -Description: -Status: install ok installed - -Package: ffmpeg -Feature: openssl -Depends: openssl -Architecture: x64-windows -Multi-Arch: same -Description: -Status: install ok installed -)"); - REQUIRE(pghs); - - StatusParagraphs status_db( - Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique(std::move(rpgh)); })); - - // Feature "openssl" is installed and should therefore be found - auto it = status_db.find_installed({unsafe_pspec("ffmpeg", Triplet::X64_WINDOWS), "openssl"}); - REQUIRE(it != status_db.end()); -} diff --git a/toolsrc/src/vcpkg-tests/strings.cpp b/toolsrc/src/vcpkg-tests/strings.cpp deleted file mode 100644 index 3168a7c95..000000000 --- a/toolsrc/src/vcpkg-tests/strings.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include - -#include - -#include -#include -#include - -TEST_CASE ("b32 encoding", "[strings]") -{ - using u64 = std::uint64_t; - - std::vector> map; - - map.emplace_back(0, "AAAAAAAAAAAAA"); - map.emplace_back(1, "BAAAAAAAAAAAA"); - - map.emplace_back(u64(1) << 32, "AAAAAAEAAAAAA"); - map.emplace_back((u64(1) << 32) + 1, "BAAAAAEAAAAAA"); - - map.emplace_back(0xE4D0'1065'D11E'0229, "JRA4RIXMQAUJO"); - map.emplace_back(0xA626'FE45'B135'07FF, "77BKTYWI6XJMK"); - map.emplace_back(0xEE36'D228'0C31'D405, "FAVDDGAFSWN4O"); - map.emplace_back(0x1405'64E7'FE7E'A88C, "MEK5H774ELBIB"); - map.emplace_back(0xFFFF'FFFF'FFFF'FFFF, "777777777777P"); - - std::string result; - for (const auto& pr : map) - { - result = vcpkg::Strings::b32_encode(pr.first); - REQUIRE(vcpkg::Strings::b32_encode(pr.first) == pr.second); - } -} diff --git a/toolsrc/src/vcpkg-tests/supports.cpp b/toolsrc/src/vcpkg-tests/supports.cpp deleted file mode 100644 index c6c88bdbc..000000000 --- a/toolsrc/src/vcpkg-tests/supports.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include - -#include - -using namespace vcpkg; -using Parse::parse_comma_list; - -TEST_CASE ("parse supports all", "[supports]") -{ - auto v = Supports::parse({ - "x64", - "x86", - "arm", - "windows", - "uwp", - "v140", - "v141", - "crt-static", - "crt-dynamic", - }); - - REQUIRE(v.has_value()); - - REQUIRE(v.get()->is_supported(System::CPUArchitecture::X64, - Supports::Platform::UWP, - Supports::Linkage::DYNAMIC, - Supports::ToolsetVersion::V140)); - REQUIRE(v.get()->is_supported(System::CPUArchitecture::ARM, - Supports::Platform::WINDOWS, - Supports::Linkage::STATIC, - Supports::ToolsetVersion::V141)); -} - -TEST_CASE ("parse supports invalid", "[supports]") -{ - auto v = Supports::parse({"arm64"}); - - REQUIRE_FALSE(v.has_value()); - - REQUIRE(v.error().size() == 1); - REQUIRE(v.error().at(0) == "arm64"); -} - -TEST_CASE ("parse supports case sensitive", "[supports]") -{ - auto v = Supports::parse({"Windows"}); - - REQUIRE_FALSE(v.has_value()); - REQUIRE(v.error().size() == 1); - REQUIRE(v.error().at(0) == "Windows"); -} - -TEST_CASE ("parse supports some", "[supports]") -{ - auto v = Supports::parse({ - "x64", - "x86", - "windows", - }); - - REQUIRE(v.has_value()); - - REQUIRE(v.get()->is_supported(System::CPUArchitecture::X64, - Supports::Platform::WINDOWS, - Supports::Linkage::DYNAMIC, - Supports::ToolsetVersion::V140)); - REQUIRE_FALSE(v.get()->is_supported(System::CPUArchitecture::ARM, - Supports::Platform::WINDOWS, - Supports::Linkage::DYNAMIC, - Supports::ToolsetVersion::V140)); - REQUIRE_FALSE(v.get()->is_supported(System::CPUArchitecture::X64, - Supports::Platform::UWP, - Supports::Linkage::DYNAMIC, - Supports::ToolsetVersion::V140)); - REQUIRE(v.get()->is_supported(System::CPUArchitecture::X64, - Supports::Platform::WINDOWS, - Supports::Linkage::STATIC, - Supports::ToolsetVersion::V141)); -} diff --git a/toolsrc/src/vcpkg-tests/update.cpp b/toolsrc/src/vcpkg-tests/update.cpp deleted file mode 100644 index 93a8f74a9..000000000 --- a/toolsrc/src/vcpkg-tests/update.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include -#include - -#include - -#include - -using namespace vcpkg; -using namespace vcpkg::Update; -using namespace vcpkg::Test; - -using Pgh = std::vector>; - -TEST_CASE ("find outdated packages basic", "[update]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a")); - status_paragraphs.back()->package.version = "2"; - - StatusParagraphs status_db(std::move(status_paragraphs)); - - std::unordered_map map; - auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}})); - map.emplace("a", SourceControlFileLocation{std::move(scf), ""}); - Dependencies::MapPortFileProvider provider(map); - - auto pkgs = SortedVector(Update::find_outdated_packages(provider, status_db), - &OutdatedPackage::compare_by_name); - - REQUIRE(pkgs.size() == 1); - REQUIRE(pkgs[0].version_diff.left.to_string() == "2"); - REQUIRE(pkgs[0].version_diff.right.to_string() == "0"); -} - -TEST_CASE ("find outdated packages features", "[update]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a")); - status_paragraphs.back()->package.version = "2"; - - status_paragraphs.push_back(make_status_feature_pgh("a", "b")); - status_paragraphs.back()->package.version = "2"; - - StatusParagraphs status_db(std::move(status_paragraphs)); - - std::unordered_map map; - auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}})); - map.emplace("a", SourceControlFileLocation{std::move(scf), ""}); - Dependencies::MapPortFileProvider provider(map); - - auto pkgs = SortedVector(Update::find_outdated_packages(provider, status_db), - &OutdatedPackage::compare_by_name); - - REQUIRE(pkgs.size() == 1); - REQUIRE(pkgs[0].version_diff.left.to_string() == "2"); - REQUIRE(pkgs[0].version_diff.right.to_string() == "0"); -} - -TEST_CASE ("find outdated packages features 2", "[update]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a")); - status_paragraphs.back()->package.version = "2"; - - status_paragraphs.push_back(make_status_feature_pgh("a", "b")); - status_paragraphs.back()->package.version = "0"; - status_paragraphs.back()->state = InstallState::NOT_INSTALLED; - status_paragraphs.back()->want = Want::PURGE; - - StatusParagraphs status_db(std::move(status_paragraphs)); - - std::unordered_map map; - auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}})); - map.emplace("a", SourceControlFileLocation{std::move(scf), ""}); - Dependencies::MapPortFileProvider provider(map); - - auto pkgs = SortedVector(Update::find_outdated_packages(provider, status_db), - &OutdatedPackage::compare_by_name); - - REQUIRE(pkgs.size() == 1); - REQUIRE(pkgs[0].version_diff.left.to_string() == "2"); - REQUIRE(pkgs[0].version_diff.right.to_string() == "0"); -} - -TEST_CASE ("find outdated packages none", "[update]") -{ - std::vector> status_paragraphs; - status_paragraphs.push_back(make_status_pgh("a")); - status_paragraphs.back()->package.version = "2"; - - StatusParagraphs status_db(std::move(status_paragraphs)); - - std::unordered_map map; - auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "2"}}})); - map.emplace("a", SourceControlFileLocation{std::move(scf), ""}); - Dependencies::MapPortFileProvider provider(map); - - auto pkgs = SortedVector(Update::find_outdated_packages(provider, status_db), - &OutdatedPackage::compare_by_name); - - REQUIRE(pkgs.size() == 0); -} diff --git a/toolsrc/src/vcpkg-tests/util.cpp b/toolsrc/src/vcpkg-tests/util.cpp deleted file mode 100644 index f14628379..000000000 --- a/toolsrc/src/vcpkg-tests/util.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include - -#include -#include - -#include -#include - -#if defined(_WIN32) -#include -#endif - -namespace vcpkg::Test -{ - std::unique_ptr make_status_pgh(const char* name, - const char* depends, - const char* default_features, - const char* triplet) - { - using Pgh = std::unordered_map; - return std::make_unique(Pgh{{"Package", name}, - {"Version", "1"}, - {"Architecture", triplet}, - {"Multi-Arch", "same"}, - {"Depends", depends}, - {"Default-Features", default_features}, - {"Status", "install ok installed"}}); - } - - std::unique_ptr make_status_feature_pgh(const char* name, - const char* feature, - const char* depends, - const char* triplet) - { - using Pgh = std::unordered_map; - return std::make_unique(Pgh{{"Package", name}, - {"Version", "1"}, - {"Feature", feature}, - {"Architecture", triplet}, - {"Multi-Arch", "same"}, - {"Depends", depends}, - {"Status", "install ok installed"}}); - } - - PackageSpec unsafe_pspec(std::string name, Triplet t) - { - auto m_ret = PackageSpec::from_name_and_triplet(name, t); - REQUIRE(m_ret.has_value()); - return m_ret.value_or_exit(VCPKG_LINE_INFO); - } - - - #if defined(_WIN32) - - static bool system_allows_symlinks() { - HKEY key; - bool allow_symlinks = true; - - const auto status = RegOpenKeyExW( - HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock)", 0, 0, &key); - - if (status == ERROR_FILE_NOT_FOUND) - { - allow_symlinks = false; - std::clog << "Symlinks are not allowed on this system\n"; - } - - if (status == ERROR_SUCCESS) RegCloseKey(key); - - return allow_symlinks; - } - - static fs::path internal_temporary_directory() { - wchar_t* tmp = static_cast(std::calloc(32'767, 2)); - - if (!GetEnvironmentVariableW(L"TEMP", tmp, 32'767)) { - std::cerr << "No temporary directory found.\n"; - std::abort(); - } - - fs::path result = tmp; - std::free(tmp); - - return result / L"vcpkg-test"; - } - - #else - - constexpr static bool system_allows_symlinks() { - return true; - } - static fs::path internal_temporary_directory() { - return "/tmp/vcpkg-test"; - } - - #endif - - const bool SYMLINKS_ALLOWED = system_allows_symlinks(); - const fs::path TEMPORARY_DIRECTORY = internal_temporary_directory(); -} -- cgit v1.2.3 From 2c20a9d98186e029ff443932295d7cdcad96980e Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Mon, 22 Jul 2019 11:16:07 -0700 Subject: fix some of the awful mix of macros --- toolsrc/src/vcpkg-test/util.cpp | 67 ++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 24 deletions(-) (limited to 'toolsrc/src') diff --git a/toolsrc/src/vcpkg-test/util.cpp b/toolsrc/src/vcpkg-test/util.cpp index 19f8f355e..a80ab36a0 100644 --- a/toolsrc/src/vcpkg-test/util.cpp +++ b/toolsrc/src/vcpkg-test/util.cpp @@ -5,6 +5,9 @@ #include #include +// used to get the implementation specific compiler flags (i.e., __cpp_lib_filesystem) +#include + #include #include @@ -12,16 +15,24 @@ #include #endif -#if defined(_MSC_VER) && _MSC_VER >= 1914 +#define FILESYSTEM_SYMLINK_STD 0 +#define FILESYSTEM_SYMLINK_UNIX 1 +#define FILESYSTEM_SYMLINK_NONE 2 -#define USE_STD_FILESYSTEM +#if defined(__cpp_lib_filesystem) +#define FILESYSTEM_SYMLINK FILESYSTEM_SYMLINK_STD #include // required for filesystem::create_{directory_}symlink #elif !defined(_MSC_VER) +#define FILESYSTEM_SYMLINK FILESYSTEM_SYMLINK_UNIX #include +#else + +#define FILESYSTEM_SYMLINK FILESYSTEM_SYMLINK_NONE + #endif namespace vcpkg::Test @@ -63,15 +74,14 @@ namespace vcpkg::Test return m_ret.value_or_exit(VCPKG_LINE_INFO); } - - - // I am so sorry for this awful mix of macros - - static bool system_allows_symlinks() { + static bool system_allows_symlinks() + { #if defined(_WIN32) - #if !defined(USE_STD_FILESYSTEM) - return false; - #else + if (!__cpp_lib_filesystem) + { + return false; + } + HKEY key; bool allow_symlinks = true; @@ -87,17 +97,18 @@ namespace vcpkg::Test if (status == ERROR_SUCCESS) RegCloseKey(key); return allow_symlinks; - #endif #else return true; #endif } - static fs::path internal_temporary_directory() { + static fs::path internal_temporary_directory() + { #if defined(_WIN32) wchar_t* tmp = static_cast(std::calloc(32'767, 2)); - if (!GetEnvironmentVariableW(L"TEMP", tmp, 32'767)) { + if (!GetEnvironmentVariableW(L"TEMP", tmp, 32'767)) + { std::cerr << "No temporary directory found.\n"; std::abort(); } @@ -114,9 +125,14 @@ namespace vcpkg::Test const bool SYMLINKS_ALLOWED = system_allows_symlinks(); const fs::path TEMPORARY_DIRECTORY = internal_temporary_directory(); - void create_symlink(const fs::path& target, const fs::path& file, std::error_code& ec) { -#if defined(_MSC_VER) - #if defined(USE_STD_FILESYSTEM) +#if FILESYSTEM_SYMLINK == FILSYSTEM_SYMLINK_NONE + constexpr inline char no_filesystem_message[] = + " doesn't exist; on windows, we don't attempt to use the win32 calls to create symlinks"; +#endif + + void create_symlink(const fs::path& target, const fs::path& file, std::error_code& ec) + { +#if FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_STD if (SYMLINKS_ALLOWED) { std::filesystem::path targetp = target.native(); @@ -125,20 +141,22 @@ namespace vcpkg::Test std::filesystem::create_symlink(targetp, filep); } else - #endif { vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "Symlinks are not allowed on this system"); } -#else - if(symlink(target.c_str(), file.c_str()) != 0) { +#elif FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_UNIX + if (symlink(target.c_str(), file.c_str()) != 0) + { ec.assign(errno, std::system_category()); } +#else + vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, no_filesystem_message); #endif } - void create_directory_symlink(const fs::path& target, const fs::path& file, std::error_code& ec) { -#if defined(_MSC_VER) - #if defined(USE_STD_FILESYSTEM) + void create_directory_symlink(const fs::path& target, const fs::path& file, std::error_code& ec) + { +#if FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_STD if (SYMLINKS_ALLOWED) { std::filesystem::path targetp = target.native(); @@ -147,12 +165,13 @@ namespace vcpkg::Test std::filesystem::create_symlink(targetp, filep); } else - #endif { vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "Symlinks are not allowed on this system"); } -#else +#elif FILESYSTEM_SYMLINK == FILESYSTEM_SYMLINK_UNIX ::vcpkg::Test::create_symlink(target, file, ec); +#else + vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, no_filesystem_message); #endif } } -- cgit v1.2.3