Merge "Add config utilities and PWS test case scripts." am: 29f972b56a am: b9e40aa256
Original change: https://android-review.googlesource.com/c/platform/tools/test/connectivity/+/1991625
Change-Id: I17b2c5df019bf926cf618a8aaaeeeb36ae87172c
diff --git a/acts/framework/acts/controllers/amarisoft_lib/config_utils.py b/acts/framework/acts/controllers/amarisoft_lib/config_utils.py
new file mode 100644
index 0000000..3dad7a3
--- /dev/null
+++ b/acts/framework/acts/controllers/amarisoft_lib/config_utils.py
@@ -0,0 +1,201 @@
+!/usr/bin/env python3
+#
+# Copyright 2022 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import enum
+import os
+import immutabledict
+
+from acts.controllers.amarisoft_lib import amarisoft_client
+
+TEMPLATE_PATH = os.path.dirname(os.path.abspath(__file__)) + '/config_templates'
+TEMPLATE_PATH_ENB = f'{TEMPLATE_PATH}/enb/'
+TEMPLATE_PATH_MME = f'{TEMPLATE_PATH}/mme/'
+
+_CLIENT_CONFIG_DIR_MAPPING = immutabledict.immutabledict({
+ 'enb': '/config/mhtest_enb.cfg',
+ 'mme': '/config/mhtest_mme.cfg',
+})
+
+
+class EnbCfg():
+ """MME configuration templates."""
+ ENB_GENERIC = 'enb-single-generic.cfg'
+ GNB_NSA_GENERIC = 'gnb-nsa-lte-ho-generic.cfg'
+ GNB_SA_GENERIC = 'gnb-sa-lte-ho-generic.cfg'
+
+
+class MmeCfg():
+ """MME configuration templates."""
+ MME_GENERIC = 'mme-generic.cfg'
+
+
+class SpecTech(enum.Enum):
+ """Spectrum usage techniques."""
+ FDD = 0
+ TDD = 1
+
+
+class ConfigUtils():
+ """Utilities for set Amarisoft configs.
+
+ Attributes:
+ remote: An amarisoft client.
+ """
+
+ def __init__(self, remote: amarisoft_client.AmariSoftClient):
+ self.remote = remote
+
+ def upload_enb_template(self, cfg: str) -> bool:
+ """Loads ENB configuration.
+
+ Args:
+ cfg: The ENB configuration to be loaded.
+
+ Returns:
+ True if the ENB configuration was loaded successfully, False otherwise.
+ """
+ cfg_template = TEMPLATE_PATH_ENB + cfg
+ if not os.path.isfile(cfg_template):
+ return False
+ cfg_path = self.remote.get_config_dir(
+ 'enb') + _CLIENT_CONFIG_DIR_MAPPING['enb']
+ self.remote.run_cmd('rm -f ' + cfg_path)
+ self.remote.sftp_upload(cfg_template, cfg_path)
+ self.remote.set_config_file('enb', cfg_path)
+ if not self.remote.is_file_exist(cfg_path):
+ return False
+ return True
+
+ def upload_mme_template(self, cfg: str) -> bool:
+ """Loads MME configuration.
+
+ Args:
+ cfg: The MME configuration to be loaded.
+
+ Returns:
+ True if the ENB configuration was loaded successfully, False otherwise.
+ """
+ cfg_template = TEMPLATE_PATH_MME + cfg
+ if not os.path.isfile(cfg_template):
+ return False
+ cfg_path = self.remote.get_config_dir(
+ 'mme') + _CLIENT_CONFIG_DIR_MAPPING['mme']
+ self.remote.run_cmd('rm -f ' + cfg_path)
+ self.remote.sftp_upload(cfg_template, cfg_path)
+ self.remote.set_config_file('mme', cfg_path)
+ if not self.remote.is_file_exist(cfg_path):
+ return False
+ return True
+
+ def enb_set_plmn(self, plmn: str) -> bool:
+ """Sets the PLMN in ENB configuration.
+
+ Args:
+ plmn: The PLMN to be set. ex: 311480
+
+ Returns:
+ True if set PLMN successfully, False otherwise.
+ """
+ cfg_path = self.remote.get_config_dir(
+ 'enb') + _CLIENT_CONFIG_DIR_MAPPING['enb']
+ if not self.remote.is_file_exist(cfg_path):
+ return False
+ string_from = '#define PLMN \"00101\"'
+ string_to = f'#define PLMN \"{plmn}\"'
+ self.remote.run_cmd(f'sed -i \'s/\\r//g\' {cfg_path}')
+ self.remote.run_cmd(
+ f'sed -i \':a;N;$!ba;s/{string_from}/{string_to}/g\' {cfg_path}')
+ return True
+
+ def mme_set_plmn(self, plmn: str) -> bool:
+ """Sets the PLMN in MME configuration.
+
+ Args:
+ plmn: The PLMN to be set. ex:'311480'
+
+ Returns:
+ True if set PLMN successfully, False otherwise.
+ """
+ cfg_path = self.remote.get_config_dir(
+ 'mme') + _CLIENT_CONFIG_DIR_MAPPING['mme']
+ if not self.remote.is_file_exist(cfg_path):
+ return False
+ string_from = '#define PLMN \"00101\"'
+ string_to = f'#define PLMN \"{plmn}\"'
+ self.remote.run_cmd(f'sed -i \'s/\\r//g\' {cfg_path}')
+ self.remote.run_cmd(
+ f'sed -i \':a;N;$!ba;s/{string_from}/{string_to}/g\' {cfg_path}')
+ return True
+
+ def enb_set_fdd_arfcn(self, arfcn: int) -> bool:
+ """Sets the FDD ARFCN in ENB configuration.
+
+ Args:
+ arfcn: The arfcn to be set. ex: 1400
+
+ Returns:
+ True if set FDD ARFCN successfully, False otherwise.
+ """
+ cfg_path = self.remote.get_config_dir(
+ 'enb') + _CLIENT_CONFIG_DIR_MAPPING['enb']
+ if not self.remote.is_file_exist(cfg_path):
+ return False
+ string_from = '#define FDD_CELL_earfcn 1400'
+ string_to = f'#define FDD_CELL_earfcn {arfcn}'
+ self.remote.run_cmd(f'sed -i \'s/\\r//g\' {cfg_path}')
+ self.remote.run_cmd(
+ f'sed -i \':a;N;$!ba;s/{string_from}/{string_to}/g\' {cfg_path}')
+ return True
+
+ def enb_set_tdd_arfcn(self, arfcn: int) -> bool:
+ """Sets the TDD ARFCN in ENB configuration.
+
+ Args:
+ arfcn: The arfcn to be set. ex: 1400
+
+ Returns:
+ True if set FDD ARFCN successfully, False otherwise.
+ """
+ cfg_path = self.remote.get_config_dir(
+ 'enb') + _CLIENT_CONFIG_DIR_MAPPING['enb']
+ if not self.remote.is_file_exist(cfg_path):
+ return False
+ string_from = '#define TDD_CELL_earfcn 40620'
+ string_to = f'#define TDD_CELL_earfcn {arfcn}'
+ self.remote.run_cmd(f'sed -i \'s/\\r//g\' {cfg_path}')
+ self.remote.run_cmd(
+ f'sed -i \':a;N;$!ba;s/{string_from}/{string_to}/g\' {cfg_path}')
+ return True
+
+ def enb_set_spectrum_tech(self, tech: int) -> bool:
+ """Sets the spectrum usage techniques in ENB configuration.
+
+ Args:
+ tech: the spectrum usage techniques. ex: SpecTech.FDD.name
+
+ Returns:
+ True if set spectrum usage techniques successfully, False otherwise.
+ """
+ cfg_path = self.remote.get_config_dir(
+ 'enb') + _CLIENT_CONFIG_DIR_MAPPING['enb']
+ if not self.remote.is_file_exist(cfg_path):
+ return False
+ string_from = '#define TDD 0'
+ string_to = f'#define TDD {tech}'
+ self.remote.run_cmd(f'sed -i \'s/\\r//g\' {cfg_path}')
+ self.remote.run_cmd(
+ f'sed -i \':a;N;$!ba;s/{string_from}/{string_to}/g\' {cfg_path}')
+ return True
diff --git a/acts_tests/tests/google/tel/lab/TelLabPwsTest.py b/acts_tests/tests/google/tel/lab/TelLabPwsTest.py
new file mode 100644
index 0000000..188783a
--- /dev/null
+++ b/acts_tests/tests/google/tel/lab/TelLabPwsTest.py
@@ -0,0 +1,373 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 - Google
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import datetime
+import enum
+import logging
+import time
+from typing import Callable
+
+from acts import asserts
+from acts.controllers.amarisoft_lib import amarisoft_client
+from acts.controllers.amarisoft_lib import config_utils
+from acts.controllers.amarisoft_lib import ssh_utils
+from acts.controllers.amarisoft_lib import ims
+from acts.controllers.amarisoft_lib import mme
+from acts.test_decorators import test_tracker_info
+from acts_contrib.test_utils.tel import tel_defines
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_volte
+from acts.libs.proc import job
+from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+PWS_ALERT_4370 = 4370
+PWS_ALERT_4371 = 4371
+PWS_ALERT_4380 = 4380
+PWS_ALERT_911 = 911
+PWS_ALERT_4383 = 4383
+PWS_ALERT_4384 = 4384
+PWS_ALERT_4393 = 4393
+PWS_ALERT_919 = 919
+
+PREFERENCES_XML_FILENAME = '/data/user_de/0/com.google.android.cellbroadcastreceiver/shared_prefs/com.google.android.cellbroadcastreceiver_preferences.xml'
+ENABLE_TEST_ALERT_CMD = (
+ "sed -i 's/"
+ "enable_test_alerts\\\" value=\\\"false/"
+ "enable_test_alerts\\\" value=\\\"true/"
+ f"' {PREFERENCES_XML_FILENAME}")
+PWS_DUPLICATE_DETECTION_OFF = (
+ 'am broadcast -a '
+ 'com.android.cellbroadcastservice.action.DUPLICATE_DETECTION '
+ '--ez enable false')
+
+IN_CALL_DURATION = datetime.timedelta(seconds=10)
+CHECK_INTERVAL = datetime.timedelta(seconds=1)
+SERVICE_RESTART_TIME_OUT = datetime.timedelta(seconds=10)
+REGISTRATION_TIMEOUT = datetime.timedelta(seconds=120)
+WAIT_CALL_STATE_TIMEOUT = datetime.timedelta(seconds=30)
+PWS_START_END_INTERVAL = datetime.timedelta(seconds=15)
+
+
+class TestScenario(enum.Enum):
+ """Test scenario for PWS test."""
+ PS = 0
+ CS = 1
+ IDLE = 2
+
+
+class CallState(enum.Enum):
+ """Telephony call state."""
+ IDLE = 0
+ RINGING = 1
+ OFFHOOK = 2
+
+
+def wait_until(condition: Callable[..., bool], interval: datetime.timedelta,
+ timeout: datetime.timedelta, ret: bool, *argv) -> bool:
+ """Waits for the condition to occur.
+
+ Args:
+ condition: Function to check specific event occur or not.
+ interval: Time period during each check.
+ timeout: A timer which wait for event occur.
+ ret: Expected result of condition.
+ *argv: Parameters used by condition.
+
+ Returns:
+ True if condition match ret, False otherwise.
+ """
+ start_time = datetime.datetime.now()
+ while datetime.datetime.now() - start_time < timeout:
+ if condition(*argv) == ret:
+ return True
+ time.sleep(interval.total_seconds())
+ return False
+
+
+def is_in_service(ad) -> bool:
+ """Checks radio service state of android device .
+
+ Args:
+ ad: Mobly's Android controller objects.
+
+ Returns:
+ True if device is in service, False otherwise.
+ """
+ service_state = ad.droid.telephonyGetServiceState()
+ if service_state is None:
+ return False
+ return service_state.get('serviceState') == 'IN_SERVICE'
+
+
+class TelLabPwsTest(TelephonyBaseTest):
+
+ def setup_class(self):
+ super().setup_class()
+ self.ad = self.android_devices[0]
+ self.ad.info = self.user_params.get('AndroidDevice')[0]
+ self.amarisoft_ip_address = self.user_params.get('amarisoft_ip_address')
+ self.amarisoft_username = self.user_params.get('amarisoft_username')
+ self.amarisoft_pw = self.user_params.get('amarisoft_pw')
+ self.amarisoft_call_num = self.user_params.get('amarisoft_call_num')
+ self.remote = amarisoft_client.AmariSoftClient(self.amarisoft_ip_address,
+ self.amarisoft_username,
+ self.amarisoft_pw)
+ self.remote.connect()
+ self.config = config_utils.ConfigUtils(self.remote)
+ self.mme = mme.MmeFunctions(self.remote)
+ self.ims = ims.ImsFunctions(self.remote)
+ self._amarisoft_preset()
+ self._android_device_preset()
+
+ def _amarisoft_preset(self) -> None:
+ """Sets Amarisoft test network."""
+
+ if not self.remote.ssh_is_connected():
+ raise ssh_utils.NotConnectedError(
+ 'amarisoft_preset: amarisoft is not connected.')
+ self.remote.lte_service_start()
+
+ asserts.skip_if(
+ not self.config.upload_enb_template(config_utils.EnbCfg.ENB_GENERIC),
+ 'amarisoft_preset: Failed to upload enb configuration.')
+ asserts.skip_if(
+ not self.config.upload_mme_template(config_utils.MmeCfg.MME_GENERIC),
+ 'amarisoft_preset: Failed to upload mme configuration.')
+ asserts.skip_if(
+ not self.config.enb_set_plmn('46697'),
+ 'amarisoft_preset: Failed to set ENB PLMN.')
+ asserts.skip_if(
+ not self.config.mme_set_plmn('46697'),
+ 'amarisoft_preset: Failed to set MME PLMN.')
+ asserts.skip_if(
+ not self.config.enb_set_spectrum_tech(config_utils.SpecTech.FDD.value),
+ 'amarisoft_preset: Failed to set ENB spectrum technique.')
+ asserts.skip_if(
+ not self.config.enb_set_fdd_arfcn(275),
+ 'amarisoft_preset: Failed to set ENB FDD ARFCN.')
+
+ self.remote.lte_service_restart()
+ start_time = datetime.datetime.now()
+ while not self.remote.lte_service_is_active():
+ if datetime.datetime.now() - start_time > SERVICE_RESTART_TIME_OUT:
+ asserts.fail('amarisoft_preset: Amarisoft service restart failed.')
+ else:
+ time.sleep(CHECK_INTERVAL)
+ self.log.info('Amarisoft preset completed.')
+
+ def _android_device_preset(self)->None:
+ """Presets the device before the test starts."""
+
+ self.log.info('Android device preset start.')
+ self.ad.droid.connectivityToggleAirplaneMode(False)
+ asserts.skip_if(
+ not wait_until(is_in_service, CHECK_INTERVAL, REGISTRATION_TIMEOUT,
+ True, self.ad), 'android_device_preset: '
+ f'{self.ad.serial} is still out of service after airplane mode off.')
+ self.ad.droid.toggleRingerSilentMode(False)
+ self.ad.adb.shell(ENABLE_TEST_ALERT_CMD)
+ self.ad.reboot()
+ self.ad.droid.setMediaVolume(3)
+ self.ad.droid.setRingerVolume(3)
+ self.ad.droid.setVoiceCallVolume(3)
+ self.ad.droid.setAlarmVolume(3)
+ asserts.assert_true(
+ phone_setup_volte(self.log, self.ad),
+ 'android_device_preset: Failed to set up VoLTE.')
+ self.log.info('Android device preset completed.')
+
+ def mo_call_to_amarisoft(self) -> None:
+ """Executes a MO call process including checking the call status during the MO call.
+
+ The method focus on if any issue found on MO side with below steps:
+ (1) Make a voice call from MO side to MT side(Amarisoft).
+ (2) MT side accepts the call.
+ (3) Check if the call is connect.
+ (4) Monitor the in-call status for MO side during in-call duration.
+ (5) End the call on MO side.
+ """
+ if not self.ad.droid.telephonyIsImsRegistered():
+ asserts.skip(
+ 'mo_call_process: No IMS registered, cannot perform VoLTE call test.')
+ self.ad.log.info('Dial a Call to callbox.')
+ self.ad.droid.telecomCallNumber(self.amarisoft_call_num, False)
+ asserts.assert_true(
+ wait_until(self.ad.droid.telecomGetCallState, CHECK_INTERVAL,
+ WAIT_CALL_STATE_TIMEOUT, CallState.OFFHOOK.name),
+ 'mo_call_process: The call is not connected.')
+ asserts.assert_false(
+ wait_until(self.ad.droid.telecomIsInCall, CHECK_INTERVAL,
+ IN_CALL_DURATION, False),
+ 'mo_call_process: UE drop call before end call.')
+ self.ad.droid.telecomEndCall()
+ asserts.assert_true(
+ wait_until(self.ad.droid.telecomGetCallState, CHECK_INTERVAL,
+ WAIT_CALL_STATE_TIMEOUT, CallState.IDLE.name),
+ 'mo_call_process: UE is still in-call after hanging up the call.')
+
+ def pws_action(self, msg: str, test_scenario: int) -> None:
+ """Performs a PWS broadcast and check android device receives PWS message.
+
+ (1) Device idle or perform mo call/ping test according to test scenario.
+ (2) Broadcast a specific PWS message.
+ (3) Wait 15 seconds for device receive PWS message.
+ (4) Stop broadcast PWS message.
+ (5) Verify android device receive PWS message by check keywords in logcat.
+ (6) Perform mo call/ping test according to test scenario.
+
+ Args:
+ msg: The PWS parameter to be broadcast.
+ test_scenario: The parameters of the test scenario to be executed.
+ """
+ if test_scenario == TestScenario.PS:
+ job.run(f'adb -s {self.ad.serial} shell ping -c 5 8.8.8.8')
+ elif test_scenario == TestScenario.CS:
+ self.mo_call_to_amarisoft()
+
+ logging.info('Broadcast PWS: %s', msg)
+ # Advance the start time by one second to avoid loss of logs
+ # due to time differences between test device and mobileharness.
+ start_time = datetime.datetime.now() - datetime.timedelta(seconds=1)
+ self.mme.pws_write(msg)
+ time.sleep(PWS_START_END_INTERVAL.seconds)
+ self.mme.pws_kill(msg)
+
+ asserts.assert_true(
+ self.ad.search_logcat(
+ f'CBChannelManager: isEmergencyMessage: true, message id = {msg}',
+ start_time), f'{msg} not received.')
+ asserts.assert_false(
+ self.ad.search_logcat('Failed to play alert sound', start_time),
+ f'{msg} failed to play alert sound.')
+
+ if msg in [PWS_ALERT_911, PWS_ALERT_919]:
+ asserts.assert_true(
+ self.ad.search_logcat('playAlertTone: alertType=INFO', start_time),
+ f'{msg} alertType not match expected (alertType=INFO).')
+ else:
+ asserts.assert_true(
+ self.ad.search_logcat('playAlertTone: alertType=DEFAULT', start_time),
+ f'{msg} alertType not match expected (alertType=DEFAULT).')
+
+ if test_scenario == TestScenario.PS:
+ job.run(f'adb -s {self.ad.serial} shell ping -c 5 8.8.8.8')
+ elif test_scenario == TestScenario.CS:
+ self.mo_call_to_amarisoft()
+
+ def teardown_test(self):
+ self.ad.adb.shell(PWS_DUPLICATE_DETECTION_OFF)
+ super().teardown_test()
+
+ def teardown_class(self):
+ self.ad.droid.connectivityToggleAirplaneMode(True)
+ super().teardown_class()
+
+ @test_tracker_info(uuid="f8971b34-fcaa-4915-ba05-36c754378987")
+ def test_pws_idle_4370(self):
+ self.pws_action(PWS_ALERT_4370, TestScenario.IDLE)
+
+ @test_tracker_info(uuid="ed925410-646f-475a-8765-44ea1631cc6a")
+ def test_pws_idle_4371(self):
+ self.pws_action(PWS_ALERT_4371, TestScenario.IDLE)
+
+ @test_tracker_info(uuid="253f2e2e-8262-43b5-a66e-65b2bc73df58")
+ def test_pws_idle_4380(self):
+ self.pws_action(PWS_ALERT_4380, TestScenario.IDLE)
+
+ @test_tracker_info(uuid="95ed6407-3c5b-4f58-9fd9-e5021972f03c")
+ def test_pws_idle_911(self):
+ self.pws_action(PWS_ALERT_911, TestScenario.IDLE)
+
+ @test_tracker_info(uuid="a6f76e03-b808-4194-b286-54a2ca02cb7f")
+ def test_pws_idle_4383(self):
+ self.pws_action(PWS_ALERT_4383, TestScenario.IDLE)
+
+ @test_tracker_info(uuid="8db4be15-2e2c-4616-8f7f-a6b8062d7265")
+ def test_pws_idle_4384(self):
+ self.pws_action(PWS_ALERT_4384, TestScenario.IDLE)
+
+ @test_tracker_info(uuid="79ba63d7-8ffb-48d3-b27e-a8b152ee5a25")
+ def test_pws_idle_4393(self):
+ self.pws_action(PWS_ALERT_4393, TestScenario.IDLE)
+
+ @test_tracker_info(uuid="a07b1c14-dd3f-4818-bc8d-120d006dcea5")
+ def test_pws_idle_919(self):
+ self.pws_action(PWS_ALERT_919, TestScenario.IDLE)
+
+ @test_tracker_info(uuid="00b607a9-e75c-4342-9c7f-9528704ae3bd")
+ def test_pws_ps_4370(self):
+ self.pws_action(PWS_ALERT_4370, TestScenario.PS)
+
+ @test_tracker_info(uuid="feff8d7a-52fe-46f0-abe5-0da698fc985c")
+ def test_pws_ps_4371(self):
+ self.pws_action(PWS_ALERT_4371, TestScenario.PS)
+
+ @test_tracker_info(uuid="22afaaa1-7738-4499-a378-eabb9ae19fa6")
+ def test_pws_ps_4380(self):
+ self.pws_action(PWS_ALERT_4380, TestScenario.PS)
+
+ @test_tracker_info(uuid="d6fb35fa-9058-4c90-ac8d-bc49d6be1070")
+ def test_pws_ps_911(self):
+ self.pws_action(PWS_ALERT_911, TestScenario.PS)
+
+ @test_tracker_info(uuid="9937c39f-4b47-47f4-904a-108123919716")
+ def test_pws_ps_4383(self):
+ self.pws_action(PWS_ALERT_4383, TestScenario.PS)
+
+ @test_tracker_info(uuid="01faa5bb-e02a-42a3-bf08-30e422c684f4")
+ def test_pws_ps_4384(self):
+ self.pws_action(PWS_ALERT_4384, TestScenario.PS)
+
+ @test_tracker_info(uuid="71d02b4a-a1a3-44e1-a28a-aea3a62f758f")
+ def test_pws_ps_4393(self):
+ self.pws_action(PWS_ALERT_4393, TestScenario.PS)
+
+ @test_tracker_info(uuid="f5e7801c-80e0-4cbe-b4b1-133fa88fa4a3")
+ def test_pws_ps_919(self):
+ self.pws_action(PWS_ALERT_919, TestScenario.PS)
+
+ @test_tracker_info(uuid="b68e5593-1748-434c-be2a-e684791f2ca8")
+ def test_pws_cs_4370(self):
+ self.pws_action(PWS_ALERT_4370, TestScenario.CS)
+
+ @test_tracker_info(uuid="a04f433d-bbf0-4a09-b958-719ec8df9991")
+ def test_pws_cs_4371(self):
+ self.pws_action(PWS_ALERT_4371, TestScenario.CS)
+
+ @test_tracker_info(uuid="48432d8d-847a-44e3-aa24-32ae704e15de")
+ def test_pws_cs_4380(self):
+ self.pws_action(PWS_ALERT_4380, TestScenario.CS)
+
+ @test_tracker_info(uuid="9fde76b2-e568-4aa5-a627-9d682ba9e1fb")
+ def test_pws_cs_911(self):
+ self.pws_action(PWS_ALERT_911, TestScenario.CS)
+
+ @test_tracker_info(uuid="fa1f0c6a-22af-4daf-ab32-a508b06de165")
+ def test_pws_cs_4383(self):
+ self.pws_action(PWS_ALERT_4383, TestScenario.CS)
+
+ @test_tracker_info(uuid="45d924be-e204-497d-b598-e18a8c668492")
+ def test_pws_cs_4384(self):
+ self.pws_action(PWS_ALERT_4384, TestScenario.CS)
+
+ @test_tracker_info(uuid="ff4f0e6e-2bda-4047-a69c-7b103868e2d5")
+ def test_pws_cs_4393(self):
+ self.pws_action(PWS_ALERT_4393, TestScenario.CS)
+
+ @test_tracker_info(uuid="ab2bd166-c5e0-4505-ba37-6192bf53226f")
+ def test_pws_cs_919(self):
+ self.pws_action(PWS_ALERT_919, TestScenario.CS)
+
+