Change how cmdset options are merged by priority - this is now a straight priority order, where the option from the higher prio goes. Also add unit tests for cmdset mergers.
This commit is contained in:
parent
300758b2dd
commit
40e1c67f88
3 changed files with 212 additions and 38 deletions
|
|
@ -329,7 +329,7 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype):
|
||||||
prio = cmdset.priority
|
prio = cmdset.priority
|
||||||
if prio in tempmergers:
|
if prio in tempmergers:
|
||||||
# merge same-prio cmdset together separately
|
# merge same-prio cmdset together separately
|
||||||
tempmergers[prio] = yield cmdset + tempmergers[prio]
|
tempmergers[prio] = yield tempmergers[prio] + cmdset
|
||||||
else:
|
else:
|
||||||
tempmergers[prio] = cmdset
|
tempmergers[prio] = cmdset
|
||||||
|
|
||||||
|
|
@ -339,7 +339,7 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype):
|
||||||
# Merge all command sets into one, beginning with the lowest-prio one
|
# Merge all command sets into one, beginning with the lowest-prio one
|
||||||
cmdset = cmdsets[0]
|
cmdset = cmdsets[0]
|
||||||
for merging_cmdset in cmdsets[1:]:
|
for merging_cmdset in cmdsets[1:]:
|
||||||
cmdset = yield merging_cmdset + cmdset
|
cmdset = yield cmdset + merging_cmdset
|
||||||
# store the full sets for diagnosis
|
# store the full sets for diagnosis
|
||||||
cmdset.merged_from = cmdsets
|
cmdset.merged_from = cmdsets
|
||||||
# cache
|
# cache
|
||||||
|
|
|
||||||
|
|
@ -351,11 +351,11 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)):
|
||||||
self._contains_cache[othercmd] = ret
|
self._contains_cache[othercmd] = ret
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def __add__(self, cmdset_b):
|
def __add__(self, cmdset_a):
|
||||||
"""
|
"""
|
||||||
Merge this cmdset (A) with another cmdset (B) using the + operator,
|
Merge this cmdset (B) with another cmdset (A) using the + operator,
|
||||||
|
|
||||||
C = A + B
|
C = B + A
|
||||||
|
|
||||||
Here, we (by convention) say that 'A is merged onto B to form
|
Here, we (by convention) say that 'A is merged onto B to form
|
||||||
C'. The actual merge operation used in the 'addition' depends
|
C'. The actual merge operation used in the 'addition' depends
|
||||||
|
|
@ -367,36 +367,45 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# It's okay to merge with None
|
# It's okay to merge with None
|
||||||
if not cmdset_b:
|
if not cmdset_a:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
sys_commands_a = self.get_system_cmds()
|
sys_commands_a = cmdset_a.get_system_cmds()
|
||||||
sys_commands_b = cmdset_b.get_system_cmds()
|
sys_commands_b = self.get_system_cmds()
|
||||||
|
|
||||||
if self.priority >= cmdset_b.priority:
|
if self.priority <= cmdset_a.priority:
|
||||||
# A higher or equal priority than B
|
# A higher or equal priority to B
|
||||||
|
|
||||||
# preserve system __commands
|
# preserve system __commands
|
||||||
sys_commands = sys_commands_a + [cmd for cmd in sys_commands_b
|
sys_commands = sys_commands_a + [cmd for cmd in sys_commands_b
|
||||||
if cmd not in sys_commands_a]
|
if cmd not in sys_commands_a]
|
||||||
|
|
||||||
mergetype = self.key_mergetypes.get(cmdset_b.key, self.mergetype)
|
mergetype = cmdset_a.key_mergetypes.get(self.key, cmdset_a.mergetype)
|
||||||
if mergetype == "Intersect":
|
if mergetype == "Intersect":
|
||||||
cmdset_c = self._intersect(self, cmdset_b)
|
cmdset_c = self._intersect(cmdset_a, self)
|
||||||
elif mergetype == "Replace":
|
elif mergetype == "Replace":
|
||||||
cmdset_c = self._replace(self, cmdset_b)
|
cmdset_c = self._replace(cmdset_a, self)
|
||||||
elif mergetype == "Remove":
|
elif mergetype == "Remove":
|
||||||
cmdset_c = self._remove(self, cmdset_b)
|
cmdset_c = self._remove(cmdset_a, self)
|
||||||
else: # Union
|
else: # Union
|
||||||
cmdset_c = self._union(self, cmdset_b)
|
cmdset_c = self._union(cmdset_a, self)
|
||||||
# update or pass-through
|
|
||||||
cmdset_c.no_channels = cmdset_b.no_channels if self.no_channels is None else self.no_channels
|
if self.priority == cmdset_a.priority:
|
||||||
cmdset_c.no_exits = cmdset_b.no_exits if self.no_exits is None else self.no_exits
|
# same prio - pass through if changed
|
||||||
cmdset_c.no_objs = cmdset_b.no_objs if self.no_objs is None else self.no_objs
|
cmdset_c.no_channels = self.no_channels if cmdset_a.no_channels is None else cmdset_a.no_channels
|
||||||
cmdset_c.duplicates = cmdset_b.duplicates if self.duplicates is None else self.duplicates
|
cmdset_c.no_exits = self.no_exits if cmdset_a.no_exits is None else cmdset_a.no_exits
|
||||||
|
cmdset_c.no_objs = self.no_objs if cmdset_a.no_objs is None else cmdset_a.no_objs
|
||||||
|
cmdset_c.duplicates = self.duplicates if cmdset_a.duplicates is None else cmdset_a.duplicates
|
||||||
|
else:
|
||||||
|
# pass through the options of the higher prio set
|
||||||
|
cmdset_c.no_channels = cmdset_a.no_channels
|
||||||
|
cmdset_c.no_exits = cmdset_a.no_exits
|
||||||
|
cmdset_c.no_objs = cmdset_a.no_objs
|
||||||
|
cmdset_c.duplicates = cmdset_a.duplicates
|
||||||
|
|
||||||
if self.key.startswith("_"):
|
if self.key.startswith("_"):
|
||||||
# don't rename new output if the merge set's name starts with _
|
# don't rename new output if the merge set's name starts with _
|
||||||
cmdset_c.key = cmdset_b.key
|
cmdset_c.key = cmdset_a.key
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# B higher priority than A
|
# B higher priority than A
|
||||||
|
|
@ -405,32 +414,33 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)):
|
||||||
sys_commands = sys_commands_b + [cmd for cmd in sys_commands_a
|
sys_commands = sys_commands_b + [cmd for cmd in sys_commands_a
|
||||||
if cmd not in sys_commands_b]
|
if cmd not in sys_commands_b]
|
||||||
|
|
||||||
mergetype = cmdset_b.key_mergetypes.get(self.key, cmdset_b.mergetype)
|
mergetype = self.key_mergetypes.get(cmdset_a.key, self.mergetype)
|
||||||
if mergetype == "Intersect":
|
if mergetype == "Intersect":
|
||||||
cmdset_c = self._intersect(cmdset_b, self)
|
cmdset_c = self._intersect(self, cmdset_a)
|
||||||
elif mergetype == "Replace":
|
elif mergetype == "Replace":
|
||||||
cmdset_c = self._replace(cmdset_b, self)
|
cmdset_c = self._replace(self, cmdset_a)
|
||||||
elif mergetype == "Remove":
|
elif mergetype == "Remove":
|
||||||
cmdset_c = self._remove(cmdset_b, self)
|
cmdset_c = self._remove(self, cmdset_a)
|
||||||
else: # Union
|
else: # Union
|
||||||
cmdset_c = self._union(cmdset_b, self)
|
cmdset_c = self._union(self, cmdset_a)
|
||||||
cmdset_c.no_channels = cmdset_b.no_channels
|
|
||||||
cmdset_c.no_exits = cmdset_b.no_exits
|
cmdset_c.no_channels = self.no_channels
|
||||||
cmdset_c.no_objs = cmdset_b.no_objs
|
cmdset_c.no_exits = self.no_exits
|
||||||
|
cmdset_c.no_objs = self.no_objs
|
||||||
|
cmdset_c.duplicates = self.duplicates
|
||||||
|
|
||||||
# update or pass-through
|
# update or pass-through
|
||||||
cmdset_c.no_channels = self.no_channels if self.no_channels is None else cmdset_b.no_channels
|
if self.key.startswith("_"):
|
||||||
cmdset_c.no_exits = self.no_exits if self.no_exits is None else cmdset_b.no_exits
|
|
||||||
cmdset_c.no_objs = self.no_objs if self.no_objs is None else cmdset_b.no_objs
|
|
||||||
cmdset_c.duplicates = self.duplicates if self.duplicates is None else cmdset_b.duplicates
|
|
||||||
if cmdset_b.key.startswith("_"):
|
|
||||||
# don't rename new output if the merge set's name starts with _
|
# don't rename new output if the merge set's name starts with _
|
||||||
cmdset_c.key = self.key
|
cmdset_c.key = cmdset_a.self.key
|
||||||
|
|
||||||
# we store actual_mergetype since key_mergetypes
|
# we store actual_mergetype since key_mergetypes
|
||||||
# might be different from the main mergetype.
|
# might be different from the main mergetype.
|
||||||
# This is used for diagnosis.
|
# This is used for diagnosis.
|
||||||
cmdset_c.actual_mergetype = mergetype
|
cmdset_c.actual_mergetype = mergetype
|
||||||
|
|
||||||
|
#print "__add__ for %s (prio %i) called with %s (prio %i)." % (self.key, self.priority, cmdset_a.key, cmdset_a.priority)
|
||||||
|
|
||||||
# return the system commands to the cmdset
|
# return the system commands to the cmdset
|
||||||
cmdset_c.add(sys_commands)
|
cmdset_c.add(sys_commands)
|
||||||
return cmdset_c
|
return cmdset_c
|
||||||
|
|
|
||||||
164
evennia/commands/tests.py
Normal file
164
evennia/commands/tests.py
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
"""
|
||||||
|
Unit testing for the Command system itself.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from evennia.utils.test_resources import EvenniaTest, SESSIONS
|
||||||
|
from evennia.commands.cmdset import CmdSet
|
||||||
|
from evennia.commands.command import Command
|
||||||
|
|
||||||
|
|
||||||
|
# Testing-command sets
|
||||||
|
|
||||||
|
class _CmdA(Command):
|
||||||
|
key = "A"
|
||||||
|
def __init__(self, cmdset, *args, **kwargs):
|
||||||
|
super(_CmdA, self).__init__(*args, **kwargs)
|
||||||
|
self.from_cmdset = cmdset
|
||||||
|
class _CmdB(Command):
|
||||||
|
key = "B"
|
||||||
|
def __init__(self, cmdset, *args, **kwargs):
|
||||||
|
super(_CmdB, self).__init__(*args, **kwargs)
|
||||||
|
self.from_cmdset = cmdset
|
||||||
|
class _CmdC(Command):
|
||||||
|
key = "C"
|
||||||
|
def __init__(self, cmdset, *args, **kwargs):
|
||||||
|
super(_CmdC, self).__init__(*args, **kwargs)
|
||||||
|
self.from_cmdset = cmdset
|
||||||
|
class _CmdD(Command):
|
||||||
|
key = "D"
|
||||||
|
def __init__(self, cmdset, *args, **kwargs):
|
||||||
|
super(_CmdD, self).__init__(*args, **kwargs)
|
||||||
|
self.from_cmdset = cmdset
|
||||||
|
|
||||||
|
class _CmdSetA(CmdSet):
|
||||||
|
key = "A"
|
||||||
|
def at_cmdset_creation(self):
|
||||||
|
self.add(_CmdA("A"))
|
||||||
|
self.add(_CmdB("A"))
|
||||||
|
self.add(_CmdC("A"))
|
||||||
|
self.add(_CmdD("A"))
|
||||||
|
class _CmdSetB(CmdSet):
|
||||||
|
key = "B"
|
||||||
|
def at_cmdset_creation(self):
|
||||||
|
self.add(_CmdA("B"))
|
||||||
|
self.add(_CmdB("B"))
|
||||||
|
self.add(_CmdC("B"))
|
||||||
|
class _CmdSetC(CmdSet):
|
||||||
|
key = "C"
|
||||||
|
def at_cmdset_creation(self):
|
||||||
|
self.add(_CmdA("C"))
|
||||||
|
self.add(_CmdB("C"))
|
||||||
|
class _CmdSetD(CmdSet):
|
||||||
|
key = "D"
|
||||||
|
def at_cmdset_creation(self):
|
||||||
|
self.add(_CmdA("D"))
|
||||||
|
self.add(_CmdB("D"))
|
||||||
|
self.add(_CmdC("D"))
|
||||||
|
self.add(_CmdD("D"))
|
||||||
|
|
||||||
|
# testing Command Sets
|
||||||
|
|
||||||
|
class TestCmdSetMergers(EvenniaTest):
|
||||||
|
"Test merging of cmdsets"
|
||||||
|
def setUp(self):
|
||||||
|
super(TestCmdSetMergers, self).setUp()
|
||||||
|
self.cmdset_a = _CmdSetA()
|
||||||
|
self.cmdset_b = _CmdSetB()
|
||||||
|
self.cmdset_c = _CmdSetC()
|
||||||
|
self.cmdset_d = _CmdSetD()
|
||||||
|
|
||||||
|
def test_order(self):
|
||||||
|
"Merge in reverse- and forward orders, same priorities"
|
||||||
|
a, b, c, d = self.cmdset_a, self.cmdset_b, self.cmdset_c, self.cmdset_d
|
||||||
|
cmdset_f = d + c + b + a # merge in reverse order of priority
|
||||||
|
self.assertEqual(cmdset_f.priority, 0)
|
||||||
|
self.assertEqual(cmdset_f.mergetype, "Union")
|
||||||
|
self.assertEqual(len(cmdset_f.commands), 4)
|
||||||
|
self.assertTrue(all(True for cmd in cmdset_f.commands if cmd.from_cmdset == "A"))
|
||||||
|
cmdset_f = a + b + c + d # merge in order of priority
|
||||||
|
self.assertEqual(cmdset_f.priority, 0)
|
||||||
|
self.assertEqual(cmdset_f.mergetype, "Union")
|
||||||
|
self.assertEqual(len(cmdset_f.commands), 4) # duplicates setting from A transfers
|
||||||
|
self.assertTrue(all(True for cmd in cmdset_f.commands if cmd.from_cmdset == "D"))
|
||||||
|
|
||||||
|
def test_priority_order(self):
|
||||||
|
"Merge in reverse- and forward order with well-defined prioritities"
|
||||||
|
a, b, c, d = self.cmdset_a, self.cmdset_b, self.cmdset_c, self.cmdset_d
|
||||||
|
a.priority = 2
|
||||||
|
b.priority = 1
|
||||||
|
c.priority = 0
|
||||||
|
d.priority = -1
|
||||||
|
cmdset_f = d + c + b + a # merge in reverse order of priority
|
||||||
|
self.assertEqual(cmdset_f.priority, 2)
|
||||||
|
self.assertEqual(cmdset_f.mergetype, "Union")
|
||||||
|
self.assertEqual(len(cmdset_f.commands), 4)
|
||||||
|
self.assertTrue(all(True for cmd in cmdset_f.commands if cmd.from_cmdset == "A"))
|
||||||
|
cmdset_f = a + b + c + d # merge in order of priority
|
||||||
|
self.assertEqual(cmdset_f.priority, 2)
|
||||||
|
self.assertEqual(cmdset_f.mergetype, "Union")
|
||||||
|
self.assertEqual(len(cmdset_f.commands), 4)
|
||||||
|
self.assertTrue(all(True for cmd in cmdset_f.commands if cmd.from_cmdset == "A"))
|
||||||
|
|
||||||
|
def test_option_transfer(self):
|
||||||
|
"Test transfer of cmdset options"
|
||||||
|
a, b, c, d = self.cmdset_a, self.cmdset_b, self.cmdset_c, self.cmdset_d
|
||||||
|
a.no_exits = True
|
||||||
|
a.no_objs = True
|
||||||
|
a.no_channels = True
|
||||||
|
a.duplicates = True
|
||||||
|
cmdset_f = d + c + b + a # reverse, same-prio
|
||||||
|
self.assertTrue(cmdset_f.no_exits)
|
||||||
|
self.assertTrue(cmdset_f.no_objs)
|
||||||
|
self.assertTrue(cmdset_f.no_channels)
|
||||||
|
self.assertTrue(cmdset_f.duplicates)
|
||||||
|
self.assertEqual(len(cmdset_f.commands), 8)
|
||||||
|
cmdset_f = a + b + c + d # forward, same-prio
|
||||||
|
self.assertTrue(cmdset_f.no_exits)
|
||||||
|
self.assertTrue(cmdset_f.no_objs)
|
||||||
|
self.assertTrue(cmdset_f.no_channels)
|
||||||
|
self.assertTrue(cmdset_f.duplicates)
|
||||||
|
self.assertEqual(len(cmdset_f.commands), 4)
|
||||||
|
a.priority = 2
|
||||||
|
b.priority = 1
|
||||||
|
c.priority = 0
|
||||||
|
d.priority = -1
|
||||||
|
cmdset_f = d + c + b + a # reverse, A top priority
|
||||||
|
self.assertTrue(cmdset_f.no_exits)
|
||||||
|
self.assertTrue(cmdset_f.no_objs)
|
||||||
|
self.assertTrue(cmdset_f.no_channels)
|
||||||
|
self.assertTrue(cmdset_f.duplicates)
|
||||||
|
self.assertEqual(len(cmdset_f.commands), 4)
|
||||||
|
cmdset_f = a + b + c + d # forward, A top priority
|
||||||
|
self.assertTrue(cmdset_f.no_exits)
|
||||||
|
self.assertTrue(cmdset_f.no_objs)
|
||||||
|
self.assertTrue(cmdset_f.no_channels)
|
||||||
|
self.assertTrue(cmdset_f.duplicates)
|
||||||
|
self.assertEqual(len(cmdset_f.commands), 4)
|
||||||
|
a.priority = -1
|
||||||
|
b.priority = 0
|
||||||
|
c.priority = 1
|
||||||
|
d.priority = 2
|
||||||
|
cmdset_f = d + c + b + a # reverse, A low prio
|
||||||
|
self.assertFalse(cmdset_f.no_exits)
|
||||||
|
self.assertFalse(cmdset_f.no_objs)
|
||||||
|
self.assertFalse(cmdset_f.no_channels)
|
||||||
|
self.assertFalse(cmdset_f.duplicates)
|
||||||
|
self.assertEqual(len(cmdset_f.commands), 4)
|
||||||
|
cmdset_f = a + b + c + d # forward, A low prio
|
||||||
|
self.assertFalse(cmdset_f.no_exits)
|
||||||
|
self.assertFalse(cmdset_f.no_objs)
|
||||||
|
self.assertFalse(cmdset_f.no_channels)
|
||||||
|
self.assertFalse(cmdset_f.duplicates)
|
||||||
|
self.assertEqual(len(cmdset_f.commands), 4)
|
||||||
|
a.priority = 0
|
||||||
|
b.priority = 0
|
||||||
|
c.priority = 0
|
||||||
|
d.priority = 0
|
||||||
|
c.duplicates = True
|
||||||
|
cmdset_f = d + b + c + a # two last mergers duplicates=True
|
||||||
|
self.assertEqual(len(cmdset_f.commands), 10)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue