diff options
Diffstat (limited to 'ACE/tests/IOStream_Test.cpp')
-rw-r--r-- | ACE/tests/IOStream_Test.cpp | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/ACE/tests/IOStream_Test.cpp b/ACE/tests/IOStream_Test.cpp new file mode 100644 index 00000000000..cd3c42a46a0 --- /dev/null +++ b/ACE/tests/IOStream_Test.cpp @@ -0,0 +1,502 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// IOStream_Test.cpp +// +// = DESCRIPTION +// This is a simple test of the IOStream class that illustrates +// how to use iostream operations on almost arbitrary I/O classes. +// +// = AUTHOR +// James CE Johnson <jcej@lads.com> +// +// ============================================================================ + +#include "test_config.h" +#include "ace/Thread.h" +#include "ace/Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/IOStream.h" +#include "ace/OS_NS_sys_wait.h" + +ACE_RCSID (tests, IOStream_Test, "$Id$") + +#if !defined (ACE_LACKS_ACE_IOSTREAM) +# include "ace/OS_NS_unistd.h" +# include "ace/os_include/os_ctype.h" // Needed for isspace() function + +typedef ACE_IOStream<ACE_SOCK_Stream> ACE_SOCK_IOStream; + +/* The biggest drawback to an iostream is that it generally + eats up whitespace when performing a get (>>) operation. + + That may be good if you're reading non-textual data but + if you're trying to read a stream of words with embedded + whitespace, it isn't going to be pleasant. + + If you've been blessed with the GNU String class, I've + already provided a derived class, QuotedString, that + makes dealing with strings very easy. + + If you're stuck with an array of characters then you + will probably need somthing like I have below. + + On the other hand, one of the biggest advantages to an + iostream is that it eats up whitespace :-) + + If you put (<<) your non-textual data to the iostream + with any number of whitespace between the data then + you can easily get (>>) the data from the iostream + without having to worry about delimeters and such. + + The main thing to keep in mind when using an iostream + between peers is that you MUST keep the data "fields" + in sync. That is, if the "putter" puts an int followed + by a float followed by a double, you must make sure + that the "getter" will be attempting to get an int + then a float then a double. + */ + +// Since I can't rely on GNU's String class being everywhere (yet), +// here's a simple class that will work with quoted strings. Use at +// your own risk! It is very incomplete! + +class qchar +{ +public: + qchar (void) { c_ = '\0'; } + + qchar (char c) : c_ (c) { }; + + operator char () const { return c_; } + + qchar operator= (char c) { return c_ = c; } + + bool operator== (char c) { return c_ == c; } + + friend ACE_SOCK_IOStream &operator>> (ACE_SOCK_IOStream & stream, qchar * buf); + friend ACE_SOCK_IOStream &operator<< (ACE_SOCK_IOStream & stream, qchar * buf); + friend ostream &operator<< (ostream & stream, qchar * buf); + +private: + char c_; +}; + +// This is taken almost directly from the QuotedString object that has +// been derived from GNU's String class. The advantage to using +// QuotedString is that it is MUCH more robust than qchar will every +// be. + +ACE_SOCK_IOStream & +operator>> (ACE_SOCK_IOStream & stream, qchar *buf) +{ + char c; + + *buf = '\0'; // Initialize the string + + stream.get (c); + + if (!stream) // eat space up to the first char + return stream; + + // if we don't have a quote, append until we see space + if (c != '"') + for (*buf++ = c; +#ifdef CHORUS + stream.get (c) && !isspace (c); +#else + (void *) stream.get (c) && !isspace (c); +#endif /* CHORUS */ + *buf++ = c) + continue; + else +#ifdef CHORUS + for (; stream.get (c) && c != '"'; *buf++ = c) +#else + for (; (void *) stream.get (c) && c != '"'; *buf++ = c) +#endif /* CHORUS */ + if (c == '\\') + { + stream.get (c); + if (c != '"') + *buf++ = '\\'; + } + + *buf = '\0'; + + return stream; +} + +ACE_SOCK_IOStream & +operator<< (ACE_SOCK_IOStream &stream, qchar *buf) +{ + stream.put ('"'); + + while (*buf) + { + if (*buf == '"') + stream.put ('\\'); + + stream.put ((char) *buf++); + } + + stream.put ('"'); + + return stream; +} + +ostream & +operator<< (ostream &stream, qchar *buf) +{ + while (*buf) + stream.put ((char) *buf++); + + return stream; +} + +// Our client thread will initiate the test by sending some data to +// the server. + +static void * +client (void *arg = 0) +{ + ACE_UNUSED_ARG (arg); + + // We don't _need_ to dynamically allocate the ACE_SOCK_IOStream. + // But if we don't, it doesn't get destroyed on some platforms, + // e.g., g++ 2.7.2.1 and Sun C++ 4.2 on Solaris 2.5.1. (It does work + // on Linux, so the code seems fine.) If we manage the storage + // ourselves, we _will_ destroy it at the end of this function. + ACE_SOCK_IOStream server; + + ACE_INET_Addr *remote_addr = (ACE_INET_Addr *) arg; + ACE_INET_Addr addr (remote_addr->get_port_number (), + ACE_DEFAULT_SERVER_HOST); + ACE_SOCK_Connector connector; + + if (connector.connect (server, addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%t) %p\n"), + ACE_TEXT ("Failed to connect to server thread")), + 0); + + // Send a string to the server which it can interpret as a qchar[] + const char *str = "\"This is a test string.\""; + ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%P|%t) Client Sending: (%s)\n"), + ACE_TEXT_CHAR_TO_TCHAR (str))); + server << str << endl; + + // Allow the server to get the string and echo it to the user. (The + // iostream doesn't need this, but humans do :) + ACE_OS::sleep (2); + + // Send another string but this time the server will read it as a + // char[]. Notice how the server's output doesn't include all of + // the spaces sent by the client. + + str = "\"THIS IS A TEST STRING.\""; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Client Sending: (%s)\n"), + str)); + server << str << endl; + + // Again, give the server time to display the happenings to the + // user. + ACE_OS::sleep (2); + + // Read from the server an int, float, long, float double. The + // iostream will pull them out by using the whitespace provided by + // the server. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Client Receiving\n"))); + + ACE_Time_Value timeout (2); + ACE_Time_Value *timeoutp = &timeout; + + server >> timeoutp; + + int i; + float f1, f2; + long l; + double d; + + while (! (server >> i)) + { + int eof = server.eof (); + if (eof) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Unrecoverable stream error/eof\n"))); + break; + } + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Recoverable stream error/timed out)\n"))); + server.clear (0); + } + } + + server >> f1; + server >> l; + server >> f2; + server >> d; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Client Received: int %d float %f long %d float %f double %f\n"), + i, + f1, + (int) l, + f2, + d)); + + // Check for proper received values. + ACE_ASSERT (i == 1 && (f1 >= 0.123420 && f1 <= 0.123422) + && l == 666555444 && (f2 >= 23.44 && f2 <= 23.46) + && (d >= -47.1e+9 && d <= -45.9e+9)); + // Reset the precision to limit ourselves to two significant digits. + server.precision (2); + + // Now, make a little change & send 'em back. + i *= -1; server << i << " "; + f1 *= -1.0; server << f1 << " "; + l *= -1; server << l << " "; + f2 *= -1.0; server << f2 << " "; + d *= -1; server << d << " "; + server << endl; + + // Shut down the test. + server.close (); + + return 0; +} + +// Test the server's ability to receive data from the client and then +// begin a two-way conversation. + +static void * +server (void *arg = 0) +{ + // We don't _need_ to dynamically allocate the ACE_SOCK_IOStream. + // But if we don't, it doesn't get destroyed on some platforms, + // e.g., g++ 2.7.2.1 and Sun C++ 4.2 on Solaris 2.5.1. (It does work + // on Linux, so the code seems fine.) If we manage the storage + // ourselves, we _will_ destroy it at the end of this function. + ACE_SOCK_IOStream client_handler; + + ACE_INET_Addr server_addr; + ACE_SOCK_Acceptor *acceptor = + reinterpret_cast<ACE_SOCK_Acceptor *> (arg); + + if (acceptor->get_local_addr (server_addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("get_local_addr")), + 0); + +#if defined (ACE_HAS_THREADS) + if (ACE_Thread_Manager::instance ()->spawn (ACE_THR_FUNC (client), + (void *) &server_addr, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%t) %p\n"), + ACE_TEXT ("spawing client thread")), + 0); +#endif /* ACE_HAS_THREADS */ + + if (acceptor->accept (client_handler) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P|%t) Failed to accept new client_handler")), + 0); + + // Read a qbuf[] from the client. Notice that all of the client's + // whitespace is preserved. + qchar qbuf[BUFSIZ]; + ACE_OS::memset (qbuf, 0, sizeof qbuf); + client_handler >> qbuf; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Server Received: (\"%s\")\n"), + ACE_TEXT_CHAR_TO_TCHAR ((char *) qbuf))); + + // Give the client time to announce the next test to the user. + ACE_OS::sleep (2); + + // Now we try to use a char[] to get a string from the client. + // Compared to the method above, this is quite messy. Notice also + // that whitespace is lost. + +#if defined (ACE_HAS_STRING_CLASS) && defined (ACE_HAS_STANDARD_CPP_LIBRARY) + ACE_IOStream_String buf; + ACE_DEBUG ((LM_DEBUG, + " (%P|%t) Server Received: (")); + + while (client_handler && + (buf.length () == 0 || buf[buf.length () - 1] != '"')) + { + if (! (client_handler >> buf)) + break; + + if (buf.length () > 0) + ACE_DEBUG ((LM_DEBUG, + "%s ", + buf.c_str ())); + } + + ACE_DEBUG ((LM_DEBUG, + ")\n")); +#else + char buf[BUFSIZ]; + ACE_OS::memset (buf, 0, sizeof buf); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Server Received: ("))); + + while (ACE_OS::strlen (buf) == 0 + || buf[ACE_OS::strlen (buf) - 1] != '"') + { + if (! (client_handler >> buf)) + break; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("%s "), + ACE_TEXT_CHAR_TO_TCHAR (buf))); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (")\n"))); +#endif /* ACE_HAS_STRING_CLASS */ + + // Send some non-textual data to the client. We use a single + // character to separate the fields but we could have used any valid + // whitespace. The data will be sent if the iostream's buffer gets + // filled or when we flush it with an explicit client.sync () + // command or the implicit <<endl. + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Server sleeping\n"))); + ACE_OS::sleep (5); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Server Sending: 1 .12342134 666555444 23.45 -46.5e9 \n"))); + client_handler << 1 << " "; + client_handler << .12342134 << " "; + client_handler << 666555444 << " "; + client_handler << 23.45 << " "; + client_handler << -46.5e9 << " "; + client_handler << endl; + + // The client will have changed the sign of each data field and sent + // 'em all back to us. At the same time, the client used the + // precision () function to change the significant digits for + // non-integer values. + int i; + float f1, f2; + long l; + double d; + client_handler >> i >> f1 >> l >> f2 >> d; + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" (%P|%t) Server Received: int %d float %f long %d float %f double %f\n"), + i, + f1, + (int) l, + f2, + d)); + + // check for proper received values + ACE_ASSERT (i == -1 && (f1 >= -0.13 && f1 <= -0.11) + && l == -666555444 && (f2 >= -24.0 && f2 <= -22.0) + && (d >= 45e+9 && d <= 47e+9)); + + client_handler.close (); + + return 0; +} + +static int +spawn (void) +{ + // Acceptor; + ACE_SOCK_Acceptor acceptor; + + if (acceptor.open (ACE_sap_any_cast (const ACE_INET_Addr &)) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT (" (%P|%t) %p\n"), + ACE_TEXT ("open")), + -1); +#if defined (ACE_HAS_THREADS) + else if (ACE_Thread_Manager::instance ()->spawn (ACE_THR_FUNC (server), + &acceptor, + THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("spawning server thread")), + -1); + + // Wait for the client and server thread to exit. + ACE_Thread_Manager::instance ()->wait (); + +#elif !defined (ACE_LACKS_FORK) + + switch (ACE_OS::fork ("child")) + { + case -1: + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n%a"), + ACE_TEXT ("fork failed"))); + ACE_OS::_exit (-1); + case 0: // In child + { + ACE_APPEND_LOG ("IOStream_Test-children"); + ACE_INET_Addr server_addr; + + if (acceptor.get_local_addr (server_addr) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("get_local_addr"))); + else + client ((void *) &server_addr); + ACE_END_LOG; + break; + } + default: // In parent + server (&acceptor); + + // Allow the client to exit, then remove the Process_Mutex. + ACE_OS::wait (); + break; + } +#else + ACE_ERROR_RETURN ((LM_INFO, + ACE_TEXT ("threads *and* processes not supported on this platform\n")), + -1); +#endif /* ACE_HAS_THREADS */ + + acceptor.close (); + + return 0; +} +#endif /* !ACE_LACKS_ACE_IOSTREAM */ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("IOStream_Test")); + +#if !defined (ACE_LACKS_ACE_IOSTREAM) + ACE_INIT_LOG (ACE_TEXT ("IOStream_Test-children")); + spawn (); +#else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("ACE_IOSTREAM not supported on this platform\n"))); +#endif /* !ACE_LACKS_ACE_IOSTREAM */ + ACE_END_TEST; + return 0; +} + |