Add testing framework for EvMenu. Implements #1484

This commit is contained in:
Griatch 2017-10-28 22:33:58 +02:00
parent b6b112b70a
commit d05495cc52
2 changed files with 205 additions and 8 deletions

View file

@ -429,8 +429,13 @@ class EvMenu(object):
self.nodetext = None self.nodetext = None
self.helptext = None self.helptext = None
self.options = None self.options = None
self.nodename = None
self.node_kwargs = {} self.node_kwargs = {}
# used for testing
self.test_options = {}
self.test_nodetext = ""
# assign kwargs as initialization vars on ourselves. # assign kwargs as initialization vars on ourselves.
if set(("_startnode", "_menutree", "_session", "_persistent", if set(("_startnode", "_menutree", "_session", "_persistent",
"cmd_on_exit", "default", "nodetext", "helptext", "cmd_on_exit", "default", "nodetext", "helptext",
@ -605,6 +610,11 @@ class EvMenu(object):
except Exception: except Exception:
self.caller.msg(_ERR_GENERAL.format(nodename=nodename), session=self._session) self.caller.msg(_ERR_GENERAL.format(nodename=nodename), session=self._session)
raise raise
# store options to make them easier to test
self.test_options = options
self.test_nodetext = nodetext
return nodetext, options return nodetext, options
def run_exec(self, nodename, raw_string, **kwargs): def run_exec(self, nodename, raw_string, **kwargs):
@ -750,6 +760,8 @@ class EvMenu(object):
(goto, goto_kwargs, execute, exec_kwargs) (goto, goto_kwargs, execute, exec_kwargs)
self.nodetext = self._format_node(nodetext, display_options) self.nodetext = self._format_node(nodetext, display_options)
self.node_kwargs = kwargs
self.nodename = nodename
# handle the helptext # handle the helptext
if helptext: if helptext:
@ -815,7 +827,7 @@ class EvMenu(object):
should also report errors directly to the user. should also report errors directly to the user.
""" """
cmd = raw_string.strip().lower() cmd = strip_ansi(raw_string.strip().lower())
if cmd in self.options: if cmd in self.options:
# this will take precedence over the default commands # this will take precedence over the default commands
@ -1124,7 +1136,7 @@ def test_start_node(caller):
def test_look_node(caller): def test_look_node(caller):
text = "" text = "This is a custom look location!"
options = {"key": ("|yL|nook", "l"), options = {"key": ("|yL|nook", "l"),
"desc": "Go back to the previous menu.", "desc": "Go back to the previous menu.",
"goto": "test_start_node"} "goto": "test_start_node"}

View file

@ -5,20 +5,205 @@ TODO: This need expansion.
""" """
import copy
from django.test import TestCase from django.test import TestCase
from evennia.utils import evmenu from evennia.utils import evmenu
from mock import Mock from evennia.utils import ansi
from mock import MagicMock
class TestEvMenu(TestCase): class TestEvMenu(TestCase):
"Run the EvMenu testing." "Run the EvMenu testing."
menutree = {} # can also be the path to the menu tree
startnode = "start"
cmdset_mergetype = "Replace"
cmdset_priority = 1
auto_quit = True
auto_look = True
auto_help = True
cmd_on_exit = "look"
persistent = False
startnode_input = ""
kwargs = {}
# this is compared against the full tree structure generated
expected_tree = []
# this allows for verifying that a given node returns a given text. The
# text is compared with .startswith, so the entire text need not be matched.
expected_node_texts = {}
# just check the number of options from each node
expected_node_options_count = {}
# check the actual options
expected_node_options = {}
# set this to print the traversal as it happens (debugging)
debug_output = False
def _debug_output(self, indent, msg):
if self.debug_output:
print(" " * indent + msg)
def _test_menutree(self, menu):
"""
This is a automatic tester of the menu tree by recursively progressing through the
structure.
"""
def _depth_first(menu, tree, visited, indent):
# we are in a given node here
nodename = menu.nodename
options = menu.test_options
if isinstance(options, dict):
options = (options, )
# run validation tests for this node
compare_text = self.expected_node_texts.get(nodename, None)
if compare_text is not None:
compare_text = ansi.strip_ansi(compare_text.strip())
node_text = menu.test_nodetext
self.assertIsNotNone(
bool(node_text),
"node: {}: node-text is None, which was not expected.".format(nodename))
node_text = ansi.strip_ansi(node_text.strip())
self.assertTrue(
node_text.startswith(compare_text),
"\nnode \"{}\':\nOutput:\n{}\n\nExpected (startswith):\n{}".format(
nodename, node_text, compare_text))
compare_options_count = self.expected_node_options_count.get(nodename, None)
if compare_options_count is not None:
self.assertEqual(
len(options), compare_options_count,
"Not the right number of options returned from node {}.".format(nodename))
compare_options = self.expected_node_options.get(nodename, None)
if compare_options:
self.assertEqual(
options, compare_options,
"Options returned from node {} does not match.".format(nodename))
self._debug_output(indent, "*{}".format(nodename))
subtree = []
if not options:
# an end node
if nodename not in visited:
visited.append(nodename)
subtree = nodename
else:
for inum, optdict in enumerate(options):
key, desc, execute, goto = optdict.get("key", ""), optdict.get("desc", None),\
optdict.get("exec", None), optdict.get("goto", None)
# prepare the key to pass to the menu
if isinstance(key, (tuple, list)) and len(key) > 1:
key = key[0]
if key == "_default":
key = "test raw input"
if not key:
key = str(inum + 1)
backup_menu = copy.copy(menu)
# step the menu
menu.parse_input(key)
# from here on we are likely in a different node
nodename = menu.nodename
if menu.close_menu.called:
# this was an end node
self._debug_output(indent, " .. menu exited! Back to previous node.")
menu = backup_menu
menu.close_menu = MagicMock()
visited.append(nodename)
subtree.append(nodename)
elif nodename not in visited:
visited.append(nodename)
subtree.append(nodename)
_depth_first(menu, subtree, visited, indent + 2)
#self._debug_output(indent, " -> arrived at {}".format(nodename))
else:
subtree.append(nodename)
#self._debug_output( indent, " -> arrived at {} (circular call)".format(nodename))
self._debug_output(indent, "-- {} ({}) -> {}".format(key, desc, goto))
if subtree:
tree.append(subtree)
# the start node has already fired at this point
visited_nodes = [menu.nodename]
traversal_tree = [menu.nodename]
_depth_first(menu, traversal_tree, visited_nodes, 1)
self.assertGreaterEqual(len(menu._menutree), len(visited_nodes))
self.assertEqual(traversal_tree, self.expected_tree)
def setUp(self): def setUp(self):
self.caller = Mock() self.menu = None
self.caller.msg = Mock() if self.menutree:
self.menu = evmenu.EvMenu(self.caller, "evennia.utils.evmenu", startnode="test_start_node", self.caller = MagicMock()
persistent=True, cmdset_mergetype="Replace", testval="val", self.caller.key = "Test"
testval2="val2") self.caller2 = MagicMock()
self.caller2.key = "Test"
self.caller.msg = MagicMock()
self.caller2.msg = MagicMock()
self.session = MagicMock()
self.session2 = MagicMock()
self.menu = evmenu.EvMenu(self.caller, self.menutree, startnode=self.startnode,
cmdset_mergetype=self.cmdset_mergetype,
cmdset_priority=self.cmdset_priority,
auto_quit=self.auto_quit, auto_look=self.auto_look,
auto_help=self.auto_help,
cmd_on_exit=self.cmd_on_exit, persistent=False,
startnode_input=self.startnode_input, session=self.session,
**self.kwargs)
# persistent version
self.pmenu = evmenu.EvMenu(self.caller2, self.menutree, startnode=self.startnode,
cmdset_mergetype=self.cmdset_mergetype,
cmdset_priority=self.cmdset_priority,
auto_quit=self.auto_quit, auto_look=self.auto_look,
auto_help=self.auto_help,
cmd_on_exit=self.cmd_on_exit, persistent=True,
startnode_input=self.startnode_input, session=self.session2,
**self.kwargs)
self.menu.close_menu = MagicMock()
self.pmenu.close_menu = MagicMock()
def test_menu_structure(self):
if self.menu:
self._test_menutree(self.menu)
self._test_menutree(self.pmenu)
class TestEvMenuExample(TestEvMenu):
menutree = "evennia.utils.evmenu"
startnode = "test_start_node"
kwargs = {"testval": "val", "testval2": "val2"}
debug_output = False
expected_node_texts = {
"test_view_node": "Your name is"}
expected_tree = \
['test_start_node',
['test_set_node',
['test_start_node'],
'test_look_node',
['test_start_node'],
'test_view_node',
['test_start_node'],
'test_dynamic_node',
['test_dynamic_node',
'test_dynamic_node',
'test_dynamic_node',
'test_dynamic_node',
'test_start_node'],
'test_end_node',
'test_displayinput_node',
['test_start_node']]]
def test_kwargsave(self): def test_kwargsave(self):
self.assertTrue(hasattr(self.menu, "testval")) self.assertTrue(hasattr(self.menu, "testval"))