]>
Commit | Line | Data |
---|---|---|
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: wxWindows 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 ) | |
84 | { | |
85 | (*m_accept)(*socket); | |
86 | delete socket; | |
87 | } | |
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 | ||
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 | ||
126 | CPPUNIT_TEST_SUITE(socketStream); | |
127 | ALL_SOCKET_TESTS(); | |
128 | // some tests don't pass with NOWAIT flag but this is probably not a | |
129 | // bug (TODO: check this) | |
130 | #if 0 | |
131 | CPPUNIT_TEST( PseudoTest_SetNoWait ); | |
132 | ALL_SOCKET_TESTS(); | |
133 | #endif | |
134 | CPPUNIT_TEST( PseudoTest_SetWaitAll ); | |
135 | ALL_SOCKET_TESTS(); | |
136 | CPPUNIT_TEST_SUITE_END(); | |
137 | ||
138 | private: | |
139 | // Implement base class functions. | |
140 | virtual wxSocketInputStream *DoCreateInStream(); | |
141 | virtual wxSocketOutputStream *DoCreateOutStream(); | |
142 | ||
143 | // socket thread functions | |
144 | static void WriteSocket(wxSocketBase& socket) | |
145 | { | |
146 | socket.Write("hello, world!", 13); | |
147 | } | |
148 | ||
149 | static void ReadSocket(wxSocketBase& socket) | |
150 | { | |
151 | char ch; | |
152 | while ( socket.Read(&ch, 1).LastCount() == 1 ) | |
153 | ; | |
154 | } | |
155 | ||
156 | void PseudoTest_SetNoWait() { ms_flags = wxSOCKET_NOWAIT; } | |
157 | void PseudoTest_SetWaitAll() { ms_flags = wxSOCKET_WAITALL; } | |
158 | ||
159 | wxSocketClient *m_readSocket, | |
160 | *m_writeSocket; | |
161 | wxThread *m_writeThread, | |
162 | *m_readThread; | |
163 | ||
164 | static wxSocketFlags ms_flags; | |
165 | }; | |
166 | ||
167 | wxSocketFlags socketStream::ms_flags = wxSOCKET_NONE; | |
168 | ||
169 | socketStream::socketStream() | |
170 | { | |
171 | m_readSocket = | |
172 | m_writeSocket = NULL; | |
173 | ||
174 | m_writeThread = | |
175 | m_readThread = NULL; | |
176 | ||
177 | wxSocketBase::Initialize(); | |
178 | } | |
179 | ||
180 | socketStream::~socketStream() | |
181 | { | |
182 | wxSocketBase::Shutdown(); | |
183 | } | |
184 | ||
185 | void socketStream::setUp() | |
186 | { | |
187 | // create the socket threads and wait until they are ready to accept | |
188 | // connections (if we called Connect() before this happens, it would fail) | |
189 | { | |
190 | wxMutexLocker lock(gs_mutex); | |
191 | ||
192 | m_writeThread = | |
193 | new SocketServerThread(TEST_PORT_READ, &socketStream::WriteSocket); | |
194 | CPPUNIT_ASSERT_EQUAL( wxCOND_NO_ERROR, gs_cond.Wait() ); | |
195 | ||
196 | m_readThread = | |
197 | new SocketServerThread(TEST_PORT_WRITE, &socketStream::ReadSocket); | |
198 | CPPUNIT_ASSERT_EQUAL( wxCOND_NO_ERROR, gs_cond.Wait() ); | |
199 | } | |
200 | ||
201 | m_readSocket = new wxSocketClient(ms_flags); | |
202 | CPPUNIT_ASSERT( m_readSocket->Connect(LocalAddress(TEST_PORT_READ)) ); | |
203 | ||
204 | m_writeSocket = new wxSocketClient(ms_flags); | |
205 | CPPUNIT_ASSERT( m_writeSocket->Connect(LocalAddress(TEST_PORT_WRITE)) ); | |
206 | } | |
207 | ||
208 | void socketStream::tearDown() | |
209 | { | |
210 | wxDELETE(m_readSocket); | |
211 | wxDELETE(m_writeSocket); | |
212 | ||
213 | m_writeThread->Wait(); | |
214 | wxDELETE(m_writeThread); | |
215 | ||
216 | m_readThread->Wait(); | |
217 | wxDELETE(m_readThread); | |
218 | } | |
219 | ||
220 | wxSocketInputStream *socketStream::DoCreateInStream() | |
221 | { | |
222 | wxSocketInputStream *pStrInStream = new wxSocketInputStream(*m_readSocket); | |
223 | CPPUNIT_ASSERT(pStrInStream->IsOk()); | |
224 | return pStrInStream; | |
225 | } | |
226 | ||
227 | wxSocketOutputStream *socketStream::DoCreateOutStream() | |
228 | { | |
229 | wxSocketOutputStream *pStrOutStream = new wxSocketOutputStream(*m_writeSocket); | |
230 | CPPUNIT_ASSERT(pStrOutStream->IsOk()); | |
231 | return pStrOutStream; | |
232 | } | |
233 | ||
234 | // Register the stream sub suite, by using some stream helper macro. | |
235 | STREAM_TEST_SUBSUITE_NAMED_REGISTRATION(socketStream) |