mirror of
https://github.com/espressif/openthread.git
synced 2026-08-18 16:39:52 +00:00
[toranj] add test-case for child table and child recovery (#2776)
This commit is contained in:
committed by
Jonathan Hui
parent
70154b1f4d
commit
139b2da408
@@ -123,6 +123,7 @@ run test-007-traffic-router-sleepy.py
|
||||
run test-008-permit-join.py
|
||||
run test-009-insecure-traffic-join.py
|
||||
run test-010-on-mesh-prefix-config-gateway.py
|
||||
run test-011-child-table.py
|
||||
run test-100-mcu-power-state.py
|
||||
run test-600-channel-manager-properties.py
|
||||
run test-601-channel-manager-channel-change.py
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 2018, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import time
|
||||
import wpan
|
||||
from wpan import verify
|
||||
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
# Test description: Child-table and child recovery
|
||||
#
|
||||
|
||||
test_name = __file__[:-3] if __file__.endswith('.py') else __file__
|
||||
print '-' * 120
|
||||
print 'Starting \'{}\''.format(test_name)
|
||||
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
# Creating `wpan.Nodes` instances
|
||||
|
||||
speedup = 4
|
||||
wpan.Node.set_time_speedup_factor(speedup)
|
||||
|
||||
NUM_CHILDREN = 7
|
||||
|
||||
router = wpan.Node()
|
||||
|
||||
children = []
|
||||
for i in range(NUM_CHILDREN):
|
||||
children.append(wpan.Node())
|
||||
|
||||
all_nodes = [router] + children
|
||||
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
# Init all nodes
|
||||
|
||||
wpan.Node.init_all_nodes()
|
||||
for node in all_nodes:
|
||||
node.set(wpan.WPAN_OT_LOG_LEVEL, '0')
|
||||
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
# Build network topology
|
||||
|
||||
router.form('child-table')
|
||||
for child in children:
|
||||
child.join_node(router, node_type=wpan.JOIN_TYPE_SLEEPY_END_DEVICE)
|
||||
child.set(wpan.WPAN_POLL_INTERVAL, '300')
|
||||
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
# Test implementation
|
||||
|
||||
# Get the child table and verify all children are in the table.
|
||||
|
||||
child_table = wpan.parse_child_table_result(router.get(wpan.WPAN_THREAD_CHILD_TABLE))
|
||||
|
||||
verify(len(child_table) == len(children))
|
||||
|
||||
for child in children:
|
||||
ext_addr = child.get(wpan.WPAN_EXT_ADDRESS)[1:-1]
|
||||
for entry in child_table:
|
||||
if entry.ext_address == ext_addr:
|
||||
break;
|
||||
else:
|
||||
print 'Failed to find a child entry for extended address {} in table'.format(ext_addr)
|
||||
exit(1)
|
||||
|
||||
verify(int(entry.rloc16, 16) == int(child.get(wpan.WPAN_THREAD_RLOC16), 16))
|
||||
verify(int(entry.timeout, 0) == 120)
|
||||
verify(entry.is_rx_on_when_idle() == False)
|
||||
verify(entry.is_ffd() == False)
|
||||
|
||||
|
||||
# Reset router and ensure all children are restored.
|
||||
router.reset()
|
||||
|
||||
start_time = time.time()
|
||||
wait_time = 5
|
||||
while time.time() - start_time < wait_time:
|
||||
time.sleep(0.25)
|
||||
child_table = wpan.parse_child_table_result(router.get(wpan.WPAN_THREAD_CHILD_TABLE))
|
||||
if (len(child_table) == len(children)):
|
||||
break
|
||||
else:
|
||||
print 'Took too long for node to recover after reset ({}>{} sec)'.format(time.time() - start_time, wait_time)
|
||||
exit(1)
|
||||
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
# Test finished
|
||||
|
||||
print '\'{}\' passed.'.format(test_name)
|
||||
+85
-31
@@ -63,36 +63,36 @@ WPAN_IP6_MESH_LOCAL_PREFIX = "IPv6:MeshLocalPrefix"
|
||||
WPAN_IP6_ALL_ADDRESSES = "IPv6:AllAddresses"
|
||||
WPAN_IP6_MULTICAST_ADDRESSES = "IPv6:MulticastAddresses"
|
||||
|
||||
WPAN_THREAD_RLOC16 = "Thread:RLOC16"
|
||||
WPAN_THREAD_ROUTER_ID = "Thread:RouterID"
|
||||
WPAN_THREAD_LEADER_ADDRESS = "Thread:Leader:Address"
|
||||
WPAN_THREAD_LEADER_ROUTER_ID = "Thread:Leader:RouterID"
|
||||
WPAN_THREAD_LEADER_WEIGHT = "Thread:Leader:Weight"
|
||||
WPAN_THREAD_LEADER_LOCAL_WEIGHT = "Thread:Leader:LocalWeight"
|
||||
WPAN_THREAD_LEADER_NETWORK_DATA = "Thread:Leader:NetworkData"
|
||||
WPAN_THREAD_STABLE_LEADER_NETWORK_DATA = "Thread:Leader:StableNetworkData"
|
||||
WPAN_THREAD_NETWORK_DATA = "Thread:NetworkData"
|
||||
WPAN_THREAD_CHILD_TABLE = "Thread:ChildTable"
|
||||
WPAN_THREAD_CHILD_TABLE_ASVALMAP = "Thread:ChildTable:AsValMap"
|
||||
WPAN_THREAD_CHILD_TABLE_ADDRESSES = "Thread:ChildTable:Addresses"
|
||||
WPAN_THREAD_NEIGHBOR_TABLE = "Thread:NeighborTable"
|
||||
WPAN_THREAD_NEIGHBOR_TABLE_ASVALMAP = "Thread:NeighborTable:AsValMap"
|
||||
WPAN_THREAD_ROUTER_TABLE = "Thread:RouterTable"
|
||||
WPAN_THREAD_ROUTER_TABLE_ASVALMAP = "Thread:RouterTable:AsValMap"
|
||||
WPAN_THREAD_NETWORK_DATA_VERSION = "Thread:NetworkDataVersion"
|
||||
WPAN_THREAD_STABLE_NETWORK_DATA = "Thread:StableNetworkData"
|
||||
WPAN_THREAD_STABLE_NETWORK_DATA_VERSION = "Thread:StableNetworkDataVersion"
|
||||
WPAN_THREAD_PREFERRED_ROUTER_ID = "Thread:PreferredRouterID"
|
||||
WPAN_THREAD_COMMISSIONER_ENABLED = "Thread:Commissioner:Enabled"
|
||||
WPAN_THREAD_DEVICE_MODE = "Thread:DeviceMode"
|
||||
WPAN_THREAD_OFF_MESH_ROUTES = "Thread:OffMeshRoutes"
|
||||
WPAN_THREAD_ON_MESH_PREFIXES = "Thread:OnMeshPrefixes"
|
||||
WPAN_THREAD_ROUTER_ROLE_ENABLED = "Thread:RouterRole:Enabled"
|
||||
WPAN_THREAD_CONFIG_FILTER_RLOC_ADDRESSES = "Thread:Config:FilterRLOCAddresses"
|
||||
WPAN_THREAD_ACTIVE_DATASET = "Thread:ActiveDataset"
|
||||
WPAN_THREAD_ACTIVE_DATASET_ASVALMAP = "Thread:ActiveDataset:AsValMap"
|
||||
WPAN_THREAD_PENDING_DATASET = "Thread:PendingDataset"
|
||||
WPAN_THREAD_PENDING_DATASET_ASVALMAP = "Thread:PendingDataset:AsValMap"
|
||||
WPAN_THREAD_RLOC16 = "Thread:RLOC16"
|
||||
WPAN_THREAD_ROUTER_ID = "Thread:RouterID"
|
||||
WPAN_THREAD_LEADER_ADDRESS = "Thread:Leader:Address"
|
||||
WPAN_THREAD_LEADER_ROUTER_ID = "Thread:Leader:RouterID"
|
||||
WPAN_THREAD_LEADER_WEIGHT = "Thread:Leader:Weight"
|
||||
WPAN_THREAD_LEADER_LOCAL_WEIGHT = "Thread:Leader:LocalWeight"
|
||||
WPAN_THREAD_LEADER_NETWORK_DATA = "Thread:Leader:NetworkData"
|
||||
WPAN_THREAD_STABLE_LEADER_NETWORK_DATA = "Thread:Leader:StableNetworkData"
|
||||
WPAN_THREAD_NETWORK_DATA = "Thread:NetworkData"
|
||||
WPAN_THREAD_CHILD_TABLE = "Thread:ChildTable"
|
||||
WPAN_THREAD_CHILD_TABLE_ASVALMAP = "Thread:ChildTable:AsValMap"
|
||||
WPAN_THREAD_CHILD_TABLE_ADDRESSES = "Thread:ChildTable:Addresses"
|
||||
WPAN_THREAD_NEIGHBOR_TABLE = "Thread:NeighborTable"
|
||||
WPAN_THREAD_NEIGHBOR_TABLE_ASVALMAP = "Thread:NeighborTable:AsValMap"
|
||||
WPAN_THREAD_ROUTER_TABLE = "Thread:RouterTable"
|
||||
WPAN_THREAD_ROUTER_TABLE_ASVALMAP = "Thread:RouterTable:AsValMap"
|
||||
WPAN_THREAD_NETWORK_DATA_VERSION = "Thread:NetworkDataVersion"
|
||||
WPAN_THREAD_STABLE_NETWORK_DATA = "Thread:StableNetworkData"
|
||||
WPAN_THREAD_STABLE_NETWORK_DATA_VERSION = "Thread:StableNetworkDataVersion"
|
||||
WPAN_THREAD_PREFERRED_ROUTER_ID = "Thread:PreferredRouterID"
|
||||
WPAN_THREAD_COMMISSIONER_ENABLED = "Thread:Commissioner:Enabled"
|
||||
WPAN_THREAD_DEVICE_MODE = "Thread:DeviceMode"
|
||||
WPAN_THREAD_OFF_MESH_ROUTES = "Thread:OffMeshRoutes"
|
||||
WPAN_THREAD_ON_MESH_PREFIXES = "Thread:OnMeshPrefixes"
|
||||
WPAN_THREAD_ROUTER_ROLE_ENABLED = "Thread:RouterRole:Enabled"
|
||||
WPAN_THREAD_CONFIG_FILTER_RLOC_ADDRESSES = "Thread:Config:FilterRLOCAddresses"
|
||||
WPAN_THREAD_ACTIVE_DATASET = "Thread:ActiveDataset"
|
||||
WPAN_THREAD_ACTIVE_DATASET_ASVALMAP = "Thread:ActiveDataset:AsValMap"
|
||||
WPAN_THREAD_PENDING_DATASET = "Thread:PendingDataset"
|
||||
WPAN_THREAD_PENDING_DATASET_ASVALMAP = "Thread:PendingDataset:AsValMap"
|
||||
|
||||
WPAN_OT_LOG_LEVEL = "OpenThread:LogLevel"
|
||||
WPAN_OT_STEERING_DATA_ADDRESS = "OpenThread:SteeringData:Address"
|
||||
@@ -755,6 +755,7 @@ def verify(condition):
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
# Parsing `wpanctl` output
|
||||
|
||||
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
class ScanResult(object):
|
||||
""" This object encapsulates a scan result (active/discover/energy scan)"""
|
||||
|
||||
@@ -850,6 +851,7 @@ def parse_list(list_string):
|
||||
#
|
||||
return [line[2:-1].split()[0] for line in list_string.split('\n')[1:-1]]
|
||||
|
||||
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
class OnMeshPrefix(object):
|
||||
""" This object encapsulates an on-mesh prefix"""
|
||||
|
||||
@@ -920,4 +922,56 @@ class OnMeshPrefix(object):
|
||||
|
||||
def parse_on_mesh_prefix_result(on_mesh_prefix_list):
|
||||
""" Parses on-mesh prefix list string and returns an array of `OnMeshPrefix` objects"""
|
||||
return [ OnMeshPrefix(item) for item in on_mesh_prefix_list.split('\n')[1:-1] ]
|
||||
return [ OnMeshPrefix(item) for item in on_mesh_prefix_list.split('\n')[1:-1] ]
|
||||
|
||||
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
class ChildEntry(object):
|
||||
""" This object encapsulates an child entry"""
|
||||
|
||||
def __init__(self, text):
|
||||
|
||||
# Example of expected text:
|
||||
#
|
||||
# `\t"E24C5F67F4B8CBB9, RLOC16:d402, NetDataVer:175, LQIn:3, AveRssi:-20, LastRssi:-20, Timeout:120, Age:0, `
|
||||
# `RxOnIdle:no, FFD:no, SecDataReq:yes, FullNetData:yes"`
|
||||
#
|
||||
|
||||
# We get rid of the first two chars `\t"' and last char '"', split the rest using whitespace as seperator.
|
||||
# Then remove any ',' at end of items in the list.
|
||||
items = [item[:-1] if item[-1] ==',' else item for item in text[2:-1].split()]
|
||||
|
||||
# First item in the extended address
|
||||
self._ext_address = items[0]
|
||||
|
||||
# Convert the rest into a dictionary by splitting using ':' as seperator
|
||||
dict = {item.split(':')[0] : item.split(':')[1] for item in items[1:]}
|
||||
|
||||
self._rloc16 = dict['RLOC16']
|
||||
self._timeout = dict['Timeout']
|
||||
self._rx_on_idle = (dict['RxOnIdle'] == 'yes')
|
||||
self._ffd = (dict['FFD'] == 'yes')
|
||||
|
||||
@property
|
||||
def ext_address(self):
|
||||
return self._ext_address
|
||||
|
||||
@property
|
||||
def rloc16(self):
|
||||
return self._rloc16
|
||||
|
||||
@property
|
||||
def timeout(self):
|
||||
return self._timeout
|
||||
|
||||
def is_rx_on_when_idle(self):
|
||||
return self._rx_on_idle
|
||||
|
||||
def is_ffd(self):
|
||||
return self._ffd
|
||||
|
||||
def __repr__(self):
|
||||
return 'ChildEntry({})'.format(self.__dict__)
|
||||
|
||||
def parse_child_table_result(child_table_list):
|
||||
""" Parses child table list string and returns an array of `ChildEntry` objects"""
|
||||
return [ ChildEntry(item) for item in child_table_list.split('\n')[1:-1] ]
|
||||
|
||||
Reference in New Issue
Block a user