(1, r'says, "\[35mfoo\[0m\."', ""),
)
+test_escape_macros = (
+ (0, '> ', "say $(red)bar$(nrm)"),
+ (1, r'says, "\$\(red\)bar\$\(nrm\)."', ""),
+)
+
test_movement = (
(0, "> ", "move north"),
(0, r"You exit to the north\.", ""),
test_preferences = (
(0, "> ", "preferences"),
- (0, r"prompt \x1b\[32m.*> ", "preferences prompt #"),
- (0, r"# ", "preferences prompt"),
- (0, r"#.*# ", "preferences prompt >"),
+ (0, r"\[32mprompt\x1b\[0m - <not set>.*> ", "preferences prompt $(foo)"),
+ (0, r"\$\(foo\) ", "preferences prompt"),
+ (0, r"\$\(foo\).*\$\(foo\) ", "preferences prompt $(time)>"),
+ (0, "[0-9]> ", "preferences loglevel 0"),
+ (0, "does not exist.*> ", "preferences prompt >"),
(2, "> ", "preferences loglevel 0"),
(2, "> ", "preferences"),
- (2, r"loglevel \x1b\[32m0\x1b\[0m.*> ", "preferences loglevel zero"),
+ (2, r"\[31mloglevel\x1b\[0m - 0.*> ", "preferences loglevel zero"),
(2, r'''cannot be set to type "<class 'str'>"\..*> ''', ""),
)
# unescaped and deduplicated to a single \xff in the buffer and then
# the line of input discarded as a non-ASCII sequence
(2, "> ", b"say argle\xff\xffbargle\r\0"),
- (2, r"Non-ASCII characters from admin: b'say argle\\xffbargle'.*> ", ""),
+ (2, r"Non-ASCII characters from admin: b'say.*argle\\xffbargle'.*> ", ""),
)
-test_telnet_unknown = (
+test_telnet_unknown_command = (
# Send an unsupported negotiation command #127 which should get filtered
# from the line of input
(2, "> ", b"say glop\xff\x7fglyf\r\0"),
(2, r'Ignored unknown command 127 from admin\..*"Glopglyf\.".*> ', ""),
)
+test_telnet_unknown_option = (
+ # Send an unassigned negotiation option #127 which should get logged
+ (2, "> ", b"\xff\xfe\x7f\r\0"),
+ (2, r'''Received "don't 127" from admin\..*> ''', ""),
+)
+
test_admin_restriction = (
(0, "> ", "help halt"),
(0, r"That is not an available command\.", "halt"),
(2, "This will save all active accounts", ""),
)
+test_help = (
+ (0, "> ", "help say"),
+ (0, r"See also: .*chat.*> ", ""),
+)
+
+test_abbrev = (
+ (0, "> ", "h"),
+ (0, r"h\[elp\].*m\[ove\].*> ", "he mo"),
+ (0, r"Move in a specific direction\..*> ", "mov nor"),
+ (0, r"You exit to the north\..*> ", "m s"),
+ (0, r"You exit to the south\..*> ", ""),
+)
+
test_reload = (
(2, "> ", "reload"),
(2, r"Reloading all code modules, configs and data\."
(2, r"Running mudpy .* on .* Python 3.*with.*pyyaml.*> ", ""),
)
+test_show_time = (
+ (2, "> ", "show time"),
+ (2, r"\r\n[0-9]+ increments elapsed.*> ", ""),
+)
+
test_show_files = (
(2, "> ", "show files"),
(2, r'These are the current files containing the universe:.*'
r' \x1b\[32mgender: \x1b\[31mfemale.*> ', ""),
)
+test_show_result = (
+ (2, "> ", "show result 12345*67890"),
+ (2, r"\r\n838102050\r\n.*> ", "show result 1/0"),
+ (2, r"Your expression raised an exception.*division by zero.*> ",
+ "show result mudpy"),
+ (2, r"<module 'mudpy' from .*> ", "show result re"),
+ (2, r"Your expression raised an exception.*name 're' is not defined.*> ",
+ "show result universe"),
+ (2, r"<mudpy.misc.Universe object at 0x.*> ", "show result actor"),
+ (2, r"Your expression raised an exception.*name 'actor' is not "
+ r"defined.*> ", ""),
+)
+
test_show_log = (
(2, "> ", "show log"),
(2, r"There are [0-9]+ log lines in memory and [0-9]+ at or above level "
(test_chat_mode, "chat mode"),
(test_wrapping, "wrapping"),
(test_forbid_ansi_input, "raw escape input is filtered"),
+ (test_escape_macros, "replacement macros are escaped"),
(test_movement, "movement"),
(test_actor_disappears, "actor spontaneous disappearance"),
(test_account1_teardown, "second account teardown"),
(test_preferences, "set and show preferences"),
(test_crlf_eol, "send crlf from the client as eol"),
(test_telnet_iac, "escape stray telnet iac bytes"),
- (test_telnet_unknown, "strip unknown telnet command"),
+ (test_telnet_unknown_command, "strip unknown telnet command"),
+ (test_telnet_unknown_option, "log unknown telnet option"),
(test_admin_restriction, "restricted admin commands"),
(test_admin_help, "admin help"),
+ (test_help, "help command"),
+ (test_abbrev, "command abbreviation"),
(test_reload, "reload"),
(test_set_facet, "set facet"),
(test_set_refused, "refuse altering read-only element"),
(test_show_version, "show version and diagnostic info"),
+ (test_show_time, "show elapsed world clock increments"),
(test_show_files, "show a list of loaded files"),
(test_show_file, "show nodes from a specific file"),
(test_show_groups, "show groups"),
(test_show_group, "show group"),
(test_show_element, "show element"),
+ (test_show_result, "show result of a python expression"),
(test_show_log, "show log"),
(test_custom_loglevel, "custom loglevel"),
(test_invalid_loglevel, "invalid loglevel"),
return True
+def option_callback(telnet_socket, command, option):
+ if option == b'\x7f':
+ # We use this unassigned option value as a canary, so short-circuit
+ # any response to avoid endlessly looping
+ pass
+ elif command in (telnetlib.DO, telnetlib.DONT):
+ telnet_socket.send(telnetlib.IAC + telnetlib.WONT + option)
+ elif command in (telnetlib.WILL, telnetlib.WONT):
+ telnet_socket.send(telnetlib.IAC + telnetlib.DONT + option)
+
+
def main():
captures = ["", "", ""]
lusers = [telnetlib.Telnet(), telnetlib.Telnet(), telnetlib.Telnet()]
service = start_service(sys.argv[1])
for luser in lusers:
luser.open("::1", 4000)
+ luser.set_option_negotiation_callback(option_callback)
for test, description in dialogue:
tlog("\nTesting %s..." % description)
test_start = time.time()
conversant].read_very_eager().decode("utf-8")
except Exception:
pass
- if index is not 0:
+ if index != 0:
tlog("\nERROR: luser%s did not receive expected string:\n\n"
"%s\n\n"
"Check the end of capture_%s.log for received data."