[toranj] test behavior of "inform previous parent" feature (#2964)

This commit adds a new test-case under `toranj` frame-work to verify
the behavior of devices under the "inform previous parent" feature.

With this feature enabled, when a child attaches to a new parent, it
will send an IP message (with empty payload) to its previous parent.
Upon receiving this message the previous parent would immediately
remove the child from its child table.

The test network topology consists of two routers/parents and a single
sleepy child. The child is first attached a parent and is then forced
to switch its parent. It's then verified that the child is removed
from first parent's child table.
This commit is contained in:
Abtin Keshavarzian
2018-08-15 10:27:52 -07:00
committed by Jonathan Hui
parent 920197398b
commit ce48bf1819
3 changed files with 167 additions and 0 deletions
+1
View File
@@ -131,6 +131,7 @@ run test-014-ip6-address-add.py
run test-015-same-prefix-on-multiple-nodes.py
run test-016-neighbor-table.py
run test-017-parent-reset-child-recovery.py
run test-019-inform-previous-parent.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,159 @@
#!/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: Test behavior of "Inform Previous Parent" feature
#
# With this feature enabled, when a child attaches to a new parent, it will send
# an IP message (with empty payload and mesh-local IP address as the source
# address) to its previous parent. Upon receiving this message the previous
# parent would immediately remove the child from its child table. Without this
# feature, the child entry on previous parent would stay (and parent would
# continue to queue messages for the sleepy child) until the child is timed out
# and removed from child table.
#
#
# Test topology:
#
# `child` is first attached to `parent2`. It is then forced to switch to `parent1`
#
# parent1--- parent2
# . /
# \ /
# . /
# child
#
# This test verifies the behavior of the child and parents under this feature.
#
# Note that `OPENTHREAD_CONFIG_INFORM_PREVIOUS_PARENT_ON_REATTACH` is enabled in
# `openthread-core-toranj.config.h` header file.
#
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)
parent1 = wpan.Node()
parent2 = wpan.Node()
child = wpan.Node()
#-----------------------------------------------------------------------------------------------------------------------
# Init all nodes
wpan.Node.init_all_nodes()
#-----------------------------------------------------------------------------------------------------------------------
# Build network topology
#
# `child` is first attached to `parent2`. It is then forced to switch to `parent1`.
#
# parent1--- parent2
# . /
# \ /
# . /
# child
#
parent1.whitelist_node(parent2)
parent2.whitelist_node(parent1)
parent2.whitelist_node(child)
parent1.form("inform-parent")
parent2.join_node(parent1, wpan.JOIN_TYPE_ROUTER)
child.join_node(parent2, wpan.JOIN_TYPE_SLEEPY_END_DEVICE);
child.set(wpan.WPAN_POLL_INTERVAL, '300')
#-----------------------------------------------------------------------------------------------------------------------
# Test implementation
#
CHILD_SUPERVISION_CHECK_TIMEOUT = 2
PARENT_SUPERVISION_INTERVAL = 1
# Verify the `child` is attached to `parent2`.
child_table = wpan.parse_list(parent2.get(wpan.WPAN_THREAD_CHILD_TABLE))
verify(len(child_table) == 1)
# Remove the `child` from whitelist of `parent2` and add it to whitelist of `parent1` instead.
parent1.whitelist_node(child)
parent2.un_whitelist_node(child)
# Enable supervision check on the `child` and also on `parent1`.
child.set(wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT, str(CHILD_SUPERVISION_CHECK_TIMEOUT))
parent1.set(wpan.WPAN_CHILD_SUPERVISION_INTERVAL, str(PARENT_SUPERVISION_INTERVAL))
# Since child supervision is not enabled on `parent2` and the `child` is
# removed from whitelist on `parent2`, after the supervision check timeout
# the `child` should realize that it can no longer talk to its current
# parent (`parent2`) and try to reattach. All re-attach attempts to `parent2`
# should fail (due to whitelist) and cause the `child` to get detached and
# search for a new parent and then attach to `parent1`.
#
# To verify that the `child` does get detached and attach to a new parent, we
# monitor the number of state changes using wpantund property "stat:ncp".
child_num_state_changes = len(wpan.parse_list(child.get("stat:ncp")))
def check_child_is_reattached():
verify(len(wpan.parse_list(child.get("stat:ncp"))) > child_num_state_changes)
child_is_in_parent2_table = (len(wpan.parse_list(parent2.get(wpan.WPAN_THREAD_CHILD_TABLE)))==1)
verify(child.is_associated())
wpan.verify_within(check_child_is_reattached, CHILD_SUPERVISION_CHECK_TIMEOUT / speedup + 5)
# Verify that the `child` is now attached to `parent1`
child_table = wpan.parse_list(parent1.get(wpan.WPAN_THREAD_CHILD_TABLE))
verify(len(child_table) == 1)
# Finally verify that the `child` is removed from previous parent's child
# table (which indicates that the `child` did indeed inform its previous
# parent).
def check_child_is_removed_from_parent2_table():
child_table = wpan.parse_list(parent2.get(wpan.WPAN_THREAD_CHILD_TABLE))
verify(len(child_table) == 0)
wpan.verify_within(check_child_is_removed_from_parent2_table, 1)
#-----------------------------------------------------------------------------------------------------------------------
# Test finished
wpan.Node.finalize_all_nodes()
print '\'{}\' passed.'.format(test_name)
+7
View File
@@ -112,6 +112,9 @@ WPAN_MAC_BLACKLIST_ENABLED = "MAC:Blacklist:Enabled"
WPAN_MAC_BLACKLIST_ENTRIES = "MAC:Blacklist:Entries"
WPAN_MAC_BLACKLIST_ENTRIES_ASVALMAP = "MAC:Blacklist:Entries:AsValMap"
WPAN_CHILD_SUPERVISION_INTERVAL = "ChildSupervision:Interval"
WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT = "ChildSupervision:CheckTimeout"
WPAN_JAM_DETECTION_STATUS = "JamDetection:Status"
WPAN_JAM_DETECTION_ENABLE = "JamDetection:Enable"
WPAN_JAM_DETECTION_RSSI_THRESHOLD = "JamDetection:RssiThreshold"
@@ -422,6 +425,10 @@ class Node(object):
self.add(WPAN_MAC_WHITELIST_ENTRIES, node.get(WPAN_EXT_ADDRESS)[1:-1])
self.set(WPAN_MAC_WHITELIST_ENABLED, '1')
def un_whitelist_node(self, node):
"""Removes a given node (of node `Node) from the whitelist"""
self.remove(WPAN_MAC_WHITELIST_ENTRIES, node.get(WPAN_EXT_ADDRESS)[1:-1])
def is_in_scan_result(self, scan_result):
"""Checks if node is in the scan results
`scan_result` must be an array of `ScanResult` object (see `parse_scan_result`).