mirror of https://github.com/progval/irctest.git
Add tests for ELIST
This commit is contained in:
parent
a9a7a2a187
commit
af001fad2e
2
Makefile
2
Makefile
|
@ -179,6 +179,7 @@ SOPEL_SELECTORS := \
|
|||
# testChathistory[AROUND] fails: https://bugs.unrealircd.org/view.php?id=5953
|
||||
# testWhoAllOpers fails because Unreal skips results when the mask is too broad
|
||||
# HELP and HELPOP tests fail because Unreal uses custom numerics https://github.com/unrealircd/unrealircd/pull/184
|
||||
# testListTopicTime fails because Unreal mistakenly advertises it as available https://github.com/unrealircd/unrealircd/pull/193
|
||||
UNREALIRCD_SELECTORS := \
|
||||
not Ergo \
|
||||
and not deprecated \
|
||||
|
@ -194,6 +195,7 @@ UNREALIRCD_SELECTORS := \
|
|||
and not (testChathistory and (between or around)) \
|
||||
and not testWhoAllOpers \
|
||||
and not HelpTestCase \
|
||||
and not testListTopicTime \
|
||||
$(EXTRA_SELECTORS)
|
||||
|
||||
.PHONY: all flakes bahamut charybdis ergo inspircd ircu2 snircd irc2 mammon limnoria sopel solanum unrealircd
|
||||
|
|
|
@ -74,7 +74,7 @@ operator {{
|
|||
|
||||
|
||||
class Plexus4Controller(BaseHybridController):
|
||||
software_name = "Hybrid"
|
||||
software_name = "Plexus4"
|
||||
binary_name = "ircd"
|
||||
services_protocol = "plexus"
|
||||
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
import functools
|
||||
import os
|
||||
<<<<<<< HEAD
|
||||
import pathlib
|
||||
import shutil
|
||||
import signal
|
||||
=======
|
||||
import shutil
|
||||
>>>>>>> 96e6642 (Add support for 'faketime', to avoid long sleeps in upcoming ELIST tests)
|
||||
import subprocess
|
||||
import textwrap
|
||||
from typing import Optional, Set, Type
|
||||
|
|
|
@ -1,8 +1,26 @@
|
|||
from irctest import cases
|
||||
import time
|
||||
|
||||
from irctest import cases, runner
|
||||
from irctest.numerics import RPL_LIST, RPL_LISTEND, RPL_LISTSTART
|
||||
|
||||
|
||||
class ListTestCase(cases.BaseServerTestCase):
|
||||
class _BasedListTestCase(cases.BaseServerTestCase):
|
||||
def _parseChanList(self, client):
|
||||
channels = set()
|
||||
while True:
|
||||
m = self.getMessage(client)
|
||||
if m.command == RPL_LISTEND:
|
||||
break
|
||||
if m.command == RPL_LIST:
|
||||
if m.params[1].startswith("&"):
|
||||
# skip local pseudo-channels listed by ngircd and ircu
|
||||
continue
|
||||
channels.add(m.params[1])
|
||||
|
||||
return channels
|
||||
|
||||
|
||||
class ListTestCase(_BasedListTestCase):
|
||||
@cases.mark_specifications("RFC1459", "RFC2812")
|
||||
def testListEmpty(self):
|
||||
"""<https://tools.ietf.org/html/rfc1459#section-4.2.6>
|
||||
|
@ -77,3 +95,304 @@ class ListTestCase(cases.BaseServerTestCase):
|
|||
fail_msg="Third reply to LIST is not 322 (RPL_LIST) "
|
||||
"or 323 (RPL_LISTEND), or but: {msg}",
|
||||
)
|
||||
|
||||
@cases.mark_isupport("ELIST")
|
||||
@cases.mark_specifications("Modern")
|
||||
def testListMask(self):
|
||||
"""
|
||||
"M: Searching based on mask."
|
||||
-- <https://modern.ircdocs.horse/#elist-parameter>
|
||||
-- https://datatracker.ietf.org/doc/html/draft-hardy-irc-isupport-00#section-4.8
|
||||
"""
|
||||
self.connectClient("foo")
|
||||
|
||||
if "M" not in self.server_support.get("ELIST", ""):
|
||||
raise runner.OptionalExtensionNotSupported("ELIST=M")
|
||||
|
||||
self.connectClient("bar")
|
||||
self.sendLine(1, "JOIN #chan1")
|
||||
self.getMessages(1)
|
||||
self.sendLine(1, "JOIN #chan2")
|
||||
self.getMessages(1)
|
||||
|
||||
self.sendLine(2, "LIST *an1")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan1"})
|
||||
|
||||
self.sendLine(2, "LIST *an2")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan2"})
|
||||
|
||||
self.sendLine(2, "LIST #c*n2")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan2"})
|
||||
|
||||
self.sendLine(2, "LIST *an3")
|
||||
self.assertEqual(self._parseChanList(2), set())
|
||||
|
||||
self.sendLine(2, "LIST #ch*")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan1", "#chan2"})
|
||||
|
||||
@cases.mark_isupport("ELIST")
|
||||
@cases.mark_specifications("Modern")
|
||||
def testListNotMask(self):
|
||||
"""
|
||||
" N: Searching based on a non-matching mask. i.e., the opposite of M."
|
||||
-- <https://modern.ircdocs.horse/#elist-parameter>
|
||||
-- https://datatracker.ietf.org/doc/html/draft-hardy-irc-isupport-00#section-4.8
|
||||
"""
|
||||
self.connectClient("foo")
|
||||
|
||||
if "N" not in self.server_support.get("ELIST", ""):
|
||||
raise runner.OptionalExtensionNotSupported("ELIST=N")
|
||||
|
||||
self.sendLine(1, "JOIN #chan1")
|
||||
self.getMessages(1)
|
||||
self.sendLine(1, "JOIN #chan2")
|
||||
self.getMessages(1)
|
||||
|
||||
self.connectClient("bar")
|
||||
|
||||
self.sendLine(2, "LIST !*an1")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan2"})
|
||||
|
||||
self.sendLine(2, "LIST !*an2")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan1"})
|
||||
|
||||
self.sendLine(2, "LIST !#c*n2")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan1"})
|
||||
|
||||
self.sendLine(2, "LIST !*an3")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan1", "#chan2"})
|
||||
|
||||
self.sendLine(2, "LIST !#ch*")
|
||||
self.assertEqual(self._parseChanList(2), set())
|
||||
|
||||
@cases.mark_isupport("ELIST")
|
||||
@cases.mark_specifications("Modern")
|
||||
def testListUsers(self):
|
||||
"""
|
||||
"U: Searching based on user count within the channel, via the "<val" and
|
||||
">val" modifiers to search for a channel that has less or more than val users,
|
||||
respectively."
|
||||
-- <https://modern.ircdocs.horse/#elist-parameter>
|
||||
-- https://datatracker.ietf.org/doc/html/draft-hardy-irc-isupport-00#section-4.8
|
||||
"""
|
||||
self.connectClient("foo")
|
||||
|
||||
if "M" not in self.server_support.get("ELIST", ""):
|
||||
raise runner.OptionalExtensionNotSupported("ELIST=M")
|
||||
|
||||
self.sendLine(1, "JOIN #chan1")
|
||||
self.getMessages(1)
|
||||
self.sendLine(1, "JOIN #chan2")
|
||||
self.getMessages(1)
|
||||
|
||||
self.connectClient("bar")
|
||||
self.sendLine(2, "JOIN #chan2")
|
||||
self.getMessages(2)
|
||||
|
||||
self.connectClient("baz")
|
||||
|
||||
self.sendLine(3, "LIST >0")
|
||||
self.assertEqual(self._parseChanList(3), {"#chan1", "#chan2"})
|
||||
|
||||
self.sendLine(3, "LIST <1")
|
||||
self.assertEqual(self._parseChanList(3), set())
|
||||
|
||||
self.sendLine(3, "LIST <100")
|
||||
self.assertEqual(self._parseChanList(3), {"#chan1", "#chan2"})
|
||||
|
||||
self.sendLine(3, "LIST >1")
|
||||
self.assertEqual(self._parseChanList(3), {"#chan2"})
|
||||
|
||||
self.sendLine(3, "LIST <2")
|
||||
self.assertEqual(self._parseChanList(3), {"#chan1"})
|
||||
|
||||
self.sendLine(3, "LIST <100")
|
||||
self.assertEqual(self._parseChanList(3), {"#chan1", "#chan2"})
|
||||
|
||||
|
||||
class FaketimeListTestCase(_BasedListTestCase):
|
||||
faketime = "+1y x30" # for every wall clock second, 1 minute passed for the server
|
||||
|
||||
def _sleep_minutes(self, n):
|
||||
for _ in range(n):
|
||||
if self.controller.faketime_enabled:
|
||||
# From the server's point of view, 1 min will pass
|
||||
time.sleep(2)
|
||||
else:
|
||||
time.sleep(60)
|
||||
|
||||
# reply to pings
|
||||
self.getMessages(1)
|
||||
self.getMessages(2)
|
||||
|
||||
@cases.mark_isupport("ELIST")
|
||||
@cases.mark_specifications("Modern")
|
||||
def testListCreationTime(self):
|
||||
"""
|
||||
" C: Searching based on channel creation time, via the "C<val" and "C>val"
|
||||
modifiers to search for a channel creation time that is higher or lower
|
||||
than val."
|
||||
-- <https://modern.ircdocs.horse/#elist-parameter>
|
||||
-- https://datatracker.ietf.org/doc/html/draft-hardy-irc-isupport-00#section-4.8
|
||||
|
||||
Unfortunately, this is ambiguous, because "val" is a time delta (in minutes),
|
||||
not a timestamp.
|
||||
|
||||
On InspIRCd and Charybdis/Solanum, "C<val" is interpreted as "the channel was
|
||||
created less than <val> minutes ago
|
||||
|
||||
On UnrealIRCd, Plexus, and Hybrid, it is interpreted as "the channel's creation
|
||||
time is a timestamp lower than <val> minutes ago" (ie. the exact opposite)
|
||||
|
||||
"C: Searching based on channel creation time, via the "C<val" and "C>val"
|
||||
modifiers to search for a channel that was created either less than `val`
|
||||
minutes ago, or more than `val` minutes ago, respectively"
|
||||
-- https://github.com/ircdocs/modern-irc/pull/171
|
||||
"""
|
||||
self.connectClient("foo")
|
||||
|
||||
if "C" not in self.server_support.get("ELIST", ""):
|
||||
raise runner.OptionalExtensionNotSupported("ELIST=C")
|
||||
|
||||
self.connectClient("bar")
|
||||
self.sendLine(1, "JOIN #chan1")
|
||||
self.getMessages(1)
|
||||
|
||||
# Helps debugging
|
||||
self.sendLine(1, "TIME")
|
||||
self.getMessages(1)
|
||||
|
||||
self._sleep_minutes(2)
|
||||
|
||||
# Helps debugging
|
||||
self.sendLine(1, "TIME")
|
||||
self.getMessages(1)
|
||||
|
||||
self.sendLine(1, "JOIN #chan2")
|
||||
self.getMessages(1)
|
||||
|
||||
self._sleep_minutes(1)
|
||||
|
||||
if self.controller.software_name in ("UnrealIRCd", "Plexus4", "Hybrid"):
|
||||
self.sendLine(2, "LIST C<2")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan1"})
|
||||
|
||||
self.sendLine(2, "LIST C>2")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan2"})
|
||||
|
||||
self.sendLine(2, "LIST C>0")
|
||||
self.assertEqual(self._parseChanList(2), set())
|
||||
|
||||
self.sendLine(2, "LIST C<0")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan1", "#chan2"})
|
||||
|
||||
self.sendLine(2, "LIST C>10")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan1", "#chan2"})
|
||||
elif self.controller.software_name in ("Solanum", "Charybdis", "InspIRCd"):
|
||||
self.sendLine(2, "LIST C>2")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan1"})
|
||||
|
||||
self.sendLine(2, "LIST C<2")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan2"})
|
||||
|
||||
self.sendLine(2, "LIST C<0")
|
||||
if self.controller.software_name == "InspIRCd":
|
||||
self.assertEqual(self._parseChanList(2), {"#chan1", "#chan2"})
|
||||
else:
|
||||
self.assertEqual(self._parseChanList(2), set())
|
||||
|
||||
self.sendLine(2, "LIST C>0")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan1", "#chan2"})
|
||||
|
||||
self.sendLine(2, "LIST C<10")
|
||||
self.assertEqual(self._parseChanList(2), {"#chan1", "#chan2"})
|
||||
else:
|
||||
assert False, f"{self.controller.software_name} not supported"
|
||||
|
||||
@cases.mark_isupport("ELIST")
|
||||
@cases.mark_specifications("Modern")
|
||||
def testListTopicTime(self):
|
||||
"""
|
||||
"T: Searching based on topic time, via the "T<val" and "T>val"
|
||||
modifiers to search for a topic time that is lower or higher than
|
||||
val respectively."
|
||||
-- <https://modern.ircdocs.horse/#elist-parameter>
|
||||
-- https://datatracker.ietf.org/doc/html/draft-hardy-irc-isupport-00#section-4.8
|
||||
|
||||
See testListCreationTime's docstring for comments on this.
|
||||
|
||||
"T: Searching based on topic set time, via the "T<val" and "T>val" modifiers
|
||||
to search for a topic time that was set less than `val` minutes ago, or more
|
||||
than `val` minutes ago, respectively."
|
||||
-- https://github.com/ircdocs/modern-irc/pull/171
|
||||
"""
|
||||
self.connectClient("foo")
|
||||
|
||||
if "T" not in self.server_support.get("ELIST", ""):
|
||||
raise runner.OptionalExtensionNotSupported("ELIST=T")
|
||||
|
||||
self.connectClient("bar")
|
||||
self.sendLine(1, "JOIN #chan1")
|
||||
self.sendLine(1, "JOIN #chan2")
|
||||
self.getMessages(1)
|
||||
|
||||
self.sendLine(1, "TOPIC #chan1 :First channel")
|
||||
self.getMessages(1)
|
||||
|
||||
# Helps debugging
|
||||
self.sendLine(1, "TIME")
|
||||
self.getMessages(1)
|
||||
|
||||
self._sleep_minutes(2)
|
||||
|
||||
# Helps debugging
|
||||
self.sendLine(1, "TIME")
|
||||
self.getMessages(1)
|
||||
|
||||
self.sendLine(1, "TOPIC #chan2 :Second channel")
|
||||
self.getMessages(1)
|
||||
|
||||
self._sleep_minutes(1)
|
||||
|
||||
if self.controller.software_name in ("UnrealIRCd",):
|
||||
self.sendLine(1, "LIST T<2")
|
||||
self.assertEqual(self._parseChanList(1), {"#chan1"})
|
||||
|
||||
self.sendLine(1, "LIST T>2")
|
||||
self.assertEqual(self._parseChanList(1), {"#chan2"})
|
||||
|
||||
self.sendLine(1, "LIST T>0")
|
||||
self.assertEqual(self._parseChanList(1), set())
|
||||
|
||||
self.sendLine(1, "LIST T<0")
|
||||
self.assertEqual(self._parseChanList(1), {"#chan1", "#chan2"})
|
||||
|
||||
self.sendLine(1, "LIST T>10")
|
||||
self.assertEqual(self._parseChanList(1), {"#chan1", "#chan2"})
|
||||
elif self.controller.software_name in (
|
||||
"Solanum",
|
||||
"Charybdis",
|
||||
"InspIRCd",
|
||||
"Plexus4",
|
||||
"Hybrid",
|
||||
):
|
||||
self.sendLine(1, "LIST T>2")
|
||||
self.assertEqual(self._parseChanList(1), {"#chan1"})
|
||||
|
||||
self.sendLine(1, "LIST T<2")
|
||||
self.assertEqual(self._parseChanList(1), {"#chan2"})
|
||||
|
||||
self.sendLine(1, "LIST T<0")
|
||||
if self.controller.software_name == "InspIRCd":
|
||||
# Insp internally represents "LIST T>0" like "LIST"
|
||||
self.assertEqual(self._parseChanList(1), {"#chan1", "#chan2"})
|
||||
else:
|
||||
self.assertEqual(self._parseChanList(1), set())
|
||||
|
||||
self.sendLine(1, "LIST T>0")
|
||||
self.assertEqual(self._parseChanList(1), {"#chan1", "#chan2"})
|
||||
|
||||
self.sendLine(1, "LIST T<10")
|
||||
self.assertEqual(self._parseChanList(1), {"#chan1", "#chan2"})
|
||||
else:
|
||||
assert False, f"{self.controller.software_name} not supported"
|
||||
|
|
|
@ -50,6 +50,7 @@ class Capabilities(enum.Enum):
|
|||
@enum.unique
|
||||
class IsupportTokens(enum.Enum):
|
||||
BOT = "BOT"
|
||||
ELIST = "ELIST"
|
||||
PREFIX = "PREFIX"
|
||||
MONITOR = "MONITOR"
|
||||
STATUSMSG = "STATUSMSG"
|
||||
|
|
|
@ -32,6 +32,7 @@ markers =
|
|||
|
||||
# isupport tokens
|
||||
BOT
|
||||
ELIST
|
||||
MONITOR
|
||||
PREFIX
|
||||
STATUSMSG
|
||||
|
|
Loading…
Reference in New Issue