test(pyargus): add general signal tests
This commit is contained in:
parent
5489ddbd09
commit
ac5867e7b0
2 changed files with 75 additions and 36 deletions
|
|
@ -136,10 +136,10 @@ macro_rules! impl_signals {
|
||||||
/// Create a new signal from some finite number of samples
|
/// Create a new signal from some finite number of samples
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
fn from_samples(_: &PyType, samples: Vec<(f64, $ty)>) -> PyResult<Py<Self>> {
|
fn from_samples(_: &PyType, samples: Vec<(f64, $ty)>) -> PyResult<Py<Self>> {
|
||||||
let ret: Signal<$ty> = samples
|
let ret: Signal::<$ty> = Signal::<$ty>::try_from_iter(samples
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(t, v)| (Duration::from_secs_f64(t), v))
|
.map(|(t, v)| (Duration::try_from_secs_f64(t).unwrap_or_else(|err| panic!("Value = {}, {}", t, err)), v))
|
||||||
.collect();
|
).map_err(PyArgusError::from)?;
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
Py::new(
|
Py::new(
|
||||||
py,
|
py,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
from typing import List, Tuple, Type, Union
|
from typing import List, Type, Union
|
||||||
|
|
||||||
from hypothesis import given, note
|
import pytest
|
||||||
|
from hypothesis import Verbosity, given, note, settings
|
||||||
from hypothesis import strategies as st
|
from hypothesis import strategies as st
|
||||||
from hypothesis.strategies import composite
|
from hypothesis.strategies import SearchStrategy, composite
|
||||||
|
|
||||||
import argus
|
import argus
|
||||||
|
|
||||||
|
|
@ -10,7 +11,7 @@ AllowedDtype = Union[bool, int, float]
|
||||||
|
|
||||||
|
|
||||||
@composite
|
@composite
|
||||||
def samples(draw, *, min_size: int, max_size: int, dtype: Type[AllowedDtype]):
|
def gen_samples(draw: st.DrawFn, *, min_size: int, max_size: int, dtype: Type[AllowedDtype]):
|
||||||
"""
|
"""
|
||||||
Generate arbitrary samples for a signal where the time stamps are strictly
|
Generate arbitrary samples for a signal where the time stamps are strictly
|
||||||
monotonically increasing
|
monotonically increasing
|
||||||
|
|
@ -27,44 +28,82 @@ def samples(draw, *, min_size: int, max_size: int, dtype: Type[AllowedDtype]):
|
||||||
raise ValueError(f"invalid dtype {dtype}")
|
raise ValueError(f"invalid dtype {dtype}")
|
||||||
|
|
||||||
values = draw(st.lists(elements, min_size=min_size, max_size=max_size))
|
values = draw(st.lists(elements, min_size=min_size, max_size=max_size))
|
||||||
return draw(
|
xs = draw(
|
||||||
st.lists(st.floats(min_value=0), unique=True, min_size=len(values), max_size=len(values))
|
st.lists(st.integers(min_value=0, max_value=2**32 - 1), unique=True, min_size=len(values), max_size=len(values))
|
||||||
.map(lambda t: sorted(t))
|
.map(lambda t: map(float, sorted(set(t))))
|
||||||
.map(lambda t: list(zip(t, values)))
|
.map(lambda t: list(zip(t, values)))
|
||||||
)
|
)
|
||||||
|
return xs
|
||||||
|
|
||||||
|
|
||||||
@composite
|
@composite
|
||||||
def samples_and_indices(
|
def draw_index(draw: st.DrawFn, vec: List) -> int:
|
||||||
draw: st.DrawFn, *, min_size: int, max_size: int
|
if len(vec) > 0:
|
||||||
) -> Tuple[List[Tuple[float, AllowedDtype]], int, int, Type[AllowedDtype]]:
|
return draw(st.integers(min_value=0, max_value=len(vec) - 1))
|
||||||
"""
|
|
||||||
Generate an arbitrary list of samples and two indices within the list
|
|
||||||
"""
|
|
||||||
dtype = draw(st.one_of(st.just(bool), st.just(int), st.just(float)))
|
|
||||||
xs = draw(samples(min_size=min_size, max_size=max_size, dtype=dtype))
|
|
||||||
if len(xs) > 0:
|
|
||||||
i0 = draw(st.integers(min_value=0, max_value=len(xs) - 1))
|
|
||||||
i1 = draw(st.integers(min_value=0, max_value=len(xs) - 1))
|
|
||||||
else:
|
else:
|
||||||
i0 = draw(st.just(0))
|
return draw(st.just(0))
|
||||||
i1 = draw(st.just(0))
|
|
||||||
|
|
||||||
return (xs, i0, i1, dtype)
|
|
||||||
|
|
||||||
|
|
||||||
@given(samples_and_indices(min_size=0, max_size=100))
|
def gen_dtype() -> SearchStrategy[Type[AllowedDtype]]:
|
||||||
def test_correctly_create_signals(data: Tuple[List[Tuple[float, AllowedDtype]], int, int, Type[AllowedDtype]]) -> None:
|
return st.one_of(st.just(bool), st.just(int), st.just(float))
|
||||||
samples: List[Tuple[float, AllowedDtype]] = data[0]
|
|
||||||
a: int = data[1]
|
|
||||||
b: int = data[2]
|
@settings(verbosity=Verbosity.verbose)
|
||||||
dtype: Type[AllowedDtype] = data[3]
|
@given(st.data())
|
||||||
|
def test_correctly_create_signals(data: st.DataObject) -> None:
|
||||||
|
dtype = data.draw(gen_dtype())
|
||||||
|
xs = data.draw(gen_samples(min_size=0, max_size=100, dtype=dtype))
|
||||||
|
|
||||||
|
note(f"Samples: {gen_samples}")
|
||||||
|
signal = argus.signal(dtype, data=xs)
|
||||||
|
if len(xs) > 0:
|
||||||
|
expected_start_time = xs[0][0]
|
||||||
|
expected_end_time = xs[-1][0]
|
||||||
|
|
||||||
|
actual_start_time = signal.start_time
|
||||||
|
actual_end_time = signal.end_time
|
||||||
|
|
||||||
|
assert actual_start_time is not None
|
||||||
|
assert actual_start_time == expected_start_time
|
||||||
|
assert actual_end_time is not None
|
||||||
|
assert actual_end_time == expected_end_time
|
||||||
|
|
||||||
note(f"Samples: {samples}")
|
|
||||||
signal = argus.signal(dtype, data=samples)
|
|
||||||
if len(samples) > 0:
|
|
||||||
assert a < len(samples)
|
|
||||||
assert b < len(samples)
|
|
||||||
else:
|
else:
|
||||||
assert signal.is_empty()
|
assert signal.is_empty()
|
||||||
assert signal.at(0) is None
|
assert signal.at(0) is None
|
||||||
|
|
||||||
|
|
||||||
|
@settings(verbosity=Verbosity.verbose)
|
||||||
|
@given(st.data())
|
||||||
|
def test_signal_at(data: st.DataObject) -> None:
|
||||||
|
dtype = data.draw(gen_dtype())
|
||||||
|
xs = data.draw(gen_samples(min_size=10, max_size=100, dtype=dtype))
|
||||||
|
a = data.draw(draw_index(xs))
|
||||||
|
|
||||||
|
assert len(xs) > 2
|
||||||
|
assert a < len(xs)
|
||||||
|
|
||||||
|
signal = argus.signal(dtype, data=xs)
|
||||||
|
|
||||||
|
at, expected_val = xs[a]
|
||||||
|
actual_val = signal.at(at)
|
||||||
|
|
||||||
|
assert actual_val is not None
|
||||||
|
assert actual_val == expected_val
|
||||||
|
|
||||||
|
|
||||||
|
@given(st.data())
|
||||||
|
def test_signal_create_should_fail(data: st.DataObject) -> None:
|
||||||
|
dtype = data.draw(gen_dtype())
|
||||||
|
xs = data.draw(gen_samples(min_size=10, max_size=100, dtype=dtype))
|
||||||
|
a = data.draw(draw_index(xs))
|
||||||
|
b = data.draw(draw_index(xs))
|
||||||
|
|
||||||
|
assert len(xs) > 2
|
||||||
|
assert a < len(xs)
|
||||||
|
assert b < len(xs)
|
||||||
|
# Swap two indices in the samples
|
||||||
|
xs[b], xs[a] = xs[a], xs[b]
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
_ = argus.signal(dtype, data=xs)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue