瀏覽代碼

initial testenvironment added

tags/v1.3.0
Dirk Alders 1 年之前
父節點
當前提交
769dffe20d

+ 13
- 9
.vscode/settings.json 查看文件

@@ -1,11 +1,15 @@
1 1
 {
2
-    "python.defaultInterpreterPath": "./venv/bin/python",
3
-    "editor.formatOnSave": true,
4
-    "autopep8.args": [
5
-        "--max-line-length=150"
6
-    ],
7
-    "editor.fontSize": 14,
8
-    "emmet.includeLanguages": {
9
-        "django-html": "html"
10
-    }
2
+  "python.defaultInterpreterPath": "./venv/bin/python",
3
+  "autopep8.args": ["--max-line-length=150"],
4
+  "python.formatting.provider": "none",
5
+  "[python]": {
6
+    "editor.defaultFormatter": "ms-python.python",
7
+    "editor.formatOnSave": true
8
+  },
9
+  "editor.formatOnSave": true,
10
+  "editor.fontSize": 14,
11
+  "emmet.includeLanguages": { "django-html": "html" },
12
+  "python.testing.pytestArgs": ["-v", "--cov", "--cov-report=xml", "__test__"],
13
+  "python.testing.unittestEnabled": false,
14
+  "python.testing.pytestEnabled": true
11 15
 }

+ 35
- 0
__test__/devices/module.py 查看文件

@@ -0,0 +1,35 @@
1
+import json
2
+from mqtt import mqtt_client
3
+import time
4
+
5
+TEST_CLIENT_ID = "__test_device_tester__"
6
+
7
+
8
+mqtt_test_client = mqtt_client(TEST_CLIENT_ID, "localhost")
9
+
10
+
11
+def init_state(all_state_keys, device):
12
+    for state_topic in all_state_keys:
13
+        assert device.get(state_topic, 0) == None
14
+
15
+
16
+def state_change_by_mqtt(all_state_keys, num_states, mqtt_test_client, base_topic, device, mqtt_data, state_data, warning_condition, mqtt_signal_time):
17
+    tm_warning = None
18
+
19
+    for i in range(num_states):
20
+        for state_topic in all_state_keys:
21
+            if device.TX_TYPE == device.TX_VALUE:
22
+                data = json.dumps(mqtt_data(state_topic)[i])
23
+                mqtt_test_client.send(base_topic + '/' + state_topic, data)
24
+            elif device.TX_TYPE == device.TX_DICT:
25
+                mqtt_test_client.send(base_topic, json.dumps({state_topic: mqtt_data(state_topic)[i]}))
26
+            else:
27
+                raise TypeError("Unknown TX_TYPE for device.")
28
+            if callable(warning_condition):
29
+                if warning_condition(state_topic, mqtt_data(state_topic)[i]):
30
+                    tm_warning = int(time.time())
31
+        time.sleep(mqtt_signal_time)
32
+        for state_topic in all_state_keys:
33
+            assert device.get(state_topic) == state_data(state_topic)[i]
34
+
35
+    return tm_warning

+ 45
- 0
__test__/devices/test_my_powerplug.py 查看文件

@@ -0,0 +1,45 @@
1
+from module import mqtt_test_client, init_state, state_change_by_mqtt
2
+from devices import my_powerplug as test_device
3
+from devices import warning
4
+from mqtt import mqtt_client
5
+import pytest
6
+import time
7
+
8
+DUT_CLIENT_ID = "__%s__" % __name__
9
+TOPIC = "__test__/%s" % __name__
10
+#
11
+MQTT_SIGNAL_TIME = 0.2
12
+
13
+
14
+ALL_STATE_KEYS = ["output/1", "output/2", "output/3", "output/4", ]
15
+BOOL_KEYS = ALL_STATE_KEYS
16
+
17
+
18
+@pytest.fixture
19
+def this_device():
20
+    mc = mqtt_client(DUT_CLIENT_ID, 'localhost')
21
+    return test_device(mc, TOPIC)
22
+
23
+
24
+def test_initial_states(this_device: test_device):
25
+    # test all initial values
26
+    init_state(ALL_STATE_KEYS, this_device)
27
+
28
+
29
+def test_state_change_by_mqtt(this_device: test_device):
30
+    def state_data(key):
31
+        return (True, False)
32
+
33
+    def mqtt_data(key):
34
+        return state_data(key)
35
+
36
+    # test state changes
37
+    tm_warning = state_change_by_mqtt(ALL_STATE_KEYS, 2, mqtt_test_client, TOPIC, this_device,
38
+                                      mqtt_data, state_data, None, MQTT_SIGNAL_TIME)
39
+
40
+
41
+def test_specific_get_functions(this_device: test_device):
42
+    assert this_device.output_0 == this_device.get(this_device.KEY_OUTPUT_0)
43
+    assert this_device.output_1 == this_device.get(this_device.KEY_OUTPUT_1)
44
+    assert this_device.output_2 == this_device.get(this_device.KEY_OUTPUT_2)
45
+    assert this_device.output_3 == this_device.get(this_device.KEY_OUTPUT_3)

+ 250
- 0
__test__/devices/test_shelly.py 查看文件

@@ -0,0 +1,250 @@
1
+from module import mqtt_test_client, init_state, state_change_by_mqtt
2
+from devices import shelly as test_device
3
+from devices import warning
4
+from mqtt import mqtt_client
5
+import pytest
6
+import time
7
+
8
+DUT_CLIENT_ID = "__%s__" % __name__
9
+TOPIC = "__test__/%s" % __name__
10
+#
11
+MQTT_SIGNAL_TIME = 0.2
12
+
13
+
14
+ALL_STATE_KEYS = ["relay/0", "relay/1", "input/0", "input/1", "longpush/0", "longpush/1", "temperature", "overtemperature"]
15
+BOOL_KEYS = ["relay/0", "relay/1", "input/0", "input/1", "longpush/0", "longpush/1", "overtemperature"]
16
+
17
+
18
+@pytest.fixture
19
+def this_device():
20
+    mc = mqtt_client(DUT_CLIENT_ID, 'localhost')
21
+    return test_device(mc, TOPIC)
22
+
23
+
24
+def test_initial_states(this_device: test_device):
25
+    # test all initial values
26
+    init_state(ALL_STATE_KEYS, this_device)
27
+
28
+
29
+def test_state_change_by_mqtt(this_device: test_device):
30
+    def state_data(key):
31
+        if key in BOOL_KEYS:
32
+            return (True, False)
33
+        elif key == "temperature":
34
+            return (85.3, 20.1)
35
+        else:
36
+            raise IndexError("No return value defined for key %s" % key)
37
+
38
+    def mqtt_data(key):
39
+        if key in ["relay/0", "relay/1"]:
40
+            return ('on', 'off')
41
+        elif key in ["input/0", "input/1", "longpush/0", "longpush/1", "overtemperature"]:
42
+            return (1, 0)
43
+        else:
44
+            return state_data(key)
45
+
46
+    def warning_condition(state_topic, value):
47
+        return state_topic == "overtemperature" and value == 1
48
+
49
+    # test state changes
50
+    tm_warning = state_change_by_mqtt(ALL_STATE_KEYS, 2, mqtt_test_client, TOPIC, this_device,
51
+                                      mqtt_data, state_data, warning_condition, MQTT_SIGNAL_TIME)
52
+
53
+    # test warning
54
+    w: warning = this_device.get(this_device.KEY_WARNING)
55
+    assert w.get(w.KEY_ID) == TOPIC
56
+    assert w.get(w.KEY_TYPE) == w.TYPE_OVERTEMPERATURE
57
+    wt = time.mktime(w.get(w.KEY_TM))
58
+    wt_min = tm_warning
59
+    wt_max = tm_warning + 2
60
+    assert wt >= wt_min and wt <= wt_max
61
+
62
+
63
+def test_specific_get_functions(this_device: test_device):
64
+    assert this_device.output_0 == this_device.get(this_device.KEY_OUTPUT_0)
65
+    assert this_device.output_1 == this_device.get(this_device.KEY_OUTPUT_1)
66
+    assert this_device.input_0 == this_device.get(this_device.KEY_INPUT_0)
67
+    assert this_device.input_1 == this_device.get(this_device.KEY_INPUT_1)
68
+    assert this_device.longpush_0 == this_device.get(this_device.KEY_LONGPUSH_0)
69
+    assert this_device.longpush_1 == this_device.get(this_device.KEY_LONGPUSH_1)
70
+    assert this_device.temperature == this_device.get(this_device.KEY_TEMPERATURE)
71
+
72
+
73
+def test_send_command(this_device: test_device):
74
+    this_device.set_output_0(True)
75
+    this_device.set_output_0(False)
76
+
77
+
78
+'''
79
+class shelly(base):
80
+    """ Communication (MQTT)
81
+
82
+        shelly
83
+            +- relay
84
+            |      +- 0 ["on" / "off"]              <- status 
85
+            |      |  +- command ["on"/ "off"]      <- command
86
+            |      |  +- energy [numeric]           <- status
87
+            |      +- 1 ["on" / "off"]              <- status
88
+            |         +- command ["on"/ "off"]      <- command
89
+            |         +- energy [numeric]           <- status
90
+            +- input
91
+            |      +- 0 [0 / 1]                     <- status
92
+            |      +- 1 [0 / 1]                     <- status
93
+            +- input_event
94
+            |      +- 0                             <- status
95
+            |      +- 1                             <- status
96
+            +- logpush
97
+            |      +- 0 [0 / 1]                     <- status
98
+            |      +- 1 [0 / 1]                     <- status
99
+            +- temperature [numeric] °C             <- status
100
+            +- temperature_f [numeric] F            <- status
101
+            +- overtemperature [0 / 1]              <- status
102
+            +- id                                   <- status
103
+            +- model                                <- status
104
+            +- mac                                  <- status
105
+            +- ip                                   <- status
106
+            +- new_fw                               <- status
107
+            +- fw_ver                               <- status
108
+    """
109
+    KEY_OUTPUT_0 = "relay/0"
110
+    KEY_OUTPUT_1 = "relay/1"
111
+    KEY_INPUT_0 = "input/0"
112
+    KEY_INPUT_1 = "input/1"
113
+    KEY_LONGPUSH_0 = "longpush/0"
114
+    KEY_LONGPUSH_1 = "longpush/1"
115
+    KEY_TEMPERATURE = "temperature"
116
+    KEY_OVERTEMPERATURE = "overtemperature"
117
+    KEY_ID = "id"
118
+    KEY_MODEL = "model"
119
+    KEY_MAC = "mac"
120
+    KEY_IP = "ip"
121
+    KEY_NEW_FIRMWARE = "new_fw"
122
+    KEY_FIRMWARE_VERSION = "fw_ver"
123
+    #
124
+    TX_TOPIC = "command"
125
+    TX_TYPE = base.TX_VALUE
126
+    TX_FILTER_DATA_KEYS = [KEY_OUTPUT_0, KEY_OUTPUT_1]
127
+    #
128
+    RX_KEYS = [KEY_OUTPUT_0, KEY_OUTPUT_1, KEY_INPUT_0, KEY_INPUT_1, KEY_LONGPUSH_0, KEY_LONGPUSH_1, KEY_OVERTEMPERATURE, KEY_TEMPERATURE,
129
+               KEY_ID, KEY_MODEL, KEY_MAC, KEY_IP, KEY_NEW_FIRMWARE, KEY_FIRMWARE_VERSION]
130
+    RX_IGNORE_TOPICS = [KEY_OUTPUT_0 + '/' + "energy", KEY_OUTPUT_1 + '/' + "energy", 'input_event/0', 'input_event/1']
131
+    RX_IGNORE_KEYS = ['temperature_f']
132
+    RX_FILTER_DATA_KEYS = [KEY_INPUT_0, KEY_INPUT_1, KEY_LONGPUSH_0, KEY_LONGPUSH_1, KEY_OUTPUT_0, KEY_OUTPUT_1, KEY_OVERTEMPERATURE]
133
+
134
+    def __init__(self, mqtt_client, topic):
135
+        super().__init__(mqtt_client, topic)
136
+        #
137
+        self.output_key_delayed = None
138
+        self.delayed_flash_task = task.delayed(0.3, self.flash_task)
139
+        self.delayed_off_task = task.delayed(0.3, self.off_task)
140
+        #
141
+        self.add_callback(self.KEY_OVERTEMPERATURE, True, self.__warning__, True)
142
+        #
143
+        self.all_off_requested = False
144
+
145
+    def flash_task(self, *args):
146
+        if self.flash_active:
147
+            self.send_command(self.output_key_delayed, not self.get(self.output_key_delayed))
148
+            self.output_key_delayed = None
149
+            if self.all_off_requested:
150
+                self.delayed_off_task.run()
151
+
152
+    def off_task(self, *args):
153
+        self.all_off()
154
+
155
+    @property
156
+    def flash_active(self):
157
+        return self.output_key_delayed is not None
158
+
159
+    #
160
+    # WARNING CALL
161
+    #
162
+    def __warning__(self, client, key, data):
163
+        w = warning(self.topic, warning.TYPE_OVERTEMPERATURE, "Temperature to high (%.1f°C)", self.get(self.KEY_TEMPERATURE) or math.nan)
164
+        self.logger.warning(w)
165
+        self.set(self.KEY_WARNING, w)
166
+
167
+    #
168
+    # RX
169
+    #
170
+    @property
171
+    def output_0(self):
172
+        """rv: [True, False]"""
173
+        return self.get(self.KEY_OUTPUT_0)
174
+
175
+    @property
176
+    def output_1(self):
177
+        """rv: [True, False]"""
178
+        return self.get(self.KEY_OUTPUT_1)
179
+
180
+    @property
181
+    def input_0(self):
182
+        """rv: [True, False]"""
183
+        return self.get(self.KEY_INPUT_0)
184
+
185
+    @property
186
+    def input_1(self):
187
+        """rv: [True, False]"""
188
+        return self.get(self.KEY_INPUT_1)
189
+
190
+    @property
191
+    def longpush_0(self):
192
+        """rv: [True, False]"""
193
+        return self.get(self.KEY_LONGPUSH_0)
194
+
195
+    @property
196
+    def longpush_1(self):
197
+        """rv: [True, False]"""
198
+        return self.get(self.KEY_LONGPUSH_1)
199
+
200
+    @property
201
+    def temperature(self):
202
+        """rv: numeric value"""
203
+        return self.get(self.KEY_TEMPERATURE)
204
+
205
+    #
206
+    # TX
207
+    #
208
+    def set_output_0(self, state):
209
+        """state: [True, False]"""
210
+        self.send_command(self.KEY_OUTPUT_0, state)
211
+
212
+    def set_output_0_mcb(self, device, key, data):
213
+        self.logger.log(logging.INFO if data != self.output_0 else logging.DEBUG, "Changing output 0 to %s", str(data))
214
+        self.set_output_0(data)
215
+
216
+    def toggle_output_0_mcb(self, device, key, data):
217
+        self.logger.info("Toggeling output 0")
218
+        self.set_output_0(not self.output_0)
219
+
220
+    def set_output_1(self, state):
221
+        """state: [True, False]"""
222
+        self.send_command(self.KEY_OUTPUT_1, state)
223
+
224
+    def set_output_1_mcb(self, device, key, data):
225
+        self.logger.log(logging.INFO if data != self.output_1 else logging.DEBUG, "Changing output 1 to %s", str(data))
226
+        self.set_output_1(data)
227
+
228
+    def toggle_output_1_mcb(self, device, key, data):
229
+        self.logger.info("Toggeling output 1")
230
+        self.set_output_1(not self.output_1)
231
+
232
+    def flash_0_mcb(self, device, key, data):
233
+        self.output_key_delayed = self.KEY_OUTPUT_0
234
+        self.toggle_output_0_mcb(device, key, data)
235
+        self.delayed_flash_task.run()
236
+
237
+    def flash_1_mcb(self, device, key, data):
238
+        self.output_key_delayed = self.KEY_OUTPUT_1
239
+        self.toggle_output_1_mcb(device, key, data)
240
+        self.delayed_flash_task.run()
241
+
242
+    def all_off(self):
243
+        if self.flash_active:
244
+            self.all_off_requested = True
245
+        else:
246
+            if self.output_0:
247
+                self.set_output_0(False)
248
+            if self.output_1:
249
+                self.set_output_1(False)
250
+'''

+ 55
- 0
__test__/devices/test_silvercrest_motion_sensor.py 查看文件

@@ -0,0 +1,55 @@
1
+from module import mqtt_test_client, init_state, state_change_by_mqtt
2
+from devices import silvercrest_motion_sensor as test_device
3
+from devices import warning
4
+from mqtt import mqtt_client
5
+import pytest
6
+import time
7
+
8
+DUT_CLIENT_ID = "__%s__" % __name__
9
+TOPIC = "__test__/%s" % __name__
10
+#
11
+MQTT_SIGNAL_TIME = 0.2
12
+
13
+
14
+ALL_STATE_KEYS = ["battery", "battery_low", "linkquality", "occupancy", "tamper", "voltage"]
15
+BOOL_KEYS = ["battery_low", "occupancy", "tamper"]
16
+
17
+
18
+@pytest.fixture
19
+def this_device():
20
+    mc = mqtt_client(DUT_CLIENT_ID, 'localhost')
21
+    return test_device(mc, TOPIC)
22
+
23
+
24
+def test_initial_states(this_device: test_device):
25
+    # test all initial values
26
+    init_state(ALL_STATE_KEYS, this_device)
27
+
28
+
29
+def test_state_change_by_mqtt(this_device: test_device):
30
+    def state_data(key):
31
+        if key in BOOL_KEYS:
32
+            return (True, False)
33
+        elif key == "battery":
34
+            return (2, 87)
35
+        elif key == "linkquality":
36
+            return (1, 217)
37
+        elif key == "voltage":
38
+            return (1.17, 2.53)
39
+        else:
40
+            raise IndexError("No return value defined for key %s" % key)
41
+
42
+    def mqtt_data(key):
43
+        return state_data(key)
44
+
45
+    def warning_condition(state_topic, value):
46
+        return state_topic == "battery_low" and value is True
47
+
48
+    # test state changes
49
+    tm_warning = state_change_by_mqtt(ALL_STATE_KEYS, 2, mqtt_test_client, TOPIC, this_device,
50
+                                      mqtt_data, state_data, None, MQTT_SIGNAL_TIME)
51
+
52
+
53
+def test_specific_get_functions(this_device: test_device):
54
+    assert this_device.linkquality == this_device.get(this_device.KEY_LINKQUALITY)
55
+    assert this_device.battery == this_device.get(this_device.KEY_BATTERY)

+ 49
- 0
__test__/devices/test_silvercrest_powerplug.py 查看文件

@@ -0,0 +1,49 @@
1
+from module import mqtt_test_client, init_state, state_change_by_mqtt
2
+from devices import silvercrest_powerplug as test_device
3
+from devices import warning
4
+from mqtt import mqtt_client
5
+import pytest
6
+import time
7
+
8
+DUT_CLIENT_ID = "__%s__" % __name__
9
+TOPIC = "__test__/%s" % __name__
10
+#
11
+MQTT_SIGNAL_TIME = 0.2
12
+
13
+
14
+ALL_STATE_KEYS = ["state"]
15
+BOOL_KEYS = ["state"]
16
+
17
+
18
+@pytest.fixture
19
+def this_device():
20
+    mc = mqtt_client(DUT_CLIENT_ID, 'localhost')
21
+    return test_device(mc, TOPIC)
22
+
23
+
24
+def test_initial_states(this_device: test_device):
25
+    # test all initial values
26
+    init_state(ALL_STATE_KEYS, this_device)
27
+
28
+
29
+def test_state_change_by_mqtt(this_device: test_device):
30
+    def state_data(key):
31
+        if key in BOOL_KEYS:
32
+            return (True, False)
33
+        else:
34
+            raise IndexError("No return value defined for key %s" % key)
35
+
36
+    def mqtt_data(key):
37
+        if key in BOOL_KEYS:
38
+            return ('ON', 'OFF')
39
+        else:
40
+            return state_data(key)
41
+
42
+    # test state changes
43
+    tm_warning = state_change_by_mqtt(ALL_STATE_KEYS, 2, mqtt_test_client, TOPIC, this_device,
44
+                                      mqtt_data, state_data, None, MQTT_SIGNAL_TIME)
45
+
46
+
47
+def test_specific_get_functions(this_device: test_device):
48
+    assert this_device.linkquality == this_device.get(this_device.KEY_LINKQUALITY)
49
+    assert this_device.output_0 == this_device.get(this_device.KEY_OUTPUT_0)

+ 40
- 0
__test__/function/modules/test_heating_function.py 查看文件

@@ -0,0 +1,40 @@
1
+from function.modules import heating_function as test_class
2
+
3
+"""
4
+config.DEFAULT_TEMPERATURE[heating_valve.topic],
5
+db_data = get_radiator_data(heating_valve.topic)
6
+**{
7
+        test_class.KEY_USER_TEMPERATURE_SETPOINT: db_data[2],
8
+        test_class.KEY_TEMPERATURE_SETPOINT: db_data[3],
9
+        test_class.KEY_AWAY_MODE: db_data[0],
10
+        test_class.KEY_SUMMER_MODE: db_data[1],
11
+    })
12
+"""
13
+
14
+
15
+def test_initial_states():
16
+
17
+    class heating_valve(object):
18
+        KEY_HEATING_SETPOINT = 'hsp'
19
+        KEY_TEMPERATURE = 'temp'
20
+
21
+        def set_heating_setpoint(self, value):
22
+            pass
23
+
24
+        def add_callback(self, key, value, callback):
25
+            pass
26
+    #
27
+    #
28
+    #
29
+    tc = test_class(
30
+        heating_valve(),
31
+        21, **{
32
+            test_class.KEY_USER_TEMPERATURE_SETPOINT: 22,
33
+            test_class.KEY_TEMPERATURE_SETPOINT: 17,
34
+            test_class.KEY_AWAY_MODE: True,
35
+            test_class.KEY_SUMMER_MODE: False,
36
+        })
37
+    assert tc.get(test_class.KEY_USER_TEMPERATURE_SETPOINT) == 22
38
+    assert tc.get(test_class.KEY_TEMPERATURE_SETPOINT) == 17
39
+    assert tc.get(test_class.KEY_AWAY_MODE) == True
40
+    assert tc.get(test_class.KEY_SUMMER_MODE) == False

+ 2
- 0
__test__/requirements.txt 查看文件

@@ -0,0 +1,2 @@
1
+pytest
2
+pytest-cov

+ 0
- 0
conftest.py 查看文件


Loading…
取消
儲存