Skip to content

Commit

Permalink
Test start and stop methods
Browse files Browse the repository at this point in the history
Got in a bit of a pickle to make the thread initialization testable.
When `client.start()` was called, a thread was spawned, so I had to
implement a way to stop these threads from executing during other
tests (hence `client.stop()`)

All other tests continue to call `_run_probes` directly because the
resulting behaviour is easier to reason about.
  • Loading branch information
unflxw committed Mar 15, 2024
1 parent 14e5a16 commit 3f14e9b
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 13 deletions.
9 changes: 4 additions & 5 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
from opentelemetry.trace import set_tracer_provider

from appsignal import probes
from appsignal.agent import agent
from appsignal.opentelemetry import METRICS_PREFERRED_TEMPORALITY

Expand Down Expand Up @@ -87,13 +88,11 @@ def remove_logging_handlers_after_tests():


@pytest.fixture(scope="function", autouse=True)
def remove_probes_after_tests():
def stop_and_clear_probes_after_tests():
yield

from appsignal.probes import _probes, _probe_states

_probes.clear()
_probe_states.clear()
probes.stop()
probes.clear()


@pytest.fixture(scope="function", autouse=True)
Expand Down
31 changes: 25 additions & 6 deletions src/appsignal/probes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import logging
from inspect import signature
from threading import Lock, Thread
from time import gmtime, sleep
from typing import Any, Callable, NoReturn, Optional, TypeVar, Union, cast
from threading import Event, Lock, Thread
from time import gmtime
from typing import Any, Callable, Optional, TypeVar, Union, cast


T = TypeVar("T")
Expand All @@ -15,6 +15,7 @@
_probe_states: dict[str, Any] = {}
_lock: Lock = Lock()
_thread: Thread | None = None
_stop_event: Event = Event()


def start() -> None:
Expand All @@ -24,12 +25,15 @@ def start() -> None:
_thread.start()


def _minutely_loop() -> NoReturn:
sleep(_initial_wait_time())
def _minutely_loop() -> None:
wait_time = _initial_wait_time()

while True:
if _stop_event.wait(timeout=wait_time):
break

_run_probes()
sleep(_wait_time())
wait_time = _wait_time()


def _run_probes() -> None:
Expand Down Expand Up @@ -88,3 +92,18 @@ def unregister(name: str) -> None:
del _probes[name]
if name in _probe_states:
del _probe_states[name]


def stop() -> None:
global _thread
if _thread is not None:
_stop_event.set()
_thread.join()
_thread = None
_stop_event.clear()


def clear() -> None:
with _lock:
_probes.clear()
_probe_states.clear()
33 changes: 31 additions & 2 deletions tests/test_probes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
from time import sleep
from typing import Any, Callable, cast

from appsignal.probes import _probe_states, _probes, _run_probes, register, unregister
from appsignal.probes import (
_probe_states,
_probes,
_run_probes,
register,
start,
stop,
unregister,
)


def test_register(mocker):
Expand Down Expand Up @@ -32,7 +41,7 @@ def test_register_with_state(mocker):
probe.assert_called_with("state")


def test_register_signatures(mocker):
def test_register_signatures():
# `mocker.Mock` is not used here because we want to test against
# specific function signatures.

Expand Down Expand Up @@ -107,3 +116,23 @@ def test_unregister(mocker):
assert "probe_name" not in _probes
assert "probe_name" not in _probe_states
probe.assert_called_once_with(None)


def test_start_stop(mocker):
mocker.patch("appsignal.probes._initial_wait_time").return_value = 0.001
mocker.patch("appsignal.probes._wait_time").return_value = 0.001

probe = mocker.Mock()
register("probe_name", probe)
start()

sleep(0.05)

probe.assert_called()

stop()
call_count = probe.call_count

sleep(0.05)

assert probe.call_count == call_count

0 comments on commit 3f14e9b

Please sign in to comment.