diff options
Diffstat (limited to 'tests')
28 files changed, 9 insertions, 3324 deletions
diff --git a/tests/auto/qdoc/CMakeLists.txt b/tests/auto/qdoc/CMakeLists.txt index a2c3ae274..08e79d8bf 100644 --- a/tests/auto/qdoc/CMakeLists.txt +++ b/tests/auto/qdoc/CMakeLists.txt @@ -6,10 +6,14 @@ if(CMAKE_VERSION VERSION_LESS "3.19" AND MSVC AND QT_FEATURE_debug_and_release) return() endif() +set(QDOC_PROJECT_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../../../src/qdoc/) set(QDOC_SOURCE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../../../src/qdoc/qdoc/) set(QDOC_INCLUDE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../../../src/qdoc/qdoc/) -add_subdirectory(catch_generators) +if(QT_BUILD_STANDALONE_TESTS) + include(${QDOC_PROJECT_DIRECTORY}/catch_generators/tests/CMakeLists.txt) +endif() + add_subdirectory(qdoc) add_subdirectory(config) add_subdirectory(generatedoutput) diff --git a/tests/auto/qdoc/catch_generators/CMakeLists.txt b/tests/auto/qdoc/catch_generators/CMakeLists.txt deleted file mode 100644 index 88c051636..000000000 --- a/tests/auto/qdoc/catch_generators/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(tests) diff --git a/tests/auto/qdoc/catch_generators/qdoc_catch_generators.h b/tests/auto/qdoc/catch_generators/qdoc_catch_generators.h deleted file mode 100644 index e99a57ec5..000000000 --- a/tests/auto/qdoc/catch_generators/qdoc_catch_generators.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "src/generators/qchar_generator.h" -#include "src/generators/qstring_generator.h" -#include "src/generators/path_generator.h" diff --git a/tests/auto/qdoc/catch_generators/src/generators/combinators/cycle_generator.h b/tests/auto/qdoc/catch_generators/src/generators/combinators/cycle_generator.h deleted file mode 100644 index b60600747..000000000 --- a/tests/auto/qdoc/catch_generators/src/generators/combinators/cycle_generator.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../../namespaces.h" -#include "../../utilities/semantics/generator_handler.h" - -#include <catch/catch.hpp> - -#include <vector> - -namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE { - namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE { - - template<typename T> - class CycleGenerator : public Catch::Generators::IGenerator<T> { - public: - CycleGenerator(Catch::Generators::GeneratorWrapper<T>&& generator) - : generator{std::move(generator)}, - cache{}, - cache_index{0} - { - // REMARK: We generally handle extracting the first - // value by using an handler, to avoid code - // duplication and the possibility of an error. - // In this specific case, we turn to a more "manual" - // approach as it better models the cache-based - // implementation, removing the need to not increment - // cache_index the first time that next is called. - cache.emplace_back(this->generator.get()); - } - - T const& get() const override { return cache[cache_index]; } - - bool next() override { - if (generator.next()) { - cache.emplace_back(generator.get()); - ++cache_index; - } else { - cache_index = (cache_index + 1) % cache.size(); - } - - return true; - } - - private: - Catch::Generators::GeneratorWrapper<T> generator; - - std::vector<T> cache; - std::size_t cache_index; - }; - - } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE - - /*! - * Returns a generator that behaves like \a generator until \a - * generator is exhausted, repeating the same generation that \a - * generator produced, infinitely, afterwards. - * - * This is generally intended to produce infinite generators from - * finite ones. - * - * For example, consider a generator that produces values based on - * another generator that it owns. - * If the owning generator needs to produce more values that the - * owned generator can support, it might fail at some point. - * By cycling over the owned generator, we can extend the sequence - * of produced values so that enough are generated, in a controlled - * way. - * - * The type T should generally be copyable for this generator to - * work. - */ - template<typename T> - inline Catch::Generators::GeneratorWrapper<T> cycle(Catch::Generators::GeneratorWrapper<T>&& generator) { - return Catch::Generators::GeneratorWrapper<T>(std::unique_ptr<Catch::Generators::IGenerator<T>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::CycleGenerator(std::move(generator)))); - } - -} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE diff --git a/tests/auto/qdoc/catch_generators/src/generators/combinators/oneof_generator.h b/tests/auto/qdoc/catch_generators/src/generators/combinators/oneof_generator.h deleted file mode 100644 index 5de9dcb6c..000000000 --- a/tests/auto/qdoc/catch_generators/src/generators/combinators/oneof_generator.h +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../../namespaces.h" -#include "../../utilities/statistics/percentages.h" -#include "../../utilities/semantics/generator_handler.h" - -#include <catch/catch.hpp> - -#include <vector> -#include <random> -#include <algorithm> -#include <numeric> - -namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE { - namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE { - - template<typename T> - class OneOfGenerator : public Catch::Generators::IGenerator<T> { - public: - OneOfGenerator( - std::vector<Catch::Generators::GeneratorWrapper<T>>&& generators, - const std::vector<double>& weights - ) : generators{std::move(generators)}, - random_engine{std::random_device{}()}, - choice_distribution{weights.cbegin(), weights.cend()} - { - assert(weights.size() == this->generators.size()); - assert(std::reduce(weights.cbegin(), weights.cend()) == Approx(100.0)); - - std::transform( - this->generators.begin(), this->generators.end(), this->generators.begin(), - [](auto& generator){ return QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(generator)); } - ); - - static_cast<void>(next()); - } - - T const& get() const override { return current_value; } - - bool next() override { - std::size_t generator_index{choice_distribution(random_engine)}; - - if (!generators[generator_index].next()) return false; - current_value = generators[generator_index].get(); - - return true; - } - - private: - std::vector<Catch::Generators::GeneratorWrapper<T>> generators; - - std::mt19937 random_engine; - std::discrete_distribution<std::size_t> choice_distribution; - - T current_value; - }; - - } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE - - /*! - * Returns a generator whose set of elements is the union of the - * set of elements of the generators in \a generators. - * - * Each time the generator produces a value, a generator from \a - * generators is randomly chosen to produce the value. - * - * The distribution for the choice is given by \a weights. - * The \e {ith} element in \a weights represent the percentage - * probability of the \e {ith} element of \a generators to be - * chosen. - * - * It follows that the size of \a weights must be the same as the - * size of \a generators. - * - * Furthermore, the sum of elements in \a weights should be a - * hundred. - * - * The generator produces values until a generator that is chosen - * to produce a value is unable to do so. - * The first such generator to do so will stop the generation - * independently of the availability of the other generators. - * - * Similarly, values will be produced as long as the chosen - * generator can produce a value, independently of the other - * generators being exhausted already. - */ - template<typename T> - inline Catch::Generators::GeneratorWrapper<T> oneof( - std::vector<Catch::Generators::GeneratorWrapper<T>>&& generators, - const std::vector<double>& weights - ) { - return Catch::Generators::GeneratorWrapper<T>(std::unique_ptr<Catch::Generators::IGenerator<T>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::OneOfGenerator(std::move(generators), weights))); - } - - - /*! - * Returns a generator whose set of elements is the union of the - * set of elements of the generators in \a generators and in which - * the distribution of the generated elements is uniform over \a - * generators. - * - * Each time the generator produces a value, a generator from \a - * generators is randomly chosen to produce the value. - * - * Each generator from \a generators has the same chance of being - * chosen. - * - * Do note that the distribution over the set of values is not - * necessarily uniform. - * - * The generator produces values until a generator that is chosen - * to produce a value is unable to do so. - * The first such generator to do so will stop the generation - * independently of the availability of the other generators. - * - * Similarly, values will be produced as long as the chosen - * generator can produce a value, independently of the other - * generators being exhausted already. - */ - template<typename T> - inline Catch::Generators::GeneratorWrapper<T> uniform_oneof( - std::vector<Catch::Generators::GeneratorWrapper<T>>&& generators - ) { - std::vector<double> weights( - generators.size(), - QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::uniform_probability(generators.size()) - ); - return oneof(std::move(generators), std::move(weights)); - } - - /*! - * Returns a generator whose set of elements is the union of the - * set of elements of the generators in \a generators and in which - * the distribution of the generated elements is uniform over the - * elements of \a generators. - * - * The generators in \a generator should have a uniform - * distribution and be finite. - * If the set of elements that the generators in \a generator is - * not disjoint, the distribution will be skewed towards repeated - * elements. - * - * Each time the generator produces a value, a generator from \a - * generators is randomly chosen to produce the value. - * - * Each generator from \a generators has a probability of being - * chosen based on the proportion of the cardinality of the subset - * it produces. - * - * The \e {ith} element of \a amounts should contain the - * cardinality of the set produced by the \e {ith} generator in \a - * generators. - * - * The generator produces values until a generator that is chosen - * to produce a value is unable to do so. - * The first such generator to do so will stop the generation - * independently of the availability of the other generators. - * - * Similarly, values will be produced as long as the chosen - * generator can produce a value, independently of the other - * generators being exhausted already. - */ - template<typename T> - inline Catch::Generators::GeneratorWrapper<T> uniformly_valued_oneof( - std::vector<Catch::Generators::GeneratorWrapper<T>>&& generators, - const std::vector<std::size_t>& amounts - ) { - std::size_t total_amount{std::accumulate(amounts.cbegin(), amounts.cend(), std::size_t{0})}; - - std::vector<double> weights; - weights.reserve(amounts.size()); - - std::transform( - amounts.cbegin(), amounts.cend(), - std::back_inserter(weights), - [total_amount](auto element){ return QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::percent_of(static_cast<double>(element), static_cast<double>(total_amount)); } - ); - - return oneof(std::move(generators), std::move(weights)); - } - -} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE diff --git a/tests/auto/qdoc/catch_generators/src/generators/k_partition_of_r_generator.h b/tests/auto/qdoc/catch_generators/src/generators/k_partition_of_r_generator.h deleted file mode 100644 index 832ee2838..000000000 --- a/tests/auto/qdoc/catch_generators/src/generators/k_partition_of_r_generator.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../namespaces.h" - -#include <catch/catch.hpp> - -#include <random> -#include <numeric> -#include <algorithm> - -namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE { - namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE { - - class KPartitionOfRGenerator : public Catch::Generators::IGenerator<std::vector<double>> { - public: - KPartitionOfRGenerator(double r, std::size_t k) - : random_engine{std::random_device{}()}, - interval_distribution{0.0, r}, - k{k}, - r{r}, - current_partition(k) - { - assert(r >= 0.0); - assert(k >= 1); - - static_cast<void>(next()); - } - - std::vector<double> const& get() const override { return current_partition; } - - bool next() override { - if (k == 1) current_partition[0] = r; - else { - // REMARK: The following wasn't formally proved - // but is based on intuition. - // It is probably erroneous but is expected to be - // good enough for our case. - - // REMARK: We aim to provide a non skewed - // distribution for the elements of the partition. - // - // The reasoning for this is to ensure that our - // testing surface has a good chance of hitting - // many of the available elements between the many - // runs. - // - // To approximate this, a specific algorithm was chosen. - // The following code can be intuitively seen as doing the following: - // - // Consider an interval [0.0, r] on the real line, where r > 0.0. - // - // k - 1 > 0 elements of the interval are chosen, - // partitioning the interval into disjoint - // sub-intervals. - // - // --------------------------------------------------------------------------------------------------------------------- - // | | | | | - // 0 k_1 k_2 k_3 r - // | | | | | - // _______--------------------_______________________________________________________----------------------------------- - // k_1 - 0 k_2 - k_1 k_3 - k_2 r - k_3 - // p1 p2 p3 p4 - // - // The length of each sub interval is chosen as one of the elements of the partition. - // - // Trivially, the sum of the chosen elements is r. - // - // Furthermore, as long as the distribution used - // to choose the elements of the original interval - // is uniform, the probability of each partition - // being produced should tend to being uniform - // itself. - std::generate(current_partition.begin(), current_partition.end() - 1, [this](){ return interval_distribution(random_engine); }); - - current_partition.back() = r; - - std::sort(current_partition.begin(), current_partition.end()); - std::adjacent_difference(current_partition.begin(), current_partition.end(), current_partition.begin()); - } - - return true; - } - - private: - std::mt19937 random_engine; - std::uniform_real_distribution<double> interval_distribution; - - std::size_t k; - double r; - - std::vector<double> current_partition; - }; - - } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE - - /*! - * Returns a generator that generates collections of \a k elements - * whose sum is \a r. - * - * \a r must be a real number greater or euqal to zero and \a k - * must be a natural number greater than zero. - * - * The generated partitions tends to be uniformely distributed - * over the set of partitions of r. - */ - inline Catch::Generators::GeneratorWrapper<std::vector<double>> k_partition_of_r(double r, std::size_t k) { - return Catch::Generators::GeneratorWrapper<std::vector<double>>(std::unique_ptr<Catch::Generators::IGenerator<std::vector<double>>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::KPartitionOfRGenerator(r, k))); - } - -} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE diff --git a/tests/auto/qdoc/catch_generators/src/generators/path_generator.h b/tests/auto/qdoc/catch_generators/src/generators/path_generator.h deleted file mode 100644 index 875502e49..000000000 --- a/tests/auto/qdoc/catch_generators/src/generators/path_generator.h +++ /dev/null @@ -1,853 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -// TODO: Change the include paths to implicitly consider -// `catch_generators` a root directory and change the CMakeLists.txt -// file to make this possible. - -#include "../namespaces.h" -#include "qchar_generator.h" -#include "qstring_generator.h" -#include "../utilities/semantics/move_into_vector.h" -#include "../utilities/semantics/generator_handler.h" - -#if defined(Q_OS_WINDOWS) - - #include "combinators/cycle_generator.h" - -#endif - -#include <catch/catch.hpp> - -#include <random> - -#include <QChar> -#include <QString> -#include <QStringList> -#include <QRegularExpression> - -#if defined(Q_OS_WINDOWS) - - #include <QStorageInfo> - -#endif - -namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE { - - - struct PathGeneratorConfiguration { - double multi_device_path_probability{0.5}; - double absolute_path_probability{0.5}; - double directory_path_probability{0.5}; - double has_trailing_separator_probability{0.5}; - std::size_t minimum_components_amount{1}; - std::size_t maximum_components_amount{10}; - - PathGeneratorConfiguration& set_multi_device_path_probability(double amount) { - multi_device_path_probability = amount; - return *this; - } - - PathGeneratorConfiguration& set_absolute_path_probability(double amount) { - absolute_path_probability = amount; - return *this; - } - - PathGeneratorConfiguration& set_directory_path_probability(double amount) { - directory_path_probability = amount; - return *this; - } - - PathGeneratorConfiguration& set_has_trailing_separator_probability(double amount) { - has_trailing_separator_probability = amount; - return *this; - } - - PathGeneratorConfiguration& set_minimum_components_amount(std::size_t amount) { - minimum_components_amount = amount; - return *this; - } - - PathGeneratorConfiguration& set_maximum_components_amount(std::size_t amount) { - maximum_components_amount = amount; - return *this; - } - }; - - /*! - * \class PathGeneratorConfiguration - * \brief Defines some parameters to customize the generation of - * paths by a PathGenerator. - */ - - /*! - * \variable PathGeneratorConfiguration::multi_device_path_probability - * - * Every path produced by a PathGenerator configured with a - * mutli_device_path_probability of n has a probability of n to be - * \e {Multi-Device} and a probability of 1.0 - n to not be \a - * {Multi-Device}. - * - * multi_device_path_probability should be a value in the range [0.0, - * 1.0]. - */ - - /*! - * \variable PathGeneratorConfiguration::absolute_path_probability - * - * Every path produced by a PathGenerator configured with an - * absolute_path_probability of n has a probability of n to be \e - * {Absolute} and a probability of 1.0 - n to be \e {Relative}. - * - * absolute_path_probability should be a value in the range [0.0, - * 1.0]. - */ - - /*! - * \variable PathGeneratorConfiguration::directory_path_probability - * - * Every path produced by a PathGenerator configured with a - * directory_path_probability of n has a probability of n to be \e - * {To a Directory} and a probability of 1.0 - n to be \e {To a - * File}. - * - * directory_path_probability should be a value in the range [0.0, - * 1.0]. - */ - - /*! - * \variable PathGeneratorConfiguration::has_trailing_separator_probability - * - * Every path produced by a PathGenerator configured with an - * has_trailing_separator_probability of n has a probability of n - * to \e {Have a Trailing Separator} and a probability of 1.0 - n - * to not \e {Have a Trailing Separator}, when this is applicable. - * - * has_trailing_separator_probability should be a value in the - * range [0.0, 1.0]. - */ - - /*! - * \variable PathGeneratorConfiguration::minimum_components_amount - * - * Every path produced by a PathGenerator configured with a - * minimum_components_amount of n will be the concatenation of at - * least n non \e {device}, non \e {root}, non \e {separator} - * components. - * - * minimum_components_amount should be greater than zero and less - * than maximum_components_amount. - */ - - /*! - * \variable PathGeneratorConfiguration::maximum_components_amount - * - * Every path produced by a PathGenerator configured with a - * maximum_components_amount of n will be the concatenation of at - * most n non \e {device}, non \e {root}, non \e {separator} components. - * - * maximum_components_amount should be greater than or equal to - * minimum_components_amount. - */ - - - namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE { - - class PathGenerator : public Catch::Generators::IGenerator<QString> { - public: - PathGenerator( - Catch::Generators::GeneratorWrapper<QString>&& device_component_generator, - Catch::Generators::GeneratorWrapper<QString>&& root_component_generator, - Catch::Generators::GeneratorWrapper<QString>&& directory_component_generator, - Catch::Generators::GeneratorWrapper<QString>&& filename_component_generator, - Catch::Generators::GeneratorWrapper<QString>&& separator_component_generator, - PathGeneratorConfiguration configuration = PathGeneratorConfiguration{} - ) : device_component_generator{QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(device_component_generator))}, - root_component_generator{QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(root_component_generator))}, - directory_component_generator{QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(directory_component_generator))}, - filename_component_generator{QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(filename_component_generator))}, - separator_component_generator{QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(separator_component_generator))}, - random_engine{std::random_device{}()}, - components_amount_distribution{configuration.minimum_components_amount, configuration.maximum_components_amount}, - is_multi_device_distribution{configuration.multi_device_path_probability}, - is_absolute_path_distribution{configuration.absolute_path_probability}, - is_directory_path_distribution{configuration.directory_path_probability}, - has_trailing_separator{configuration.has_trailing_separator_probability}, - current_path{} - { - assert(configuration.minimum_components_amount > 0); - assert(configuration.minimum_components_amount <= configuration.maximum_components_amount); - - if (!next()) - Catch::throw_exception("Not enough values to initialize the first string"); - } - - QString const& get() const override { return current_path; } - - bool next() override { - std::size_t components_amount{components_amount_distribution(random_engine)}; - - current_path = ""; - - // REMARK: As per our specification of a path, we - // do not count device components, and separators, - // when considering the amount of components in a - // path. - // This is a tradeoff that is not necessarily - // precise. - // Counting those kinds of components, on one - // hand, would allow a device component to stands - // on its own as a path, for example "C:", which - // might actually be correct in some path format. - // On the other hand, counting those kinds of - // components makes the construction of paths for - // our model much more complex with regards, for - // example, to the amount of component. - // - // Counting device components, since they can - // appear both in relative and absolute paths, - // makes the minimum amount of components - // different for different kinds of paths. - // - // Since absolute paths always require a root - // component, the minimum amount of components for - // a multi-device absolute path is 2. - // - // But an absolute path that is not multi-device - // would only require one minimum component. - // - // Similarly, problems arise with the existence of - // Windows' relative multi-device path, which - // require a leading separator component after a - // device component. - // - // This problem mostly comes from our model - // simplifying the definition of paths quite a bit - // into binary-forms. - // This simplifies the code and its structure, - // sacrificing some precision. - // The lost precision is almost none for POSIX - // based paths, but is graver for DOS paths, since - // they have a more complex specification. - // - // Currently, we expect that the paths that QDoc - // will encounter will mostly be in POSIX-like - // forms, even on Windows, and aim to support - // that, such that the simplification of code is - // considered a better tradeoff compared to the - // loss of precision. - // - // If this changes, the model should be changed to - // pursue a Windows-first modeling, moving the - // categorization of paths from the current binary - // model to the absolute, drive-relative and - // relative triptych that Windows uses. - // This more complex model should be able to - // completely describe posix paths too, making it - // a superior choice as long as the complexity is - // warranted. - // - // Do note that the model similarly can become - // inconsistent when used to generate format of - // paths such as the one used in some resource - // systems. - // Those are considered out-of-scope for our needs - // and were not taken into account when developing - // this generator. - if (is_multi_device_distribution(random_engine)) { - if (!device_component_generator.next()) return false; - current_path += device_component_generator.get(); - } - - // REMARK: Similarly to not counting other form of - // components, we do not count root components - // towards the amounts of components that the path - // has to simplify the code. - // To support the "special" root path on, for - // example, posix systems, we require a more - // complex branching logic that changes based on - // the path being absolute or not. - // - // We don't expect root to be a particularly - // useful path for QDoc purposes and expect to not - // have to consider it for our tests. - // If consideration for it become required, it is - // possible to test it directly in the affected - // systemss as a special case. - // - // If most systems are affected by the handling of - // a root path, then the model should be slightly - // changed to accommodate its generation. - if (is_absolute_path_distribution(random_engine)) { - if (!root_component_generator.next()) return false; - - current_path += root_component_generator.get(); - } - - std::size_t prefix_components_amount{std::max(std::size_t{1}, components_amount) - 1}; - while (prefix_components_amount > 0) { - if (!directory_component_generator.next()) return false; - if (!separator_component_generator.next()) return false; - - current_path += directory_component_generator.get() + separator_component_generator.get(); - --prefix_components_amount; - } - - if (is_directory_path_distribution(random_engine)) { - if (!directory_component_generator.next()) return false; - current_path += directory_component_generator.get(); - - if (has_trailing_separator(random_engine)) { - if (!separator_component_generator.next()) return false; - current_path += separator_component_generator.get(); - } - } else { - if (!filename_component_generator.next()) return false; - current_path += filename_component_generator.get(); - } - - return true; - } - - private: - Catch::Generators::GeneratorWrapper<QString> device_component_generator; - Catch::Generators::GeneratorWrapper<QString> root_component_generator; - Catch::Generators::GeneratorWrapper<QString> directory_component_generator; - Catch::Generators::GeneratorWrapper<QString> filename_component_generator; - Catch::Generators::GeneratorWrapper<QString> separator_component_generator; - - std::mt19937 random_engine; - std::uniform_int_distribution<std::size_t> components_amount_distribution; - std::bernoulli_distribution is_multi_device_distribution; - std::bernoulli_distribution is_absolute_path_distribution; - std::bernoulli_distribution is_directory_path_distribution; - std::bernoulli_distribution has_trailing_separator; - - QString current_path; - }; - - } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE - -/*! - * Returns a generator that produces QStrings that represent a - * path in a filesystem. - * - * A path is formed by the following components, loosely based - * on the abstraction that is used by std::filesystem::path: - * - * \list - * \li \b {device}: - * Represents the device on the filesystem that - * the path should be considered in terms of. - * This is an optional components that is sometimes - * present on multi-device systems, such as Windows, to - * distinguish which device the path refers to. - * When present, it always appears before any other - * component. - * \li \b {root}: - * A special sequence that marks the path as absolute. - * This is an optional component that is present, always, - * in absolute paths. - * \li \b {directory}: - * A component that represents a directory on the - * filesystem that the path "passes-trough". - * Zero or more of this components can be present in the - * path. - * A path pointing to a directory on the filesystem that - * is not \e {root} always ends with a component of this - * type. - * \li \b {filename}: - * A component that represents a file on the - * filesystem. - * When this component is present, it is present only once - * and always as the last component of the path. - * A path that has such a component is a path that points - * to a file on the filesystem. - * For some path formats, there is no difference in the - * format of a \e {filename} and a \e {directory}. - * \li \b {separator}: - * A component that is interleaved between other types of - * components to separate them so that they are - * recognizable. - * A path that points to a directory on the filesystem may - * sometimes have a \e {separator} at the end, after the - * ending \e {directory} component. - * \endlist - * - * Each component is representable as a string and a path is a - * concatenation of the string representation of some - * components, with the following rules: - * - * \list - * \li There is at most one \e {device} component. - * \li If a \e {device} component is present it always - * precedes all other components. - * \li There is at most one \e {root} component. - * \li If a \e {root} component is present it: - * \list - * \li Succeeds the \e {device} component if it is present. - * \li Precedes every other components if the \e {device} - * component is not present. - * \endlist - * \li There are zero or more \e {directory} component. - * \li There is at most one \e {filename} component. - * \li If a \e {filename} component is present it always - * succeeds all other components. - * \li Between any two successive \e {directory} components - * there is a \e {separator} component. - * \li Between each successive \e {directory} and \e - * {filename} component there is a \e {separator} component. - * \li If the last component is a \e {directory} component it - * can be optionally followed by a \e {separator} component. - * \li At least one component that is not a \e {device}, a \e - * {root} or \e {separator} component is present. - * \endlist - * - * For example, if "C:" is a \e {device} component, "\\" is a - * \e {root} component, "\\" is a \e {separator} component, - * "directory" is a \e {directory} component and "filename" is - * a \e {filename} component, the following are all paths: - * - * "C:\\directory", "C:\\directory\\directory", "C:filename", - * "directory\\directory\\", "\\directory\\filename", "filename". - * - * While the following aren't: - * - * "C:", "C:\\", "directory\\C:", "foo", "C:filename\\", - * "filename\\directory\\filename", "filename\\filename", - * "directorydirectory"." - * - * The format of different components type can be the same. - * For example, the \e {root} and \e {separator} component in - * the above example. - * For the purpose of generation, we do not care about the - * format itself and consider a component of a certain type - * depending only on how it is generated/where it is generated - * from. - * - * For example, if every component is formatted as the string - * "a", the string "aaa" could be a generated path. - * By the string alone, it is not possible to simply discern - * which components form it, but it would be possible to - * generate it if the first "a" is a \a {device} component, - * the second "a" is a \e {root} component and the third "a" - * is a \e {directory} or \e {filename} component. - * - * A path, is further said to have some properties, pairs of - * which are exclusive to each other. - * - * A path is said to be: - * - * \list - * \li \b {Multi-Device}: - * When it contains a \e {device} component. - * \li \b {Absolute}: - * When it contains a \e {root} component. - * If the path is \e {Absolute} it is not \e {Relative}. - * \li \b {Relative}: - * When it does not contain a \e {root} component. - * If the path is \e {Relative} it is not \e {Absolute}. - * \li \b {To a Directory}: - * When its last component is a \e {directory} component - * or a \e {directory} component followed by a \e - * {separator} component. - * If the path is \e {To a Directory} it is not \e {To a - * File}. - * \li \b {To a File}: - * When its last component is a \e {filename}. - * If the path is \e {To a File} it is not \e {To a - * Directory}. - * \endlist - * - * All path are \e {Relative/Absolute}, \e {To a - * Directory/To a File} and \e {Multi-Device} or not. - * - * Furthermore, a path that is \e {To a Directory} and whose - * last component is a \e {separator} component is said to \e - * {Have a Trailing Separator}. - */ - inline Catch::Generators::GeneratorWrapper<QString> path( - Catch::Generators::GeneratorWrapper<QString>&& device_generator, - Catch::Generators::GeneratorWrapper<QString>&& root_component_generator, - Catch::Generators::GeneratorWrapper<QString>&& directory_generator, - Catch::Generators::GeneratorWrapper<QString>&& filename_generator, - Catch::Generators::GeneratorWrapper<QString>&& separator_generator, - PathGeneratorConfiguration configuration = PathGeneratorConfiguration{} - ) { - return Catch::Generators::GeneratorWrapper<QString>( - std::unique_ptr<Catch::Generators::IGenerator<QString>>( - new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::PathGenerator(std::move(device_generator), std::move(root_component_generator), std::move(directory_generator), std::move(filename_generator), std::move(separator_generator), configuration) - ) - ); - } - - namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE { - - // REMARK: We need a bounded length for the generation of path - // components as strings. - // We trivially do not want components to be the empty string, - // such that we have a minimum length of 1, but the maximum - // length is more malleable. - // We don't want components that are too long to avoid - // incurring in a big performance overhead, as we may generate - // many of them. - // At the same time, we want some freedom in having diffent - // length components. - // The value that was chosen is based on the general value for - // POSIX's NAME_MAX, which seems to tend to be 14 on many systems. - // We see this value as a small enough but not too much value - // that further brings with itself a relation to paths, - // increasing our portability even if it is out of scope, as - // almost no modern respects NAME_MAX. - // We don't use POSIX's NAME_MAX directly as it may not be available - // on all systems. - inline static constexpr std::size_t minimum_component_length{1}; - inline static constexpr std::size_t maximum_component_length{14}; - - /*! - * Returns a generator that generates strings that are - * suitable to be used as a root component in POSIX paths. - * - * As per - * \l {https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_02}, - * this is any sequence of slash characters that is not of - * length 2. - */ - inline Catch::Generators::GeneratorWrapper<QString> posix_root() { - return uniformly_valued_oneof( - QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::move_into_vector( - string(character('/', '/'), 1, 1), - string(character('/', '/'), 3, maximum_component_length) - ), - std::vector{1, maximum_component_length - 3} - ); - } - - /*! - * Returns a generator that generates strings that are - * suitable to be used as directory components in POSIX paths - * and that use an alphabet that should generally be supported - * by other systems. - * - * Components of this kind use the \l - * {https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282}{Portable Filename Character Set}. - */ - inline Catch::Generators::GeneratorWrapper<QString> portable_posix_directory_name() { - return string( - QDOC_CATCH_GENERATORS_QCHAR_ALPHABETS_NAMESPACE::portable_posix_filename(), - minimum_component_length, maximum_component_length - ); - } - - /*! - * Returns a generator that generates strings that are - * suitable to be used as filenames in POSIX paths and that - * use an alphabet that should generally be supported by - * other systems. - * - * Filenames of this kind use the \l - * {https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282}{Portable Filename Character Set}. - */ - inline Catch::Generators::GeneratorWrapper<QString> portable_posix_filename() { - // REMARK: "." and ".." always represent directories so we - // avoid generating them. Other than this, there is no - // difference between a file name and a directory name. - return filter([](auto& filename) { return filename != "." && filename != ".."; }, portable_posix_directory_name()); - } - - /*! - * Returns a generator that generates strings that can be used - * as POSIX compliant separators. - * - * As per \l - * {https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_271}, - * a separator is a sequence of one or more slashes. - */ - inline Catch::Generators::GeneratorWrapper<QString> posix_separator() { - return string(character('/', '/'), minimum_component_length, maximum_component_length); - } - - /*! - * Returns a generator that generates strings that can be - * suitably used as logical drive names in Windows' paths. - * - * As per \l - * {https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats#traditional-dos-paths} - * and \l - * {https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getlogicaldrives}, - * they are composed of a single letter. - * Each generated string always follows the lettet with a - * colon, as it is specifically intended for path usages, - * where this is required. - * - * We use only uppercase letters for the drives names albeit, - * depending on case sensitivity, lowercase letter could be - * used. - */ - inline Catch::Generators::GeneratorWrapper<QString> windows_logical_drives() { - // REMARK: If a Windows path is generated on Windows - // itself, we expect that it may be used to interact with - // the filesystem, similar to how we expect a POSIX path - // to be used on Linux. - // For this reason, we only generate a specific drive, the one - // that contains the current working directory, so that we - // know it is an actually available drive and to contain the - // possible modifications to the filesystem to an easily - // foundable place. - -#if defined(Q_OS_WINDOWS) - - auto root_device{QStorageInfo{QDir()}.rootPath().first(1) + ":"}; - - return cycle(Catch::Generators::value(std::move(root_device))); - -#else - - return Catch::Generators::map( - [](QString letter){ return letter + ':';}, - string(QDOC_CATCH_GENERATORS_QCHAR_ALPHABETS_NAMESPACE::ascii_uppercase(), 1, 1) - ); - -#endif - } - - /*! - * Returns a generator that generate strings that can be used - * as separators in Windows based paths. - * - * As per \l - * {https://docs.microsoft.com/en-us/dotnet/api/system.io.path.directoryseparatorchar?view=net-6.0} - * and \l - * {https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats#canonicalize-separators}, - * this is a sequence of one or more backward or forward slashes. - */ - inline Catch::Generators::GeneratorWrapper<QString> windows_separator() { - return uniform_oneof( - QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::move_into_vector( - string(character('\\', '\\'), minimum_component_length, maximum_component_length), - string(character('/', '/'), minimum_component_length, maximum_component_length) - ) - ); - } - - } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE - - /*! - * Returns a generator that generates strings representing - * POSIX compatible paths. - * - * The generated paths follows the format specified in \l - * {https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_271}. - * - * The optional length-requirements, such as PATH_MAX and - * NAME_MAX, are relaxed away as they are generally not - * respected by modern systems. - * - * It is possible to set the probability of obtaining a - * relative or absolute path through \a - * absolute_path_probability and the one of obtaining a path - * potentially pointing ot a directory or on a file through \a - * directory_path_probability. - */ - inline Catch::Generators::GeneratorWrapper<QString> relaxed_portable_posix_path(double absolute_path_probability = 0.5, double directory_path_probability = 0.5) { - return path( - // POSIX path are never multi-device, so that we have - // provide an empty device component generator and set - // the probability for Multi-Device paths to zero. - string(character(), 0, 0), - QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::posix_root(), - QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::portable_posix_directory_name(), - QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::portable_posix_filename(), - QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::posix_separator(), - PathGeneratorConfiguration{} - .set_multi_device_path_probability(0.0) - .set_absolute_path_probability(absolute_path_probability) - .set_directory_path_probability(directory_path_probability) - ); - } - - /*! - * Returns a generator that produces strings that represents - * traditional DOS paths as defined in \l - * {https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats#traditional-dos-paths}. - * - * The directory and filename components of a path generated - * in this way are, currently, restricted to use a portable - * character set as defined by POSIX. - * - * Do note that most paths themselves, will not be portable, on - * the whole, albeit they may be valid paths on other systems, as - * Windows uses a path system that is generally incompatible with - * other systems. - * - * Some possibly valid special path, such as a "C:" or "\" - * will never be generated. - */ - inline Catch::Generators::GeneratorWrapper<QString> traditional_dos_path( - double absolute_path_probability = 0.5, - double directory_path_probability = 0.5, - double multi_device_path_probability = 0.5 - ) { - return path( - QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::windows_logical_drives(), - QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::windows_separator(), - // REMAKR: Windows treats trailing dots as if they were a - // component of their own, that is, as the special - // relative paths. - // This seems to not be correctly handled by Qt's - // filesystem methods, resulting in inconsistencies when - // one such path is encountered. - // To avoid the issue, considering that an equivalent path - // can be formed by actually having the dots on their own - // as a component, we filter out all those paths that have - // trailing dots but are not only composed of dots. - Catch::Generators::filter( - [](auto& path){ return !(path.endsWith(".") && path.contains(QRegularExpression("[^.]"))) ; }, - QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::portable_posix_directory_name() - ), - QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::portable_posix_filename(), - QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::windows_separator(), - PathGeneratorConfiguration{} - .set_multi_device_path_probability(multi_device_path_probability) - .set_absolute_path_probability(absolute_path_probability) - .set_directory_path_probability(directory_path_probability) - ); - } - - // TODO: Find a good way to test the following functions. - // native_path can probably be tied to the tests for the - // OS-specific functions, with TEMPLATE_TEST_CASE. - // The other ones may follow a similar pattern but require a bit - // more work so that they tie to a specific case instead of the - // general one. - // Nonetheless, this approach is both error prone and difficult to - // parse, because of the required if preprocessor directives, - // and should be avoided if possible. - - /*! - * Returns a generator that generates QStrings that represents - * paths native to the underlying OS. - * - * On Windows, paths that refer to a drive always refer to the - * root drive. - * - * native* functions should always be chosen when using paths for - * testing interfacing with the filesystem itself. - * - * System outside Linux, macOS or Windows are not supported. - */ - inline Catch::Generators::GeneratorWrapper<QString> native_path(double absolute_path_probability = 0.5, double directory_path_probability = 0.5) { -#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) - - return relaxed_portable_posix_path(absolute_path_probability, directory_path_probability); - -#elif defined(Q_OS_WINDOWS) - - // REMARK: When generating native paths for testing we - // generally want to avoid relative paths that are - // drive-specific, as we want them to be tied to a specific - // working directory that may not be the current directory on - // the drive. - // Hence, we avoid generating paths that may have a drive component. - // For tests where those kind of paths are interesting, a - // specific Windows-only test should be made, using - // traditional_dos_path to generate drive-relative paths only. - return traditional_dos_path(absolute_path_probability, directory_path_probability, 0.0); - -#endif - } - - /*! - * Returns a generator that generates QStrings that represents - * paths native to the underlying OS and that are always \e - * {Relative}. - * - * Avoids generating paths that refer to a directory that is not - * included in the path itself. - * - * System outside Linux, macOS or Windows are not supported. - */ - inline Catch::Generators::GeneratorWrapper<QString> native_relative_path(double directory_path_probability = 0.5) { - // REMARK: When testing, we generally use some specific - // directory as a root for relative paths. - // We want the generated path to be relative to that - // directory because we need a clean state for the test to - // be reliable. - // When generating paths, it is possible, correctly, to - // have a path that refers to that directory or some - // parent of it, removing us from the clean state that we - // need. - // To avoid that, we filter out paths that end up referring to a directory that is not under our "root" directory. - // - // We can think of each generated component moving us - // further down or up, in case of "..", a directory - // hierarchy, or keeping us at the same place in case of - // ".". - // Any path that ends up under our original "root" - // directory will safely keep our clean state for testing. - // - // Each "." keeps us at the same level in the hierarchy. - // Each ".." moves us up one level in the hierarchy. - // Each component that is not "." or ".." moves us down - // one level into the hierarchy. - // - // Then, to avoid referring to the "root" directory or one - // of its parents, we need to balance out each "." and - // ".." with the components that precedes or follow their - // appearance. - // - // Since "." keeps us at the same level, it can appear how - // many times it wants as long as the path referes to the - // "root" directory or a directory or file under it and at - // least one other component referes to a directory or - // file that is under the "root" directory. - // - // Since ".." moves us one level up in the hierarchy, a - // sequence of n ".." components is safe when at least n + - // 1 non "." or ".." components appear before it. - // - // To avoid the above problem, we filter away paths that - // do not respect those rules. - return Catch::Generators::filter( - [](auto& path){ - QStringList components{path.split(QRegularExpression{R"((\\|\/)+)"}, Qt::SkipEmptyParts)}; - int depth{0}; - - for (auto& component : components) { - if (component == "..") - --depth; - else if (component != ".") - ++depth; - - if (depth < 0) return false; - } - - return (depth > 0); - }, - native_path(0.0, directory_path_probability) - ); - } - - /*! - * Returns a generator that generates QStrings that represents - * paths native to the underlying OS and that are always \e - * {Relative} and \e {To a File}. - * - * System outside Linux, macOS or Windows are not supported. - */ - inline Catch::Generators::GeneratorWrapper<QString> native_relative_file_path() { - return native_relative_path(0.0); - } - - /*! - * Returns a generator that generates QStrings that represents - * paths native to the underlying OS and that are always \e - * {Relative} and \e {To a Directory}. - * - * System outside Linux, macOS or Windows are not supported. - */ - inline Catch::Generators::GeneratorWrapper<QString> native_relative_directory_path() { - return native_relative_path(1.0); - } - -} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE diff --git a/tests/auto/qdoc/catch_generators/src/generators/qchar_generator.h b/tests/auto/qdoc/catch_generators/src/generators/qchar_generator.h deleted file mode 100644 index 33efc5ea4..000000000 --- a/tests/auto/qdoc/catch_generators/src/generators/qchar_generator.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../namespaces.h" -#include "../utilities/semantics/move_into_vector.h" -#include "combinators/oneof_generator.h" - -#include <catch/catch.hpp> - -#include <random> - -#include <QChar> - -namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE { - namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE { - - class QCharGenerator : public Catch::Generators::IGenerator<QChar> { - public: - QCharGenerator( - char16_t lower_bound = std::numeric_limits<char16_t>::min(), - char16_t upper_bound = std::numeric_limits<char16_t>::max() - ) : random_engine{std::random_device{}()}, - distribution{static_cast<unsigned int>(lower_bound), static_cast<unsigned int>(upper_bound)} - { - assert(lower_bound <= upper_bound); - static_cast<void>(next()); - } - - QChar const& get() const override { return current_character; } - - bool next() override { - current_character = QChar(static_cast<char16_t>(distribution(random_engine))); - - return true; - } - - private: - QChar current_character; - - std::mt19937 random_engine; - std::uniform_int_distribution<unsigned int> distribution; - }; - - } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE - - - /*! - * Returns a generator of that generates elements of QChar whose - * ucs value is in the range [\a lower_bound, \a upper_bound]. - * - * When \a lower_bound = \a upper_bound, the generator infinitely - * generates the same character. - */ - inline Catch::Generators::GeneratorWrapper<QChar> character(char16_t lower_bound = std::numeric_limits<char16_t>::min(), char16_t upper_bound = std::numeric_limits<char16_t>::max()) { - return Catch::Generators::GeneratorWrapper<QChar>(std::unique_ptr<Catch::Generators::IGenerator<QChar>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::QCharGenerator(lower_bound, upper_bound))); - } - - - namespace QDOC_CATCH_GENERATORS_QCHAR_ALPHABETS_NAMESPACE { - - namespace QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE { - - enum class Alphabets : std::size_t {digit, ascii_lowercase, ascii_uppercase, ascii_alpha, ascii_alphanumeric, portable_posix_filename}; - - template<Alphabets alphabet> - struct sizeof_alphabet; - - template<Alphabets alphabet> - inline constexpr std::size_t sizeof_alphabet_v = sizeof_alphabet<alphabet>::value; - - template <> struct sizeof_alphabet<Alphabets::digit> { static constexpr std::size_t value{'9' - '0'}; }; - template <> struct sizeof_alphabet<Alphabets::ascii_lowercase> { static constexpr std::size_t value{'z' - 'a'}; }; - template<> struct sizeof_alphabet<Alphabets::ascii_uppercase> { static constexpr std::size_t value{'Z' - 'A'}; }; - template<> struct sizeof_alphabet<Alphabets::ascii_alpha> { static constexpr std::size_t value{sizeof_alphabet_v<Alphabets::ascii_lowercase> + sizeof_alphabet_v<Alphabets::ascii_uppercase>}; }; - template<> struct sizeof_alphabet<Alphabets::ascii_alphanumeric>{ static constexpr std::size_t value{sizeof_alphabet_v<Alphabets::ascii_alpha> + sizeof_alphabet_v<Alphabets::digit>}; }; - - } // end QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE - - - inline Catch::Generators::GeneratorWrapper<QChar> digit() { - return Catch::Generators::GeneratorWrapper<QChar>(std::unique_ptr<Catch::Generators::IGenerator<QChar>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::QCharGenerator('0', '9'))); - } - - inline Catch::Generators::GeneratorWrapper<QChar> ascii_lowercase() { - return Catch::Generators::GeneratorWrapper<QChar>(std::unique_ptr<Catch::Generators::IGenerator<QChar>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::QCharGenerator('a', 'z'))); - } - - inline Catch::Generators::GeneratorWrapper<QChar> ascii_uppercase() { - return Catch::Generators::GeneratorWrapper<QChar>(std::unique_ptr<Catch::Generators::IGenerator<QChar>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::QCharGenerator('A', 'Z'))); - } - - inline Catch::Generators::GeneratorWrapper<QChar> ascii_alpha() { - return uniform_oneof(QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::move_into_vector(ascii_lowercase(), ascii_uppercase())); - } - - inline Catch::Generators::GeneratorWrapper<QChar> ascii_alphanumeric() { - return uniformly_valued_oneof(QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::move_into_vector(ascii_alpha(), digit()), std::vector{traits::sizeof_alphabet_v<traits::Alphabets::ascii_alpha> , traits::sizeof_alphabet_v<traits::Alphabets::digit>}); - } - - inline Catch::Generators::GeneratorWrapper<QChar> portable_posix_filename() { - return uniformly_valued_oneof(QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::move_into_vector(ascii_alphanumeric(), character('.', '.'), character('-', '-'), character('_', '_')), - std::vector{traits::sizeof_alphabet_v<traits::Alphabets::ascii_alphanumeric>, std::size_t{1}, std::size_t{1}, std::size_t{1}}); - } - - } // end QDOC_CATCH_GENERATORS_QCHAR_ALPHABETS_NAMESPACE - - -} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE diff --git a/tests/auto/qdoc/catch_generators/src/generators/qstring_generator.h b/tests/auto/qdoc/catch_generators/src/generators/qstring_generator.h deleted file mode 100644 index fe854d22f..000000000 --- a/tests/auto/qdoc/catch_generators/src/generators/qstring_generator.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../namespaces.h" -#include "qchar_generator.h" -#include "../utilities/semantics/generator_handler.h" - -#include <catch/catch.hpp> - -#include <random> - -#include <QString> - -namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE { - namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE { - - class QStringGenerator : public Catch::Generators::IGenerator<QString> { - public: - QStringGenerator(Catch::Generators::GeneratorWrapper<QChar>&& character_generator, qsizetype minimum_length, qsizetype maximum_length) - : character_generator{QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(character_generator))}, - random_engine{std::random_device{}()}, - length_distribution{minimum_length, maximum_length}, - current_string{} - { - assert(minimum_length >= 0); - assert(maximum_length >= 0); - assert(minimum_length <= maximum_length); - - if (!next()) - Catch::throw_exception("Not enough values to initialize the first string"); - } - - QString const& get() const override { return current_string; } - - bool next() override { - qsizetype length{length_distribution(random_engine)}; - - current_string = QString(); - for (qsizetype length_index{0}; length_index < length; ++length_index) { - if (!character_generator.next()) return false; - - current_string += character_generator.get(); - } - - return true; - } - - private: - Catch::Generators::GeneratorWrapper<QChar> character_generator; - - std::mt19937 random_engine; - std::uniform_int_distribution<qsizetype> length_distribution; - - QString current_string; - }; - - } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE - - /*! - * Returns a generator that generates elements of QString from - * some amount of elements taken from \a character_generator. - * - * The generated strings will have a length in the range - * [\a minimum_length, \a maximum_length]. - * - * For compatibility with the Qt API, it is possible to provide - * negative bounds for the length. This is, nonetheless, - * considered an error such that the bounds should always be - * greater or equal to zero. - * - * It is similarly considered an error to have minimum_length <= - * maximum_length. - * - * The provided generator will generate elements until \a - * character_generator is exhausted. - */ - inline Catch::Generators::GeneratorWrapper<QString> string(Catch::Generators::GeneratorWrapper<QChar>&& character_generator, qsizetype minimum_length, qsizetype maximum_length) { - return Catch::Generators::GeneratorWrapper<QString>(std::unique_ptr<Catch::Generators::IGenerator<QString>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::QStringGenerator(std::move(character_generator), minimum_length, maximum_length))); - } - - /*! - * Returns an infinite generator whose elements are the empty - * QString. - */ - inline Catch::Generators::GeneratorWrapper<QString> empty_string() { - return Catch::Generators::GeneratorWrapper<QString>(std::unique_ptr<Catch::Generators::IGenerator<QString>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::QStringGenerator(character(), 0, 0))); - } - - -} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE diff --git a/tests/auto/qdoc/catch_generators/src/namespaces.h b/tests/auto/qdoc/catch_generators/src/namespaces.h deleted file mode 100644 index 7b954feb0..000000000 --- a/tests/auto/qdoc/catch_generators/src/namespaces.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#define QDOC_CATCH_GENERATORS_ROOT_NAMESPACE qdoc::catch_generators - -#define QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE details - -#define QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE traits - -#define QDOC_CATCH_GENERATORS_QCHAR_ALPHABETS_NAMESPACE alphabets - -#define QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE QDOC_CATCH_GENERATORS_ROOT_NAMESPACE::QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::utils diff --git a/tests/auto/qdoc/catch_generators/src/utilities/semantics/copy_value.h b/tests/auto/qdoc/catch_generators/src/utilities/semantics/copy_value.h deleted file mode 100644 index 57798be1a..000000000 --- a/tests/auto/qdoc/catch_generators/src/utilities/semantics/copy_value.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../../namespaces.h" - -#include <type_traits> - -namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE { - - /*! - * Forces \value to be copied in an expression context. - * - * This is used in contexts where inferences of a type that - * requires generality might identify a reference when ownership - * is required. - * - * Note that the compiler might optmize the copy away. This is a - * non-issue as we are only interested in breaking lifetime - * dependencies. - */ - template<typename T> - std::remove_reference_t<T> copy_value(T value) { return value; } - -} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE diff --git a/tests/auto/qdoc/catch_generators/src/utilities/semantics/generator_handler.h b/tests/auto/qdoc/catch_generators/src/utilities/semantics/generator_handler.h deleted file mode 100644 index 328627512..000000000 --- a/tests/auto/qdoc/catch_generators/src/utilities/semantics/generator_handler.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../../namespaces.h" - -#include <catch/catch.hpp> - -#include <optional> -#include <cassert> - -namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE { - - template<typename T> - class GeneratorHandler : public Catch::Generators::IGenerator<T> { - public: - - GeneratorHandler(Catch::Generators::GeneratorWrapper<T>&& generator) - : generator{std::move(generator)}, - first_call{true} - {} - - T const& get() const override { - assert(!first_call); - return generator.get(); - } - - bool next() override { - if (first_call) { - first_call = false; - return true; - } - - return generator.next(); - } - - private: - Catch::Generators::GeneratorWrapper<T> generator; - bool first_call; - }; - - - /*! - * Returns a generator wrapping \a generator that ensures that - * changes its semantics so that the first call to get should be - * preceded by a call to next. - * - * Catch generators require that is valid to call get and obtain a - * valid value on a generator that was just created. - * That is, generators should be non-empty and their first value - * should be initialized on construction. - * - * Normally, this is not a problem, and the next implementation of - * the generator can be simply called in the constructor. - * But when a generator depends on other generators, doing so will - * generally skip the first value that the generator - * produces, as the wrapping generator will need to advance the - * underlying generator, losing the value in the process. - * This is in particular, a problem, on generators that are finite - * or infinite and ordered. - * - * To solve the issue, the original value can be saved before - * advancing the generator or some code can be duplicated or - * abstracted so that what a new element can be generated without - * advancing the underlying generator. - * - * While this is acceptable, it can be error prone on more complex - * generators, generators that randomly access a collection of - * generators and so on. - * - * To simplify this process, this generator changes the semantics - * of the wrapped generator such that the first value of the - * generator is produced after the first call to next and the - * generator is considered in an invalid state before the first - * advancement. - * - * In this way, by wrapping all generators that a generator - * depends on, the implementation required for the first value is - * the same as the one required for all following values, with - * regards to the sequencing of next and get operations, - * simplifying the implementation of dependent generators. - * - * Do note that, while the generator returned by this function - * implments the generator interface that Catch2 requires, it - * cannot be normally used as a generator as it fails to comply - * with the first value semantics that a generator requires. - * Indeed, it should only be used as an intermediate wrapper for - * the implementation of generators that depends on other - * generators. - */ - template<typename T> - inline Catch::Generators::GeneratorWrapper<T> handler(Catch::Generators::GeneratorWrapper<T>&& generator) { - return Catch::Generators::GeneratorWrapper<T>(std::unique_ptr<Catch::Generators::IGenerator<T>>(new GeneratorHandler(std::move(generator)))); - } - -} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE diff --git a/tests/auto/qdoc/catch_generators/src/utilities/semantics/move_into_vector.h b/tests/auto/qdoc/catch_generators/src/utilities/semantics/move_into_vector.h deleted file mode 100644 index 5e780085b..000000000 --- a/tests/auto/qdoc/catch_generators/src/utilities/semantics/move_into_vector.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../../namespaces.h" - -#include <vector> -#include <tuple> - -namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE { - - namespace QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE { - - /*! - * Returns the type of the first element of Args. - * - * Args is expected to have at least one - */ - template<typename... Args> - using first_from_pack_t = std::tuple_element_t<0, std::tuple<Args...>>; - - } // end QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE - - - /*! - * Builds an std::vector by moving \a movables into it. - * - * \a movables must be made of homogenous types. - * - * This function is intended to allow the construction of an - * std::vector<T>, where T is a move only type, as an expression, - * to lighten the idiom. - * - * For example, Catch's GeneratorWrapper<T> adapts a - * std::unique_ptr, which is move only, making it impossible to - * build a std::vector from them in place. - * - * Then, everywhere this is needed, a more complex approach of - * generating the collection of objects, generating a vector of a - * suitable size and iterating the objects to move-emplace them in - * the vector is required. - * - * This not only complicates the code but is incompatible with a - * GENERATE expression, making it extremely hard, noisy and error - * prone to use them together. - * - * In those cases, then, a call to move_into_vector can be used as - * an expression to circumvent the problem. - */ - template<typename... MoveOnlyTypes> - inline auto move_into_vector(MoveOnlyTypes... movables) { - std::vector<QDOC_CATCH_GENERATORS_TRAITS_NAMESPACE::first_from_pack_t<MoveOnlyTypes...>> - moved_into_vector; - moved_into_vector.reserve(sizeof...(movables)); - - (moved_into_vector.emplace_back(std::move(movables)), ...); - - return moved_into_vector; - } - -} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE diff --git a/tests/auto/qdoc/catch_generators/src/utilities/statistics/distribution.h b/tests/auto/qdoc/catch_generators/src/utilities/statistics/distribution.h deleted file mode 100644 index 4374993bf..000000000 --- a/tests/auto/qdoc/catch_generators/src/utilities/statistics/distribution.h +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../../namespaces.h" - -#include <functional> -#include <optional> -#include <ostream> -#include <unordered_map> - -namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE { - - template<typename T> - using Histogram = std::unordered_map<T, std::size_t>; - - template<typename InputIt, typename GroupBy> - auto make_histogram(InputIt begin, InputIt end, GroupBy&& group_by) { - Histogram<std::invoke_result_t<GroupBy, decltype(*begin)>> histogram{}; - - while (begin != end) { - auto key{std::invoke(std::forward<GroupBy>(group_by), *begin)}; - - histogram.try_emplace(key, 0); - histogram[key] += 1; - ++begin; - } - - return histogram; - } - - template<typename T> - struct DistributionError { - T value; - double probability; - double expected_probability; - }; - - template<typename T> - inline std::ostream& operator<<(std::ostream& os, const DistributionError<T>& error) { - return os << "DistributionError{" << - "The value { " << error.value << - " } appear with a probability of { " << error.probability << - " } while a probability of { " << error.expected_probability << " } was expected." << - "}"; - } - - // REMARK: The following should really return an Either of unit/error - // but std::variant in C++ is both extremely unusable and comes with a - // strong overhead unless certain conditions are met. - // For this reason, we keep to the less intutitive optional error. - - /*! - * Returns true when the given \a sequence approximately respects a - * given distribution. - * - * The \a sequence respects a given distribution when the count of - * each collection of values is a percentage of the total values that - * is near the percentage probability described by distribution. - * - * The values in \a sequence are collected according to \a group_by. - * \a group_by, given an element of \a sequence, should return a value - * of some type that represent the category of the inspected value. - * Values that have the same category share their count. - * - * The distribution that should be respected is given by \a - * probability_of. \a probability_of is a function that takes a - * category that was produced from a call to \a group_by and returns - * the expect probability, in percentage, of apperance for that - * category. - * - * The given probability is then compared to the one found by counting - * the element of \a sequence under \a group_by, to ensure that it - * matches. - * - * The margin of error for the comparison is given, in percentage - * points, by \a margin. - * The approximation uses an absolute comparison and scales the - * margin inversely based on the size of \a sequence, to account for the - * precision of the data set itself. - * - * When the distribution is not respected, a DistributionError is - * returned enclosed in an optional value. - * The error allows reports which the first category for which the - * comparison failed, along with its expected probability and the one - * that was actually inferred from \a sequence. - */ - template<typename T, typename GroupBy, typename ProbabilityOf> - std::optional<DistributionError<T>> respects_distribution(std::vector<T>&& sequence, GroupBy&& group_by, ProbabilityOf&& probability_of, double margin = 33) { - std::size_t data_point_amount{sequence.size()}; - - // REMARK: We scale the margin based on the data set to allow for - // an easier change in downstream tests. - // The precision required for the approximation will vary - // depending on how many values we generate. - // The amount of values we generate depends on how much time we - // want the tests to take. - // This amount may change in the future. For example, as code is - // added and tests are added, we might need some expensive - // computations here and there. - // Sometimes, this will increase the test suite runtime without an - // obvious way of improving the performance of the underlying code - // to reduce it. - // In those cases, the total run time can be decreased by running - // less generations for battle-tested tests. - // If some code has not been changed for a long time, it will have - // had thousands of generations by that point, giving us a good - // degree of certainty of it not being bugged (for whatever bugs - // the tests account for). - // Then, running a certain amount of generation is not required - // anymore such that some of them can be optimized out. - // For tests like the one using this function, where our ability - // to test is always dependent on the amount of generations, - // changing the generated amount will mean that we will need to - // change our conditions too, potentially changing the meaning of - // the test. - // To take this into account, we perform a scaling on the - // condition itself, so that if the amount of data points that are - // generated changes, we do not generally have to change anything - // in the condition. - // - // For this case, we scale logarithmically_10 for the simple - // reason that we tend to generate values in power of tens, - // starting with the 100 values default that Quickcheck used. - // - // The default value for the margin on which the scaling is based, - // was chosen heuristically. - // As we expect generation under 10^3 to be generally meaningless - // for this kind of testing, the value was chosen so that it would - // start to normalize around that amount. - // Deviation of about 5-10% were identified trough various - // generations for an amount of data points near 1000, while a - // deviation of about 1-3% was identified with about 10000 values. - // With the chosen default value, the scaling approaches those - // percentage points with some margin of error. - // - // We expect up to a 10%, or a bit more, deviation to be suitable - // for our purposes, as it would still allow for a varied - // distribution in downstream consumers. - double scaled_margin{margin * (1.0/std::log10(data_point_amount))}; - - auto histogram{make_histogram(sequence.begin(), sequence.end(), std::forward<GroupBy>(group_by))}; - - for (auto& bin : histogram) { - auto [key, count] = bin; - - double actual_percentage{percent_of(static_cast<double>(count), static_cast<double>(data_point_amount))}; - double expected_percentage{std::invoke(std::forward<ProbabilityOf>(probability_of), key)}; - - if (!(actual_percentage == Approx(expected_percentage).margin(scaled_margin))) - return std::make_optional(DistributionError<T>{key, actual_percentage, expected_percentage}); - } - - return std::nullopt; - } - -} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE diff --git a/tests/auto/qdoc/catch_generators/src/utilities/statistics/percentages.h b/tests/auto/qdoc/catch_generators/src/utilities/statistics/percentages.h deleted file mode 100644 index 2d80a459f..000000000 --- a/tests/auto/qdoc/catch_generators/src/utilities/statistics/percentages.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../../namespaces.h" - -#include <cassert> - -namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE { - - /*! - * Returns the percentage of \amount over \a total. - * - * \a amount needs to be greater or equal to zero and \a total - * needs to be greater than zero. - */ - inline double percent_of(double amount, double total) { - assert(amount >= 0.0); - assert(total > 0.0); - - return (amount / total) * 100.0; - } - - /*! - * Given the cardinality of a set, returns the percentage - * probability that applied to every element of the set generates - * a uniform distribution. - */ - inline double uniform_probability(std::size_t cardinality) { - assert(cardinality > 0); - - return (100.0 / static_cast<double>(cardinality)); - } - - /*! - * Returns a percentage probability that is equal to \a - * probability. - * - * \a probability must be in the range [0.0, 1.0] - */ - inline double probability_to_percentage(double probability) { - assert(probability >= 0.0); - assert(probability <= 1.0); - - return probability * 100.0; - } - -} // end QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE diff --git a/tests/auto/qdoc/catch_generators/tests/CMakeLists.txt b/tests/auto/qdoc/catch_generators/tests/CMakeLists.txt deleted file mode 100644 index 76c366331..000000000 --- a/tests/auto/qdoc/catch_generators/tests/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause - -qt_internal_add_test(tst_QDoc_Catch_Generators - SOURCES - main.cpp - generators/catch_qchar_generator.cpp - generators/catch_qstring_generator.cpp - generators/catch_k_partition_of_r_generator.cpp - generators/catch_path_generator.cpp - - generators/combinators/catch_oneof_generator.cpp - generators/combinators/catch_cycle_generator.cpp - - utilities/semantics/catch_generator_handler.cpp - INCLUDE_DIRECTORIES - ../src - LIBRARIES - Qt::QDocCatchPrivate - Qt::QDocCatchConversionsPrivate -) diff --git a/tests/auto/qdoc/catch_generators/tests/generators/catch_k_partition_of_r_generator.cpp b/tests/auto/qdoc/catch_generators/tests/generators/catch_k_partition_of_r_generator.cpp deleted file mode 100644 index 9055c5411..000000000 --- a/tests/auto/qdoc/catch_generators/tests/generators/catch_k_partition_of_r_generator.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "namespaces.h" -#include "generators/k_partition_of_r_generator.h" - -#include <catch/catch.hpp> - -#include <numeric> - -using namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE; - -SCENARIO("Generating a k-partition of a real number", "[Partition][Reals]") { - GIVEN("A real number r greater or equal to zero") { - double r = GENERATE(take(10, random(0.0, 1000000.0))); - - AND_GIVEN("An amount of desired elements k greater than zero") { - std::size_t k = GENERATE(take(10, random(1, 100))); - - WHEN("A k-partition of r is generated") { - auto k_partition = GENERATE_COPY(take(10, k_partition_of_r(r, k))); - - THEN("The partition contains k elements") { - REQUIRE(k_partition.size() == k); - - AND_THEN("The sum of those elements is r") { - REQUIRE(std::accumulate(k_partition.begin(), k_partition.end(), 0.0) == Approx(r)); - } - } - } - } - } -} - -TEST_CASE("All 1-partition of r are singleton collection with r as their element", "[Partition][Reals][SpecialCase]") { - double r = GENERATE(take(10, random(0.0, 1000000.0))); - auto k_partition = GENERATE_COPY(take(10, k_partition_of_r(r, 1))); - - REQUIRE(k_partition.size() == 1); - REQUIRE(k_partition.front() == r); -} diff --git a/tests/auto/qdoc/catch_generators/tests/generators/catch_path_generator.cpp b/tests/auto/qdoc/catch_generators/tests/generators/catch_path_generator.cpp deleted file mode 100644 index 968008a56..000000000 --- a/tests/auto/qdoc/catch_generators/tests/generators/catch_path_generator.cpp +++ /dev/null @@ -1,755 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "namespaces.h" -#include "generators/qchar_generator.h" -#include "generators/qstring_generator.h" -#include "generators/path_generator.h" -#include "generators/combinators/cycle_generator.h" -#include "utilities/statistics/percentages.h" -#include "utilities/statistics/distribution.h" -#include "utilities/semantics/copy_value.h" - -#include <catch_conversions/qt_catch_conversions.h> - -#include <catch/catch.hpp> - -#include <QString> -#include <QStringList> -#include <QRegularExpression> - -using namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE; -using namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE; - -using namespace Qt::StringLiterals; - -TEST_CASE("A path generated with a multi_device_path_probability of 1.0 always contains a device component.", "[Path][Content][SpecialCase]") { - QString device_component_value{"C:"}; - auto path_generator = path( - Catch::Generators::value(copy_value(device_component_value)), - empty_string(), - empty_string(), - empty_string(), - empty_string(), - PathGeneratorConfiguration{}.set_multi_device_path_probability(1.0) - ); - - auto generated_path = GENERATE_REF(take(100, std::move(path_generator))); - - REQUIRE(generated_path.contains(device_component_value)); -} - -TEST_CASE("A path generated with a multi_device_path_probability of 0.0 never contains a device component.", "[Path][Content][SpecialCase]") { - QString device_component_value{"C:"}; - auto path_generator = path( - Catch::Generators::value(copy_value(device_component_value)), - empty_string(), - empty_string(), - empty_string(), - empty_string(), - PathGeneratorConfiguration{}.set_multi_device_path_probability(0.0) - ); - - auto generated_path = GENERATE_REF(take(100, std::move(path_generator))); - - REQUIRE(!generated_path.contains(device_component_value)); -} - -TEST_CASE("A path generated with an absolute_path_probability of 1.0 always contains a root component.", "[Path][Content][SpecialCase]") { - QString root_component_value{"\\"}; - auto path_generator = path( - empty_string(), - Catch::Generators::value(copy_value(root_component_value)), - empty_string(), - empty_string(), - empty_string(), - PathGeneratorConfiguration{}.set_absolute_path_probability(1.0) - ); - - auto generated_path = GENERATE_REF(take(100, std::move(path_generator))); - - REQUIRE(generated_path.contains(root_component_value)); -} - -TEST_CASE("A path generated with an absolute_path_probability of 0.0 never contains a root component.", "[Path][Content][SpecialCase]") { - QString root_component_value{"\\"}; - auto path_generator = path( - empty_string(), - Catch::Generators::value(copy_value(root_component_value)), - empty_string(), - empty_string(), - empty_string(), - PathGeneratorConfiguration{}.set_absolute_path_probability(0.0) - ); - - auto generated_path = GENERATE_REF(take(100, std::move(path_generator))); - - REQUIRE(!generated_path.contains(root_component_value)); -} - -TEST_CASE("A path generated with a directory_path_probability of 1.0 always ends with a root, directory or directory followed by separator component.", "[Path][Content][SpecialCase]") { - QString root_component_value{"root"}; - QString directory_component_value{"dir"}; - QString separator_component_value{"sep"}; - - auto path_generator = path( - cycle(Catch::Generators::value(QString("device"))), - cycle(Catch::Generators::value(copy_value(root_component_value))), - cycle(Catch::Generators::value(copy_value(directory_component_value))), - cycle(Catch::Generators::value(QString("filename"))), - cycle(Catch::Generators::value(copy_value(separator_component_value))), - PathGeneratorConfiguration{}.set_directory_path_probability(1.0) - ); - - auto generated_path = GENERATE_REF(take(100, std::move(path_generator))); - - REQUIRE(( - generated_path.endsWith(root_component_value) || - generated_path.endsWith(directory_component_value) || - generated_path.endsWith(directory_component_value + separator_component_value) - )); -} - -TEST_CASE("A path generated with a directory_path_probability of 0.0 always ends with a filename component.", "[Path][Content][SpecialCase]") { - QString filename_component_value{"file"}; - - auto path_generator = path( - cycle(Catch::Generators::value(QString("device"))), - cycle(Catch::Generators::value(QString("root"))), - cycle(Catch::Generators::value(QString("dir"))), - cycle(Catch::Generators::value(copy_value(filename_component_value))), - cycle(Catch::Generators::value(QString("sep"))), - PathGeneratorConfiguration{}.set_directory_path_probability(0.0) - ); - - auto generated_path = GENERATE_REF(take(100, std::move(path_generator))); - - REQUIRE(generated_path.endsWith(filename_component_value)); -} - -TEST_CASE("A directory path generated with a has_trailing_separator_probability of 1.0 always ends with a separator component.", "[Path][Content][SpecialCase]") { - QString separator_component_value{"sep"}; - - auto path_generator = path( - cycle(Catch::Generators::value(QString("device"))), - cycle(Catch::Generators::value(QString("root"))), - cycle(Catch::Generators::value(QString("directory"))), - cycle(Catch::Generators::value(QString("filename"))), - cycle(Catch::Generators::value(copy_value(separator_component_value))), - PathGeneratorConfiguration{}.set_directory_path_probability(1.0).set_has_trailing_separator_probability(1.0) - ); - - auto generated_path = GENERATE_REF(take(100, std::move(path_generator))); - - REQUIRE(generated_path.endsWith(separator_component_value)); -} - -TEST_CASE("A directory path generated with a has_trailing_separator_probability of 0.0 never ends with a separator component.", "[Path][Content][SpecialCase]") { - QString separator_component_value{"sep"}; - - auto path_generator = path( - cycle(Catch::Generators::value(QString("device"))), - cycle(Catch::Generators::value(QString("root"))), - cycle(Catch::Generators::value(QString("directory"))), - cycle(Catch::Generators::value(QString("filename"))), - cycle(Catch::Generators::value(copy_value(separator_component_value))), - PathGeneratorConfiguration{}.set_directory_path_probability(1.0).set_has_trailing_separator_probability(0.0) - ); - - auto generated_path = GENERATE_REF(take(100, std::move(path_generator))); - - REQUIRE(!generated_path.endsWith(separator_component_value)); -} - -SCENARIO("Binding a path to a component range", "[Path][Bounds]") { - GIVEN("A minimum amount of components") { - auto minimum_components_amount = GENERATE(take(100, random(std::size_t{1}, std::size_t{100}))); - - AND_GIVEN("A maximum amount of components that is greater or equal than the minimum amount of components") { - auto maximum_components_amount = GENERATE_COPY(take(100, random(minimum_components_amount, std::size_t{100}))); - - WHEN("A path is generated from those bounds") { - QString countable_component_value{"a"}; - - QString generated_path = GENERATE_COPY( - take(1, - path( - empty_string(), - empty_string(), - cycle(Catch::Generators::value(copy_value(countable_component_value))), - cycle(Catch::Generators::value(copy_value(countable_component_value))), - empty_string(), - PathGeneratorConfiguration{}.set_minimum_components_amount(minimum_components_amount).set_maximum_components_amount(maximum_components_amount) - ) - ) - ); - - THEN("The amount of non device, non root, non separator components in the generated path is in the range [minimum_components_amount, maximum_components_amount]") { - std::size_t components_amount{static_cast<std::size_t>(generated_path.count(countable_component_value))}; - - REQUIRE(components_amount >= minimum_components_amount); - REQUIRE(components_amount <= maximum_components_amount); - } - } - } - } -} - -TEST_CASE( - "When the maximum amount of components and the minimum amount of components are equal, all generated paths have the same amount of non device, non root, non separator components", - "[Path][Bounds][SpecialCase]") -{ - auto components_amount = GENERATE(take(10, random(std::size_t{1}, std::size_t{100}))); - - QString countable_component_value{"a"}; - QString generated_path = GENERATE_COPY( - take(10, - path( - empty_string(), - empty_string(), - cycle(Catch::Generators::value(copy_value(countable_component_value))), - cycle(Catch::Generators::value(copy_value(countable_component_value))), - empty_string(), - PathGeneratorConfiguration{}.set_minimum_components_amount(components_amount).set_maximum_components_amount(components_amount) - ) - ) - ); - - REQUIRE(static_cast<std::size_t>(generated_path.count(countable_component_value)) == components_amount); -} - -SCENARIO("The format of a path", "[Path][Contents]") { - GIVEN("A series of components generators") { - // TODO: Could probably move this to the global scope to - // lighen the tests. - QString device_component_value{"device"}; - QString root_component_value{"root"}; - QString directory_component_value{"dir"}; - QString filename_component_value{"file"}; - QString separator_component_value{"sep"}; - - auto device_component_generator = cycle(Catch::Generators::value(copy_value(device_component_value))); - auto root_component_generator = cycle(Catch::Generators::value(copy_value(root_component_value))); - auto directory_component_generator = cycle(Catch::Generators::value(copy_value(directory_component_value))); - auto filename_component_generator = cycle(Catch::Generators::value(copy_value(filename_component_value))); - auto separator_component_generator = cycle(Catch::Generators::value(copy_value(separator_component_value))); - - AND_GIVEN("A generator of paths using those components generator") { - // TODO: We should actually randomize the configuration by - // making a simple generator for it. - auto path_generator = path( - std::move(device_component_generator), - std::move(root_component_generator), - std::move(directory_component_generator), - std::move(filename_component_generator), - std::move(separator_component_generator) - ); - - WHEN("A path is generated from that generator") { - auto generated_path = GENERATE_REF(take(10, std::move(path_generator))); - - THEN("At most one device component is in the generated path") { - REQUIRE(generated_path.count(device_component_value) <= 1); - } - - THEN("At most one root component is in the generated path") { - REQUIRE(generated_path.count(root_component_value) <= 1); - } - - THEN("At most one filename component is in the generated path") { - REQUIRE(generated_path.count(filename_component_value) <= 1); - } - - THEN("At least one non device, non root, non separator component is in the generated path") { - REQUIRE((generated_path.contains(directory_component_value) || generated_path.contains(filename_component_value))); - } - - THEN("There is a separator component between any two successive directory components") { - // REMARK: To test this condition, which is not - // easy to test directly, as, if the generator is - // working as it should, the concept of successive - // directories stops existing. - // To test it, then, we split the condition into - // two parts, that are easier to test, that - // achieve the same effect. - // First, if all directories have a separator - // component between them, it is impossible to - // have a directory component that is directly - // followed by another directory component. - // Second, when this holds, any two directory - // components must have one or more non-directory - // components between them. - // For those directories that have exactly one - // component between them, it must be a separator. - // This is equivalent to the original condition as - // long as it is not allowed for anything else to - // be between two directory components that have - // exactly one component between them. - // This is true at the time of writing of this - // test, such that this will work correctly, but - // if this changes the test is invalidated. - // If a test for the original condition is found - // that is not contrived (as it is possible to - // test the original condition but it is a bit - // more complex than we would like the test to - // be), it should replace this current - // implementation to improve the resiliency of the - // test. - REQUIRE_FALSE(generated_path.contains(directory_component_value + directory_component_value)); - - auto successive_directories_re{ - QRegularExpression(u"%1(%2)%3"_s.arg(directory_component_value) - .arg(QStringList{device_component_value, root_component_value, filename_component_value, separator_component_value}.join("|")) - .arg(directory_component_value) - )}; - - auto successive_directories_match(successive_directories_re.match(generated_path)); - while (successive_directories_match.hasMatch()) { - auto in_between_component{successive_directories_match.captured(1)}; - - // TODO: Having this in a loop makes it so - // the amount of assertions will vary slightly - // per-run. - // It would be better to avoid this, even if - // it should not really be a problem - // generally. - // Try to find a better way to express this - // condition that does not require a loop. - // This could be as easy as just collection - // the results and then using a std::all_of. - REQUIRE(in_between_component == separator_component_value); - - successive_directories_match = successive_directories_re.match(generated_path, successive_directories_match.capturedEnd(1)); - } - } - - - THEN("There is a separator component between each successive directory and filename components") { - REQUIRE_FALSE(generated_path.contains(directory_component_value + filename_component_value)); - - auto successive_directory_filename_re{ - QRegularExpression(u"%1(%2)%3"_s.arg(directory_component_value) - .arg(QStringList{device_component_value, root_component_value, filename_component_value, separator_component_value}.join("|")) - .arg(filename_component_value) - )}; - - auto successive_directory_filename_match(successive_directory_filename_re.match(generated_path)); - while (successive_directory_filename_match.hasMatch()) { - auto in_between_component{successive_directory_filename_match.captured(1)}; - - REQUIRE(in_between_component == separator_component_value); - - successive_directory_filename_match = successive_directory_filename_re.match(generated_path, successive_directory_filename_match.capturedEnd(1)); - } - } - } - } - - AND_GIVEN("A generator of paths using those components generator that generates Multi-Device paths") { - auto path_generator = path( - std::move(device_component_generator), - std::move(root_component_generator), - std::move(directory_component_generator), - std::move(filename_component_generator), - std::move(separator_component_generator), - PathGeneratorConfiguration{}.set_multi_device_path_probability(1.0) - ); - - WHEN("A path is generated from that generator") { - auto generated_path = GENERATE_REF(take(10, std::move(path_generator))); - - THEN("Exactly one device component is in the generated path") { - REQUIRE(generated_path.count(device_component_value) == 1); - - AND_THEN("The device component is the first component in the generated path") { - REQUIRE(generated_path.startsWith(device_component_value)); - } - } - } - } - - AND_GIVEN("A generator of paths using those components generator that generates Absolute paths") { - auto path_generator = path( - std::move(device_component_generator), - std::move(root_component_generator), - std::move(directory_component_generator), - std::move(filename_component_generator), - std::move(separator_component_generator), - PathGeneratorConfiguration{}.set_absolute_path_probability(1.0) - ); - - WHEN("A path is generated from that generator") { - auto generated_path = GENERATE_REF(take(10, std::move(path_generator))); - - THEN("Exactly one root component is in the generated path") { - REQUIRE(generated_path.count(root_component_value) == 1); - } - } - } - - AND_GIVEN("A generator of paths using those components generator that generates Absolute paths that are not Multi-Device") { - auto path_generator = path( - std::move(device_component_generator), - std::move(root_component_generator), - std::move(directory_component_generator), - std::move(filename_component_generator), - std::move(separator_component_generator), - PathGeneratorConfiguration{}.set_multi_device_path_probability(0.0).set_absolute_path_probability(1.0) - ); - - WHEN("A path is generated from that generator") { - auto generated_path = GENERATE_REF(take(10, std::move(path_generator))); - - THEN("The root component is the first component in the generated path") { - REQUIRE(generated_path.startsWith(root_component_value)); - } - } - } - - AND_GIVEN("A generator of paths using those components generator that generates Multi-Device, Absolute paths") { - auto path_generator = path( - std::move(device_component_generator), - std::move(root_component_generator), - std::move(directory_component_generator), - std::move(filename_component_generator), - std::move(separator_component_generator), - PathGeneratorConfiguration{}.set_multi_device_path_probability(1.0).set_absolute_path_probability(1.0) - ); - - WHEN("A path is generated from that generator") { - auto generated_path = GENERATE_REF(take(10, std::move(path_generator))); - - THEN("The root component succeeds the device component in the generated path") { - REQUIRE(generated_path.contains(device_component_value + root_component_value)); - } - } - } - - AND_GIVEN("A generator of paths using those components generator that generates paths that are To a Directory and do not Have a Trailing Separator") { - auto path_generator = path( - std::move(device_component_generator), - std::move(root_component_generator), - std::move(directory_component_generator), - std::move(filename_component_generator), - std::move(separator_component_generator), - PathGeneratorConfiguration{}.set_directory_path_probability(1.0).set_has_trailing_separator_probability(0.0) - ); - - WHEN("A path is generated from that generator") { - auto generated_path = GENERATE_REF(take(10, std::move(path_generator))); - - THEN("The last component of in the path is a directory component") { - REQUIRE(generated_path.endsWith(directory_component_value)); - } - } - } - - AND_GIVEN("A generator of paths using those components generator that generates paths that are To a Directory and Have a Trailing Separator") { - auto path_generator = path( - std::move(device_component_generator), - std::move(root_component_generator), - std::move(directory_component_generator), - std::move(filename_component_generator), - std::move(separator_component_generator), - PathGeneratorConfiguration{}.set_directory_path_probability(1.0).set_has_trailing_separator_probability(1.0) - ); - - WHEN("A path is generated from that generator") { - auto generated_path = GENERATE_REF(take(10, std::move(path_generator))); - - THEN("The last component in the path is a separator component that is preceded by a directory component") { - REQUIRE(generated_path.endsWith(directory_component_value + separator_component_value)); - } - } - } - - - AND_GIVEN("A generator of paths using those components generator that generates paths that are To a File") { - auto path_generator = path( - std::move(device_component_generator), - std::move(root_component_generator), - std::move(directory_component_generator), - std::move(filename_component_generator), - std::move(separator_component_generator), - PathGeneratorConfiguration{}.set_directory_path_probability(0.0) - ); - - WHEN("A path is generated from that generator") { - auto generated_path = GENERATE_REF(take(10, std::move(path_generator))); - - THEN("Exactly one filename component is in the path") { - REQUIRE(generated_path.contains(filename_component_value)); - - AND_THEN("The filename component is the last component in the path") { - REQUIRE(generated_path.endsWith(filename_component_value)); - } - } - } - } - } -} - -// REMARK: [mayfail][distribution] -SCENARIO("Observing the distribution of paths based on their configuration", "[Path][Statistics][!mayfail]") { - GIVEN("A series of components generators") { - QString device_component_value{"device"}; - QString root_component_value{"root"}; - QString directory_component_value{"dir"}; - QString filename_component_value{"file"}; - QString separator_component_value{"sep"}; - - auto device_component_generator = cycle(Catch::Generators::value(copy_value(device_component_value))); - auto root_component_generator = cycle(Catch::Generators::value(copy_value(root_component_value))); - auto directory_component_generator = cycle(Catch::Generators::value(copy_value(directory_component_value))); - auto filename_component_generator = cycle(Catch::Generators::value(copy_value(filename_component_value))); - auto separator_component_generator = cycle(Catch::Generators::value(copy_value(separator_component_value))); - - AND_GIVEN("A generator of paths using those components generator that produces paths that are Multi-Device with a probability of n") { - double multi_device_path_probability = GENERATE(take(10, random(0.0, 1.0))); - - auto path_generator = path( - std::move(device_component_generator), - std::move(root_component_generator), - std::move(directory_component_generator), - std::move(filename_component_generator), - std::move(separator_component_generator), - PathGeneratorConfiguration{}.set_multi_device_path_probability(multi_device_path_probability) - ); - - WHEN("A certain amount of paths are generated from that generator") { - auto paths = GENERATE_REF(take(1, chunk(10000, std::move(path_generator)))); - - THEN("The amount of paths that are Multi-Device approximately respects the given probability and the amount of paths that are not approximately respects a probability of 1 - n") { - auto maybe_distribution_error{respects_distribution( - std::move(paths), - [&device_component_value](const QString& path){ return (path.startsWith(device_component_value)) ? "Multi-Device" : "Non Multi-Device"; }, - [multi_device_path_probability](const QString& key){ return probability_to_percentage((key == "Multi-Device") ? multi_device_path_probability : 1 - multi_device_path_probability); } - )}; - - REQUIRE_FALSE(maybe_distribution_error); - } - } - } - - AND_GIVEN("A generator of paths using those components generator that produces paths that are Absolute with a probability of n") { - double absolute_path_probability = GENERATE(take(10, random(0.0, 1.0))); - - auto path_generator = path( - std::move(device_component_generator), - std::move(root_component_generator), - std::move(directory_component_generator), - std::move(filename_component_generator), - std::move(separator_component_generator), - PathGeneratorConfiguration{}.set_absolute_path_probability(absolute_path_probability) - ); - - WHEN("A certain amount of paths are generated from that generator") { - auto paths = GENERATE_REF(take(1, chunk(10000, std::move(path_generator)))); - - THEN("The amount of paths that are Absolute approximately respects the given probability and the amount of paths that are Relative approximately respects a probability of 1 - n") { - auto maybe_distribution_error{respects_distribution( - std::move(paths), - [&root_component_value](const QString& path){ return (path.contains(root_component_value)) ? "Absolute" : "Relative"; }, - [absolute_path_probability](const QString& key){ return probability_to_percentage((key == "Absolute") ? absolute_path_probability : 1 - absolute_path_probability); } - )}; - - REQUIRE_FALSE(maybe_distribution_error); - } - } - } - - AND_GIVEN("A generator of paths using those components generator that produces paths that are To a Directory with a probability of n") { - double directory_path_probability = GENERATE(take(10, random(0.0, 1.0))); - - auto path_generator = path( - std::move(device_component_generator), - std::move(root_component_generator), - std::move(directory_component_generator), - std::move(filename_component_generator), - std::move(separator_component_generator), - PathGeneratorConfiguration{}.set_directory_path_probability(directory_path_probability) - ); - - WHEN("A certain amount of paths are generated from that generator") { - auto paths = GENERATE_REF(take(1, chunk(10000, std::move(path_generator)))); - - THEN("The amount of paths that are To a Directory approximately respects the given probability and the amount of paths that are To a File approximately respects a probability of 1 - n") { - auto maybe_distribution_error{respects_distribution( - std::move(paths), - [&filename_component_value](const QString& path){ return (path.contains(filename_component_value)) ? "To a File" : "To a Directory"; }, - [directory_path_probability](const QString& key){ return probability_to_percentage((key == "To a Directory") ? directory_path_probability : 1 - directory_path_probability); } - )}; - - REQUIRE_FALSE(maybe_distribution_error); - } - } - } - - AND_GIVEN("A generator of paths using those components generator that produces paths that are To a Directory with a probability of n to Have a Trailing Separator") { - double has_trailing_separator_probability = GENERATE(take(10, random(0.0, 1.0))); - - auto path_generator = path( - std::move(device_component_generator), - std::move(root_component_generator), - std::move(directory_component_generator), - std::move(filename_component_generator), - std::move(separator_component_generator), - PathGeneratorConfiguration{}.set_directory_path_probability(1.0).set_has_trailing_separator_probability(has_trailing_separator_probability) - ); - - WHEN("A certain amount of paths are generated from that generator") { - auto paths = GENERATE_REF(take(1, chunk(10000, std::move(path_generator)))); - - THEN("The amount of paths that are Have a Trailing Separator approximately respects the given probability and the amount of paths that do not Have a Trailing Separator approximately respects a probability of 1 - n") { - auto maybe_distribution_error{respects_distribution( - std::move(paths), - [&separator_component_value](const QString& path){ return (path.endsWith(separator_component_value)) ? "Have a Trailing Separator" : "Doesn't Have a Trailing Separator"; }, - [has_trailing_separator_probability](const QString& key){ return probability_to_percentage((key == "Have a Trailing Separator") ? has_trailing_separator_probability : 1 - has_trailing_separator_probability); } - )}; - - REQUIRE_FALSE(maybe_distribution_error); - } - } - } - } -} - -TEST_CASE("The first component of the passed in device components generator is not lost", "[Path][GeneratorFirstElement][SpecialCase]") { - QString device_component_generator_first_value{"device"}; - - auto generated_path = GENERATE_COPY(take(1, - path( - values({device_component_generator_first_value, QString{""}}), - empty_string(), - empty_string(), - empty_string(), - empty_string(), - PathGeneratorConfiguration{} - .set_multi_device_path_probability(1.0) - .set_minimum_components_amount(1) - .set_maximum_components_amount(1) - ) - )); - - REQUIRE(generated_path.contains(device_component_generator_first_value)); -} - -TEST_CASE("The first component of the passed in root components generator is not lost", "[Path][GeneratorFirstElement][SpecialCase]") { - QString root_component_generator_first_value{"root"}; - - auto generated_path = GENERATE_COPY(take(1, - path( - empty_string(), - values({root_component_generator_first_value, QString{""}}), - empty_string(), - empty_string(), - empty_string(), - PathGeneratorConfiguration{} - .set_absolute_path_probability(1.0) - .set_minimum_components_amount(1) - .set_maximum_components_amount(1) - ) - )); - - REQUIRE(generated_path.contains(root_component_generator_first_value)); -} - -TEST_CASE("The first component of the passed in directory components generator is not lost", "[Path][GeneratorFirstElement][SpecialCase]") { - QString directory_component_generator_first_value{"dir"}; - - auto generated_path = GENERATE_COPY(take(1, - path( - empty_string(), - empty_string(), - values({directory_component_generator_first_value, QString{""}}), - empty_string(), - empty_string(), - PathGeneratorConfiguration{} - .set_directory_path_probability(1.0) - .set_minimum_components_amount(1) - .set_maximum_components_amount(1) - ) - )); - - REQUIRE(generated_path.contains(directory_component_generator_first_value)); -} - -TEST_CASE("The first component of the passed in filename components generator is not lost", "[Path][GeneratorFirstElement][SpecialCase]") { - QString filename_component_generator_first_value{"dir"}; - - auto generated_path = GENERATE_COPY(take(1, - path( - empty_string(), - empty_string(), - empty_string(), - values({filename_component_generator_first_value, QString{""}}), - empty_string(), - PathGeneratorConfiguration{} - .set_directory_path_probability(0.0) - .set_minimum_components_amount(1) - .set_maximum_components_amount(1) - ) - )); - - REQUIRE(generated_path.contains(filename_component_generator_first_value)); -} - -TEST_CASE("The first component of the passed in separator components generator is not lost", "[Path][GeneratorFirstElement][SpecialCase]") { - QString separator_component_generator_first_value{"sep"}; - - auto generated_path = GENERATE_COPY(take(1, - path( - empty_string(), - empty_string(), - empty_string(), - empty_string(), - values({separator_component_generator_first_value, QString{""}}), - PathGeneratorConfiguration{} - .set_directory_path_probability(0.0) - .set_minimum_components_amount(2) - .set_maximum_components_amount(2) - ) - )); - - REQUIRE(generated_path.contains(separator_component_generator_first_value)); -} - -SCENARIO("Generating paths that are suitable to be used on POSIX systems", "[Path][POSIX][Content]") { - GIVEN("A generator that generates Strings representing paths on a POSIX system that are portable") { - auto path_generator = relaxed_portable_posix_path(); - - WHEN("A path is generated from it") { - auto generated_path = GENERATE_REF(take(100, std::move(path_generator))); - - THEN("The path is composed only by one or more characters in the class [-_./a-zA-Z0-9]") { - REQUIRE(QRegularExpression{R"(\A[-_.\/a-zA-Z0-9]+\z)"}.match(generated_path).hasMatch()); - } - } - } -} - -SCENARIO("Generating paths that are suitable to be used on Windows", "[Path][Windows][Content]") { - GIVEN("A generator that generates Strings representing paths on a Windows system") { - auto path_generator = traditional_dos_path(); - - WHEN("A path is generated from it") { - auto generated_path = GENERATE_REF(take(100, std::move(path_generator))); - - CAPTURE(generated_path); - - THEN("The path starts with an uppercase letter followed by a colon, a backward or forward slash or a character in the class [-_.a-zA-Z0-9]") { - QRegularExpression beginning_re{"([A-Z]:|\\|\\/|[-_.a-zA-Z0-9])"}; - - auto beginning_match{beginning_re.match(generated_path)}; - - REQUIRE(beginning_match.hasMatch()); - - generated_path.remove(0, beginning_match.capturedEnd()); - - AND_THEN("The rest of the path is composed by zero or more characters in the class [-_./\\a-zA-Z0-9]") { - REQUIRE(QRegularExpression{R"(\A[-_.\/\\a-zA-Z0-9]*\z)"}.match(generated_path).hasMatch()); - } - } - } - } -} diff --git a/tests/auto/qdoc/catch_generators/tests/generators/catch_qchar_generator.cpp b/tests/auto/qdoc/catch_generators/tests/generators/catch_qchar_generator.cpp deleted file mode 100644 index 47ef23364..000000000 --- a/tests/auto/qdoc/catch_generators/tests/generators/catch_qchar_generator.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "namespaces.h" -#include "generators/qchar_generator.h" - -#include <catch_conversions/qt_catch_conversions.h> - -#include <catch/catch.hpp> - -#include <QChar> - -using namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE; -using namespace QDOC_CATCH_GENERATORS_QCHAR_ALPHABETS_NAMESPACE; - -SCENARIO("Binding a generated QChar to a range", "[QChar][Bounds]") { - GIVEN("A lower bound") { - auto lower_bound = GENERATE(take(100, random( - static_cast<unsigned int>(std::numeric_limits<char16_t>::min()), - static_cast<unsigned int>(std::numeric_limits<char16_t>::max()) - ))); - - AND_GIVEN("An upper bound that is greater or equal than the lower bound") { - auto upper_bound = GENERATE_COPY(take(100, random(lower_bound, static_cast<unsigned int>(std::numeric_limits<char16_t>::max())))); - - WHEN("A QChar is generated from those bounds") { - QChar generated_character = GENERATE_COPY(take(1, character(lower_bound, upper_bound))); - - THEN("The generated character has a unicode value in the range [lower_bound, upper_bound]") { - REQUIRE(generated_character.unicode() >= lower_bound); - REQUIRE(generated_character.unicode() <= upper_bound); - } - } - } - } -} - -TEST_CASE( - "When lower_bound and upper_bound are equal, let their value be n, the only generated character is the one with unicode value n", - "[QChar][Bounds]" -) { - auto bound = GENERATE(take(100, random( - static_cast<unsigned int>(std::numeric_limits<char16_t>::min()), - static_cast<unsigned int>(std::numeric_limits<char16_t>::max()) - ))); - auto generated_character = GENERATE_COPY(take(100, character(bound, bound))); - - REQUIRE(generated_character.unicode() == bound); -} - -TEST_CASE("When generating digits, each generated character is in the class [0-9]", "[QChar][SpecialCase]") { - auto generated_character = GENERATE(take(100, digit())); - - REQUIRE(generated_character >= '0'); - REQUIRE(generated_character <= '9'); -} - -TEST_CASE("When generating lowercase ascii characters, each generated character is in the class [a-z]", "[QChar][SpecialCase]") { - auto generated_character = GENERATE(take(100, ascii_lowercase())); - - REQUIRE(generated_character >= 'a'); - REQUIRE(generated_character <= 'z'); -} - -TEST_CASE("When generating uppercase ascii characters, each generated character is in the class [A-Z]", "[QChar][SpecialCase]") { - auto generated_character = GENERATE(take(100, ascii_uppercase())); - - REQUIRE(generated_character >= 'A'); - REQUIRE(generated_character <= 'Z'); -} - -TEST_CASE("When generating ascii alphabetic characters, each generated character is in the class [a-zA-Z]", "[QChar][SpecialCase]") { - auto generated_character = GENERATE(take(100, ascii_alpha())); - - REQUIRE(( - (generated_character >= 'a' && generated_character <= 'z') || - (generated_character >= 'A' && generated_character <= 'Z') - )); -} - -TEST_CASE("When generating ascii alphabetic characters, each generated character is in the class [a-zA-Z0-9]", "[QChar][SpecialCase]") { - auto generated_character = GENERATE(take(100, ascii_alpha())); - - REQUIRE(( - (generated_character >= 'a' && generated_character <= 'z') || - (generated_character >= 'A' && generated_character <= 'Z') || - (generated_character >= '0' && generated_character <= '9') - )); -} - -TEST_CASE("When generating portable posix filename, each generated character is in the class [-_.a-zA-Z0-9]", "[QChar][SpecialCase]") { - auto generated_character = GENERATE(take(100, ascii_alpha())); - - REQUIRE(( - (generated_character == '-') || - (generated_character == '_') || - (generated_character == '.') || - (generated_character >= 'a' && generated_character <= 'z') || - (generated_character >= 'A' && generated_character <= 'Z') || - (generated_character >= '0' && generated_character <= '9') - )); -} diff --git a/tests/auto/qdoc/catch_generators/tests/generators/catch_qstring_generator.cpp b/tests/auto/qdoc/catch_generators/tests/generators/catch_qstring_generator.cpp deleted file mode 100644 index 75d7efcf1..000000000 --- a/tests/auto/qdoc/catch_generators/tests/generators/catch_qstring_generator.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "namespaces.h" -#include "generators/qchar_generator.h" -#include "generators/qstring_generator.h" - -#include <catch_conversions/qt_catch_conversions.h> - -#include <catch/catch.hpp> - -using namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE; - -#include <algorithm> - -SCENARIO("Binding a QString to a length range", "[QString][Bounds]") { - GIVEN("A minimum length") { - auto minimum_length = GENERATE(take(100, random(0, 100))); - - AND_GIVEN("A maximum length that is greater or equal than the minimum length") { - auto maximum_length = GENERATE_COPY(take(100, random(minimum_length, 100))); - - WHEN("A QString is generated from those bounds") { - QString generated_string = GENERATE_COPY(take(1, string(character(), minimum_length, maximum_length))); - - THEN("The generated string's length is in the range [minimum_length, maximum_length]") { - REQUIRE(generated_string.size() >= minimum_length); - REQUIRE(generated_string.size() <= maximum_length); - } - } - } - } -} - -TEST_CASE("When the maximum length and the minimum length are zero all generated strings are the empty string", "[QString][Bounds][SpecialCase][BoundingValue]") { - QString generated_string = GENERATE(take(100, string(character(), 0, 0))); - - REQUIRE(generated_string.isEmpty()); -} - -TEST_CASE("When the maximum length and the minimum length are equal, all generated strings have the same length equal to the given length", "[QString][Bounds][SpecialCase]") { - auto length = GENERATE(take(100, random(0, 100))); - auto generated_string = GENERATE_COPY(take(100, string(character(), length, length))); - - REQUIRE(generated_string.size() == length); -} - -SCENARIO("Limiting the characters that can compose a QString", "[QString][Contents]") { - GIVEN("A list of characters candidates") { - auto lower_character_bound = GENERATE(take(10, random( - static_cast<unsigned int>(std::numeric_limits<char16_t>::min()), - static_cast<unsigned int>(std::numeric_limits<char16_t>::max()) - ))); - auto upper_character_bound = GENERATE_COPY(take(10, random(lower_character_bound, static_cast<unsigned int>(std::numeric_limits<char16_t>::max())))); - - auto character_candidates = character(lower_character_bound, upper_character_bound); - - WHEN("A QString is generated from that list") { - QString generated_string = GENERATE_REF(take(100, string(std::move(character_candidates), 1, 50))); - - THEN("The string is composed only of characters that are in the list of characters") { - REQUIRE( - std::all_of( - generated_string.cbegin(), generated_string.cend(), - [lower_character_bound, upper_character_bound](QChar element){ return element.unicode() >= lower_character_bound && element.unicode() <= upper_character_bound; } - ) - ); - } - } - } -} - -TEST_CASE("The strings generated by a generator of empty string are all empty", "[QString][Contents]") { - QString generated_string = GENERATE(take(100, empty_string())); - - REQUIRE(generated_string.isEmpty()); -} - - -TEST_CASE("The first element of the passsed in generator is not lost", "[QString][GeneratorFirstElement][SpecialCase]") { - QChar first_value{'a'}; - - // REMARK: We use two values to avoid having the generator throw - // an exception if the first element is actually lost. - auto character_generator{Catch::Generators::values({first_value, QChar{'b'}})}; - auto generated_string = GENERATE_REF(take(1, string(std::move(character_generator), 1, 1))); - - REQUIRE(generated_string == QString{first_value}); -} diff --git a/tests/auto/qdoc/catch_generators/tests/generators/combinators/catch_cycle_generator.cpp b/tests/auto/qdoc/catch_generators/tests/generators/combinators/catch_cycle_generator.cpp deleted file mode 100644 index 43bae006b..000000000 --- a/tests/auto/qdoc/catch_generators/tests/generators/combinators/catch_cycle_generator.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "namespaces.h" -#include "generators/combinators/cycle_generator.h" - -#include <catch/catch.hpp> - -using namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE; - -// REMARK: We use fixed-values-generators for those tests so that it -// is trivial to identify when their generation will end, which -// values we should expect and how many values we should expect. -// This is unfortunately not general, but we don't have, by default, -// enough tools to generalize this without having to provide our own -// (being able to generate fixed values from a vector) and adding more -// to the complexity, which is already high. - -TEST_CASE( - "The xn + m element, where 0 < m < n, from a repeating generator whose underlying generator produces n elements, will produce an element equivalent to the mth element of the generation produced by the underlying generator", - "[Cycle][Combinators]" -) { - std::size_t n{10}; - - auto owned_generator{Catch::Generators::values({'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'})}; - auto owned_generator_copy{Catch::Generators::values({'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'})}; - - auto original_generation = GENERATE_REF(take(1, chunk(n, std::move(owned_generator_copy)))); - - std::size_t x = GENERATE(take(10, random(std::size_t{0}, std::size_t{20}))); - std::size_t m = GENERATE_COPY(take(10, random(std::size_t{1}, std::size_t{n}))); - - auto repeating_generator = cycle(std::move(owned_generator)); - auto repeating_generation = GENERATE_REF(take(1, chunk((x * n) + m, std::move(repeating_generator)))); - - REQUIRE(repeating_generation.back() == original_generation[m - 1]); -} - -SCENARIO("Repeating a generation ad infinitum", "[Cycle][Combinators]") { - GIVEN("Some finite generator") { - std::size_t values_amount{3}; - - auto owned_generator{Catch::Generators::values({'a', 'b', 'c'})}; - auto owned_generator_copy{Catch::Generators::values({'a', 'b', 'c'})}; - - AND_GIVEN("A way to repeat the generation of that generator infinitely") { - auto repeating_generator = cycle(std::move(owned_generator)); - - WHEN("Generating exactly enough values to exhaust the original generator") { - auto repeating_generation = GENERATE_REF(take(1, chunk(values_amount, std::move(repeating_generator)))); - auto original_generation = GENERATE_REF(take(1, chunk(values_amount, std::move(owned_generator_copy)))); - - THEN("The repeating generator behaves equally to the original finite generator") { - REQUIRE(repeating_generation == original_generation); - } - } - - WHEN("Generating exactly n times the amount of values required to exhaust the original generator") { - std::size_t n = GENERATE(take(10, random(2, 10))); - - auto original_generation = GENERATE_REF(take(1, chunk(values_amount, std::move(owned_generator_copy)))); - auto repeating_generation = GENERATE_REF(take(n, chunk(values_amount, std::move(repeating_generator)))); - - THEN("The n generation of the repeating generator are always the same as the generation of the original generation") { - REQUIRE(repeating_generation == original_generation); - } - } - } - } -} diff --git a/tests/auto/qdoc/catch_generators/tests/generators/combinators/catch_oneof_generator.cpp b/tests/auto/qdoc/catch_generators/tests/generators/combinators/catch_oneof_generator.cpp deleted file mode 100644 index 68e990813..000000000 --- a/tests/auto/qdoc/catch_generators/tests/generators/combinators/catch_oneof_generator.cpp +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include <catch_conversions/std_catch_conversions.h> - -#include "namespaces.h" -#include "generators/k_partition_of_r_generator.h" -#include "generators/combinators/oneof_generator.h" -#include "generators/combinators/cycle_generator.h" -#include "utilities/statistics/percentages.h" -#include "utilities/statistics/distribution.h" -#include "utilities/semantics/copy_value.h" - -#include <catch/catch.hpp> - -#include <cmath> -#include <iterator> -#include <vector> -#include <algorithm> -#include <unordered_map> - -using namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE; -using namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE; - -SCENARIO("Choosing between one of many generators", "[OneOf][Combinators]") { - GIVEN("Some generators producing values of the same type") { - auto generators_amount = GENERATE(take(10, random(1, 10))); - auto generators_values = GENERATE_COPY(take(10, chunk(generators_amount, random(0, 100000)))); - - std::vector<Catch::Generators::GeneratorWrapper<int>> generators; - generators.reserve(generators_amount); - std::transform( - generators_values.begin(), generators_values.end(), std::back_inserter(generators), - [](auto& value){ return Catch::Generators::value(copy_value(value)); } - ); - - AND_GIVEN("A generator choosing between them based on some distribution") { - std::vector<double> weights = GENERATE_COPY(take(10, k_partition_of_r(100.0, generators_amount))); - auto choosing_generator = oneof(std::move(generators), std::move(weights)); - - WHEN("A value is extracted from the choosing generator") { - auto generated_value = GENERATE_REF(take(100, std::move(choosing_generator))); - - THEN("The generated value is a member of one of the original generators") { - REQUIRE(std::find(generators_values.cbegin(), generators_values.cend(), generated_value) != generators_values.cend()); - } - } - } - - AND_GIVEN("A generator choosing between them with the same probability") { - auto choosing_generator = uniform_oneof(std::move(generators)); - - WHEN("A value is extracted from the choosing generator") { - auto generated_value = GENERATE_REF(take(100, std::move(choosing_generator))); - - THEN("The generated value is a member of one of the original generators") { - REQUIRE(std::find(generators_values.cbegin(), generators_values.cend(), generated_value) != generators_values.cend()); - } - } - } - - AND_GIVEN("A generator choosing between them such that each possible value has the same probability of being chosen") { - auto choosing_generator = uniformly_valued_oneof(std::move(generators), std::vector(generators_amount, std::size_t{1})); - - WHEN("A value is extracted from the choosing generator") { - auto generated_value = GENERATE_REF(take(100, std::move(choosing_generator))); - - THEN("The generated value is a member of one of the original generators") { - REQUIRE(std::find(generators_values.cbegin(), generators_values.cend(), generated_value) != generators_values.cend()); - } - } - } - } -} - -// TODO: The following is a generally complex test. Nonetheless, we -// can probably ease some of the complexity by moving it out into some -// generators or by abstracting it a little to remove the need to know -// some of the implementation details. -// Check if this is possible. - -// REMARK: [mayfail][distribution] -// This tests cannot be precise as it depends on randomized output. -// For this reason, we mark it as !mayfail. -// This allows us to see cases where it fails without having the -// test-run itself fail. -// We generally expect this test to not fail, but it may fail randomly -// every now and then simply because of how a correctly randomized -// distribution may behave. -// As long as this test doesn't fail consistently, with values that -// shows an unsustainable deviation, it should be considered to be -// working. -SCENARIO("Observing the distribution of generators that are chosen from", "[OneOf][Combinators][Statistics][!mayfail]") { - GIVEN("Some generators producing values of the same type") { - std::size_t generators_amount = GENERATE(take(10, random(1, 10))); - - // REMARK: To test the distribution, we want to have some - // amount of generators to choose from whose generated values - // can be uniquely reconducted to the generating generator so - // that we may count how many times a specific generator was - // chosen. - // The easiest way would be to have generators that produce a - // single value. - // Nonetheless, to test the version that provides an - // approximate uniform distribution over the values themselves - // correctly, we need to have generators that can produce a - // different amount of elements. - // When that is not the case, indeed, a generator that - // approximately distributes uniformly over values is - // equivalent to one that approximately distributes uniformely - // over the generators themselves. - // As such, we use ranges of positive integers, as they are - // the simplest multi-valued finite generator that can be dinamically - // construted, while still providing an easy way to infer the - // amount of values it contains so that we can derive the - // cardinality of our domain. - // We produce those ranges as disjoint subsequent ranges - // starting from 0 upward. - // We require the ranges to be disjoint so that we do not lose - // the ability of uniquely identifying a generator that - // produced the value. - // - // To do so, we generate a series of disjoint least upper - // bounds for the ranges. - // Then, we produce the ith range by using the successor of - // the (i - 1)th upper bound as its lower bound and the ith - // upper bound as its upper bound. - // - // We take further care to ensure that the collection of upper - // bounds is sorted, as this simplifies to a linear search our - // need to index the collection of generators to find the - // identifying generator and its associated probability. - std::vector<std::size_t> generators_bounds(generators_amount, 0); - std::vector<Catch::Generators::GeneratorWrapper<std::size_t>> generators; - generators.reserve(generators_amount); - - std::size_t lowest_bound{0}; - std::size_t generators_step{1000}; - std::size_t lower_bound_offset{1}; - - generators_bounds[0] = Catch::Generators::random(lowest_bound, generators_step).get(); - generators.push_back(Catch::Generators::random(lowest_bound, generators_bounds[0])); - - // We use this one to group together values that are generated - // from the same generator and to provide an index for that - // generator to use for finding its associated probability. - // Since our generators are defined by their upper bounds and - // the collection of upper bounds is sorted, the first - // encountered upper bound that is not less than the value - // itself must be the least upper bound of the generator that - // produced the value. - // Then, the index of that upper bound must be the same as the - // index of the producing generator and its associated - // probability. - auto find_index_of_producing_generator = [&generators_bounds](auto value) { - return static_cast<std::size_t>(std::distance( - generators_bounds.begin(), - std::find_if(generators_bounds.begin(), generators_bounds.end(), [&value](auto element){ return value <= element; }) - )); - }; - - for (std::size_t index{1}; index < generators_amount; ++index) { - generators_bounds[index] = Catch::Generators::random(generators_bounds[index - 1] + lower_bound_offset + 1, generators_bounds[index - 1] + lower_bound_offset + 1 + generators_step).get(); - generators.push_back(Catch::Generators::random(generators_bounds[index - 1] + lower_bound_offset, generators_bounds[index])); - } - - AND_GIVEN("A probability of being chosen, in percentage, for each of the generators, such that the sum of the percentages is one hundred") { - std::vector<double> probabilities = GENERATE_COPY(take(10, k_partition_of_r(100.0, generators_amount))); - - AND_GIVEN("A choosing generator for those generators based on the given probabilities") { - auto choosing_generator = oneof(std::move(generators), probabilities); - - WHEN("A certain amount of values are generated from the choosing generator") { - auto values = GENERATE_REF(take(1, chunk(10000, std::move(choosing_generator)))); - - THEN("The distribution of elements for each generator approximately respects the weight that was given to it") { - auto maybe_distribution_error{respects_distribution( - std::move(values), - find_index_of_producing_generator, - [&probabilities](auto key){ return probabilities[key]; } - )}; - - REQUIRE_FALSE(maybe_distribution_error); - } - } - } - } - - AND_GIVEN("A choosing generator for those generators that will choose each generator with the same probability") { - auto choosing_generator = uniform_oneof(std::move(generators)); - - WHEN("A certain amount of values are generated from the choosing generator") { - auto values = GENERATE_REF(take(1, chunk(10000, std::move(choosing_generator)))); - - THEN("The distribution of elements approximates uniformity over the generators") { - double probability{uniform_probability(generators_amount)}; - - auto maybe_distribution_error{respects_distribution( - std::move(values), - find_index_of_producing_generator, - [&probability](auto _){ (void)(_); return probability; } - )}; - - REQUIRE_FALSE(maybe_distribution_error); - } - } - } - - AND_GIVEN("A choosing generator for those generators that will choose each generator such that each possible value has the same probability of being chosen") { - // REMARK: We need to know the total amount of - // unique values that can be generated by our - // generators, so that we can construct an - // appropriate distribution. - // Since our generators are ranges defined by the - // collection of upper bounds we can find their - // length by finding the difference between - // adjacent elements of the collection. - // - // Some more care must be taken to ensure tha the - // correct amount is produced. - // Since we need our ranges to be disjoint, we - // apply a small offset from the element of the - // upper bounds that is used as a lower bound, - // since that upper bound is inclusive for the - // range that precedes the one we are making the - // calculation for. - // - // Furthermore, the first range is treated - // specially. - // As no range precedes it, it doesn't need any - // offset to be applied. - // Additionally, we implicitly use 0 as the first - // lower bound, such that the length of the first - // range is indeed equal to its upper bound. - // - // To account for this, we remove that offset from - // the total amount for each range after the first - // one and use the first upper bound as a seeding - // value to account for the length of the first - // range. - std::vector<std::size_t> generators_cardinality(generators_amount, generators_bounds[0]); - - std::adjacent_difference(generators_bounds.begin(), generators_bounds.end(), generators_bounds.begin()); - std::transform(std::next(generators_cardinality.begin()), generators_cardinality.end(), std::next(generators_cardinality.begin()), [](auto element){ return element - 1; }); - - std::size_t output_cardinality{std::accumulate(generators_cardinality.begin(), generators_cardinality.end(), std::size_t{0})}; - - auto choosing_generator = uniformly_valued_oneof(std::move(generators), std::move(generators_cardinality)); - - WHEN("A certain amount of values are generated from the choosing generator") { - auto values = GENERATE_REF(take(1, chunk(10000, std::move(choosing_generator)))); - - THEN("The distribution of elements approximates uniformity for each value") { - double probability{uniform_probability(output_cardinality)}; - - auto maybe_distribution_error{respects_distribution( - std::move(values), - [](auto value){ return value; }, - [&probability](auto _){ (void)(_); return probability; } - )}; - - REQUIRE_FALSE(maybe_distribution_error); - } - } - } - } -} - -TEST_CASE("A generator with a weight of zero is never chosen when choosing between many generators", "[OneOf][Combinators][SpecialCase]") { - auto excluded_value = GENERATE(take(100, random(0, 10000))); - - std::vector<Catch::Generators::GeneratorWrapper<int>> generators; - generators.reserve(2); - generators.emplace_back(Catch::Generators::random(excluded_value + 1, std::numeric_limits<int>::max())); - generators.emplace_back(Catch::Generators::value(copy_value(excluded_value))); - - auto generated_value = GENERATE_REF(take(100, oneof(std::move(generators), std::vector{100.0, 0.0}))); - - REQUIRE(generated_value != excluded_value); -} - -TEST_CASE("The first element of the passed in generators are not lost", "[OneOf][Combinators][GeneratorFirstElement][SpecialCase]") { - // REMARK: We want to test that, for each generator, the first - // time it is chosen the first value is produced. - // This is complicated because of the fact that OneOf chooses - // random generators in a random order. - // This means that some generators may never be chosen, never be - // chosen more than once and so on. - // Furthermore, this specific test is particularly important only - // for finite generators or non-completely random, ordered, - // infinite generators. - // Additionally, we need to ensure that we test with multiple - // generators, as this test is a consequence of a first bugged - // implementation where only the first chosen generator respected - // the first value, which would pass a test where a single - // generator is used. - // - // This is non-trivial due to the randomized nature of OneOf. - // It can be simplified if we express it in a non-deterministic - // way and mark it as mayfail, where we can recognize with a good - // certainty that the test is actually passing. - // - // To avoid having this flaky test, we approach it as follows: - // - // We provide some amount of infinite generators. Those generators - // are ensured to produce one specific value as their first value - // and then infinitely produce a different value. - // We ensure that each generator that is provided produces unique - // values, that is, no two generators produce a first value or 1 < - // nth value that is equal to the one produced by another - // generator. - // - // Then we pass those generators to oneof and generate enough - // values such that at least one of the generators must have been - // chosen twice or more, at random. - // - // We count the appearances of each value in the produced set. - // Then, if a value that is generated by the 1 < nth choice of a - // specific generator is encountered, we check that the first - // value that the specific generator would produce is in the set - // of values that were generated. - // That is, if a generator has produced his non-first value, it - // must have been chosen twice or more. - // This in turn implies that the first time that the generator was - // chosen, its first value was actually produced. - - struct IncreaseAfterFirst { - std::size_t increase; - bool first_application = true; - - std::size_t operator()(std::size_t value) { - if (first_application) { - first_application = false; - return value; - } - - return value + increase; - } - }; - - std::size_t maximum_generator_amount{100}; - auto generators_amount = GENERATE_COPY(take(10, random(std::size_t{1}, maximum_generator_amount))); - - std::vector<Catch::Generators::GeneratorWrapper<std::size_t>> generators; - generators.reserve(generators_amount); - - for (std::size_t index{0}; index < generators_amount; ++index) { - generators.push_back(Catch::Generators::map(IncreaseAfterFirst{maximum_generator_amount}, cycle(Catch::Generators::value(copy_value(index))))); - } - - auto values = GENERATE_REF(take(1, chunk(generators_amount + 1, uniform_oneof(std::move(generators))))); - auto histogram{make_histogram(values.begin(), values.end(), [](auto e){ return e; })}; - - for (std::size_t index{0}; index < generators_amount; ++index) { - std::size_t second_value{index + maximum_generator_amount}; - histogram.try_emplace(second_value, 0); - - if (histogram[second_value] > 0) { - REQUIRE(histogram.find(index) != histogram.end()); - } - } -} diff --git a/tests/auto/qdoc/catch_generators/tests/main.cpp b/tests/auto/qdoc/catch_generators/tests/main.cpp deleted file mode 100644 index cec18afba..000000000 --- a/tests/auto/qdoc/catch_generators/tests/main.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#define CATCH_CONFIG_RUNNER -#include <catch/catch.hpp> - -// A custom main was provided to avoid linking errors when using minGW -// that were appearing in CI. -// See https://github.com/catchorg/Catch2/issues/1287 -int main(int argc, char* argv[]) { - return Catch::Session().run(argc, argv); -} diff --git a/tests/auto/qdoc/catch_generators/tests/utilities/semantics/catch_generator_handler.cpp b/tests/auto/qdoc/catch_generators/tests/utilities/semantics/catch_generator_handler.cpp deleted file mode 100644 index 913646be5..000000000 --- a/tests/auto/qdoc/catch_generators/tests/utilities/semantics/catch_generator_handler.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include <catch/catch.hpp> - -#include "namespaces.h" -#include "utilities/semantics/generator_handler.h" - -using namespace QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE; - -TEST_CASE( - "Calling next 0 < n times and then calling get on a GeneratorHandler wrapping a generator behaves the same as only calling next (n-1) times and then get on the generator that is wrapped", - "[GeneratorHandler][Utilities][Semantics][Generators]" -) { - auto n = GENERATE(take(100, random(1, 100))); - auto generator_values = GENERATE_COPY(take(1, chunk(n, random(0, 100000)))); - - auto generator_handler = handler(Catch::Generators::from_range(generator_values.begin(), generator_values.end())); - auto generator{Catch::Generators::from_range(generator_values.begin(), generator_values.end())}; - - generator_handler.next(); - for (int times{1}; times < n; ++times) { - generator_handler.next(); - generator.next(); - } - - REQUIRE(generator_handler.get() == generator.get()); -} diff --git a/tests/auto/qdoc/qdoc/CMakeLists.txt b/tests/auto/qdoc/qdoc/CMakeLists.txt index 925290ecd..7e145ab30 100644 --- a/tests/auto/qdoc/qdoc/CMakeLists.txt +++ b/tests/auto/qdoc/qdoc/CMakeLists.txt @@ -15,8 +15,8 @@ qt_internal_add_test(tst_QDoc ${QDOC_SOURCE_DIRECTORY}/filesystem/fileresolver.cpp INCLUDE_DIRECTORIES ${QDOC_INCLUDE_DIRECTORY} - ../catch_generators/ LIBRARIES Qt::QDocCatchPrivate Qt::QDocCatchConversionsPrivate + Qt::QDocCatchGeneratorsPrivate ) diff --git a/tests/auto/qdoc/qdoc/boundaries/filesystem/catch_directorypath.cpp b/tests/auto/qdoc/qdoc/boundaries/filesystem/catch_directorypath.cpp index 5321e2619..31a45bfa4 100644 --- a/tests/auto/qdoc/qdoc/boundaries/filesystem/catch_directorypath.cpp +++ b/tests/auto/qdoc/qdoc/boundaries/filesystem/catch_directorypath.cpp @@ -7,7 +7,7 @@ #include <boundaries/filesystem/directorypath.h> -#include <qdoc_catch_generators.h> +#include <catch_generators/generators/path_generator.h> #include <QFileInfo> #include <QTemporaryDir> diff --git a/tests/auto/qdoc/qdoc/boundaries/filesystem/catch_filepath.cpp b/tests/auto/qdoc/qdoc/boundaries/filesystem/catch_filepath.cpp index 46481e4d6..957a13127 100644 --- a/tests/auto/qdoc/qdoc/boundaries/filesystem/catch_filepath.cpp +++ b/tests/auto/qdoc/qdoc/boundaries/filesystem/catch_filepath.cpp @@ -7,7 +7,7 @@ #include <boundaries/filesystem/filepath.h> -#include <qdoc_catch_generators.h> +#include <catch_generators/generators/path_generator.h> #include <QFileInfo> #include <QTemporaryDir> diff --git a/tests/auto/qdoc/qdoc/filesystem/catch_fileresolver.cpp b/tests/auto/qdoc/qdoc/filesystem/catch_fileresolver.cpp index fca33debb..75774c9cc 100644 --- a/tests/auto/qdoc/qdoc/filesystem/catch_fileresolver.cpp +++ b/tests/auto/qdoc/qdoc/filesystem/catch_fileresolver.cpp @@ -7,7 +7,7 @@ #include <filesystem/fileresolver.h> -#include <qdoc_catch_generators.h> +#include <catch_generators/generators/path_generator.h> #include <vector> #include <algorithm> |