Skip to content

Commit

Permalink
Improve tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dustalov committed Jul 6, 2024
1 parent a69117d commit 3d50938
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 86 deletions.
65 changes: 65 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from collections.abc import Callable
from typing import Any, NamedTuple

import evalica
import numpy as np
import numpy.typing as npt
import pandas as pd
import pytest
from hypothesis import strategies as st
from hypothesis.strategies import SearchStrategy


@pytest.fixture()
def simple() -> npt.NDArray[np.float64]:
return np.array([
[0, 1, 2, 0, 1],
[2, 0, 2, 1, 0],
[1, 2, 0, 0, 1],
[1, 2, 1, 0, 2],
[2, 0, 1, 3, 0],
], dtype=np.float64)


@pytest.fixture()
def simple_win_tie(simple: npt.NDArray[np.float64]) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
T = np.minimum(simple, simple.T).astype(np.float64) # noqa: N806
W = simple - T # noqa: N806

return W, T


@pytest.fixture()
def food() -> tuple[list[str], list[str], list[evalica.Winner]]:
df_food = pd.read_csv("food.csv", dtype=str)

xs = df_food["left"]
ys = df_food["right"]
ws = df_food["winner"].map({
"left": evalica.Winner.X,
"right": evalica.Winner.Y,
"tie": evalica.Winner.Draw,
})

return xs.tolist(), ys.tolist(), ws.tolist()


class Example(NamedTuple):
"""A tuple holding example data."""

xs: list[int]
ys: npt.NDArray[np.int64]
ws: list[evalica.Winner]


@st.composite
def xs_ys_ws(draw: Callable[[SearchStrategy[Any]], Any]) -> Example:
length = draw(st.integers(0, 5))
elements = st.lists(st.integers(0, length), min_size=length, max_size=length)
winners = st.lists(st.sampled_from(evalica.WINNERS), min_size=length, max_size=length)

return Example(
xs=draw(elements),
ys=np.array(draw(elements)),
ws=draw(winners),
)
115 changes: 31 additions & 84 deletions python/evalica/test_evalica.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
from pathlib import Path

import hypothesis.strategies as st
import numpy as np
import numpy.typing as npt
import pandas as pd
import pytest
from hypothesis import given
from hypothesis.extra.numpy import arrays

import evalica
from evalica import Winner
from conftest import Example, xs_ys_ws


def test_version() -> None:
Expand All @@ -21,24 +16,22 @@ def test_exports() -> None:
for attr in evalica.__all__:
assert hasattr(evalica, attr), f"missing attribute: {attr}"

@given(
st.lists(st.integers(0, 2), min_size=2, max_size=2),
arrays(dtype=np.int64, shape=(2,)),
)
def test_index(xs: list[int], ys: npt.NDArray[np.int64]) -> None:

@given(xs_ys_ws=xs_ys_ws())
def test_index(xs_ys_ws: Example) -> None:
xs, ys, ws = xs_ys_ws

index = evalica.index(xs, ys)

assert isinstance(index, dict)
assert len(index) == len(set(xs) | set(ys))
assert max(index.values()) == len(index) - 1
assert not xs or max(index.values()) == len(index) - 1


@given(
st.lists(st.integers(0, 2), min_size=2, max_size=2),
st.lists(st.integers(0, 2), min_size=2, max_size=2),
st.lists(st.sampled_from(evalica.WINNERS), min_size=2, max_size=2),
)
def test_matrices(xs: list[int], ys: list[int], ws: list[evalica.Winner]) -> None:
@given(xs_ys_ws=xs_ys_ws())
def test_matrices(xs_ys_ws: Example) -> None:
xs, ys, ws = xs_ys_ws

n = len(set(xs) | set(ys))

wins = sum(status in [evalica.Winner.X, evalica.Winner.Y] for status in ws)
Expand All @@ -52,36 +45,30 @@ def test_matrices(xs: list[int], ys: list[int], ws: list[evalica.Winner]) -> Non
assert result.tie_matrix.sum() == 2 * ties


@given(
st.lists(st.integers(0, 2), min_size=2, max_size=2),
st.lists(st.integers(0, 2), min_size=2, max_size=2),
st.lists(st.sampled_from(evalica.WINNERS), min_size=2, max_size=2),
)
def test_counting(xs: list[int], ys: list[int], ws: list[evalica.Winner]) -> None:
@given(xs_ys_ws=xs_ys_ws())
def test_counting(xs_ys_ws: Example) -> None:
xs, ys, ws = xs_ys_ws

result = evalica.counting(xs, ys, ws)

assert result.win_matrix.shape[0] == len(result.scores)
assert np.isfinite(result.scores).all()

@given(
st.lists(st.integers(0, 2), min_size=2, max_size=2),
st.lists(st.integers(0, 2), min_size=2, max_size=2),
st.lists(st.sampled_from(evalica.WINNERS), min_size=2, max_size=2),
)
def test_bradley_terry(xs: list[int], ys: list[int], ws: list[evalica.Winner]) -> None:
@given(xs_ys_ws=xs_ys_ws())
def test_bradley_terry(xs_ys_ws: Example) -> None:
xs, ys, ws = xs_ys_ws

result = evalica.bradley_terry(xs, ys, ws)

assert result.matrix.shape[0] == len(result.scores)
assert np.isfinite(result.scores).all()
assert result.iterations > 0


@given(
st.lists(st.integers(0, 2), min_size=2, max_size=2),
st.lists(st.integers(0, 2), min_size=2, max_size=2),
st.lists(st.sampled_from(evalica.WINNERS), min_size=2, max_size=2),
)
def test_newman(xs: list[int], ys: list[int], ws: list[evalica.Winner]) -> None:
@given(xs_ys_ws=xs_ys_ws())
def test_newman(xs_ys_ws: Example) -> None:
xs, ys, ws = xs_ys_ws

result = evalica.newman(xs, ys, ws)

assert result.win_matrix.shape[0] == len(result.scores)
Expand All @@ -91,49 +78,25 @@ def test_newman(xs: list[int], ys: list[int], ws: list[evalica.Winner]) -> None:
assert result.iterations > 0


@given(
st.lists(st.integers(0, 2), min_size=2, max_size=2),
st.lists(st.integers(0, 2), min_size=2, max_size=2),
st.lists(st.sampled_from(evalica.WINNERS), min_size=2, max_size=2),
)
def test_elo(xs: list[int], ys: list[int], ws: list[evalica.Winner]) -> None:
@given(xs_ys_ws=xs_ys_ws())
def test_elo(xs_ys_ws: Example) -> None:
xs, ys, ws = xs_ys_ws

result = evalica.elo(xs, ys, ws)

assert len(result.scores) == len(set(xs) | set(ys))
assert np.isfinite(result.scores).all()


@given(
st.lists(st.integers(0, 2), min_size=2, max_size=2),
st.lists(st.integers(0, 2), min_size=2, max_size=2),
st.lists(st.sampled_from(evalica.WINNERS), min_size=2, max_size=2),
)
def test_eigen(xs: list[int], ys: list[int], ws: list[evalica.Winner]) -> None:
@given(xs_ys_ws=xs_ys_ws())
def test_eigen(xs_ys_ws: Example) -> None:
xs, ys, ws = xs_ys_ws

result = evalica.eigen(xs, ys, ws)

assert len(result.scores) == len(set(xs) | set(ys))
assert np.isfinite(result.scores).all()


@pytest.fixture()
def simple() -> npt.NDArray[np.float64]:
return np.array([
[0, 1, 2, 0, 1],
[2, 0, 2, 1, 0],
[1, 2, 0, 0, 1],
[1, 2, 1, 0, 2],
[2, 0, 1, 3, 0],
], dtype=np.float64)


@pytest.fixture()
def simple_win_tie(simple: npt.NDArray[np.float64]) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
T = np.minimum(simple, simple.T).astype(np.float64) # noqa: N806
W = simple - T # noqa: N806

return W, T


def test_bradley_terry_simple(simple: npt.NDArray[np.float64], tolerance: float = 1e-4) -> None:
p_naive, _ = evalica.bradley_terry_naive(simple, tolerance)
p, _ = evalica.py_bradley_terry(simple, tolerance, 100) # type: ignore[attr-defined]
Expand All @@ -150,22 +113,6 @@ def test_newman_simple(simple_win_tie: tuple[npt.NDArray[np.float64], npt.NDArra

assert p == pytest.approx(p_naive, abs=tolerance)


@pytest.fixture()
def food() -> tuple[list[str], list[str], list[evalica.Winner]]:
df_food = pd.read_csv(Path().parent.parent / "food.csv", dtype=str)

xs = df_food["left"]
ys = df_food["right"]
ws = df_food["winner"].map({
"left": Winner.X,
"right": Winner.Y,
"tie": Winner.Draw,
})

return xs.tolist(), ys.tolist(), ws.tolist()


def test_bradley_terry_food(food: tuple[list[str], list[str], list[evalica.Winner]]) -> None:
xs, ys, ws = food

Expand Down
4 changes: 3 additions & 1 deletion src/elo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ pub fn elo(
ws.len()
);

assert!(!xs.is_empty(), "empty inputs");
if xs.is_empty() {
return Array1::zeros(0);
}

let n = 1 + std::cmp::max(*xs.iter().max().unwrap(), *ys.iter().max().unwrap());

Expand Down
4 changes: 3 additions & 1 deletion src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ pub fn matrices(
ws.len()
);

assert!(!xs.is_empty(), "empty inputs");
if xs.is_empty() {
return (Array2::zeros((0, 0)), Array2::zeros((0, 0)));
}

let n = 1 + std::cmp::max(*xs.iter().max().unwrap(), *ys.iter().max().unwrap());

Expand Down

0 comments on commit 3d50938

Please sign in to comment.