
#include "etwork/etwork.h" #include "etwork/buffer.h" #include "etwork/errors.h" #include "etwork/notify.h" #include "etwork/marshal.h" #include <assert.h> #include <stdio.h> #include <string> #include <math.h> #if defined( NDEBUG ) #pragma warning( disable: 4101 ) // unreferenced local variable #endif void TestEtworkCreate() { EtworkSettings es; es.accepting = true; es.port = 11147; ISocketManager * sm = CreateEtwork( &es ); sm->dispose(); } void TestEtworkBuffer() { etwork::Buffer b( 1000, 3000, 10 ); b.put_message( "hello, world!", 13 ); b.put_message( "1234567890", 10 ); char buf[100]; assert( b.space_used() == 10+13 ); int r = b.get_data( buf, 100 ); assert( r == 10+13+2+2 ); assert( !b.space_used() ); assert( r == 2 + 13 + 2 + 10 ); assert( buf[0] == 0 && buf[1] == 13 ); assert( !strncmp( &buf[2], "hello, world!", 13 ) ); assert( buf[2+13] == 0 && buf[3+13] == 10 ); assert( !strncmp( &buf[2+13+2], "1234567890", 10 ) ); r = b.put_data( buf, 10+13+2+2 ); assert( r == 10+13+2+2 ); assert( b.space_used() == 10+13 ); assert( b.get_message( buf, 100 ) == 13 ); assert( !strncmp( buf, "hello, world!", 13 ) ); assert( b.space_used() == 10 ); assert( b.get_message( buf, 100 ) == 10 ); assert( !strncmp( buf, "1234567890", 10 ) ); assert( b.get_message( buf, 100 ) == -1 ); assert( b.space_used() == 0 ); } void TestEtworkBufferEvil() { etwork::Buffer b( 10, 20, 5 ); assert( b.put_message( "1234567890", 10 ) == 10 ); assert( b.put_message( "", 0 ) == 0 ); assert( b.space_used() == 10 ); assert( b.put_message( "1234567890-", 11 ) == -1 ); assert( b.space_used() == 10 ); char buf[100]; assert( b.get_data( buf, 100 ) == 10+2+2 ); assert( b.space_used() == 0 ); assert( b.put_data( buf, 1 ) == 1 ); assert( b.put_data( &buf[1], 1 ) == 1 ); assert( b.put_data( &buf[2], 9 ) == 9 ); assert( b.space_used() == 0 ); assert( b.put_data( &buf[11], 2 ) == 2 ); assert( b.space_used() == 10 ); assert( b.get_message( buf, 9 ) == -1 ); assert( b.space_used() == 10 ); assert( b.get_message( buf, 10 ) == 10 ); assert( b.space_used() == 0 ); assert( b.get_message( buf, 10 ) == -1 ); assert( b.put_data( &buf[13], 1 ) == 1 ); assert( b.space_used() == 0 ); assert( b.get_message( buf, 100 ) == 0 ); assert( b.get_message( buf, 100 ) == -1 ); } void TestEtworkTcp() { EtworkSettings es; es.accepting = true; es.reliable = true; es.port = 11147; ISocketManager * sm = CreateEtwork( &es ); ISocket * s1; int i = sm->connect( "127.0.0.1", 11147, &s1 ); assert( i == 1 ); ISocket * active[4]; i = sm->poll( 0.1, active, 4 ); assert( i == 0 ); i = sm->accept( active, 4 ); assert( i == 1 ); ISocket * s2 = active[0]; i = s1->write( "hello, world!\n", 14 ); assert( i == 14 ); i = s1->write( "", 0 ); assert( i == 0 ); char buf[200]; assert( s2->read( buf, 200 ) == -1 ); i = sm->poll( 0.1, active, 4 ); assert( i == 2 ); // s2 received data, s1 wrote data assert( active[0] == s2 || active[1] == s2 ); assert( active[0] == s1 || active[1] == s1 ); assert( s2->read( buf, 200 ) == 14 ); assert( !strncmp( buf, "hello, world!\n", 14 ) ); assert( s2->read( buf, 200 ) == 0 ); assert( s2->read( buf, 200 ) == -1 ); s2->dispose(); s1->write( "X", 2 ); assert( !s1->closed() ); i = sm->poll( 0.1, active, 4 ); assert( i == 1 ); i = sm->poll( 0.1, active, 4 ); if( !s1->closed() ) { i = sm->poll( 0.1, active, 4 ); } assert( s1->closed() ); s1->dispose(); sm->dispose(); } void TestEtworkUdp() { EtworkSettings es1; es1.accepting = true; es1.reliable = false; es1.port = 11147; ISocketManager * sm1 = CreateEtwork( &es1 ); assert( sm1 != 0 ); EtworkSettings es2; es2.accepting = true; es2.reliable = false; es2.port = 11148; ISocketManager * sm2 = CreateEtwork( &es2 ); assert( sm2 != 0 ); ISocket * s1 = 0; int i = sm1->connect( "127.0.0.1", 11148, &s1 ); assert( i == 1 ); ISocket * active[4]; i = sm1->poll( 0.1, active, 4 ); assert( i == 1 ); // the newly connected socket i = sm2->poll( 0.1, active, 4 ); assert( i == 0 ); ISocket * s2 = 0; i = sm2->accept( &s2, 1 ); assert( i == 1 ); char buf[200]; assert( s2->read( buf, 200 ) == -1 ); // can't receive data until accepted i = s2->write( "hello, world!\n", 14 ); assert( i == 14 ); i = s2->write( "xyzzy", 6 ); assert( i == 6 ); i = sm1->poll( 0.1, active, 4 ); assert( i == 1 ); // s1 is receiving greeting message assert( active[0] == s1 ); i = s1->read( buf, 200 ); assert( i == 0 ); i = s1->read( buf, 200 ); assert( i == -1 ); i = sm2->poll( 0.1, active, 4 ); assert( i == 1 ); // s2 just sent data i = sm1->poll( 0.1, active, 4 ); assert( i == 1 ); // s1 received it assert( s2->read( buf, 200 ) == -1 ); i = s1->read( buf, 200 ); assert( i == 14 ); assert( !strncmp( buf, "hello, world!\n", 14 ) ); i = s1->read( buf, 200 ); assert( i == 6 ); assert( !strncmp( buf, "xyzzy", 6 ) ); // UDP doesn't give us notice when it closes s1->dispose(); s2->dispose(); sm1->dispose(); sm2->dispose(); } class ErrorNotify : public IErrorNotify { public: ErrorInfo error_; ErrorNotify() { clear(); } virtual void onSocketError( ErrorInfo const & info ) { error_ = info; } void clear() { error_.socket = 0; error_.osError = 0; error_.error = 0; } }; void TestEtworkErrors() { ISocketManager * mgr; ErrorNotify en; SetEtworkErrorNotify( &en ); EtworkSettings st; st.port = 0; st.accepting = true; mgr = CreateEtwork( &st ); assert( mgr == 0 ); assert( (int)en.error_.error != 0 ); SetEtworkErrorNotify( 0 ); } class SocketNotify : public INotify { public: SocketNotify() { notified_ = false; } void clear() { notified_ = false; } void onNotify() { notified_ = true; } bool notified_; }; void TestEtworkNotify() { EtworkSettings st; st.accepting = true; st.port = 61234; ISocketManager * mgr = CreateEtwork( &st ); assert( mgr != 0 ); ISocket * active[2]; mgr->poll( 0.01, active, 2 ); ISocket * sock = 0; int r = mgr->connect( "127.0.0.1", 61234, &sock ); assert( r == 1 ); mgr->poll( 0.01, active, 2 ); ISocket * sock2 = 0; assert( mgr->accept( &sock2, 1 ) == 1 ); SocketNotify n1, n2; SetEtworkSocketNotify( sock, &n1 ); SetEtworkSocketNotify( sock2, &n2 ); r = mgr->poll( 0.01, active, 2 ); assert( r == 0 ); assert( !n1.notified_ ); assert( !n2.notified_ ); r = sock->write( "hello", 5 ); assert( r == 5 ); r = mgr->poll( 0.01, active, 2 ); assert( r == 0 ); r = mgr->poll( 0.01, active, 2 ); assert( r == 0 ); assert( n1.notified_ ); // because it wrote assert( n2.notified_ ); // because it received n1.clear(); n2.clear(); r = mgr->poll( 0.01, active, 2 ); assert( r == 0 ); assert( !n1.notified_ ); assert( !n2.notified_ ); sock->dispose(); sock2->dispose(); mgr->dispose(); } void TestBlock() { char abuf[32]; Block a( abuf, 32 ); Block b( 40 ); b << a; assert( b.pos() == 32 ); assert( b.left() == 8 ); b << a; assert( b.eof() ); b.seek( 0 ); assert( !b.eof() ); assert( b.read( abuf, 32 ) == 32 ); assert( !b.eof() ); assert( b.read( abuf, 32 ) == 8 ); assert( !b.eof() ); assert( b.read( abuf, 32 ) == 0 ); assert( b.eof() ); assert( a.begin() == (unsigned char *)abuf ); assert( a.end() == (unsigned char *)&abuf[32] ); } struct MarshalTest { float f; bool b; std::string s; int i; }; struct AMarshalTest2 { int i; MarshalTest mt; }; MARSHAL_BEGIN_TYPE( AMarshalTest2 ) // make sure it's alphabetically before MarshalTest MARSHAL_INT( i, 0, 2 ) MARSHAL_TYPE( MarshalTest, mt ) MARSHAL_END_TYPE( AMarshalTest2, 2 ) MARSHAL_BEGIN_TYPE( MarshalTest ) MARSHAL_FLOAT( f, -1, 1, 0.01f ) MARSHAL_INT( i, 0, 200 ) MARSHAL_STRING( s, 200 ) MARSHAL_BOOL( b ) MARSHAL_END_TYPE( MarshalTest, 1 ) void TestMarshal() { char const * err = IMarshalManager::startup(); assert( !err ); Block buf( 1000 ); MarshalTest mt; mt.f = -0.5; mt.b = true; mt.s = "hello, world!"; mt.i = 200; // Each call to Marshaller<MarhsalTest>() creates a new // marshaller instance. In reality, we use the registry // to get the tester class. size_t s = Marshaller< MarshalTest >().marshal( &mt, buf ); assert( s == 17 ); buf.seek( 0 ); char mem[ sizeof( MarshalTest ) ]; memset( mem, 0, sizeof( mem ) ); Marshaller< MarshalTest >().construct( mem ); s = Marshaller< MarshalTest >().demarshal( buf, mem ); assert( s == 17 ); MarshalTest * mtp = (MarshalTest *)mem; assert( ::fabsf( mtp->f - -0.5f ) < 0.005f ); assert( mtp->b == true ); assert( mtp->s == "hello, world!" ); assert( mtp->i == 200 ); Marshaller< MarshalTest >().destruct( mem ); buf.seek( 0 ); bool b = IMarshalManager::instance()->marshal( mt, buf ); assert( b ); assert( buf.pos() == 17 ); buf.seek( 0 ); b = IMarshalManager::instance()->demarshal( mt, buf ); assert( b ); assert( buf.pos() == 17 ); } struct AcceptPacket { unsigned int user_; unsigned int expiry_; unsigned int protoCount_; }; MARSHAL_BEGIN_TYPE( AcceptPacket ) MARSHAL_INT( user_, 0, 1000 ) MARSHAL_INT( expiry_, 0, 30000 ) MARSHAL_INT( protoCount_, 0, 10000 ) MARSHAL_END_TYPE( AcceptPacket, 0x13 ) struct Uint64Packet { unsigned long long uint64_; }; MARSHAL_BEGIN_TYPE( Uint64Packet ) MARSHAL_UINT64( uint64_, 64 ) MARSHAL_END_TYPE( Uint64Packet, 0x14 ) void TestMarshalBugs() { // I had a bug provoked by the AcceptPacket AcceptPacket ap; memset(&ap, 0, sizeof(ap)); ap.user_ = 100; ap.expiry_ = 100; ap.protoCount_ = 4; { Block b(200); IMarshalManager::instance()->marshal(ap, b); memset(&ap, 0, sizeof(ap)); b.seek(0); IMarshalManager::instance()->demarshal(ap, b); } assert( ap.user_ == 100 ); assert( ap.expiry_ == 100 ); assert( ap.protoCount_ == 4 ); // I had a bug provoked by a single uint64 field Uint64Packet up; memset(&up, 0, sizeof(up)); up.uint64_ = 1234; { Block b(200); IMarshalManager::instance()->marshal(up, b); memset(&up, 0, sizeof(up)); b.seek(0); IMarshalManager::instance()->demarshal(up, b); } assert( up.uint64_ == 1234 ); IMarshaller *m = IMarshalManager::instance()->marshaller(typeid(Uint64Packet).name()); assert(m); assert(m->id() == 0x14); } int main() { fprintf( stderr, "Testing Etwork...\n" ); TestEtworkCreate(); TestEtworkBuffer(); TestEtworkBufferEvil(); TestEtworkTcp(); TestEtworkUdp(); TestEtworkErrors(); TestEtworkNotify(); TestBlock(); TestMarshal(); TestMarshalBugs(); fprintf( stderr, "All tests passed!\n" ); return 0; }