]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Set timeout for WatchLog per-instance rather than per-call
authorNicki Křížek <nicki@isc.org>
Mon, 16 Jun 2025 15:12:47 +0000 (17:12 +0200)
committerNicki Křížek <nicki@isc.org>
Fri, 18 Jul 2025 09:32:41 +0000 (11:32 +0200)
To simplify usage of multiple wait_for_*() calls, configure the timeout
value for the WatchLog instance, rather than specifying it for each
call.

This is a preparation/cleanup for implementing multiple wait_for_*()
calls in subsequent commits.

bin/tests/system/isctest/instance.py
bin/tests/system/isctest/log/watchlog.py
bin/tests/system/xferquota/tests_xferquota.py

index adbe2f8fb43645dc0fbb1e99d528fb7cd18e9ad4..ee2916b9e6dbdab3b0c16182052585983b73bb95 100644 (file)
@@ -183,19 +183,23 @@ class NamedInstance:
         debug(f"update of zone {zone} to server {self.ip} successful")
         return response
 
-    def watch_log_from_start(self) -> WatchLogFromStart:
+    def watch_log_from_start(
+        self, timeout: float = WatchLogFromStart.DEFAULT_TIMEOUT
+    ) -> WatchLogFromStart:
         """
         Return an instance of the `WatchLogFromStart` context manager for this
         `named` instance's log file.
         """
-        return WatchLogFromStart(self.log.path)
+        return WatchLogFromStart(self.log.path, timeout)
 
-    def watch_log_from_here(self) -> WatchLogFromHere:
+    def watch_log_from_here(
+        self, timeout: float = WatchLogFromHere.DEFAULT_TIMEOUT
+    ) -> WatchLogFromHere:
         """
         Return an instance of the `WatchLogFromHere` context manager for this
         `named` instance's log file.
         """
-        return WatchLogFromHere(self.log.path)
+        return WatchLogFromHere(self.log.path, timeout)
 
     def reconfigure(self) -> None:
         """
index 0ac18477ac9ba434249811bf64bcaf86c1ad785d..8173c92c5640cfe0d8a59ae9c07d2ed18332356d 100644 (file)
@@ -65,9 +65,12 @@ class WatchLog(abc.ABC):
     by the `NamedInstance` class (see below for recommended usage patterns).
     """
 
-    def __init__(self, path: str) -> None:
+    DEFAULT_TIMEOUT = 10.0
+
+    def __init__(self, path: str, timeout: float = DEFAULT_TIMEOUT) -> None:
         """
         `path` is the path to the log file to watch.
+        `timeout` is the number of seconds (float) to wait for each wait call.
 
         Every instance of this class must call one of the `wait_for_*()`
         methods exactly once or else an `Exception` is thrown.
@@ -78,29 +81,32 @@ class WatchLog(abc.ABC):
           ...
         isctest.log.watchlog.WatchLogException: wait_for_*() was not called
 
-        >>> with WatchLogFromHere("/dev/null") as watcher:
+        >>> with WatchLogFromHere("/dev/null", timeout=0.1) as watcher:
         ...     try:
-        ...         watcher.wait_for_line("foo", timeout=0.1)
+        ...         watcher.wait_for_line("foo")
         ...     except TimeoutError:
         ...         pass
         ...     try:
-        ...         watcher.wait_for_lines({"bar": 42}, timeout=0.1)
+        ...         watcher.wait_for_lines({"bar": 42})
         ...     except TimeoutError:
         ...         pass
         Traceback (most recent call last):
           ...
         isctest.log.watchlog.WatchLogException: wait_for_*() was already called
 
-        >>> with WatchLogFromHere("/dev/null") as watcher:
-        ...     watcher.wait_for_line("foo", timeout=0)
+        >>> with WatchLogFromHere("/dev/null", timeout=0.0) as watcher:
+        ...     watcher.wait_for_line("foo")
         Traceback (most recent call last):
           ...
-        AssertionError: Do not use this class unless you want to WAIT for something.
+        isctest.log.watchlog.WatchLogException: timeout must be greater than 0
         """
         self._fd = None  # type: Optional[TextIO]
         self._path = path
         self._wait_function_called = False
         self._linebuf = ""
+        if timeout <= 0.0:
+            raise WatchLogException("timeout must be greater than 0")
+        self._timeout = timeout
 
     def _readline(self) -> Optional[str]:
         """
@@ -131,14 +137,14 @@ class WatchLog(abc.ABC):
                 return
             yield line
 
-    def wait_for_line(self, string: str, timeout: int = 10) -> None:
+    def wait_for_line(self, string: str) -> None:
         """
         Block execution until a line containing the provided `string` appears
         in the log file.  Return `None` once the line is found or raise a
-        `TimeoutError` after `timeout` seconds (default: 10) if `string` does
-        not appear in the log file (strings and regular expressions are
-        supported).  (Catching this exception is discouraged as it indicates
-        that the test code did not behave as expected.)
+        `TimeoutError` after timeout if `string` does not appear in the log
+        file (strings and regular expressions are supported).  (Catching this
+        exception is discouraged as it indicates that the test code did not
+        behave as expected.)
 
         Recommended use:
 
@@ -162,13 +168,13 @@ class WatchLog(abc.ABC):
         >>> with tempfile.NamedTemporaryFile("w") as file:
         ...     print("foo", file=file, flush=True)
         ...     with WatchLogFromStart(file.name) as watcher:
-        ...         retval = watcher.wait_for_line("foo", timeout=1)
+        ...         retval = watcher.wait_for_line("foo")
         >>> print(retval)
         None
         >>> with tempfile.NamedTemporaryFile("w") as file:
         ...     with WatchLogFromStart(file.name) as watcher:
         ...         print("foo", file=file, flush=True)
-        ...         retval = watcher.wait_for_line("foo", timeout=1)
+        ...         retval = watcher.wait_for_line("foo")
         >>> print(retval)
         None
 
@@ -178,8 +184,8 @@ class WatchLog(abc.ABC):
         >>> import tempfile
         >>> with tempfile.NamedTemporaryFile("w") as file:
         ...     print("foo", file=file, flush=True)
-        ...     with WatchLogFromHere(file.name) as watcher:
-        ...         watcher.wait_for_line("foo", timeout=1) #doctest: +ELLIPSIS
+        ...     with WatchLogFromHere(file.name, timeout=0.1) as watcher:
+        ...         watcher.wait_for_line("foo") #doctest: +ELLIPSIS
         Traceback (most recent call last):
           ...
         TimeoutError: Timeout reached watching ...
@@ -187,15 +193,13 @@ class WatchLog(abc.ABC):
         ...     print("foo", file=file, flush=True)
         ...     with WatchLogFromHere(file.name) as watcher:
         ...         print("foo", file=file, flush=True)
-        ...         retval = watcher.wait_for_line("foo", timeout=1)
+        ...         retval = watcher.wait_for_line("foo")
         >>> print(retval)
         None
         """
-        return self._wait_for({string: None}, timeout)
+        return self._wait_for({string: None})
 
-    def wait_for_lines(
-        self, strings: Dict[Union[str, Pattern], Any], timeout: int = 10
-    ) -> None:
+    def wait_for_lines(self, strings: Dict[Union[str, Pattern], Any]) -> None:
         """
         Block execution until a line of interest appears in the log file.  This
         function is a "multi-match" variant of `wait_for_line()` which is
@@ -205,10 +209,9 @@ class WatchLog(abc.ABC):
         `strings` is a `dict` associating each string to look for with the
         value this function should return when that string is found in the log
         file (strings and regular expressions are supported).  If none of the
-        `strings` being looked for appear in the log file after `timeout`
-        seconds, a `TimeoutError` is raised.  (Catching this exception is
-        discouraged as it indicates that the test code did not behave as
-        expected.)
+        `strings` being looked for appear in the log file after timeout, a
+        `TimeoutError` is raised.  (Catching this exception is discouraged as
+        it indicates that the test code did not behave as expected.)
 
         Since `strings` is a `dict` and preserves key order (in CPython 3.6 as
         implementation detail, since 3.7 by language design), each line is
@@ -241,28 +244,28 @@ class WatchLog(abc.ABC):
         >>> with tempfile.NamedTemporaryFile("w") as file:
         ...     print("foo", file=file, flush=True)
         ...     with WatchLogFromStart(file.name) as watcher:
-        ...         retval1 = watcher.wait_for_lines(triggers, timeout=1)
+        ...         retval1 = watcher.wait_for_lines(triggers)
         ...     with WatchLogFromHere(file.name) as watcher:
         ...         print("bar", file=file, flush=True)
-        ...         retval2 = watcher.wait_for_lines(triggers, timeout=1)
+        ...         retval2 = watcher.wait_for_lines(triggers)
         >>> print(retval1)
         42
         >>> print(retval2)
         1337
         """
-        return self._wait_for(strings, timeout)
+        return self._wait_for(strings)
 
-    def _wait_for(self, patterns: Dict[Union[str, Pattern], Any], timeout: int) -> Any:
+    def _wait_for(self, patterns: Dict[Union[str, Pattern], Any]) -> Any:
         """
         Block execution until one of the `strings` being looked for appears in
         the log file.  Raise a `TimeoutError` if none of the `strings` being
-        looked for are found in the log file for `timeout` seconds.
+        looked for are found in the log file after timeout.
         """
         if self._wait_function_called:
             raise WatchLogException("wait_for_*() was already called")
         self._wait_function_called = True
-        assert timeout, "Do not use this class unless you want to WAIT for something."
-        deadline = time.monotonic() + timeout
+
+        deadline = time.monotonic() + self._timeout
         while time.monotonic() < deadline:
             for line in self._readlines():
                 for string, retval in patterns.items():
index 3ca538193a810a577680e0c60386fdc54a547422..c31700e4a263bdd368c63f1d8caad89ee6a570c3 100644 (file)
@@ -75,9 +75,6 @@ def test_xferquota(named_port, servers):
         f"transfer of 'changing/IN' from 10.53.0.1#{named_port}: "
         f"Transfer completed: .*\\(serial 2\\)"
     )
-    with servers["ns2"].watch_log_from_start() as watcher:
-        watcher.wait_for_line(
-            pattern,
-            timeout=30,
-        )
+    with servers["ns2"].watch_log_from_start(timeout=30) as watcher:
+        watcher.wait_for_line(pattern)
     query_and_compare(a_msg)