Skip to content

Commit 895bc49

Browse files
authored
Merge pull request #157 from robotpy/alert-tests
wpilib: Add context support and tests to Alert
2 parents 1bf304b + e52deb0 commit 895bc49

File tree

3 files changed

+245
-2
lines changed

3 files changed

+245
-2
lines changed

subprojects/robotpy-wpilib/gen/Alert.yml

+12
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,15 @@ classes:
1818
SetText:
1919
GetText:
2020
GetType:
21+
inline_code: |
22+
.def("close", [](frc::Alert &self) {
23+
py::gil_scoped_release release;
24+
self.Set(false);
25+
}, py::doc("Disables the alert"))
26+
.def("__enter__", [](frc::Alert &self) -> frc::Alert& {
27+
return self;
28+
})
29+
.def("__exit__", [](frc::Alert &self, py::args args) {
30+
py::gil_scoped_release release;
31+
self.Set(false);
32+
})

subprojects/robotpy-wpilib/tests/conftest.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pytest
44
import ntcore
55
import wpilib
6+
from wpilib.simulation._simulation import _resetWpilibSimulationData
67

78

89
@pytest.fixture
@@ -11,7 +12,15 @@ def cfg_logging(caplog):
1112

1213

1314
@pytest.fixture(scope="function")
14-
def nt(cfg_logging):
15+
def wpilib_state():
16+
try:
17+
yield None
18+
finally:
19+
_resetWpilibSimulationData()
20+
21+
22+
@pytest.fixture(scope="function")
23+
def nt(cfg_logging, wpilib_state):
1524
instance = ntcore.NetworkTableInstance.getDefault()
1625
instance.startLocal()
1726

@@ -20,4 +29,3 @@ def nt(cfg_logging):
2029
finally:
2130
instance.stopLocal()
2231
instance._reset()
23-
wpilib._wpilib._clearSmartDashboardData()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
import typing as T
2+
3+
import pytest
4+
5+
from ntcore import NetworkTableInstance
6+
from wpilib import Alert, SmartDashboard
7+
from wpilib.simulation import pauseTiming, resumeTiming, stepTiming
8+
9+
AlertType = Alert.AlertType
10+
11+
12+
@pytest.fixture(scope="function")
13+
def group_name(nt, request):
14+
15+
group_name = f"AlertTest_{request.node.name}"
16+
yield group_name
17+
18+
SmartDashboard.updateValues()
19+
assert len(get_active_alerts(nt, group_name, AlertType.kError)) == 0
20+
assert len(get_active_alerts(nt, group_name, AlertType.kWarning)) == 0
21+
assert len(get_active_alerts(nt, group_name, AlertType.kInfo)) == 0
22+
23+
24+
def get_subscriber_for_type(
25+
nt: NetworkTableInstance, group_name: str, alert_type: AlertType
26+
):
27+
subtable_name = {
28+
AlertType.kError: "errors",
29+
AlertType.kWarning: "warnings",
30+
AlertType.kInfo: "infos",
31+
}.get(alert_type, "unknown")
32+
topic = f"/SmartDashboard/{group_name}/{subtable_name}"
33+
return nt.getStringArrayTopic(topic).subscribe([])
34+
35+
36+
def get_active_alerts(
37+
nt: NetworkTableInstance, group_name: str, alert_type: AlertType
38+
) -> T.List[str]:
39+
SmartDashboard.updateValues()
40+
with get_subscriber_for_type(nt, group_name, alert_type) as sub:
41+
return sub.get()
42+
43+
44+
def is_alert_active(
45+
nt: NetworkTableInstance, group_name: str, text: str, alert_type: AlertType
46+
):
47+
active_alerts = get_active_alerts(nt, group_name, alert_type)
48+
return text in active_alerts
49+
50+
51+
def assert_state(
52+
nt: NetworkTableInstance,
53+
group_name: str,
54+
alert_type: AlertType,
55+
expected_state: T.List[str],
56+
):
57+
assert expected_state == get_active_alerts(nt, group_name, alert_type)
58+
59+
60+
def test_set_unset_single(nt, group_name):
61+
with Alert(group_name, "one", AlertType.kError) as one:
62+
63+
assert not is_alert_active(nt, group_name, "one", AlertType.kError)
64+
assert not is_alert_active(nt, group_name, "two", AlertType.kInfo)
65+
66+
one.set(True)
67+
assert is_alert_active(nt, group_name, "one", AlertType.kError)
68+
69+
one.set(True)
70+
assert is_alert_active(nt, group_name, "one", AlertType.kError)
71+
72+
one.set(False)
73+
assert not is_alert_active(nt, group_name, "one", AlertType.kError)
74+
75+
76+
def test_set_unset_multiple(nt, group_name):
77+
with (
78+
Alert(group_name, "one", AlertType.kError) as one,
79+
Alert(group_name, "two", AlertType.kInfo) as two,
80+
):
81+
82+
assert not is_alert_active(nt, group_name, "one", AlertType.kError)
83+
assert not is_alert_active(nt, group_name, "two", AlertType.kInfo)
84+
85+
one.set(True)
86+
assert is_alert_active(nt, group_name, "one", AlertType.kError)
87+
assert not is_alert_active(nt, group_name, "two", AlertType.kInfo)
88+
89+
one.set(True)
90+
two.set(True)
91+
assert is_alert_active(nt, group_name, "one", AlertType.kError)
92+
assert is_alert_active(nt, group_name, "two", AlertType.kInfo)
93+
94+
one.set(False)
95+
assert not is_alert_active(nt, group_name, "one", AlertType.kError)
96+
assert is_alert_active(nt, group_name, "two", AlertType.kInfo)
97+
98+
99+
def test_set_is_idempotent(nt, group_name):
100+
group_name = group_name
101+
with (
102+
Alert(group_name, "A", AlertType.kInfo) as a,
103+
Alert(group_name, "B", AlertType.kInfo) as b,
104+
Alert(group_name, "C", AlertType.kInfo) as c,
105+
):
106+
107+
a.set(True)
108+
b.set(True)
109+
c.set(True)
110+
111+
start_state = get_active_alerts(nt, group_name, AlertType.kInfo)
112+
assert set(start_state) == {"A", "B", "C"}
113+
114+
b.set(True)
115+
assert_state(nt, group_name, AlertType.kInfo, start_state)
116+
117+
a.set(True)
118+
assert_state(nt, group_name, AlertType.kInfo, start_state)
119+
120+
121+
def test_close_unsets_alert(nt, group_name):
122+
group_name = group_name
123+
with Alert(group_name, "alert", AlertType.kWarning) as alert:
124+
alert.set(True)
125+
assert is_alert_active(nt, group_name, "alert", AlertType.kWarning)
126+
assert not is_alert_active(nt, group_name, "alert", AlertType.kWarning)
127+
128+
129+
def test_set_text_while_unset(nt, group_name):
130+
group_name = group_name
131+
with Alert(group_name, "BEFORE", AlertType.kInfo) as alert:
132+
assert alert.getText() == "BEFORE"
133+
alert.set(True)
134+
assert is_alert_active(nt, group_name, "BEFORE", AlertType.kInfo)
135+
alert.set(False)
136+
assert not is_alert_active(nt, group_name, "BEFORE", AlertType.kInfo)
137+
alert.setText("AFTER")
138+
assert alert.getText() == "AFTER"
139+
alert.set(True)
140+
assert not is_alert_active(nt, group_name, "BEFORE", AlertType.kInfo)
141+
assert is_alert_active(nt, group_name, "AFTER", AlertType.kInfo)
142+
143+
144+
def test_set_text_while_set(nt, group_name):
145+
with Alert(group_name, "BEFORE", AlertType.kInfo) as alert:
146+
assert alert.getText() == "BEFORE"
147+
alert.set(True)
148+
assert is_alert_active(nt, group_name, "BEFORE", AlertType.kInfo)
149+
alert.setText("AFTER")
150+
assert alert.getText() == "AFTER"
151+
assert not is_alert_active(nt, group_name, "BEFORE", AlertType.kInfo)
152+
assert is_alert_active(nt, group_name, "AFTER", AlertType.kInfo)
153+
154+
155+
def test_set_text_does_not_affect_sort(nt, group_name):
156+
pauseTiming()
157+
try:
158+
with (
159+
Alert(group_name, "A", AlertType.kInfo) as a,
160+
Alert(group_name, "B", AlertType.kInfo) as b,
161+
Alert(group_name, "C", AlertType.kInfo) as c,
162+
):
163+
164+
a.set(True)
165+
stepTiming(1)
166+
b.set(True)
167+
stepTiming(1)
168+
c.set(True)
169+
170+
expected_state = get_active_alerts(nt, group_name, AlertType.kInfo)
171+
expected_state[expected_state.index("B")] = "AFTER"
172+
173+
b.setText("AFTER")
174+
assert_state(nt, group_name, AlertType.kInfo, expected_state)
175+
finally:
176+
resumeTiming()
177+
178+
179+
def test_sort_order(nt, group_name):
180+
pauseTiming()
181+
try:
182+
with (
183+
Alert(group_name, "A", AlertType.kInfo) as a,
184+
Alert(group_name, "B", AlertType.kInfo) as b,
185+
Alert(group_name, "C", AlertType.kInfo) as c,
186+
):
187+
188+
a.set(True)
189+
assert_state(nt, group_name, AlertType.kInfo, ["A"])
190+
191+
stepTiming(1)
192+
b.set(True)
193+
assert_state(nt, group_name, AlertType.kInfo, ["B", "A"])
194+
195+
stepTiming(1)
196+
c.set(True)
197+
assert_state(nt, group_name, AlertType.kInfo, ["C", "B", "A"])
198+
199+
stepTiming(1)
200+
c.set(False)
201+
assert_state(nt, group_name, AlertType.kInfo, ["B", "A"])
202+
203+
stepTiming(1)
204+
c.set(True)
205+
assert_state(nt, group_name, AlertType.kInfo, ["C", "B", "A"])
206+
207+
stepTiming(1)
208+
a.set(False)
209+
assert_state(nt, group_name, AlertType.kInfo, ["C", "B"])
210+
211+
stepTiming(1)
212+
b.set(False)
213+
assert_state(nt, group_name, AlertType.kInfo, ["C"])
214+
215+
stepTiming(1)
216+
b.set(True)
217+
assert_state(nt, group_name, AlertType.kInfo, ["B", "C"])
218+
219+
stepTiming(1)
220+
a.set(True)
221+
assert_state(nt, group_name, AlertType.kInfo, ["A", "B", "C"])
222+
finally:
223+
resumeTiming()

0 commit comments

Comments
 (0)