1#!/usr/bin/env python3 2# 3# Copyright (c) 2022, 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 29from cli import verify 30from cli import verify_within 31import cli 32import time 33 34# ----------------------------------------------------------------------------------------------------------------------- 35# Test description: 36# 37# Verifies `ChannelManager` channel selection procedure 38 39test_name = __file__[:-3] if __file__.endswith('.py') else __file__ 40print('-' * 120) 41print('Starting \'{}\''.format(test_name)) 42 43# ----------------------------------------------------------------------------------------------------------------------- 44# Creating `cli.Node` instances 45 46# Run the test with 10,000 time speedup factor 47speedup = 10000 48cli.Node.set_time_speedup_factor(speedup) 49 50node = cli.Node() 51 52# ----------------------------------------------------------------------------------------------------------------------- 53# Form topology 54 55node.form('chan-sel', channel=24) 56 57verify(node.get_state() == 'leader') 58 59# ----------------------------------------------------------------------------------------------------------------------- 60# Test Implementation 61 62channel = 24 63 64 65def check_channel(): 66 verify(int(node.get_channel()) == channel) 67 68 69delay = int(node.cli('channel manager delay')[0]) 70# add kRequestStartJitterInterval=10000ms to expected channel manager delay 71delay += 10 / speedup 72 73check_channel() 74 75all_channels_mask = int('0x7fff800', 0) 76chan_12_to_15_mask = int('0x000f000', 0) 77chan_15_to_17_mask = int('0x0038000', 0) 78 79# Set supported channel mask to be all channels 80node.cli('channel manager supported', all_channels_mask) 81 82# Sleep for 4.5 second with speedup factor of 10,000 this is more than 12 83# hours. We sleep instead of immediately checking the sample counter in 84# order to not add more actions/events into simulation (specially since 85# we are running at very high speedup). 86time.sleep(4.5) 87 88result = cli.Node.parse_list(node.cli('channel monitor')[:5]) 89verify(result['enabled'] == '1') 90verify(int(result['count']) > 970) 91 92# Issue a channel-select with quality check enabled, and verify that no 93# action is taken. 94 95node.cli('channel manager select 0') 96result = cli.Node.parse_list(node.cli('channel manager')) 97verify(result['channel'] == '0') 98 99# Issue a channel-select with quality check disabled, verify that channel 100# is switched to channel 11. 101 102node.cli('channel manager select 1') 103result = cli.Node.parse_list(node.cli('channel manager')) 104verify(result['channel'] == '11') 105channel = 11 106verify_within(check_channel, delay) 107 108# Set channels 12-15 as favorable and request a channel select, verify 109# that channel is switched to 12. 110# 111# Even though 11 would be best, quality difference between 11 and 12 112# is not high enough for selection algorithm to pick an unfavored 113# channel. 114 115node.cli('channel manager favored', chan_12_to_15_mask) 116 117channel = 25 118node.cli('channel manager change', channel) 119verify_within(check_channel, delay) 120 121node.cli('channel manager select 1') 122result = cli.Node.parse_list(node.cli('channel manager')) 123verify(result['channel'] == '12') 124channel = 12 125verify_within(check_channel, delay) 126 127# Set channels 15-17 as favorables and request a channel select, 128# verify that channel is switched to 11. 129# 130# This time the quality difference between 11 and 15 should be high 131# enough for selection algorithm to pick the best though unfavored 132# channel (i.e., channel 11). 133 134channel = 25 135node.cli('channel manager change', channel) 136verify_within(check_channel, delay) 137 138node.cli('channel manager favored', chan_15_to_17_mask) 139 140node.cli('channel manager select 1') 141result = cli.Node.parse_list(node.cli('channel manager')) 142verify(result['channel'] == '11') 143channel = 11 144verify_within(check_channel, delay) 145 146# Set channels 12-15 as favorable and request a channel select, verify 147# that channel is not switched. 148 149node.cli('channel manager favored', chan_12_to_15_mask) 150 151node.cli('channel manager select 1') 152 153result = cli.Node.parse_list(node.cli('channel manager')) 154verify(result['channel'] == '11') 155channel = 11 156verify_within(check_channel, delay) 157 158# Starting from channel 12 and issuing a channel select (which would 159# pick 11 as best channel). However, since quality difference between 160# current channel 12 and new best channel 11 is not large enough, no 161# action should be taken. 162 163channel = 12 164node.cli('channel manager change', channel) 165verify_within(check_channel, delay) 166 167node.cli('channel manager favored', all_channels_mask) 168 169node.cli('channel manager select 1') 170result = cli.Node.parse_list(node.cli('channel manager')) 171verify(result['channel'] == '12') 172verify_within(check_channel, delay) 173 174# ----------------------------------------------------------------------------------------------------------------------- 175# Auto Select 176 177# Set channel manager cca failure rate threshold to 0 178# as we cannot control cca success in simulation 179node.cli('channel manager threshold 0') 180 181# Set short channel selection interval to speedup 182interval = 30 183node.cli(f'channel manager interval {interval}') 184 185# Set channels 15-17 as favorable and request a channel select, verify 186# that channel is switched to 11. 187 188channel = 25 189node.cli('channel manager change', channel) 190verify_within(check_channel, delay) 191node.cli('channel manager favored', chan_15_to_17_mask) 192 193# Active auto channel selection 194node.cli('channel manager auto 1') 195 196channel = 11 197result = cli.Node.parse_list(node.cli('channel manager')) 198verify(result['auto'] == '1') 199verify(result['channel'] == str(channel)) 200 201verify_within(check_channel, delay) 202 203# while channel selection timer is running change to channel 25, 204# set channels 12-15 as favorable, wait for auto channel selection 205# and verify that channel is switched to 12. 206 207node.cli('channel manager favored', chan_12_to_15_mask) 208channel = 25 209node.cli('channel manager change', channel) 210 211# wait for timeout of auto selection timer 212time.sleep(2 * interval / speedup) 213 214channel = 12 215result = cli.Node.parse_list(node.cli('channel manager')) 216verify(result['channel'] == str(channel)) 217 218verify_within(check_channel, delay) 219 220# ----------------------------------------------------------------------------------------------------------------------- 221# Test finished 222 223cli.Node.finalize_all_nodes() 224 225print('\'{}\' passed.'.format(test_name)) 226