[test] add multi-radio and TREL test cases (#4440)

This commit  adds new builds under "simulation-build-autotools"
to cover multi-radio configurations: TREL only, 15.4+TREL,
15.4+TREL+ToBLE.

It also adds new GitHub action tests covering all `toranj`
test-cases with different configurations:

  1) NCP model with TREL radio only,
  2) POSIX App and RCP model with TREL radio only (emulated over
     a virtual Ethernet netif).
This commit is contained in:
Abtin Keshavarzian
2020-12-10 11:46:07 -08:00
committed by Jonathan Hui
parent 451ae39be4
commit 3d21a846d9
11 changed files with 1186 additions and 9 deletions
+103
View File
@@ -44,6 +44,7 @@ jobs:
runs-on: ubuntu-18.04
env:
COVERAGE: 1
TORANJ_RADIO : 15.4
steps:
- uses: actions/checkout@v2
- name: Bootstrap
@@ -81,6 +82,7 @@ jobs:
env:
COVERAGE: 1
TORANJ_POSIX_RCP_MODEL: 1
TORANJ_RADIO : 15.4
steps:
- uses: actions/checkout@v2
- name: Bootstrap
@@ -113,10 +115,111 @@ jobs:
name: cov-toranj-rcp
path: tmp/coverage.info
toranj-ncp-trel:
runs-on: ubuntu-18.04
env:
COVERAGE: 1
TORANJ_RADIO : trel
steps:
- uses: actions/checkout@v2
- name: Bootstrap
run: |
sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
sudo apt-get --no-install-recommends install -y dbus libdbus-1-dev
sudo apt-get --no-install-recommends install -y autoconf-archive
sudo apt-get --no-install-recommends install -y bsdtar
sudo apt-get --no-install-recommends install -y libtool
sudo apt-get --no-install-recommends install -y libglib2.0-dev
sudo apt-get --no-install-recommends install -y libboost-dev libboost-signals-dev
sudo apt-get --no-install-recommends install -y lcov
git clone --depth=1 --branch=master https://github.com/openthread/wpantund.git
cd wpantund
./bootstrap.sh
./configure
sudo make -j2
sudo make install
- name: Build & Run
run: |
top_builddir=$(pwd)/build/toranj ./tests/toranj/start.sh
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- uses: actions/upload-artifact@v2
with:
name: cov-toranj-ncp-trel
path: tmp/coverage.info
toranj-rcp-trel:
runs-on: ubuntu-18.04
env:
COVERAGE: 1
TORANJ_POSIX_RCP_MODEL: 1
TORANJ_RADIO : trel
steps:
- uses: actions/checkout@v2
- name: Bootstrap
run: |
sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
sudo apt-get --no-install-recommends install -y dbus libdbus-1-dev
sudo apt-get --no-install-recommends install -y autoconf-archive
sudo apt-get --no-install-recommends install -y bsdtar
sudo apt-get --no-install-recommends install -y libtool
sudo apt-get --no-install-recommends install -y libglib2.0-dev
sudo apt-get --no-install-recommends install -y libboost-dev libboost-signals-dev
sudo apt-get --no-install-recommends install -y lcov
git clone --depth=1 --branch=master https://github.com/openthread/wpantund.git
cd wpantund
./bootstrap.sh
./configure
sudo make -j2
sudo make install
- name: Build & Run
run: |
top_builddir=$(pwd)/build/toranj ./tests/toranj/start.sh
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- uses: actions/upload-artifact@v2
with:
name: cov-toranj-rcp-trel
path: tmp/coverage.info
toranj-ncp-multi:
runs-on: ubuntu-18.04
env:
COVERAGE: 1
TORANJ_RADIO : multi
steps:
- uses: actions/checkout@v2
- name: Bootstrap
run: |
sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
sudo apt-get --no-install-recommends install -y dbus libdbus-1-dev
sudo apt-get --no-install-recommends install -y autoconf-archive
sudo apt-get --no-install-recommends install -y bsdtar
sudo apt-get --no-install-recommends install -y libtool
sudo apt-get --no-install-recommends install -y libglib2.0-dev
sudo apt-get --no-install-recommends install -y libboost-dev libboost-signals-dev
sudo apt-get --no-install-recommends install -y lcov
git clone --depth=1 --branch=master https://github.com/openthread/wpantund.git
cd wpantund
./bootstrap.sh
./configure
sudo make -j2
sudo make install
- name: Build & Run
run: |
top_builddir=$(pwd)/build/toranj ./tests/toranj/start.sh
upload-coverage:
needs:
- toranj-ncp
- toranj-rcp
- toranj-ncp-trel
- toranj-rcp-trel
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
+56
View File
@@ -192,12 +192,68 @@ build_nest_common()
cd ..
}
build_multi_radio_links()
{
# TREL radio link only.
local options=(
"-DOPENTHREAD_CONFIG_NCP_UART_ENABLE=1"
"-DOPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE=1"
"-DOPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_DEBG"
"-DOPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE=0"
"-DOPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE=1"
)
export CPPFLAGS="${options[*]}"
reset_source
mkdir build && cd build
../configure \
--enable-cli \
--enable-ftd \
--enable-mtd \
--enable-ncp \
--enable-radio-only \
--with-examples=simulation \
--disable-docs \
--disable-tests
make -j"${OT_BUILD_JOBS}"
cd ..
# Multi radio link - 15.4 and TREL.
options=(
"-DOPENTHREAD_CONFIG_NCP_UART_ENABLE=1"
"-DOPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE=1"
"-DOPENTHREAD_CONFIG_DIAG_ENABLE=1"
"-DOPENTHREAD_CONFIG_LINK_RAW_ENABLE=1"
"-DOPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE=1"
"-DOPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_DEBG"
"-DOPENTHREAD_CONFIG_MAC_FILTER_ENABLE=1"
"-DOPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE=1"
"-DOPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE=1"
)
export CPPFLAGS="${options[*]}"
reset_source
mkdir build && cd build
../configure \
--enable-cli \
--enable-ftd \
--enable-mtd \
--enable-ncp \
--enable-radio-only \
--with-examples=simulation \
--disable-docs \
--disable-tests
make -j"${OT_BUILD_JOBS}"
cd ..
}
main()
{
./bootstrap
build_all_features
build_nest_common
build_multi_radio_links
}
main "$@"
+3
View File
@@ -134,6 +134,7 @@ case ${build_config} in
--with-examples=simulation \
"${configure_options[@]}" || die
make -j 8 || die
cp -p ${top_builddir}/examples/apps/ncp/ot-ncp-ftd ${top_builddir}/examples/apps/ncp/ot-ncp-ftd-15.4
;;
ncp-trel)
@@ -150,6 +151,7 @@ case ${build_config} in
--with-examples=simulation \
"${configure_options[@]}" || die
make -j 8 || die
cp -p ${top_builddir}/examples/apps/ncp/ot-ncp-ftd ${top_builddir}/examples/apps/ncp/ot-ncp-ftd-trel
;;
ncp-15.4+trel | ncp-trel+15.4)
@@ -166,6 +168,7 @@ case ${build_config} in
--with-examples=simulation \
"${configure_options[@]}" || die
make -j 8 || die
cp -p ${top_builddir}/examples/apps/ncp/ot-ncp-ftd ${top_builddir}/examples/apps/ncp/ot-ncp-ftd-15.4-trel
;;
rcp)
+28 -4
View File
@@ -75,8 +75,8 @@ run()
return
fi
# We allow a failed test to be retried up to 3 attempts.
if [ "$counter" -lt 2 ]; then
# We allow a failed test to be retried up to 7 attempts.
if [ "$counter" -lt 7 ]; then
counter=$((counter + 1))
echo Attempt $counter running "$1" failed. Trying again.
cleanup
@@ -92,6 +92,10 @@ run()
cd "$(dirname "$0")" || die "cd failed"
if [ -z "${top_builddir}" ]; then
top_builddir=.
fi
if [ "$COVERAGE" = 1 ]; then
coverage_option="--enable-coverage"
else
@@ -108,8 +112,17 @@ case $TORANJ_POSIX_RCP_MODEL in
esac
if [ "$use_posix_with_rcp" = "no" ]; then
./build.sh ${coverage_option} ncp-"${TORANJ_RADIO}" || die "ncp build failed"
if [ "$TORANJ_RADIO" = "multi" ]; then
# Build all combinations
./build.sh ${coverage_option} ncp-15.4 || die "ncp-15.4 build failed"
(cd ${top_builddir} && make clean) || die "cd and clean failed"
./build.sh ${coverage_option} ncp-trel || die "ncp-trel build failed"
(cd ${top_builddir} && make clean) || die "cd and clean failed"
./build.sh ${coverage_option} ncp-15.4+trel || die "ncp-15.4+trel build failed"
(cd ${top_builddir} && make clean) || die "cd and clean failed"
else
./build.sh ${coverage_option} ncp-"${TORANJ_RADIO}" || die "ncp build failed"
fi
else
./build.sh ${coverage_option} rcp || die "rcp build failed"
./build.sh ${coverage_option} posix-"${TORANJ_RADIO}" || die "posix build failed"
@@ -121,6 +134,17 @@ fi
cleanup
if [ "$TORANJ_RADIO" = "multi" ]; then
run test-700-multi-radio-join.py
run test-701-multi-radio-probe.py
run test-702-multi-radio-discovery-by-rx.py
run test-703-multi-radio-mesh-header-msg.py
run test-704-multi-radio-scan.py
run test-705-multi-radio-discover-scan.py
exit 0
fi
run test-001-get-set.py
run test-002-form.py
run test-003-join.py
+131
View File
@@ -0,0 +1,131 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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: This test covers joining of nodes with different radio links
#
# Parent node with trel and 15.4 with 3 children.
#
# parent
# (trel + 15.4)
# / | \
# / | \
# / | \
# c1 c2 c3
# (15.4) (trel) (trel+15.4)
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)
parent = wpan.Node(wpan.NODE_15_4_TREL)
c1 = wpan.Node(wpan.NODE_15_4)
c2 = wpan.Node(wpan.NODE_TREL)
c3 = wpan.Node(wpan.NODE_15_4_TREL)
# -----------------------------------------------------------------------------------------------------------------------
# Init all nodes
wpan.Node.init_all_nodes()
# -----------------------------------------------------------------------------------------------------------------------
# Test implementation
WAIT_TIME = 5
# Verify that each node supports the correct radio links:
parent_radios = wpan.parse_list(parent.get(wpan.WPAN_OT_SUPPORTED_RADIO_LINKS))
verify(
len(parent_radios) == 2 and (wpan.RADIO_LINK_IEEE_802_15_4 in parent_radios) and
(wpan.RADIO_LINK_TREL_UDP6 in parent_radios))
c1_radios = wpan.parse_list(c1.get(wpan.WPAN_OT_SUPPORTED_RADIO_LINKS))
verify(len(c1_radios) == 1 and wpan.RADIO_LINK_IEEE_802_15_4 in c1_radios)
c2_radios = wpan.parse_list(c2.get(wpan.WPAN_OT_SUPPORTED_RADIO_LINKS))
verify(len(c2_radios) == 1 and wpan.RADIO_LINK_TREL_UDP6 in c2_radios)
c3_radios = wpan.parse_list(c3.get(wpan.WPAN_OT_SUPPORTED_RADIO_LINKS))
verify(
len(c3_radios) == 2 and (wpan.RADIO_LINK_IEEE_802_15_4 in c3_radios) and (wpan.RADIO_LINK_TREL_UDP6 in c3_radios))
parent.form("multi-radio")
c1.join_node(parent, wpan.JOIN_TYPE_END_DEVICE)
c2.join_node(parent, wpan.JOIN_TYPE_END_DEVICE)
c3.join_node(parent, wpan.JOIN_TYPE_END_DEVICE)
# Verify that parent correctly knows about all the children and their supported radio links
radio_info_entries = wpan.parse_multi_radio_result(parent.get(wpan.WPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO))
verify(len(radio_info_entries) == 3)
for node in [c1, c2, c3]:
# Find the entry matching node ext address
address = node.get(wpan.WPAN_EXT_ADDRESS)[1:-1]
filtred_list = [entry for entry in radio_info_entries if entry.ext_address == address]
verify(len(filtred_list) == 1)
entry = filtred_list[0]
# Verify that the multi radio info matches the radio links supported by the node
node_radios = wpan.parse_list(node.get(wpan.WPAN_OT_SUPPORTED_RADIO_LINKS))
verify(len(node_radios) == len(entry.radios))
for radio in node_radios:
verify(entry.supports(radio))
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Reset the parent and check that all children are attached back successfully
parent.reset()
def check_all_children_are_attached():
neighbor_table = wpan.parse_neighbor_table_result(parent.get(wpan.WPAN_THREAD_NEIGHBOR_TABLE))
verify(len(neighbor_table) == 3)
wpan.verify_within(check_all_children_are_attached, WAIT_TIME)
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
wpan.Node.finalize_all_nodes()
print('\'{}\' passed.'.format(test_name))
+205
View File
@@ -0,0 +1,205 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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: This test covers behavior of device after trel network is temporarily disabled
# and rediscovery of trel radio using probe mechanism.
#
# r1 ---------- r2
# (15.4+trel) (15.4+trel)
#
# On r2 we disable trel temporarily.
#
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)
r1 = wpan.Node(wpan.NODE_15_4_TREL)
r2 = wpan.Node(wpan.NODE_15_4_TREL)
c2 = wpan.Node(wpan.NODE_15_4)
# -----------------------------------------------------------------------------------------------------------------------
# Init all nodes
wpan.Node.init_all_nodes()
# -----------------------------------------------------------------------------------------------------------------------
# Build network topology
#
r1.allowlist_node(r2)
r2.allowlist_node(r1)
r2.allowlist_node(c2)
c2.allowlist_node(r2)
r1.form("discover-by-rx")
r2.join_node(r1, wpan.JOIN_TYPE_ROUTER)
c2.join_node(r2, wpan.JOIN_TYPE_SLEEPY_END_DEVICE)
# -----------------------------------------------------------------------------------------------------------------------
# Test implementation
WAIT_TIME = 5
HIGH_PREFERENCE_THRESHOLD = 220
MIN_PREFERENCE_THRESHOLD = 0
r1_ext_address = r1.get(wpan.WPAN_EXT_ADDRESS)[1:-1]
r1_rloc = int(r1.get(wpan.WPAN_THREAD_RLOC16), 16)
r1_ml_address = r1.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
r2_ext_address = r2.get(wpan.WPAN_EXT_ADDRESS)[1:-1]
r2_rloc = int(r2.get(wpan.WPAN_THREAD_RLOC16), 16)
r2_ml_address = r2.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
# Wait for r2 to become router and r1 establishes a link with it
def check_r1_router_table():
router_table = wpan.parse_router_table_result(r1.get(wpan.WPAN_THREAD_ROUTER_TABLE))
verify(len(router_table) == 2)
for entry in router_table:
verify(entry.rloc16 == r1_rloc or entry.is_link_established())
wpan.verify_within(check_r1_router_table, WAIT_TIME)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Check that r1 detected both trel and 15.4 as supported radio by r2
r1_radios = wpan.parse_list(r1.get(wpan.WPAN_OT_SUPPORTED_RADIO_LINKS))
verify(
len(r1_radios) == 2 and (wpan.RADIO_LINK_IEEE_802_15_4 in r1_radios) and (wpan.RADIO_LINK_TREL_UDP6 in r1_radios))
r2_radios = wpan.parse_list(r2.get(wpan.WPAN_OT_SUPPORTED_RADIO_LINKS))
verify(
len(r2_radios) == 2 and (wpan.RADIO_LINK_IEEE_802_15_4 in r2_radios) and (wpan.RADIO_LINK_TREL_UDP6 in r2_radios))
def check_r1_sees_r2_has_two_radio_links():
r1_neighbor_radios = wpan.parse_multi_radio_result(r1.get(wpan.WPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO))
verify(len(r1_neighbor_radios) == 1)
verify(len(r1_neighbor_radios[0].radios) == 2)
wpan.verify_within(check_r1_sees_r2_has_two_radio_links, WAIT_TIME)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Send from r1 to r2 and verify that r1 prefers trel radio link for sending to r2.
sender = r1.prepare_tx(r1_ml_address, r2_ml_address, "Hi r2 from r1", 5)
recver = r2.prepare_rx(sender)
wpan.Node.perform_async_tx_rx()
verify(sender.was_successful)
verify(recver.was_successful)
r1_neighbor_radios = wpan.parse_multi_radio_result(r1.get(wpan.WPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO))
verify(len(r1_neighbor_radios) == 1)
r2_radio_info = r1_neighbor_radios[0]
verify(r2_radio_info.supports(wpan.RADIO_LINK_TREL_UDP6))
verify(r2_radio_info.preference(wpan.RADIO_LINK_TREL_UDP6) >= HIGH_PREFERENCE_THRESHOLD)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Now disable trel link on r2 and send again. We expect that r1 would quickly learn that trel is
# no longer supported by r2 and prefer 15.4 for tx to r2.
r2.set(wpan.WPAN_OT_TREL_TEST_MODE_ENABLE, 'false')
verify(r2.get(wpan.WPAN_OT_TREL_TEST_MODE_ENABLE) == 'false')
sender = r1.prepare_tx(r1_ml_address, r2_ml_address, "Hi again r2 from r1", 5)
wpan.Node.perform_async_tx_rx()
verify(sender.was_successful)
def check_r1_does_not_prefer_trel_for_r2():
r1_neighbor_radios = wpan.parse_multi_radio_result(r1.get(wpan.WPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO))
verify(len(r1_neighbor_radios) == 1)
r2_radio_info = r1_neighbor_radios[0]
verify(r2_radio_info.supports(wpan.RADIO_LINK_TREL_UDP6))
verify(r2_radio_info.preference(wpan.RADIO_LINK_TREL_UDP6) <= MIN_PREFERENCE_THRESHOLD)
wpan.verify_within(check_r1_does_not_prefer_trel_for_r2, WAIT_TIME)
# Check that we can send between r1 and r2 (now all tx should use 15.4)
sender = r1.prepare_tx(r1_ml_address, r2_ml_address, "Hi on 15.4 r2 from r1", 5)
recver = r2.prepare_rx(sender)
wpan.Node.perform_async_tx_rx()
verify(sender.was_successful)
verify(recver.was_successful)
r1_neighbor_radios = wpan.parse_multi_radio_result(r1.get(wpan.WPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO))
verify(len(r1_neighbor_radios) == 1)
r2_radio_info = r1_neighbor_radios[0]
verify(r2_radio_info.supports(wpan.RADIO_LINK_TREL_UDP6))
verify(r2_radio_info.preference(wpan.RADIO_LINK_TREL_UDP6) <= MIN_PREFERENCE_THRESHOLD)
verify(r2_radio_info.supports(wpan.RADIO_LINK_IEEE_802_15_4))
verify(r2_radio_info.preference(wpan.RADIO_LINK_IEEE_802_15_4) >= HIGH_PREFERENCE_THRESHOLD)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Enable trel back on r2, start sending traffic from r1 to r2
# The probe mechanism should kick and detect that r2 has trel again.
r2.set(wpan.WPAN_OT_TREL_TEST_MODE_ENABLE, 'true')
verify(r2.get(wpan.WPAN_OT_TREL_TEST_MODE_ENABLE) == 'true')
sender = r1.prepare_tx(r1_ml_address, r2_ml_address, "Probing r2 from r1", 80)
recver = r2.prepare_rx(sender)
wpan.Node.perform_async_tx_rx()
verify(sender.was_successful)
verify(recver.was_successful)
def check_r1_again_prefers_trel_for_r2():
r1_neighbor_radios = wpan.parse_multi_radio_result(r1.get(wpan.WPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO))
verify(len(r1_neighbor_radios) == 1)
r2_radio_info = r1_neighbor_radios[0]
verify(r2_radio_info.supports(wpan.RADIO_LINK_TREL_UDP6))
verify(r2_radio_info.preference(wpan.RADIO_LINK_TREL_UDP6) >= HIGH_PREFERENCE_THRESHOLD)
wpan.verify_within(check_r1_again_prefers_trel_for_r2, WAIT_TIME)
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
wpan.Node.finalize_all_nodes()
print('\'{}\' passed.'.format(test_name))
@@ -0,0 +1,206 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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: This test covers behavior of device after TREL network is temporarily disabled
# and rediscovery of TREL by receiving message over that radio from the neighbor.
#
# r1 ---------- r2
# (15.4+trel) (15.4+trel)
#
# On r2 we disable trel temporarily.
#
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)
r1 = wpan.Node(wpan.NODE_15_4_TREL)
r2 = wpan.Node(wpan.NODE_15_4_TREL)
c2 = wpan.Node(wpan.NODE_15_4)
# -----------------------------------------------------------------------------------------------------------------------
# Init all nodes
wpan.Node.init_all_nodes()
# -----------------------------------------------------------------------------------------------------------------------
# Build network topology
#
r1.allowlist_node(r2)
r2.allowlist_node(r1)
r2.allowlist_node(c2)
c2.allowlist_node(r2)
r1.form("discover-by-rx")
r2.join_node(r1, wpan.JOIN_TYPE_ROUTER)
c2.join_node(r2, wpan.JOIN_TYPE_SLEEPY_END_DEVICE)
# -----------------------------------------------------------------------------------------------------------------------
# Test implementation
WAIT_TIME = 5
HIGH_PREFERENCE_THRESHOLD = 220
MIN_PREFERENCE_THRESHOLD = 0
r1_ext_address = r1.get(wpan.WPAN_EXT_ADDRESS)[1:-1]
r1_rloc = int(r1.get(wpan.WPAN_THREAD_RLOC16), 16)
r1_ml_address = r1.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
r2_ext_address = r2.get(wpan.WPAN_EXT_ADDRESS)[1:-1]
r2_rloc = int(r2.get(wpan.WPAN_THREAD_RLOC16), 16)
r2_ml_address = r2.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
# Wait for r2 to become router and r1 establishes a link with it
def check_r1_router_table():
router_table = wpan.parse_router_table_result(r1.get(wpan.WPAN_THREAD_ROUTER_TABLE))
verify(len(router_table) == 2)
for entry in router_table:
verify(entry.rloc16 == r1_rloc or entry.is_link_established())
wpan.verify_within(check_r1_router_table, WAIT_TIME)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Check that r1 detected both TREL and 15.4 as supported radio by r2
r1_radios = wpan.parse_list(r1.get(wpan.WPAN_OT_SUPPORTED_RADIO_LINKS))
verify(
len(r1_radios) == 2 and (wpan.RADIO_LINK_IEEE_802_15_4 in r1_radios) and (wpan.RADIO_LINK_TREL_UDP6 in r1_radios))
r2_radios = wpan.parse_list(r2.get(wpan.WPAN_OT_SUPPORTED_RADIO_LINKS))
verify(
len(r2_radios) == 2 and (wpan.RADIO_LINK_IEEE_802_15_4 in r2_radios) and (wpan.RADIO_LINK_TREL_UDP6 in r2_radios))
def check_r1_sees_r2_has_two_radio_links():
r1_neighbor_radios = wpan.parse_multi_radio_result(r1.get(wpan.WPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO))
verify(len(r1_neighbor_radios) == 1)
verify(len(r1_neighbor_radios[0].radios) == 2)
wpan.verify_within(check_r1_sees_r2_has_two_radio_links, WAIT_TIME)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Send from r1 to r2 and verify that r1 prefers TREL radio link for sending to r2.
sender = r1.prepare_tx(r1_ml_address, r2_ml_address, "Hi r2 from r1", 5)
recver = r2.prepare_rx(sender)
wpan.Node.perform_async_tx_rx()
verify(sender.was_successful)
verify(recver.was_successful)
r1_neighbor_radios = wpan.parse_multi_radio_result(r1.get(wpan.WPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO))
verify(len(r1_neighbor_radios) == 1)
r2_radio_info = r1_neighbor_radios[0]
verify(r2_radio_info.supports(wpan.RADIO_LINK_TREL_UDP6))
verify(r2_radio_info.preference(wpan.RADIO_LINK_TREL_UDP6) >= HIGH_PREFERENCE_THRESHOLD)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Now disable TREL link on r2 and send again. We expect that r1 would quickly learn that trel is
# no longer supported by r2 and prefer 15.4 for tx to r2.
r2.set(wpan.WPAN_OT_TREL_TEST_MODE_ENABLE, 'false')
verify(r2.get(wpan.WPAN_OT_TREL_TEST_MODE_ENABLE) == 'false')
sender = r1.prepare_tx(r1_ml_address, r2_ml_address, "Hi again r2 from r1", 5)
wpan.Node.perform_async_tx_rx()
verify(sender.was_successful)
def check_r1_does_not_prefer_trel_for_r2():
r1_neighbor_radios = wpan.parse_multi_radio_result(r1.get(wpan.WPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO))
verify(len(r1_neighbor_radios) == 1)
r2_radio_info = r1_neighbor_radios[0]
verify(r2_radio_info.supports(wpan.RADIO_LINK_TREL_UDP6))
verify(r2_radio_info.preference(wpan.RADIO_LINK_TREL_UDP6) <= MIN_PREFERENCE_THRESHOLD)
wpan.verify_within(check_r1_does_not_prefer_trel_for_r2, WAIT_TIME)
# Check that we can send between r1 and r2 (now all tx should use 15.4)
sender = r1.prepare_tx(r1_ml_address, r2_ml_address, "Hi on 15.4 r2 from r1", 5)
recver = r2.prepare_rx(sender)
wpan.Node.perform_async_tx_rx()
verify(sender.was_successful)
verify(recver.was_successful)
r1_neighbor_radios = wpan.parse_multi_radio_result(r1.get(wpan.WPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO))
verify(len(r1_neighbor_radios) == 1)
r2_radio_info = r1_neighbor_radios[0]
verify(r2_radio_info.supports(wpan.RADIO_LINK_TREL_UDP6))
verify(r2_radio_info.preference(wpan.RADIO_LINK_TREL_UDP6) <= MIN_PREFERENCE_THRESHOLD)
verify(r2_radio_info.supports(wpan.RADIO_LINK_IEEE_802_15_4))
verify(r2_radio_info.preference(wpan.RADIO_LINK_IEEE_802_15_4) >= HIGH_PREFERENCE_THRESHOLD)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Enable trel back on r2, start sending traffic from r2 to r1.
# r2 would use probe mehcnasim to discover that trel is enabled again
# r1 should notice new rx on trel and update trel preference for r1
r2.set(wpan.WPAN_OT_TREL_TEST_MODE_ENABLE, 'true')
verify(r2.get(wpan.WPAN_OT_TREL_TEST_MODE_ENABLE) == 'true')
sender = r2.prepare_tx(r2_ml_address, r1_ml_address, "Probing r1 from r2", 80)
recver = r1.prepare_rx(sender)
wpan.Node.perform_async_tx_rx()
verify(sender.was_successful)
verify(recver.was_successful)
def check_r1_again_prefers_trel_for_r2():
r1_neighbor_radios = wpan.parse_multi_radio_result(r1.get(wpan.WPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO))
verify(len(r1_neighbor_radios) == 1)
r2_radio_info = r1_neighbor_radios[0]
verify(r2_radio_info.supports(wpan.RADIO_LINK_TREL_UDP6))
verify(r2_radio_info.preference(wpan.RADIO_LINK_TREL_UDP6) >= HIGH_PREFERENCE_THRESHOLD)
wpan.verify_within(check_r1_again_prefers_trel_for_r2, WAIT_TIME)
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
wpan.Node.finalize_all_nodes()
print('\'{}\' passed.'.format(test_name))
@@ -0,0 +1,142 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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: This test covers behavior of MeshHeader messages (message sent over multi-hop) when the
# underlying devices (in the path) support different radio types with different frame MTU length.
#
# r1 --------- r2 ---------- r3
# (trel) (15.4+trel) (15.4)
# |
# |
# c3 (15.4)
#
# c3 is used for quick router promotion of r3.
#
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)
r1 = wpan.Node(wpan.NODE_TREL)
r2 = wpan.Node(wpan.NODE_15_4_TREL)
r3 = wpan.Node(wpan.NODE_15_4)
c3 = wpan.Node(wpan.NODE_15_4)
# -----------------------------------------------------------------------------------------------------------------------
# Init all nodes
wpan.Node.init_all_nodes()
# -----------------------------------------------------------------------------------------------------------------------
# Build network topology
#
r1.allowlist_node(r2)
r2.allowlist_node(r1)
r2.allowlist_node(r3)
r3.allowlist_node(r2)
r3.allowlist_node(c3)
c3.allowlist_node(r3)
r1.form("mesh-header")
r2.join_node(r1, wpan.JOIN_TYPE_ROUTER)
r3.join_node(r2, wpan.JOIN_TYPE_ROUTER)
c3.join_node(r3, wpan.JOIN_TYPE_END_DEVICE)
# -----------------------------------------------------------------------------------------------------------------------
# Test implementation
WAIT_TIME = 5
INVALID_ROUTER_ID = 63
MSG_LEN = 1000
MSG_COUNT = 7
# Verify that each node supports the correct radio links.
r1_radios = wpan.parse_list(r1.get(wpan.WPAN_OT_SUPPORTED_RADIO_LINKS))
verify(len(r1_radios) == 1 and (wpan.RADIO_LINK_TREL_UDP6 in r1_radios))
r2_radios = wpan.parse_list(r2.get(wpan.WPAN_OT_SUPPORTED_RADIO_LINKS))
verify(
len(r2_radios) == 2 and (wpan.RADIO_LINK_IEEE_802_15_4 in r2_radios) and (wpan.RADIO_LINK_TREL_UDP6 in r2_radios))
r3_radios = wpan.parse_list(r3.get(wpan.WPAN_OT_SUPPORTED_RADIO_LINKS))
verify(len(r3_radios) == 1 and (wpan.RADIO_LINK_IEEE_802_15_4 in r3_radios))
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Wait for all router entries are updated
r1_rloc = int(r1.get(wpan.WPAN_THREAD_RLOC16), 16)
def check_r1_router_table():
router_table = wpan.parse_router_table_result(r1.get(wpan.WPAN_THREAD_ROUTER_TABLE))
verify(len(router_table) == 3)
for entry in router_table:
verify(entry.rloc16 == r1_rloc or entry.is_link_established() or entry.next_hop != INVALID_ROUTER_ID)
wpan.verify_within(check_r1_router_table, WAIT_TIME)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Send large message from r1 to r3. Such a messag would be sent as a
# Mesh Header message. Note that the origin r1 is a trel-only device
# whereas the final destination r3 is a 15.4-only device. The originator
# should use smaller MTU size (which then fragment the message into
# multiple frames) otherwise the 15.4 link won't be able to handle it.
r1_ml_address = r1.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
r3_ml_address = r3.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
sender = r1.prepare_tx(r1_ml_address, r3_ml_address, MSG_LEN, MSG_COUNT)
recver = r3.prepare_rx(sender)
wpan.Node.perform_async_tx_rx()
verify(sender.was_successful)
verify(recver.was_successful)
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
wpan.Node.finalize_all_nodes()
print('\'{}\' passed.'.format(test_name))
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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.
from wpan import verify
import wpan
# -----------------------------------------------------------------------------------------------------------------------
# Test description: Test active scan with nodes supporting different radios
#
test_name = __file__[:-3] if __file__.endswith('.py') else __file__
print('-' * 120)
print('Starting \'{}\''.format(test_name))
# -----------------------------------------------------------------------------------------------------------------------
# Creating `wpan.Nodes` instances
speedup = 1
wpan.Node.set_time_speedup_factor(speedup)
n1 = wpan.Node(wpan.NODE_15_4)
n2 = wpan.Node(wpan.NODE_TREL)
n3 = wpan.Node(wpan.NODE_15_4_TREL)
s1 = wpan.Node(wpan.NODE_15_4)
s2 = wpan.Node(wpan.NODE_TREL)
s3 = wpan.Node(wpan.NODE_15_4_TREL)
# -----------------------------------------------------------------------------------------------------------------------
# Init all nodes
wpan.Node.init_all_nodes()
# -----------------------------------------------------------------------------------------------------------------------
# Build network topology
n1.form("n1", channel='20')
n2.form("n2", channel='21')
n3.form("n3", channel='22')
# -----------------------------------------------------------------------------------------------------------------------
# Test implementation
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Scan by scanner nodes (no network)
# Scan by s1 (15.4 only), expect to see n1(15.4) and n3(15.4+trel)
result = wpan.parse_scan_result(s1.active_scan())
verify(n1.is_in_scan_result(result))
verify(not n2.is_in_scan_result(result))
verify(n3.is_in_scan_result(result))
# Scan by s2 (trel only), expect to see n2(trel) and n3(15.4+trel)
result = wpan.parse_scan_result(s2.active_scan())
verify(not n1.is_in_scan_result(result))
verify(n2.is_in_scan_result(result))
verify(n3.is_in_scan_result(result))
# Scan by s3 (trel+15.4), expect to see all nodes
result = wpan.parse_scan_result(s3.active_scan())
verify(n1.is_in_scan_result(result))
verify(n2.is_in_scan_result(result))
verify(n3.is_in_scan_result(result))
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Scan by the nodes
# Scan by n1 (15.4 only), expect to see only n3(15.4+trel)
result = wpan.parse_scan_result(n1.active_scan())
verify(not n1.is_in_scan_result(result))
verify(not n2.is_in_scan_result(result))
verify(n3.is_in_scan_result(result))
# Scan by n2 (trel only), expect to see only n3(15.4+trel)
result = wpan.parse_scan_result(n2.active_scan())
verify(not n1.is_in_scan_result(result))
verify(not n2.is_in_scan_result(result))
verify(n3.is_in_scan_result(result))
# Scan by n3 (15.4+trel), expect to see n1(15.4) and n2(trel)
result = wpan.parse_scan_result(n3.active_scan())
verify(n1.is_in_scan_result(result))
verify(n2.is_in_scan_result(result))
verify(not n3.is_in_scan_result(result))
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
wpan.Node.finalize_all_nodes()
print('\'{}\' passed.'.format(test_name))
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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.
from wpan import verify
import wpan
# -----------------------------------------------------------------------------------------------------------------------
# Test description: Test MLE discover scan with nodes supporting different radios
#
test_name = __file__[:-3] if __file__.endswith('.py') else __file__
print('-' * 120)
print('Starting \'{}\''.format(test_name))
# -----------------------------------------------------------------------------------------------------------------------
# Creating `wpan.Nodes` instances
speedup = 1
wpan.Node.set_time_speedup_factor(speedup)
n1 = wpan.Node(wpan.NODE_15_4)
n2 = wpan.Node(wpan.NODE_TREL)
n3 = wpan.Node(wpan.NODE_15_4_TREL)
s1 = wpan.Node(wpan.NODE_15_4)
s2 = wpan.Node(wpan.NODE_TREL)
s3 = wpan.Node(wpan.NODE_15_4_TREL)
# -----------------------------------------------------------------------------------------------------------------------
# Init all nodes
wpan.Node.init_all_nodes()
# -----------------------------------------------------------------------------------------------------------------------
# Build network topology
n1.form("n1", channel='20')
n2.form("n2", channel='21')
n3.form("n3", channel='22')
# -----------------------------------------------------------------------------------------------------------------------
# Test implementation
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Scan by scanner nodes (no network)
# Scan by s1 (15.4 only), expect to see n1(15.4) and n3(15.4+trel)
result = wpan.parse_scan_result(s1.discover_scan())
verify(n1.is_in_scan_result(result))
verify(not n2.is_in_scan_result(result))
verify(n3.is_in_scan_result(result))
# Scan by s2 (trel only), expect to see n2(trel) and n3(15.4+trel)
result = wpan.parse_scan_result(s2.discover_scan())
verify(not n1.is_in_scan_result(result))
verify(n2.is_in_scan_result(result))
verify(n3.is_in_scan_result(result))
# Scan by s3 (trel+15.4), expect to see all nodes
result = wpan.parse_scan_result(s3.discover_scan())
verify(n1.is_in_scan_result(result))
verify(n2.is_in_scan_result(result))
verify(n3.is_in_scan_result(result))
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Scan by the nodes
# Scan by n1 (15.4 only), expect to see only n3(15.4+trel)
result = wpan.parse_scan_result(n1.discover_scan())
verify(not n1.is_in_scan_result(result))
verify(not n2.is_in_scan_result(result))
verify(n3.is_in_scan_result(result))
# Scan by n2 (trel only), expect to see only n3(15.4+trel)
result = wpan.parse_scan_result(n2.discover_scan())
verify(not n1.is_in_scan_result(result))
verify(not n2.is_in_scan_result(result))
verify(n3.is_in_scan_result(result))
# Scan by n3 (15.4+trel), expect to see n1(15.4) and n2(trel)
result = wpan.parse_scan_result(n3.discover_scan())
verify(n1.is_in_scan_result(result))
verify(n2.is_in_scan_result(result))
verify(not n3.is_in_scan_result(result))
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
wpan.Node.finalize_all_nodes()
print('\'{}\' passed.'.format(test_name))
+82 -5
View File
@@ -122,6 +122,9 @@ WPAN_OT_MSG_BUFFER_COUNTERS = "OpenThread:MsgBufferCounters"
WPAN_OT_MSG_BUFFER_COUNTERS_AS_STRING = "OpenThread:MsgBufferCounters:AsString"
WPAN_OT_DEBUG_TEST_ASSERT = "OpenThread:Debug:TestAssert"
WPAN_OT_DEBUG_TEST_WATCHDOG = "OpenThread:Debug:TestWatchdog"
WPAN_OT_SUPPORTED_RADIO_LINKS = "OpenThread:SupportedRadioLinks"
WPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO = "OpenThread:NeighborTable::MultiRadioInfo"
WPAN_OT_TREL_TEST_MODE_ENABLE = "OpenThread:Trel:TestMode:Enable"
WPAN_MAC_ALLOWLIST_ENABLED = "MAC:Allowlist:Enabled"
WPAN_MAC_ALLOWLIST_ENTRIES = "MAC:Allowlist:Entries"
@@ -228,6 +231,13 @@ MCU_POWER_STATE_ON = '"on"'
MCU_POWER_STATE_LOW_POWER = '"low-power"'
MCU_POWER_STATE_OFF = '"off"'
# -----------------------------------------------------------------------------------------------------------------------
# Node Radio Link Types (Use as input to `Node()` initializer)
NODE_15_4 = "-15.4"
NODE_TREL = "-trel"
NODE_15_4_TREL = "-15.4-trel"
# -----------------------------------------------------------------------------------------------------------------------
# Node types (from `WPAN_NODE_TYPE` property)
@@ -261,6 +271,13 @@ THREAD_MODE_FLAG_FULL_NETWORK_DATA = (1 << 0)
THREAD_MODE_FLAG_FULL_THREAD_DEV = (1 << 1)
THREAD_MODE_FLAG_RX_ON_WHEN_IDLE = (1 << 3)
# -----------------------------------------------------------------------------------------------------------------------
# Radio Link type
RADIO_LINK_IEEE_802_15_4 = "IEEE_802_15_4"
RADIO_LINK_TREL_UDP6 = "TREL_UDP6"
RADIO_LINK_TOBLE = "TOBLE"
_OT_BUILDDIR = os.getenv('top_builddir', '../..')
_WPANTUND_PREFIX = os.getenv('WPANTUND_PREFIX', '/usr/local')
@@ -281,7 +298,6 @@ def _log(text, new_line=True, flush=True):
class Node(object):
""" A wpantund OT NCP instance """
# defines the default verbosity setting (can be changed per `Node`)
_VERBOSE = os.getenv('TORANJ_VERBOSE', 'no').lower() in ['true', '1', 't', 'y', 'yes', 'on']
_SPEED_UP_FACTOR = 1 # defines the default time speed up factor
@@ -312,7 +328,7 @@ class Node(object):
_cur_index = _START_INDEX
_all_nodes = weakref.WeakSet()
def __init__(self, verbose=_VERBOSE):
def __init__(self, radios=None, verbose=_VERBOSE):
"""Creates a new `Node` instance"""
index = Node._cur_index
@@ -330,10 +346,11 @@ class Node(object):
self._use_posix_with_rcp = False
if self._use_posix_with_rcp:
ncp_socket_path = 'system:{} -s {} spinel+hdlc+uart://{}?forkpty-arg={}'.format(
self._OT_NCP_FTD_POSIX, self._SPEED_UP_FACTOR, self._OT_RCP, index)
ncp_socket_path = 'system:{}{} -s {} spinel+hdlc+uart://{}?forkpty-arg={}'.format(
self._OT_NCP_FTD_POSIX, '' if radios is None else radios, self._SPEED_UP_FACTOR, self._OT_RCP, index)
else:
ncp_socket_path = 'system:{} {} {}'.format(self._OT_NCP_FTD, index, self._SPEED_UP_FACTOR)
ncp_socket_path = 'system:{}{} {} {}'.format(self._OT_NCP_FTD, '' if radios is None else radios, index,
self._SPEED_UP_FACTOR)
cmd = self._WPANTUND + \
' -o Config:NCP:SocketPath \"{}\"'.format(ncp_socket_path) + \
@@ -1536,3 +1553,63 @@ class InterfaceRoute(object):
def parse_interface_routes_result(interface_routes_list):
""" Parses interface routes list string and returns an array of `InterfaceRoute` objects"""
return [InterfaceRoute(item) for item in interface_routes_list.split('\n')[1:-1]]
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class MultiRadioEntry(object):
""" This object encapsulates a multi radio info entry"""
def __init__(self, text):
# Example of expected text:
#
# `\t"0EB758375B4976E7, RLOC16:f403, Radios:[IEEE_802_15_4(200), TREL_UDP6(255)]"`
#
# We get rid of the first two chars `\t"' and last char '"', split the rest using whitespace as separator.
# 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 is the extended address
self._ext_address = items[0]
# Second items is 'RLCO16:{rloc}'
self._rloc16 = items[1].split(':')[1]
# Join back rest of items, split using ":" to get list of radios of form "[IEEE_802_15_4(200) TREL_UDP6(255)]"
radios = " ".join(items[2:]).split(":")[1]
if radios != "[]":
# Get rid of `[ and `]`, then split using " ", then convert to dictionary mapping radio type
# to its preference value.
self._radios = {radio.split("(")[0]: radio.split("(")[1][:-1] for radio in radios[1:-1].split(' ')}
else:
self._radios = {}
@property
def ext_address(self):
return self._ext_address
@property
def rloc16(self):
return self._rloc16
@property
def radios(self):
return self._radios
def supports(self, radio_type):
return radio_type in self._radios
def preference(self, radio_type):
return int(self._radios[radio_type], 0) if self.supports(radio_type) else None
def __repr__(self):
return 'MultiRadioEntry({})'.format(self.__dict__)
def parse_multi_radio_result(multi_radio_list):
""" Parses multi radio neighbor list string and returns an array of `MultiRadioEntry` objects"""
return [MultiRadioEntry(item) for item in multi_radio_list.split('\n')[1:-1]]