]>
Commit | Line | Data |
---|---|---|
f9caf1af VZ |
1 | /////////////////////////////////////////////////////////////////////////////// |
2 | // Name: tests/streams/socketstream.cpp | |
3 | // Purpose: Test wxSocketInputStream/wxSocketOutputStream | |
4 | // Author: Vadim Zeitlin | |
5 | // RCS-ID: $Id$ | |
6 | // Copyright: (c) 2008 Vadim Zeitlin | |
7 | // Licence: wxWidgets licence | |
8 | /////////////////////////////////////////////////////////////////////////////// | |
9 | ||
10 | // For compilers that support precompilation, includes "wx/wx.h". | |
11 | // and "wx/cppunit.h" | |
12 | #include "testprec.h" | |
13 | ||
14 | #ifdef __BORLANDC__ | |
15 | #pragma hdrstop | |
16 | #endif | |
17 | ||
18 | // for all others, include the necessary headers | |
19 | #ifndef WX_PRECOMP | |
20 | #include "wx/log.h" | |
21 | #endif | |
22 | ||
23 | #include "wx/socket.h" | |
24 | #include "wx/sckstrm.h" | |
25 | #include "wx/thread.h" | |
26 | ||
27 | #include "bstream.h" | |
28 | ||
29 | namespace | |
30 | { | |
31 | ||
32 | const int TEST_PORT_READ = 0x7778; // arbitrary, chosen because == "wx" | |
33 | const int TEST_PORT_WRITE = 0x7779; // well, "wy" | |
34 | ||
35 | // these cond and mutex are used to minimize the risk of the main thread | |
36 | // Connect()-ing before this thread starts Accept()-ing connections but | |
37 | // unfortunately we can't make this truly safe, see comment in | |
38 | // SocketServerThread::Entry() | |
39 | wxMutex gs_mutex; | |
40 | wxCondition gs_cond(gs_mutex); | |
41 | } // anonymous namespace | |
42 | ||
43 | // return address for the given port on local host | |
44 | static inline wxIPV4address LocalAddress(int port) | |
45 | { | |
46 | wxIPV4address addr; | |
47 | addr.LocalHost(); | |
48 | addr.Service(port); | |
49 | ||
50 | return addr; | |
51 | } | |
52 | ||
53 | // A thread which creates a listening socket on the specified port and executes | |
54 | // the given function with each socket which connects to it | |
55 | class SocketServerThread : public wxThread | |
56 | { | |
57 | public: | |
58 | // port is the port to listen on and function will be called on each | |
59 | // accepted socket | |
60 | SocketServerThread(int port, void (*accept)(wxSocketBase&)) | |
61 | : wxThread(wxTHREAD_JOINABLE), | |
62 | m_port(port), | |
63 | m_accept(accept) | |
64 | { | |
65 | Create(); | |
66 | Run(); | |
67 | } | |
68 | ||
69 | protected: | |
70 | virtual void *Entry() | |
71 | { | |
72 | wxSocketServer srv(LocalAddress(m_port), wxSOCKET_REUSEADDR); | |
73 | ||
74 | // FIXME: this is still not atomic, of course and the main thread could | |
75 | // call Connect() before we have time to Accept() but there is | |
76 | // no way to fix it with current API | |
77 | { | |
78 | wxMutexLocker lock(gs_mutex); | |
79 | gs_cond.Signal(); | |
80 | } | |
81 | ||
82 | wxSocketBase *socket = srv.Accept(); | |
83 | if ( socket ) | |
c515e20b | 84 | { |
f9caf1af | 85 | (*m_accept)(*socket); |
c515e20b VZ |
86 | delete socket; |
87 | } | |
f9caf1af VZ |
88 | |
89 | return NULL; | |
90 | } | |
91 | ||
92 | int m_port; | |
93 | void (*m_accept)(wxSocketBase&); | |
94 | ||
95 | DECLARE_NO_COPY_CLASS(SocketServerThread) | |
96 | }; | |
97 | ||
98 | // The test case for socket streams | |
99 | class socketStream : | |
100 | public BaseStreamTestCase<wxSocketInputStream, wxSocketOutputStream> | |
101 | { | |
102 | public: | |
103 | socketStream(); | |
104 | virtual ~socketStream(); | |
105 | ||
106 | virtual void setUp(); | |
107 | virtual void tearDown(); | |
108 | ||
e6f68879 VZ |
109 | // repeat all socket tests several times with different socket flags, so we |
110 | // define this macro which is used several times in the test suite | |
111 | // | |
112 | // there must be some more elegant way to do this but I didn't find it... | |
113 | #define ALL_SOCKET_TESTS() \ | |
114 | CPPUNIT_TEST(Input_GetC); \ | |
115 | CPPUNIT_TEST(Input_Eof); \ | |
116 | CPPUNIT_TEST(Input_Read); \ | |
117 | CPPUNIT_TEST(Input_LastRead); \ | |
118 | CPPUNIT_TEST(Input_CanRead); \ | |
119 | CPPUNIT_TEST(Input_Peek); \ | |
120 | CPPUNIT_TEST(Input_Ungetch); \ | |
121 | \ | |
122 | CPPUNIT_TEST(Output_PutC); \ | |
123 | CPPUNIT_TEST(Output_Write); \ | |
124 | CPPUNIT_TEST(Output_LastWrite) | |
125 | ||
f9caf1af | 126 | CPPUNIT_TEST_SUITE(socketStream); |
e6f68879 VZ |
127 | ALL_SOCKET_TESTS(); |
128 | CPPUNIT_TEST( PseudoTest_SetNoWait ); | |
129 | ALL_SOCKET_TESTS(); | |
130 | CPPUNIT_TEST( PseudoTest_SetWaitAll ); | |
131 | ALL_SOCKET_TESTS(); | |
f9caf1af VZ |
132 | CPPUNIT_TEST_SUITE_END(); |
133 | ||
134 | private: | |
135 | // Implement base class functions. | |
136 | virtual wxSocketInputStream *DoCreateInStream(); | |
137 | virtual wxSocketOutputStream *DoCreateOutStream(); | |
138 | ||
139 | // socket thread functions | |
140 | static void WriteSocket(wxSocketBase& socket) | |
141 | { | |
142 | socket.Write("hello, world!", 13); | |
143 | } | |
144 | ||
145 | static void ReadSocket(wxSocketBase& socket) | |
146 | { | |
147 | char ch; | |
148 | while ( socket.Read(&ch, 1).LastCount() == 1 ) | |
149 | ; | |
150 | } | |
151 | ||
e6f68879 VZ |
152 | void PseudoTest_SetNoWait() { m_flags = wxSOCKET_NOWAIT; } |
153 | void PseudoTest_SetWaitAll() { m_flags = wxSOCKET_WAITALL; } | |
154 | ||
f9caf1af VZ |
155 | wxSocketClient *m_readSocket, |
156 | *m_writeSocket; | |
157 | wxThread *m_writeThread, | |
158 | *m_readThread; | |
e6f68879 VZ |
159 | |
160 | wxSocketFlags m_flags; | |
f9caf1af VZ |
161 | }; |
162 | ||
163 | socketStream::socketStream() | |
164 | { | |
165 | m_readSocket = | |
166 | m_writeSocket = NULL; | |
167 | ||
168 | m_writeThread = | |
169 | m_readThread = NULL; | |
170 | ||
e6f68879 VZ |
171 | m_flags = wxSOCKET_NONE; |
172 | ||
41cef82a | 173 | wxSocketBase::Initialize(); |
f9caf1af VZ |
174 | } |
175 | ||
176 | socketStream::~socketStream() | |
177 | { | |
41cef82a | 178 | wxSocketBase::Shutdown(); |
f9caf1af VZ |
179 | } |
180 | ||
181 | void socketStream::setUp() | |
182 | { | |
183 | // create the socket threads and wait until they are ready to accept | |
184 | // connections (if we called Connect() before this happens, it would fail) | |
185 | { | |
186 | wxMutexLocker lock(gs_mutex); | |
187 | ||
188 | m_writeThread = | |
189 | new SocketServerThread(TEST_PORT_READ, &socketStream::WriteSocket); | |
190 | CPPUNIT_ASSERT_EQUAL( wxCOND_NO_ERROR, gs_cond.Wait() ); | |
191 | ||
192 | m_readThread = | |
193 | new SocketServerThread(TEST_PORT_WRITE, &socketStream::ReadSocket); | |
194 | CPPUNIT_ASSERT_EQUAL( wxCOND_NO_ERROR, gs_cond.Wait() ); | |
195 | } | |
196 | ||
e6f68879 | 197 | m_readSocket = new wxSocketClient(m_flags); |
f9caf1af VZ |
198 | CPPUNIT_ASSERT( m_readSocket->Connect(LocalAddress(TEST_PORT_READ)) ); |
199 | ||
e6f68879 | 200 | m_writeSocket = new wxSocketClient(m_flags); |
f9caf1af VZ |
201 | CPPUNIT_ASSERT( m_writeSocket->Connect(LocalAddress(TEST_PORT_WRITE)) ); |
202 | } | |
203 | ||
204 | void socketStream::tearDown() | |
205 | { | |
206 | wxDELETE(m_readSocket); | |
207 | wxDELETE(m_writeSocket); | |
208 | ||
209 | m_writeThread->Wait(); | |
210 | wxDELETE(m_writeThread); | |
211 | ||
212 | m_readThread->Wait(); | |
213 | wxDELETE(m_readThread); | |
214 | } | |
215 | ||
216 | wxSocketInputStream *socketStream::DoCreateInStream() | |
217 | { | |
218 | wxSocketInputStream *pStrInStream = new wxSocketInputStream(*m_readSocket); | |
219 | CPPUNIT_ASSERT(pStrInStream->IsOk()); | |
220 | return pStrInStream; | |
221 | } | |
222 | ||
223 | wxSocketOutputStream *socketStream::DoCreateOutStream() | |
224 | { | |
225 | wxSocketOutputStream *pStrOutStream = new wxSocketOutputStream(*m_writeSocket); | |
226 | CPPUNIT_ASSERT(pStrOutStream->IsOk()); | |
227 | return pStrOutStream; | |
228 | } | |
229 | ||
230 | // Register the stream sub suite, by using some stream helper macro. | |
231 | STREAM_TEST_SUBSUITE_NAMED_REGISTRATION(socketStream) |