Final unit tests for evmenu templating

This commit is contained in:
Griatch 2020-10-04 21:26:07 +02:00
parent a94e723d6b
commit 1746aaf06b
3 changed files with 77 additions and 30 deletions

View file

@ -290,10 +290,20 @@ def goto_command_demo_room(caller, raw_string, **kwargs):
_maintain_demo_room(caller) _maintain_demo_room(caller)
caller.cmdset.remove(DemoCommandSetHelp) caller.cmdset.remove(DemoCommandSetHelp)
caller.cmdset.remove(DemoCommandSetComms) caller.cmdset.remove(DemoCommandSetComms)
caller.cmdset.add(DemoCommandSetRoom) # TODO - make persistent caller.cmdset.add(DemoCommandSetRoom)
return "command_demo_room" return "command_demo_room"
def goto_cleanup_cmdsets(caller, raw_strings, **kwargs):
"""
Cleanup all cmdsets.
"""
caller.cmdset.remove(DemoCommandSetHelp)
caller.cmdset.remove(DemoCommandSetComms)
caller.cmdset.remove(DemoCommandSetRoom)
return kwargs.get("gotonode")
# register all callables that can be used in the menu template # register all callables that can be used in the menu template
GOTO_CALLABLES = { GOTO_CALLABLES = {
@ -302,6 +312,7 @@ GOTO_CALLABLES = {
"goto_command_demo_help": goto_command_demo_help, "goto_command_demo_help": goto_command_demo_help,
"goto_command_demo_comms": goto_command_demo_comms, "goto_command_demo_comms": goto_command_demo_comms,
"goto_command_demo_room": goto_command_demo_room, "goto_command_demo_room": goto_command_demo_room,
"goto_cleanup_cmdsets": goto_cleanup_cmdsets,
} }
@ -365,7 +376,7 @@ gaming style you like and possibly any new ones you can come up with!
## OPTIONS ## OPTIONS
next;n: About Evennia -> about_evennia next;n: About Evennia -> about_evennia
back to start;start;t: start back to start;back;start;t: start
>: about_evennia >: about_evennia
# --------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------
@ -498,7 +509,7 @@ those channels ...
## OPTIONS ## OPTIONS
next;n: Talk on Channels -> talk on channels next;n: Talk on Channels -> talk on channels
back;b: Using the webclient -> using webclient back;b: Using the webclient -> goto_cleanup_cmdsets(gotonode='using webclient')
back to start;start: start back to start;start: start
>: talk on channels >: talk on channels
@ -556,7 +567,7 @@ include other people/objects in the emote, reference things by a short-descripti
## OPTIONS ## OPTIONS
next;n: Paging people -> paging_people next;n: Paging people -> paging_people
back;b: Talk on Channels -> talk on channels back;b: Talk on Channels -> goto_command_demo_help(gotonode='talk on channels')
back to start;start: start back to start;start: start
>: paging_people >: paging_people
@ -627,7 +638,7 @@ color codes printed, try
## OPTIONS ## OPTIONS
next;n: Moving and Exploring -> goto_command_demo_room() next;n: Moving and Exploring -> goto_command_demo_room()
back;b: Paging people -> paging_people back;b: Paging people -> goto_command_demo_comms(gotonode='paging_people')
back to start;start: start back to start;start: start
>: goto_command_demo_room() >: goto_command_demo_room()
@ -650,7 +661,7 @@ around in. Explore a little and use |ynext|n when you are done.
## OPTIONS ## OPTIONS
next;n: Conclusions -> conclusions next;n: Conclusions -> conclusions
back;b: Channel commands -> testing_colors back;b: Channel commands -> goto_command_demo_comms(gotonode='testing_colors')
back to start;start: start back to start;start: start
>: conclusions >: conclusions
@ -667,13 +678,11 @@ if you get stuck!
Write |ynext|n to end this wizard and continue to the tutorial-world quest! Write |ynext|n to end this wizard and continue to the tutorial-world quest!
If you want there is also some |wextra|n info for where to go beyond that. If you want there is also some |wextra|n info for where to go beyond that.
Good luck!
## OPTIONS ## OPTIONS
extra: Some more help on where to go next -> post scriptum extra: Where to go next -> post scriptum
next;next;n: end next;next;n: End -> end
back;b: goto_command_demo_room() back;b: Moving and Exploring -> goto_command_demo_room()
back to start;start: start back to start;start: start
>: end >: end
@ -718,7 +727,7 @@ back: conclusions
## NODE end ## NODE end
Thanks for trying out the tutorial! |gGood luck!|n
""" """

View file

@ -1634,7 +1634,7 @@ _RE_NODE = re.compile(r"##\s*?NODE\s+?(?P<nodename>\S[\S\s]*?)$", re.I + re.M)
_RE_OPTIONS_SEP = re.compile(r"##\s*?OPTIONS\s*?$", re.I + re.M) _RE_OPTIONS_SEP = re.compile(r"##\s*?OPTIONS\s*?$", re.I + re.M)
_RE_CALLABLE = re.compile(r"\S+?\(\)", re.I + re.M) _RE_CALLABLE = re.compile(r"\S+?\(\)", re.I + re.M)
_RE_CALLABLE = re.compile( _RE_CALLABLE = re.compile(
r"(?P<funcname>\S+?)(?:\((?P<kwargs>[\S\s]+?=[\S\s]+?)\)|\(\))", re.I + re.M r"(?P<funcname>\S+?)(?:\((?P<kwargs>[\S\s]+?)\)|\(\))", re.I + re.M
) )
_HELP_NO_OPTION_MATCH = _("Choose an option or try 'help'.") _HELP_NO_OPTION_MATCH = _("Choose an option or try 'help'.")
@ -1664,13 +1664,12 @@ def _process_callable(caller, goto, goto_callables, raw_string,
gotokwargs = match.group("kwargs") or "" gotokwargs = match.group("kwargs") or ""
if gotofunc in goto_callables: if gotofunc in goto_callables:
for kwarg in gotokwargs.split(","): for kwarg in gotokwargs.split(","):
if kwarg and "=" in kwarg:
key, value = [part.strip() for part in kwarg.split("=", 1)] key, value = [part.strip() for part in kwarg.split("=", 1)]
if key in ("evmenu_goto", "evmenu_gotomap", "_current_nodename", if key in ("evmenu_goto", "evmenu_gotomap", "_current_nodename",
"evmenu_current_nodename", "evmenu_goto_callables"): "evmenu_current_nodename", "evmenu_goto_callables"):
raise RuntimeError( raise RuntimeError(
f"EvMenu template error: goto-callable '{goto}' uses a " f"EvMenu template error: goto-callable '{goto}' uses a "
f"kwarg ({key}) that is reserved for the EvMenu templating " f"kwarg ({kwarg}) that is reserved for the EvMenu templating "
"system. Rename the kwarg.") "system. Rename the kwarg.")
try: try:
key = literal_eval(key) key = literal_eval(key)
@ -1681,6 +1680,7 @@ def _process_callable(caller, goto, goto_callables, raw_string,
except ValueError: except ValueError:
pass pass
kwargs[key] = value kwargs[key] = value
goto = goto_callables[gotofunc](caller, raw_string, **kwargs) goto = goto_callables[gotofunc](caller, raw_string, **kwargs)
if goto is None: if goto is None:
return goto, {"generated_nodename": current_nodename} return goto, {"generated_nodename": current_nodename}
@ -1755,6 +1755,23 @@ def parse_menu_template(caller, menu_template, goto_callables=None):
dict: A `{"node": nodefunc}` menutree suitable to pass into EvMenu. dict: A `{"node": nodefunc}` menutree suitable to pass into EvMenu.
""" """
def _validate_kwarg(goto, kwarg):
"""
Validate goto-callable kwarg is on correct form.
"""
if not "=" in kwarg:
raise RuntimeError(
f"EvMenu template error: goto-callable '{goto}' has a "
f"non-kwarg argument ({kwarg}). All callables in the "
"template must have only keyword-arguments, or no "
"args at all.")
key, _ = [part.strip() for part in kwarg.split("=", 1)]
if key in ("evmenu_goto", "evmenu_gotomap", "_current_nodename",
"evmenu_current_nodename", "evmenu_goto_callables"):
raise RuntimeError(
f"EvMenu template error: goto-callable '{goto}' uses a "
f"kwarg ({kwarg}) that is reserved for the EvMenu templating "
"system. Rename the kwarg.")
def _parse_options(nodename, optiontxt, goto_callables): def _parse_options(nodename, optiontxt, goto_callables):
""" """
@ -1779,6 +1796,14 @@ def parse_menu_template(caller, menu_template, goto_callables=None):
if _OPTION_CALL_MARKER in goto: if _OPTION_CALL_MARKER in goto:
desc, goto = [part.strip() for part in goto.split(_OPTION_CALL_MARKER, 1)] desc, goto = [part.strip() for part in goto.split(_OPTION_CALL_MARKER, 1)]
# validate callable
match = _RE_CALLABLE.match(goto)
if match:
kwargs = match.group("kwargs")
if kwargs:
for kwarg in kwargs.split(','):
_validate_kwarg(goto, kwarg)
# parse key [;aliases|pattern] # parse key [;aliases|pattern]
key = [part.strip() for part in key.split(_OPTION_ALIAS_MARKER)] key = [part.strip() for part in key.split(_OPTION_ALIAS_MARKER)]
if not key: if not key:

View file

@ -327,3 +327,16 @@ class TestMenuTemplateParse(EvenniaTest):
def test_template2menu(self): def test_template2menu(self):
evmenu.template2menu(self.char1, self.menu_template, self.goto_callables) evmenu.template2menu(self.char1, self.menu_template, self.goto_callables)
def test_parse_menu_fail(self):
template = """
## NODE
Text
## OPTIONS
next: callnode2(invalid)
"""
with self.assertRaises(RuntimeError):
evmenu.parse_menu_template(self.char1, template, self.goto_callables)