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/Account.h>
28 
29 #include <Eris/Connection.h>
30 #include <Eris/Avatar.h>
31 #include <Eris/Exceptions.h>
32 #include <Eris/Log.h>
33 #include <Eris/SpawnPoint.h>
34 #include <Eris/CharacterType.h>
35 
36 #include "SignalFlagger.h"
37 
38 #include <Atlas/Objects/Anonymous.h>
39 #include <Atlas/Objects/Entity.h>
40 #include <Atlas/Objects/Operation.h>
41 
42 #include <sigc++/adaptors/hide.h>
43 
44 #include <iostream>
45 
46 class TestConnection : public Eris::Connection {
47   public:
TestConnection(const std::string & name,const std::string & host,short port,bool debug)48     TestConnection(const std::string & name, const std::string & host,
49                    short port, bool debug) :
50                    Eris::Connection(name, host, port, debug) { }
51 
test_setStatus(Eris::BaseConnection::Status sc)52     void test_setStatus(Eris::BaseConnection::Status sc) {
53         setStatus(sc);
54     }
55 
send(const Atlas::Objects::Root & obj)56     virtual void send(const Atlas::Objects::Root &obj) {
57         std::cout << "Sending " << obj->getParents().front()
58                   << std::endl << std::flush;
59     }
60 };
61 
62 class TestAccount : public Eris::Account {
63   public:
TestAccount(Eris::Connection * con)64     TestAccount(Eris::Connection * con) : Eris::Account(con) { }
65 
setup_setStatus(Eris::Account::Status s)66     void setup_setStatus(Eris::Account::Status s) {
67         m_status = s;
68     }
69 
setup_setDoingCharacterRefresh(bool f)70     void setup_setDoingCharacterRefresh(bool f) {
71         m_doingCharacterRefresh = f;
72     }
73 
setup_fakeCharacter(const std::string & id)74     void setup_fakeCharacter(const std::string & id) {
75         m_characterIds.insert(id);
76     }
77 
setup_insertActiveCharacters(Eris::Avatar * ea)78     void setup_insertActiveCharacters(Eris::Avatar * ea) {
79         m_activeCharacters.insert(std::make_pair(ea->getId(), ea));
80     }
81 
setup_setUsername(const std::string & u)82     void setup_setUsername(const std::string & u) {
83         m_username = u;
84     }
85 
setup_setPassword(const std::string & p)86     void setup_setPassword(const std::string & p) {
87         m_pass = p;
88     }
89 
query_getStatus() const90     Eris::Account::Status query_getStatus() const {
91         return m_status;
92     }
93 
test_logoutResponse(const Atlas::Objects::Operation::RootOperation & op)94     void test_logoutResponse(const Atlas::Objects::Operation::RootOperation& op) {
95         logoutResponse(op);
96     }
97 
test_loginComplete(const Atlas::Objects::Entity::Account & p)98     void test_loginComplete(const Atlas::Objects::Entity::Account &p) {
99         loginComplete(p);
100     }
101 
test_updateFromObject(const Atlas::Objects::Entity::Account & p)102     void test_updateFromObject(const Atlas::Objects::Entity::Account &p) {
103         updateFromObject(p);
104     }
105 
test_loginError(const Atlas::Objects::Operation::Error & err)106     void test_loginError(const Atlas::Objects::Operation::Error& err) {
107         loginError(err);
108     }
109 
test_handleLoginTimeout()110     void test_handleLoginTimeout() {
111         handleLoginTimeout();
112     }
113 
test_avatarResponse(const Atlas::Objects::Operation::RootOperation & op)114     void test_avatarResponse(const Atlas::Objects::Operation::RootOperation& op) {
115         avatarResponse(op);
116     }
117 
test_internalDeactivateCharacter(Eris::Avatar * ea)118     void test_internalDeactivateCharacter(Eris::Avatar * ea) {
119         internalDeactivateCharacter(ea);
120     }
121 
test_sightCharacter(const Atlas::Objects::Operation::RootOperation & op)122     void test_sightCharacter(const Atlas::Objects::Operation::RootOperation& op) {
123         sightCharacter(op);
124     }
125 
test_netConnected()126     void test_netConnected() {
127         netConnected();
128     }
129 
test_netDisconnecting()130     bool test_netDisconnecting() {
131         return netDisconnecting();
132     }
133 
test_netFailure(const std::string & msg)134     void test_netFailure(const std::string & msg) {
135         netFailure(msg);
136     }
137 
test_handleLogoutTimeout()138     void test_handleLogoutTimeout() {
139         handleLogoutTimeout();
140     }
141 
test_avatarLogoutResponse(const Atlas::Objects::Operation::RootOperation & op)142     void test_avatarLogoutResponse(const Atlas::Objects::Operation::RootOperation& op) {
143         avatarLogoutResponse(op);
144     }
145 
test_internalLogin(const std::string & u,const std::string & p)146     Eris::Result test_internalLogin(const std::string & u, const std::string & p) {
147         return internalLogin(u, p);
148     }
149 
150     static const Eris::Account::Status DISCONNECTED = Eris::Account::DISCONNECTED;
151     static const Eris::Account::Status LOGGING_IN = Eris::Account::LOGGING_IN;
152     static const Eris::Account::Status LOGGED_IN = Eris::Account::LOGGED_IN;
153     static const Eris::Account::Status LOGGING_OUT = Eris::Account::LOGGING_OUT;
154     static const Eris::Account::Status CREATING_CHAR = Eris::Account::CREATING_CHAR;
155     static const Eris::Account::Status TAKING_CHAR = Eris::Account::TAKING_CHAR;
156 };
157 
158 class TestAvatar : public Eris::Avatar {
159   public:
TestAvatar(Eris::Account * ac,const std::string & ent_id)160     TestAvatar(Eris::Account * ac, const std::string & ent_id) :
161                Eris::Avatar(*ac, ent_id) { }
162 };
163 
writeLog(Eris::LogLevel,const std::string & msg)164 static void writeLog(Eris::LogLevel, const std::string & msg)
165 {
166     std::cerr << msg << std::endl << std::flush;
167 }
168 
main()169 int main()
170 {
171     Eris::Logged.connect(sigc::ptr_fun(writeLog));
172     Eris::setLogLevel(Eris::LOG_DEBUG);
173 
174     // Test constructor
175     {
176         Eris::Connection * con = new Eris::Connection("name", "localhost",
177                                                       6767, true);
178 
179         Eris::Account acc(con);
180     }
181     // Test constructor throws on a null connection.
182     {
183         try {
184             Eris::Account acc(0);
185             abort();
186         }
187         catch (Eris::InvalidOperation & eio) {
188         }
189     }
190     // Test getActiveCharacters()
191     {
192         Eris::Connection * con = new Eris::Connection("name", "localhost",
193                                                       6767, true);
194 
195         Eris::Account acc(con);
196 
197         const Eris::ActiveCharacterMap & acm = acc.getActiveCharacters();
198 
199         assert(acm.empty());
200     }
201     // Test getId()
202     {
203         Eris::Connection * con = new Eris::Connection("name", "localhost",
204                                                       6767, true);
205 
206         Eris::Account acc(con);
207 
208         const std::string & id = acc.getId();
209 
210         assert(id.empty());
211     }
212     // Test getUsername()
213     {
214         Eris::Connection * con = new Eris::Connection("name", "localhost",
215                                                       6767, true);
216 
217         Eris::Account acc(con);
218 
219         const std::string & username = acc.getUsername();
220 
221         assert(username.empty());
222     }
223 
224     // Test getConnection()
225     {
226         Eris::Connection * con = new Eris::Connection("name", "localhost",
227                                                       6767, true);
228 
229         Eris::Account acc(con);
230 
231         Eris::Connection * got_con = acc.getConnection();
232 
233         assert(got_con == con);
234     }
235 
236     // Test login() fails if not connected
237     {
238         TestConnection * con = new TestConnection("name", "localhost",
239                                                   6767, true);
240 
241         Eris::Account acc(con);
242 
243         Eris::Result res =  acc.login("foo", "bar");
244         assert(res == Eris::NOT_CONNECTED);
245     }
246 
247     // Test login() fails if we fake connected and logged in
248     {
249         TestConnection * con = new TestConnection("name", "localhost",
250                                                   6767, true);
251 
252         TestAccount acc(con);
253 
254         con->test_setStatus(Eris::BaseConnection::CONNECTED);
255         acc.setup_setStatus(TestAccount::LOGGING_IN);
256 
257         Eris::Result res =  acc.login("foo", "bar");
258         assert(res == Eris::ALREADY_LOGGED_IN);
259     }
260 
261     // Test login() works if we fake connected
262     {
263         TestConnection * con = new TestConnection("name", "localhost",
264                                                   6767, true);
265 
266         TestAccount acc(con);
267 
268         con->test_setStatus(Eris::BaseConnection::CONNECTED);
269 
270         Eris::Result res =  acc.login("foo", "bar");
271         assert(res == Eris::NO_ERR);
272     }
273 
274     // Test createAccount() fails if not connected
275     {
276         TestConnection * con = new TestConnection("name", "localhost",
277                                                   6767, true);
278 
279         Eris::Account acc(con);
280 
281         Eris::Result res =  acc.createAccount("foo", "bar", "baz");
282         assert(res == Eris::NOT_CONNECTED);
283     }
284 
285     // Test createAccount() works if we fake connected.
286     {
287         TestConnection * con = new TestConnection("name", "localhost",
288                                                   6767, true);
289 
290         TestAccount acc(con);
291 
292         con->test_setStatus(Eris::BaseConnection::CONNECTED);
293 
294         Eris::Result res =  acc.createAccount("foo", "bar", "baz");
295         assert(res == Eris::NO_ERR);
296     }
297 
298     // Test logout() fails if not connected.
299     {
300         TestConnection * con = new TestConnection("name", "localhost",
301                                                   6767, true);
302 
303         TestAccount acc(con);
304 
305         Eris::Result res =  acc.logout();
306         assert(res == Eris::NOT_CONNECTED);
307     }
308 
309     // Test logout() does nothing if we fake connected, and logging out.
310     {
311         TestConnection * con = new TestConnection("name", "localhost",
312                                                   6767, true);
313 
314         TestAccount acc(con);
315         con->test_setStatus(Eris::BaseConnection::CONNECTED);
316         acc.setup_setStatus(TestAccount::LOGGING_OUT);
317 
318         Eris::Result res =  acc.logout();
319         assert(res == Eris::NO_ERR);
320     }
321 
322     // Test logout() fails if we are fake connected but not logged in.
323     {
324         TestConnection * con = new TestConnection("name", "localhost",
325                                                   6767, true);
326 
327         TestAccount acc(con);
328         con->test_setStatus(Eris::BaseConnection::CONNECTED);
329 
330         Eris::Result res =  acc.logout();
331         assert(res == Eris::NOT_LOGGED_IN);
332     }
333 
334     // Test logout() works if we are fake connected and logged in.
335     {
336         TestConnection * con = new TestConnection("name", "localhost",
337                                                   6767, true);
338 
339         TestAccount acc(con);
340         con->test_setStatus(Eris::BaseConnection::CONNECTED);
341         acc.setup_setStatus(TestAccount::LOGGED_IN);
342 
343         Eris::Result res =  acc.logout();
344         assert(res == Eris::NO_ERR);
345     }
346 
347     // Test getCharacters()
348     {
349         TestConnection * con = new TestConnection("name", "localhost",
350                                                   6767, true);
351 
352         TestAccount acc(con);
353 
354         const Eris::CharacterMap & ecm = acc.getCharacters();
355 
356         assert(ecm.empty());
357     }
358 
359     // Test refreshCharacterInfo() fails if not connected
360     {
361         TestConnection * con = new TestConnection("name", "localhost",
362                                                   6767, true);
363 
364         Eris::Account acc(con);
365 
366         Eris::Result res =  acc.refreshCharacterInfo();
367         assert(res == Eris::NOT_CONNECTED);
368     }
369 
370     // Test refreshCharacterInfo() fails if we fake connected and logging in
371     {
372         TestConnection * con = new TestConnection("name", "localhost",
373                                                   6767, true);
374 
375         TestAccount acc(con);
376 
377         con->test_setStatus(Eris::BaseConnection::CONNECTED);
378         acc.setup_setStatus(TestAccount::LOGGING_IN);
379 
380         Eris::Result res =  acc.refreshCharacterInfo();
381         assert(res == Eris::NOT_LOGGED_IN);
382     }
383 
384     // Test refreshCharacterInfo() returns if we fake connected and logged in,
385     // and pretend a query is already occuring.
386     {
387         TestConnection * con = new TestConnection("name", "localhost",
388                                                   6767, true);
389 
390         TestAccount acc(con);
391 
392         con->test_setStatus(Eris::BaseConnection::CONNECTED);
393         acc.setup_setStatus(TestAccount::LOGGED_IN);
394         acc.setup_setDoingCharacterRefresh(true);
395 
396         Eris::Result res =  acc.refreshCharacterInfo();
397         assert(res == Eris::NO_ERR);
398 
399         // Make sure it no longer thinks it is connected, so it does not try
400         // to log out in the destructor.
401         acc.setup_setStatus(TestAccount::LOGGING_OUT);
402     }
403 
404     // Test refreshCharacterInfo() returns if we fake connected and logged in,
405     // and have no characters IDs.
406     {
407         TestConnection * con = new TestConnection("name", "localhost",
408                                                   6767, true);
409 
410         TestAccount acc(con);
411         SignalFlagger gotAllCharacters_checker;
412 
413         con->test_setStatus(Eris::BaseConnection::CONNECTED);
414         acc.setup_setStatus(TestAccount::LOGGED_IN);
415         acc.GotAllCharacters.connect(sigc::mem_fun(gotAllCharacters_checker,
416                                                    &SignalFlagger::set));
417 
418         assert(!gotAllCharacters_checker.flagged());
419         Eris::Result res =  acc.refreshCharacterInfo();
420         assert(res == Eris::NO_ERR);
421         assert(gotAllCharacters_checker.flagged());
422 
423         // Make sure it no longer thinks it is connected, so it does not try
424         // to log out in the destructor.
425         acc.setup_setStatus(TestAccount::LOGGING_OUT);
426     }
427 
428     // Test refreshCharacterInfo() works if we fake connected and logged in,
429     // and have characters IDs.
430     {
431         TestConnection * con = new TestConnection("name", "localhost",
432                                                   6767, true);
433 
434         TestAccount acc(con);
435         SignalFlagger gotAllCharacters_checker;
436         std::string fake_char_id("1");
437 
438         con->test_setStatus(Eris::BaseConnection::CONNECTED);
439         acc.setup_setStatus(TestAccount::LOGGED_IN);
440         acc.GotAllCharacters.connect(sigc::mem_fun(gotAllCharacters_checker,
441                                                    &SignalFlagger::set));
442         acc.setup_fakeCharacter(fake_char_id);
443 
444 
445         assert(!gotAllCharacters_checker.flagged());
446         Eris::Result res =  acc.refreshCharacterInfo();
447         assert(res == Eris::NO_ERR);
448         assert(!gotAllCharacters_checker.flagged());
449 
450         // Make sure it no longer thinks it is connected, so it does not try
451         // to log out in the destructor.
452         acc.setup_setStatus(TestAccount::LOGGING_OUT);
453     }
454 
455     // Test createCharacter() fails if not connected
456     {
457         TestConnection * con = new TestConnection("name", "localhost",
458                                                   6767, true);
459 
460         Eris::Account acc(con);
461         Atlas::Objects::Entity::Anonymous ent;
462 
463         Eris::Result res =  acc.createCharacter(ent);
464         assert(res == Eris::NOT_CONNECTED);
465     }
466 
467     // Test createCharacter() fails if we fake connected and creating char
468     {
469         TestConnection * con = new TestConnection("name", "localhost",
470                                                   6767, true);
471 
472         TestAccount acc(con);
473         Atlas::Objects::Entity::Anonymous ent;
474 
475         con->test_setStatus(Eris::BaseConnection::CONNECTED);
476         acc.setup_setStatus(TestAccount::CREATING_CHAR);
477 
478         Eris::Result res =  acc.createCharacter(ent);
479         assert(res == Eris::DUPLICATE_CHAR_ACTIVE);
480 
481         // Make sure it no longer thinks it is connected, so it does not try
482         // to log out in the destructor.
483         acc.setup_setStatus(TestAccount::LOGGING_OUT);
484     }
485 
486     // Test createCharacter() fails if we fake connected and taking char
487     {
488         TestConnection * con = new TestConnection("name", "localhost",
489                                                   6767, true);
490 
491         TestAccount acc(con);
492         Atlas::Objects::Entity::Anonymous ent;
493 
494         con->test_setStatus(Eris::BaseConnection::CONNECTED);
495         acc.setup_setStatus(TestAccount::TAKING_CHAR);
496 
497         Eris::Result res =  acc.createCharacter(ent);
498         assert(res == Eris::DUPLICATE_CHAR_ACTIVE);
499 
500         // Make sure it no longer thinks it is connected, so it does not try
501         // to log out in the destructor.
502         acc.setup_setStatus(TestAccount::LOGGING_OUT);
503     }
504 
505     // Test createCharacter() fails if we fake connected but not logged in
506     {
507         TestConnection * con = new TestConnection("name", "localhost",
508                                                   6767, true);
509 
510         TestAccount acc(con);
511         Atlas::Objects::Entity::Anonymous ent;
512 
513         con->test_setStatus(Eris::BaseConnection::CONNECTED);
514         acc.setup_setStatus(TestAccount::LOGGING_IN);
515 
516         Eris::Result res =  acc.createCharacter(ent);
517         assert(res == Eris::NOT_LOGGED_IN);
518     }
519 
520     // Test createCharacter() works if we fake connected and logged in
521     {
522         TestConnection * con = new TestConnection("name", "localhost",
523                                                   6767, true);
524 
525         TestAccount acc(con);
526         Atlas::Objects::Entity::Anonymous ent;
527 
528         con->test_setStatus(Eris::BaseConnection::CONNECTED);
529         acc.setup_setStatus(TestAccount::LOGGED_IN);
530 
531         Eris::Result res =  acc.createCharacter(ent);
532         assert(res == Eris::NO_ERR);
533     }
534 
535     // Test takeCharacter() fails if ID is not a real character
536     {
537         TestConnection * con = new TestConnection("name", "localhost",
538                                                   6767, true);
539 
540         Eris::Account acc(con);
541         std::string fake_char_id("1");
542 
543         Eris::Result res =  acc.takeCharacter(fake_char_id);
544         assert(res == Eris::BAD_CHARACTER_ID);
545     }
546 
547     // Test takeCharacter() fails if not connected
548     {
549         TestConnection * con = new TestConnection("name", "localhost",
550                                                   6767, true);
551 
552         TestAccount acc(con);
553         std::string fake_char_id("1");
554 
555         acc.setup_fakeCharacter(fake_char_id);
556 
557         Eris::Result res =  acc.takeCharacter(fake_char_id);
558         assert(res == Eris::NOT_CONNECTED);
559     }
560 
561     // Test takeCharacter() fails if we fake connected and logging in
562     {
563         TestConnection * con = new TestConnection("name", "localhost",
564                                                   6767, true);
565 
566         TestAccount acc(con);
567         std::string fake_char_id("1");
568 
569         con->test_setStatus(Eris::BaseConnection::CONNECTED);
570         acc.setup_setStatus(TestAccount::LOGGING_IN);
571         acc.setup_fakeCharacter(fake_char_id);
572 
573         Eris::Result res =  acc.takeCharacter(fake_char_id);
574         assert(res == Eris::NOT_LOGGED_IN);
575     }
576 
577     // Test takeCharacter() fails if we fake connected and logging in
578     {
579         TestConnection * con = new TestConnection("name", "localhost",
580                                                   6767, true);
581 
582         TestAccount acc(con);
583         std::string fake_char_id("1");
584 
585         con->test_setStatus(Eris::BaseConnection::CONNECTED);
586         acc.setup_setStatus(TestAccount::CREATING_CHAR);
587         acc.setup_fakeCharacter(fake_char_id);
588 
589         Eris::Result res =  acc.takeCharacter(fake_char_id);
590         assert(res == Eris::DUPLICATE_CHAR_ACTIVE);
591 
592         // Make sure it no longer thinks it is connected, so it does not try
593         // to log out in the destructor.
594         acc.setup_setStatus(TestAccount::LOGGING_OUT);
595     }
596 
597     // Test takeCharacter() fails if we fake connected and logging in
598     {
599         TestConnection * con = new TestConnection("name", "localhost",
600                                                   6767, true);
601 
602         TestAccount acc(con);
603         std::string fake_char_id("1");
604 
605         con->test_setStatus(Eris::BaseConnection::CONNECTED);
606         acc.setup_setStatus(TestAccount::TAKING_CHAR);
607         acc.setup_fakeCharacter(fake_char_id);
608 
609         Eris::Result res =  acc.takeCharacter(fake_char_id);
610         assert(res == Eris::DUPLICATE_CHAR_ACTIVE);
611 
612         // Make sure it no longer thinks it is connected, so it does not try
613         // to log out in the destructor.
614         acc.setup_setStatus(TestAccount::LOGGING_OUT);
615     }
616 
617     // Test takeCharacter() works if we fake connected and logged in
618     {
619         TestConnection * con = new TestConnection("name", "localhost",
620                                                   6767, true);
621 
622         TestAccount acc(con);
623         std::string fake_char_id("1");
624 
625         con->test_setStatus(Eris::BaseConnection::CONNECTED);
626         acc.setup_setStatus(TestAccount::LOGGED_IN);
627         acc.setup_fakeCharacter(fake_char_id);
628 
629         Eris::Result res =  acc.takeCharacter(fake_char_id);
630         assert(res == Eris::NO_ERR);
631 
632         // Make sure it no longer thinks it is connected, so it does not try
633         // to log out in the destructor.
634         acc.setup_setStatus(TestAccount::LOGGING_OUT);
635     }
636 
637     // Test deactivateCharacter()
638     {
639         TestConnection * con = new TestConnection("name", "localhost",
640                                                   6767, true);
641 
642         TestAccount acc(con);
643         std::string fake_char_id("1");
644         Eris::Avatar * ea = new TestAvatar(&acc, fake_char_id);
645 
646         Eris::Result res = acc.deactivateCharacter(ea);
647         assert(res == Eris::NO_ERR);
648     }
649 
650     // Test isLoggedIn()
651     {
652         TestConnection * con = new TestConnection("name", "localhost",
653                                                   6767, true);
654 
655         TestAccount acc(con);
656 
657         assert(!acc.isLoggedIn());
658     }
659 
660     // Test isLoggedIn()
661     {
662         TestConnection * con = new TestConnection("name", "localhost",
663                                                   6767, true);
664 
665         TestAccount acc(con);
666 
667         acc.setup_setStatus(TestAccount::LOGGED_IN);
668 
669         assert(acc.isLoggedIn());
670     }
671 
672     // Test isLoggedIn()
673     {
674         TestConnection * con = new TestConnection("name", "localhost",
675                                                   6767, true);
676 
677         TestAccount acc(con);
678 
679         acc.setup_setStatus(TestAccount::TAKING_CHAR);
680 
681         assert(acc.isLoggedIn());
682     }
683 
684     // Test isLoggedIn()
685     {
686         TestConnection * con = new TestConnection("name", "localhost",
687                                                   6767, true);
688 
689         TestAccount acc(con);
690 
691         acc.setup_setStatus(TestAccount::CREATING_CHAR);
692 
693         assert(acc.isLoggedIn());
694     }
695 
696     // Test internalLogin()
697     {
698         TestConnection * con = new TestConnection("name", "localhost",
699                                                   6767, true);
700 
701         TestAccount acc(con);
702 
703         Eris::Result res = acc.test_internalLogin("foo", "bar");
704 
705         assert(res == Eris::NO_ERR);
706     }
707 
708     // FIXME Cover logoutResponse once we can fake connections.
709 #if 0
710     // Test logoutResponse() does nothing when given the wrong op.
711     {
712         TestConnection * con = new TestConnection("name", "localhost",
713                                                   6767, true);
714 
715         TestAccount acc(con);
716 
717         Atlas::Objects::Operation::RootOperation op;
718         acc.test_logoutResponse(op);
719     }
720 #endif
721     // FIXME Cover internalLogout once we can fake connections.
722     // FIXME Cover loginResponse once we can fake connections.
723 
724     // Test loginComplete() does nothing when not logged in.
725     {
726         TestConnection * con = new TestConnection("name", "localhost",
727                                                   6767, true);
728 
729         TestAccount acc(con);
730         Atlas::Objects::Entity::Account p;
731 
732         p->setUsername("bob");
733 
734         acc.test_loginComplete(p);
735     }
736 
737     // Test loginComplete()
738     {
739         TestConnection * con = new TestConnection("name", "localhost",
740                                                   6767, true);
741 
742         TestAccount acc(con);
743         Atlas::Objects::Entity::Account p;
744         SignalFlagger loginSuccess_checker;
745 
746         p->setUsername("bob");
747         acc.LoginSuccess.connect(sigc::mem_fun(loginSuccess_checker,
748                                                &SignalFlagger::set));
749 
750         acc.test_loginComplete(p);
751 
752         assert(acc.isLoggedIn());
753         assert(loginSuccess_checker.flagged());
754         // FIXME Verify timeout has been cleared?
755     }
756 
757     // Test updateFromObject() with a bad character list
758     {
759         TestConnection * con = new TestConnection("name", "localhost",
760                                                   6767, true);
761 
762         TestAccount acc(con);
763         Atlas::Objects::Entity::Account p;
764 
765         p->setAttr("character_types", "non-list");
766 
767         acc.test_updateFromObject(p);
768     }
769 
770     // Test updateFromObject() with a character list with something bad in it
771     {
772         TestConnection * con = new TestConnection("name", "localhost",
773                                                   6767, true);
774 
775         TestAccount acc(con);
776         Atlas::Objects::Entity::Account p;
777 
778         Atlas::Message::ListType character_types;
779         character_types.push_back(1);
780         character_types.push_back("string");
781 
782         p->setAttr("character_types", character_types);
783 
784         acc.test_updateFromObject(p);
785         assert(acc.getCharacterTypes().size() == 1);
786         assert(acc.getCharacterTypes().front() == "string");
787 
788     }
789 
790     {
791         TestConnection * con = new TestConnection("name", "localhost",
792                                                   6767, true);
793 
794         TestAccount acc(con);
795         Atlas::Objects::Entity::Account p;
796 
797         Atlas::Message::ListType spawnPoints;
798         Atlas::Message::MapType spawnPoint1;
799         spawnPoint1["name"] = "spawn1";
800         Atlas::Message::ListType charTypes1;
801         charTypes1.push_back(1);
802         charTypes1.push_back("string");
803         spawnPoint1["character_types"] = charTypes1;
804         spawnPoints.push_back(spawnPoint1);
805         spawnPoints.push_back(Atlas::Message::ListType());
806         p->setAttr("spawns", spawnPoints);
807 
808         acc.test_updateFromObject(p);
809         assert(acc.getCharacterTypes().size() == 0);
810         assert(acc.getSpawnPoints().size() == 1);
811         assert(acc.getSpawnPoints().find("spawn1")->second.getAvailableCharacterTypes().size() == 1);
812         assert(acc.getSpawnPoints().find("spawn1")->second.getAvailableCharacterTypes().front().getName() == "string");
813 
814     }
815     // Test loginError() with no arg
816     {
817         TestConnection * con = new TestConnection("name", "localhost",
818                                                   6767, true);
819 
820         TestAccount acc(con);
821         Atlas::Objects::Operation::Error err;
822 
823         acc.test_loginError(err);
824     }
825 
826     // Test loginError() with empty arg
827     {
828         TestConnection * con = new TestConnection("name", "localhost",
829                                                   6767, true);
830 
831         TestAccount acc(con);
832         Atlas::Objects::Operation::Error err;
833         Atlas::Objects::Root err_arg;
834         err->setArgs1(err_arg);
835 
836         acc.test_loginError(err);
837     }
838 
839     // Test loginError() with arg that has non-string message
840     {
841         TestConnection * con = new TestConnection("name", "localhost",
842                                                   6767, true);
843 
844         TestAccount acc(con);
845         Atlas::Objects::Operation::Error err;
846         Atlas::Objects::Root err_arg;
847         err_arg->setAttr("message", 1);
848         err->setArgs1(err_arg);
849 
850         acc.test_loginError(err);
851     }
852 
853     // Test loginError() with arg that has non-string message
854     {
855         TestConnection * con = new TestConnection("name", "localhost",
856                                                   6767, true);
857 
858         TestAccount acc(con);
859         Atlas::Objects::Operation::Error err;
860         Atlas::Objects::Root err_arg;
861         SignalFlagger loginFailure_checker;
862 
863         err_arg->setAttr("message", "Yur loginz, let me show you them.");
864         err->setArgs1(err_arg);
865         acc.LoginFailure.connect(sigc::hide(sigc::mem_fun(loginFailure_checker,
866                                                           &SignalFlagger::set)));
867 
868         acc.test_loginError(err);
869 
870         assert(loginFailure_checker.flagged());
871     }
872 
873     // Test handleLoginTimeout()
874     {
875         TestConnection * con = new TestConnection("name", "localhost",
876                                                   6767, true);
877 
878         TestAccount acc(con);
879         SignalFlagger loginFailure_checker;
880 
881         acc.LoginFailure.connect(sigc::hide(sigc::mem_fun(loginFailure_checker,
882                                                           &SignalFlagger::set)));
883 
884         acc.test_handleLoginTimeout();
885 
886         assert(loginFailure_checker.flagged());
887     }
888 
889     // Test avatarResponse() Error op
890     {
891         TestConnection * con = new TestConnection("name", "localhost",
892                                                   6767, true);
893 
894         TestAccount acc(con);
895         Atlas::Objects::Operation::Error op;
896         SignalFlagger avatarFailure_checker;
897 
898         acc.AvatarFailure.connect(sigc::hide(sigc::mem_fun(avatarFailure_checker,
899                                                            &SignalFlagger::set)));
900 
901         acc.test_avatarResponse(op);
902         assert(avatarFailure_checker.flagged());
903         assert(acc.query_getStatus() == TestAccount::LOGGED_IN);
904 
905         acc.setup_setStatus(TestAccount::DISCONNECTED);
906     }
907 
908     // Test avatarResponse() Info op with no arg
909     {
910         TestConnection * con = new TestConnection("name", "localhost",
911                                                   6767, true);
912 
913         TestAccount acc(con);
914         Atlas::Objects::Operation::Info op;
915         SignalFlagger avatarSuccess_checker;
916 
917         acc.AvatarSuccess.connect(sigc::hide(sigc::mem_fun(avatarSuccess_checker,
918                                                            &SignalFlagger::set)));
919 
920         acc.test_avatarResponse(op);
921         assert(!avatarSuccess_checker.flagged());
922         assert(acc.query_getStatus() == TestAccount::DISCONNECTED);
923     }
924 
925     // Test avatarResponse() Info op with non-entity arg
926     {
927         TestConnection * con = new TestConnection("name", "localhost",
928                                                   6767, true);
929 
930         TestAccount acc(con);
931         Atlas::Objects::Operation::Info op;
932         Atlas::Objects::Root bad_arg;
933         SignalFlagger avatarSuccess_checker;
934 
935         op->setArgs1(bad_arg);
936         acc.AvatarSuccess.connect(sigc::hide(sigc::mem_fun(avatarSuccess_checker,
937                                                            &SignalFlagger::set)));
938 
939         acc.test_avatarResponse(op);
940         assert(!avatarSuccess_checker.flagged());
941         assert(acc.query_getStatus() == TestAccount::DISCONNECTED);
942     }
943 
944     // Test avatarResponse() Info op with entity arg
945     {
946         TestConnection * con = new TestConnection("name", "localhost",
947                                                   6767, true);
948 
949         TestAccount acc(con);
950         Atlas::Objects::Operation::Info op;
951         Atlas::Objects::Entity::Anonymous info_arg;
952         SignalFlagger avatarSuccess_checker;
953 
954         op->setArgs1(info_arg);
955         acc.AvatarSuccess.connect(sigc::hide(sigc::mem_fun(avatarSuccess_checker,
956                                                            &SignalFlagger::set)));
957 
958         acc.test_avatarResponse(op);
959         assert(avatarSuccess_checker.flagged());
960         assert(acc.query_getStatus() == TestAccount::LOGGED_IN);
961 
962         acc.setup_setStatus(TestAccount::DISCONNECTED);
963     }
964 
965     // Test avatarResponse() Get (not info or error) op with entity arg
966     {
967         TestConnection * con = new TestConnection("name", "localhost",
968                                                   6767, true);
969 
970         TestAccount acc(con);
971         Atlas::Objects::Operation::Get op; // Arbitrary not Info or Error
972         Atlas::Objects::Entity::Anonymous info_arg;
973         SignalFlagger avatarSuccess_checker;
974 
975         op->setArgs1(info_arg);
976         acc.AvatarSuccess.connect(sigc::hide(sigc::mem_fun(avatarSuccess_checker,
977                                                            &SignalFlagger::set)));
978 
979         acc.test_avatarResponse(op);
980         assert(!avatarSuccess_checker.flagged());
981         assert(acc.query_getStatus() == TestAccount::DISCONNECTED);
982     }
983 
984     // Test internalDeactivateCharacter() indirectly
985     {
986         TestConnection * con = new TestConnection("name", "localhost",
987                                                   6767, true);
988 
989         TestAccount acc(con);
990         std::string fake_char_id("1");
991         Eris::Avatar * ea = new TestAvatar(&acc, fake_char_id);
992 
993         acc.setup_insertActiveCharacters(ea);
994 
995         assert(!acc.getActiveCharacters().empty());
996 
997         // Eris::Avatar destructor calls
998         // Eris::Account::internalDeactivateCharacter
999         delete ea;
1000 
1001         assert(acc.getActiveCharacters().empty());
1002     }
1003 
1004     // Test internalDeactivateCharacter() directly
1005     {
1006         TestConnection * con = new TestConnection("name", "localhost",
1007                                                   6767, true);
1008 
1009         TestAccount acc(con);
1010         std::string fake_char_id("1");
1011         Eris::Avatar * ea = new TestAvatar(&acc, fake_char_id);
1012 
1013         acc.setup_insertActiveCharacters(ea);
1014 
1015         assert(!acc.getActiveCharacters().empty());
1016 
1017         acc.test_internalDeactivateCharacter(ea);
1018 
1019         assert(acc.getActiveCharacters().empty());
1020     }
1021 
1022     // Test sightCharacter() with empty sight
1023     {
1024         TestConnection * con = new TestConnection("name", "localhost",
1025                                                   6767, true);
1026 
1027         TestAccount acc(con);
1028         Atlas::Objects::Operation::Sight sight;
1029         SignalFlagger gotCharacterInfo_checker;
1030 
1031         acc.setup_setDoingCharacterRefresh(true);
1032         acc.GotCharacterInfo.connect(sigc::hide(sigc::mem_fun(gotCharacterInfo_checker,
1033                                                    &SignalFlagger::set)));
1034 
1035         acc.test_sightCharacter(sight);
1036 
1037         assert(!gotCharacterInfo_checker.flagged());
1038     }
1039 
1040     // Test sightCharacter() with Sight with bad args
1041     {
1042         TestConnection * con = new TestConnection("name", "localhost",
1043                                                   6767, true);
1044 
1045         TestAccount acc(con);
1046         Atlas::Objects::Operation::Sight sight;
1047         Atlas::Objects::Root bad_arg;
1048         SignalFlagger gotCharacterInfo_checker;
1049 
1050         acc.setup_setDoingCharacterRefresh(true);
1051         acc.GotCharacterInfo.connect(sigc::hide(sigc::mem_fun(gotCharacterInfo_checker,
1052                                                    &SignalFlagger::set)));
1053         sight->setArgs1(bad_arg);
1054 
1055         acc.test_sightCharacter(sight);
1056 
1057         assert(!gotCharacterInfo_checker.flagged());
1058     }
1059 
1060     // Test sightCharacter() with Sight with valid args
1061     {
1062         TestConnection * con = new TestConnection("name", "localhost",
1063                                                   6767, true);
1064 
1065         TestAccount acc(con);
1066         Atlas::Objects::Operation::Sight sight;
1067         Atlas::Objects::Entity::Anonymous sight_arg;
1068         SignalFlagger gotCharacterInfo_checker, gotAllCharacters_checker;
1069 
1070         acc.setup_setDoingCharacterRefresh(true);
1071         acc.GotCharacterInfo.connect(sigc::hide(sigc::mem_fun(gotCharacterInfo_checker,
1072                                                    &SignalFlagger::set)));
1073         acc.GotAllCharacters.connect(sigc::mem_fun(gotAllCharacters_checker,
1074                                                    &SignalFlagger::set));
1075         sight->setArgs1(sight_arg);
1076         acc.setup_fakeCharacter("1");
1077 
1078         acc.test_sightCharacter(sight);
1079 
1080         assert(gotCharacterInfo_checker.flagged());
1081         assert(gotAllCharacters_checker.flagged());
1082     }
1083 
1084     // Test sightCharacter() with Sight with valid args, but one character reamining
1085     {
1086         TestConnection * con = new TestConnection("name", "localhost",
1087                                                   6767, true);
1088 
1089         TestAccount acc(con);
1090         Atlas::Objects::Operation::Sight sight;
1091         Atlas::Objects::Entity::Anonymous sight_arg;
1092         SignalFlagger gotCharacterInfo_checker, gotAllCharacters_checker;
1093 
1094         acc.setup_setDoingCharacterRefresh(true);
1095         acc.GotCharacterInfo.connect(sigc::hide(sigc::mem_fun(gotCharacterInfo_checker,
1096                                                    &SignalFlagger::set)));
1097         acc.GotAllCharacters.connect(sigc::mem_fun(gotAllCharacters_checker,
1098                                                    &SignalFlagger::set));
1099         sight->setArgs1(sight_arg);
1100         acc.setup_fakeCharacter("1");
1101         acc.setup_fakeCharacter("2");
1102 
1103         acc.test_sightCharacter(sight);
1104 
1105         assert(gotCharacterInfo_checker.flagged());
1106         assert(!gotAllCharacters_checker.flagged());
1107     }
1108 
1109     // Test netConnected() Does nothing with an empty password
1110     {
1111         TestConnection * con = new TestConnection("name", "localhost",
1112                                                   6767, true);
1113 
1114         TestAccount acc(con);
1115 
1116         acc.test_netConnected();
1117     }
1118 
1119     // Test netConnected() Does nothing with valid username and password,
1120     // but not DISCONNECTED
1121     {
1122         TestConnection * con = new TestConnection("name", "localhost",
1123                                                   6767, true);
1124 
1125         TestAccount acc(con);
1126 
1127         acc.setup_setUsername("foo");
1128         acc.setup_setPassword("foo");
1129         acc.setup_setStatus(TestAccount::LOGGING_IN);
1130 
1131         acc.test_netConnected();
1132     }
1133 
1134     // Test netConnected() works with valid username and password,
1135     {
1136         TestConnection * con = new TestConnection("name", "localhost",
1137                                                   6767, true);
1138 
1139         TestAccount acc(con);
1140 
1141         acc.setup_setUsername("foo");
1142         acc.setup_setPassword("foo");
1143 
1144         acc.test_netConnected();
1145     }
1146 
1147     // Test netDisconnecting() when account is not logged in.
1148     {
1149         TestConnection * con = new TestConnection("name", "localhost",
1150                                                   6767, true);
1151 
1152         TestAccount acc(con);
1153 
1154         bool res = acc.test_netDisconnecting();
1155 
1156         assert(res);
1157     }
1158 
1159     // Test netDisconnecting() when account is logged in, but connection is
1160     // disconnected.
1161     {
1162         TestConnection * con = new TestConnection("name", "localhost",
1163                                                   6767, true);
1164 
1165         TestAccount acc(con);
1166 
1167         acc.setup_setStatus(TestAccount::LOGGED_IN);
1168 
1169         // The fact that the connection is not really connected will be
1170         // caught in Account::logout() safely.
1171         bool res = acc.test_netDisconnecting();
1172 
1173         assert(!res);
1174 
1175         acc.setup_setStatus(TestAccount::DISCONNECTED);
1176     }
1177 
1178     // Test netFailure()
1179     {
1180         TestConnection * con = new TestConnection("name", "localhost",
1181                                                   6767, true);
1182 
1183         TestAccount acc(con);
1184 
1185         acc.test_netFailure("foo");
1186     }
1187 
1188     // Test handleLogoutTimeout()
1189     {
1190         TestConnection * con = new TestConnection("name", "localhost",
1191                                                   6767, true);
1192 
1193         TestAccount acc(con);
1194         SignalFlagger logoutComplete_checker;
1195 
1196         acc.setup_setStatus(TestAccount::LOGGED_IN);
1197         acc.LogoutComplete.connect(sigc::hide(sigc::mem_fun(logoutComplete_checker, &SignalFlagger::set)));
1198 
1199         acc.test_handleLogoutTimeout();
1200 
1201         assert(acc.query_getStatus() == TestAccount::DISCONNECTED);
1202         assert(logoutComplete_checker.flagged());
1203     }
1204 
1205     // Test avatarLogoutResponse() with non Info operation
1206     {
1207         TestConnection * con = new TestConnection("name", "localhost",
1208                                                   6767, true);
1209 
1210         TestAccount acc(con);
1211         SignalFlagger avatarDeactivated_checker;
1212         Atlas::Objects::Operation::Get op;
1213 
1214         acc.AvatarDeactivated.connect(sigc::hide(sigc::mem_fun(avatarDeactivated_checker, &SignalFlagger::set)));
1215 
1216         acc.test_avatarLogoutResponse(op);
1217 
1218         assert(!avatarDeactivated_checker.flagged());
1219     }
1220 
1221     // Test avatarLogoutResponse() with Info operation
1222     {
1223         TestConnection * con = new TestConnection("name", "localhost",
1224                                                   6767, true);
1225 
1226         TestAccount acc(con);
1227         SignalFlagger avatarDeactivated_checker;
1228         Atlas::Objects::Operation::Info op;
1229 
1230         acc.AvatarDeactivated.connect(sigc::hide(sigc::mem_fun(avatarDeactivated_checker, &SignalFlagger::set)));
1231 
1232         acc.test_avatarLogoutResponse(op);
1233 
1234         assert(!avatarDeactivated_checker.flagged());
1235     }
1236 
1237     // Test avatarLogoutResponse() with Info operation with bad args
1238     {
1239         TestConnection * con = new TestConnection("name", "localhost",
1240                                                   6767, true);
1241 
1242         TestAccount acc(con);
1243         SignalFlagger avatarDeactivated_checker;
1244         Atlas::Objects::Operation::Info op;
1245         Atlas::Objects::Root bad_arg;
1246         op->setArgs1(bad_arg);
1247 
1248         acc.AvatarDeactivated.connect(sigc::hide(sigc::mem_fun(avatarDeactivated_checker, &SignalFlagger::set)));
1249 
1250         acc.test_avatarLogoutResponse(op);
1251 
1252         assert(!avatarDeactivated_checker.flagged());
1253     }
1254 
1255     // Test avatarLogoutResponse() with Info operation with logout args, which
1256     // have no args
1257     {
1258         TestConnection * con = new TestConnection("name", "localhost",
1259                                                   6767, true);
1260 
1261         TestAccount acc(con);
1262         SignalFlagger avatarDeactivated_checker;
1263         Atlas::Objects::Operation::Info op;
1264         Atlas::Objects::Operation::Logout info_arg;
1265 
1266         op->setArgs1(info_arg);
1267 
1268         acc.AvatarDeactivated.connect(sigc::hide(sigc::mem_fun(avatarDeactivated_checker, &SignalFlagger::set)));
1269 
1270         acc.test_avatarLogoutResponse(op);
1271 
1272         assert(!avatarDeactivated_checker.flagged());
1273     }
1274 
1275     // Test avatarLogoutResponse() with Info operation with completely well
1276     // formed args for an ID which does not exist
1277     {
1278         TestConnection * con = new TestConnection("name", "localhost",
1279                                                   6767, true);
1280 
1281         TestAccount acc(con);
1282         SignalFlagger avatarDeactivated_checker;
1283         Atlas::Objects::Operation::Info op;
1284         Atlas::Objects::Operation::Logout info_arg;
1285         Atlas::Objects::Entity::Anonymous logout_arg;
1286         std::string fake_char_id("1");
1287 
1288         op->setArgs1(info_arg);
1289         info_arg->setArgs1(logout_arg);
1290         logout_arg->setId(fake_char_id);
1291 
1292         acc.AvatarDeactivated.connect(sigc::hide(sigc::mem_fun(avatarDeactivated_checker, &SignalFlagger::set)));
1293 
1294         acc.test_avatarLogoutResponse(op);
1295 
1296         assert(!avatarDeactivated_checker.flagged());
1297     }
1298 
1299     // Test avatarLogoutResponse() with Info operation with completely well
1300     // formed args for an ID which is one of our characters, but not currently
1301     // active
1302     {
1303         TestConnection * con = new TestConnection("name", "localhost",
1304                                                   6767, true);
1305 
1306         TestAccount acc(con);
1307         SignalFlagger avatarDeactivated_checker;
1308         Atlas::Objects::Operation::Info op;
1309         Atlas::Objects::Operation::Logout info_arg;
1310         Atlas::Objects::Entity::Anonymous logout_arg;
1311         std::string fake_char_id("1");
1312 
1313         op->setArgs1(info_arg);
1314         info_arg->setArgs1(logout_arg);
1315         logout_arg->setId(fake_char_id);
1316         acc.setup_fakeCharacter(fake_char_id);
1317         acc.AvatarDeactivated.connect(sigc::hide(sigc::mem_fun(avatarDeactivated_checker, &SignalFlagger::set)));
1318 
1319         acc.test_avatarLogoutResponse(op);
1320 
1321         assert(!avatarDeactivated_checker.flagged());
1322     }
1323 
1324     // Test avatarLogoutResponse() with Info operation with completely well
1325     // formed args for an ID which is one of our characters, and currently
1326     // active
1327     {
1328         TestConnection * con = new TestConnection("name", "localhost",
1329                                                   6767, true);
1330 
1331         TestAccount acc(con);
1332         SignalFlagger avatarDeactivated_checker;
1333         Atlas::Objects::Operation::Info op;
1334         Atlas::Objects::Operation::Logout info_arg;
1335         Atlas::Objects::Entity::Anonymous logout_arg;
1336         std::string fake_char_id("1");
1337         Eris::Avatar * ea = new TestAvatar(&acc, fake_char_id);
1338 
1339         op->setArgs1(info_arg);
1340         info_arg->setArgs1(logout_arg);
1341         logout_arg->setId(fake_char_id);
1342         acc.setup_fakeCharacter(fake_char_id);
1343         acc.setup_insertActiveCharacters(ea);
1344         acc.AvatarDeactivated.connect(sigc::hide(sigc::mem_fun(avatarDeactivated_checker, &SignalFlagger::set)));
1345 
1346         acc.test_avatarLogoutResponse(op);
1347 
1348         assert(avatarDeactivated_checker.flagged());
1349     }
1350 
1351 
1352     return 0;
1353 }
1354