2018-06-12 18:50:39 +02:00
|
|
|
/*
|
|
|
|
* Created by Phil Nash on 15/6/2018.
|
|
|
|
*
|
|
|
|
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
*/
|
|
|
|
#ifndef TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
|
|
|
|
#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
|
|
|
|
|
|
|
|
#include "catch_interfaces_generatortracker.h"
|
|
|
|
#include "catch_common.h"
|
2018-09-03 10:03:47 +02:00
|
|
|
#include "catch_enforce.h"
|
2018-06-12 18:50:39 +02:00
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
#include <vector>
|
|
|
|
#include <cassert>
|
|
|
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
namespace Catch {
|
|
|
|
namespace Generators {
|
|
|
|
|
|
|
|
// !TBD move this into its own location?
|
|
|
|
namespace pf{
|
|
|
|
template<typename T, typename... Args>
|
|
|
|
std::unique_ptr<T> make_unique( Args&&... args ) {
|
|
|
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct IGenerator {
|
|
|
|
virtual ~IGenerator() {}
|
|
|
|
virtual auto get( size_t index ) const -> T = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class SingleValueGenerator : public IGenerator<T> {
|
|
|
|
T m_value;
|
|
|
|
public:
|
|
|
|
SingleValueGenerator( T const& value ) : m_value( value ) {}
|
|
|
|
|
|
|
|
auto get( size_t ) const -> T override {
|
|
|
|
return m_value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class FixedValuesGenerator : public IGenerator<T> {
|
|
|
|
std::vector<T> m_values;
|
|
|
|
|
|
|
|
public:
|
|
|
|
FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
|
|
|
|
|
|
|
|
auto get( size_t index ) const -> T override {
|
|
|
|
return m_values[index];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class RangeGenerator : public IGenerator<T> {
|
|
|
|
T const m_first;
|
|
|
|
T const m_last;
|
|
|
|
|
|
|
|
public:
|
|
|
|
RangeGenerator( T const& first, T const& last ) : m_first( first ), m_last( last ) {
|
|
|
|
assert( m_last > m_first );
|
|
|
|
}
|
|
|
|
|
|
|
|
auto get( size_t index ) const -> T override {
|
|
|
|
// ToDo:: introduce a safe cast to catch potential overflows
|
|
|
|
return static_cast<T>(m_first+index);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct NullGenerator : IGenerator<T> {
|
|
|
|
auto get( size_t ) const -> T override {
|
2018-09-03 10:03:47 +02:00
|
|
|
CATCH_INTERNAL_ERROR("A Null Generator is always empty");
|
2018-06-12 18:50:39 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class Generator {
|
|
|
|
std::unique_ptr<IGenerator<T>> m_generator;
|
|
|
|
size_t m_size;
|
|
|
|
|
|
|
|
public:
|
|
|
|
Generator( size_t size, std::unique_ptr<IGenerator<T>> generator )
|
|
|
|
: m_generator( std::move( generator ) ),
|
|
|
|
m_size( size )
|
|
|
|
{}
|
|
|
|
|
|
|
|
auto size() const -> size_t { return m_size; }
|
|
|
|
auto operator[]( size_t index ) const -> T {
|
|
|
|
assert( index < m_size );
|
|
|
|
return m_generator->get( index );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize );
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class GeneratorRandomiser : public IGenerator<T> {
|
|
|
|
Generator<T> m_baseGenerator;
|
|
|
|
|
|
|
|
std::vector<size_t> m_indices;
|
|
|
|
public:
|
|
|
|
GeneratorRandomiser( Generator<T>&& baseGenerator, size_t numberOfItems )
|
|
|
|
: m_baseGenerator( std::move( baseGenerator ) ),
|
|
|
|
m_indices( randomiseIndices( numberOfItems, m_baseGenerator.size() ) )
|
|
|
|
{}
|
|
|
|
|
|
|
|
auto get( size_t index ) const -> T override {
|
|
|
|
return m_baseGenerator[m_indices[index]];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct RequiresASpecialisationFor;
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
auto all() -> Generator<T> { return RequiresASpecialisationFor<T>(); }
|
|
|
|
|
2018-08-23 12:49:05 +02:00
|
|
|
template<>
|
|
|
|
auto all<int>() -> Generator<int>;
|
|
|
|
|
2018-06-12 18:50:39 +02:00
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
auto range( T const& first, T const& last ) -> Generator<T> {
|
|
|
|
return Generator<T>( (last-first), pf::make_unique<RangeGenerator<T>>( first, last ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
auto random( T const& first, T const& last ) -> Generator<T> {
|
|
|
|
auto gen = range( first, last );
|
|
|
|
auto size = gen.size();
|
|
|
|
|
|
|
|
return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( std::move( gen ), size ) );
|
|
|
|
}
|
|
|
|
template<typename T>
|
|
|
|
auto random( size_t size ) -> Generator<T> {
|
|
|
|
return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( all<T>(), size ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
auto values( std::initializer_list<T> values ) -> Generator<T> {
|
|
|
|
return Generator<T>( values.size(), pf::make_unique<FixedValuesGenerator<T>>( values ) );
|
|
|
|
}
|
|
|
|
template<typename T>
|
|
|
|
auto value( T const& val ) -> Generator<T> {
|
|
|
|
return Generator<T>( 1, pf::make_unique<SingleValueGenerator<T>>( val ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
auto as() -> Generator<T> {
|
|
|
|
return Generator<T>( 0, pf::make_unique<NullGenerator<T>>() );
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename... Ts>
|
|
|
|
auto table( std::initializer_list<std::tuple<Ts...>>&& tuples ) -> Generator<std::tuple<Ts...>> {
|
|
|
|
return values<std::tuple<Ts...>>( std::forward<std::initializer_list<std::tuple<Ts...>>>( tuples ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct Generators : GeneratorBase {
|
|
|
|
std::vector<Generator<T>> m_generators;
|
|
|
|
|
|
|
|
using type = T;
|
|
|
|
|
|
|
|
Generators() : GeneratorBase( 0 ) {}
|
|
|
|
|
|
|
|
void populate( T&& val ) {
|
|
|
|
m_size += 1;
|
|
|
|
m_generators.emplace_back( value( std::move( val ) ) );
|
|
|
|
}
|
|
|
|
template<typename U>
|
|
|
|
void populate( U&& val ) {
|
|
|
|
populate( T( std::move( val ) ) );
|
|
|
|
}
|
|
|
|
void populate( Generator<T>&& generator ) {
|
|
|
|
m_size += generator.size();
|
|
|
|
m_generators.emplace_back( std::move( generator ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename U, typename... Gs>
|
|
|
|
void populate( U&& valueOrGenerator, Gs... moreGenerators ) {
|
|
|
|
populate( std::forward<U>( valueOrGenerator ) );
|
|
|
|
populate( std::forward<Gs>( moreGenerators )... );
|
|
|
|
}
|
|
|
|
|
|
|
|
auto operator[]( size_t index ) const -> T {
|
|
|
|
size_t sizes = 0;
|
|
|
|
for( auto const& gen : m_generators ) {
|
|
|
|
auto localIndex = index-sizes;
|
|
|
|
sizes += gen.size();
|
|
|
|
if( index < sizes )
|
|
|
|
return gen[localIndex];
|
|
|
|
}
|
2018-09-03 10:03:47 +02:00
|
|
|
CATCH_INTERNAL_ERROR("Index '" << index << "' is out of range (" << sizes << ')');
|
2018-06-12 18:50:39 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T, typename... Gs>
|
|
|
|
auto makeGenerators( Generator<T>&& generator, Gs... moreGenerators ) -> Generators<T> {
|
|
|
|
Generators<T> generators;
|
|
|
|
generators.m_generators.reserve( 1+sizeof...(Gs) );
|
|
|
|
generators.populate( std::move( generator ), std::forward<Gs>( moreGenerators )... );
|
|
|
|
return generators;
|
|
|
|
}
|
|
|
|
template<typename T>
|
|
|
|
auto makeGenerators( Generator<T>&& generator ) -> Generators<T> {
|
|
|
|
Generators<T> generators;
|
|
|
|
generators.populate( std::move( generator ) );
|
|
|
|
return generators;
|
|
|
|
}
|
|
|
|
template<typename T, typename... Gs>
|
|
|
|
auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> {
|
|
|
|
return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
|
|
|
|
}
|
|
|
|
template<typename T, typename U, typename... Gs>
|
|
|
|
auto makeGenerators( U&& val, Gs... moreGenerators ) -> Generators<T> {
|
|
|
|
return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
|
|
|
|
|
|
|
|
template<typename L>
|
|
|
|
// Note: The type after -> is weird, because VS2015 cannot parse
|
|
|
|
// the expression used in the typedef inside, when it is in
|
|
|
|
// return type. Yeah, ¯\_(ツ)_/¯
|
|
|
|
auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>()[0]) {
|
|
|
|
using UnderlyingType = typename decltype(generatorExpression())::type;
|
|
|
|
|
|
|
|
IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo );
|
|
|
|
if( !tracker.hasGenerator() )
|
|
|
|
tracker.setGenerator( pf::make_unique<Generators<UnderlyingType>>( generatorExpression() ) );
|
|
|
|
|
|
|
|
auto const& generator = static_cast<Generators<UnderlyingType> const&>( *tracker.getGenerator() );
|
|
|
|
return generator[tracker.getIndex()];
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Generators
|
|
|
|
} // namespace Catch
|
|
|
|
|
|
|
|
#define GENERATE( ... ) \
|
|
|
|
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
|
|
|
|
|
|
|
|
|
|
|
|
#endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
|