From 4cff93e8e173b88d37900ca70fd67e8d804457c3 Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 8 Oct 2018 18:20:35 +0200 Subject: [PATCH 01/26] Resolve bug when trying to examine self when unprivileged --- evennia/accounts/accounts.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index 2c33e5c1f8..3eab69a3ce 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -1001,7 +1001,10 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)): if target and not is_iter(target): # single target - just show it - return target.return_appearance(self) + if hasattr(target, "return_appearance"): + return target.return_appearance(self) + else: + return "{} has no in-game appearance.".format(target) else: # list of targets - make list to disconnect from db characters = list(tar for tar in target if tar) if target else [] From e0e72bda70a7539ba95c57ada7bc68dd0e977c3d Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 8 Oct 2018 18:50:33 +0200 Subject: [PATCH 02/26] Make tutorial_world roots give clearer errors. Allow home/quit from dark room. Resolves #1584. --- evennia/contrib/tutorial_world/objects.py | 10 +++++----- evennia/contrib/tutorial_world/rooms.py | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/evennia/contrib/tutorial_world/objects.py b/evennia/contrib/tutorial_world/objects.py index f83462ad6b..331b6b1a21 100644 --- a/evennia/contrib/tutorial_world/objects.py +++ b/evennia/contrib/tutorial_world/objects.py @@ -475,14 +475,14 @@ class CmdShiftRoot(Command): root_pos["blue"] -= 1 self.caller.msg("The root with blue flowers gets in the way and is pushed to the left.") else: - self.caller.msg("You cannot move the root in that direction.") + self.caller.msg("The root hangs straight down - you can only move it left or right.") elif color == "blue": if direction == "left": root_pos[color] = max(-1, root_pos[color] - 1) self.caller.msg("You shift the root with small blue flowers to the left.") if root_pos[color] != 0 and root_pos[color] == root_pos["red"]: root_pos["red"] += 1 - self.caller.msg("The reddish root is to big to fit as well, so that one falls away to the left.") + self.caller.msg("The reddish root is too big to fit as well, so that one falls away to the left.") elif direction == "right": root_pos[color] = min(1, root_pos[color] + 1) self.caller.msg("You shove the root adorned with small blue flowers to the right.") @@ -490,7 +490,7 @@ class CmdShiftRoot(Command): root_pos["red"] -= 1 self.caller.msg("The thick reddish root gets in the way and is pushed back to the left.") else: - self.caller.msg("You cannot move the root in that direction.") + self.caller.msg("The root hangs straight down - you can only move it left or right.") # now the horizontal roots (yellow/green). They can be moved up/down elif color == "yellow": @@ -507,7 +507,7 @@ class CmdShiftRoot(Command): root_pos["green"] -= 1 self.caller.msg("The weedy green root is shifted upwards to make room.") else: - self.caller.msg("You cannot move the root in that direction.") + self.caller.msg("The root hangs across the wall - you can only move it up or down.") elif color == "green": if direction == "up": root_pos[color] = max(-1, root_pos[color] - 1) @@ -522,7 +522,7 @@ class CmdShiftRoot(Command): root_pos["yellow"] -= 1 self.caller.msg("The root with yellow flowers gets in the way and is pushed upwards.") else: - self.caller.msg("You cannot move the root in that direction.") + self.caller.msg("The root hangs across the wall - you can only move it up or down.") # we have moved the root. Store new position self.obj.db.root_pos = root_pos diff --git a/evennia/contrib/tutorial_world/rooms.py b/evennia/contrib/tutorial_world/rooms.py index 780f774af7..58e13a1356 100644 --- a/evennia/contrib/tutorial_world/rooms.py +++ b/evennia/contrib/tutorial_world/rooms.py @@ -747,9 +747,16 @@ class CmdLookDark(Command): """ caller = self.caller - if random.random() < 0.75: + # count how many searches we've done + nr_searches = caller.ndb.dark_searches + if nr_searches is None: + nr_searches = 0 + caller.ndb.dark_searches = nr_searches + + if nr_searches < 4 and random.random() < 0.90: # we don't find anything caller.msg(random.choice(DARK_MESSAGES)) + caller.ndb.dark_searches += 1 else: # we could have found something! if any(obj for obj in caller.contents if utils.inherits_from(obj, LightSource)): @@ -791,7 +798,8 @@ class CmdDarkNoMatch(Command): def func(self): """Implements the command.""" - self.caller.msg("Until you find some light, there's not much you can do. Try feeling around.") + self.caller.msg("Until you find some light, there's not much you can do. " + "Try feeling around, maybe you'll find something helpful!") class DarkCmdSet(CmdSet): @@ -814,7 +822,9 @@ class DarkCmdSet(CmdSet): self.add(CmdLookDark()) self.add(CmdDarkHelp()) self.add(CmdDarkNoMatch()) - self.add(default_cmds.CmdSay) + self.add(default_cmds.CmdSay()) + self.add(default_cmds.CmdQuit()) + self.add(default_cmds.CmdHome()) class DarkRoom(TutorialRoom): From 8f3a1599935b252894d473bc04a8930478b0a942 Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 8 Oct 2018 19:03:15 +0200 Subject: [PATCH 03/26] Handle prototype modules with non-dicts as global variables --- evennia/prototypes/prototypes.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/evennia/prototypes/prototypes.py b/evennia/prototypes/prototypes.py index eac53a6504..fc8edb55ab 100644 --- a/evennia/prototypes/prototypes.py +++ b/evennia/prototypes/prototypes.py @@ -107,9 +107,10 @@ for mod in settings.PROTOTYPE_MODULES: # internally we store as (key, desc, locks, tags, prototype_dict) prots = [] for variable_name, prot in all_from_module(mod).items(): - if "prototype_key" not in prot: - prot['prototype_key'] = variable_name.lower() - prots.append((prot['prototype_key'], homogenize_prototype(prot))) + if isinstance(prot, dict): + if "prototype_key" not in prot: + prot['prototype_key'] = variable_name.lower() + prots.append((prot['prototype_key'], homogenize_prototype(prot))) # assign module path to each prototype_key for easy reference _MODULE_PROTOTYPE_MODULES.update({prototype_key.lower(): mod for prototype_key, _ in prots}) # make sure the prototype contains all meta info From 1aa447564e8354e910c8bccd8bbeca00d1dc8d5e Mon Sep 17 00:00:00 2001 From: Griatch Date: Wed, 10 Oct 2018 23:26:20 +0200 Subject: [PATCH 04/26] Make Session.execute_cmd consistent with Account/Object by accepting the `session` keyword --- evennia/server/serversession.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/evennia/server/serversession.py b/evennia/server/serversession.py index b7f74cef5d..c5de7cf5be 100644 --- a/evennia/server/serversession.py +++ b/evennia/server/serversession.py @@ -407,7 +407,7 @@ class ServerSession(Session): else: self.data_out(**kwargs) - def execute_cmd(self, raw_string, **kwargs): + def execute_cmd(self, raw_string, session=None, **kwargs): """ Do something as this object. This method is normally never called directly, instead incoming command instructions are @@ -417,6 +417,9 @@ class ServerSession(Session): Args: raw_string (string): Raw command input + session (Session): This is here to make API consistent with + Account/Object.execute_cmd. If given, data is passed to + that Session, otherwise use self. Kwargs: Other keyword arguments will be added to the found command object instace as variables before it executes. This is @@ -426,7 +429,7 @@ class ServerSession(Session): """ # inject instruction into input stream kwargs["text"] = ((raw_string,), {}) - self.sessionhandler.data_in(self, **kwargs) + self.sessionhandler.data_in(session or self, **kwargs) def __eq__(self, other): """Handle session comparisons""" From 94fcd45156a351929428248d96b6b145cc3ab564 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 13 Oct 2018 16:59:07 +0200 Subject: [PATCH 05/26] Fix (again) of tag batch creation --- evennia/prototypes/spawner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/prototypes/spawner.py b/evennia/prototypes/spawner.py index ac6ad854b1..d1c099fb57 100644 --- a/evennia/prototypes/spawner.py +++ b/evennia/prototypes/spawner.py @@ -716,7 +716,7 @@ def spawn(*prototypes, **kwargs): val = prot.pop("tags", []) tags = [] for (tag, category, data) in val: - tags.append((init_spawn_value(val, str), category, data)) + tags.append((init_spawn_value(tag, str), category, data)) prototype_key = prototype.get('prototype_key', None) if prototype_key: From c69675e925f960533eb7d45768a200d8c37b8289 Mon Sep 17 00:00:00 2001 From: Griatch Date: Tue, 16 Oct 2018 10:13:05 +0200 Subject: [PATCH 06/26] Add requirement changes to 0.8 changelog, for clarity --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afe5ad1ba7..13c450939f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Evennia 0.8 (2018) +### Requirements + +- Up requirements to Django 1.11.x, Twisted 18 and pillow 5.2.0 +- Add `autobahn` dependency for Websocket support, removing very old embedded txWS library (from a + time before websocket specification was still not fixed). +- Add `inflect` dependency for automatic pluralization of object names. + ### Server/Portal - Removed `evennia_runner`, completely refactor `evennia_launcher.py` (the 'evennia' program) @@ -85,7 +92,6 @@ ### General -- Up requirements to Django 1.11.x, Twisted 18 and pillow 5.2.0 - Start structuring the `CHANGELOG` to list features in more detail. - Docker image `evennia/evennia:develop` is now auto-built, tracking the develop branch. - Inflection and grouping of multiple objects in default room (an box, three boxes) From 465b0f5b07ffa969aee9b97c70dcaf8ddac958a7 Mon Sep 17 00:00:00 2001 From: Tehom Date: Tue, 16 Oct 2018 19:51:43 -0400 Subject: [PATCH 07/26] Add stub for testing Telnet --- evennia/server/portal/tests.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/evennia/server/portal/tests.py b/evennia/server/portal/tests.py index be400144c6..b09f71ae03 100644 --- a/evennia/server/portal/tests.py +++ b/evennia/server/portal/tests.py @@ -11,6 +11,11 @@ except ImportError: import string from evennia.server.portal import irc +from twisted.test import proto_helpers +from twisted.trial.unittest import TestCase as TwistedTestCase + +from .telnet import TelnetServerFactory + class TestIRC(TestCase): @@ -73,3 +78,15 @@ class TestIRC(TestCase): s = r'|wthis|Xis|gis|Ma|C|complex|*string' self.assertEqual(irc.parse_irc_to_ansi(irc.parse_ansi_to_irc(s)), s) + + +class TestTelnet(TwistedTestCase): + def setUp(self): + super(TestTelnet, self).setUp() + factory = TelnetServerFactory() + self.proto = factory.buildProtocol(("localhost", 0)) + self.transport = proto_helpers.StringTransport() + + def test_connect(self): + self.proto.makeConnection(self.transport) + # TODO: Add rest of stuff for testing connection From fde9f1518893fd4973aaedfb964d672cc9cdf593 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Tue, 16 Oct 2018 19:31:10 -0500 Subject: [PATCH 08/26] Tests for @desc obj= --- evennia/commands/default/tests.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index a3be98984a..6010b008da 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -263,6 +263,20 @@ class TestBuilding(CommandTest): def test_desc(self): self.call(building.CmdDesc(), "Obj2=TestDesc", "The description was set on Obj2(#5).") + def test_empty_desc(self): + o2d = self.obj2.db.desc + r1d = self.room1.db.desc + self.call(building.CmdDesc(), "Obj2=", "The description was set on Obj2(#5).") + assert self.obj2.db.desc == '' + assert self.room1.db.desc == r1d + + def test_desc_default_to_room(self): + o2d = self.obj2.db.desc + r1d = self.room1.db.desc + self.call(building.CmdDesc(), "Obj2", "The description was set on Room(#1).") + assert self.obj2.db.desc == o2d + assert self.room1.db.desc == 'Obj2' + def test_wipe(self): confirm = building.CmdDestroy.confirm building.CmdDestroy.confirm = False @@ -446,4 +460,4 @@ class TestUnconnectedCommand(CommandTest): settings.SERVERNAME, datetime.datetime.fromtimestamp(gametime.SERVER_START_TIME).ctime(), SESSIONS.account_count(), utils.get_evennia_version()) - self.call(unloggedin.CmdUnconnectedInfo(), "", expected) \ No newline at end of file + self.call(unloggedin.CmdUnconnectedInfo(), "", expected) From 5b315e7ad56eda83ade2a4470de858980a0aa3f5 Mon Sep 17 00:00:00 2001 From: Tehom Date: Tue, 16 Oct 2018 20:33:12 -0400 Subject: [PATCH 09/26] Add some cleanup steps to prevent unclean reactor --- evennia/server/portal/telnet.py | 2 +- evennia/server/portal/tests.py | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/evennia/server/portal/telnet.py b/evennia/server/portal/telnet.py index 955ea5e918..83e2fa03a2 100644 --- a/evennia/server/portal/telnet.py +++ b/evennia/server/portal/telnet.py @@ -84,7 +84,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): from evennia.utils.utils import delay # timeout the handshakes in case the client doesn't reply at all - delay(2, callback=self.handshake_done, timeout=True) + self._handshake_delay = delay(2, callback=self.handshake_done, timeout=True) # TCP/IP keepalive watches for dead links self.transport.setTcpKeepAlive(1) diff --git a/evennia/server/portal/tests.py b/evennia/server/portal/tests.py index b09f71ae03..53a732f121 100644 --- a/evennia/server/portal/tests.py +++ b/evennia/server/portal/tests.py @@ -8,13 +8,15 @@ try: except ImportError: import unittest +from mock import Mock import string from evennia.server.portal import irc from twisted.test import proto_helpers from twisted.trial.unittest import TestCase as TwistedTestCase -from .telnet import TelnetServerFactory +from .telnet import TelnetServerFactory, TelnetProtocol +from .portal import PORTAL_SESSIONS class TestIRC(TestCase): @@ -84,9 +86,19 @@ class TestTelnet(TwistedTestCase): def setUp(self): super(TestTelnet, self).setUp() factory = TelnetServerFactory() + factory.protocol = TelnetProtocol + factory.sessionhandler = PORTAL_SESSIONS + factory.sessionhandler.portal = Mock() self.proto = factory.buildProtocol(("localhost", 0)) self.transport = proto_helpers.StringTransport() + self.addCleanup(factory.sessionhandler.disconnect_all) def test_connect(self): - self.proto.makeConnection(self.transport) + self.transport.client = ["localhost"] + self.transport.setTcpKeepAlive = Mock() + d = self.proto.makeConnection(self.transport) # TODO: Add rest of stuff for testing connection + # clean up to prevent Unclean reactor + self.proto.nop_keep_alive.stop() + self.proto._handshake_delay.cancel() + return d From 9238a4c6340335c6c5c212476f0d4093b24c1ba8 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Tue, 16 Oct 2018 19:49:19 -0500 Subject: [PATCH 10/26] In @desc command, validate rhs based on = sign present in orig args. Default MUX parsing assigns None to rhs if there is nothing on the right of the = sign. --- evennia/commands/default/building.py | 4 ++-- evennia/commands/default/tests.py | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index 0afeea8fe5..aaafea30ac 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -589,12 +589,12 @@ class CmdDesc(COMMAND_DEFAULT_CLASS): self.edit_handler() return - if self.rhs: + if '=' in self.args: # We have an = obj = caller.search(self.lhs) if not obj: return - desc = self.rhs + desc = self.rhs or '' else: obj = caller.location or self.msg("|rYou can't describe oblivion.|n") if not obj: diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index 6010b008da..6e5877608c 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -264,6 +264,9 @@ class TestBuilding(CommandTest): self.call(building.CmdDesc(), "Obj2=TestDesc", "The description was set on Obj2(#5).") def test_empty_desc(self): + """ + empty desc sets desc as '' + """ o2d = self.obj2.db.desc r1d = self.room1.db.desc self.call(building.CmdDesc(), "Obj2=", "The description was set on Obj2(#5).") @@ -271,6 +274,7 @@ class TestBuilding(CommandTest): assert self.room1.db.desc == r1d def test_desc_default_to_room(self): + """no rhs changes room's desc""" o2d = self.obj2.db.desc r1d = self.room1.db.desc self.call(building.CmdDesc(), "Obj2", "The description was set on Room(#1).") From ea55f8b782741955ce084b335044dc5bbcb5aa04 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Tue, 16 Oct 2018 20:09:17 -0500 Subject: [PATCH 11/26] Harden assertions --- evennia/commands/default/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index 6e5877608c..1b97a59f9f 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -270,7 +270,7 @@ class TestBuilding(CommandTest): o2d = self.obj2.db.desc r1d = self.room1.db.desc self.call(building.CmdDesc(), "Obj2=", "The description was set on Obj2(#5).") - assert self.obj2.db.desc == '' + assert self.obj2.db.desc == '' and self.obj2.db.desc != o2d assert self.room1.db.desc == r1d def test_desc_default_to_room(self): @@ -279,7 +279,7 @@ class TestBuilding(CommandTest): r1d = self.room1.db.desc self.call(building.CmdDesc(), "Obj2", "The description was set on Room(#1).") assert self.obj2.db.desc == o2d - assert self.room1.db.desc == 'Obj2' + assert self.room1.db.desc == 'Obj2' and self.room1.db.desc != r1d def test_wipe(self): confirm = building.CmdDestroy.confirm From 4561e3f5f4aab44fd64d0f7a6f4970448119638c Mon Sep 17 00:00:00 2001 From: Tehom Date: Wed, 17 Oct 2018 01:50:57 -0400 Subject: [PATCH 12/26] Add test of NOGOAHEAD --- evennia/server/portal/tests.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/evennia/server/portal/tests.py b/evennia/server/portal/tests.py index 53a732f121..4142ca297e 100644 --- a/evennia/server/portal/tests.py +++ b/evennia/server/portal/tests.py @@ -12,11 +12,13 @@ from mock import Mock import string from evennia.server.portal import irc +from twisted.conch.telnet import IAC, NOP, LINEMODE, GA, WILL, WONT, ECHO, NULL, DONT from twisted.test import proto_helpers from twisted.trial.unittest import TestCase as TwistedTestCase from .telnet import TelnetServerFactory, TelnetProtocol from .portal import PORTAL_SESSIONS +from .suppress_ga import SUPPRESS_GA class TestIRC(TestCase): @@ -98,6 +100,10 @@ class TestTelnet(TwistedTestCase): self.transport.setTcpKeepAlive = Mock() d = self.proto.makeConnection(self.transport) # TODO: Add rest of stuff for testing connection + self.assertTrue(self.proto.protocol_flags["NOGOAHEAD"]) + self.proto.dataReceived(IAC + DONT+ SUPPRESS_GA) + self.assertFalse(self.proto.protocol_flags["NOGOAHEAD"]) + self.assertEqual(self.proto.handshakes, 7) # clean up to prevent Unclean reactor self.proto.nop_keep_alive.stop() self.proto._handshake_delay.cancel() From b69e0f8dbcda68fd3fd772a614f589a5eaf4bf75 Mon Sep 17 00:00:00 2001 From: Tehom Date: Wed, 17 Oct 2018 12:49:48 -0400 Subject: [PATCH 13/26] Add various simple tests for different handshakes --- evennia/server/portal/tests.py | 49 +++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/evennia/server/portal/tests.py b/evennia/server/portal/tests.py index 4142ca297e..791e5172a4 100644 --- a/evennia/server/portal/tests.py +++ b/evennia/server/portal/tests.py @@ -12,13 +12,19 @@ from mock import Mock import string from evennia.server.portal import irc -from twisted.conch.telnet import IAC, NOP, LINEMODE, GA, WILL, WONT, ECHO, NULL, DONT +from twisted.conch.telnet import IAC, WILL, DONT, SB, SE, NAWS, DO from twisted.test import proto_helpers from twisted.trial.unittest import TestCase as TwistedTestCase from .telnet import TelnetServerFactory, TelnetProtocol from .portal import PORTAL_SESSIONS from .suppress_ga import SUPPRESS_GA +from .naws import DEFAULT_HEIGHT, DEFAULT_WIDTH +from .ttype import TTYPE, IS +from .mccp import MCCP +from .mssp import MSSP +from .mxp import MXP +from .telnet_oob import MSDP, MSDP_VAL, MSDP_VAR class TestIRC(TestCase): @@ -95,15 +101,50 @@ class TestTelnet(TwistedTestCase): self.transport = proto_helpers.StringTransport() self.addCleanup(factory.sessionhandler.disconnect_all) - def test_connect(self): + def test_mudlet_ttype(self): self.transport.client = ["localhost"] self.transport.setTcpKeepAlive = Mock() d = self.proto.makeConnection(self.transport) - # TODO: Add rest of stuff for testing connection + # test suppress_ga self.assertTrue(self.proto.protocol_flags["NOGOAHEAD"]) - self.proto.dataReceived(IAC + DONT+ SUPPRESS_GA) + self.proto.dataReceived(IAC + DONT + SUPPRESS_GA) self.assertFalse(self.proto.protocol_flags["NOGOAHEAD"]) self.assertEqual(self.proto.handshakes, 7) + # test naws + self.assertEqual(self.proto.protocol_flags['SCREENWIDTH'], {0: DEFAULT_WIDTH}) + self.assertEqual(self.proto.protocol_flags['SCREENHEIGHT'], {0: DEFAULT_HEIGHT}) + self.proto.dataReceived(IAC + WILL + NAWS) + self.proto.dataReceived([IAC, SB, NAWS, '', 'x', '', 'd', IAC, SE]) + self.assertEqual(self.proto.protocol_flags['SCREENWIDTH'][0], 120) + self.assertEqual(self.proto.protocol_flags['SCREENHEIGHT'][0], 100) + self.assertEqual(self.proto.handshakes, 6) + # test ttype + self.assertTrue(self.proto.protocol_flags["FORCEDENDLINE"]) + self.assertFalse(self.proto.protocol_flags["TTYPE"]) + self.assertTrue(self.proto.protocol_flags["ANSI"]) + self.proto.dataReceived(IAC + WILL + TTYPE) + self.proto.dataReceived([IAC, SB, TTYPE, IS, "MUDLET", IAC, SE]) + self.assertTrue(self.proto.protocol_flags["XTERM256"]) + self.assertEqual(self.proto.protocol_flags["CLIENTNAME"], "MUDLET") + self.proto.dataReceived([IAC, SB, TTYPE, IS, "XTERM", IAC, SE]) + self.proto.dataReceived([IAC, SB, TTYPE, IS, "MTTS 137", IAC, SE]) + self.assertEqual(self.proto.handshakes, 5) + # test mccp + self.proto.dataReceived(IAC + DONT + MCCP) + self.assertFalse(self.proto.protocol_flags['MCCP']) + self.assertEqual(self.proto.handshakes, 4) + # test mssp + self.proto.dataReceived(IAC + DONT + MSSP) + self.assertEqual(self.proto.handshakes, 3) + # test oob + self.proto.dataReceived(IAC + DO + MSDP) + self.proto.dataReceived([IAC, SB, MSDP, MSDP_VAR, "LIST", MSDP_VAL, "COMMANDS", IAC, SE]) + self.assertTrue(self.proto.protocol_flags['OOB']) + self.assertEqual(self.proto.handshakes, 2) + # test mxp + self.proto.dataReceived(IAC + DONT + MXP) + self.assertFalse(self.proto.protocol_flags['MXP']) + self.assertEqual(self.proto.handshakes, 1) # clean up to prevent Unclean reactor self.proto.nop_keep_alive.stop() self.proto._handshake_delay.cancel() From 78213ea160f2d1742f9d4a783a81e8f113a1602a Mon Sep 17 00:00:00 2001 From: Tehom Date: Wed, 17 Oct 2018 22:43:33 -0400 Subject: [PATCH 14/26] Add tests for memplot --- evennia/server/profiling/memplot.py | 6 +++--- evennia/server/profiling/tests.py | 21 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/evennia/server/profiling/memplot.py b/evennia/server/profiling/memplot.py index e8d7e76b12..c6a227a370 100644 --- a/evennia/server/profiling/memplot.py +++ b/evennia/server/profiling/memplot.py @@ -13,14 +13,14 @@ import time # TODO! #sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) #os.environ['DJANGO_SETTINGS_MODULE'] = 'game.settings' -import ev -from evennia.utils.idmapper import base as _idmapper +import evennia +from evennia.utils.idmapper import models as _idmapper LOGFILE = "logs/memoryusage.log" INTERVAL = 30 # log every 30 seconds -class Memplot(ev.Script): +class Memplot(evennia.DefaultScript): """ Describes a memory plotting action. diff --git a/evennia/server/profiling/tests.py b/evennia/server/profiling/tests.py index b3e9fba8d5..cca4e0d99b 100644 --- a/evennia/server/profiling/tests.py +++ b/evennia/server/profiling/tests.py @@ -1,7 +1,8 @@ from django.test import TestCase -from mock import Mock +from mock import Mock, patch, mock_open from .dummyrunner_settings import (c_creates_button, c_creates_obj, c_digs, c_examines, c_help, c_idles, c_login, c_login_nodig, c_logout, c_looks, c_moves, c_moves_n, c_moves_s, c_socialize) +import memplot class TestDummyrunnerSettings(TestCase): @@ -91,3 +92,21 @@ class TestDummyrunnerSettings(TestCase): def test_c_move_s(self): self.assertEqual(c_moves_s(self.client), "south") + + +class TestMemPlot(TestCase): + @patch.object(memplot, "_idmapper") + @patch.object(memplot, "os") + @patch.object(memplot, "open", new_callable=mock_open, create=True) + @patch.object(memplot, "time") + def test_memplot(self, mock_time, mocked_open, mocked_os, mocked_idmapper): + from evennia.utils.create import create_script + mocked_idmapper.cache_size.return_value = (9, 5000) + mock_time.time = Mock(return_value=6000.0) + script = create_script(memplot.Memplot) + script.db.starttime = 0.0 + mocked_os.popen.read.return_value = 5000.0 + script.at_repeat() + handle = mocked_open() + handle.write.assert_called_with('100.0, 0.001, 0.001, 9\n') + script.stop() From dc787dae8996a61ed2c63b3471c131f3c63b2fc5 Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 18 Oct 2018 17:15:54 +0200 Subject: [PATCH 15/26] Some clarification in the launcher --- CHANGELOG.md | 2 -- evennia/server/evennia_launcher.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13c450939f..5b56c29d85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,6 @@ ### Requirements - Up requirements to Django 1.11.x, Twisted 18 and pillow 5.2.0 -- Add `autobahn` dependency for Websocket support, removing very old embedded txWS library (from a - time before websocket specification was still not fixed). - Add `inflect` dependency for automatic pluralization of object names. ### Server/Portal diff --git a/evennia/server/evennia_launcher.py b/evennia/server/evennia_launcher.py index 779a1e4aa2..ef6bf61055 100644 --- a/evennia/server/evennia_launcher.py +++ b/evennia/server/evennia_launcher.py @@ -325,7 +325,7 @@ MENU = \ | 7) Kill Server only (send kill signal to process) | | 8) Kill Portal + Server | +--- Information -----------------------------------------------+ - | 9) Tail log files (quickly see errors) | + | 9) Tail log files (quickly see errors - Ctrl-C to exit) | | 10) Status | | 11) Port info | +--- Testing ---------------------------------------------------+ From d4b375d9cce1b3a1fbde10bee2c8d05e8963ce9c Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Thu, 18 Oct 2018 17:52:25 -0500 Subject: [PATCH 16/26] Tests for @spawn/edit and @spawn/examine --- evennia/commands/default/tests.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index a3be98984a..cbae47866d 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -372,6 +372,17 @@ class TestBuilding(CommandTest): self.call(building.CmdSpawn(), \ "'NO_EXIST'", "No prototype named 'NO_EXIST'") + # @span/edit + self.call( + building.CmdSpawn(), + '/edit', + 'spawn: Extra switch "/edit" ignored') + + # @span/examine + self.call( + '/examine', + building.CmdSpawn(), 'spawn: Extra switch "/examine" ignored') + class TestComms(CommandTest): @@ -446,4 +457,4 @@ class TestUnconnectedCommand(CommandTest): settings.SERVERNAME, datetime.datetime.fromtimestamp(gametime.SERVER_START_TIME).ctime(), SESSIONS.account_count(), utils.get_evennia_version()) - self.call(unloggedin.CmdUnconnectedInfo(), "", expected) \ No newline at end of file + self.call(unloggedin.CmdUnconnectedInfo(), "", expected) From b82e18e794bb5cf92a6f7ef2b1aa204f8bc8bc5e Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Fri, 19 Oct 2018 18:44:45 -0500 Subject: [PATCH 17/26] Baseline tests: assert current behavior (disregarding requirements) --- evennia/commands/default/tests.py | 36 +++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index 691e1e63cb..be47a06aac 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -460,16 +460,44 @@ class TestBuilding(CommandTest): # Test listing commands self.call(building.CmdSpawn(), "/list", "Key ") - # @span/edit + # @span/edit (missing prototype) self.call( building.CmdSpawn(), '/edit', - 'spawn: Extra switch "/edit" ignored') + '@spawn: Extra switch "/edit" ignored.|Usage: @spawn or {key: value, ...}\n (2 existing prototypes. Use /list to inspect)') + # assert 'Prototype wizard' in msg - # @span/examine + # @spawn/edit with valid prototype + # with self.assertRaises(AttributeError): self.call( + building.CmdSpawn(), + '/edit BALL', + '@spawn: Extra switch "/edit" ignored.|Spawned Ball(#13).') + + # @spawn/edit with invalid prototype + #`with self.assertRaises(AttributeError): + self.call( + building.CmdSpawn(), + '/edit NO_EXISTS', + '@spawn: Extra switch "/edit" ignored.|No prototype named \'NO_EXISTS\'.') + + # @spawn/examine (missing prototype) + self.call( + building.CmdSpawn(), '/examine', - building.CmdSpawn(), 'spawn: Extra switch "/examine" ignored') + '@spawn: Extra switch "/examine" ignored.|Usage: @spawn or {key: value, ...}\n (2 existing prototypes. Use /list to inspect)') + + # @spawn/examine with valid prototype + self.call( + building.CmdSpawn(), + '/examine BALL', + '@spawn: Extra switch "/examine" ignored.|Spawned Ball(#14).') + + # @spawn/examine with invalid prototype + self.call( + building.CmdSpawn(), + '/examine NO_EXISTS', + '@spawn: Extra switch "/examine" ignored.|No prototype named \'NO_EXISTS\'.') class TestComms(CommandTest): From 1a6d7a22adcb6b68de154e42f9756dc7b2669eac Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Fri, 19 Oct 2018 19:01:27 -0500 Subject: [PATCH 18/26] @spawn/edit is equivalent to @spawn/olc and @olc --- evennia/commands/default/building.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index f0ae108f00..ad613609ba 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -2856,7 +2856,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): key = "@spawn" aliases = ["olc"] - switch_options = ("noloc", "search", "list", "show", "save", "delete", "menu", "olc", "update") + switch_options = ("noloc", "search", "list", "show", "save", "delete", "menu", "olc", "update", "edit") locks = "cmd:perm(spawn) or perm(Builder)" help_category = "Building" @@ -2907,7 +2907,8 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): caller = self.caller - if self.cmdstring == "olc" or 'menu' in self.switches or 'olc' in self.switches: + if self.cmdstring == "olc" or 'menu' in self.switches \ + or 'olc' in self.switches or 'edit' in self.switches: # OLC menu mode prototype = None if self.lhs: From 387a2444a70adafbfa093a1b858668e1624b1b09 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Fri, 19 Oct 2018 19:16:02 -0500 Subject: [PATCH 19/26] Modify tests so they pass. Some new tests throw exceptions --- evennia/commands/default/tests.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index be47a06aac..0007a84e07 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -461,25 +461,25 @@ class TestBuilding(CommandTest): self.call(building.CmdSpawn(), "/list", "Key ") # @span/edit (missing prototype) - self.call( + msg = self.call( building.CmdSpawn(), - '/edit', - '@spawn: Extra switch "/edit" ignored.|Usage: @spawn or {key: value, ...}\n (2 existing prototypes. Use /list to inspect)') - # assert 'Prototype wizard' in msg + '/edit') + # '@spawn: Extra switch "/edit" ignored.|Usage: @spawn or {key: value, ...}\n (2 existing prototypes. Use /list to inspect)') + assert 'Prototype wizard' in msg # @spawn/edit with valid prototype - # with self.assertRaises(AttributeError): - self.call( - building.CmdSpawn(), - '/edit BALL', - '@spawn: Extra switch "/edit" ignored.|Spawned Ball(#13).') + with self.assertRaises(AttributeError): + self.call( + building.CmdSpawn(), + '/edit BALL', + '@spawn: Extra switch "/edit" ignored.|Spawned Ball(#13).') # @spawn/edit with invalid prototype - #`with self.assertRaises(AttributeError): - self.call( - building.CmdSpawn(), - '/edit NO_EXISTS', - '@spawn: Extra switch "/edit" ignored.|No prototype named \'NO_EXISTS\'.') + with self.assertRaises(AttributeError): + self.call( + building.CmdSpawn(), + '/edit NO_EXISTS', + '@spawn: Extra switch "/edit" ignored.|No prototype named \'NO_EXISTS\'.') # @spawn/examine (missing prototype) self.call( @@ -491,7 +491,8 @@ class TestBuilding(CommandTest): self.call( building.CmdSpawn(), '/examine BALL', - '@spawn: Extra switch "/examine" ignored.|Spawned Ball(#14).') + # '@spawn: Extra switch "/examine" ignored.|Spawned Ball(#14).') + '@spawn: Extra switch "/examine" ignored.|Spawned Ball(#13).') # @spawn/examine with invalid prototype self.call( From 15c940fba4da4af1999b2bbb781debb4a40b6891 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Sat, 20 Oct 2018 20:11:41 -0500 Subject: [PATCH 20/26] Handle '@spawn/edit ' (no prototype key given) brings OLC menu. Handle '@spawn/edit testball' brings OLC menu. Handle '@spawn/edit NO_EXISTS' that returns error --- evennia/commands/default/building.py | 6 +++++- evennia/commands/default/tests.py | 27 +++++++++++++++------------ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index ed6f6d273f..65fedaf5c2 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -2913,7 +2913,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): prototype = None if self.lhs: key = self.lhs - prototype = spawner.search_prototype(key=key, return_meta=True) + prototype = protlib.search_prototype(key=key) if len(prototype) > 1: caller.msg("More than one match for {}:\n{}".format( key, "\n".join(proto.get('prototype_key', '') for proto in prototype))) @@ -2921,6 +2921,10 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): elif prototype: # one match prototype = prototype[0] + else: + # no match + caller.msg("No prototype '{}' was found.".format(key)) + return olc_menus.start_olc(caller, session=self.session, prototype=prototype) return diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index 59fdc7db69..1cbb81b090 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -479,25 +479,28 @@ class TestBuilding(CommandTest): self.call(building.CmdSpawn(), "/list", "Key ") # @span/edit (missing prototype) + # brings up olc menu msg = self.call( building.CmdSpawn(), '/edit') - # '@spawn: Extra switch "/edit" ignored.|Usage: @spawn or {key: value, ...}\n (2 existing prototypes. Use /list to inspect)') - assert 'Prototype wizard' in msg + assert msg.startswith('______________________________________________________________________________\n\n --- Prototype wizard --- \n\n') # @spawn/edit with valid prototype - with self.assertRaises(AttributeError): - self.call( - building.CmdSpawn(), - '/edit BALL', - '@spawn: Extra switch "/edit" ignored.|Spawned Ball(#13).') + self.call( + building.CmdSpawn(), + '/edit testball') + # TODO: OLC menu comes up but it gives no + # indication of testball prototype being + # edited ... Is this correct? + # On top of OCL being shown, msg is preceded + # by Room(#1)... + assert 'Prototype wizard' in msg # @spawn/edit with invalid prototype - with self.assertRaises(AttributeError): - self.call( - building.CmdSpawn(), - '/edit NO_EXISTS', - '@spawn: Extra switch "/edit" ignored.|No prototype named \'NO_EXISTS\'.') + msg = self.call( + building.CmdSpawn(), + '/edit NO_EXISTS', + "No prototype 'NO_EXISTS' was found.") # @spawn/examine (missing prototype) self.call( From 3893cfca092a37da67508f19df48b3fa6404098e Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Sat, 20 Oct 2018 20:12:55 -0500 Subject: [PATCH 21/26] code cleanup --- evennia/commands/default/tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index 1cbb81b090..d8657a7113 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -512,7 +512,6 @@ class TestBuilding(CommandTest): self.call( building.CmdSpawn(), '/examine BALL', - # '@spawn: Extra switch "/examine" ignored.|Spawned Ball(#14).') '@spawn: Extra switch "/examine" ignored.|Spawned Ball(#13).') # @spawn/examine with invalid prototype From 4c6205259b8762d864906b3643ea8cbf94132369 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Sat, 20 Oct 2018 20:22:11 -0500 Subject: [PATCH 22/26] Test @spawn/edit BALL (synonym) --- evennia/commands/default/tests.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index d8657a7113..8dce5ccc46 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -486,7 +486,7 @@ class TestBuilding(CommandTest): assert msg.startswith('______________________________________________________________________________\n\n --- Prototype wizard --- \n\n') # @spawn/edit with valid prototype - self.call( + msg = self.call( building.CmdSpawn(), '/edit testball') # TODO: OLC menu comes up but it gives no @@ -496,6 +496,12 @@ class TestBuilding(CommandTest): # by Room(#1)... assert 'Prototype wizard' in msg + # @spawn/edit with valid prototype (synomym) + msg = self.call( + building.CmdSpawn(), + '/edit BALL') + assert 'Prototype wizard' in msg + # @spawn/edit with invalid prototype msg = self.call( building.CmdSpawn(), From de9546b70ac56c5f787a4659e1c00eebe1412e2d Mon Sep 17 00:00:00 2001 From: Brenden Tuck Date: Sun, 21 Oct 2018 10:03:54 -0400 Subject: [PATCH 23/26] Fix #1686 cursor placement issues --- .../web/webclient/static/webclient/js/plugins/history.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/evennia/web/webclient/static/webclient/js/plugins/history.js b/evennia/web/webclient/static/webclient/js/plugins/history.js index c33dbcabf9..60b1a2b163 100644 --- a/evennia/web/webclient/static/webclient/js/plugins/history.js +++ b/evennia/web/webclient/static/webclient/js/plugins/history.js @@ -69,8 +69,12 @@ let history_plugin = (function () { } if (history_entry !== null) { - // Doing a history navigation; replace the text in the input. - inputfield.val(history_entry); + // Performing a history navigation + // replace the text in the input and move the cursor to the end of the new value + inputfield.val(''); + inputfield.blur().focus().val(history_entry); + event.preventDefault(); + return true; } return false; From 9d800aae7c81b6baddb361fd7cd7b040049d97d0 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Sun, 21 Oct 2018 13:43:36 -0500 Subject: [PATCH 24/26] Harden test '@spawn/edit ' by asserting that olc_menu has been loaded with valid-prototype.UX Enhancement to OLC menu. Underneath the title, display 'Editing: key(prototype_key)' of the prototype being edited. If none, show blank line --- evennia/commands/default/tests.py | 20 +++++++++++++------- evennia/prototypes/menus.py | 12 ++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index 8dce5ccc46..f285cc9614 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -478,29 +478,33 @@ class TestBuilding(CommandTest): # Test listing commands self.call(building.CmdSpawn(), "/list", "Key ") - # @span/edit (missing prototype) + # @spawn/edit (missing prototype) # brings up olc menu msg = self.call( building.CmdSpawn(), '/edit') - assert msg.startswith('______________________________________________________________________________\n\n --- Prototype wizard --- \n\n') + assert 'Prototype wizard' in msg # @spawn/edit with valid prototype + # brings up olc menu loaded with prototype msg = self.call( building.CmdSpawn(), '/edit testball') - # TODO: OLC menu comes up but it gives no - # indication of testball prototype being - # edited ... Is this correct? - # On top of OCL being shown, msg is preceded - # by Room(#1)... assert 'Prototype wizard' in msg + assert hasattr(self.char1.ndb._menutree, "olc_prototype") + assert dict == type(self.char1.ndb._menutree.olc_prototype) \ + and 'prototype_key' in self.char1.ndb._menutree.olc_prototype \ + and 'key' in self.char1.ndb._menutree.olc_prototype \ + and 'testball' == self.char1.ndb._menutree.olc_prototype['prototype_key'] \ + and 'Ball' == self.char1.ndb._menutree.olc_prototype['key'] + assert 'Ball' in msg and 'testball' in msg # @spawn/edit with valid prototype (synomym) msg = self.call( building.CmdSpawn(), '/edit BALL') assert 'Prototype wizard' in msg + assert 'Ball' in msg and 'testball' in msg # @spawn/edit with invalid prototype msg = self.call( @@ -518,6 +522,8 @@ class TestBuilding(CommandTest): self.call( building.CmdSpawn(), '/examine BALL', + # FIXME: should this print the existing prototype + # instead of spawning it? '@spawn: Extra switch "/examine" ignored.|Spawned Ball(#13).') # @spawn/examine with invalid prototype diff --git a/evennia/prototypes/menus.py b/evennia/prototypes/menus.py index 9605ea1a8f..c10f32429f 100644 --- a/evennia/prototypes/menus.py +++ b/evennia/prototypes/menus.py @@ -562,6 +562,7 @@ def node_index(caller): text = """ |c --- Prototype wizard --- |n + %s A |cprototype|n is a 'template' for |wspawning|n an in-game entity. A field of the prototype can either be hard-coded, left empty or scripted using |w$protfuncs|n - for example to @@ -599,6 +600,17 @@ def node_index(caller): {pfuncs} """.format(pfuncs=_format_protfuncs()) + # If a prototype is being edited, show its key and + # prototype_key under the title + loaded_prototype = '' + if 'prototype_key' in prototype \ + or 'key' in prototype: + loaded_prototype = ' --- Editing: |y{}({})|n --- '.format( + prototype.get('key', ''), + prototype.get('prototype_key', '') + ) + text = text % (loaded_prototype) + text = (text, helptxt) options = [] From 79a64ce3d256f7c03dfac0aa46928a007945f520 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Sun, 21 Oct 2018 14:15:34 -0500 Subject: [PATCH 25/26] Add 'examine' as valid switch alias for 'show' with its corresponding tests --- evennia/commands/default/building.py | 2 +- evennia/commands/default/tests.py | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index 65fedaf5c2..503a9c15e9 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -2856,7 +2856,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): key = "@spawn" aliases = ["olc"] - switch_options = ("noloc", "search", "list", "show", "save", "delete", "menu", "olc", "update", "edit") + switch_options = ("noloc", "search", "list", "show", "examine", "save", "delete", "menu", "olc", "update", "edit") locks = "cmd:perm(spawn) or perm(Builder)" help_category = "Building" diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index f285cc9614..7b2c78951b 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -513,24 +513,25 @@ class TestBuilding(CommandTest): "No prototype 'NO_EXISTS' was found.") # @spawn/examine (missing prototype) - self.call( + # lists all prototypes that exist + msg = self.call( building.CmdSpawn(), - '/examine', - '@spawn: Extra switch "/examine" ignored.|Usage: @spawn or {key: value, ...}\n (2 existing prototypes. Use /list to inspect)') + '/examine') + assert 'testball' in msg and 'testprot' in msg # @spawn/examine with valid prototype - self.call( + # prints the prototype + msg = self.call( building.CmdSpawn(), - '/examine BALL', - # FIXME: should this print the existing prototype - # instead of spawning it? - '@spawn: Extra switch "/examine" ignored.|Spawned Ball(#13).') + '/examine BALL') + assert 'Ball' in msg and 'testball' in msg # @spawn/examine with invalid prototype + # shows error self.call( building.CmdSpawn(), '/examine NO_EXISTS', - '@spawn: Extra switch "/examine" ignored.|No prototype named \'NO_EXISTS\'.') + "No prototype 'NO_EXISTS' was found.") class TestComms(CommandTest): From ed66563bdc01835cf46317af1928640fce2d3fe4 Mon Sep 17 00:00:00 2001 From: Griatch Date: Tue, 23 Oct 2018 15:17:59 +0200 Subject: [PATCH 26/26] Fix lagging pillow requirement for Windows. Resolves #1702. --- win_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win_requirements.txt b/win_requirements.txt index 5e23f6fe67..02984ec6ca 100644 --- a/win_requirements.txt +++ b/win_requirements.txt @@ -5,7 +5,7 @@ pypiwin32 django > 1.11, < 2.0 twisted >= 18.0.0, < 19.0.0 -pillow == 2.9.0 +pillow == 5.2.0 pytz future >= 0.15.2 django-sekizai