|
4 | 4 | """Tests for the collections module.""" |
5 | 5 |
|
6 | 6 |
|
7 | | -from frequenz.core.collections import Bounds |
| 7 | +from typing import Self |
8 | 8 |
|
| 9 | +import pytest |
9 | 10 |
|
10 | | -def test_bounds_contains() -> None: |
11 | | - """Tests with complete bounds.""" |
12 | | - bounds = Bounds(lower=10.0, upper=100.0) |
13 | | - assert 50.0 in bounds # within |
14 | | - assert 10.0 in bounds # at lower |
15 | | - assert 100.0 in bounds # at upper |
16 | | - assert 9.0 not in bounds # below lower |
17 | | - assert 101.0 not in bounds # above upper |
| 11 | +from frequenz.core.collections import Bounds, LessThanComparable |
18 | 12 |
|
19 | 13 |
|
20 | | -def test_bounds_contains_no_lower() -> None: |
21 | | - """Tests without lower bound.""" |
22 | | - bounds_no_lower = Bounds(lower=None, upper=100.0) |
23 | | - assert 50.0 in bounds_no_lower # within upper |
24 | | - assert 100.0 in bounds_no_lower # at upper |
25 | | - assert 101.0 not in bounds_no_lower # above upper |
| 14 | +class CustomComparable: |
| 15 | + """A custom comparable class.""" |
26 | 16 |
|
| 17 | + def __init__(self, value: int) -> None: |
| 18 | + """Initialize this instance.""" |
| 19 | + self.value = value |
27 | 20 |
|
28 | | -def test_bounds_contains_no_upper() -> None: |
29 | | - """Tests without upper bound.""" |
30 | | - bounds_no_upper = Bounds(lower=10.0, upper=None) |
31 | | - assert 50.0 in bounds_no_upper # within lower |
32 | | - assert 10.0 in bounds_no_upper # at lower |
33 | | - assert 9.0 not in bounds_no_upper # below lower |
| 21 | + def __lt__(self, other: Self) -> bool: |
| 22 | + """Return whether this instance is less than other.""" |
| 23 | + return self.value < other.value |
34 | 24 |
|
| 25 | + def __eq__(self, other: object) -> bool: |
| 26 | + """Return whether this instance is equal to other.""" |
| 27 | + if not isinstance(other, CustomComparable): |
| 28 | + return False |
| 29 | + return self.value == other.value |
35 | 30 |
|
36 | | -def test_bounds_contains_no_bounds() -> None: |
37 | | - """Tests with no bounds.""" |
38 | | - bounds_no_bounds: Bounds[float | None] = Bounds(lower=None, upper=None) |
39 | | - assert 50.0 in bounds_no_bounds # any value within bounds |
| 31 | + def __repr__(self) -> str: |
| 32 | + """Return a string representation of this instance.""" |
| 33 | + return str(self.value) |
| 34 | + |
| 35 | + |
| 36 | +@pytest.mark.parametrize( |
| 37 | + "lower, upper, within, at_lower, at_upper, below_lower, above_upper", |
| 38 | + [ |
| 39 | + (10.0, 100.0, 50.0, 10.0, 100.0, 9.0, 101.0), |
| 40 | + ( |
| 41 | + CustomComparable(10), |
| 42 | + CustomComparable(100), |
| 43 | + CustomComparable(50), |
| 44 | + CustomComparable(10), |
| 45 | + CustomComparable(100), |
| 46 | + CustomComparable(9), |
| 47 | + CustomComparable(101), |
| 48 | + ), |
| 49 | + ], |
| 50 | +) |
| 51 | +def test_bounds_contains( # pylint: disable=too-many-arguments |
| 52 | + lower: LessThanComparable, |
| 53 | + upper: LessThanComparable, |
| 54 | + within: LessThanComparable, |
| 55 | + at_lower: LessThanComparable, |
| 56 | + at_upper: LessThanComparable, |
| 57 | + below_lower: LessThanComparable, |
| 58 | + above_upper: LessThanComparable, |
| 59 | +) -> None: |
| 60 | + """Test if a value is within the bounds.""" |
| 61 | + bounds = Bounds(lower=lower, upper=upper) |
| 62 | + assert within in bounds # within |
| 63 | + assert at_lower in bounds # at lower |
| 64 | + assert at_upper in bounds # at upper |
| 65 | + assert below_lower not in bounds # below lower |
| 66 | + assert above_upper not in bounds # above upper |
| 67 | + |
| 68 | + |
| 69 | +@pytest.mark.parametrize( |
| 70 | + "upper, within, at_upper, above_upper", |
| 71 | + [ |
| 72 | + (100.0, 50.0, 100.0, 101.0), |
| 73 | + ( |
| 74 | + CustomComparable(100), |
| 75 | + CustomComparable(50), |
| 76 | + CustomComparable(100), |
| 77 | + CustomComparable(101), |
| 78 | + ), |
| 79 | + ], |
| 80 | +) |
| 81 | +def test_bounds_contains_no_lower( |
| 82 | + upper: LessThanComparable, |
| 83 | + within: LessThanComparable, |
| 84 | + at_upper: LessThanComparable, |
| 85 | + above_upper: LessThanComparable, |
| 86 | +) -> None: |
| 87 | + """Test if a value is within the bounds with no lower bound.""" |
| 88 | + bounds_no_lower = Bounds(lower=None, upper=upper) |
| 89 | + assert within in bounds_no_lower # within upper |
| 90 | + assert at_upper in bounds_no_lower # at upper |
| 91 | + assert above_upper not in bounds_no_lower # above upper |
| 92 | + |
| 93 | + |
| 94 | +@pytest.mark.parametrize( |
| 95 | + "lower, within, at_lower, below_lower", |
| 96 | + [ |
| 97 | + (10.0, 50.0, 10.0, 9.0), |
| 98 | + ( |
| 99 | + CustomComparable(10), |
| 100 | + CustomComparable(50), |
| 101 | + CustomComparable(10), |
| 102 | + CustomComparable(9), |
| 103 | + ), |
| 104 | + ], |
| 105 | +) |
| 106 | +def test_bounds_contains_no_upper( |
| 107 | + lower: LessThanComparable, |
| 108 | + within: LessThanComparable, |
| 109 | + at_lower: LessThanComparable, |
| 110 | + below_lower: LessThanComparable, |
| 111 | +) -> None: |
| 112 | + """Test if a value is within the bounds with no upper bound.""" |
| 113 | + bounds_no_upper = Bounds(lower=lower, upper=None) |
| 114 | + assert within in bounds_no_upper # within lower |
| 115 | + assert at_lower in bounds_no_upper # at lower |
| 116 | + assert below_lower not in bounds_no_upper # below lower |
| 117 | + |
| 118 | + |
| 119 | +@pytest.mark.parametrize( |
| 120 | + "value", |
| 121 | + [ |
| 122 | + 50.0, |
| 123 | + 10.0, |
| 124 | + -10.0, |
| 125 | + CustomComparable(50), |
| 126 | + CustomComparable(10), |
| 127 | + CustomComparable(-10), |
| 128 | + ], |
| 129 | +) |
| 130 | +def test_bounds_contains_no_bounds(value: LessThanComparable) -> None: |
| 131 | + """Test if a value is within the bounds with no bounds.""" |
| 132 | + bounds_no_bounds: Bounds[LessThanComparable | None] = Bounds(lower=None, upper=None) |
| 133 | + assert value in bounds_no_bounds # any value within bounds |
0 commit comments