diff options
Diffstat (limited to 'tests/auto/qdoc/catch_generators/tests')
9 files changed, 0 insertions, 1477 deletions
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()); -} |