wxWidgets/tests/net/socket.cpp
Brian Nixon 644b99da6c Make wxSocket::Peek() work with UDP too
Fix wxSocketImpl::RecvDgram() when caller requests fewer bytes than
are present in datagram, as we must always read all the available bytes
when using UDP -- they are not buffered by the OS and so won't be
provided by the next Read() call.

See #23594, #23604.

(cherry picked from commit b9d0541f9ac4d4c356b6ab03772c1fe10a2fd1bc)
2023-07-03 13:55:34 +02:00

361 lines
9.5 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: tests/net/socket.cpp
// Purpose: wxSocket unit tests
// Author: Vadim Zeitlin
// Copyright: (c) 2008 Vadim Zeitlin
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
/*
IMPORTANT NOTE: the environment variable WX_TEST_SERVER must be set to the
hostname of the server to use for the tests below, if it is not set all
tests are silently skipped (rationale: this makes it possible to run the
test in the restricted environments (e.g. sandboxes) without any network
connectivity).
*/
// For compilers that support precompilation, includes "wx/wx.h".
// and "wx/cppunit.h"
#include "testprec.h"
#if wxUSE_SOCKETS
#include "wx/socket.h"
#include "wx/url.h"
#include "wx/scopedptr.h"
#include "wx/sstream.h"
#include "wx/evtloop.h"
typedef wxScopedPtr<wxSockAddress> wxSockAddressPtr;
typedef wxScopedPtr<wxSocketClient> wxSocketClientPtr;
static wxString gs_serverHost(wxGetenv("WX_TEST_SERVER"));
class SocketTestCase : public CppUnit::TestCase
{
public:
SocketTestCase() { }
// get the address to connect to, if NULL is returned it means that the
// test is disabled and shouldn't run at all
static wxSockAddress* GetServer();
// get the socket to read HTTP reply from, returns NULL if the test is
// disabled
static wxSocketClient* GetHTTPSocket(int flags = wxSOCKET_NONE);
private:
// we need to repeat the tests twice as the sockets behave differently when
// there is an active event loop and without it
#define ALL_SOCKET_TESTS() \
CPPUNIT_TEST( BlockingConnect ); \
CPPUNIT_TEST( NonblockingConnect ); \
CPPUNIT_TEST( ReadNormal ); \
CPPUNIT_TEST( ReadBlock ); \
CPPUNIT_TEST( ReadNowait ); \
CPPUNIT_TEST( ReadWaitall ); \
CPPUNIT_TEST( ReadAnotherThread ); \
CPPUNIT_TEST( UrlTest )
CPPUNIT_TEST_SUITE( SocketTestCase );
ALL_SOCKET_TESTS();
CPPUNIT_TEST( PseudoTest_SetUseEventLoop );
ALL_SOCKET_TESTS();
CPPUNIT_TEST_SUITE_END();
// helper event loop class which sets itself as active only if we pass it
// true in ctor
class SocketTestEventLoop : public wxEventLoop
{
public:
SocketTestEventLoop(bool useLoop)
{
m_useLoop = useLoop;
if ( useLoop )
{
m_evtLoopOld = wxEventLoopBase::GetActive();
SetActive(this);
}
}
virtual ~SocketTestEventLoop()
{
if ( m_useLoop )
{
wxEventLoopBase::SetActive(m_evtLoopOld);
}
}
private:
bool m_useLoop;
wxEventLoopBase *m_evtLoopOld;
};
void PseudoTest_SetUseEventLoop() { ms_useLoop = true; }
void BlockingConnect();
void NonblockingConnect();
void ReadNormal();
void ReadBlock();
void ReadNowait();
void ReadWaitall();
void ReadAnotherThread();
void UrlTest();
static bool ms_useLoop;
wxDECLARE_NO_COPY_CLASS(SocketTestCase);
};
bool SocketTestCase::ms_useLoop = false;
CPPUNIT_TEST_SUITE_REGISTRATION( SocketTestCase );
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( SocketTestCase, "SocketTestCase" );
wxSockAddress* SocketTestCase::GetServer()
{
if ( gs_serverHost.empty() )
return NULL;
wxIPV4address *addr = new wxIPV4address;
addr->Hostname(gs_serverHost);
addr->Service("www");
return addr;
}
wxSocketClient* SocketTestCase::GetHTTPSocket(int flags)
{
wxSockAddress *addr = GetServer();
if ( !addr )
return NULL;
wxSocketClient *sock = new wxSocketClient(flags);
sock->SetTimeout(1);
CPPUNIT_ASSERT( sock->Connect(*addr) );
const wxString httpGetRoot =
"GET / HTTP/1.1\r\n"
"Host: " + gs_serverHost + "\r\n"
"\r\n";
sock->Write(httpGetRoot.ToAscii(), httpGetRoot.length());
return sock;
}
void SocketTestCase::BlockingConnect()
{
wxSockAddressPtr addr(GetServer());
if ( !addr.get() )
return;
wxSocketClient sock;
CPPUNIT_ASSERT( sock.Connect(*addr) );
}
void SocketTestCase::NonblockingConnect()
{
wxSockAddressPtr addr(GetServer());
if ( !addr.get() )
return;
SocketTestEventLoop loop(ms_useLoop);
wxSocketClient sock;
sock.Connect(*addr, false);
sock.WaitOnConnect(10);
CPPUNIT_ASSERT( sock.IsConnected() );
}
void SocketTestCase::ReadNormal()
{
SocketTestEventLoop loop(ms_useLoop);
wxSocketClientPtr sock(GetHTTPSocket());
if ( !sock.get() )
return;
char bufSmall[128];
sock->Read(bufSmall, WXSIZEOF(bufSmall));
CPPUNIT_ASSERT_EQUAL( wxSOCKET_NOERROR, sock->LastError() );
CPPUNIT_ASSERT_EQUAL( WXSIZEOF(bufSmall), (size_t)sock->LastCount() );
CPPUNIT_ASSERT_EQUAL( WXSIZEOF(bufSmall), (size_t)sock->LastReadCount() );
char bufBig[102400];
sock->Read(bufBig, WXSIZEOF(bufBig));
CPPUNIT_ASSERT_EQUAL( wxSOCKET_NOERROR, sock->LastError() );
CPPUNIT_ASSERT( WXSIZEOF(bufBig) >= sock->LastCount() );
CPPUNIT_ASSERT( WXSIZEOF(bufBig) >= sock->LastReadCount() );
}
void SocketTestCase::ReadBlock()
{
wxSocketClientPtr sock(GetHTTPSocket(wxSOCKET_BLOCK));
if ( !sock.get() )
return;
char bufSmall[128];
sock->Read(bufSmall, WXSIZEOF(bufSmall));
CPPUNIT_ASSERT_EQUAL( wxSOCKET_NOERROR, sock->LastError() );
CPPUNIT_ASSERT_EQUAL( WXSIZEOF(bufSmall), (size_t)sock->LastCount() );
CPPUNIT_ASSERT_EQUAL( WXSIZEOF(bufSmall), (size_t)sock->LastReadCount() );
char bufBig[102400];
sock->Read(bufBig, WXSIZEOF(bufBig));
CPPUNIT_ASSERT_EQUAL( wxSOCKET_NOERROR, sock->LastError() );
CPPUNIT_ASSERT( WXSIZEOF(bufBig) >= sock->LastCount() );
CPPUNIT_ASSERT( WXSIZEOF(bufBig) >= sock->LastReadCount() );
}
void SocketTestCase::ReadNowait()
{
wxSocketClientPtr sock(GetHTTPSocket(wxSOCKET_NOWAIT));
if ( !sock.get() )
return;
char buf[1024];
sock->Read(buf, WXSIZEOF(buf));
if ( sock->LastError() != wxSOCKET_WOULDBLOCK )
{
CPPUNIT_ASSERT_EQUAL( wxSOCKET_NOERROR, sock->LastError() );
}
}
void SocketTestCase::ReadWaitall()
{
SocketTestEventLoop loop(ms_useLoop);
wxSocketClientPtr sock(GetHTTPSocket(wxSOCKET_WAITALL));
if ( !sock.get() )
return;
char buf[128];
sock->Read(buf, WXSIZEOF(buf));
CPPUNIT_ASSERT_EQUAL( wxSOCKET_NOERROR, sock->LastError() );
CPPUNIT_ASSERT_EQUAL( WXSIZEOF(buf), (size_t)sock->LastCount() );
CPPUNIT_ASSERT_EQUAL( WXSIZEOF(buf), (size_t)sock->LastReadCount() );
}
void SocketTestCase::ReadAnotherThread()
{
class SocketThread : public wxThread
{
public:
SocketThread()
: wxThread(wxTHREAD_JOINABLE)
{
}
virtual void* Entry() wxOVERRIDE
{
wxSocketClientPtr sock(SocketTestCase::GetHTTPSocket(wxSOCKET_BLOCK));
if ( !sock )
return NULL;
char bufSmall[128];
sock->Read(bufSmall, WXSIZEOF(bufSmall));
REQUIRE( sock->LastError() == wxSOCKET_NOERROR );
CHECK( sock->LastCount() == WXSIZEOF(bufSmall) );
CHECK( sock->LastReadCount() == WXSIZEOF(bufSmall) );
REQUIRE_NOTHROW( sock.reset() );
return NULL;
}
};
SocketThread thr;
SocketTestEventLoop loop(ms_useLoop);
thr.Run();
CHECK( thr.Wait() == NULL );
}
void SocketTestCase::UrlTest()
{
if ( gs_serverHost.empty() )
return;
SocketTestEventLoop loop(ms_useLoop);
wxURL url("http://" + gs_serverHost);
const wxScopedPtr<wxInputStream> in(url.GetInputStream());
CPPUNIT_ASSERT( in.get() );
wxStringOutputStream out;
CPPUNIT_ASSERT_EQUAL( wxSTREAM_EOF, in->Read(out).GetLastError() );
}
TEST_CASE("wxDatagramSocket::ShortRead", "[socket][dgram]")
{
// Check that reading fewer bytes than are present in a
// datagram does not leave the socket in an error state
wxIPV4address addr;
addr.LocalHost();
addr.Service(19898);// Arbitrary port number
wxDatagramSocket sock(addr);
// Send ourselves a datagram
unsigned int sendbuf[4] = {1, 2, 3, 4};
sock.SendTo(addr, sendbuf, sizeof(sendbuf));
// Read less than we know we sent
unsigned int recvbuf[1] = {0};
sock.Read(recvbuf, sizeof(recvbuf));
CHECK(!sock.Error());
CHECK(sock.LastReadCount() == sizeof(recvbuf));
CHECK(recvbuf[0] == sendbuf[0]);
}
TEST_CASE("wxDatagramSocket::ShortPeek", "[socket][dgram]")
{
// Check that peeking fewer bytes than are present in a datagram
// does not lose the rest of the data in that datagram (#23594)
wxIPV4address addr;
addr.LocalHost();
addr.Service(27384);// Arbitrary port number
wxDatagramSocket sock(addr);
// Send ourselves 2 datagrams
unsigned int sendbuf1[2] = {1, 2};
sock.SendTo(addr, sendbuf1, sizeof(sendbuf1));
unsigned int sendbuf2[2] = {3, 4};
sock.SendTo(addr, sendbuf2, sizeof(sendbuf2));
long timeout_s = 1;
if ( !sock.WaitForRead(timeout_s) )
return;
// Peek the first word
unsigned int peekbuf[1] = {0};
sock.Peek(peekbuf, sizeof(peekbuf));
CHECK(sock.LastCount() == sizeof(peekbuf));
CHECK(peekbuf[0] == sendbuf1[0]);
// Read the whole of the first datagram
unsigned int recvbuf[2] = {0};
sock.Read(recvbuf, sizeof(recvbuf));
CHECK(sock.LastReadCount() == sizeof(recvbuf));
CHECK(recvbuf[0] == sendbuf1[0]);
CHECK(recvbuf[1] == sendbuf1[1]);
}
#endif // wxUSE_SOCKETS