• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3#  Copyright (c) 2020, The OpenThread Authors.
4#  All rights reserved.
5#
6#  Redistribution and use in source and binary forms, with or without
7#  modification, are permitted provided that the following conditions are met:
8#  1. Redistributions of source code must retain the above copyright
9#     notice, this list of conditions and the following disclaimer.
10#  2. Redistributions in binary form must reproduce the above copyright
11#     notice, this list of conditions and the following disclaimer in the
12#     documentation and/or other materials provided with the distribution.
13#  3. Neither the name of the copyright holder nor the
14#     names of its contributors may be used to endorse or promote products
15#     derived from this software without specific prior written permission.
16#
17#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27#  POSSIBILITY OF SUCH DAMAGE.
28#
29
30import urllib.request
31import urllib.error
32import ipaddress
33import json
34import re
35from threading import Thread
36
37rest_api_addr = "http://0.0.0.0:8081"
38
39
40def assert_is_ipv6_address(string):
41    assert (type(ipaddress.ip_address(string)) is ipaddress.IPv6Address)
42
43def get_data_from_url(url, result, index):
44    response = urllib.request.urlopen(urllib.request.Request(url))
45    body = response.read()
46    data = json.loads(body)
47    result[index] = data
48
49
50def get_error_from_url(url, result, index):
51    try:
52        urllib.request.urlopen(urllib.request.Request(url))
53        assert False
54
55    except urllib.error.HTTPError as e:
56        result[index] = e
57
58
59def create_multi_thread(func, url, thread_num, response_data):
60    threads = [None] * thread_num
61
62    for i in range(thread_num):
63        threads[i] = Thread(target=func, args=(url, response_data, i))
64
65    for thread in threads:
66        thread.start()
67
68    for thread in threads:
69        thread.join()
70
71
72def error404_check(data):
73    assert data is not None
74
75    assert (data.code == 404)
76
77    return True
78
79
80def diagnostics_check(data):
81    assert data is not None
82
83    if len(data) == 0:
84        return 1
85    for diag in data:
86        expected_keys = [
87            "ExtAddress", "Rloc16", "Mode", "Connectivity", "Route",
88            "LeaderData", "NetworkData", "IP6AddressList", "MACCounters",
89            "ChildTable", "ChannelPages"
90        ]
91        expected_value_type = [
92            str, int, dict, dict, dict, dict, str, list, dict, list,
93            str
94        ]
95        expected_check_dict = dict(zip(expected_keys, expected_value_type))
96
97        for key, value in expected_check_dict.items():
98            assert (key in diag)
99            assert (type(diag[key]) == value)
100
101        assert (re.match(r'^[A-F0-9]{16}$', diag["ExtAddress"]) is not None)
102
103        mode = diag["Mode"]
104        mode_expected_keys = [
105            "RxOnWhenIdle", "DeviceType", "NetworkData"
106        ]
107        for key in mode_expected_keys:
108            assert (key in mode)
109            assert (type(mode[key]) == int)
110
111        connectivity = diag["Connectivity"]
112        connectivity_expected_keys = [
113            "ParentPriority", "LinkQuality3", "LinkQuality2", "LinkQuality1",
114            "LeaderCost", "IdSequence", "ActiveRouters", "SedBufferSize",
115            "SedDatagramCount"
116        ]
117        for key in connectivity_expected_keys:
118            assert (key in connectivity)
119            assert (type(connectivity[key]) == int)
120
121        route = diag["Route"]
122        assert ("IdSequence" in route)
123        assert (type(route["IdSequence"]) == int)
124
125        assert ("RouteData" in route)
126        route_routedata = route["RouteData"]
127        assert (type(route["RouteData"]) == list)
128
129        routedata_expected_keys = [
130            "RouteId", "LinkQualityOut", "LinkQualityIn", "RouteCost"
131        ]
132
133        for item in route_routedata:
134            for key in routedata_expected_keys:
135                assert (key in item)
136                assert (type(item[key]) == int)
137
138        leaderdata = diag["LeaderData"]
139        leaderdata_expected_keys = [
140            "PartitionId", "Weighting", "DataVersion", "StableDataVersion",
141            "LeaderRouterId"
142        ]
143
144        for key in leaderdata_expected_keys:
145            assert (key in leaderdata)
146            assert (type(leaderdata[key]) == int)
147
148        assert (re.match(r'^[A-F0-9]{12}$', diag["NetworkData"]) is not None)
149
150        ip6_address_list = diag["IP6AddressList"]
151        assert (type(ip6_address_list) == list)
152
153        for ip6_address in ip6_address_list:
154            assert (type(ip6_address) == str)
155            assert_is_ipv6_address(ip6_address)
156
157        mac_counters = diag["MACCounters"]
158        assert (type(mac_counters) == dict)
159        mac_counters_expected_keys = [
160            "IfInUnknownProtos", "IfInErrors", "IfOutErrors", "IfInUcastPkts",
161            "IfInBroadcastPkts", "IfInDiscards", "IfOutUcastPkts",
162            "IfOutBroadcastPkts", "IfOutDiscards"
163        ]
164        for key in mac_counters_expected_keys:
165            assert (key in mac_counters)
166            assert (type(mac_counters[key]) == int)
167
168        child_table = diag["ChildTable"]
169        assert (type(child_table) == list)
170
171        for child in child_table:
172            assert ("ChildId" in child)
173            assert (type(child["ChildId"]) == int)
174            assert ("Timeout" in child)
175            assert (type(child["Timeout"]) == int)
176            assert ("Mode" in child)
177            mode = child["Mode"]
178            assert (type(mode) == dict)
179            for key in mode_expected_keys:
180                assert (key in mode)
181                assert (type(mode[key]) == int)
182
183        assert (type(diag["ChannelPages"]) == str)
184        assert (re.match(r'^[A-F0-9]{2}$', diag["ChannelPages"]) is not None)
185
186    return 2
187
188
189def node_check(data):
190    assert data is not None
191
192    expected_keys = [
193        "State", "NumOfRouter", "RlocAddress", "NetworkName", "ExtAddress",
194        "Rloc16", "LeaderData", "ExtPanId"
195    ]
196    expected_value_type = [
197        str, int, str, str, str, int, dict, str
198    ]
199    expected_check_dict = dict(zip(expected_keys, expected_value_type))
200
201    for key, value in expected_check_dict.items():
202        assert (key in data)
203        assert (type(data[key]) == value)
204
205    assert_is_ipv6_address(data["RlocAddress"])
206
207    assert (re.match(r'^[A-F0-9]{16}$', data["ExtAddress"]) is not None)
208    assert (re.match(r'[A-F0-9]{16}', data["ExtPanId"]) is not None)
209
210    leaderdata = data["LeaderData"]
211    leaderdata_expected_keys = [
212        "PartitionId", "Weighting", "DataVersion", "StableDataVersion",
213        "LeaderRouterId"
214    ]
215
216    for key in leaderdata_expected_keys:
217        assert (key in leaderdata)
218        assert (type(leaderdata[key]) == int)
219
220    return True
221
222
223def node_rloc_check(data):
224    assert data is not None
225
226    assert (type(data) == str)
227
228    assert_is_ipv6_address(data)
229
230    return True
231
232
233def node_rloc16_check(data):
234    assert data is not None
235
236    assert (type(data) == int)
237
238    return True
239
240
241def node_ext_address_check(data):
242    assert data is not None
243
244    assert (type(data) == str)
245    assert (re.match(r'^[A-F0-9]{16}$', data) is not None)
246
247    return True
248
249
250def node_state_check(data):
251    assert data is not None
252
253    assert (type(data) == str)
254
255    return True
256
257
258def node_network_name_check(data):
259    assert data is not None
260
261    assert (type(data) == str)
262
263    return True
264
265
266def node_leader_data_check(data):
267    assert data is not None
268
269    assert (type(data) == dict)
270
271    leaderdata_expected_keys = [
272        "PartitionId", "Weighting", "DataVersion", "StableDataVersion",
273        "LeaderRouterId"
274    ]
275
276    for key in leaderdata_expected_keys:
277        assert (key in data)
278        assert (type(data[key]) == int)
279
280    return True
281
282
283def node_num_of_router_check(data):
284    assert data is not None
285
286    assert (type(data) == int)
287
288    return True
289
290
291def node_ext_panid_check(data):
292    assert data is not None
293
294    assert (type(data) == str)
295
296    return True
297
298
299def node_coprocessor_version_check(data):
300    assert data is not None
301
302    assert (type(data) == str)
303
304    return True
305
306
307def node_test(thread_num):
308    url = rest_api_addr + "/node"
309
310    response_data = [None] * thread_num
311
312    create_multi_thread(get_data_from_url, url, thread_num, response_data)
313
314    valid = [node_check(data) for data in response_data].count(True)
315
316    print(" /node : all {}, valid {} ".format(thread_num, valid))
317
318
319def node_rloc_test(thread_num):
320    url = rest_api_addr + "/node/rloc"
321
322    response_data = [None] * thread_num
323
324    create_multi_thread(get_data_from_url, url, thread_num, response_data)
325
326    valid = [node_rloc_check(data) for data in response_data].count(True)
327
328    print(" /node/rloc : all {}, valid {} ".format(thread_num, valid))
329
330
331def node_rloc16_test(thread_num):
332    url = rest_api_addr + "/node/rloc16"
333
334    response_data = [None] * thread_num
335
336    create_multi_thread(get_data_from_url, url, thread_num, response_data)
337
338    valid = [node_rloc16_check(data) for data in response_data].count(True)
339
340    print(" /node/rloc16 : all {}, valid {} ".format(thread_num, valid))
341
342
343def node_ext_address_test(thread_num):
344    url = rest_api_addr + "/node/ext-address"
345
346    response_data = [None] * thread_num
347
348    create_multi_thread(get_data_from_url, url, thread_num, response_data)
349
350    valid = [node_ext_address_check(data) for data in response_data].count(True)
351
352    print(" /node/ext-address : all {}, valid {} ".format(thread_num, valid))
353
354
355def node_state_test(thread_num):
356    url = rest_api_addr + "/node/state"
357
358    response_data = [None] * thread_num
359
360    create_multi_thread(get_data_from_url, url, thread_num, response_data)
361
362    valid = [node_state_check(data) for data in response_data].count(True)
363
364    print(" /node/state : all {}, valid {} ".format(thread_num, valid))
365
366
367def node_network_name_test(thread_num):
368    url = rest_api_addr + "/node/network-name"
369
370    response_data = [None] * thread_num
371
372    create_multi_thread(get_data_from_url, url, thread_num, response_data)
373
374    valid = [node_network_name_check(data) for data in response_data
375             ].count(True)
376
377    print(" /node/network-name : all {}, valid {} ".format(thread_num, valid))
378
379
380def node_leader_data_test(thread_num):
381    url = rest_api_addr + "/node/leader-data"
382
383    response_data = [None] * thread_num
384
385    create_multi_thread(get_data_from_url, url, thread_num, response_data)
386
387    valid = [node_leader_data_check(data) for data in response_data].count(True)
388
389    print(" /node/leader-data : all {}, valid {} ".format(thread_num, valid))
390
391
392def node_num_of_router_test(thread_num):
393    url = rest_api_addr + "/node/num-of-router"
394
395    response_data = [None] * thread_num
396
397    create_multi_thread(get_data_from_url, url, thread_num, response_data)
398
399    valid = [node_num_of_router_check(data) for data in response_data
400             ].count(True)
401
402    print(" /v1/node/num-of-router : all {}, valid {} ".format(thread_num, valid))
403
404
405def node_ext_panid_test(thread_num):
406    url = rest_api_addr + "/node/ext-panid"
407
408    response_data = [None] * thread_num
409
410    create_multi_thread(get_data_from_url, url, thread_num, response_data)
411
412    valid = [node_ext_panid_check(data) for data in response_data].count(True)
413
414    print(" /node/ext-panid : all {}, valid {} ".format(thread_num, valid))
415
416
417def node_coprocessor_version_test(thread_num):
418    url = rest_api_addr + "/node/coprocessor/version"
419
420    response_data = [None] * thread_num
421
422    create_multi_thread(get_data_from_url, url, thread_num, response_data)
423
424    valid = [node_coprocessor_version_check(data) for data in response_data].count(True)
425
426    print(" /node/coprocessor/version : all {}, valid {} ".format(thread_num, valid))
427
428
429def diagnostics_test(thread_num):
430    url = rest_api_addr + "/diagnostics"
431
432    response_data = [None] * thread_num
433
434    create_multi_thread(get_data_from_url, url, thread_num, response_data)
435
436    valid = 0
437    has_content = 0
438    for data in response_data:
439
440        ret = diagnostics_check(data)
441        if ret == 1:
442            valid += 1
443        elif ret == 2:
444            valid += 1
445            has_content += 1
446
447    print(" /diagnostics : all {}, has content {}, valid {} ".format(
448        thread_num, has_content, valid))
449
450
451def error_test(thread_num):
452    url = rest_api_addr + "/hello"
453
454    response_data = [None] * thread_num
455
456    create_multi_thread(get_error_from_url, url, thread_num, response_data)
457
458    valid = [error404_check(data) for data in response_data].count(True)
459
460    print(" /v1/hello : all {}, valid {} ".format(thread_num, valid))
461
462
463def main():
464    node_test(200)
465    node_rloc_test(200)
466    node_rloc16_test(200)
467    node_ext_address_test(200)
468    node_state_test(200)
469    node_network_name_test(200)
470    node_leader_data_test(200)
471    node_num_of_router_test(200)
472    node_ext_panid_test(200)
473    node_coprocessor_version_test(200)
474    diagnostics_test(20)
475    error_test(10)
476
477    return 0
478
479
480if __name__ == '__main__':
481    exit(main())
482