// 300-Gen-OwnGenerator.cpp
// Shows how to define a custom generator.

// Specifically we will implement a random number generator for integers
// It will have infinite capacity and settable lower/upper bound

#include <catch2/catch.hpp>

#include <random>

// This class shows how to implement a simple generator for Catch tests
class RandomIntGenerator : public Catch::Generators::IGenerator<int> {
    std::minstd_rand m_rand;
    std::uniform_int_distribution<> m_dist;
    int current_number;
public:

    RandomIntGenerator(int low, int high):
        m_rand(std::random_device{}()),
        m_dist(low, high)
    {
        static_cast<void>(next());
    }

    int const& get() const override;
    bool next() override {
        current_number = m_dist(m_rand);
        return true;
    }
};

// Avoids -Wweak-vtables
int const& RandomIntGenerator::get() const {
    return current_number;
}

// This helper function provides a nicer UX when instantiating the generator
// Notice that it returns an instance of GeneratorWrapper<int>, which
// is a value-wrapper around std::unique_ptr<IGenerator<int>>.
Catch::Generators::GeneratorWrapper<int> random(int low, int high) {
    return Catch::Generators::GeneratorWrapper<int>(std::unique_ptr<Catch::Generators::IGenerator<int>>(new RandomIntGenerator(low, high)));
}

// The two sections in this test case are equivalent, but the first one
// is much more readable/nicer to use
TEST_CASE("Generating random ints", "[example][generator]") {
    SECTION("Nice UX") {
        auto i = GENERATE(take(100, random(-100, 100)));
        REQUIRE(i >= -100);
        REQUIRE(i <= 100);
    }
    SECTION("Creating the random generator directly") {
        auto i = GENERATE(take(100, GeneratorWrapper<int>(std::unique_ptr<IGenerator<int>>(new RandomIntGenerator(-100, 100)))));
        REQUIRE(i >= -100);
        REQUIRE(i <= 100);
    }
}

// Compiling and running this file will result in 400 successful assertions
