summaryrefslogtreecommitdiffstats
path: root/src/qdoc/qdoc/tests/qdoc/filesystem/catch_fileresolver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qdoc/qdoc/tests/qdoc/filesystem/catch_fileresolver.cpp')
-rw-r--r--src/qdoc/qdoc/tests/qdoc/filesystem/catch_fileresolver.cpp307
1 files changed, 307 insertions, 0 deletions
diff --git a/src/qdoc/qdoc/tests/qdoc/filesystem/catch_fileresolver.cpp b/src/qdoc/qdoc/tests/qdoc/filesystem/catch_fileresolver.cpp
new file mode 100644
index 000000000..f9b5af66b
--- /dev/null
+++ b/src/qdoc/qdoc/tests/qdoc/filesystem/catch_fileresolver.cpp
@@ -0,0 +1,307 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <catch_conversions/qdoc_catch_conversions.h>
+
+#include <catch/catch.hpp>
+
+#include <qdoc/filesystem/fileresolver.h>
+
+#include <catch_generators/generators/path_generator.h>
+
+#include <vector>
+#include <algorithm>
+#include <random>
+
+#include <QTemporaryDir>
+#include <QFileInfo>
+#include <QDir>
+#include <QIODeviceBase>
+
+SCENARIO("Inspecting the directories that will be used for searching", "[ResolvingFiles][Directory][Path][Canonicalization][Contents]") {
+ GIVEN("Some collection of paths representing existing directories on the filesystem") {
+ std::size_t directories_amount = GENERATE(take(10, random(2, 10)));
+
+ std::vector<QTemporaryDir> working_directories(directories_amount);
+
+ std::vector<DirectoryPath> directories;
+ directories.reserve(directories_amount);
+
+ std::transform(
+ working_directories.begin(), working_directories.end(),
+ std::back_inserter(directories),
+ [](auto& dir){ return *DirectoryPath::refine(dir.path()); }
+ );
+
+ AND_GIVEN("That the collection of those paths is ordered and contains no duplicates") {
+ std::sort(directories.begin(), directories.end());
+ directories.erase(std::unique(directories.begin(), directories.end()), directories.end());
+
+ WHEN("A mean of searching for files is obtained from that collection") {
+ FileResolver file_resolver{std::vector(directories)};
+
+ THEN("The collection of directories that will be used for searching is equivalent to the one from which a mean of searching for files was obtained") {
+ REQUIRE(file_resolver.get_search_directories() == directories);
+ }
+ }
+ }
+
+ AND_GIVEN("That the collection of those paths is potentially unordered but contains no duplicates") {
+ std::sort(directories.begin(), directories.end());
+ directories.erase(std::unique(directories.begin(), directories.end()), directories.end());
+
+ std::shuffle(directories.begin(), directories.end(), std::mt19937{std::random_device{}()});
+
+ WHEN("A mean of searching for files is obtained from that collection") {
+ FileResolver file_resolver{std::vector(directories)};
+
+ THEN("The collection of directories that will be used for searching is equivalent to the one from which a mean of searching for files was obtained if it was sorted") {
+ std::sort(directories.begin(), directories.end());
+
+ REQUIRE(file_resolver.get_search_directories() == directories);
+ }
+ }
+ }
+
+ AND_GIVEN("That the collection of those paths is ordered but contains duplicates") {
+ directories.reserve(directories.size());
+
+ std::transform(
+ working_directories.begin(), working_directories.end(),
+ std::back_inserter(directories),
+ [](auto& dir){ return *DirectoryPath::refine(dir.path()); }
+ );
+
+ std::sort(directories.begin(), directories.end());
+
+ WHEN("A mean of searching for files is obtained from that collection") {
+ FileResolver file_resolver{std::vector(directories)};
+
+ THEN("The collection of directories that will be used for searching is equivalent to the one from which a mean of searching for files was obtained if it contained no duplicates") {
+ directories.erase(std::unique(directories.begin(), directories.end()), directories.end());
+
+ REQUIRE(file_resolver.get_search_directories() == directories);
+ }
+ }
+ }
+
+ AND_GIVEN("That the collection of those paths is potentially unordered and contains duplicates") {
+ directories.reserve(directories.size());
+
+ std::transform(
+ working_directories.begin(), working_directories.end(),
+ std::back_inserter(directories),
+ [](auto& dir){ return *DirectoryPath::refine(dir.path()); }
+ );
+
+ std::shuffle(directories.begin(), directories.end(), std::mt19937{std::random_device{}()});
+
+ WHEN("A mean of searching for files is obtained from that collection") {
+ FileResolver file_resolver{std::vector(directories)};
+
+ THEN("The collection of directories that will be used for searching is equivalent to the one from which a mean of searching for files was obtained if it was sorted and it contained no duplicates") {
+ std::sort(directories.begin(), directories.end());
+ directories.erase(std::unique(directories.begin(), directories.end()), directories.end());
+
+ REQUIRE(file_resolver.get_search_directories() == directories);
+ }
+ }
+ }
+ }
+}
+
+SCENARIO("Finding a file based on some root search directories", "[ResolvingFiles][File][Path][Validation]") {
+ // TODO: Rewrite those tests under a single setup. Be careful of
+ // how this is done as Catch will rerun sections only under
+ // specific condition such that we may incur in collisions for the
+ // path if done incorrectly.
+ GIVEN("Some directory on the filesystem") {
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ DirectoryPath directory{*DirectoryPath::refine(working_directory.path())};
+
+ AND_GIVEN("A mean of searching for files based on that directory") {
+ FileResolver file_resolver{std::vector{directory}};
+
+ AND_GIVEN("A relative path that does not represent an element on the filesystem that is reachable from that directory") {
+ QString relative_path = GENERATE(filter([](auto& path){ return path != "." && path != ".."; }, take(100, qdoc::catch_generators::native_relative_path())));
+ CAPTURE(relative_path);
+
+ REQUIRE(!QFileInfo{working_directory.path() + '/' + relative_path}.exists());
+
+ WHEN("The relative path is used as a query to resolve a file") {
+ auto maybe_resolved_file{file_resolver.resolve(relative_path)};
+
+ THEN("The query cannot be resolved") {
+ REQUIRE(!maybe_resolved_file);
+ }
+ }
+ }
+ }
+ }
+
+ GIVEN("Some directory on the filesystem") {
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ DirectoryPath directory{*DirectoryPath::refine(working_directory.path())};
+
+ AND_GIVEN("A mean of searching for files based on that directory") {
+ FileResolver file_resolver{std::vector{directory}};
+
+ AND_GIVEN("A relative path that represents an existing directory on the filesystem that is reachable from that directory") {
+ QString relative_path = GENERATE(take(100, qdoc::catch_generators::native_relative_directory_path()));
+ CAPTURE(relative_path);
+
+ REQUIRE(QDir{working_directory.path()}.mkpath(relative_path));
+
+ WHEN("The relative path is used as a query to resolve a file") {
+ auto maybe_resolved_file{file_resolver.resolve(relative_path)};
+
+ THEN("The query cannot be resolved") {
+ REQUIRE(!maybe_resolved_file);
+ }
+ }
+ }
+ }
+ }
+
+
+ GIVEN("Some directory on the filesystem") {
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ DirectoryPath directory{*DirectoryPath::refine(working_directory.path())};
+
+ AND_GIVEN("A mean of searching for files based on that directory") {
+ FileResolver file_resolver{std::vector{directory}};
+
+ AND_GIVEN("A relative path that represents an existing file on the filesystem that is reachable from that directory") {
+ QString relative_path = GENERATE(take(100, qdoc::catch_generators::native_relative_file_path()));
+ CAPTURE(relative_path);
+
+ REQUIRE(QDir{working_directory.path()}.mkpath(QFileInfo{relative_path}.path()));
+ REQUIRE(QFile{working_directory.path() + "/" + relative_path}.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly));
+
+ WHEN("The relative path is used as a query to resolve a file") {
+ auto maybe_resolved_file{file_resolver.resolve(relative_path)};
+
+ THEN("The query can be resolved") {
+ REQUIRE(maybe_resolved_file);
+ }
+ }
+ }
+ }
+ }
+
+ GIVEN("Some directories on the filesystem") {
+ std::size_t directories_amount = GENERATE(take(10, random(2, 10)));
+
+ std::vector<QTemporaryDir> working_directories(directories_amount);
+ REQUIRE(std::all_of(working_directories.cbegin(), working_directories.cend(), [](auto& dir){ return dir.isValid(); }));
+
+ std::vector<DirectoryPath> directories;
+ directories.reserve(directories_amount);
+
+ std::transform(
+ working_directories.begin(), working_directories.end(),
+ std::back_inserter(directories),
+ [](auto& dir){ return *DirectoryPath::refine(dir.path()); }
+ );
+
+ AND_GIVEN("A relative path that represents an existing file on the filesystem that is reachable from exactly one of those directories") {
+ QString relative_path = GENERATE(take(10, qdoc::catch_generators::native_relative_file_path()));
+ CAPTURE(relative_path);
+
+ std::size_t containing_directory_index = GENERATE_COPY(take(1, random(std::size_t{0}, directories_amount - 1)));
+ CAPTURE(containing_directory_index);
+ CAPTURE(working_directories[containing_directory_index].path());
+
+ REQUIRE(QDir{working_directories[containing_directory_index].path()}.mkpath(QFileInfo{relative_path}.path()));
+ REQUIRE(QFile{working_directories[containing_directory_index].path() + "/" + relative_path}.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly));
+
+ AND_GIVEN("A mean of searching for files based on all of those directories") {
+ FileResolver file_resolver{std::move(directories)};
+
+ WHEN("The relative path is used as a query to resolve a file") {
+ auto maybe_resolved_file{file_resolver.resolve(relative_path)};
+
+ THEN("The query can be resolved") {
+ REQUIRE(maybe_resolved_file);
+ }
+ }
+ }
+ }
+ }
+}
+
+SCENARIO("Inspecting the content of a file that was resolved", "[ResolvingFiles][File][Path][Validation][Contents]") {
+ GIVEN("A mean of resolving files based on some directory") {
+ QTemporaryDir working_directory{};
+ REQUIRE(working_directory.isValid());
+
+ DirectoryPath directory{*DirectoryPath::refine(working_directory.path())};
+
+ FileResolver file_resolver{std::vector{directory}};
+
+ AND_GIVEN("A relative path that represents an existing file on the filesystem that is reachable from that directory") {
+ QString relative_path = GENERATE(take(100, qdoc::catch_generators::native_relative_file_path()));
+ CAPTURE(relative_path);
+
+ REQUIRE(QDir{working_directory.path()}.mkpath(QFileInfo{relative_path}.path()));
+ REQUIRE(QFile{working_directory.path() + "/" + relative_path}.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly));
+
+ WHEN("A file is resolved using that path as a query") {
+ auto resolved_file{*file_resolver.resolve(relative_path)};
+
+ THEN("The resolved file contains the query that was used to resolve it") {
+ REQUIRE(resolved_file.get_query() == relative_path);
+ }
+
+ THEN("The resolved file contains a canonicalized path pointing to the resolved file") {
+ REQUIRE(resolved_file.get_path() == QFileInfo{working_directory.path() + "/" + relative_path}.canonicalFilePath());
+ }
+ }
+ }
+ }
+}
+
+TEST_CASE(
+ "When a query can be resolved in more than one search directory, it is resolved in the greatest lower bound of the set of directories",
+ "[ResolvingFiles][File][Path][Validation][SpecialCase]"
+) {
+ std::size_t directories_amount = GENERATE(take(10, random(2, 10)));
+
+ QString relative_path = GENERATE(take(10, qdoc::catch_generators::native_relative_file_path()));
+ CAPTURE(relative_path);
+
+ std::vector<QTemporaryDir> working_directories(directories_amount);
+ REQUIRE(std::all_of(working_directories.cbegin(), working_directories.cend(), [](auto& dir){ return dir.isValid(); }));
+
+ for (const auto& directory : working_directories) {
+ CAPTURE(directory.path());
+ REQUIRE(QDir{directory.path()}.mkpath(QFileInfo{relative_path}.path()));
+ REQUIRE(QFile{directory.path() + "/" + relative_path}.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly));
+ }
+
+ std::vector<DirectoryPath> directories;
+ directories.reserve(directories_amount);
+
+ std::transform(
+ working_directories.begin(), working_directories.end(),
+ std::back_inserter(directories),
+ [](auto& dir){ return *DirectoryPath::refine(dir.path()); }
+ );
+
+ FileResolver file_resolver{std::move(directories)};
+ auto resolved_file{*file_resolver.resolve(relative_path)};
+
+ auto& greatest_lower_bound{*std::min_element(file_resolver.get_search_directories().cbegin(), file_resolver.get_search_directories().cend())};
+
+ REQUIRE(
+ resolved_file.get_path()
+ ==
+ QFileInfo{greatest_lower_bound.value() + "/" + relative_path}.canonicalFilePath()
+ );
+}