blob: a2ed5a3217abfe92bd501386a53deb4f29205ee2 [file] [log] [blame]
// Copyright Oliver Kowalke 2009.
// 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)
#include <algorithm>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
#include <cstdio>
#include <boost/assert.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/move/move.hpp>
#include <boost/range.hpp>
#include <boost/ref.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/utility.hpp>
#include <boost/coroutine/asymmetric_coroutine.hpp>
namespace coro = boost::coroutines;
int value1 = 0;
std::string value2 = "";
bool value3 = false;
double value4 = .0;
int * value5 = 0;
int& value6 = value1;
int& value7 = value1;
int value8 = 0;
int value9 = 0;
struct X : private boost::noncopyable
{
X() { value1 = 7; }
~X() { value1 = 0; }
};
class copyable
{
public:
bool state;
copyable() :
state( false)
{}
copyable( int) :
state( true)
{}
void operator()( coro::asymmetric_coroutine< int >::push_type &)
{ value3 = state; }
};
class moveable
{
private:
BOOST_MOVABLE_BUT_NOT_COPYABLE( moveable)
public:
bool state;
moveable() :
state( false)
{}
moveable( int) :
state( true)
{}
moveable( BOOST_RV_REF( moveable) other) :
state( false)
{ std::swap( state, other.state); }
moveable & operator=( BOOST_RV_REF( moveable) other)
{
if ( this == & other) return * this;
moveable tmp( boost::move( other) );
std::swap( state, tmp.state);
return * this;
}
void operator()( coro::asymmetric_coroutine< int >::push_type &)
{ value3 = state; }
};
struct my_exception {};
void f1( coro::asymmetric_coroutine< void >::push_type & c)
{
while ( c)
c();
}
void f2( coro::asymmetric_coroutine< void >::push_type &)
{ ++value1; }
void f3( coro::asymmetric_coroutine< void >::push_type & c)
{
++value1;
c();
++value1;
}
void f4( coro::asymmetric_coroutine< int >::push_type & c)
{
c( 3);
c( 7);
}
void f5( coro::asymmetric_coroutine< std::string >::push_type & c)
{
std::string res("abc");
c( res);
res = "xyz";
c( res);
}
void f6( coro::asymmetric_coroutine< int >::pull_type & c)
{ value1 = c.get(); }
void f7( coro::asymmetric_coroutine< std::string >::pull_type & c)
{ value2 = c.get(); }
void f8( coro::asymmetric_coroutine< boost::tuple< double, double > >::pull_type & c)
{
double x = 0, y = 0;
boost::tie( x, y) = c.get();
value4 = x + y;
c();
boost::tie( x, y) = c.get();
value4 = x + y;
}
void f9( coro::asymmetric_coroutine< int * >::pull_type & c)
{ value5 = c.get(); }
void f91( coro::asymmetric_coroutine< int const* >::pull_type & c)
{ value5 = const_cast< int * >( c.get() ); }
void f10( coro::asymmetric_coroutine< int & >::pull_type & c)
{
int const& i = c.get();
value5 = const_cast< int * >( & i);
}
void f101( coro::asymmetric_coroutine< int const& >::pull_type & c)
{
int const& i = c.get();
value5 = const_cast< int * >( & i);
}
void f11( coro::asymmetric_coroutine< boost::tuple< int, int > >::pull_type & c)
{
boost::tie( value8, value9) = c.get();
}
void f12( coro::asymmetric_coroutine< void >::pull_type & c)
{
X x_;
c();
c();
}
template< typename E >
void f14( coro::asymmetric_coroutine< void >::pull_type &, E const& e)
{ throw e; }
void f16( coro::asymmetric_coroutine< int >::push_type & c)
{
c( 1);
c( 2);
c( 3);
c( 4);
c( 5);
}
void f17( coro::asymmetric_coroutine< int >::pull_type & c, std::vector< int > & vec)
{
int x = c.get();
while ( 5 > x)
{
vec.push_back( x);
x = c().get();
}
}
void f19( coro::asymmetric_coroutine< int* >::push_type & c, std::vector< int * > & vec)
{
BOOST_FOREACH( int * ptr, vec)
{ c( ptr); }
}
void f20( coro::asymmetric_coroutine< int >::push_type &)
{}
void f21( coro::asymmetric_coroutine< int >::pull_type & c)
{
while ( c)
{
value1 = c.get();
c();
}
}
void test_move()
{
{
coro::asymmetric_coroutine< void >::pull_type coro1;
coro::asymmetric_coroutine< void >::pull_type coro2( f1);
BOOST_CHECK( ! coro1);
BOOST_CHECK( coro2);
coro2();
coro1 = boost::move( coro2);
BOOST_CHECK( coro1);
coro1();
BOOST_CHECK( ! coro2);
}
{
value3 = false;
copyable cp( 3);
BOOST_CHECK( cp.state);
BOOST_CHECK( ! value3);
coro::asymmetric_coroutine< int >::pull_type coro( cp);
BOOST_CHECK( cp.state);
BOOST_CHECK( value3);
}
{
value3 = false;
moveable mv( 7);
BOOST_CHECK( mv.state);
BOOST_CHECK( ! value3);
coro::asymmetric_coroutine< int >::pull_type coro( boost::move( mv) );
BOOST_CHECK( ! mv.state);
BOOST_CHECK( value3);
}
}
void test_complete()
{
value1 = 0;
coro::asymmetric_coroutine< void >::pull_type coro( f2);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( ( int)1, value1);
}
void test_jump()
{
value1 = 0;
coro::asymmetric_coroutine< void >::pull_type coro( f3);
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( ( int)1, value1);
coro();
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( ( int)2, value1);
}
void test_result_int()
{
coro::asymmetric_coroutine< int >::pull_type coro( f4);
BOOST_CHECK( coro);
int result = coro.get();
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( 3, result);
result = coro().get();
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( 7, result);
coro();
BOOST_CHECK( ! coro);
}
void test_result_string()
{
coro::asymmetric_coroutine< std::string >::pull_type coro( f5);
BOOST_CHECK( coro);
std::string result = coro.get();
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( std::string("abc"), result);
result = coro().get();
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( std::string("xyz"), result);
coro();
BOOST_CHECK( ! coro);
}
void test_arg_int()
{
value1 = 0;
coro::asymmetric_coroutine< int >::push_type coro( f6);
BOOST_CHECK( coro);
coro( 3);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( 3, value1);
}
void test_arg_string()
{
value2 = "";
coro::asymmetric_coroutine< std::string >::push_type coro( f7);
BOOST_CHECK( coro);
coro( std::string("abc") );
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( std::string("abc"), value2);
}
void test_fp()
{
value4 = 0;
coro::asymmetric_coroutine< boost::tuple< double, double > >::push_type coro( f8);
BOOST_CHECK( coro);
coro( boost::make_tuple( 7.35, 3.14) );
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( ( double) 10.49, value4);
value4 = 0;
coro( boost::make_tuple( 1.15, 3.14) );
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( ( double) 4.29, value4);
}
void test_ptr()
{
value5 = 0;
int a = 3;
coro::asymmetric_coroutine< int * >::push_type coro( f9);
BOOST_CHECK( coro);
coro( & a);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( & a, value5);
}
void test_const_ptr()
{
value5 = 0;
int a = 3;
coro::asymmetric_coroutine< int const* >::push_type coro( f91);
BOOST_CHECK( coro);
coro( & a);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( & a, value5);
}
void test_ref()
{
value5 = 0;
int a = 3;
coro::asymmetric_coroutine< int & >::push_type coro( f10);
BOOST_CHECK( coro);
coro( a);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( & a, value5);
}
void test_const_ref()
{
value5 = 0;
int a = 3;
coro::asymmetric_coroutine< int const& >::push_type coro( f101);
BOOST_CHECK( coro);
coro( a);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( & a, value5);
}
void test_tuple()
{
value8 = 0;
value9 = 0;
int a = 3, b = 7;
boost::tuple< int, int > tpl( a, b);
BOOST_CHECK_EQUAL( a, tpl.get< 0 >() );
BOOST_CHECK_EQUAL( b, tpl.get< 1 >() );
coro::asymmetric_coroutine< boost::tuple< int, int > >::push_type coro( f11);
BOOST_CHECK( coro);
coro( tpl);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( a, value8);
BOOST_CHECK_EQUAL( b, value9);
}
void test_unwind()
{
value1 = 0;
{
coro::asymmetric_coroutine< void >::push_type coro( f12);
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( ( int) 0, value1);
coro();
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( ( int) 7, value1);
coro();
BOOST_CHECK_EQUAL( ( int) 7, value1);
}
BOOST_CHECK_EQUAL( ( int) 0, value1);
}
void test_no_unwind()
{
value1 = 0;
{
coro::asymmetric_coroutine< void >::push_type coro(
f12,
coro::attributes(
coro::stack_allocator::traits_type::default_size(),
coro::no_stack_unwind) );
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( ( int) 0, value1);
coro();
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( ( int) 7, value1);
coro();
BOOST_CHECK_EQUAL( ( int) 7, value1);
}
BOOST_CHECK_EQUAL( ( int) 7, value1);
}
void test_exceptions()
{
bool thrown = false;
std::runtime_error ex("abc");
try
{
coro::asymmetric_coroutine< void >::push_type coro( boost::bind( f14< std::runtime_error >, _1, ex) );
BOOST_CHECK( coro);
coro();
BOOST_CHECK( ! coro);
BOOST_CHECK( false);
}
catch ( std::runtime_error const&)
{ thrown = true; }
catch ( std::exception const&)
{}
catch (...)
{}
BOOST_CHECK( thrown);
}
void test_input_iterator()
{
{
std::vector< int > vec;
coro::asymmetric_coroutine< int >::pull_type coro( f16);
BOOST_FOREACH( int i, coro)
{ vec.push_back( i); }
BOOST_CHECK_EQUAL( ( std::size_t)5, vec.size() );
BOOST_CHECK_EQUAL( ( int)1, vec[0] );
BOOST_CHECK_EQUAL( ( int)2, vec[1] );
BOOST_CHECK_EQUAL( ( int)3, vec[2] );
BOOST_CHECK_EQUAL( ( int)4, vec[3] );
BOOST_CHECK_EQUAL( ( int)5, vec[4] );
}
{
std::vector< int > vec;
coro::asymmetric_coroutine< int >::pull_type coro( f16);
coro::asymmetric_coroutine< int >::pull_type::iterator e = boost::end( coro);
for (
coro::asymmetric_coroutine< int >::pull_type::iterator i = boost::begin( coro);
i != e; ++i)
{ vec.push_back( * i); }
BOOST_CHECK_EQUAL( ( std::size_t)5, vec.size() );
BOOST_CHECK_EQUAL( ( int)1, vec[0] );
BOOST_CHECK_EQUAL( ( int)2, vec[1] );
BOOST_CHECK_EQUAL( ( int)3, vec[2] );
BOOST_CHECK_EQUAL( ( int)4, vec[3] );
BOOST_CHECK_EQUAL( ( int)5, vec[4] );
}
{
int i1 = 1, i2 = 2, i3 = 3;
std::vector< int* > vec_in;
vec_in.push_back( & i1);
vec_in.push_back( & i2);
vec_in.push_back( & i3);
std::vector< int* > vec_out;
coro::asymmetric_coroutine< int* >::pull_type coro( boost::bind( f19, _1, boost::ref( vec_in) ) );
coro::asymmetric_coroutine< int* >::pull_type::iterator e = boost::end( coro);
for (
coro::asymmetric_coroutine< int* >::pull_type::iterator i = boost::begin( coro);
i != e; ++i)
{
int * p = * i;
vec_out.push_back( p);
}
BOOST_CHECK_EQUAL( ( std::size_t)3, vec_out.size() );
BOOST_CHECK_EQUAL( & i1, vec_out[0] );
BOOST_CHECK_EQUAL( & i2, vec_out[1] );
BOOST_CHECK_EQUAL( & i3, vec_out[2] );
}
}
void test_output_iterator()
{
int counter = 0;
std::vector< int > vec;
coro::asymmetric_coroutine< int >::push_type coro(
boost::bind( f17, _1, boost::ref( vec) ) );
coro::asymmetric_coroutine< int >::push_type::iterator e( boost::end( coro) );
for ( coro::asymmetric_coroutine< int >::push_type::iterator i( boost::begin( coro) );
i != e; ++i)
{
i = ++counter;
}
BOOST_CHECK_EQUAL( ( std::size_t)4, vec.size() );
BOOST_CHECK_EQUAL( ( int)1, vec[0] );
BOOST_CHECK_EQUAL( ( int)2, vec[1] );
BOOST_CHECK_EQUAL( ( int)3, vec[2] );
BOOST_CHECK_EQUAL( ( int)4, vec[3] );
}
void test_invalid_result()
{
bool catched = false;
coro::asymmetric_coroutine< int >::pull_type coro( f20);
BOOST_CHECK( ! coro);
try
{
int i = coro.get();
(void)i;
}
catch ( coro::invalid_result const&)
{
catched = true;
}
BOOST_CHECK( catched);
}
void test_move_coro()
{
value1 = 0;
coro::asymmetric_coroutine< int >::push_type coro1( f21);
coro::asymmetric_coroutine< int >::push_type coro2;
BOOST_CHECK( coro1);
BOOST_CHECK( ! coro2);
coro1( 1);
BOOST_CHECK_EQUAL( ( int)1, value1);
coro2 = boost::move( coro1);
BOOST_CHECK( ! coro1);
BOOST_CHECK( coro2);
coro2( 2);
BOOST_CHECK_EQUAL( ( int)2, value1);
coro1 = boost::move( coro2);
BOOST_CHECK( coro1);
BOOST_CHECK( ! coro2);
coro1( 3);
BOOST_CHECK_EQUAL( ( int)3, value1);
coro2 = boost::move( coro1);
BOOST_CHECK( ! coro1);
BOOST_CHECK( coro2);
coro2( 4);
BOOST_CHECK_EQUAL( ( int)4, value1);
}
boost::unit_test::test_suite * init_unit_test_suite( int, char* [])
{
boost::unit_test::test_suite * test =
BOOST_TEST_SUITE("Boost.coroutine: asymmetric coroutine test suite");
test->add( BOOST_TEST_CASE( & test_move) );
test->add( BOOST_TEST_CASE( & test_complete) );
test->add( BOOST_TEST_CASE( & test_jump) );
test->add( BOOST_TEST_CASE( & test_result_int) );
test->add( BOOST_TEST_CASE( & test_result_string) );
test->add( BOOST_TEST_CASE( & test_arg_int) );
test->add( BOOST_TEST_CASE( & test_arg_string) );
test->add( BOOST_TEST_CASE( & test_fp) );
test->add( BOOST_TEST_CASE( & test_ptr) );
test->add( BOOST_TEST_CASE( & test_const_ptr) );
test->add( BOOST_TEST_CASE( & test_invalid_result) );
test->add( BOOST_TEST_CASE( & test_ref) );
test->add( BOOST_TEST_CASE( & test_const_ref) );
test->add( BOOST_TEST_CASE( & test_tuple) );
test->add( BOOST_TEST_CASE( & test_unwind) );
test->add( BOOST_TEST_CASE( & test_no_unwind) );
test->add( BOOST_TEST_CASE( & test_exceptions) );
test->add( BOOST_TEST_CASE( & test_input_iterator) );
test->add( BOOST_TEST_CASE( & test_output_iterator) );
return test;
}