1 // Eris Online RPG Protocol Library
2 // Copyright (C) 2007 Alistair Riddoch
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software Foundation,
16 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 
18 // $Id$
19 
20 #ifdef NDEBUG
21 #undef NDEBUG
22 #endif
23 #ifndef DEBUG
24 #define DEBUG
25 #endif
26 
27 #include <Eris/BaseConnection.h>
28 #include <Eris/Exceptions.h>
29 #include <Eris/Log.h>
30 
31 #include "SignalFlagger.h"
32 
33 #include <Atlas/Codecs/XML.h>
34 #include <Atlas/Net/Stream.h>
35 #include <Atlas/Message/QueuedDecoder.h>
36 #include <Atlas/Objects/objectFactory.h>
37 #include <Atlas/Objects/Encoder.h>
38 
39 #include <skstream/skstream.h>
40 
41 #include <cstdlib>
42 
43 #include <cassert>
44 
45 class TestBaseConnection : public Eris::BaseConnection {
46   public:
47     bool failure;
48     bool timeout;
49 
TestBaseConnection(Atlas::Bridge * b)50     TestBaseConnection(Atlas::Bridge * b) : Eris::BaseConnection("test", "1", b), failure(false), timeout(false) { }
51 
handleFailure(const std::string & msg)52     virtual void handleFailure(const std::string & msg) {
53         failure = true;
54     }
55 
handleTimeout(const std::string & msg)56     virtual void handleTimeout(const std::string & msg) {
57         timeout = true;
58     }
59 
test_setStatus(Eris::BaseConnection::Status sc)60     void test_setStatus(Eris::BaseConnection::Status sc) {
61         setStatus(sc);
62     }
63 
test_onConnect()64     void test_onConnect() {
65         onConnect();
66     }
67 
test_hardDisconnect(bool flag)68     void test_hardDisconnect(bool flag) {
69         hardDisconnect(flag);
70     }
71 
test_onConnectTimeout()72     void test_onConnectTimeout() {
73         onConnectTimeout();
74     }
75 
test_onNegotiateTimeout()76     void test_onNegotiateTimeout() {
77         onNegotiateTimeout();
78     }
79 
setup_stream()80     void setup_stream() {
81         _stream = new tcp_socket_stream;
82     }
83 
setup_codec()84     void setup_codec() {
85         m_codec = new Atlas::Codecs::XML(*_stream, *_bridge);
86     }
87 
setup_encode()88     void setup_encode() {
89         _encode = new Atlas::Objects::ObjectsEncoder(*m_codec);
90     }
91 
setup_sc()92     void setup_sc() {
93         _sc = new Atlas::Net::StreamConnect(_clientName, *_stream);
94     }
95 };
96 
writeLog(Eris::LogLevel,const std::string & msg)97 static void writeLog(Eris::LogLevel, const std::string & msg)
98 {
99     std::cerr << msg << std::endl << std::flush;
100 }
101 
main()102 int main()
103 {
104     Eris::Logged.connect(sigc::ptr_fun(writeLog));
105 
106     Atlas::Objects::Factories * f = Atlas::Objects::Factories::instance();
107     assert(!f->hasFactory("unseen"));
108 
109     {
110         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
111     }
112 
113     // Make sure the op classes have been installed, and the initial
114     // constructor code path has been tested.
115     assert(f->hasFactory("unseen"));
116     assert(f->hasFactory("attack"));
117 
118     // Test the other code path when a second connection is created.
119     {
120         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
121     }
122 
123     // Test isConnected.
124     {
125         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
126 
127         assert(!tbc.isConnected());
128     }
129 
130     // Test getFileDescriptor.
131     {
132         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
133 
134         try {
135             tbc.getFileDescriptor();
136 
137             std::cerr << "FAIL: BaseConnection::getFileDescriptor() should throw Eris::InvalidOperation" << std::endl << "FAIL: when not coonected." << std::endl << std::flush;
138             abort();
139         }
140         catch (Eris::InvalidOperation & eio) {
141         }
142     }
143 
144     // Test getStatus().
145     {
146         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
147 
148         assert(tbc.getStatus() == Eris::BaseConnection::DISCONNECTED);
149     }
150 
151     // Test setStatus().
152     {
153         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
154 
155         assert(tbc.getStatus() == Eris::BaseConnection::DISCONNECTED);
156 
157         tbc.test_setStatus(Eris::BaseConnection::CONNECTED);
158 
159         assert(tbc.getStatus() == Eris::BaseConnection::CONNECTED);
160 
161         tbc.test_setStatus(Eris::BaseConnection::DISCONNECTED);
162     }
163 
164     // Test alternate path through desctructor using setStatus()
165     {
166         TestBaseConnection * tbc = new TestBaseConnection(new Atlas::Message::QueuedDecoder);
167 
168         assert(tbc->getStatus() == Eris::BaseConnection::DISCONNECTED);
169 
170         tbc->setup_stream();
171         tbc->setup_codec();
172         tbc->setup_encode();
173         tbc->test_setStatus(Eris::BaseConnection::CONNECTED);
174 
175         // The destructor will throw in hardDisconnect();
176         try {
177             delete tbc;
178             abort();
179         }
180         catch (Eris::InvalidOperation & eio) {
181         }
182     }
183 
184     // Test connect() and verify getStatus() changes.
185     {
186         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
187 
188         assert(tbc.getStatus() == Eris::BaseConnection::DISCONNECTED);
189 
190         int ret = tbc.connect("localhost", 6723);
191 
192         assert(ret == 0);
193 
194         assert(tbc.getStatus() == Eris::BaseConnection::CONNECTING);
195     }
196 
197     // Test onConnect() does nothing.
198     {
199         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
200 
201         tbc.test_onConnect();
202     }
203 
204     // Test onConnect() emits the signal.
205     {
206         SignalFlagger onConnect_checker;
207 
208         assert(!onConnect_checker.flagged());
209 
210         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
211 
212         tbc.Connected.connect(sigc::mem_fun(onConnect_checker,
213                                             &SignalFlagger::set));
214 
215         assert(!onConnect_checker.flagged());
216 
217         tbc.test_onConnect();
218 
219         assert(onConnect_checker.flagged());
220     }
221 
222     // Test hardDisconnect() does nothing.
223     {
224         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
225 
226         tbc.test_hardDisconnect(true);
227     }
228 
229     // Test hardDisconnect() throws in polldefault when connected
230     {
231         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
232 
233         // Add members to be consistent with connected state.
234         tbc.setup_stream();
235         tbc.setup_codec();
236         tbc.setup_encode();
237         // Make the state different
238         tbc.test_setStatus(Eris::BaseConnection::CONNECTED);
239 
240         try {
241             tbc.test_hardDisconnect(true);
242         }
243         catch (Eris::InvalidOperation & eio) {
244         }
245 
246         // Make it disconnected again, or we crash on destructor
247         tbc.test_setStatus(Eris::BaseConnection::DISCONNECTED);
248     }
249 
250     // Test hardDisconnect() throws in polldefault when disconnecting
251     {
252         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
253 
254         // Add members to be consistent with disconnecting state.
255         tbc.setup_stream();
256         tbc.setup_codec();
257         tbc.setup_encode();
258         // Make the state different
259         tbc.test_setStatus(Eris::BaseConnection::DISCONNECTING);
260 
261         try {
262             tbc.test_hardDisconnect(true);
263         }
264         catch (Eris::InvalidOperation & eio) {
265         }
266 
267         // Make it disconnected again, or we crash on destructor
268         tbc.test_setStatus(Eris::BaseConnection::DISCONNECTED);
269     }
270 
271     // Test hardDisconnect() throws in polldefault when negotiating
272     {
273         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
274 
275         // Add members to be consistent with negotiating state.
276         tbc.setup_stream();
277         tbc.setup_sc();
278         // Make the state different
279         tbc.test_setStatus(Eris::BaseConnection::NEGOTIATE);
280 
281         try {
282             tbc.test_hardDisconnect(true);
283         }
284         catch (Eris::InvalidOperation & eio) {
285         }
286 
287         // Make it disconnected again, or we crash on destructor
288         tbc.test_setStatus(Eris::BaseConnection::DISCONNECTED);
289     }
290 
291     // Test hardDisconnect() throws in polldefault when negotiating
292     {
293         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
294 
295         // Add members to be consistent with connecting state.
296         tbc.setup_stream();
297         // Make the state different
298         tbc.test_setStatus(Eris::BaseConnection::CONNECTING);
299 
300         try {
301             tbc.test_hardDisconnect(true);
302         }
303         catch (Eris::InvalidOperation & eio) {
304         }
305 
306         // Make it disconnected again, or we crash on destructor
307         tbc.test_setStatus(Eris::BaseConnection::DISCONNECTED);
308     }
309 
310     // Test hardDisconnect() throws in polldefault when negotiating
311     {
312         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
313 
314         // Add members to be consistent with connecting state.
315         tbc.setup_stream();
316         // Make the state different
317         tbc.test_setStatus(Eris::BaseConnection::INVALID_STATUS);
318 
319         try {
320             tbc.test_hardDisconnect(true);
321         }
322         catch (Eris::InvalidOperation & eio) {
323         }
324 
325         // Make it disconnected again, or we crash on destructor
326         tbc.test_setStatus(Eris::BaseConnection::DISCONNECTED);
327     }
328 
329     // Test onConnectTimeout()
330     {
331         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
332 
333         tbc.test_onConnectTimeout();
334 
335         assert(tbc.timeout);
336     }
337 
338     // Test onNegotiateTimeout()
339     {
340         TestBaseConnection tbc(new Atlas::Message::QueuedDecoder);
341 
342         tbc.test_onNegotiateTimeout();
343 
344         assert(tbc.timeout);
345     }
346 
347     return 0;
348 }
349