From 44996565c16e04ba3156578b33c5832ad792337d Mon Sep 17 00:00:00 2001 From: Sassan Haradji Date: Sat, 4 May 2024 23:41:49 +0400 Subject: [PATCH] feat(test): add performance tests to check it doesn't timeout in edge cases --- CHANGELOG.md | 1 + pyproject.toml | 2 +- tests/test_performance.py | 88 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 tests/test_performance.py diff --git a/CHANGELOG.md b/CHANGELOG.md index cb758c3..30dabf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - feat(core): add `view` method to `Store` to allow computing a derived value from the state only when it is accessed and caching the result until the relevant parts of the state change +- feat(test): add performance tests to check it doesn't timeout in edge cases ## Version 0.15.0 diff --git a/pyproject.toml b/pyproject.toml index 7d07d36..65c7279 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "python-redux" -version = "0.15.0" +version = "0.15.1" description = "Redux implementation for Python" authors = ["Sassan Haradji "] license = "Apache-2.0" diff --git a/tests/test_performance.py b/tests/test_performance.py new file mode 100644 index 0000000..5464a6f --- /dev/null +++ b/tests/test_performance.py @@ -0,0 +1,88 @@ +# ruff: noqa: D100, D101, D102, D103, D104, D107 + +from __future__ import annotations + +from dataclasses import replace +from typing import Generator + +import pytest +from immutable import Immutable + +from redux.basic_types import ( + BaseAction, + CompleteReducerResult, + CreateStoreOptions, + FinishAction, + FinishEvent, + InitAction, + InitializationActionError, +) +from redux.main import Store + + +class StateType(Immutable): + value: int + + +class IncrementAction(BaseAction): ... + + +Action = IncrementAction | InitAction | FinishAction + + +def reducer( + state: StateType | None, + action: Action, +) -> StateType | CompleteReducerResult[StateType, Action, FinishEvent]: + if state is None: + if isinstance(action, InitAction): + return StateType(value=0) + raise InitializationActionError(action) + + if isinstance(action, IncrementAction): + return replace(state, value=state.value + 1) + + return state + + +class StoreType(Store[StateType, Action, FinishEvent]): + @property + def state(self: StoreType) -> StateType | None: + return self._state + + +@pytest.fixture() +def store() -> Generator[StoreType, None, None]: + store = StoreType(reducer, options=CreateStoreOptions(auto_init=True)) + yield store + + store.dispatch(FinishAction()) + + +# These tests will timeout if they take a long time to run, indicating a performance +# issue. + + +def test_simple_dispatch(store: StoreType) -> None: + count = 100000 + for _ in range(count): + store.dispatch(IncrementAction()) + + assert store.state is not None + assert store.state.value == count + + +def test_dispatch_with_subscriptions(store: StoreType) -> None: + for _ in range(1000): + + def callback(_: StateType | None) -> None: + pass + + store.subscribe(callback) + + count = 500 + for _ in range(count): + store.dispatch(IncrementAction()) + + assert store.state is not None + assert store.state.value == count