/*
*  psocksxx - A C++ wrapper for POSIX sockets
*  Copyright (C) 2013 Uditha Atukorala
*
*  This software library is free software; you can redistribute it and/or modify
*  it under the terms of the GNU Lesser General Public License as published by
*  the Free Software Foundation; either version 3 of the License, or
*  (at your option) any later version.
*
*  This software library is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this software library. If not, see .
*
*/
#include "tcpnsockstream_test.h"
#include "necho.h"
#include 
#include 
#include 
// register the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( tcpnsockstream_test );
// use namespace psocksxx
using namespace psocksxx;
void tcpnsockstream_test::connect_naddr( nsockaddr * naddr ) throw() {
	int csock;
	// not worth having error handling here so we hope for the best
	csock = socket( AF_INET, SOCK_STREAM, 0 );
	connect( csock, naddr->psockaddr(), naddr->size() );
}
void tcpnsockstream_test::test_constructors() {
	// default constructor
	CPPUNIT_ASSERT_NO_THROW( tcpnsockstream ss );
}
void tcpnsockstream_test::test_connect_addr() {
	// tcp socket stream
	tcpnsockstream ss;
	// network echo server
	necho n( NSOCK_NODE, NSOCK_SERVICE );
	// network address to connect to
	nsockaddr saddr( NSOCK_NODE, NSOCK_SERVICE );
	// connect
	CPPUNIT_ASSERT_NO_THROW( ss.connect( &saddr ) );
}
void tcpnsockstream_test::test_connect_host_port() {
	// tcp socket stream
	tcpnsockstream ss;
	// network echo server
	necho n( NSOCK_NODE, NSOCK_SERVICE_2 );
	// connect
	CPPUNIT_ASSERT_NO_THROW( ss.connect( NSOCK_NODE, atoi( NSOCK_SERVICE_2 ) ) );
}
void tcpnsockstream_test::test_bind_addr() {
	// tcp socket stream
	tcpnsockstream ss;
	// network address to bind to
	nsockaddr naddr( NSOCK_NODE, NSOCK_BIND_SERVICE );
	// bind
	CPPUNIT_ASSERT_NO_THROW( ss.bind( &naddr, true ) );
}
void tcpnsockstream_test::test_listen_addr() {
	// tcp socket stream
	tcpnsockstream ss;
	// network address to bind to
	nsockaddr naddr( NSOCK_NODE, NSOCK_BIND_SERVICE );
	// bind
	try {
		ss.bind( &naddr, true );
	} catch( sockexception &e ) {
		CPPUNIT_FAIL( e.what() );
		return;
	}
	// listen
	CPPUNIT_ASSERT_NO_THROW( ss.listen( 1 ) );
}
void tcpnsockstream_test::test_accept_addr() {
	// fork variables
	pid_t cpid, wpid;
	int   wpid_status;
	// tcp socket stream
	tcpnsockstream ss;
	// network socket pointer
	nsockstream * nsock = 0;
	// network address to bind to
	nsockaddr naddr( NSOCK_NODE, NSOCK_BIND_SERVICE );
	// bind
	try {
		ss.bind( &naddr, true );
	} catch( sockexception &e ) {
		CPPUNIT_FAIL( e.what() );
		return;
	}
	// listen
	try {
		ss.listen( 1 );
	} catch( sockexception &e ) {
		CPPUNIT_FAIL( e.what() );
		return;
	}
	// fork
	cpid = fork();
	if ( cpid == -1 ) {
		CPPUNIT_FAIL( "failed to fork" );
	} else if ( cpid == 0 ) {
		// child - connect to the network socket created by the parent
		connect_naddr( &naddr );
		// exit
		exit( 0 );
	} else {
		// parent - accept a connection from the child
		CPPUNIT_ASSERT_NO_THROW( nsock = ss.accept() );
		CPPUNIT_ASSERT( nsock != 0 );
		// cleanup
		if ( nsock != 0 ) {
			delete nsock;
		}
		// wait for child to exit
		if ( ( wpid = waitpid( cpid, &wpid_status, 0 ) ) == -1 ) {
			CPPUNIT_FAIL( "failed waiting for the child process to terminate" );
		}
	}
}
void tcpnsockstream_test::test_bind_addr_fail() {
	// tcp socket stream
	tcpnsockstream ss, ssf;
	// network address to bind to
	nsockaddr naddr( NSOCK_NODE, NSOCK_BIND_SERVICE + 1 );
	try {
		ss.bind( &naddr, true );
	} catch ( sockexception &e ) {
		CPPUNIT_FAIL( e.what() );
		return;
	}
	// bind - this should fail with address already in use error
	CPPUNIT_ASSERT_THROW( ssf.bind( &naddr ), sockexception );
}
void tcpnsockstream_test::test_io_addr() {
	// tcp socket stream
	tcpnsockstream ss;
	// network echo server
	necho n( NSOCK_NODE, NSOCK_SERVICE );
	// network address to connect to
	nsockaddr saddr( NSOCK_NODE, NSOCK_SERVICE );
	// connect
	try {
		ss.connect( &saddr );
	} catch ( sockexception &e ) {
		CPPUNIT_FAIL( e.what() );
		return;
	}
	// write
	ss << 'c' << std::endl;
	// read
	char c;
	ss >> c;
	// assert
	CPPUNIT_ASSERT( c == 'c' );
}