From 72a12ff5ce9cb644ad58194f1670992ca85e5318 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 20 Mar 2022 14:05:45 +0100 Subject: [PATCH 1/3] Add support for 'faketime', to avoid long sleeps in upcoming ELIST tests --- .github/workflows/test-devel.yml | 76 ++++++++++---------- .github/workflows/test-devel_release.yml | 12 ++-- .github/workflows/test-stable.yml | 88 ++++++++++++------------ README.md | 1 + irctest/basecontrollers.py | 5 ++ irctest/cases.py | 7 ++ irctest/controllers/bahamut.py | 11 ++- irctest/controllers/base_hybrid.py | 10 +++ irctest/controllers/ergo.py | 11 ++- irctest/controllers/external_server.py | 1 + irctest/controllers/inspircd.py | 10 +++ irctest/controllers/irc2.py | 10 +++ irctest/controllers/ircu2.py | 10 +++ irctest/controllers/mammon.py | 10 +++ irctest/controllers/ngircd.py | 11 +++ irctest/controllers/snircd.py | 10 +++ irctest/controllers/unrealircd.py | 13 ++++ make_workflows.py | 4 +- 18 files changed, 207 insertions(+), 93 deletions(-) diff --git a/.github/workflows/test-devel.yml b/.github/workflows/test-devel.yml index 640e680..5d18ad2 100644 --- a/.github/workflows/test-devel.yml +++ b/.github/workflows/test-devel.yml @@ -435,8 +435,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -473,8 +473,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -505,8 +505,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -544,8 +544,8 @@ jobs: cd $GITHUB_WORKSPACE/ergo/ make build make install - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -582,8 +582,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -614,8 +614,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -652,8 +652,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -690,8 +690,8 @@ jobs: ./configure --prefix=$HOME/.local/ --with-maxcon=1024 --enable-debug make -j 4 make install - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -717,8 +717,8 @@ jobs: - name: Install dependencies run: pip install git+https://github.com/ProgVal/Limnoria.git@testing cryptography pyxmpp2-scram - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -749,8 +749,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -787,8 +787,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -819,8 +819,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -857,8 +857,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -889,8 +889,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -915,8 +915,8 @@ jobs: python-version: 3.7 - name: Install dependencies run: pip install git+https://github.com/sopel-irc/sopel.git - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -947,8 +947,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -979,8 +979,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -1017,8 +1017,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -1049,8 +1049,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip diff --git a/.github/workflows/test-devel_release.yml b/.github/workflows/test-devel_release.yml index 0b25abb..02d2524 100644 --- a/.github/workflows/test-devel_release.yml +++ b/.github/workflows/test-devel_release.yml @@ -121,8 +121,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -159,8 +159,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -191,8 +191,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip diff --git a/.github/workflows/test-stable.yml b/.github/workflows/test-stable.yml index 0e66d6d..7e0a1a3 100644 --- a/.github/workflows/test-stable.yml +++ b/.github/workflows/test-stable.yml @@ -478,8 +478,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -516,8 +516,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -548,8 +548,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -580,8 +580,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -619,8 +619,8 @@ jobs: cd $GITHUB_WORKSPACE/ergo/ make build make install - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -657,8 +657,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -689,8 +689,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -727,8 +727,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -759,8 +759,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -813,8 +813,8 @@ jobs: mkdir -p $HOME/.local/bin cp $HOME/.local/sbin/ircd $HOME/.local/bin/ircd' - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -851,8 +851,8 @@ jobs: ./configure --prefix=$HOME/.local/ --with-maxcon=1024 --enable-debug make -j 4 make install - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -877,8 +877,8 @@ jobs: python-version: 3.7 - name: Install dependencies run: pip install limnoria==2022.03.17 cryptography pyxmpp2-scram - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -909,8 +909,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -947,8 +947,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -979,8 +979,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -1017,8 +1017,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -1049,8 +1049,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -1075,8 +1075,8 @@ jobs: python-version: 3.7 - name: Install dependencies run: pip install sopel==7.1.8 - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -1107,8 +1107,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -1139,8 +1139,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -1177,8 +1177,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip @@ -1209,8 +1209,8 @@ jobs: path: '~' - name: Unpack artefacts run: cd ~; find -name 'artefacts-*.tar.gz' -exec tar -xzf '{}' \; - - name: Install Atheme - run: sudo apt-get install atheme-services + - name: Install system dependencies + run: sudo apt-get install atheme-services faketime - name: Install irctest dependencies run: |- python -m pip install --upgrade pip diff --git a/README.md b/README.md index 7aac195..18edbc5 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ have no side effect. Install irctest and dependencies: ``` +sudo apt install faketime # Optional, but greatly speeds up irctest/server_tests/list.py cd ~ git clone https://github.com/ProgVal/irctest.git cd irctest diff --git a/irctest/basecontrollers.py b/irctest/basecontrollers.py index b1464ac..e8680b3 100644 --- a/irctest/basecontrollers.py +++ b/irctest/basecontrollers.py @@ -189,6 +189,10 @@ class BaseServerController(_BaseController): """Character used for the 'mute' extban""" nickserv = "NickServ" + def __init__(self, *args: Any, **kwargs: Any): + super().__init__(*args, **kwargs) + self.faketime_enabled = False + def get_hostname_and_port(self) -> Tuple[str, int]: return find_hostname_and_port() @@ -202,6 +206,7 @@ class BaseServerController(_BaseController): run_services: bool, valid_metadata_keys: Optional[Set[str]], invalid_metadata_keys: Optional[Set[str]], + faketime: Optional[str], ) -> None: raise NotImplementedError() diff --git a/irctest/cases.py b/irctest/cases.py index 4dbd43a..a259b7f 100644 --- a/irctest/cases.py +++ b/irctest/cases.py @@ -508,6 +508,12 @@ class BaseServerTestCase( server_support: Optional[Dict[str, Optional[str]]] run_services = False + faketime: Optional[str] = None + """If not None and the controller supports it and libfaketime is available, + runs the server using faketime and this value set as the $FAKETIME env variable. + Tests must check ``self.controller.faketime_enabled`` is True before + relying on this.""" + __new__ = object.__new__ # pytest won't collect Generic[] subclasses otherwise def setUp(self) -> None: @@ -522,6 +528,7 @@ class BaseServerTestCase( invalid_metadata_keys=self.invalid_metadata_keys, ssl=self.ssl, run_services=self.run_services, + faketime=self.faketime, ) self.clients: Dict[TClientName, client_mock.ClientMock] = {} diff --git a/irctest/controllers/bahamut.py b/irctest/controllers/bahamut.py index 01cd4dd..6a23aaa 100644 --- a/irctest/controllers/bahamut.py +++ b/irctest/controllers/bahamut.py @@ -102,6 +102,7 @@ class BahamutController(BaseServerController, DirectoryBasedController): valid_metadata_keys: Optional[Set[str]] = None, invalid_metadata_keys: Optional[Set[str]] = None, restricted_metadata_keys: Optional[Set[str]] = None, + faketime: Optional[str], ) -> None: if valid_metadata_keys or invalid_metadata_keys: raise NotImplementedByController( @@ -136,15 +137,21 @@ class BahamutController(BaseServerController, DirectoryBasedController): # pem_path=self.pem_path, ) ) + + if faketime and shutil.which("faketime"): + faketime_cmd = ["faketime", "-f", faketime] + self.faketime_enabled = True + else: + faketime_cmd = [] + self.proc = subprocess.Popen( [ - # "strace", "-f", "-e", "file", + *faketime_cmd, "ircd", "-t", # don't fork "-f", os.path.join(self.directory, "server.conf"), ], - # stdout=subprocess.DEVNULL, ) if run_services: diff --git a/irctest/controllers/base_hybrid.py b/irctest/controllers/base_hybrid.py index f91229a..e2c0418 100644 --- a/irctest/controllers/base_hybrid.py +++ b/irctest/controllers/base_hybrid.py @@ -1,4 +1,5 @@ import os +import shutil import subprocess from typing import Optional, Set @@ -43,6 +44,7 @@ class BaseHybridController(BaseServerController, DirectoryBasedController): run_services: bool, valid_metadata_keys: Optional[Set[str]] = None, invalid_metadata_keys: Optional[Set[str]] = None, + faketime: Optional[str], ) -> None: if valid_metadata_keys or invalid_metadata_keys: raise NotImplementedByController( @@ -73,8 +75,16 @@ class BaseHybridController(BaseServerController, DirectoryBasedController): ) ) assert self.directory + + if faketime and shutil.which("faketime"): + faketime_cmd = ["faketime", "-f", faketime] + self.faketime_enabled = True + else: + faketime_cmd = [] + self.proc = subprocess.Popen( [ + *faketime_cmd, self.binary_name, "-foreground", "-configfile", diff --git a/irctest/controllers/ergo.py b/irctest/controllers/ergo.py index d6e80ab..3878e7b 100644 --- a/irctest/controllers/ergo.py +++ b/irctest/controllers/ergo.py @@ -1,6 +1,7 @@ import copy import json import os +import shutil import subprocess from typing import Any, Dict, Optional, Set, Type, Union @@ -155,6 +156,7 @@ class ErgoController(BaseServerController, DirectoryBasedController): valid_metadata_keys: Optional[Set[str]] = None, invalid_metadata_keys: Optional[Set[str]] = None, restricted_metadata_keys: Optional[Set[str]] = None, + faketime: Optional[str], config: Optional[Any] = None, ) -> None: if valid_metadata_keys or invalid_metadata_keys: @@ -202,8 +204,15 @@ class ErgoController(BaseServerController, DirectoryBasedController): self._write_config() subprocess.call(["ergo", "initdb", "--conf", self._config_path, "--quiet"]) subprocess.call(["ergo", "mkcerts", "--conf", self._config_path, "--quiet"]) + + if faketime and shutil.which("faketime"): + faketime_cmd = ["faketime", "-f", faketime] + self.faketime_enabled = True + else: + faketime_cmd = [] + self.proc = subprocess.Popen( - ["ergo", "run", "--conf", self._config_path, "--quiet"] + [*faketime_cmd, "ergo", "run", "--conf", self._config_path, "--quiet"] ) def wait_for_services(self) -> None: diff --git a/irctest/controllers/external_server.py b/irctest/controllers/external_server.py index 5ecbae0..e8c822a 100644 --- a/irctest/controllers/external_server.py +++ b/irctest/controllers/external_server.py @@ -42,6 +42,7 @@ class ExternalServerController(BaseServerController): valid_metadata_keys: Optional[Set[str]] = None, invalid_metadata_keys: Optional[Set[str]] = None, restricted_metadata_keys: Optional[Set[str]] = None, + faketime: Optional[str], ) -> None: pass diff --git a/irctest/controllers/inspircd.py b/irctest/controllers/inspircd.py index 1cd40ba..24167aa 100644 --- a/irctest/controllers/inspircd.py +++ b/irctest/controllers/inspircd.py @@ -1,4 +1,5 @@ import os +import shutil import subprocess from typing import Optional, Set, Type @@ -114,6 +115,7 @@ class InspircdController(BaseServerController, DirectoryBasedController): valid_metadata_keys: Optional[Set[str]] = None, invalid_metadata_keys: Optional[Set[str]] = None, restricted_metadata_keys: Optional[Set[str]] = None, + faketime: Optional[str] = None, ) -> None: if valid_metadata_keys or invalid_metadata_keys: raise NotImplementedByController( @@ -147,8 +149,16 @@ class InspircdController(BaseServerController, DirectoryBasedController): ) ) assert self.directory + + if faketime and shutil.which("faketime"): + faketime_cmd = ["faketime", "-f", faketime] + self.faketime_enabled = True + else: + faketime_cmd = [] + self.proc = subprocess.Popen( [ + *faketime_cmd, "inspircd", "--nofork", "--config", diff --git a/irctest/controllers/irc2.py b/irctest/controllers/irc2.py index 56defc8..d901c0d 100644 --- a/irctest/controllers/irc2.py +++ b/irctest/controllers/irc2.py @@ -1,4 +1,5 @@ import os +import shutil import subprocess from typing import Optional, Set, Type @@ -51,6 +52,7 @@ class Ircu2Controller(BaseServerController, DirectoryBasedController): run_services: bool, valid_metadata_keys: Optional[Set[str]] = None, invalid_metadata_keys: Optional[Set[str]] = None, + faketime: Optional[str], ) -> None: if valid_metadata_keys or invalid_metadata_keys: raise NotImplementedByController( @@ -76,8 +78,16 @@ class Ircu2Controller(BaseServerController, DirectoryBasedController): pidfile=pidfile, ) ) + + if faketime and shutil.which("faketime"): + faketime_cmd = ["faketime", "-f", faketime] + self.faketime_enabled = True + else: + faketime_cmd = [] + self.proc = subprocess.Popen( [ + *faketime_cmd, "ircd", "-s", # no iauth "-p", diff --git a/irctest/controllers/ircu2.py b/irctest/controllers/ircu2.py index 592cfd2..1b87338 100644 --- a/irctest/controllers/ircu2.py +++ b/irctest/controllers/ircu2.py @@ -1,4 +1,5 @@ import os +import shutil import subprocess from typing import Optional, Set, Type @@ -69,6 +70,7 @@ class Ircu2Controller(BaseServerController, DirectoryBasedController): run_services: bool, valid_metadata_keys: Optional[Set[str]] = None, invalid_metadata_keys: Optional[Set[str]] = None, + faketime: Optional[str], ) -> None: if valid_metadata_keys or invalid_metadata_keys: raise NotImplementedByController( @@ -94,8 +96,16 @@ class Ircu2Controller(BaseServerController, DirectoryBasedController): pidfile=pidfile, ) ) + + if faketime and shutil.which("faketime"): + faketime_cmd = ["faketime", "-f", faketime] + self.faketime_enabled = True + else: + faketime_cmd = [] + self.proc = subprocess.Popen( [ + *faketime_cmd, "ircd", "-n", # don't detach "-f", diff --git a/irctest/controllers/mammon.py b/irctest/controllers/mammon.py index c437139..591347e 100644 --- a/irctest/controllers/mammon.py +++ b/irctest/controllers/mammon.py @@ -1,4 +1,5 @@ import os +import shutil import subprocess from typing import Optional, Set, Type @@ -92,6 +93,7 @@ class MammonController(BaseServerController, DirectoryBasedController): valid_metadata_keys: Optional[Set[str]] = None, invalid_metadata_keys: Optional[Set[str]] = None, restricted_metadata_keys: Optional[Set[str]] = None, + faketime: Optional[str], ) -> None: if password is not None: raise NotImplementedByController("PASS command") @@ -113,8 +115,16 @@ class MammonController(BaseServerController, DirectoryBasedController): # with self.open_file('server.yml', 'r') as fd: # print(fd.read()) assert self.directory + + if faketime and shutil.which("faketime"): + faketime_cmd = ["faketime", "-f", faketime] + self.faketime_enabled = True + else: + faketime_cmd = [] + self.proc = subprocess.Popen( [ + *faketime_cmd, "mammond", "--nofork", # '--debug', "--config", diff --git a/irctest/controllers/ngircd.py b/irctest/controllers/ngircd.py index 17b3540..0999863 100644 --- a/irctest/controllers/ngircd.py +++ b/irctest/controllers/ngircd.py @@ -1,4 +1,5 @@ import os +import shutil import subprocess from typing import Optional, Set, Type @@ -56,6 +57,7 @@ class NgircdController(BaseServerController, DirectoryBasedController): valid_metadata_keys: Optional[Set[str]] = None, invalid_metadata_keys: Optional[Set[str]] = None, restricted_metadata_keys: Optional[Set[str]] = None, + faketime: Optional[str], ) -> None: if valid_metadata_keys or invalid_metadata_keys: raise NotImplementedByController( @@ -81,6 +83,7 @@ class NgircdController(BaseServerController, DirectoryBasedController): fd.write("\n") assert self.directory + with self.open_file("server.conf") as fd: fd.write( TEMPLATE_CONFIG.format( @@ -94,8 +97,16 @@ class NgircdController(BaseServerController, DirectoryBasedController): empty_file=os.path.join(self.directory, "empty.txt"), ) ) + + if faketime and shutil.which("faketime"): + faketime_cmd = ["faketime", "-f", faketime] + self.faketime_enabled = True + else: + faketime_cmd = [] + self.proc = subprocess.Popen( [ + *faketime_cmd, "ngircd", "--nodaemon", "--config", diff --git a/irctest/controllers/snircd.py b/irctest/controllers/snircd.py index 7fa9acc..2edb85c 100644 --- a/irctest/controllers/snircd.py +++ b/irctest/controllers/snircd.py @@ -1,4 +1,5 @@ import os +import shutil import subprocess from typing import Optional, Set, Type @@ -69,6 +70,7 @@ class SnircdController(BaseServerController, DirectoryBasedController): run_services: bool, valid_metadata_keys: Optional[Set[str]] = None, invalid_metadata_keys: Optional[Set[str]] = None, + faketime: Optional[str], ) -> None: if valid_metadata_keys or invalid_metadata_keys: raise NotImplementedByController( @@ -94,8 +96,16 @@ class SnircdController(BaseServerController, DirectoryBasedController): pidfile=pidfile, ) ) + + if faketime and shutil.which("faketime"): + faketime_cmd = ["faketime", "-f", faketime] + self.faketime_enabled = True + else: + faketime_cmd = [] + self.proc = subprocess.Popen( [ + *faketime_cmd, "ircd", "-n", # don't detach "-f", diff --git a/irctest/controllers/unrealircd.py b/irctest/controllers/unrealircd.py index af397c1..d9db353 100644 --- a/irctest/controllers/unrealircd.py +++ b/irctest/controllers/unrealircd.py @@ -1,8 +1,12 @@ 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 @@ -156,6 +160,7 @@ class UnrealircdController(BaseServerController, DirectoryBasedController): valid_metadata_keys: Optional[Set[str]] = None, invalid_metadata_keys: Optional[Set[str]] = None, restricted_metadata_keys: Optional[Set[str]] = None, + faketime: Optional[str], ) -> None: if valid_metadata_keys or invalid_metadata_keys: raise NotImplementedByController( @@ -192,6 +197,7 @@ class UnrealircdController(BaseServerController, DirectoryBasedController): fd.write("\n") assert self.directory + with self.open_file("unrealircd.conf") as fd: fd.write( TEMPLATE_CONFIG.format( @@ -225,9 +231,16 @@ class UnrealircdController(BaseServerController, DirectoryBasedController): proot_cmd = ["proot", "-b", f"{tmpdir}:{unrealircd_prefix}/tmp"] self.using_proot = True + if faketime and shutil.which("faketime"): + faketime_cmd = ["faketime", "-f", faketime] + self.faketime_enabled = True + else: + faketime_cmd = [] + self.proc = subprocess.Popen( [ *proot_cmd, + *faketime_cmd, "unrealircd", "-t", "-F", # BOOT_NOFORK diff --git a/make_workflows.py b/make_workflows.py index 4a74f90..3e1895a 100644 --- a/make_workflows.py +++ b/make_workflows.py @@ -209,8 +209,8 @@ def get_test_job(*, config, test_config, test_id, version_flavor, jobs): *unpack, *install_steps, { - "name": "Install Atheme", - "run": "sudo apt-get install atheme-services", + "name": "Install system dependencies", + "run": "sudo apt-get install atheme-services faketime", }, { "name": "Install irctest dependencies", From a9a7a2a1875c7ae0153c8a43635d55f676c6d81f Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 20 Mar 2022 14:06:54 +0100 Subject: [PATCH 2/3] list: Modernize tests a bit --- irctest/numerics.py | 1 + irctest/server_tests/list.py | 28 ++++++++++++++++------------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/irctest/numerics.py b/irctest/numerics.py index 28006fe..8aafb08 100644 --- a/irctest/numerics.py +++ b/irctest/numerics.py @@ -66,6 +66,7 @@ RPL_WHOISIDLE = "317" RPL_ENDOFWHOIS = "318" RPL_WHOISCHANNELS = "319" RPL_WHOISSPECIAL = "320" +RPL_LISTSTART = "321" RPL_LIST = "322" RPL_LISTEND = "323" RPL_CHANNELMODEIS = "324" diff --git a/irctest/server_tests/list.py b/irctest/server_tests/list.py index 9529cb9..3ef89d0 100644 --- a/irctest/server_tests/list.py +++ b/irctest/server_tests/list.py @@ -1,4 +1,5 @@ from irctest import cases +from irctest.numerics import RPL_LIST, RPL_LISTEND, RPL_LISTSTART class ListTestCase(cases.BaseServerTestCase): @@ -6,26 +7,27 @@ class ListTestCase(cases.BaseServerTestCase): def testListEmpty(self): """ + """ self.connectClient("foo") self.connectClient("bar") self.getMessages(1) self.sendLine(2, "LIST") m = self.getMessage(2) - if m.command == "321": - # skip RPL_LISTSTART + if m.command == RPL_LISTSTART: + # skip m = self.getMessage(2) # skip local pseudo-channels listed by ngircd and ircu - while m.command == "322" and m.params[1].startswith("&"): + while m.command == RPL_LIST and m.params[1].startswith("&"): m = self.getMessage(2) self.assertNotEqual( m.command, - "322", # RPL_LIST + RPL_LIST, "LIST response gives (at least) one channel, whereas there " "is none.", ) self.assertMessageMatch( m, - command="323", # RPL_LISTEND + command=RPL_LISTEND, fail_msg="Second reply to LIST is not 322 (RPL_LIST) " "or 323 (RPL_LISTEND), or but: {msg}", ) @@ -35,6 +37,8 @@ class ListTestCase(cases.BaseServerTestCase): """When a channel exists, LIST should get it in a reply. + + """ self.connectClient("foo") self.connectClient("bar") @@ -42,34 +46,34 @@ class ListTestCase(cases.BaseServerTestCase): self.getMessages(1) self.sendLine(2, "LIST") m = self.getMessage(2) - if m.command == "321": - # skip RPL_LISTSTART + if m.command == RPL_LISTSTART: + # skip m = self.getMessage(2) self.assertNotEqual( m.command, - "323", # RPL_LISTEND + RPL_LISTEND, fail_msg="LIST response ended (ie. 323, aka RPL_LISTEND) " "without listing any channel, whereas there is one.", ) self.assertMessageMatch( m, - command="322", # RPL_LIST + command=RPL_LIST, fail_msg="Second reply to LIST is not 322 (RPL_LIST), " "nor 323 (RPL_LISTEND) but: {msg}", ) m = self.getMessage(2) # skip local pseudo-channels listed by ngircd and ircu - while m.command == "322" and m.params[1].startswith("&"): + while m.command == RPL_LIST and m.params[1].startswith("&"): m = self.getMessage(2) self.assertNotEqual( m.command, - "322", # RPL_LIST + RPL_LIST, fail_msg="LIST response gives (at least) two channels, " "whereas there is only one.", ) self.assertMessageMatch( m, - command="323", # RPL_LISTEND + command=RPL_LISTEND, fail_msg="Third reply to LIST is not 322 (RPL_LIST) " "or 323 (RPL_LISTEND), or but: {msg}", ) From af001fad2e1b5acc1b2ece2ccbe47b6761e83885 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 20 Mar 2022 14:07:46 +0100 Subject: [PATCH 3/3] Add tests for ELIST --- Makefile | 2 + irctest/controllers/plexus4.py | 2 +- irctest/controllers/unrealircd.py | 4 - irctest/server_tests/list.py | 323 +++++++++++++++++++++++++++++- irctest/specifications.py | 1 + pytest.ini | 1 + 6 files changed, 326 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index eef477a..0052a74 100644 --- a/Makefile +++ b/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 diff --git a/irctest/controllers/plexus4.py b/irctest/controllers/plexus4.py index 395b4e4..a968e56 100644 --- a/irctest/controllers/plexus4.py +++ b/irctest/controllers/plexus4.py @@ -74,7 +74,7 @@ operator {{ class Plexus4Controller(BaseHybridController): - software_name = "Hybrid" + software_name = "Plexus4" binary_name = "ircd" services_protocol = "plexus" diff --git a/irctest/controllers/unrealircd.py b/irctest/controllers/unrealircd.py index d9db353..a149fd4 100644 --- a/irctest/controllers/unrealircd.py +++ b/irctest/controllers/unrealircd.py @@ -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 diff --git a/irctest/server_tests/list.py b/irctest/server_tests/list.py index 3ef89d0..18a8b3c 100644 --- a/irctest/server_tests/list.py +++ b/irctest/server_tests/list.py @@ -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): """ @@ -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://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://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" modifiers to search for a channel that has less or more than val users, + respectively." + -- + -- 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 "Cval" + modifiers to search for a channel creation time that is higher or lower + than val." + -- + -- 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 minutes ago + + On UnrealIRCd, Plexus, and Hybrid, it is interpreted as "the channel's creation + time is a timestamp lower than minutes ago" (ie. the exact opposite) + + "C: Searching based on channel creation time, via the "Cval" + 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 "Tval" + modifiers to search for a topic time that is lower or higher than + val respectively." + -- + -- 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 "Tval" 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" diff --git a/irctest/specifications.py b/irctest/specifications.py index 8a629c7..16257fa 100644 --- a/irctest/specifications.py +++ b/irctest/specifications.py @@ -50,6 +50,7 @@ class Capabilities(enum.Enum): @enum.unique class IsupportTokens(enum.Enum): BOT = "BOT" + ELIST = "ELIST" PREFIX = "PREFIX" MONITOR = "MONITOR" STATUSMSG = "STATUSMSG" diff --git a/pytest.ini b/pytest.ini index 19518e3..34ae065 100644 --- a/pytest.ini +++ b/pytest.ini @@ -32,6 +32,7 @@ markers = # isupport tokens BOT + ELIST MONITOR PREFIX STATUSMSG