Skip to content

Commit 6a38e71

Browse files
spilkey-ciscoyxieca
authored andcommitted
Default implementation of under/over speed checks (#382)
* Default implementation of under/over speed checks * Update comment, remove requirement for float conversion * Fan test coverage
1 parent 9f2f61d commit 6a38e71

File tree

2 files changed

+204
-0
lines changed

2 files changed

+204
-0
lines changed

sonic_platform_base/fan_base.py

+46
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,52 @@ def get_speed_tolerance(self):
6666
"""
6767
raise NotImplementedError
6868

69+
def is_under_speed(self):
70+
"""
71+
Calculates if the fan speed is under the tolerated low speed threshold
72+
73+
Default calculation requires get_speed_tolerance to be implemented, and checks
74+
if the current fan speed (expressed as a percentage) is lower than <get_speed_tolerance>
75+
percent below the target fan speed (expressed as a percentage)
76+
77+
Returns:
78+
A boolean, True if fan speed is under the low threshold, False if not
79+
"""
80+
speed = self.get_speed()
81+
target_speed = self.get_target_speed()
82+
tolerance = self.get_speed_tolerance()
83+
84+
for param, value in (('speed', speed), ('target speed', target_speed), ('speed tolerance', tolerance)):
85+
if not isinstance(value, int):
86+
raise TypeError(f'Fan {param} is not an integer value: {param}={value}')
87+
if value < 0 or value > 100:
88+
raise ValueError(f'Fan {param} is not a valid percentage value: {param}={value}')
89+
90+
return speed * 100 < target_speed * (100 - tolerance)
91+
92+
def is_over_speed(self):
93+
"""
94+
Calculates if the fan speed is over the tolerated high speed threshold
95+
96+
Default calculation requires get_speed_tolerance to be implemented, and checks
97+
if the current fan speed (expressed as a percentage) is higher than <get_speed_tolerance>
98+
percent above the target fan speed (expressed as a percentage)
99+
100+
Returns:
101+
A boolean, True if fan speed is over the high threshold, False if not
102+
"""
103+
speed = self.get_speed()
104+
target_speed = self.get_target_speed()
105+
tolerance = self.get_speed_tolerance()
106+
107+
for param, value in (('speed', speed), ('target speed', target_speed), ('speed tolerance', tolerance)):
108+
if not isinstance(value, int):
109+
raise TypeError(f'Fan {param} is not an integer value: {param}={value}')
110+
if value < 0 or value > 100:
111+
raise ValueError(f'Fan {param} is not a valid percentage value: {param}={value}')
112+
113+
return speed * 100 > target_speed * (100 + tolerance)
114+
69115
def set_speed(self, speed):
70116
"""
71117
Sets the fan speed

tests/fan_base_test.py

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
'''
2+
Test FanBase module
3+
'''
4+
5+
from unittest import mock
6+
from sonic_platform_base.fan_base import FanBase
7+
8+
class TestFanBase:
9+
'''
10+
Collection of FanBase test methods
11+
'''
12+
13+
@staticmethod
14+
def test_is_under_speed():
15+
'''
16+
Test fan.is_under_speed default implementation
17+
'''
18+
fan = FanBase()
19+
fan.get_speed = mock.MagicMock(return_value=100)
20+
fan.get_target_speed = mock.MagicMock(return_value=50)
21+
fan.get_speed_tolerance = mock.MagicMock(return_value=10)
22+
23+
for func in (fan.get_speed, fan.get_target_speed, fan.get_speed_tolerance):
24+
return_val = func()
25+
26+
# Check type and bounds errors
27+
for value, exc_type in ((None, TypeError), (-1, ValueError), (101, ValueError)):
28+
func.return_value = value
29+
expected_exception = False
30+
try:
31+
fan.is_under_speed()
32+
except Exception as exc:
33+
expected_exception = isinstance(exc, exc_type)
34+
assert expected_exception
35+
36+
# Reset function return value
37+
func.return_value = return_val
38+
39+
# speed=100, minimum tolerated speed=45, not under speed
40+
assert not fan.is_under_speed()
41+
42+
# speed=46, minimum tolerated speed=45, not under speed
43+
fan.get_speed.return_value = 46
44+
assert not fan.is_under_speed()
45+
46+
# speed=45, minimum tolerated speed=45, not under speed
47+
fan.get_speed.return_value = 45
48+
assert not fan.is_under_speed()
49+
50+
# speed=44, minimum tolerated speed=45, under speed
51+
fan.get_speed.return_value = 44
52+
assert fan.is_under_speed()
53+
54+
# speed=44, minimum tolerated speed=40, not under speed
55+
fan.get_speed_tolerance.return_value = 20
56+
assert not fan.is_under_speed()
57+
58+
# speed=41, minimum tolerated speed=40, not under speed
59+
fan.get_speed.return_value = 41
60+
assert not fan.is_under_speed()
61+
62+
# speed=40, minimum tolerated speed=40, not under speed
63+
fan.get_speed.return_value = 40
64+
assert not fan.is_under_speed()
65+
66+
# speed=39, minimum tolerated speed=40, under speed
67+
fan.get_speed.return_value = 39
68+
assert fan.is_under_speed()
69+
70+
# speed=1, minimum tolerated speed=40, under speed
71+
fan.get_speed.return_value = 1
72+
assert fan.is_under_speed()
73+
74+
@staticmethod
75+
def test_is_over_speed():
76+
'''
77+
test fan.is_over_speed default implementation
78+
'''
79+
fan = FanBase()
80+
fan.get_speed = mock.MagicMock(return_value=1)
81+
fan.get_target_speed = mock.MagicMock(return_value=50)
82+
fan.get_speed_tolerance = mock.MagicMock(return_value=10)
83+
84+
for func in (fan.get_speed, fan.get_target_speed, fan.get_speed_tolerance):
85+
return_val = func()
86+
87+
# Check type and bounds errors
88+
for value, exc_type in ((None, TypeError), (-1, ValueError), (101, ValueError)):
89+
func.return_value = value
90+
expected_exception = False
91+
try:
92+
fan.is_under_speed()
93+
except Exception as exc:
94+
expected_exception = isinstance(exc, exc_type)
95+
assert expected_exception
96+
97+
# Reset function return value
98+
func.return_value = return_val
99+
100+
# speed=1, maximum tolerated speed=55, not over speed
101+
assert not fan.is_over_speed()
102+
103+
# speed=54, maximum tolerated speed=55, not over speed
104+
fan.get_speed.return_value = 54
105+
assert not fan.is_over_speed()
106+
107+
# speed=55, maximum tolerated speed=55, not over speed
108+
fan.get_speed.return_value = 55
109+
assert not fan.is_over_speed()
110+
111+
# speed=56, maximum tolerated speed=55, over speed
112+
fan.get_speed.return_value = 56
113+
assert fan.is_over_speed()
114+
115+
# speed=56, maximum tolerated speed=60, not over speed
116+
fan.get_speed_tolerance.return_value = 20
117+
assert not fan.is_over_speed()
118+
119+
# speed=59, maximum tolerated speed=60, not over speed
120+
fan.get_speed.return_value = 59
121+
assert not fan.is_over_speed()
122+
123+
# speed=60, maximum tolerated speed=60, not over speed
124+
fan.get_speed.return_value = 60
125+
assert not fan.is_over_speed()
126+
127+
# speed=61, maximum tolerated speed=60, over speed
128+
fan.get_speed.return_value = 61
129+
assert fan.is_over_speed()
130+
131+
# speed=100, maximum tolerated speed=60, over speed
132+
fan.get_speed.return_value = 100
133+
assert fan.is_over_speed()
134+
135+
@staticmethod
136+
def test_fan_base():
137+
'''
138+
Verify unimplemented methods
139+
'''
140+
fan = FanBase()
141+
not_implemented_methods = [
142+
(fan.get_direction,),
143+
(fan.get_speed,),
144+
(fan.get_target_speed,),
145+
(fan.get_speed_tolerance,),
146+
(fan.set_speed, 50),
147+
(fan.set_status_led, 'green'),
148+
(fan.get_status_led,)]
149+
150+
for method in not_implemented_methods:
151+
expected_exception = False
152+
try:
153+
func = method[0]
154+
args = method[1:]
155+
func(*args)
156+
except Exception as exc:
157+
expected_exception = isinstance(exc, NotImplementedError)
158+
assert expected_exception

0 commit comments

Comments
 (0)