*/
/** @file
*
- * (c) Copyright 2007 by Intra2net AG
+ * (c) Copyright 2007-2009 by Intra2net AG
*/
-//#define NOISEDEBUG
+#define NOISEDEBUG
#include <string>
#include <iostream>
#include <async_timer.hpp>
#include <async_callout.hpp>
#include <async_socket.hpp>
-#include <filefunc.hxx>
-#include <containerfunc.hpp>
+#include <asyncio_system_tools.hpp>
+#include <asyncio_containerfunc.hpp>
#include <boost/signal.hpp>
#include <boost/bind.hpp>
#include <boost/random.hpp>
struct Counter
{
int value;
-
-
+
+
Counter() : value(0) { DOUT("Counter construct");}
-
+
void reset() { value=0;}
-
+
void advance()
{
DOUT(" advance called");
++value;
}
-
+
int operator()()
{
DOUT(" () called");
class TestTimer : public TimerBase
{
public:
-
+
TestTimer()
: m_counter(0u)
{
} // eo TestTimer()
-
-
+
+
void setDelta(long msec)
{
setDeltaWhenTime(0,msec);
activate();
} // eo setDelta(long)
-
-
+
+
unsigned m_counter;
-
+
protected:
-
+
virtual void execute()
{
++m_counter;
} // eo execute()
-
+
}; // eo class TestTimer
{
std::vector<std::string> m_received_vector;
std::string m_received_string;
-
+
unsigned long m_count_lines;
unsigned long m_data_size;
-
-
+
+
void resetReceivedData()
{
m_received_vector.clear();
++m_count_lines;
m_data_size += data.size();
} // eo receiveData(const std::string&)
-
+
}; // eo struct ReceivedData
} // eo TestPipe()
-
+
protected:
-
+
}; // eo class TestPipe
{
m_signal_read.connect( boost::bind(&TestProcess::slotReceivedData, this) );
} // eo Testprocess(const std::string&, const std::vector<std::string>&)
-
+
TestProcess(
const std::string& path,
const std::string& arg1 )
{
m_signal_read.connect( boost::bind(&TestProcess::slotReceivedData, this) );
} // eo Testprocess(const std::string&, const std::vector<std::string>&)
-
+
TestProcess(
const std::string& path,
const std::string& arg1, const std::string& arg2 )
bool kill( Signal signal) { return ProcessImplementation::kill(signal); }
protected:
-
+
void slotReceivedData()
{
receiveData(m_input_buffer);
m_input_buffer.clear();
} // eo slotReceivedData()
-
+
}; // eo class TestProcess
, public ReceivedData
{
public:
-
+
TestUnixIOSocket()
: UnixIOSocket()
{
m_signal_read.connect( boost::bind(&TestUnixIOSocket::slotReceivedData, this) );
} // eo TestUnixIOSocket()
-
-
+
+
TestUnixIOSocket(
int fd, const std::string& path,
unsigned int peer_pid, unsigned int peer_uid, unsigned int peer_gid
{
m_signal_read.connect( boost::bind(&TestUnixIOSocket::slotReceivedData, this) );
} // eo TestUnixIOSocket()
-
-
+
+
void sendData(const std::string& data)
{
lowSend(data);
} // eo sendData(const std::string&)
-
+
protected:
-
+
void slotReceivedData()
{
receiveData(m_input_buffer);
m_input_buffer.clear();
} // eo slotReceivedData()
-
+
}; // eo class TestUnixIOSocket
typedef boost::shared_ptr< TestUnixIOSocket > TestUnixIOSocketPtr;
: public std::vector< UnixIOSocketPtr >
{
public:
-
+
void operator()(UnixIOSocketPtr ptr)
{
push_back(ptr);
}
-
+
void storeBase (IOImplementationPtr ptr)
{
push_back(boost::dynamic_pointer_cast< UnixIOSocket >(ptr) );
}
-
+
void store (UnixIOSocketPtr ptr)
{
push_back(ptr);
}
-
+
TestUnixIOSocketPtr get(int idx)
{
return boost::dynamic_pointer_cast<
{
static std::string chars("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz=+-*/%(){}<>.,:;\\");
std::string s;
-
+
boost::uniform_int<> discreter(0, chars.size()-1);
boost::variate_generator<boost::mt19937&, boost::uniform_int<> > idxgen(g_random_gen, discreter);
-
+
for(; len-->0;) s+= chars.at( idxgen() );
-
+
return s;
} // eo makeRandomAsciiString
class TestSimpleioBasics : public TestFixture
{
CPPUNIT_TEST_SUITE(TestSimpleioBasics);
-
+
CPPUNIT_TEST(EmptyBackendStepCall);
CPPUNIT_TEST(NonEmptyBackendStepCall);
CPPUNIT_TEST(SingleTimerShot);
CPPUNIT_TEST(SimpleTimerShot);
CPPUNIT_TEST(SimpleTimerShot2);
-
+
CPPUNIT_TEST(EmptyWantTest);
CPPUNIT_TEST(SimplePipeTest);
CPPUNIT_TEST(SimplePipePump);
-
+
CPPUNIT_TEST(SimpleProcessTestBinTrue);
CPPUNIT_TEST(SimpleProcessTestBinFalse);
CPPUNIT_TEST(SimpleProcessTestEcho);
CPPUNIT_TEST(SimpleProcessTestStderr);
CPPUNIT_TEST(SignaledProcessTermination);
-
-
+
+
CPPUNIT_TEST(CallOut1);
CPPUNIT_TEST(CallOut2);
CPPUNIT_TEST(RemoveCallOut1);
-
+
CPPUNIT_TEST(FrozenCall_Thaw);
CPPUNIT_TEST(FrozenCall_Decay);
-
-
+
+
CPPUNIT_TEST(UnixSockets_ClientServer);
-
+
//CPPUNIT_TEST(Dummy);
CPPUNIT_TEST_SUITE_END();
-
+
protected:
-
+
Backend *backend;
std::set<std::string> used_check_files;
-
-
+
+
template<class Callable>
bool backendLoopUntil( Callable condition, int maxLoops=100 )
{
}
return condition();
} // eo backendLoopUntil
-
-
+
+
bool backendStep(int msTimeout= 10, int count=1)
{
bool res= true;
}
return res;
} // eo backendStep
-
-
+
+
std::string getCheckFilepath(std::string tag)
{
std::string result;
used_check_files.insert(result);
return result;
} // eo get_check_file_path
-
-
+
+
void removeCheckFiles()
{
for(std::set<std::string>::iterator it= used_check_files.begin();
++it)
{
std::string filepath(*it);
- if (path_exists(filepath))
+ if (Utils::FileStat(filepath))
{
- unlink(filepath);
+ Utils::unlink(filepath);
}
}
used_check_files.clear();
} // eo removeCheckFiles
-
-
-
+
+
+
public:
-
+
void setUp()
{
backend = Backend::getBackend();
installChildHandler();
used_check_files.clear();
} // eo setUp
-
-
+
+
void tearDown()
{
restoreChildHandler();
removeCheckFiles();
} // eo tearDown
-
-
+
+
/*
* the tests:
*/
-
-
+
+
/*
** basics:
*/
-
-
+
+
void EmptyBackendStepCall()
{
CPPUNIT_ASSERT( backend );
-
+
// a backend call without active objects should return false:
bool result = backend->doOneStep(0);
-
+
CPPUNIT_ASSERT_EQUAL( false, result );
} // eo EmptyBackendStepCall
-
-
+
+
void NonEmptyBackendStepCall()
{
CPPUNIT_ASSERT(backend);
-
+
{
TestTimer timer;
timer.setDelta(10);
bool result = backend->doOneStep(0);
CPPUNIT_ASSERT_EQUAL( false, result );
} // eo NonEmptyBackendStepCall
-
-
-
+
+
+
/**
* check for timer to execute immediatly.
*/
void SingleTimerShot()
{
CPPUNIT_ASSERT(backend);
-
+
TestTimer timer;
timer.setDelta(0); // shot now!
-
+
bool result = backend->doOneStep(10);
-
+
CPPUNIT_ASSERT_EQUAL( true, result );
// the timer should be executed once:
CPPUNIT_ASSERT_EQUAL( 1u, timer.m_counter );
-
+
result = backend->doOneStep(0);
-
+
CPPUNIT_ASSERT_EQUAL( false, result );
// the timer should not be executed again:
CPPUNIT_ASSERT_EQUAL( 1u, timer.m_counter );
-
+
} // eo SingleTimerShot()
-
-
-
+
+
+
/**
* tests a simple timer class to be executed with a timeout.
*/
{
bool res;
CPPUNIT_ASSERT(backend);
-
+
SimpleTimer timer1;
Counter counter1;
timer1.addAction( boost::bind(&Counter::advance,&counter1) );
CPPUNIT_ASSERT_EQUAL(false, timer1.active());
-
+
timer1.startTimerMS( 100 );
CPPUNIT_ASSERT_EQUAL(true, timer1.active());
-
+
res=backend->doOneStep( 1000 );
CPPUNIT_ASSERT_EQUAL( true, res );
-
+
CPPUNIT_ASSERT_EQUAL( 1, counter1.value );
} // eo SimpleTimerShot
-
-
-
+
+
+
/**
* tests 3 timers; after the first was active, disable another and check if the remaining one fires.
*/
{
bool res;
CPPUNIT_ASSERT(backend);
-
+
SimpleTimer timer1, timer2, timer3;
Counter counter1, counter2, counter3;
timer1.addAction( boost::bind(&Counter::advance,&counter1) );
CPPUNIT_ASSERT_EQUAL(false, timer1.active());
CPPUNIT_ASSERT_EQUAL(false, timer2.active());
CPPUNIT_ASSERT_EQUAL(false, timer3.active());
-
+
timer1.startTimerMS( 100 );
timer2.startTimerMS( 500 );
timer3.startTimerMS( 400 );
CPPUNIT_ASSERT_EQUAL(true, timer1.active());
CPPUNIT_ASSERT_EQUAL(true, timer2.active());
CPPUNIT_ASSERT_EQUAL(true, timer3.active());
-
+
res=backend->doOneStep( 1000 );
CPPUNIT_ASSERT_EQUAL( true, res );
-
+
CPPUNIT_ASSERT_EQUAL(false, timer1.active());
CPPUNIT_ASSERT_EQUAL(true, timer2.active());
CPPUNIT_ASSERT_EQUAL(true, timer3.active());
-
+
CPPUNIT_ASSERT_EQUAL( 1, counter1.value );
CPPUNIT_ASSERT_EQUAL( 0, counter2.value );
CPPUNIT_ASSERT_EQUAL( 0, counter3.value );
-
+
// now stop the next timer:
timer3.stopTimer();
CPPUNIT_ASSERT_EQUAL(false, timer3.active());
-
+
res=backend->doOneStep( 1000 );
CPPUNIT_ASSERT_EQUAL( true, res );
-
+
CPPUNIT_ASSERT_EQUAL( 1, counter1.value );
CPPUNIT_ASSERT_EQUAL( 1, counter2.value );
CPPUNIT_ASSERT_EQUAL( 0, counter3.value );
} // eo SimpleTimerShot2
-
-
-
-
+
+
+
+
/*
** I/O tests:
*/
-
+
void EmptyWantTest()
{
IOImplementation io;
-
+
CPPUNIT_ASSERT_EQUAL(false, io.wantRead() );
CPPUNIT_ASSERT_EQUAL(false, io.wantWrite() );
} // eo EmptyWantTest
-
-
+
+
/**
* a simple pipe (and io) test.
*
* - signal chains for received data
* - eof detection
* .
- *
+ *
*/
void SimplePipeTest()
{
static const std::string test_string("a test string");
static const std::string test_string2("only another short test string");
-
+
CPPUNIT_ASSERT(backend);
-
+
TestPipe pipe1, pipe2;
-
+
bool res= pipe1.makePipe(pipe2);
-
+
CPPUNIT_ASSERT_EQUAL(true, res);
CPPUNIT_ASSERT_EQUAL(true, pipe1.opened());
CPPUNIT_ASSERT_EQUAL(true, pipe2.opened());
-
+
res= backend->doOneStep(0);
CPPUNIT_ASSERT_EQUAL(true, res);
-
+
pipe1.sendString(test_string);
-
+
res= backend->doOneStep(0);
CPPUNIT_ASSERT_EQUAL(true, res);
-
+
CPPUNIT_ASSERT_EQUAL( test_string, pipe2.m_received_string );
-
+
pipe2.sendString(test_string2);
-
+
res= backend->doOneStep(0);
CPPUNIT_ASSERT_EQUAL(true, res);
-
+
CPPUNIT_ASSERT_EQUAL( test_string2, pipe1.m_received_string );
-
+
pipe1.close();
CPPUNIT_ASSERT_EQUAL(false, pipe1.opened());
-
+
res= backend->doOneStep(0);
CPPUNIT_ASSERT_EQUAL(true, res);
-
+
CPPUNIT_ASSERT_EQUAL(true, pipe2.eof());
} // eo SimplePipeTest
-
-
-
+
+
+
/**
* sends a larger data chunk through a pipe.
* This tests if sending and receiving data in (smaller internal) chunks works.
void SimplePipePump()
{
CPPUNIT_ASSERT(backend);
-
+
TestPipe pipe1, pipe2;
-
+
bool res= pipe1.makePipe(pipe2);
-
+
CPPUNIT_ASSERT_EQUAL(true, res);
CPPUNIT_ASSERT_EQUAL(true, pipe1.opened());
CPPUNIT_ASSERT_EQUAL(true, pipe2.opened());
-
+
res= backend->doOneStep(0);
CPPUNIT_ASSERT_EQUAL(true, res);
-
+
std::string test_string= makeRandomAsciiString(256*1024);
-
+
pipe1.sendString(test_string);
-
+
res= backend->doOneStep(0);
CPPUNIT_ASSERT_EQUAL(true, res);
-
+
// do some backend cycles to empty the pipe:
for (int i=32; i-->0 && res && !pipe1.empty(); )
{
res= backend->doOneStep(100);
};
-
+
pipe1.close();
CPPUNIT_ASSERT_EQUAL(false, pipe1.opened());
-
+
// now read the remaining data until we recognize EOF:
for (int i=32; i-->0 && res && !pipe2.eof();)
{
res= backend->doOneStep(100);
}
-
+
CPPUNIT_ASSERT_EQUAL( test_string.size(), pipe2.m_received_string.size() );
CPPUNIT_ASSERT_EQUAL( test_string, pipe2.m_received_string );
-
+
CPPUNIT_ASSERT_EQUAL(true, pipe2.eof());
} // eo SimplePipePump
-
-
-
+
+
+
/**
* fork a subprocess (/bin/true) and test exit code.
*/
{
bool res;
CPPUNIT_ASSERT(backend);
-
+
TestProcess proc("/bin/true");
-
+
res= proc.startProcess();
CPPUNIT_ASSERT_EQUAL(true, res);
-
+
res= backend->doOneStep(200);
CPPUNIT_ASSERT_EQUAL(true, res);
-
+
for(int i=20; i-->0 && proc.processState() != ProcessState::stopped;)
{
backend->doOneStep(15);
}
-
+
CPPUNIT_ASSERT_EQUAL( ProcessState(ProcessState::stopped), proc.processState() );
CPPUNIT_ASSERT_EQUAL( true, proc.eof() );
CPPUNIT_ASSERT_EQUAL( 0, proc.exitCode() );
} // eo SimpleProcessTestBinTrue
-
-
+
+
/**
* fork a subprocess (/bin/false) and test exit code.
*/
{
bool res;
CPPUNIT_ASSERT(backend);
-
+
TestProcess proc("/bin/false");
-
+
res= proc.startProcess();
CPPUNIT_ASSERT_EQUAL(true, res);
-
+
res= backend->doOneStep(200);
CPPUNIT_ASSERT_EQUAL(true, res);
for(int i=20; i-->0 && proc.processState() != ProcessState::stopped;)
{
backend->doOneStep(15);
}
-
+
CPPUNIT_ASSERT_EQUAL( ProcessState(ProcessState::stopped), proc.processState() );
CPPUNIT_ASSERT_EQUAL( true, proc.eof() );
CPPUNIT_ASSERT_EQUAL( 1, proc.exitCode() );
DOUT("leave SimpleProcessTestBinFalse");
} // eo SimpleProcessTestBinFalse
-
-
+
+
/**
* fork an echo subprocess and read back the output.
*/
DOUT("enter SimpleProcessTestEcho");
bool res;
CPPUNIT_ASSERT(backend);
-
+
TestProcess proc(
"/bin/echo",
TransientPushBackFiller<std::string, std::vector >()("Eine")("Zeichenkette")
);
-
+
res= proc.startProcess();
CPPUNIT_ASSERT_EQUAL(true, res);
-
+
res= backend->doOneStep(200);
CPPUNIT_ASSERT_EQUAL(true, res);
for(int i=20; i-->0 && (proc.processState()!= ProcessState::stopped || !proc.eof());)
{
backend->doOneStep(10);
}
-
+
CPPUNIT_ASSERT_EQUAL( ProcessState(ProcessState::stopped), proc.processState() );
CPPUNIT_ASSERT_EQUAL( true, proc.eof() );
CPPUNIT_ASSERT_EQUAL( 0, proc.exitCode() );
CPPUNIT_ASSERT_EQUAL( std::string("Eine Zeichenkette\n"), proc.m_received_string);
} // eo SimpleProcessTestEcho
-
-
-
+
+
+
/**
* fork a bash subprocess, echo something on stderr and read back the output.
*/
CPPUNIT_ASSERT_EQUAL( std::string("Eine Zeichenkette\n"), my_stderr.m_received_string);
DOUT("leave Test SimpleProcessTestStderr");
} // eo SimpleProcessTestStderr
-
-
-
+
+
+
/**
* checks termination of process by signal and if the signal is returned.
*/
{
bool res;
CPPUNIT_ASSERT(backend);
-
+
TestProcess proc("/bin/sleep","2");
res= proc.startProcess();
CPPUNIT_ASSERT_EQUAL(true, res);
-
+
res= backend->doOneStep(10);
CPPUNIT_ASSERT_EQUAL(true, res);
CPPUNIT_ASSERT_EQUAL( ProcessState(ProcessState::running), proc.processState() );
-
+
res= backend->doOneStep(50);
-
+
// now send the process an USR1 (which terminates the process)
res=proc.kill( Signal::USR1 );
CPPUNIT_ASSERT_EQUAL(true, res);
-
+
// give the backend a chance to process the termination event:
for(int i=30; i-->0 && proc.processState()!=ProcessState::stopped;) backend->doOneStep(10);
-
+
CPPUNIT_ASSERT_EQUAL( ProcessState(ProcessState::stopped), proc.processState() );
CPPUNIT_ASSERT_EQUAL( true, proc.eof() );
CPPUNIT_ASSERT_EQUAL( Signal::USR1 , proc.exitCode()>>8 );
} // eo SignaledProcessTermination
-
-
-
+
+
+
void CallOut1()
{
Counter count;
-
+
callOut( boost::bind(&Counter::advance, &count), 1 );
backend->doOneStep( 10 );
-
+
CPPUNIT_ASSERT_EQUAL( 0, count.value );
backend->doOneStep( 1100 );
-
+
CPPUNIT_ASSERT_EQUAL( 1, count.value );
} // eo CallOut1()
-
-
-
+
+
+
void CallOut2()
{
Counter count;
-
+
callOut( boost::bind(&Counter::advance, &count), 0.5 );
backend->doOneStep( 10 );
-
+
CPPUNIT_ASSERT_EQUAL( 0, count.value );
backend->doOneStep( 800 );
-
+
CPPUNIT_ASSERT_EQUAL( 1, count.value );
} // eo CallOut2()
-
-
-
+
+
+
void RemoveCallOut1()
{
Counter count;
-
+
CallOutId id= callOut( boost::bind(&Counter::advance, &count), 1 );
backend->doOneStep( 10 );
-
+
CPPUNIT_ASSERT_EQUAL( 0, count.value );
bool res1 = removeCallOut(id);
bool res2 = removeCallOut(id);
-
+
CPPUNIT_ASSERT_EQUAL( true, res1 );
CPPUNIT_ASSERT_EQUAL( false, res2 );
-
+
backend->doOneStep( 1100 );
-
+
CPPUNIT_ASSERT_EQUAL( 0, count.value );
} // eo RemoveCallOut1()
-
-
-
+
+
+
void FrozenCall_Thaw()
{
Counter count;
-
+
CallOutId id= frozenCall( boost::bind(&Counter::advance, &count), 1 );
backend->doOneStep( 10 );
-
+
CPPUNIT_ASSERT_EQUAL( 0, count.value );
id.thaw();
-
+
backend->doOneStep( 1100 );
-
+
CPPUNIT_ASSERT_EQUAL( 1, count.value );
} // eo FrozenCall_Thaw()
-
-
-
+
+
+
void FrozenCall_Decay()
{
Counter count;
-
+
CallOutId id= frozenCall( boost::bind(&Counter::advance, &count), 1 );
backend->doOneStep( 10 );
-
+
CPPUNIT_ASSERT_EQUAL( 0, count.value );
CPPUNIT_ASSERT_EQUAL( true, id.active() );
backend->doOneStep( 1100 );
-
+
CPPUNIT_ASSERT_EQUAL( 0, count.value );
CPPUNIT_ASSERT_EQUAL( false, id.active() );
} // eo FrozenCall_Decay()
-
-
-
+
+
+
void UnixSockets_ClientServer()
{
std::string path= getCheckFilepath("UDS_CS");
-
+
UnixIOSocketHolder server_holder;
UnixServerSocket< TestUnixIOSocket > server_port;
UnixIOSocketPtr server;
TestUnixIOSocket client0;
TestUnixIOSocket client1;
-
+
bool res1 = server_port.open(path, 0600);
CPPUNIT_ASSERT_EQUAL( true, res1 );
-
+
{
- Stat stat(path,false);
+ Utils::FileStat stat(path,false);
CPPUNIT_ASSERT( stat.is_socket() );
CPPUNIT_ASSERT_EQUAL( 0600u, (stat.mode() & 0777));
}
-
+
server_port.setNewConnectionCallback(
boost::bind( &UnixIOSocketHolder::store, &server_holder, _1)
);
-
+
// open a first client
bool res2= client0.open(path);
CPPUNIT_ASSERT_EQUAL( true, res2 );
-
+
CPPUNIT_ASSERT_EQUAL(0u, server_holder.size() );
backendStep(5,1);
CPPUNIT_ASSERT_EQUAL(1u, server_holder.size() );
CPPUNIT_ASSERT( server_holder.get(0).get() );
-
+
client0.sendData("a simple test string.");
backendStep(3,2);
-
+
CPPUNIT_ASSERT_EQUAL(
std::string("a simple test string."),
server_holder.get(0)->m_received_string
server_holder.get(0)->sendData("reply 1");
backendStep(3,2);
CPPUNIT_ASSERT_EQUAL( std::string("reply 1"), client0.m_received_string );
-
+
// open a second client
res2= client1.open(path);
CPPUNIT_ASSERT_EQUAL( true, res2 );
backendStep(5,1);
CPPUNIT_ASSERT_EQUAL(2u, server_holder.size() );
CPPUNIT_ASSERT( server_holder.get(1).get() );
-
+
server_holder.get(1)->sendData("::reply 2");
backendStep(3,2);
CPPUNIT_ASSERT_EQUAL( std::string("::reply 2"), client1.m_received_string );
-
+
client1.sendData("another simple test string. 124");
backendStep(3,2);
-
+
CPPUNIT_ASSERT_EQUAL(
std::string("another simple test string. 124"),
server_holder.get(1)->m_received_string
);
-
+
// close first client
client0.close();
CPPUNIT_ASSERT_EQUAL( false, server_holder.get(0)->eof() );
backendStep(3,2);
CPPUNIT_ASSERT_EQUAL( true, server_holder.get(0)->eof() );
server_holder.get(0)->close();
-
+
// close second connection from server side
CPPUNIT_ASSERT_EQUAL( false, client1.eof() );
server_holder.get(1)->close();
CPPUNIT_ASSERT_EQUAL( true, client1.eof() );
client1.close();
} // eo UnixSockets_ClientServer()
-
-
+
+
void Dummy()
{
using namespace std;
cout << " " << makeRandomAsciiString(70)<< endl;
}
} // eo Dummy
-
-
+
+
}; // eo class TestSimpleioBasics