# -*- coding: utf-8 -*- """ Testing suite for contrib folder """ import time import datetime from anything import Anything from django.test import override_settings from evennia.commands.default.tests import CommandTest from evennia.utils.test_resources import EvenniaTest, mockdelay, mockdeferLater from mock import Mock, patch # Testing of rplanguage module from evennia.contrib import rplanguage mtrans = {"testing": "1", "is": "2", "a": "3", "human": "4"} atrans = ["An", "automated", "advantageous", "repeatable", "faster"] text = ( "Automated testing is advantageous for a number of reasons: " "tests may be executed Continuously without the need for human " "intervention, They are easily repeatable, and often faster." ) class TestLanguage(EvenniaTest): def setUp(self): super().setUp() rplanguage.add_language( key="testlang", word_length_variance=1, noun_prefix="bara", noun_postfix="'y", manual_translations=mtrans, auto_translations=atrans, force=True, ) rplanguage.add_language( key="binary", phonemes="oo ii a ck w b d t", grammar="cvvv cvv cvvcv cvvcvv cvvvc cvvvcvv cvvc", noun_prefix="beep-", word_length_variance=4, ) def tearDown(self): super().tearDown() rplanguage._LANGUAGE_HANDLER.delete() rplanguage._LANGUAGE_HANDLER = None def test_obfuscate_language(self): result0 = rplanguage.obfuscate_language(text, level=0.0, language="testlang") self.assertEqual(result0, text) result1 = rplanguage.obfuscate_language(text, level=1.0, language="testlang") result2 = rplanguage.obfuscate_language(text, level=1.0, language="testlang") result3 = rplanguage.obfuscate_language(text, level=1.0, language="binary") self.assertNotEqual(result1, text) self.assertNotEqual(result3, text) result1, result2 = result1.split(), result2.split() self.assertEqual(result1[:4], result2[:4]) self.assertEqual(result1[1], "1") self.assertEqual(result1[2], "2") self.assertEqual(result2[-1], result2[-1]) def test_faulty_language(self): self.assertRaises( rplanguage.LanguageError, rplanguage.add_language, key="binary2", phonemes="w b d t oe ee, oo e o a wh dw bw", # erroneous comma grammar="cvvv cvv cvvcv cvvcvvo cvvvc cvvvcvv cvvc c v cc vv ccvvc ccvvccvv ", vowels="oea", word_length_variance=4, ) def test_available_languages(self): self.assertEqual(list(sorted(rplanguage.available_languages())), ["binary", "testlang"]) def test_obfuscate_whisper(self): self.assertEqual(rplanguage.obfuscate_whisper(text, level=0.0), text) assert rplanguage.obfuscate_whisper(text, level=0.1).startswith( "-utom-t-d t-sting is -dv-nt-g-ous for - numb-r of r--sons: t-sts m-y b- -x-cut-d Continuously" ) assert rplanguage.obfuscate_whisper(text, level=0.5).startswith( "--------- --s---- -s -----------s f-- - ------ -f ---s--s: --s-s " ) self.assertEqual(rplanguage.obfuscate_whisper(text, level=1.0), "...") # Testing of emoting / sdesc / recog system from evennia import create_object from evennia.contrib import rpsystem sdesc0 = "A nice sender of emotes" sdesc1 = "The first receiver of emotes." sdesc2 = "Another nice colliding sdesc-guy for tests" recog01 = "Mr Receiver" recog02 = "Mr Receiver2" recog10 = "Mr Sender" emote = 'With a flair, /me looks at /first and /colliding sdesc-guy. She says "This is a test."' case_emote = "/me looks at /first, then /FIRST, /First and /Colliding twice." class TestRPSystem(EvenniaTest): maxDiff = None def setUp(self): super().setUp() self.room = create_object(rpsystem.ContribRPRoom, key="Location") self.speaker = create_object(rpsystem.ContribRPCharacter, key="Sender", location=self.room) self.receiver1 = create_object( rpsystem.ContribRPCharacter, key="Receiver1", location=self.room ) self.receiver2 = create_object( rpsystem.ContribRPCharacter, key="Receiver2", location=self.room ) def test_ordered_permutation_regex(self): self.assertEqual( rpsystem.ordered_permutation_regex(sdesc0), "/[0-9]*-*A\\ nice\\ sender\\ of\\ emotes(?=\\W|$)+|" "/[0-9]*-*nice\\ sender\\ of\\ emotes(?=\\W|$)+|" "/[0-9]*-*A\\ nice\\ sender\\ of(?=\\W|$)+|" "/[0-9]*-*sender\\ of\\ emotes(?=\\W|$)+|" "/[0-9]*-*nice\\ sender\\ of(?=\\W|$)+|" "/[0-9]*-*A\\ nice\\ sender(?=\\W|$)+|" "/[0-9]*-*nice\\ sender(?=\\W|$)+|" "/[0-9]*-*of\\ emotes(?=\\W|$)+|" "/[0-9]*-*sender\\ of(?=\\W|$)+|" "/[0-9]*-*A\\ nice(?=\\W|$)+|" "/[0-9]*-*emotes(?=\\W|$)+|" "/[0-9]*-*sender(?=\\W|$)+|" "/[0-9]*-*nice(?=\\W|$)+|" "/[0-9]*-*of(?=\\W|$)+|" "/[0-9]*-*A(?=\\W|$)+", ) def test_sdesc_handler(self): self.speaker.sdesc.add(sdesc0) self.assertEqual(self.speaker.sdesc.get(), sdesc0) self.speaker.sdesc.add("This is {#324} ignored") self.assertEqual(self.speaker.sdesc.get(), "This is 324 ignored") self.speaker.sdesc.add("Testing three words") self.assertEqual( self.speaker.sdesc.get_regex_tuple()[0].pattern, "/[0-9]*-*Testing\ three\ words(?=\W|$)+|" "/[0-9]*-*Testing\ three(?=\W|$)+|" "/[0-9]*-*three\ words(?=\W|$)+|" "/[0-9]*-*Testing(?=\W|$)+|" "/[0-9]*-*three(?=\W|$)+|" "/[0-9]*-*words(?=\W|$)+", ) def test_recog_handler(self): self.speaker.sdesc.add(sdesc0) self.receiver1.sdesc.add(sdesc1) self.speaker.recog.add(self.receiver1, recog01) self.speaker.recog.add(self.receiver2, recog02) self.assertEqual(self.speaker.recog.get(self.receiver1), recog01) self.assertEqual(self.speaker.recog.get(self.receiver2), recog02) self.assertEqual( self.speaker.recog.get_regex_tuple(self.receiver1)[0].pattern, "/[0-9]*-*Mr\\ Receiver(?=\\W|$)+|/[0-9]*-*Receiver(?=\\W|$)+|/[0-9]*-*Mr(?=\\W|$)+", ) self.speaker.recog.remove(self.receiver1) self.assertEqual(self.speaker.recog.get(self.receiver1), sdesc1) self.assertEqual(self.speaker.recog.all(), {"Mr Receiver2": self.receiver2}) def test_parse_language(self): self.assertEqual( rpsystem.parse_language(self.speaker, emote), ( "With a flair, /me looks at /first and /colliding sdesc-guy. She says {##0}", {"##0": (None, '"This is a test."')}, ), ) def parse_sdescs_and_recogs(self): speaker = self.speaker speaker.sdesc.add(sdesc0) self.receiver1.sdesc.add(sdesc1) self.receiver2.sdesc.add(sdesc2) candidates = (self.receiver1, self.receiver2) result = ( 'With a flair, {#9} looks at {#10} and {#11}. She says "This is a test."', { "#11": "Another nice colliding sdesc-guy for tests", "#10": "The first receiver of emotes.", "#9": "A nice sender of emotes", }, ) self.assertEqual(rpsystem.parse_sdescs_and_recogs( speaker, candidates, emote, case_sensitive=False), result) self.speaker.recog.add(self.receiver1, recog01) self.assertEqual(rpsystem.parse_sdescs_and_recogs( speaker, candidates, emote, case_sensitive=False), result) def test_send_emote(self): speaker = self.speaker receiver1 = self.receiver1 receiver2 = self.receiver2 receivers = [speaker, receiver1, receiver2] speaker.sdesc.add(sdesc0) receiver1.sdesc.add(sdesc1) receiver2.sdesc.add(sdesc2) speaker.msg = lambda text, **kwargs: setattr(self, "out0", text) receiver1.msg = lambda text, **kwargs: setattr(self, "out1", text) receiver2.msg = lambda text, **kwargs: setattr(self, "out2", text) rpsystem.send_emote(speaker, receivers, emote, case_sensitive=False) self.assertEqual( self.out0, "With a flair, |bSender|n looks at |bThe first receiver of emotes.|n " 'and |bAnother nice colliding sdesc-guy for tests|n. She says |w"This is a test."|n', ) self.assertEqual( self.out1, "With a flair, |bA nice sender of emotes|n looks at |bReceiver1|n and " '|bAnother nice colliding sdesc-guy for tests|n. She says |w"This is a test."|n', ) self.assertEqual( self.out2, "With a flair, |bA nice sender of emotes|n looks at |bThe first " 'receiver of emotes.|n and |bReceiver2|n. She says |w"This is a test."|n', ) def test_send_case_sensitive_emote(self): """Test new case-sensitive rp-parsing""" speaker = self.speaker receiver1 = self.receiver1 receiver2 = self.receiver2 receivers = [speaker, receiver1, receiver2] speaker.sdesc.add(sdesc0) receiver1.sdesc.add(sdesc1) receiver2.sdesc.add(sdesc2) speaker.msg = lambda text, **kwargs: setattr(self, "out0", text) receiver1.msg = lambda text, **kwargs: setattr(self, "out1", text) receiver2.msg = lambda text, **kwargs: setattr(self, "out2", text) rpsystem.send_emote(speaker, receivers, case_emote) self.assertEqual( self.out0, "|bSender|n looks at |bthe first receiver of emotes.|n, then " "|bTHE FIRST RECEIVER OF EMOTES.|n, |bThe first receiver of emotes.|n and " "|bAnother nice colliding sdesc-guy for tests|n twice." ) self.assertEqual( self.out1, "|bA nice sender of emotes|n looks at |bReceiver1|n, then |bReceiver1|n, " "|bReceiver1|n and |bAnother nice colliding sdesc-guy for tests|n twice." ) self.assertEqual( self.out2, "|bA nice sender of emotes|n looks at |bthe first receiver of emotes.|n, " "then |bTHE FIRST RECEIVER OF EMOTES.|n, |bThe first receiver of " "emotes.|n and |bReceiver2|n twice." ) def test_rpsearch(self): self.speaker.sdesc.add(sdesc0) self.receiver1.sdesc.add(sdesc1) self.receiver2.sdesc.add(sdesc2) self.speaker.msg = lambda text, **kwargs: setattr(self, "out0", text) self.assertEqual(self.speaker.search("receiver of emotes"), self.receiver1) self.assertEqual(self.speaker.search("colliding"), self.receiver2) def test_regex_tuple_from_key_alias(self): self.speaker.aliases.add("foo bar") self.speaker.aliases.add("this thing is a long thing") t0 = time.time() result = rpsystem.regex_tuple_from_key_alias(self.speaker) t1 = time.time() result = rpsystem.regex_tuple_from_key_alias(self.speaker) t2 = time.time() # print(f"t1: {t1 - t0}, t2: {t2 - t1}") self.assertLess(t2-t1, 10**-4) self.assertEqual(result, (Anything, self.speaker, self.speaker.key)) class TestRPSystemCommands(CommandTest): def setUp(self): super().setUp() self.char1.swap_typeclass(rpsystem.ContribRPCharacter) self.char2.swap_typeclass(rpsystem.ContribRPCharacter) def test_commands(self): self.call( rpsystem.CmdSdesc(), "Foobar Character", "Char's sdesc was set to 'Foobar Character'." ) self.call( rpsystem.CmdSdesc(), "BarFoo Character", "Char2's sdesc was set to 'BarFoo Character'.", caller=self.char2, ) self.call(rpsystem.CmdSay(), "Hello!", 'Char says, "Hello!"') self.call(rpsystem.CmdEmote(), "/me smiles to /BarFoo.", "Char smiles to BarFoo Character") self.call( rpsystem.CmdPose(), "stands by the bar", "Pose will read 'Foobar Character stands by the bar.'.", ) self.call( rpsystem.CmdRecog(), "barfoo as friend", "Char will now remember BarFoo Character as friend.", ) self.call( rpsystem.CmdRecog(), "", "Currently recognized (use 'recog as ' to add new " "and 'forget ' to remove):\n friend (BarFoo Character)", ) self.call( rpsystem.CmdRecog(), "friend", "Char will now know them only as 'BarFoo Character'", cmdstring="forget", ) # Testing of ExtendedRoom contrib from django.conf import settings from evennia.contrib import extended_room from evennia.objects.objects import DefaultRoom class ForceUTCDatetime(datetime.datetime): """Force UTC datetime.""" @classmethod def fromtimestamp(cls, timestamp): """Force fromtimestamp to run with naive datetimes.""" return datetime.datetime.utcfromtimestamp(timestamp) @patch("evennia.contrib.extended_room.datetime.datetime", ForceUTCDatetime) # mock gametime to return April 9, 2064, at 21:06 (spring evening) @patch("evennia.utils.gametime.gametime", new=Mock(return_value=2975000766)) class TestExtendedRoom(CommandTest): room_typeclass = extended_room.ExtendedRoom DETAIL_DESC = "A test detail." SPRING_DESC = "A spring description." OLD_DESC = "Old description." settings.TIME_ZONE = "UTC" def setUp(self): super().setUp() self.room1.ndb.last_timeslot = "afternoon" self.room1.ndb.last_season = "winter" self.room1.db.details = {"testdetail": self.DETAIL_DESC} self.room1.db.spring_desc = self.SPRING_DESC self.room1.db.desc = self.OLD_DESC def test_return_appearance(self): # get the appearance of a non-extended room for contrast purposes old_desc = DefaultRoom.return_appearance(self.room1, self.char1) # the new appearance should be the old one, but with the desc switched self.assertEqual( old_desc.replace(self.OLD_DESC, self.SPRING_DESC), self.room1.return_appearance(self.char1), ) self.assertEqual("spring", self.room1.ndb.last_season) self.assertEqual("evening", self.room1.ndb.last_timeslot) def test_return_detail(self): self.assertEqual(self.DETAIL_DESC, self.room1.return_detail("testdetail")) def test_cmdextendedlook(self): rid = self.room1.id self.call( extended_room.CmdExtendedRoomLook(), "here", "Room(#{})\n{}".format(rid, self.SPRING_DESC), ) self.call(extended_room.CmdExtendedRoomLook(), "testdetail", self.DETAIL_DESC) self.call( extended_room.CmdExtendedRoomLook(), "nonexistent", "Could not find 'nonexistent'." ) def test_cmdsetdetail(self): self.call(extended_room.CmdExtendedRoomDetail(), "", "Details on Room") self.call( extended_room.CmdExtendedRoomDetail(), "thingie = newdetail with spaces", "Detail set 'thingie': 'newdetail with spaces'", ) self.call(extended_room.CmdExtendedRoomDetail(), "thingie", "Detail 'thingie' on Room:\n") self.call( extended_room.CmdExtendedRoomDetail(), "/del thingie", "Detail thingie deleted, if it existed.", cmdstring="detail", ) self.call(extended_room.CmdExtendedRoomDetail(), "thingie", "Detail 'thingie' not found.") # Test with aliases self.call(extended_room.CmdExtendedRoomDetail(), "", "Details on Room") self.call( extended_room.CmdExtendedRoomDetail(), "thingie;other;stuff = newdetail with spaces", "Detail set 'thingie;other;stuff': 'newdetail with spaces'", ) self.call(extended_room.CmdExtendedRoomDetail(), "thingie", "Detail 'thingie' on Room:\n") self.call(extended_room.CmdExtendedRoomDetail(), "other", "Detail 'other' on Room:\n") self.call(extended_room.CmdExtendedRoomDetail(), "stuff", "Detail 'stuff' on Room:\n") self.call( extended_room.CmdExtendedRoomDetail(), "/del other;stuff", "Detail other;stuff deleted, if it existed.", ) self.call(extended_room.CmdExtendedRoomDetail(), "other", "Detail 'other' not found.") self.call(extended_room.CmdExtendedRoomDetail(), "stuff", "Detail 'stuff' not found.") def test_cmdgametime(self): self.call(extended_room.CmdExtendedRoomGameTime(), "", "It's a spring day, in the evening.") # Test the contrib barter system from evennia.contrib import barter class TestBarter(CommandTest): def setUp(self): super().setUp() self.tradeitem1 = create_object(key="TradeItem1", location=self.char1) self.tradeitem2 = create_object(key="TradeItem2", location=self.char1) self.tradeitem3 = create_object(key="TradeItem3", location=self.char2) def test_tradehandler_base(self): self.char1.msg = Mock() self.char2.msg = Mock() # test all methods of the tradehandler handler = barter.TradeHandler(self.char1, self.char2) self.assertEqual(handler.part_a, self.char1) self.assertEqual(handler.part_b, self.char2) handler.msg_other(self.char1, "Want to trade?") handler.msg_other(self.char2, "Yes!") handler.msg_other(None, "Talking to myself...") self.assertEqual(self.char2.msg.mock_calls[0][1][0], "Want to trade?") self.assertEqual(self.char1.msg.mock_calls[0][1][0], "Yes!") self.assertEqual(self.char1.msg.mock_calls[1][1][0], "Talking to myself...") self.assertEqual(handler.get_other(self.char1), self.char2) handler.finish(force=True) def test_tradehandler_joins(self): handler = barter.TradeHandler(self.char1, self.char2) self.assertTrue(handler.join(self.char2)) self.assertTrue(handler.unjoin(self.char2)) self.assertFalse(handler.join(self.char1)) self.assertFalse(handler.unjoin(self.char1)) handler.finish(force=True) def test_tradehandler_offers(self): handler = barter.TradeHandler(self.char1, self.char2) handler.join(self.char2) handler.offer(self.char1, self.tradeitem1, self.tradeitem2) self.assertEqual(handler.part_a_offers, [self.tradeitem1, self.tradeitem2]) self.assertFalse(handler.part_a_accepted) self.assertFalse(handler.part_b_accepted) handler.offer(self.char2, self.tradeitem3) self.assertEqual(handler.list(), ([self.tradeitem1, self.tradeitem2], [self.tradeitem3])) self.assertEqual(handler.search("TradeItem2"), self.tradeitem2) self.assertEqual(handler.search("TradeItem3"), self.tradeitem3) self.assertEqual(handler.search("nonexisting"), None) self.assertFalse(handler.finish()) # should fail since offer not yet accepted handler.accept(self.char1) handler.decline(self.char1) handler.accept(self.char2) handler.accept(self.char1) # should trigger handler.finish() automatically self.assertEqual(self.tradeitem1.location, self.char2) self.assertEqual(self.tradeitem2.location, self.char2) self.assertEqual(self.tradeitem3.location, self.char1) def test_cmdtrade(self): self.call( barter.CmdTrade(), "Char2 : Hey wanna trade?", 'You say, "Hey wanna trade?"', caller=self.char1, ) self.call(barter.CmdTrade(), "Char decline : Nope!", 'You say, "Nope!"', caller=self.char2) self.call( barter.CmdTrade(), "Char2 : Hey wanna trade?", 'You say, "Hey wanna trade?"', caller=self.char1, ) self.call(barter.CmdTrade(), "Char accept : Sure!", 'You say, "Sure!"', caller=self.char2) self.call( barter.CmdOffer(), "TradeItem3", "Your trade action: You offer TradeItem3", caller=self.char2, ) self.call( barter.CmdOffer(), "TradeItem1 : Here's my offer.", 'You say, "Here\'s my offer."\n [You offer TradeItem1]', ) self.call( barter.CmdAccept(), "", "Your trade action: You accept the offer. Char2 must now also accept", ) self.call( barter.CmdDecline(), "", "Your trade action: You change your mind, declining the current offer.", ) self.call( barter.CmdAccept(), ": Sounds good.", 'You say, "Sounds good."\n' " [You accept the offer. Char must now also accept.", caller=self.char2, ) self.call( barter.CmdDecline(), ":No way!", 'You say, "No way!"\n [You change your mind, declining the current offer.]', caller=self.char2, ) self.call( barter.CmdOffer(), "TradeItem1, TradeItem2 : My final offer!", 'You say, "My final offer!"\n [You offer TradeItem1 and TradeItem2]', ) self.call( barter.CmdAccept(), "", "Your trade action: You accept the offer. Char2 must now also accept.", caller=self.char1, ) self.call(barter.CmdStatus(), "", "Offered by Char:", caller=self.char2) self.tradeitem1.db.desc = "A great offer." self.call(barter.CmdEvaluate(), "TradeItem1", "A great offer.") self.call( barter.CmdAccept(), ":Ok then.", 'You say, "Ok then."\n [You accept the deal.', caller=self.char2, ) self.assertEqual(self.tradeitem1.location, self.char2) self.assertEqual(self.tradeitem2.location, self.char2) self.assertEqual(self.tradeitem3.location, self.char1) def test_cmdtradehelp(self): self.call( barter.CmdTrade(), "Char2 : Hey wanna trade?", 'You say, "Hey wanna trade?"', caller=self.char1, ) self.call(barter.CmdTradeHelp(), "", "Trading commands\n", caller=self.char1) self.call( barter.CmdFinish(), ": Ending.", 'You say, "Ending."\n [You aborted trade. No deal was made.]', ) # Test wilderness from evennia.contrib import wilderness from evennia import DefaultCharacter class TestWilderness(EvenniaTest): def setUp(self): super().setUp() self.char1 = create_object(DefaultCharacter, key="char1") self.char2 = create_object(DefaultCharacter, key="char2") def get_wilderness_script(self, name="default"): w = wilderness.WildernessScript.objects.get("default") return w def test_create_wilderness_default_name(self): wilderness.create_wilderness() w = self.get_wilderness_script() self.assertIsNotNone(w) def test_create_wilderness_custom_name(self): name = "customname" wilderness.create_wilderness(name) w = self.get_wilderness_script(name) self.assertIsNotNone(w) def test_enter_wilderness(self): wilderness.create_wilderness() wilderness.enter_wilderness(self.char1) self.assertIsInstance(self.char1.location, wilderness.WildernessRoom) w = self.get_wilderness_script() self.assertEqual(w.db.itemcoordinates[self.char1], (0, 0)) def test_enter_wilderness_custom_coordinates(self): wilderness.create_wilderness() wilderness.enter_wilderness(self.char1, coordinates=(1, 2)) self.assertIsInstance(self.char1.location, wilderness.WildernessRoom) w = self.get_wilderness_script() self.assertEqual(w.db.itemcoordinates[self.char1], (1, 2)) def test_enter_wilderness_custom_name(self): name = "customnname" wilderness.create_wilderness(name) wilderness.enter_wilderness(self.char1, name=name) self.assertIsInstance(self.char1.location, wilderness.WildernessRoom) def test_wilderness_correct_exits(self): wilderness.create_wilderness() wilderness.enter_wilderness(self.char1) # By default we enter at a corner (0, 0), so only a few exits should # be visible / traversable exits = [ i for i in self.char1.location.contents if i.destination and (i.access(self.char1, "view") or i.access(self.char1, "traverse")) ] self.assertEqual(len(exits), 3) exitsok = ["north", "northeast", "east"] for each_exit in exitsok: self.assertTrue(any([e for e in exits if e.key == each_exit])) # If we move to another location not on an edge, then all directions # should be visible / traversable wilderness.enter_wilderness(self.char1, coordinates=(1, 1)) exits = [ i for i in self.char1.location.contents if i.destination and (i.access(self.char1, "view") or i.access(self.char1, "traverse")) ] self.assertEqual(len(exits), 8) exitsok = [ "north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest", ] for each_exit in exitsok: self.assertTrue(any([e for e in exits if e.key == each_exit])) def test_room_creation(self): # Pretend that both char1 and char2 are connected... self.char1.sessions.add(1) self.char2.sessions.add(1) self.assertTrue(self.char1.has_account) self.assertTrue(self.char2.has_account) wilderness.create_wilderness() w = self.get_wilderness_script() # We should have no unused room after moving the first account in. self.assertEqual(len(w.db.unused_rooms), 0) w.move_obj(self.char1, (0, 0)) self.assertEqual(len(w.db.unused_rooms), 0) # And also no unused room after moving the second one in. w.move_obj(self.char2, (1, 1)) self.assertEqual(len(w.db.unused_rooms), 0) # But if char2 moves into char1's room, we should have one unused room # Which should be char2's old room that got created. w.move_obj(self.char2, (0, 0)) self.assertEqual(len(w.db.unused_rooms), 1) self.assertEqual(self.char1.location, self.char2.location) # And if char2 moves back out, that unused room should be put back to # use again. w.move_obj(self.char2, (1, 1)) self.assertNotEqual(self.char1.location, self.char2.location) self.assertEqual(len(w.db.unused_rooms), 0) def test_get_new_coordinates(self): loc = (1, 1) directions = { "north": (1, 2), "northeast": (2, 2), "east": (2, 1), "southeast": (2, 0), "south": (1, 0), "southwest": (0, 0), "west": (0, 1), "northwest": (0, 2), } for (direction, correct_loc) in directions.items(): # Not compatible with Python 3 new_loc = wilderness.get_new_coordinates(loc, direction) self.assertEqual(new_loc, correct_loc, direction) # Testing chargen contrib from evennia.contrib import chargen class TestChargen(CommandTest): def test_ooclook(self): self.call( chargen.CmdOOCLook(), "foo", "You have no characters to look at", caller=self.account ) self.call( chargen.CmdOOCLook(), "", "You, TestAccount, are an OOC ghost without form.", caller=self.account, ) def test_charcreate(self): self.call( chargen.CmdOOCCharacterCreate(), "testchar", "The character testchar was successfully created!", caller=self.account, ) self.call( chargen.CmdOOCCharacterCreate(), "testchar", "Character testchar already exists.", caller=self.account, ) self.assertTrue(self.account.db._character_dbrefs) self.call( chargen.CmdOOCLook(), "", "You, TestAccount, are an OOC ghost without form.", caller=self.account, ) self.call(chargen.CmdOOCLook(), "testchar", "testchar(", caller=self.account) # Testing clothing contrib from evennia.contrib import clothing class TestClothingCmd(CommandTest): def test_clothingcommands(self): wearer = create_object(clothing.ClothedCharacter, key="Wearer") friend = create_object(clothing.ClothedCharacter, key="Friend") room = create_object(DefaultRoom, key="room") wearer.location = room friend.location = room # Make a test hat test_hat = create_object(clothing.Clothing, key="test hat") test_hat.db.clothing_type = "hat" test_hat.location = wearer # Make a test scarf test_scarf = create_object(clothing.Clothing, key="test scarf") test_scarf.db.clothing_type = "accessory" test_scarf.location = wearer # Test wear command self.call(clothing.CmdWear(), "", "Usage: wear [wear style]", caller=wearer) self.call(clothing.CmdWear(), "hat", "Wearer puts on test hat.", caller=wearer) self.call( clothing.CmdWear(), "scarf stylishly", "Wearer wears test scarf stylishly.", caller=wearer, ) # Test cover command. self.call( clothing.CmdCover(), "", "Usage: cover [with] ", caller=wearer, ) self.call( clothing.CmdCover(), "hat with scarf", "Wearer covers test hat with test scarf.", caller=wearer, ) # Test remove command. self.call(clothing.CmdRemove(), "", "Could not find ''.", caller=wearer) self.call( clothing.CmdRemove(), "hat", "You have to take off test scarf first.", caller=wearer ) self.call( clothing.CmdRemove(), "scarf", "Wearer removes test scarf, revealing test hat.", caller=wearer, ) # Test uncover command. test_scarf.wear(wearer, True) test_hat.db.covered_by = test_scarf self.call(clothing.CmdUncover(), "", "Usage: uncover ", caller=wearer) self.call(clothing.CmdUncover(), "hat", "Wearer uncovers test hat.", caller=wearer) # Test drop command. test_hat.db.covered_by = test_scarf self.call(clothing.CmdDrop(), "", "Drop what?", caller=wearer) self.call( clothing.CmdDrop(), "hat", "You can't drop that because it's covered by test scarf.", caller=wearer, ) self.call(clothing.CmdDrop(), "scarf", "You drop test scarf.", caller=wearer) # Test give command. self.call( clothing.CmdGive(), "", "Usage: give = ", caller=wearer ) self.call( clothing.CmdGive(), "hat = Friend", "Wearer removes test hat.|You give test hat to Friend.", caller=wearer, ) # Test inventory command. self.call( clothing.CmdInventory(), "", "You are not carrying or wearing anything.", caller=wearer ) class TestClothingFunc(EvenniaTest): def test_clothingfunctions(self): wearer = create_object(clothing.ClothedCharacter, key="Wearer") room = create_object(DefaultRoom, key="room") wearer.location = room # Make a test hat test_hat = create_object(clothing.Clothing, key="test hat") test_hat.db.clothing_type = "hat" test_hat.location = wearer # Make a test shirt test_shirt = create_object(clothing.Clothing, key="test shirt") test_shirt.db.clothing_type = "top" test_shirt.location = wearer # Make a test pants test_pants = create_object(clothing.Clothing, key="test pants") test_pants.db.clothing_type = "bottom" test_pants.location = wearer test_hat.wear(wearer, "on the head") self.assertEqual(test_hat.db.worn, "on the head") test_hat.remove(wearer) self.assertEqual(test_hat.db.worn, False) test_hat.worn = True test_hat.at_get(wearer) self.assertEqual(test_hat.db.worn, False) clothes_list = [test_shirt, test_hat, test_pants] self.assertEqual( clothing.order_clothes_list(clothes_list), [test_hat, test_shirt, test_pants] ) test_hat.wear(wearer, True) test_pants.wear(wearer, True) self.assertEqual(clothing.get_worn_clothes(wearer), [test_hat, test_pants]) self.assertEqual( clothing.clothing_type_count(clothes_list), {"hat": 1, "top": 1, "bottom": 1} ) self.assertEqual(clothing.single_type_count(clothes_list, "hat"), 1) # Testing custom_gametime from evennia.contrib import custom_gametime def _testcallback(): pass @patch("evennia.utils.gametime.gametime", new=Mock(return_value=2975000898.46)) class TestCustomGameTime(EvenniaTest): def tearDown(self): if hasattr(self, "timescript"): self.timescript.stop() def test_time_to_tuple(self): self.assertEqual(custom_gametime.time_to_tuple(10000, 34, 2, 4, 6, 1), (294, 2, 0, 0, 0, 0)) self.assertEqual(custom_gametime.time_to_tuple(10000, 3, 3, 4), (3333, 0, 0, 1)) self.assertEqual(custom_gametime.time_to_tuple(100000, 239, 24, 3), (418, 4, 0, 2)) def test_gametime_to_realtime(self): self.assertEqual(custom_gametime.gametime_to_realtime(days=2, mins=4), 86520.0) self.assertEqual( custom_gametime.gametime_to_realtime(format=True, days=2), (0, 0, 0, 1, 0, 0, 0) ) def test_realtime_to_gametime(self): self.assertEqual(custom_gametime.realtime_to_gametime(days=3, mins=34), 349680.0) self.assertEqual( custom_gametime.realtime_to_gametime(days=3, mins=34, format=True), (0, 0, 0, 4, 1, 8, 0), ) self.assertEqual( custom_gametime.realtime_to_gametime(format=True, days=3, mins=4), (0, 0, 0, 4, 0, 8, 0) ) def test_custom_gametime(self): self.assertEqual(custom_gametime.custom_gametime(), (102, 5, 2, 6, 21, 8, 18)) self.assertEqual(custom_gametime.custom_gametime(absolute=True), (102, 5, 2, 6, 21, 8, 18)) def test_real_seconds_until(self): self.assertEqual( custom_gametime.real_seconds_until(year=2300, month=12, day=7), 31911667199.77 ) def test_schedule(self): self.timescript = custom_gametime.schedule(_testcallback, repeat=True, min=5, sec=0) self.assertEqual(self.timescript.interval, 1700.7699999809265) # Test dice module from evennia.contrib import dice # noqa @patch("evennia.contrib.dice.randint", return_value=5) class TestDice(CommandTest): def test_roll_dice(self, mocked_randint): self.assertEqual(dice.roll_dice(6, 6, modifier=("+", 4)), mocked_randint() * 6 + 4) self.assertEqual(dice.roll_dice(6, 6, conditional=("<", 35)), True) self.assertEqual(dice.roll_dice(6, 6, conditional=(">", 33)), False) def test_cmddice(self, mocked_randint): self.call( dice.CmdDice(), "3d6 + 4", "You roll 3d6 + 4.| Roll(s): 5, 5 and 5. Total result is 19." ) self.call(dice.CmdDice(), "100000d1000", "The maximum roll allowed is 10000d10000.") self.call(dice.CmdDice(), "/secret 3d6 + 4", "You roll 3d6 + 4 (secret, not echoed).") # Test email-login from evennia.contrib import email_login # noqa class TestEmailLogin(CommandTest): def test_connect(self): self.call( email_login.CmdUnconnectedConnect(), "mytest@test.com test", "The email 'mytest@test.com' does not match any accounts.", ) self.call( email_login.CmdUnconnectedCreate(), '"mytest" mytest@test.com test11111', "A new account 'mytest' was created. Welcome!", ) self.call( email_login.CmdUnconnectedConnect(), "mytest@test.com test11111", "", caller=self.account.sessions.get()[0], ) def test_quit(self): self.call(email_login.CmdUnconnectedQuit(), "", "", caller=self.account.sessions.get()[0]) def test_unconnectedlook(self): self.call(email_login.CmdUnconnectedLook(), "", "==========") def test_unconnectedhelp(self): self.call(email_login.CmdUnconnectedHelp(), "", "You are not yet logged into the game.") # test gendersub contrib from evennia.contrib import gendersub class TestGenderSub(CommandTest): def test_setgender(self): self.call(gendersub.SetGender(), "male", "Your gender was set to male.") self.call(gendersub.SetGender(), "ambiguous", "Your gender was set to ambiguous.") self.call(gendersub.SetGender(), "Foo", "Usage: @gender") def test_gendercharacter(self): char = create_object(gendersub.GenderCharacter, key="Gendered", location=self.room1) txt = "Test |p gender" self.assertEqual( gendersub._RE_GENDER_PRONOUN.sub(char._get_pronoun, txt), "Test their gender" ) with patch("evennia.contrib.gendersub.DefaultCharacter.msg") as mock_msg: char.db.gender = "female" char.msg("Test |p gender") mock_msg.assert_called_with("Test her gender", from_obj=None, session=None) # test health bar contrib from evennia.contrib import health_bar class TestHealthBar(EvenniaTest): def test_healthbar(self): expected_bar_str = "|[R|w|n|[B|w test0 / 200test |n" self.assertEqual( health_bar.display_meter( 0, 200, length=40, pre_text="test", post_text="test", align="center" ), expected_bar_str, ) expected_bar_str = "|[R|w |n|[B|w test24 / 200test |n" self.assertEqual( health_bar.display_meter( 24, 200, length=40, pre_text="test", post_text="test", align="center" ), expected_bar_str, ) expected_bar_str = "|[Y|w test100 /|n|[B|w 200test |n" self.assertEqual( health_bar.display_meter( 100, 200, length=40, pre_text="test", post_text="test", align="center" ), expected_bar_str, ) expected_bar_str = "|[G|w test180 / 200test |n|[B|w |n" self.assertEqual( health_bar.display_meter( 180, 200, length=40, pre_text="test", post_text="test", align="center" ), expected_bar_str, ) expected_bar_str = "|[G|w test200 / 200test |n|[B|w|n" self.assertEqual( health_bar.display_meter( 200, 200, length=40, pre_text="test", post_text="test", align="center" ), expected_bar_str, ) # test mail contrib from evennia.contrib import mail class TestMail(CommandTest): def test_mail(self): self.call(mail.CmdMail(), "2", "'2' is not a valid mail id.", caller=self.account) self.call(mail.CmdMail(), "test", "'test' is not a valid mail id.", caller=self.account) self.call(mail.CmdMail(), "", "There are no messages in your inbox.", caller=self.account) self.call( mail.CmdMailCharacter(), "Char=Message 1", "You have received a new @mail from Char|You sent your message.", caller=self.char1, ) self.call( mail.CmdMailCharacter(), "Char=Message 2", "You sent your message.", caller=self.char2 ) self.call( mail.CmdMail(), "TestAccount2=Message 2", "You have received a new @mail from TestAccount2", caller=self.account2, ) self.call( mail.CmdMail(), "TestAccount=Message 1", "You sent your message.", caller=self.account2 ) self.call( mail.CmdMail(), "TestAccount=Message 2", "You sent your message.", caller=self.account2 ) self.call(mail.CmdMail(), "", "| ID From Subject", caller=self.account) self.call(mail.CmdMail(), "2", "From: TestAccount2", caller=self.account) self.call( mail.CmdMail(), "/forward TestAccount2 = 1/Forward message", "You sent your message.|Message forwarded.", caller=self.account, ) self.call( mail.CmdMail(), "/reply 2=Reply Message2", "You sent your message.", caller=self.account ) self.call(mail.CmdMail(), "/delete 2", "Message 2 deleted", caller=self.account) # test map builder contrib from evennia.contrib import mapbuilder class TestMapBuilder(CommandTest): def test_cmdmapbuilder(self): self.call( mapbuilder.CmdMapBuilder(), "evennia.contrib.mapbuilder.EXAMPLE1_MAP evennia.contrib.mapbuilder.EXAMPLE1_LEGEND", """Creating Map...|≈≈≈≈≈ ≈♣n♣≈ ≈∩▲∩≈ ≈♠n♠≈ ≈≈≈≈≈ |Creating Landmass...|""", ) self.call( mapbuilder.CmdMapBuilder(), "evennia.contrib.mapbuilder.EXAMPLE2_MAP evennia.contrib.mapbuilder.EXAMPLE2_LEGEND", """Creating Map...|≈ ≈ ≈ ≈ ≈ ≈ ♣-♣-♣ ≈ ≈ ♣ ♣ ♣ ≈ ≈ ♣-♣-♣ ≈ ≈ ≈ ≈ ≈ ≈ |Creating Landmass...|""", ) # test menu_login from evennia.contrib import menu_login class TestMenuLogin(CommandTest): def test_cmdunloggedlook(self): self.call(menu_login.CmdUnloggedinLook(), "", "======") # test multidescer contrib from evennia.contrib import multidescer class TestMultidescer(CommandTest): def test_cmdmultidesc(self): self.call(multidescer.CmdMultiDesc(), "/list", "Stored descs:\ncaller:") self.call( multidescer.CmdMultiDesc(), "test = Desc 1", "Stored description 'test': \"Desc 1\"" ) self.call( multidescer.CmdMultiDesc(), "test2 = Desc 2", "Stored description 'test2': \"Desc 2\"" ) self.call( multidescer.CmdMultiDesc(), "/swap test-test2", "Swapped descs 'test' and 'test2'." ) self.call( multidescer.CmdMultiDesc(), "test3 = Desc 3init", "Stored description 'test3': \"Desc 3init\"", ) self.call( multidescer.CmdMultiDesc(), "/list", "Stored descs:\ntest3: Desc 3init\ntest: Desc 1\ntest2: Desc 2\ncaller:", ) self.call( multidescer.CmdMultiDesc(), "test3 = Desc 3", "Stored description 'test3': \"Desc 3\"" ) self.call( multidescer.CmdMultiDesc(), "/set test1 + test2 + + test3", "test1 Desc 2 Desc 3\n\n" "The above was set as the current description.", ) self.assertEqual(self.char1.db.desc, "test1 Desc 2 Desc 3") # test simpledoor contrib from evennia.contrib import simpledoor class TestSimpleDoor(CommandTest): def test_cmdopen(self): self.call( simpledoor.CmdOpen(), "newdoor;door:contrib.simpledoor.SimpleDoor,backdoor;door = Room2", "Created new Exit 'newdoor' from Room to Room2 (aliases: door).|Note: A door-type exit was " "created - ignored eventual custom return-exit type.|Created new Exit 'newdoor' from Room2 to Room (aliases: door).", ) self.call(simpledoor.CmdOpenCloseDoor(), "newdoor", "You close newdoor.", cmdstring="close") self.call( simpledoor.CmdOpenCloseDoor(), "newdoor", "newdoor is already closed.", cmdstring="close", ) self.call(simpledoor.CmdOpenCloseDoor(), "newdoor", "You open newdoor.", cmdstring="open") self.call( simpledoor.CmdOpenCloseDoor(), "newdoor", "newdoor is already open.", cmdstring="open" ) # test slow_exit contrib from evennia.contrib import slow_exit slow_exit.MOVE_DELAY = {"stroll": 0, "walk": 0, "run": 0, "sprint": 0} def _cancellable_mockdelay(time, callback, *args, **kwargs): callback(*args, **kwargs) return Mock() class TestSlowExit(CommandTest): @patch("evennia.utils.delay", _cancellable_mockdelay) def test_exit(self): exi = create_object( slow_exit.SlowExit, key="slowexit", location=self.room1, destination=self.room2 ) exi.at_traverse(self.char1, self.room2) self.call(slow_exit.CmdSetSpeed(), "walk", "You are now walking.") self.call(slow_exit.CmdStop(), "", "You stop moving.") # test talking npc contrib from evennia.contrib import talking_npc class TestTalkingNPC(CommandTest): def test_talkingnpc(self): npc = create_object(talking_npc.TalkingNPC, key="npctalker", location=self.room1) self.call(talking_npc.CmdTalk(), "", "(You walk up and talk to Char.)") npc.delete() # tests for the tutorial world # test tutorial_world/mob from evennia.contrib.tutorial_world import mob class TestTutorialWorldMob(EvenniaTest): def test_mob(self): mobobj = create_object(mob.Mob, key="mob") self.assertEqual(mobobj.db.is_dead, True) mobobj.set_alive() self.assertEqual(mobobj.db.is_dead, False) mobobj.set_dead() self.assertEqual(mobobj.db.is_dead, True) mobobj._set_ticker(0, "foo", stop=True) # TODO should be expanded with further tests of the modes and damage etc. # test tutorial_world/objects from evennia.contrib.tutorial_world import objects as tutobjects from mock.mock import MagicMock from twisted.trial.unittest import TestCase as TwistedTestCase from twisted.internet.base import DelayedCall DelayedCall.debug = True class TestTutorialWorldObjects(TwistedTestCase, CommandTest): def test_tutorialobj(self): obj1 = create_object(tutobjects.TutorialObject, key="tutobj") obj1.reset() self.assertEqual(obj1.location, obj1.home) def test_readable(self): readable = create_object(tutobjects.TutorialReadable, key="book", location=self.room1) readable.db.readable_text = "Text to read" self.call(tutobjects.CmdRead(), "book", "You read book:\n Text to read", obj=readable) def test_climbable(self): climbable = create_object(tutobjects.TutorialClimbable, key="tree", location=self.room1) self.call( tutobjects.CmdClimb(), "tree", "You climb tree. Having looked around, you climb down again.", obj=climbable, ) self.assertEqual( self.char1.tags.get("tutorial_climbed_tree", category="tutorial_world"), "tutorial_climbed_tree", ) def test_obelisk(self): obelisk = create_object(tutobjects.Obelisk, key="obelisk", location=self.room1) self.assertEqual(obelisk.return_appearance(self.char1).startswith("|cobelisk("), True) @patch("evennia.contrib.tutorial_world.objects.delay", mockdelay) @patch("evennia.scripts.taskhandler.deferLater", mockdeferLater) def test_lightsource(self): light = create_object(tutobjects.LightSource, key="torch", location=self.room1) self.call( tutobjects.CmdLight(), "", "A torch on the floor flickers and dies.|You light torch.", obj=light, ) self.assertFalse(light.pk) @patch("evennia.contrib.tutorial_world.objects.delay", mockdelay) @patch("evennia.scripts.taskhandler.deferLater", mockdeferLater) def test_crumblingwall(self): wall = create_object(tutobjects.CrumblingWall, key="wall", location=self.room1) wall.db.destination = self.room2.dbref self.assertFalse(wall.db.button_exposed) self.assertFalse(wall.db.exit_open) wall.db.root_pos = {"yellow": 0, "green": 0, "red": 0, "blue": 0} self.call( tutobjects.CmdShiftRoot(), "blue root right", "You shove the root adorned with small blue flowers to the right.", obj=wall, ) self.call( tutobjects.CmdShiftRoot(), "red root left", "You shift the reddish root to the left.", obj=wall, ) self.call( tutobjects.CmdShiftRoot(), "yellow root down", "You shove the root adorned with small yellow flowers downwards.", obj=wall, ) self.call( tutobjects.CmdShiftRoot(), "green root up", "You shift the weedy green root upwards.|Holding aside the root you think you notice something behind it ...", obj=wall, ) self.call( tutobjects.CmdPressButton(), "", "You move your fingers over the suspicious depression, then gives it a decisive push. First", obj=wall, ) # we patch out the delay, so these are closed immediately self.assertFalse(wall.db.button_exposed) self.assertFalse(wall.db.exit_open) def test_weapon(self): weapon = create_object(tutobjects.TutorialWeapon, key="sword", location=self.char1) self.call( tutobjects.CmdAttack(), "Char", "You stab with sword.", obj=weapon, cmdstring="stab" ) self.call( tutobjects.CmdAttack(), "Char", "You slash with sword.", obj=weapon, cmdstring="slash" ) def test_weaponrack(self): rack = create_object(tutobjects.TutorialWeaponRack, key="rack", location=self.room1) rack.db.available_weapons = ["sword"] self.call(tutobjects.CmdGetWeapon(), "", "You find Rusty sword.", obj=rack) # test tutorial_world/ from evennia.contrib.tutorial_world import rooms as tutrooms class TestTutorialWorldRooms(CommandTest): def test_cmdtutorial(self): room = create_object(tutrooms.TutorialRoom, key="tutroom") self.char1.location = room self.call(tutrooms.CmdTutorial(), "", "Sorry, there is no tutorial help available here.") self.call( tutrooms.CmdTutorialSetDetail(), "detail;foo;foo2 = A detail", "Detail set: 'detail;foo;foo2': 'A detail'", obj=room, ) self.call(tutrooms.CmdTutorialLook(), "", "tutroom(", obj=room) self.call(tutrooms.CmdTutorialLook(), "detail", "A detail", obj=room) self.call(tutrooms.CmdTutorialLook(), "foo", "A detail", obj=room) room.delete() def test_weatherroom(self): room = create_object(tutrooms.WeatherRoom, key="weatherroom") room.update_weather() tutrooms.TICKER_HANDLER.remove( interval=room.db.interval, callback=room.update_weather, idstring="tutorial" ) room.delete() def test_introroom(self): room = create_object(tutrooms.IntroRoom, key="introroom") room.at_object_receive(self.char1, self.room1) def test_bridgeroom(self): room = create_object(tutrooms.BridgeRoom, key="bridgeroom") room.update_weather() self.char1.move_to(room) self.call( tutrooms.CmdBridgeHelp(), "", "You are trying hard not to fall off the bridge ...", obj=room, ) self.call( tutrooms.CmdLookBridge(), "", "bridgeroom\nYou are standing very close to the the bridge's western foundation.", obj=room, ) room.at_object_leave(self.char1, self.room1) tutrooms.TICKER_HANDLER.remove( interval=room.db.interval, callback=room.update_weather, idstring="tutorial" ) room.delete() def test_darkroom(self): room = create_object(tutrooms.DarkRoom, key="darkroom") self.char1.move_to(room) self.call(tutrooms.CmdDarkHelp(), "", "Can't help you until") def test_teleportroom(self): create_object(tutrooms.TeleportRoom, key="teleportroom") def test_outroroom(self): create_object(tutrooms.OutroRoom, key="outroroom") # test turnbattle from evennia.contrib.turnbattle import tb_basic, tb_equip, tb_range, tb_items, tb_magic class TestTurnBattleBasicCmd(CommandTest): # Test basic combat commands def test_turnbattlecmd(self): self.call(tb_basic.CmdFight(), "", "You can't start a fight if you've been defeated!") self.call(tb_basic.CmdAttack(), "", "You can only do that in combat. (see: help fight)") self.call(tb_basic.CmdPass(), "", "You can only do that in combat. (see: help fight)") self.call(tb_basic.CmdDisengage(), "", "You can only do that in combat. (see: help fight)") self.call(tb_basic.CmdRest(), "", "Char rests to recover HP.") class TestTurnBattleEquipCmd(CommandTest): def setUp(self): super(TestTurnBattleEquipCmd, self).setUp() self.testweapon = create_object(tb_equip.TBEWeapon, key="test weapon") self.testarmor = create_object(tb_equip.TBEArmor, key="test armor") self.testweapon.move_to(self.char1) self.testarmor.move_to(self.char1) # Test equipment commands def test_turnbattleequipcmd(self): # Start with equip module specific commands. self.call(tb_equip.CmdWield(), "weapon", "Char wields test weapon.") self.call(tb_equip.CmdUnwield(), "", "Char lowers test weapon.") self.call(tb_equip.CmdDon(), "armor", "Char dons test armor.") self.call(tb_equip.CmdDoff(), "", "Char removes test armor.") # Also test the commands that are the same in the basic module self.call(tb_equip.CmdFight(), "", "You can't start a fight if you've been defeated!") self.call(tb_equip.CmdAttack(), "", "You can only do that in combat. (see: help fight)") self.call(tb_equip.CmdPass(), "", "You can only do that in combat. (see: help fight)") self.call(tb_equip.CmdDisengage(), "", "You can only do that in combat. (see: help fight)") self.call(tb_equip.CmdRest(), "", "Char rests to recover HP.") class TestTurnBattleRangeCmd(CommandTest): # Test range commands def test_turnbattlerangecmd(self): # Start with range module specific commands. self.call(tb_range.CmdShoot(), "", "You can only do that in combat. (see: help fight)") self.call(tb_range.CmdApproach(), "", "You can only do that in combat. (see: help fight)") self.call(tb_range.CmdWithdraw(), "", "You can only do that in combat. (see: help fight)") self.call(tb_range.CmdStatus(), "", "HP Remaining: 100 / 100") # Also test the commands that are the same in the basic module self.call(tb_range.CmdFight(), "", "There's nobody here to fight!") self.call(tb_range.CmdAttack(), "", "You can only do that in combat. (see: help fight)") self.call(tb_range.CmdPass(), "", "You can only do that in combat. (see: help fight)") self.call(tb_range.CmdDisengage(), "", "You can only do that in combat. (see: help fight)") self.call(tb_range.CmdRest(), "", "Char rests to recover HP.") class TestTurnBattleItemsCmd(CommandTest): def setUp(self): super(TestTurnBattleItemsCmd, self).setUp() self.testitem = create_object(key="test item") self.testitem.move_to(self.char1) # Test item commands def test_turnbattleitemcmd(self): self.call(tb_items.CmdUse(), "item", "'Test item' is not a usable item.") # Also test the commands that are the same in the basic module self.call(tb_items.CmdFight(), "", "You can't start a fight if you've been defeated!") self.call(tb_items.CmdAttack(), "", "You can only do that in combat. (see: help fight)") self.call(tb_items.CmdPass(), "", "You can only do that in combat. (see: help fight)") self.call(tb_items.CmdDisengage(), "", "You can only do that in combat. (see: help fight)") self.call(tb_items.CmdRest(), "", "Char rests to recover HP.") class TestTurnBattleMagicCmd(CommandTest): # Test magic commands def test_turnbattlemagiccmd(self): self.call(tb_magic.CmdStatus(), "", "You have 100 / 100 HP and 20 / 20 MP.") self.call(tb_magic.CmdLearnSpell(), "test spell", "There is no spell with that name.") self.call(tb_magic.CmdCast(), "", "Usage: cast = , ") # Also test the commands that are the same in the basic module self.call(tb_magic.CmdFight(), "", "There's nobody here to fight!") self.call(tb_magic.CmdAttack(), "", "You can only do that in combat. (see: help fight)") self.call(tb_magic.CmdPass(), "", "You can only do that in combat. (see: help fight)") self.call(tb_magic.CmdDisengage(), "", "You can only do that in combat. (see: help fight)") self.call(tb_magic.CmdRest(), "", "Char rests to recover HP and MP.") class TestTurnBattleBasicFunc(EvenniaTest): def setUp(self): super(TestTurnBattleBasicFunc, self).setUp() self.testroom = create_object(DefaultRoom, key="Test Room") self.attacker = create_object( tb_basic.TBBasicCharacter, key="Attacker", location=self.testroom ) self.defender = create_object( tb_basic.TBBasicCharacter, key="Defender", location=self.testroom ) self.joiner = create_object(tb_basic.TBBasicCharacter, key="Joiner", location=None) def tearDown(self): super(TestTurnBattleBasicFunc, self).tearDown() self.turnhandler.stop() self.testroom.delete() self.attacker.delete() self.defender.delete() self.joiner.delete() # Test combat functions def test_tbbasicfunc(self): # Initiative roll initiative = tb_basic.roll_init(self.attacker) self.assertTrue(initiative >= 0 and initiative <= 1000) # Attack roll attack_roll = tb_basic.get_attack(self.attacker, self.defender) self.assertTrue(attack_roll >= 0 and attack_roll <= 100) # Defense roll defense_roll = tb_basic.get_defense(self.attacker, self.defender) self.assertTrue(defense_roll == 50) # Damage roll damage_roll = tb_basic.get_damage(self.attacker, self.defender) self.assertTrue(damage_roll >= 15 and damage_roll <= 25) # Apply damage self.defender.db.hp = 10 tb_basic.apply_damage(self.defender, 3) self.assertTrue(self.defender.db.hp == 7) # Resolve attack self.defender.db.hp = 40 tb_basic.resolve_attack(self.attacker, self.defender, attack_value=20, defense_value=10) self.assertTrue(self.defender.db.hp < 40) # Combat cleanup self.attacker.db.Combat_attribute = True tb_basic.combat_cleanup(self.attacker) self.assertFalse(self.attacker.db.combat_attribute) # Is in combat self.assertFalse(tb_basic.is_in_combat(self.attacker)) # Set up turn handler script for further tests self.attacker.location.scripts.add(tb_basic.TBBasicTurnHandler) self.turnhandler = self.attacker.db.combat_TurnHandler self.assertTrue(self.attacker.db.combat_TurnHandler) # Set the turn handler's interval very high to keep it from repeating during tests. self.turnhandler.interval = 10000 # Force turn order self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 # Test is turn self.assertTrue(tb_basic.is_turn(self.attacker)) # Spend actions self.attacker.db.Combat_ActionsLeft = 1 tb_basic.spend_action(self.attacker, 1, action_name="Test") self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0) self.assertTrue(self.attacker.db.Combat_LastAction == "Test") # Initialize for combat self.attacker.db.Combat_ActionsLeft = 983 self.turnhandler.initialize_for_combat(self.attacker) self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0) self.assertTrue(self.attacker.db.Combat_LastAction == "null") # Start turn self.defender.db.Combat_ActionsLeft = 0 self.turnhandler.start_turn(self.defender) self.assertTrue(self.defender.db.Combat_ActionsLeft == 1) # Next turn self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.turnhandler.next_turn() self.assertTrue(self.turnhandler.db.turn == 1) # Turn end check self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.attacker.db.Combat_ActionsLeft = 0 self.turnhandler.turn_end_check(self.attacker) self.assertTrue(self.turnhandler.db.turn == 1) # Join fight self.joiner.location = self.testroom self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.turnhandler.join_fight(self.joiner) self.assertTrue(self.turnhandler.db.turn == 1) self.assertTrue(self.turnhandler.db.fighters == [self.joiner, self.attacker, self.defender]) class TestTurnBattleEquipFunc(EvenniaTest): def setUp(self): super(TestTurnBattleEquipFunc, self).setUp() self.testroom = create_object(DefaultRoom, key="Test Room") self.attacker = create_object( tb_equip.TBEquipCharacter, key="Attacker", location=self.testroom ) self.defender = create_object( tb_equip.TBEquipCharacter, key="Defender", location=self.testroom ) self.joiner = create_object(tb_equip.TBEquipCharacter, key="Joiner", location=None) def tearDown(self): super(TestTurnBattleEquipFunc, self).tearDown() self.turnhandler.stop() self.testroom.delete() self.attacker.delete() self.defender.delete() self.joiner.delete() # Test the combat functions in tb_equip too. They work mostly the same. def test_tbequipfunc(self): # Initiative roll initiative = tb_equip.roll_init(self.attacker) self.assertTrue(initiative >= 0 and initiative <= 1000) # Attack roll attack_roll = tb_equip.get_attack(self.attacker, self.defender) self.assertTrue(attack_roll >= -50 and attack_roll <= 150) # Defense roll defense_roll = tb_equip.get_defense(self.attacker, self.defender) self.assertTrue(defense_roll == 50) # Damage roll damage_roll = tb_equip.get_damage(self.attacker, self.defender) self.assertTrue(damage_roll >= 0 and damage_roll <= 50) # Apply damage self.defender.db.hp = 10 tb_equip.apply_damage(self.defender, 3) self.assertTrue(self.defender.db.hp == 7) # Resolve attack self.defender.db.hp = 40 tb_equip.resolve_attack(self.attacker, self.defender, attack_value=20, defense_value=10) self.assertTrue(self.defender.db.hp < 40) # Combat cleanup self.attacker.db.Combat_attribute = True tb_equip.combat_cleanup(self.attacker) self.assertFalse(self.attacker.db.combat_attribute) # Is in combat self.assertFalse(tb_equip.is_in_combat(self.attacker)) # Set up turn handler script for further tests self.attacker.location.scripts.add(tb_equip.TBEquipTurnHandler) self.turnhandler = self.attacker.db.combat_TurnHandler self.assertTrue(self.attacker.db.combat_TurnHandler) # Set the turn handler's interval very high to keep it from repeating during tests. self.turnhandler.interval = 10000 # Force turn order self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 # Test is turn self.assertTrue(tb_equip.is_turn(self.attacker)) # Spend actions self.attacker.db.Combat_ActionsLeft = 1 tb_equip.spend_action(self.attacker, 1, action_name="Test") self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0) self.assertTrue(self.attacker.db.Combat_LastAction == "Test") # Initialize for combat self.attacker.db.Combat_ActionsLeft = 983 self.turnhandler.initialize_for_combat(self.attacker) self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0) self.assertTrue(self.attacker.db.Combat_LastAction == "null") # Start turn self.defender.db.Combat_ActionsLeft = 0 self.turnhandler.start_turn(self.defender) self.assertTrue(self.defender.db.Combat_ActionsLeft == 1) # Next turn self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.turnhandler.next_turn() self.assertTrue(self.turnhandler.db.turn == 1) # Turn end check self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.attacker.db.Combat_ActionsLeft = 0 self.turnhandler.turn_end_check(self.attacker) self.assertTrue(self.turnhandler.db.turn == 1) # Join fight self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.turnhandler.join_fight(self.joiner) self.assertTrue(self.turnhandler.db.turn == 1) self.assertTrue(self.turnhandler.db.fighters == [self.joiner, self.attacker, self.defender]) class TestTurnBattleRangeFunc(EvenniaTest): def setUp(self): super(TestTurnBattleRangeFunc, self).setUp() self.testroom = create_object(DefaultRoom, key="Test Room") self.attacker = create_object( tb_range.TBRangeCharacter, key="Attacker", location=self.testroom ) self.defender = create_object( tb_range.TBRangeCharacter, key="Defender", location=self.testroom ) self.joiner = create_object(tb_range.TBRangeCharacter, key="Joiner", location=self.testroom) def tearDown(self): super(TestTurnBattleRangeFunc, self).tearDown() self.turnhandler.stop() self.testroom.delete() self.attacker.delete() self.defender.delete() self.joiner.delete() # Test combat functions in tb_range too. def test_tbrangefunc(self): # Initiative roll initiative = tb_range.roll_init(self.attacker) self.assertTrue(initiative >= 0 and initiative <= 1000) # Attack roll attack_roll = tb_range.get_attack(self.attacker, self.defender, "test") self.assertTrue(attack_roll >= 0 and attack_roll <= 100) # Defense roll defense_roll = tb_range.get_defense(self.attacker, self.defender, "test") self.assertTrue(defense_roll == 50) # Damage roll damage_roll = tb_range.get_damage(self.attacker, self.defender) self.assertTrue(damage_roll >= 15 and damage_roll <= 25) # Apply damage self.defender.db.hp = 10 tb_range.apply_damage(self.defender, 3) self.assertTrue(self.defender.db.hp == 7) # Resolve attack self.defender.db.hp = 40 tb_range.resolve_attack( self.attacker, self.defender, "test", attack_value=20, defense_value=10 ) self.assertTrue(self.defender.db.hp < 40) # Combat cleanup self.attacker.db.Combat_attribute = True tb_range.combat_cleanup(self.attacker) self.assertFalse(self.attacker.db.combat_attribute) # Is in combat self.assertFalse(tb_range.is_in_combat(self.attacker)) # Set up turn handler script for further tests self.attacker.location.scripts.add(tb_range.TBRangeTurnHandler) self.turnhandler = self.attacker.db.combat_TurnHandler self.assertTrue(self.attacker.db.combat_TurnHandler) # Set the turn handler's interval very high to keep it from repeating during tests. self.turnhandler.interval = 10000 # Force turn order self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 # Test is turn self.assertTrue(tb_range.is_turn(self.attacker)) # Spend actions self.attacker.db.Combat_ActionsLeft = 1 tb_range.spend_action(self.attacker, 1, action_name="Test") self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0) self.assertTrue(self.attacker.db.Combat_LastAction == "Test") # Initialize for combat self.attacker.db.Combat_ActionsLeft = 983 self.turnhandler.initialize_for_combat(self.attacker) self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0) self.assertTrue(self.attacker.db.Combat_LastAction == "null") # Set up ranges again, since initialize_for_combat clears them self.attacker.db.combat_range = {} self.attacker.db.combat_range[self.attacker] = 0 self.attacker.db.combat_range[self.defender] = 1 self.defender.db.combat_range = {} self.defender.db.combat_range[self.defender] = 0 self.defender.db.combat_range[self.attacker] = 1 # Start turn self.defender.db.Combat_ActionsLeft = 0 self.turnhandler.start_turn(self.defender) self.assertTrue(self.defender.db.Combat_ActionsLeft == 2) # Next turn self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.turnhandler.next_turn() self.assertTrue(self.turnhandler.db.turn == 1) # Turn end check self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.attacker.db.Combat_ActionsLeft = 0 self.turnhandler.turn_end_check(self.attacker) self.assertTrue(self.turnhandler.db.turn == 1) # Join fight self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.turnhandler.join_fight(self.joiner) self.assertTrue(self.turnhandler.db.turn == 1) self.assertTrue(self.turnhandler.db.fighters == [self.joiner, self.attacker, self.defender]) # Now, test for approach/withdraw functions self.assertTrue(tb_range.get_range(self.attacker, self.defender) == 1) # Approach tb_range.approach(self.attacker, self.defender) self.assertTrue(tb_range.get_range(self.attacker, self.defender) == 0) # Withdraw tb_range.withdraw(self.attacker, self.defender) self.assertTrue(tb_range.get_range(self.attacker, self.defender) == 1) class TestTurnBattleItemsFunc(EvenniaTest): @patch("evennia.contrib.turnbattle.tb_items.tickerhandler", new=MagicMock()) def setUp(self): super(TestTurnBattleItemsFunc, self).setUp() self.testroom = create_object(DefaultRoom, key="Test Room") self.attacker = create_object( tb_items.TBItemsCharacter, key="Attacker", location=self.testroom ) self.defender = create_object( tb_items.TBItemsCharacter, key="Defender", location=self.testroom ) self.joiner = create_object(tb_items.TBItemsCharacter, key="Joiner", location=self.testroom) self.user = create_object(tb_items.TBItemsCharacter, key="User", location=self.testroom) self.test_healpotion = create_object(key="healing potion") self.test_healpotion.db.item_func = "heal" self.test_healpotion.db.item_uses = 3 def tearDown(self): super(TestTurnBattleItemsFunc, self).tearDown() self.turnhandler.stop() self.testroom.delete() self.attacker.delete() self.defender.delete() self.joiner.delete() self.user.delete() # Test functions in tb_items. def test_tbitemsfunc(self): # Initiative roll initiative = tb_items.roll_init(self.attacker) self.assertTrue(initiative >= 0 and initiative <= 1000) # Attack roll attack_roll = tb_items.get_attack(self.attacker, self.defender) self.assertTrue(attack_roll >= 0 and attack_roll <= 100) # Defense roll defense_roll = tb_items.get_defense(self.attacker, self.defender) self.assertTrue(defense_roll == 50) # Damage roll damage_roll = tb_items.get_damage(self.attacker, self.defender) self.assertTrue(damage_roll >= 15 and damage_roll <= 25) # Apply damage self.defender.db.hp = 10 tb_items.apply_damage(self.defender, 3) self.assertTrue(self.defender.db.hp == 7) # Resolve attack self.defender.db.hp = 40 tb_items.resolve_attack(self.attacker, self.defender, attack_value=20, defense_value=10) self.assertTrue(self.defender.db.hp < 40) # Combat cleanup self.attacker.db.Combat_attribute = True tb_items.combat_cleanup(self.attacker) self.assertFalse(self.attacker.db.combat_attribute) # Is in combat self.assertFalse(tb_items.is_in_combat(self.attacker)) # Set up turn handler script for further tests self.attacker.location.scripts.add(tb_items.TBItemsTurnHandler) self.turnhandler = self.attacker.db.combat_TurnHandler self.assertTrue(self.attacker.db.combat_TurnHandler) # Set the turn handler's interval very high to keep it from repeating during tests. self.turnhandler.interval = 10000 # Force turn order self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 # Test is turn self.assertTrue(tb_items.is_turn(self.attacker)) # Spend actions self.attacker.db.Combat_ActionsLeft = 1 tb_items.spend_action(self.attacker, 1, action_name="Test") self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0) self.assertTrue(self.attacker.db.Combat_LastAction == "Test") # Initialize for combat self.attacker.db.Combat_ActionsLeft = 983 self.turnhandler.initialize_for_combat(self.attacker) self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0) self.assertTrue(self.attacker.db.Combat_LastAction == "null") # Start turn self.defender.db.Combat_ActionsLeft = 0 self.turnhandler.start_turn(self.defender) self.assertTrue(self.defender.db.Combat_ActionsLeft == 1) # Next turn self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.turnhandler.next_turn() self.assertTrue(self.turnhandler.db.turn == 1) # Turn end check self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.attacker.db.Combat_ActionsLeft = 0 self.turnhandler.turn_end_check(self.attacker) self.assertTrue(self.turnhandler.db.turn == 1) # Join fight self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.turnhandler.join_fight(self.joiner) self.assertTrue(self.turnhandler.db.turn == 1) self.assertTrue(self.turnhandler.db.fighters == [self.joiner, self.attacker, self.defender]) # Now time to test item stuff. # Spend item use tb_items.spend_item_use(self.test_healpotion, self.user) self.assertTrue(self.test_healpotion.db.item_uses == 2) # Use item self.user.db.hp = 2 tb_items.use_item(self.user, self.test_healpotion, self.user) self.assertTrue(self.user.db.hp > 2) # Add contition tb_items.add_condition(self.user, self.user, "Test", 5) self.assertTrue(self.user.db.conditions == {"Test": [5, self.user]}) # Condition tickdown tb_items.condition_tickdown(self.user, self.user) self.assertTrue(self.user.db.conditions == {"Test": [4, self.user]}) # Test item functions now! # Item heal self.user.db.hp = 2 tb_items.itemfunc_heal(self.test_healpotion, self.user, self.user) # Item add condition self.user.db.conditions = {} tb_items.itemfunc_add_condition(self.test_healpotion, self.user, self.user) self.assertTrue(self.user.db.conditions == {"Regeneration": [5, self.user]}) # Item cure condition self.user.db.conditions = {"Poisoned": [5, self.user]} tb_items.itemfunc_cure_condition(self.test_healpotion, self.user, self.user) self.assertTrue(self.user.db.conditions == {}) class TestTurnBattleMagicFunc(EvenniaTest): def setUp(self): super(TestTurnBattleMagicFunc, self).setUp() self.testroom = create_object(DefaultRoom, key="Test Room") self.attacker = create_object( tb_magic.TBMagicCharacter, key="Attacker", location=self.testroom ) self.defender = create_object( tb_magic.TBMagicCharacter, key="Defender", location=self.testroom ) self.joiner = create_object(tb_magic.TBMagicCharacter, key="Joiner", location=self.testroom) def tearDown(self): super(TestTurnBattleMagicFunc, self).tearDown() self.turnhandler.stop() self.testroom.delete() self.attacker.delete() self.defender.delete() self.joiner.delete() # Test combat functions in tb_magic. def test_tbbasicfunc(self): # Initiative roll initiative = tb_magic.roll_init(self.attacker) self.assertTrue(initiative >= 0 and initiative <= 1000) # Attack roll attack_roll = tb_magic.get_attack(self.attacker, self.defender) self.assertTrue(attack_roll >= 0 and attack_roll <= 100) # Defense roll defense_roll = tb_magic.get_defense(self.attacker, self.defender) self.assertTrue(defense_roll == 50) # Damage roll damage_roll = tb_magic.get_damage(self.attacker, self.defender) self.assertTrue(damage_roll >= 15 and damage_roll <= 25) # Apply damage self.defender.db.hp = 10 tb_magic.apply_damage(self.defender, 3) self.assertTrue(self.defender.db.hp == 7) # Resolve attack self.defender.db.hp = 40 tb_magic.resolve_attack(self.attacker, self.defender, attack_value=20, defense_value=10) self.assertTrue(self.defender.db.hp < 40) # Combat cleanup self.attacker.db.Combat_attribute = True tb_magic.combat_cleanup(self.attacker) self.assertFalse(self.attacker.db.combat_attribute) # Is in combat self.assertFalse(tb_magic.is_in_combat(self.attacker)) # Set up turn handler script for further tests self.attacker.location.scripts.add(tb_magic.TBMagicTurnHandler) self.turnhandler = self.attacker.db.combat_TurnHandler self.assertTrue(self.attacker.db.combat_TurnHandler) # Set the turn handler's interval very high to keep it from repeating during tests. self.turnhandler.interval = 10000 # Force turn order self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 # Test is turn self.assertTrue(tb_magic.is_turn(self.attacker)) # Spend actions self.attacker.db.Combat_ActionsLeft = 1 tb_magic.spend_action(self.attacker, 1, action_name="Test") self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0) self.assertTrue(self.attacker.db.Combat_LastAction == "Test") # Initialize for combat self.attacker.db.Combat_ActionsLeft = 983 self.turnhandler.initialize_for_combat(self.attacker) self.assertTrue(self.attacker.db.Combat_ActionsLeft == 0) self.assertTrue(self.attacker.db.Combat_LastAction == "null") # Start turn self.defender.db.Combat_ActionsLeft = 0 self.turnhandler.start_turn(self.defender) self.assertTrue(self.defender.db.Combat_ActionsLeft == 1) # Next turn self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.turnhandler.next_turn() self.assertTrue(self.turnhandler.db.turn == 1) # Turn end check self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.attacker.db.Combat_ActionsLeft = 0 self.turnhandler.turn_end_check(self.attacker) self.assertTrue(self.turnhandler.db.turn == 1) # Join fight self.turnhandler.db.fighters = [self.attacker, self.defender] self.turnhandler.db.turn = 0 self.turnhandler.join_fight(self.joiner) self.assertTrue(self.turnhandler.db.turn == 1) self.assertTrue(self.turnhandler.db.fighters == [self.joiner, self.attacker, self.defender]) # Test tree select from evennia.contrib import tree_select TREE_MENU_TESTSTR = """Foo Bar -Baz --Baz 1 --Baz 2 -Qux""" class TestTreeSelectFunc(EvenniaTest): def test_tree_functions(self): # Dash counter self.assertTrue(tree_select.dashcount("--test") == 2) # Is category self.assertTrue(tree_select.is_category(TREE_MENU_TESTSTR, 1) == True) # Parse options self.assertTrue( tree_select.parse_opts(TREE_MENU_TESTSTR, category_index=2) == [(3, "Baz 1"), (4, "Baz 2")] ) # Index to selection self.assertTrue(tree_select.index_to_selection(TREE_MENU_TESTSTR, 2) == "Baz") # Go up one category self.assertTrue(tree_select.go_up_one_category(TREE_MENU_TESTSTR, 4) == 2) # Option list to menu options test_optlist = tree_select.parse_opts(TREE_MENU_TESTSTR, category_index=2) optlist_to_menu_expected_result = [ {"goto": ["menunode_treeselect", {"newindex": 3}], "key": "Baz 1"}, {"goto": ["menunode_treeselect", {"newindex": 4}], "key": "Baz 2"}, { "goto": ["menunode_treeselect", {"newindex": 1}], "key": ["<< Go Back", "go back", "back"], "desc": "Return to the previous menu.", }, ] self.assertTrue( tree_select.optlist_to_menuoptions(TREE_MENU_TESTSTR, test_optlist, 2, True, True) == optlist_to_menu_expected_result ) # Test field fill from evennia.contrib import fieldfill FIELD_TEST_TEMPLATE = [ {"fieldname": "TextTest", "fieldtype": "text"}, {"fieldname": "NumberTest", "fieldtype": "number", "blankmsg": "Number here!"}, {"fieldname": "DefaultText", "fieldtype": "text", "default": "Test"}, {"fieldname": "DefaultNum", "fieldtype": "number", "default": 3}, ] FIELD_TEST_DATA = {"TextTest": None, "NumberTest": None, "DefaultText": "Test", "DefaultNum": 3} class TestFieldFillFunc(EvenniaTest): def test_field_functions(self): self.assertTrue(fieldfill.form_template_to_dict(FIELD_TEST_TEMPLATE) == FIELD_TEST_DATA) # Test of the unixcommand module from evennia.contrib.unixcommand import UnixCommand class CmdDummy(UnixCommand): """A dummy UnixCommand.""" key = "dummy" def init_parser(self): """Fill out options.""" self.parser.add_argument("nb1", type=int, help="the first number") self.parser.add_argument("nb2", type=int, help="the second number") self.parser.add_argument("-v", "--verbose", action="store_true") def func(self): nb1 = self.opts.nb1 nb2 = self.opts.nb2 result = nb1 * nb2 verbose = self.opts.verbose if verbose: self.msg("{} times {} is {}".format(nb1, nb2, result)) else: self.msg("{} * {} = {}".format(nb1, nb2, result)) class TestUnixCommand(CommandTest): def test_success(self): """See the command parsing succeed.""" self.call(CmdDummy(), "5 10", "5 * 10 = 50") self.call(CmdDummy(), "5 10 -v", "5 times 10 is 50") def test_failure(self): """If not provided with the right info, should fail.""" ret = self.call(CmdDummy(), "5") lines = ret.splitlines() self.assertTrue(any(l.startswith("usage:") for l in lines)) self.assertTrue(any(l.startswith("dummy: error:") for l in lines)) # If we specify an incorrect number as parameter ret = self.call(CmdDummy(), "five ten") lines = ret.splitlines() self.assertTrue(any(l.startswith("usage:") for l in lines)) self.assertTrue(any(l.startswith("dummy: error:") for l in lines)) import re from evennia.contrib import color_markups class TestColorMarkup(EvenniaTest): """ Note: Normally this would be tested by importing the ansi parser and run the mappings through it. This is not possible since the ansi module creates its mapping at the module/class level; since the ansi module is used by so many other modules it appears that trying to overload settings to test it causes issues with unrelated tests. """ def test_curly_markup(self): ansi_map = color_markups.CURLY_COLOR_ANSI_EXTRA_MAP self.assertIsNotNone(re.match(re.escape(ansi_map[7][0]), "{r")) self.assertIsNotNone(re.match(re.escape(ansi_map[-1][0]), "{[X")) xterm_fg = color_markups.CURLY_COLOR_XTERM256_EXTRA_FG self.assertIsNotNone(re.match(xterm_fg[0], "{001")) self.assertIsNotNone(re.match(xterm_fg[0], "{123")) self.assertIsNotNone(re.match(xterm_fg[0], "{455")) xterm_bg = color_markups.CURLY_COLOR_XTERM256_EXTRA_BG self.assertIsNotNone(re.match(xterm_bg[0], "{[001")) self.assertIsNotNone(re.match(xterm_bg[0], "{[123")) self.assertIsNotNone(re.match(xterm_bg[0], "{[455")) xterm_gfg = color_markups.CURLY_COLOR_XTERM256_EXTRA_GFG self.assertIsNotNone(re.match(xterm_gfg[0], "{=h")) self.assertIsNotNone(re.match(xterm_gfg[0], "{=e")) self.assertIsNotNone(re.match(xterm_gfg[0], "{=w")) xterm_gbg = color_markups.CURLY_COLOR_XTERM256_EXTRA_GBG self.assertIsNotNone(re.match(xterm_gbg[0], "{[=a")) self.assertIsNotNone(re.match(xterm_gbg[0], "{[=k")) self.assertIsNotNone(re.match(xterm_gbg[0], "{[=z")) bright_map = color_markups.CURLY_COLOR_ANSI_XTERM256_BRIGHT_BG_EXTRA_MAP self.assertEqual(bright_map[0][1], "{[500") self.assertEqual(bright_map[-1][1], "{[222") def test_mux_markup(self): ansi_map = color_markups.MUX_COLOR_ANSI_EXTRA_MAP self.assertIsNotNone(re.match(re.escape(ansi_map[10][0]), "%cr")) self.assertIsNotNone(re.match(re.escape(ansi_map[-1][0]), "%cX")) xterm_fg = color_markups.MUX_COLOR_XTERM256_EXTRA_FG self.assertIsNotNone(re.match(xterm_fg[0], "%c001")) self.assertIsNotNone(re.match(xterm_fg[0], "%c123")) self.assertIsNotNone(re.match(xterm_fg[0], "%c455")) xterm_bg = color_markups.MUX_COLOR_XTERM256_EXTRA_BG self.assertIsNotNone(re.match(xterm_bg[0], "%c[001")) self.assertIsNotNone(re.match(xterm_bg[0], "%c[123")) self.assertIsNotNone(re.match(xterm_bg[0], "%c[455")) xterm_gfg = color_markups.MUX_COLOR_XTERM256_EXTRA_GFG self.assertIsNotNone(re.match(xterm_gfg[0], "%c=h")) self.assertIsNotNone(re.match(xterm_gfg[0], "%c=e")) self.assertIsNotNone(re.match(xterm_gfg[0], "%c=w")) xterm_gbg = color_markups.MUX_COLOR_XTERM256_EXTRA_GBG self.assertIsNotNone(re.match(xterm_gbg[0], "%c[=a")) self.assertIsNotNone(re.match(xterm_gbg[0], "%c[=k")) self.assertIsNotNone(re.match(xterm_gbg[0], "%c[=z")) bright_map = color_markups.MUX_COLOR_ANSI_XTERM256_BRIGHT_BG_EXTRA_MAP self.assertEqual(bright_map[0][1], "%c[500") self.assertEqual(bright_map[-1][1], "%c[222") from evennia.contrib import random_string_generator SIMPLE_GENERATOR = random_string_generator.RandomStringGenerator("simple", "[01]{2}") class TestRandomStringGenerator(EvenniaTest): def test_generate(self): """Generate and fail when exhausted.""" generated = [] for i in range(4): generated.append(SIMPLE_GENERATOR.get()) generated.sort() self.assertEqual(generated, ["00", "01", "10", "11"]) # At this point, we have generated 4 strings. # We can't generate one more with self.assertRaises(random_string_generator.ExhaustedGenerator): SIMPLE_GENERATOR.get() # Test of the Puzzles module import itertools from evennia.contrib import puzzles from evennia.utils import search class TestPuzzles(CommandTest): def setUp(self): super(TestPuzzles, self).setUp() self.steel = create_object(self.object_typeclass, key="steel", location=self.char1.location) self.flint = create_object(self.object_typeclass, key="flint", location=self.char1.location) self.fire = create_object(self.object_typeclass, key="fire", location=self.char1.location) self.steel.tags.add("tag-steel") self.steel.tags.add("tag-steel", category="tagcat") self.flint.tags.add("tag-flint") self.flint.tags.add("tag-flint", category="tagcat") self.fire.tags.add("tag-fire") self.fire.tags.add("tag-fire", category="tagcat") def _assert_msg_matched(self, msg, regexs, re_flags=0): matches = [] for regex in regexs: m = re.search(regex, msg, re_flags) self.assertIsNotNone(m, "%r didn't match %r" % (regex, msg)) matches.append(m) return matches def _assert_recipe(self, name, parts, results, and_destroy_it=True, expected_count=1): def _keys(items): return [item["key"] for item in items] recipes = search.search_script_tag("", category=puzzles._PUZZLES_TAG_CATEGORY) self.assertEqual(expected_count, len(recipes)) self.assertEqual(name, recipes[expected_count - 1].db.puzzle_name) self.assertEqual(parts, _keys(recipes[expected_count - 1].db.parts)) self.assertEqual(results, _keys(recipes[expected_count - 1].db.results)) self.assertEqual( puzzles._PUZZLES_TAG_RECIPE, recipes[expected_count - 1].tags.get(category=puzzles._PUZZLES_TAG_CATEGORY), ) recipe_dbref = recipes[expected_count - 1].dbref if and_destroy_it: recipes[expected_count - 1].delete() return recipe_dbref if not and_destroy_it else None def _assert_no_recipes(self): self.assertEqual( 0, len(search.search_script_tag("", category=puzzles._PUZZLES_TAG_CATEGORY)) ) # good recipes def _good_recipe(self, name, parts, results, and_destroy_it=True, expected_count=1): regexs = [] for p in parts: regexs.append(r"^Part %s\(#\d+\)$" % (p)) for r in results: regexs.append(r"^Result %s\(#\d+\)$" % (r)) regexs.append(r"^Puzzle '%s' %s\(#\d+\) has been created successfully.$" % (name, name)) lhs = [name] + parts cmdstr = ",".join(lhs) + "=" + ",".join(results) msg = self.call(puzzles.CmdCreatePuzzleRecipe(), cmdstr, caller=self.char1) recipe_dbref = self._assert_recipe(name, parts, results, and_destroy_it, expected_count) self._assert_msg_matched(msg, regexs, re_flags=re.MULTILINE | re.DOTALL) return recipe_dbref def _check_room_contents(self, expected, check_test_tags=False): by_obj_key = lambda o: o.key room1_contents = sorted(self.room1.contents, key=by_obj_key) for key, grp in itertools.groupby(room1_contents, by_obj_key): if key in expected: grp = list(grp) self.assertEqual( expected[key], len(grp), "Expected %d but got %d for %s" % (expected[key], len(grp), key), ) if check_test_tags: for gi in grp: tags = gi.tags.all(return_key_and_category=True) self.assertIn(("tag-" + gi.key, "tagcat"), tags) def _arm(self, recipe_dbref, name, parts): regexs = [ r"^Puzzle Recipe %s\(#\d+\) '%s' found.$" % (name, name), r"^Spawning %d parts ...$" % (len(parts)), ] for p in parts: regexs.append(r"^Part %s\(#\d+\) spawned .*$" % (p)) regexs.append(r"^Puzzle armed successfully.$") msg = self.call(puzzles.CmdArmPuzzle(), recipe_dbref, caller=self.char1) matches = self._assert_msg_matched(msg, regexs, re_flags=re.MULTILINE | re.DOTALL) def test_cmdset_puzzle(self): self.char1.cmdset.add("evennia.contrib.puzzles.PuzzleSystemCmdSet") # FIXME: testing nothing, this is just to bump up coverage def test_cmd_puzzle(self): self._assert_no_recipes() # bad syntax def _bad_syntax(cmdstr): self.call( puzzles.CmdCreatePuzzleRecipe(), cmdstr, "Usage: @puzzle name, = ", caller=self.char1, ) _bad_syntax("") _bad_syntax("=") _bad_syntax("nothing =") _bad_syntax("= nothing") _bad_syntax("nothing") _bad_syntax(",nothing") _bad_syntax("name, nothing") _bad_syntax("name, nothing =") self._assert_no_recipes() self._good_recipe("makefire", ["steel", "flint"], ["fire", "steel", "flint"]) self._good_recipe("hot steels", ["steel", "fire"], ["steel", "fire"]) self._good_recipe( "furnace", ["steel", "steel", "fire"], ["steel", "steel", "fire", "fire", "fire", "fire"], ) # bad recipes def _bad_recipe(name, parts, results, fail_regex): cmdstr = ",".join([name] + parts) + "=" + ",".join(results) msg = self.call(puzzles.CmdCreatePuzzleRecipe(), cmdstr, caller=self.char1) self._assert_no_recipes() self.assertIsNotNone(re.match(fail_regex, msg), msg) _bad_recipe("name", ["nothing"], ["neither"], r"Could not find 'nothing'.") _bad_recipe("name", ["steel"], ["nothing"], r"Could not find 'nothing'.") _bad_recipe("", ["steel", "fire"], ["steel", "fire"], r"^Invalid puzzle name ''.") self.steel.location = self.char1 _bad_recipe("name", ["steel"], ["fire"], r"^Invalid location for steel$") _bad_recipe("name", ["flint"], ["steel"], r"^Invalid location for steel$") _bad_recipe("name", ["self"], ["fire"], r"^Invalid typeclass for Char$") _bad_recipe("name", ["here"], ["fire"], r"^Invalid typeclass for Room$") self._assert_no_recipes() def test_cmd_armpuzzle(self): # bad arms self.call( puzzles.CmdArmPuzzle(), "1", "A puzzle recipe's #dbref must be specified", caller=self.char1, ) self.call(puzzles.CmdArmPuzzle(), "#1", "Invalid puzzle '#1'", caller=self.char1) recipe_dbref = self._good_recipe( "makefire", ["steel", "flint"], ["fire", "steel", "flint"], and_destroy_it=False ) # delete proto parts and proto result self.steel.delete() self.flint.delete() self.fire.delete() # good arm self._arm(recipe_dbref, "makefire", ["steel", "flint"]) self._check_room_contents({"steel": 1, "flint": 1}, check_test_tags=True) def _use(self, cmdstr, expmsg): msg = self.call(puzzles.CmdUsePuzzleParts(), cmdstr, expmsg, caller=self.char1) return msg def test_cmd_use(self): self._use("", "Use what?") self._use("something", "There is no something around.") self._use("steel", "You have no idea how this can be used") self._use("steel flint", "There is no steel flint around.") self._use("steel, flint", "You have no idea how these can be used") recipe_dbref = self._good_recipe( "makefire", ["steel", "flint"], ["fire"], and_destroy_it=False ) recipe2_dbref = self._good_recipe( "makefire2", ["steel", "flint"], ["fire"], and_destroy_it=False, expected_count=2 ) # although there is steel and flint # those aren't valid puzzle parts because # the puzzle hasn't been armed self._use("steel", "You have no idea how this can be used") self._use("steel, flint", "You have no idea how these can be used") self._arm(recipe_dbref, "makefire", ["steel", "flint"]) self._check_room_contents({"steel": 2, "flint": 2}, check_test_tags=True) # there are duplicated objects now self._use("steel", "Which steel. There are many") self._use("flint", "Which flint. There are many") # delete proto parts and proto results self.steel.delete() self.flint.delete() self.fire.delete() # solve puzzle self._use("steel, flint", "You are a Genius") self.assertEqual( 1, len( list( filter( lambda o: o.key == "fire" and ("makefire", puzzles._PUZZLES_TAG_CATEGORY) in o.tags.all(return_key_and_category=True) and (puzzles._PUZZLES_TAG_MEMBER, puzzles._PUZZLES_TAG_CATEGORY) in o.tags.all(return_key_and_category=True), self.room1.contents, ) ) ), ) self._check_room_contents({"steel": 0, "flint": 0, "fire": 1}, check_test_tags=True) # trying again will fail as it was resolved already # and the parts were destroyed self._use("steel, flint", "There is no steel around") self._use("flint, steel", "There is no flint around") # arm same puzzle twice so there are duplicated parts self._arm(recipe_dbref, "makefire", ["steel", "flint"]) self._arm(recipe_dbref, "makefire", ["steel", "flint"]) self._check_room_contents({"steel": 2, "flint": 2, "fire": 1}, check_test_tags=True) # try solving with multiple parts but incomplete set self._use( "steel-1, steel-2", "You try to utilize these but nothing happens ... something amiss?" ) # arm the other puzzle. Their parts are identical self._arm(recipe2_dbref, "makefire2", ["steel", "flint"]) self._check_room_contents({"steel": 3, "flint": 3, "fire": 1}, check_test_tags=True) # solve with multiple parts for # multiple puzzles. Both can be solved but # only one is. self._use( "steel-1, flint-2, steel-3, flint-3", "Your gears start turning and 2 different ideas come to your mind ... ", ) self._check_room_contents({"steel": 2, "flint": 2, "fire": 2}, check_test_tags=True) self.room1.msg_contents = Mock() # solve all self._use("steel-1, flint-1", "You are a Genius") self.room1.msg_contents.assert_called_once_with( "|cChar|n performs some kind of tribal dance and |yfire|n seems to appear from thin air", exclude=(self.char1,), ) self._use("steel, flint", "You are a Genius") self._check_room_contents({"steel": 0, "flint": 0, "fire": 4}, check_test_tags=True) def test_puzzleedit(self): recipe_dbref = self._good_recipe( "makefire", ["steel", "flint"], ["fire"], and_destroy_it=False ) def _puzzleedit(swt, dbref, args, expmsg): if (swt is None) and (dbref is None) and (args is None): cmdstr = "" else: cmdstr = "%s %s%s" % (swt, dbref, args) self.call(puzzles.CmdEditPuzzle(), cmdstr, expmsg, caller=self.char1) # delete proto parts and proto results self.steel.delete() self.flint.delete() self.fire.delete() sid = self.script.id # bad syntax _puzzleedit( None, None, None, "A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit" ) _puzzleedit("", "1", "", "A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit") _puzzleedit("", "", "", "A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit") _puzzleedit( "", recipe_dbref, "dummy", "A puzzle recipe's #dbref must be specified.\nUsage: @puzzleedit", ) _puzzleedit("", self.script.dbref, "", "Script(#{}) is not a puzzle".format(sid)) # edit use_success_message and use_success_location_message _puzzleedit( "", recipe_dbref, "/use_success_message = Yes!", "makefire(%s) use_success_message = Yes!" % recipe_dbref, ) _puzzleedit( "", recipe_dbref, "/use_success_location_message = {result_names} Yeah baby! {caller}", "makefire(%s) use_success_location_message = {result_names} Yeah baby! {caller}" % recipe_dbref, ) self._arm(recipe_dbref, "makefire", ["steel", "flint"]) self.room1.msg_contents = Mock() self._use("steel, flint", "Yes!") self.room1.msg_contents.assert_called_once_with( "fire Yeah baby! Char", exclude=(self.char1,) ) self.room1.msg_contents.reset_mock() # edit mask: exclude location and desc during matching _puzzleedit( "", recipe_dbref, "/mask = location,desc", "makefire(%s) mask = ('location', 'desc')" % recipe_dbref, ) self._arm(recipe_dbref, "makefire", ["steel", "flint"]) # change location and desc self.char1.search("steel").db.desc = "A solid bar of steel" self.char1.search("steel").location = self.char1 self.char1.search("flint").db.desc = "A flint steel" self.char1.search("flint").location = self.char1 self._use("steel, flint", "Yes!") self.room1.msg_contents.assert_called_once_with( "fire Yeah baby! Char", exclude=(self.char1,) ) # delete _puzzleedit("/delete", recipe_dbref, "", "makefire(%s) was deleted" % recipe_dbref) self._assert_no_recipes() def test_puzzleedit_add_remove_parts_results(self): recipe_dbref = self._good_recipe( "makefire", ["steel", "flint"], ["fire"], and_destroy_it=False ) def _puzzleedit(swt, dbref, rhslist, expmsg): cmdstr = "%s %s = %s" % (swt, dbref, ", ".join(rhslist)) self.call(puzzles.CmdEditPuzzle(), cmdstr, expmsg, caller=self.char1) red_steel = create_object( self.object_typeclass, key="red steel", location=self.char1.location ) smoke = create_object(self.object_typeclass, key="smoke", location=self.char1.location) _puzzleedit("/addresult", recipe_dbref, ["smoke"], "smoke were added to results") _puzzleedit( "/addpart", recipe_dbref, ["red steel", "steel"], "red steel, steel were added to parts" ) # create a box so we can put all objects in # so that they can't be found during puzzle resolution self.box = create_object(self.object_typeclass, key="box", location=self.char1.location) def _box_all(): for o in self.room1.contents: if o not in [self.char1, self.char2, self.exit, self.obj1, self.obj2, self.box]: o.location = self.box _box_all() self._arm(recipe_dbref, "makefire", ["steel", "flint", "red steel", "steel"]) self._check_room_contents({"steel": 2, "red steel": 1, "flint": 1}) self._use( "steel-1, flint", "You try to utilize these but nothing happens ... something amiss?" ) self._use("steel-1, flint, red steel, steel-3", "You are a Genius") self._check_room_contents({"smoke": 1, "fire": 1}) _box_all() self.fire.location = self.room1 self.steel.location = self.room1 _puzzleedit("/delresult", recipe_dbref, ["fire"], "fire were removed from results") _puzzleedit( "/delpart", recipe_dbref, ["steel", "steel"], "steel, steel were removed from parts" ) _box_all() self._arm(recipe_dbref, "makefire", ["flint", "red steel"]) self._check_room_contents({"red steel": 1, "flint": 1}) self._use("red steel, flint", "You are a Genius") self._check_room_contents({"smoke": 1, "fire": 0}) def test_lspuzzlerecipes_lsarmedpuzzles(self): msg = self.call(puzzles.CmdListPuzzleRecipes(), "", caller=self.char1) self._assert_msg_matched( msg, [r"^-+$", r"^Found 0 puzzle\(s\)\.$", r"-+$"], re.MULTILINE | re.DOTALL ) recipe_dbref = self._good_recipe( "makefire", ["steel", "flint"], ["fire"], and_destroy_it=False ) msg = self.call(puzzles.CmdListPuzzleRecipes(), "", caller=self.char1) self._assert_msg_matched( msg, [ r"^-+$", r"^Puzzle 'makefire'.*$", r"^Success Caller message:$", r"^Success Location message:$", r"^Mask:$", r"^Parts$", r"^.*key: steel$", r"^.*key: flint$", r"^Results$", r"^.*key: fire$", r"^.*key: steel$", r"^.*key: flint$", r"^-+$", r"^Found 1 puzzle\(s\)\.$", r"^-+$", ], re.MULTILINE | re.DOTALL, ) msg = self.call(puzzles.CmdListArmedPuzzles(), "", caller=self.char1) self._assert_msg_matched( msg, [r"^-+$", r"^-+$", r"^Found 0 armed puzzle\(s\)\.$", r"^-+$"], re.MULTILINE | re.DOTALL, ) self._arm(recipe_dbref, "makefire", ["steel", "flint"]) msg = self.call(puzzles.CmdListArmedPuzzles(), "", caller=self.char1) self._assert_msg_matched( msg, [ r"^-+$", r"^Puzzle name: makefire$", r"^.*steel.* at \s+ Room.*$", r"^.*flint.* at \s+ Room.*$", r"^Found 1 armed puzzle\(s\)\.$", r"^-+$", ], re.MULTILINE | re.DOTALL, ) def test_e2e(self): def _destroy_objs_in_room(keys): for obj in self.room1.contents: if obj.key in keys: obj.delete() # parts don't survive resolution # but produce a large result set tree = create_object(self.object_typeclass, key="tree", location=self.char1.location) axe = create_object(self.object_typeclass, key="axe", location=self.char1.location) sweat = create_object(self.object_typeclass, key="sweat", location=self.char1.location) dull_axe = create_object( self.object_typeclass, key="dull axe", location=self.char1.location ) timber = create_object(self.object_typeclass, key="timber", location=self.char1.location) log = create_object(self.object_typeclass, key="log", location=self.char1.location) parts = ["tree", "axe"] results = (["sweat"] * 10) + ["dull axe"] + (["timber"] * 20) + (["log"] * 50) recipe_dbref = self._good_recipe("lumberjack", parts, results, and_destroy_it=False) _destroy_objs_in_room(set(parts + results)) sps = sorted(parts) expected = {key: len(list(grp)) for key, grp in itertools.groupby(sps)} expected.update({r: 0 for r in set(results)}) self._arm(recipe_dbref, "lumberjack", parts) self._check_room_contents(expected) self._use(",".join(parts), "You are a Genius") srs = sorted(set(results)) expected = {(key, len(list(grp))) for key, grp in itertools.groupby(srs)} expected.update({p: 0 for p in set(parts)}) self._check_room_contents(expected) # parts also appear in results # causing a new puzzle to be armed 'automatically' # i.e. the puzzle is self-sustaining hole = create_object(self.object_typeclass, key="hole", location=self.char1.location) shovel = create_object(self.object_typeclass, key="shovel", location=self.char1.location) dirt = create_object(self.object_typeclass, key="dirt", location=self.char1.location) parts = ["shovel", "hole"] results = ["dirt", "hole", "shovel"] recipe_dbref = self._good_recipe( "digger", parts, results, and_destroy_it=False, expected_count=2 ) _destroy_objs_in_room(set(parts + results)) nresolutions = 0 sps = sorted(set(parts)) expected = {key: len(list(grp)) for key, grp in itertools.groupby(sps)} expected.update({"dirt": nresolutions}) self._arm(recipe_dbref, "digger", parts) self._check_room_contents(expected) for i in range(10): self._use(",".join(parts), "You are a Genius") nresolutions += 1 expected.update({"dirt": nresolutions}) self._check_room_contents(expected) # Uppercase puzzle name balloon = create_object(self.object_typeclass, key="Balloon", location=self.char1.location) parts = ["Balloon"] results = ["Balloon"] recipe_dbref = self._good_recipe( "boom!!!", parts, results, and_destroy_it=False, expected_count=3 ) _destroy_objs_in_room(set(parts + results)) sps = sorted(parts) expected = {key: len(list(grp)) for key, grp in itertools.groupby(sps)} self._arm(recipe_dbref, "boom!!!", parts) self._check_room_contents(expected) self._use(",".join(parts), "You are a Genius") srs = sorted(set(results)) expected = {(key, len(list(grp))) for key, grp in itertools.groupby(srs)} self._check_room_contents(expected) def test_e2e_accumulative(self): flashlight = create_object( self.object_typeclass, key="flashlight", location=self.char1.location ) flashlight_w_1 = create_object( self.object_typeclass, key="flashlight-w-1", location=self.char1.location ) flashlight_w_2 = create_object( self.object_typeclass, key="flashlight-w-2", location=self.char1.location ) flashlight_w_3 = create_object( self.object_typeclass, key="flashlight-w-3", location=self.char1.location ) battery = create_object(self.object_typeclass, key="battery", location=self.char1.location) battery.tags.add("flashlight-1", category=puzzles._PUZZLES_TAG_CATEGORY) battery.tags.add("flashlight-2", category=puzzles._PUZZLES_TAG_CATEGORY) battery.tags.add("flashlight-3", category=puzzles._PUZZLES_TAG_CATEGORY) # TODO: instead of tagging each flashlight, # arm and resolve each puzzle in order so they all # are tagged correctly # it will be necessary to add/remove parts/results because # each battery is supposed to be consumed during resolution # as the new flashlight has one more battery than before flashlight_w_1.tags.add("flashlight-2", category=puzzles._PUZZLES_TAG_CATEGORY) flashlight_w_2.tags.add("flashlight-3", category=puzzles._PUZZLES_TAG_CATEGORY) recipe_fl1_dbref = self._good_recipe( "flashlight-1", ["flashlight", "battery"], ["flashlight-w-1"], and_destroy_it=False, expected_count=1, ) recipe_fl2_dbref = self._good_recipe( "flashlight-2", ["flashlight-w-1", "battery"], ["flashlight-w-2"], and_destroy_it=False, expected_count=2, ) recipe_fl3_dbref = self._good_recipe( "flashlight-3", ["flashlight-w-2", "battery"], ["flashlight-w-3"], and_destroy_it=False, expected_count=3, ) # delete protoparts for obj in [battery, flashlight, flashlight_w_1, flashlight_w_2, flashlight_w_3]: obj.delete() def _group_parts(parts, excluding=set()): group = dict() dbrefs = dict() for o in self.room1.contents: if o.key in parts and o.dbref not in excluding: if o.key not in group: group[o.key] = [] group[o.key].append(o.dbref) dbrefs[o.dbref] = o return group, dbrefs # arm each puzzle and group its parts self._arm(recipe_fl1_dbref, "flashlight-1", ["battery", "flashlight"]) fl1_parts, fl1_dbrefs = _group_parts(["battery", "flashlight"]) self._arm(recipe_fl2_dbref, "flashlight-2", ["battery", "flashlight-w-1"]) fl2_parts, fl2_dbrefs = _group_parts( ["battery", "flashlight-w-1"], excluding=list(fl1_dbrefs.keys()) ) self._arm(recipe_fl3_dbref, "flashlight-3", ["battery", "flashlight-w-2"]) fl3_parts, fl3_dbrefs = _group_parts( ["battery", "flashlight-w-2"], excluding=set(list(fl1_dbrefs.keys()) + list(fl2_dbrefs.keys())), ) self._check_room_contents( { "battery": 3, "flashlight": 1, "flashlight-w-1": 1, "flashlight-w-2": 1, "flashlight-w-3": 0, } ) # all batteries have identical protodefs battery_1 = fl1_dbrefs[fl1_parts["battery"][0]] battery_2 = fl2_dbrefs[fl2_parts["battery"][0]] battery_3 = fl3_dbrefs[fl3_parts["battery"][0]] protodef_battery_1 = puzzles.proto_def(battery_1, with_tags=False) del protodef_battery_1["prototype_key"] protodef_battery_2 = puzzles.proto_def(battery_2, with_tags=False) del protodef_battery_2["prototype_key"] protodef_battery_3 = puzzles.proto_def(battery_3, with_tags=False) del protodef_battery_3["prototype_key"] assert protodef_battery_1 == protodef_battery_2 == protodef_battery_3 # each battery can be used in every other puzzle b1_parts_dict, b1_puzzlenames, b1_protodefs = puzzles._lookups_parts_puzzlenames_protodefs( [battery_1] ) _puzzles = puzzles._puzzles_by_names(b1_puzzlenames.keys()) assert set(["flashlight-1", "flashlight-2", "flashlight-3"]) == set( [p.db.puzzle_name for p in _puzzles] ) matched_puzzles = puzzles._matching_puzzles(_puzzles, b1_puzzlenames, b1_protodefs) assert 0 == len(matched_puzzles) b2_parts_dict, b2_puzzlenames, b2_protodefs = puzzles._lookups_parts_puzzlenames_protodefs( [battery_2] ) _puzzles = puzzles._puzzles_by_names(b2_puzzlenames.keys()) assert set(["flashlight-1", "flashlight-2", "flashlight-3"]) == set( [p.db.puzzle_name for p in _puzzles] ) matched_puzzles = puzzles._matching_puzzles(_puzzles, b2_puzzlenames, b2_protodefs) assert 0 == len(matched_puzzles) b3_parts_dict, b3_puzzlenames, b3_protodefs = puzzles._lookups_parts_puzzlenames_protodefs( [battery_3] ) _puzzles = puzzles._puzzles_by_names(b3_puzzlenames.keys()) assert set(["flashlight-1", "flashlight-2", "flashlight-3"]) == set( [p.db.puzzle_name for p in _puzzles] ) matched_puzzles = puzzles._matching_puzzles(_puzzles, b3_puzzlenames, b3_protodefs) assert 0 == len(matched_puzzles) assert battery_1 == list(b1_parts_dict.values())[0] assert battery_2 == list(b2_parts_dict.values())[0] assert battery_3 == list(b3_parts_dict.values())[0] assert b1_puzzlenames.keys() == b2_puzzlenames.keys() == b3_puzzlenames.keys() for puzzle_name in ["flashlight-1", "flashlight-2", "flashlight-3"]: assert puzzle_name in b1_puzzlenames assert puzzle_name in b2_puzzlenames assert puzzle_name in b3_puzzlenames assert ( list(b1_protodefs.values())[0] == list(b2_protodefs.values())[0] == list(b3_protodefs.values())[0] == protodef_battery_1 == protodef_battery_2 == protodef_battery_3 ) # all flashlights have similar protodefs except their key flashlight_1 = fl1_dbrefs[fl1_parts["flashlight"][0]] flashlight_2 = fl2_dbrefs[fl2_parts["flashlight-w-1"][0]] flashlight_3 = fl3_dbrefs[fl3_parts["flashlight-w-2"][0]] protodef_flashlight_1 = puzzles.proto_def(flashlight_1, with_tags=False) del protodef_flashlight_1["prototype_key"] assert protodef_flashlight_1["key"] == "flashlight" del protodef_flashlight_1["key"] protodef_flashlight_2 = puzzles.proto_def(flashlight_2, with_tags=False) del protodef_flashlight_2["prototype_key"] assert protodef_flashlight_2["key"] == "flashlight-w-1" del protodef_flashlight_2["key"] protodef_flashlight_3 = puzzles.proto_def(flashlight_3, with_tags=False) del protodef_flashlight_3["prototype_key"] assert protodef_flashlight_3["key"] == "flashlight-w-2" del protodef_flashlight_3["key"] assert protodef_flashlight_1 == protodef_flashlight_2 == protodef_flashlight_3 # each flashlight can only be used in its own puzzle f1_parts_dict, f1_puzzlenames, f1_protodefs = puzzles._lookups_parts_puzzlenames_protodefs( [flashlight_1] ) _puzzles = puzzles._puzzles_by_names(f1_puzzlenames.keys()) assert set(["flashlight-1"]) == set([p.db.puzzle_name for p in _puzzles]) matched_puzzles = puzzles._matching_puzzles(_puzzles, f1_puzzlenames, f1_protodefs) assert 0 == len(matched_puzzles) f2_parts_dict, f2_puzzlenames, f2_protodefs = puzzles._lookups_parts_puzzlenames_protodefs( [flashlight_2] ) _puzzles = puzzles._puzzles_by_names(f2_puzzlenames.keys()) assert set(["flashlight-2"]) == set([p.db.puzzle_name for p in _puzzles]) matched_puzzles = puzzles._matching_puzzles(_puzzles, f2_puzzlenames, f2_protodefs) assert 0 == len(matched_puzzles) f3_parts_dict, f3_puzzlenames, f3_protodefs = puzzles._lookups_parts_puzzlenames_protodefs( [flashlight_3] ) _puzzles = puzzles._puzzles_by_names(f3_puzzlenames.keys()) assert set(["flashlight-3"]) == set([p.db.puzzle_name for p in _puzzles]) matched_puzzles = puzzles._matching_puzzles(_puzzles, f3_puzzlenames, f3_protodefs) assert 0 == len(matched_puzzles) assert flashlight_1 == list(f1_parts_dict.values())[0] assert flashlight_2 == list(f2_parts_dict.values())[0] assert flashlight_3 == list(f3_parts_dict.values())[0] for puzzle_name in set( list(f1_puzzlenames.keys()) + list(f2_puzzlenames.keys()) + list(f3_puzzlenames.keys()) ): assert puzzle_name in ["flashlight-1", "flashlight-2", "flashlight-3", "puzzle_member"] protodef_flashlight_1["key"] = "flashlight" assert list(f1_protodefs.values())[0] == protodef_flashlight_1 protodef_flashlight_2["key"] = "flashlight-w-1" assert list(f2_protodefs.values())[0] == protodef_flashlight_2 protodef_flashlight_3["key"] = "flashlight-w-2" assert list(f3_protodefs.values())[0] == protodef_flashlight_3 # each battery can be matched with every other flashlight # to potentially resolve each puzzle for batt in [battery_1, battery_2, battery_3]: parts_dict, puzzlenames, protodefs = puzzles._lookups_parts_puzzlenames_protodefs( [batt, flashlight_1] ) assert set([batt.dbref, flashlight_1.dbref]) == set(puzzlenames["flashlight-1"]) assert set([batt.dbref]) == set(puzzlenames["flashlight-2"]) assert set([batt.dbref]) == set(puzzlenames["flashlight-3"]) _puzzles = puzzles._puzzles_by_names(puzzlenames.keys()) assert set(["flashlight-1", "flashlight-2", "flashlight-3"]) == set( [p.db.puzzle_name for p in _puzzles] ) matched_puzzles = puzzles._matching_puzzles(_puzzles, puzzlenames, protodefs) assert 1 == len(matched_puzzles) parts_dict, puzzlenames, protodefs = puzzles._lookups_parts_puzzlenames_protodefs( [batt, flashlight_2] ) assert set([batt.dbref]) == set(puzzlenames["flashlight-1"]) assert set([batt.dbref, flashlight_2.dbref]) == set(puzzlenames["flashlight-2"]) assert set([batt.dbref]) == set(puzzlenames["flashlight-3"]) _puzzles = puzzles._puzzles_by_names(puzzlenames.keys()) assert set(["flashlight-1", "flashlight-2", "flashlight-3"]) == set( [p.db.puzzle_name for p in _puzzles] ) matched_puzzles = puzzles._matching_puzzles(_puzzles, puzzlenames, protodefs) assert 1 == len(matched_puzzles) parts_dict, puzzlenames, protodefs = puzzles._lookups_parts_puzzlenames_protodefs( [batt, flashlight_3] ) assert set([batt.dbref]) == set(puzzlenames["flashlight-1"]) assert set([batt.dbref]) == set(puzzlenames["flashlight-2"]) assert set([batt.dbref, flashlight_3.dbref]) == set(puzzlenames["flashlight-3"]) _puzzles = puzzles._puzzles_by_names(puzzlenames.keys()) assert set(["flashlight-1", "flashlight-2", "flashlight-3"]) == set( [p.db.puzzle_name for p in _puzzles] ) matched_puzzles = puzzles._matching_puzzles(_puzzles, puzzlenames, protodefs) assert 1 == len(matched_puzzles) # delete all parts for part in ( list(fl1_dbrefs.values()) + list(fl2_dbrefs.values()) + list(fl3_dbrefs.values()) ): part.delete() self._check_room_contents( { "battery": 0, "flashlight": 0, "flashlight-w-1": 0, "flashlight-w-2": 0, "flashlight-w-3": 0, } ) # arm first puzzle 3 times and group its parts so we can solve # all puzzles with the parts from the 1st armed for i in range(3): self._arm(recipe_fl1_dbref, "flashlight-1", ["battery", "flashlight"]) fl1_parts, fl1_dbrefs = _group_parts(["battery", "flashlight"]) # delete the 2 extra flashlights so we can start solving for flashlight_dbref in fl1_parts["flashlight"][1:]: fl1_dbrefs[flashlight_dbref].delete() self._check_room_contents( { "battery": 3, "flashlight": 1, "flashlight-w-1": 0, "flashlight-w-2": 0, "flashlight-w-3": 0, } ) self._use("battery-1, flashlight", "You are a Genius") self._check_room_contents( { "battery": 2, "flashlight": 0, "flashlight-w-1": 1, "flashlight-w-2": 0, "flashlight-w-3": 0, } ) self._use("battery-1, flashlight-w-1", "You are a Genius") self._check_room_contents( { "battery": 1, "flashlight": 0, "flashlight-w-1": 0, "flashlight-w-2": 1, "flashlight-w-3": 0, } ) self._use("battery, flashlight-w-2", "You are a Genius") self._check_room_contents( { "battery": 0, "flashlight": 0, "flashlight-w-1": 0, "flashlight-w-2": 0, "flashlight-w-3": 1, } ) def test_e2e_interchangeable_parts_and_results(self): # Parts and Results can be used in multiple puzzles egg = create_object(self.object_typeclass, key="egg", location=self.char1.location) flour = create_object(self.object_typeclass, key="flour", location=self.char1.location) boiling_water = create_object( self.object_typeclass, key="boiling water", location=self.char1.location ) boiled_egg = create_object( self.object_typeclass, key="boiled egg", location=self.char1.location ) dough = create_object(self.object_typeclass, key="dough", location=self.char1.location) pasta = create_object(self.object_typeclass, key="pasta", location=self.char1.location) # Three recipes: # 1. breakfast: egg + boiling water = boiled egg & boiling water # 2. dough: egg + flour = dough # 3. entree: dough + boiling water = pasta & boiling water # tag interchangeable parts according to their puzzles' name egg.tags.add("breakfast", category=puzzles._PUZZLES_TAG_CATEGORY) egg.tags.add("dough", category=puzzles._PUZZLES_TAG_CATEGORY) dough.tags.add("entree", category=puzzles._PUZZLES_TAG_CATEGORY) boiling_water.tags.add("breakfast", category=puzzles._PUZZLES_TAG_CATEGORY) boiling_water.tags.add("entree", category=puzzles._PUZZLES_TAG_CATEGORY) # create recipes recipe1_dbref = self._good_recipe( "breakfast", ["egg", "boiling water"], ["boiled egg", "boiling water"], and_destroy_it=False, ) recipe2_dbref = self._good_recipe( "dough", ["egg", "flour"], ["dough"], and_destroy_it=False, expected_count=2 ) recipe3_dbref = self._good_recipe( "entree", ["dough", "boiling water"], ["pasta", "boiling water"], and_destroy_it=False, expected_count=3, ) # delete protoparts for obj in [egg, flour, boiling_water, boiled_egg, dough, pasta]: obj.delete() # arm each puzzle and group its parts def _group_parts(parts, excluding=set()): group = dict() dbrefs = dict() for o in self.room1.contents: if o.key in parts and o.dbref not in excluding: if o.key not in group: group[o.key] = [] group[o.key].append(o.dbref) dbrefs[o.dbref] = o return group, dbrefs self._arm(recipe1_dbref, "breakfast", ["egg", "boiling water"]) breakfast_parts, breakfast_dbrefs = _group_parts(["egg", "boiling water"]) self._arm(recipe2_dbref, "dough", ["egg", "flour"]) dough_parts, dough_dbrefs = _group_parts( ["egg", "flour"], excluding=list(breakfast_dbrefs.keys()) ) self._arm(recipe3_dbref, "entree", ["dough", "boiling water"]) entree_parts, entree_dbrefs = _group_parts( ["dough", "boiling water"], excluding=set(list(breakfast_dbrefs.keys()) + list(dough_dbrefs.keys())), ) # create a box so we can put all objects in # so that they can't be found during puzzle resolution self.box = create_object(self.object_typeclass, key="box", location=self.char1.location) def _box_all(): # print "boxing all\n", "-"*20 for o in self.room1.contents: if o not in [self.char1, self.char2, self.exit, self.obj1, self.obj2, self.box]: o.location = self.box # print o.key, o.dbref, "boxed" else: # print "skipped", o.key, o.dbref pass def _unbox(dbrefs): # print "unboxing", dbrefs, "\n", "-"*20 for o in self.box.contents: if o.dbref in dbrefs: o.location = self.room1 # print "unboxed", o.key, o.dbref # solve dough puzzle using breakfast's egg # and dough's flour. A new dough will be created _box_all() _unbox(breakfast_parts.pop("egg") + dough_parts.pop("flour")) self._use("egg, flour", "You are a Genius") # solve entree puzzle with newly created dough # and breakfast's boiling water. A new # boiling water and pasta will be created _unbox(breakfast_parts.pop("boiling water")) self._use("boiling water, dough", "You are a Genius") # solve breakfast puzzle with dough's egg # and newly created boiling water. A new # boiling water and boiled egg will be created _unbox(dough_parts.pop("egg")) self._use("boiling water, egg", "You are a Genius") # solve entree puzzle using entree's dough # and newly created boiling water. A new # boiling water and pasta will be created _unbox(entree_parts.pop("dough")) self._use("boiling water, dough", "You are a Genius") self._check_room_contents({"boiling water": 1, "pasta": 2, "boiled egg": 1}) # Tests for the building_menu contrib from evennia.contrib.building_menu import BuildingMenu, CmdNoMatch class Submenu(BuildingMenu): def init(self, exit): self.add_choice("title", key="t", attr="key") class TestBuildingMenu(CommandTest): def setUp(self): super(TestBuildingMenu, self).setUp() self.menu = BuildingMenu(caller=self.char1, obj=self.room1, title="test") self.menu.add_choice("title", key="t", attr="key") def test_quit(self): """Try to quit the building menu.""" self.assertFalse(self.char1.cmdset.has("building_menu")) self.menu.open() self.assertTrue(self.char1.cmdset.has("building_menu")) self.call(CmdNoMatch(building_menu=self.menu), "q") # char1 tries to quit the editor self.assertFalse(self.char1.cmdset.has("building_menu")) def test_setattr(self): """Test the simple setattr provided by building menus.""" key = self.room1.key self.menu.open() self.call(CmdNoMatch(building_menu=self.menu), "t") self.assertIsNotNone(self.menu.current_choice) self.call(CmdNoMatch(building_menu=self.menu), "some new title") self.call(CmdNoMatch(building_menu=self.menu), "@") self.assertIsNone(self.menu.current_choice) self.assertEqual(self.room1.key, "some new title") self.call(CmdNoMatch(building_menu=self.menu), "q") def test_add_choice_without_key(self): """Try to add choices without keys.""" choices = [] for i in range(20): choices.append(self.menu.add_choice("choice", attr="test")) self.menu._add_keys_choice() keys = [ "c", "h", "o", "i", "e", "ch", "ho", "oi", "ic", "ce", "cho", "hoi", "oic", "ice", "choi", "hoic", "oice", "choic", "hoice", "choice", ] for i in range(20): self.assertEqual(choices[i].key, keys[i]) # Adding another key of the same title would break, no more available shortcut self.menu.add_choice("choice", attr="test") with self.assertRaises(ValueError): self.menu._add_keys_choice() def test_callbacks(self): """Test callbacks in menus.""" self.room1.key = "room1" def on_enter(caller, menu): caller.msg("on_enter:{}".format(menu.title)) def on_nomatch(caller, string, choice): caller.msg("on_nomatch:{},{}".format(string, choice.key)) def on_leave(caller, obj): caller.msg("on_leave:{}".format(obj.key)) self.menu.add_choice( "test", key="e", on_enter=on_enter, on_nomatch=on_nomatch, on_leave=on_leave ) self.call(CmdNoMatch(building_menu=self.menu), "e", "on_enter:test") self.call(CmdNoMatch(building_menu=self.menu), "ok", "on_nomatch:ok,e") self.call(CmdNoMatch(building_menu=self.menu), "@", "on_leave:room1") self.call(CmdNoMatch(building_menu=self.menu), "q") def test_multi_level(self): """Test multi-level choices.""" # Creaste three succeeding menu (t2 is contained in t1, t3 is contained in t2) def on_nomatch_t1(caller, menu): menu.move("whatever") # this will be valid since after t1 is a joker def on_nomatch_t2(caller, menu): menu.move("t3") # this time the key matters t1 = self.menu.add_choice("what", key="t1", on_nomatch=on_nomatch_t1) t2 = self.menu.add_choice("and", key="t1.*", on_nomatch=on_nomatch_t2) t3 = self.menu.add_choice("why", key="t1.*.t3") self.menu.open() # Move into t1 self.assertIn(t1, self.menu.relevant_choices) self.assertNotIn(t2, self.menu.relevant_choices) self.assertNotIn(t3, self.menu.relevant_choices) self.assertIsNone(self.menu.current_choice) self.call(CmdNoMatch(building_menu=self.menu), "t1") self.assertEqual(self.menu.current_choice, t1) self.assertNotIn(t1, self.menu.relevant_choices) self.assertIn(t2, self.menu.relevant_choices) self.assertNotIn(t3, self.menu.relevant_choices) # Move into t2 self.call(CmdNoMatch(building_menu=self.menu), "t2") self.assertEqual(self.menu.current_choice, t2) self.assertNotIn(t1, self.menu.relevant_choices) self.assertNotIn(t2, self.menu.relevant_choices) self.assertIn(t3, self.menu.relevant_choices) # Move into t3 self.call(CmdNoMatch(building_menu=self.menu), "t3") self.assertEqual(self.menu.current_choice, t3) self.assertNotIn(t1, self.menu.relevant_choices) self.assertNotIn(t2, self.menu.relevant_choices) self.assertNotIn(t3, self.menu.relevant_choices) # Move back to t2 self.call(CmdNoMatch(building_menu=self.menu), "@") self.assertEqual(self.menu.current_choice, t2) self.assertNotIn(t1, self.menu.relevant_choices) self.assertNotIn(t2, self.menu.relevant_choices) self.assertIn(t3, self.menu.relevant_choices) # Move back into t1 self.call(CmdNoMatch(building_menu=self.menu), "@") self.assertEqual(self.menu.current_choice, t1) self.assertNotIn(t1, self.menu.relevant_choices) self.assertIn(t2, self.menu.relevant_choices) self.assertNotIn(t3, self.menu.relevant_choices) # Moves back to the main menu self.call(CmdNoMatch(building_menu=self.menu), "@") self.assertIn(t1, self.menu.relevant_choices) self.assertNotIn(t2, self.menu.relevant_choices) self.assertNotIn(t3, self.menu.relevant_choices) self.assertIsNone(self.menu.current_choice) self.call(CmdNoMatch(building_menu=self.menu), "q") def test_submenu(self): """Test to add sub-menus.""" def open_exit(menu): menu.open_submenu("evennia.contrib.tests.Submenu", self.exit) return False self.menu.add_choice("exit", key="x", on_enter=open_exit) self.menu.open() self.call(CmdNoMatch(building_menu=self.menu), "x") self.menu = self.char1.ndb._building_menu self.call(CmdNoMatch(building_menu=self.menu), "t") self.call(CmdNoMatch(building_menu=self.menu), "in") self.call(CmdNoMatch(building_menu=self.menu), "@") self.call(CmdNoMatch(building_menu=self.menu), "@") self.menu = self.char1.ndb._building_menu self.assertEqual(self.char1.ndb._building_menu.obj, self.room1) self.call(CmdNoMatch(building_menu=self.menu), "q") self.assertEqual(self.exit.key, "in") from evennia.contrib import mux_comms_cmds as comms # noqa class TestLegacyMuxComms(CommandTest): """ Test the legacy comms contrib. """ def setUp(self): super(CommandTest, self).setUp() self.call( comms.CmdChannelCreate(), "testchan;test=Test Channel", "Created channel testchan and connected to it.", receiver=self.account, ) def test_toggle_com(self): self.call( comms.CmdAddCom(), "tc = testchan", "You are already connected to channel testchan.| You can now", receiver=self.account, ) self.call( comms.CmdDelCom(), "tc", "Any alias 'tc' for channel testchan was cleared.", receiver=self.account, ) def test_all_com(self): self.call( comms.CmdAllCom(), "", "Available channels:", receiver=self.account, ) def test_clock(self): self.call( comms.CmdClock(), "testchan=send:all()", "Lock(s) applied. Current locks on testchan:", receiver=self.account, ) def test_cdesc(self): self.call( comms.CmdCdesc(), "testchan = Test Channel", "Description of channel 'testchan' set to 'Test Channel'.", receiver=self.account, ) def test_cwho(self): self.call( comms.CmdCWho(), "testchan", "Channel subscriptions\ntestchan:\n TestAccount", receiver=self.account, ) def test_page(self): self.call( comms.CmdPage(), "TestAccount2 = Test", "TestAccount2 is offline. They will see your message if they list their pages later." "|You paged TestAccount2 with: 'Test'.", receiver=self.account, ) def test_cboot(self): # No one else connected to boot self.call( comms.CmdCBoot(), "", "Usage: cboot[/quiet] = [:reason]", receiver=self.account, ) def test_cdestroy(self): self.call( comms.CmdCdestroy(), "testchan", "[testchan] TestAccount: testchan is being destroyed. Make sure to change your aliases." "|Channel 'testchan' was destroyed.", receiver=self.account, )