blob: 917c1e4e773cf03882894fb756780093b66effc6 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2019 - The Android Open Source Project
#
# 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.
"""
Prerequisites:
Windows 10
Bluetooth PTS installed
Recommended: Running cmder as Admin: https://cmder.net/
### BEGIN SETUP STEPS###
1. Install latest version of Python for windows:
https://www.python.org/downloads/windows/
Tested successfully on Python 3.7.3.:
https://www.python.org/ftp/python/3.7.3/python-3.7.3.exe
2. Launch Powershell and setup PATH:
Setx PATH “%PATH%;C:/Users/<username>/AppData/Local/Programs/Python/Python37-32/Scripts”
3. Launch Cmder as Admin before running any PTS related ACTS tests.
### END SETUP STEPS###
Bluetooth PTS controller.
Mandatory parameters are log_directory and sig_root_directory.
ACTS Config setup:
"BluetoothPtsDevice": {
"log_directory": "C:\\Users\\fsbtt\\Documents\\Profile Tuning Suite\\Test_Dir",
"sig_root_directory": "C:\\Program Files (x86)\\Bluetooth SIG"
}
"""
from acts import signals
from datetime import datetime
from threading import Thread
import ctypes
import logging
import os
import subprocess
import time
import xml.etree.ElementTree as ET
from xml.dom import minidom
from xml.etree.ElementTree import Element
class BluetoothPtsDeviceConfigError(signals.ControllerError):
pass
class BluetoothPtsSnifferError(signals.ControllerError):
pass
MOBLY_CONTROLLER_CONFIG_NAME = "BluetoothPtsDevice"
ACTS_CONTROLLER_REFERENCE_NAME = "bluetooth_pts_device"
# Prefix to identify final verdict string. This is a PTS specific log String.
VERDICT = 'VERDICT/'
# Verdict strings that are specific to PTS.
VERDICT_STRINGS = {
'RESULT_PASS': 'PASS',
'RESULT_FAIL': 'FAIL',
'RESULT_INCONC': 'INCONC',
'RESULT_INCOMP':
'INCOMP', # Initial final verdict meaning that test has not completed yet.
'RESULT_NONE':
'NONE', # Error verdict usually indicating internal PTS error.
}
# Sniffer ready log message.
SNIFFER_READY = 'SNIFFER/Save and clear complete'
# PTS Log Types as defined by PTS:
LOG_TYPE_GENERAL_TEXT = 0
LOG_TYPE_FIRST = 1
LOG_TYPE_START_TEST_CASE = 1
LOG_TYPE_TEST_CASE_ENDED = 2
LOG_TYPE_START_DEFAULT = 3
LOG_TYPE_DEFAULT_ENDED = 4
LOG_TYPE_FINAL_VERDICT = 5
LOG_TYPE_PRELIMINARY_VERDICT = 6
LOG_TYPE_TIMEOUT = 7
LOG_TYPE_ASSIGNMENT = 8
LOG_TYPE_START_TIMER = 9
LOG_TYPE_STOP_TIMER = 10
LOG_TYPE_CANCEL_TIMER = 11
LOG_TYPE_READ_TIMER = 12
LOG_TYPE_ATTACH = 13
LOG_TYPE_IMPLICIT_SEND = 14
LOG_TYPE_GOTO = 15
LOG_TYPE_TIMED_OUT_TIMER = 16
LOG_TYPE_ERROR = 17
LOG_TYPE_CREATE = 18
LOG_TYPE_DONE = 19
LOG_TYPE_ACTIVATE = 20
LOG_TYPE_MESSAGE = 21
LOG_TYPE_LINE_MATCHED = 22
LOG_TYPE_LINE_NOT_MATCHED = 23
LOG_TYPE_SEND_EVENT = 24
LOG_TYPE_RECEIVE_EVENT = 25
LOG_TYPE_OTHERWISE_EVENT = 26
LOG_TYPE_RECEIVED_ON_PCO = 27
LOG_TYPE_MATCH_FAILED = 28
LOG_TYPE_COORDINATION_MESSAGE = 29
PTS_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!"
def create(config):
if not config:
raise errors.PTS_DEVICE_EMPTY_CONFIG_MSG
return get_instance(config)
def destroy(pts):
try:
pts[0].clean_up()
except:
pts[0].log.error("Failed to clean up properly.")
def get_info(pts_devices):
"""Get information from the BluetoothPtsDevice object.
Args:
pts_devices: A list of BluetoothPtsDevice objects although only one
will ever be specified.
Returns:
A dict, representing info for BluetoothPtsDevice object.
"""
return {
"address": pts_devices[0].address,
"sniffer_ready": pts_devices[0].sniffer_ready,
"ets_manager_library": pts_devices[0].ets_manager_library,
"log_directory": pts_devices[0].log_directory,
"pts_installation_directory":
pts_devices[0].pts_installation_directory,
}
def get_instance(config):
"""Create BluetoothPtsDevice instance from a dictionary containing
information related to PTS. Namely the SIG root directory as
sig_root_directory and the log directory represented by the log_directory.
Args:
config: A dict that contains BluetoothPtsDevice device info.
Returns:
A list of BluetoothPtsDevice objects.
"""
result = []
try:
log_directory = config.pop("log_directory")
except KeyError:
raise BluetoothPtsDeviceConfigError(
"Missing mandatory log_directory in config.")
try:
sig_root_directory = config.pop("sig_root_directory")
except KeyError:
example_path = \
"C:\\\\Program Files (x86)\\\\Bluetooth SIG"
raise BluetoothPtsDeviceConfigError(
"Missing mandatory sig_root_directory in config. Example path: {}".
format(example_path))
# "C:\\Program Files (x86)\\Bluetooth SIG\\Bluetooth PTS\\bin\\ETSManager.dll"
ets_manager_library = "{}\\Bluetooth PTS\\bin\\ETSManager.dll".format(
sig_root_directory)
# "C:\\Program Files (x86)\\Bluetooth SIG\\Bluetooth PTS\\bin"
pts_installation_directory = "{}\\Bluetooth PTS\\bin".format(
sig_root_directory)
# "C:\\Program Files (x86)\\Bluetooth SIG\\Bluetooth Protocol Viewer"
pts_sniffer_directory = "{}\\Bluetooth Protocol Viewer".format(
sig_root_directory)
result.append(
BluetoothPtsDevice(ets_manager_library, log_directory,
pts_installation_directory, pts_sniffer_directory))
return result
class BluetoothPtsDevice:
"""Class representing an Bluetooth PTS device and associated functions.
Each object of this class represents one BluetoothPtsDevice in ACTS.
"""
_next_action = -1
_observers = []
address = ""
current_implicit_send_description = ""
devices = []
extra_answers = []
log_directory = ""
log = None
ics = None
ixit = None
profile_under_test = None
pts_library = None
pts_profile_mmi_request = ""
pts_test_result = VERDICT_STRINGS['RESULT_INCOMP']
sniffer_ready = False
test_log_directory = ""
test_log_prefix = ""
def __init__(self, ets_manager_library, log_directory,
pts_installation_directory, pts_sniffer_directory):
self.log = logging.getLogger()
if ets_manager_library is not None:
self.ets_manager_library = ets_manager_library
self.log_directory = log_directory
if pts_installation_directory is not None:
self.pts_installation_directory = pts_installation_directory
if pts_sniffer_directory is not None:
self.pts_sniffer_directory = pts_sniffer_directory
# Define callback functions
self.USEAUTOIMPLSENDFUNC = ctypes.CFUNCTYPE(ctypes.c_bool)
self.use_auto_impl_send_func = self.USEAUTOIMPLSENDFUNC(
self.UseAutoImplicitSend)
self.DONGLE_MSG_FUNC = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_char_p)
self.dongle_msg_func = self.DONGLE_MSG_FUNC(self.DongleMsg)
self.DEVICE_SEARCH_MSG_FUNC = ctypes.CFUNCTYPE(ctypes.c_bool,
ctypes.c_char_p,
ctypes.c_char_p,
ctypes.c_char_p)
self.dev_search_msg_func = self.DEVICE_SEARCH_MSG_FUNC(
self.DeviceSearchMsg)
self.LOGFUNC = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_char_p,
ctypes.c_char_p, ctypes.c_char_p,
ctypes.c_int, ctypes.c_void_p)
self.log_func = self.LOGFUNC(self.Log)
self.ONIMPLSENDFUNC = ctypes.CFUNCTYPE(ctypes.c_char_p,
ctypes.c_char_p, ctypes.c_int)
self.onimplsend_func = self.ONIMPLSENDFUNC(self.ImplicitSend)
# Helps with PTS reliability.
os.chdir(self.pts_installation_directory)
# Load EtsManager
self.pts_library = ctypes.cdll.LoadLibrary(self.ets_manager_library)
self.log.info("ETS Manager library {0:s} has been loaded".format(
self.ets_manager_library))
# If post-logging is turned on all callbacks to LPLOG-type function
# will be executed after test execution is complete. It is recommended
# that post-logging is turned on to avoid simultaneous invocations of
# LPLOG and LPAUTOIMPLICITSEND callbacks.
self.pts_library.SetPostLoggingEx(True)
self.xml_root = Element("ARCHIVE")
version = Element("VERSION")
version.text = "2.0"
self.xml_root.append(version)
self.xml_pts_pixit = Element("PicsPixit")
self.xml_pts_pixit.text = ""
self.xml_pts_running_log = Element("LOG")
self.xml_pts_running_log.text = ""
self.xml_pts_running_summary = Element("SUMMARY")
self.xml_pts_running_summary.text = ""
def clean_up(self):
# Since we have no insight to the actual PTS library,
# catch all Exceptions and log them.
try:
self.log.info("Cleaning up Stack...")
self.pts_library.ExitStackEx(self.profile_under_test)
except Exception as err:
self.log.error(
"Failed to clean up BluetoothPtsDevice: {}".format(err))
try:
self.log.info("Unregistering Profile...")
self.pts_library.UnregisterProfileEx.argtypes = [ctypes.c_char_p]
self.pts_library.UnregisterProfileEx(
self.profile_under_test.encode())
self.pts_library.UnRegisterGetDevInfoEx()
except Exception as err:
self.log.error(
"Failed to clean up BluetoothPtsDevice: {}".format(err))
try:
self.log.info("Cleaning up Sniffer")
self.pts_library.SnifferTerminateEx()
except Exception as err:
self.log.error(
"Failed to clean up BluetoothPtsDevice: {}".format(err))
self.log.info("Cleanup Done.")
def write_xml_pts_pixit_values_for_current_test(self):
""" Writes the current PICS and IXIT values to the XML result.
"""
self.xml_pts_pixit.text = "ICS VALUES:\n\n"
for key, value in self.ics.items():
self.xml_pts_pixit.text += "{} {}\n".format(
key.decode(), value.decode())
self.xml_pts_pixit.text += "\nIXIT VALUES:\n\n"
for key, (_, value) in self.ixit.items():
self.xml_pts_pixit.text += "{} {}\n".format(
key.decode(), value.decode())
def set_ics_and_ixit(self, ics, ixit):
self.ics = ics
self.ixit = ixit
def set_profile_under_test(self, profile):
self.profile_under_test = profile
def setup_pts(self):
"""Prepares PTS to run tests. This needs to be called in test classes
after ICS, IXIT, and setting Profile under test.
Specifically BluetoothPtsDevice functions:
set_profile_under_test
set_ics_and_ixit
"""
# Register layer to test with callbacks
self.pts_library.RegisterProfileWithCallbacks.argtypes = [
ctypes.c_char_p, self.USEAUTOIMPLSENDFUNC, self.ONIMPLSENDFUNC,
self.LOGFUNC, self.DEVICE_SEARCH_MSG_FUNC, self.DONGLE_MSG_FUNC
]
res = self.pts_library.RegisterProfileWithCallbacks(
self.profile_under_test.encode(), self.use_auto_impl_send_func,
self.onimplsend_func, self.log_func, self.dev_search_msg_func,
self.dongle_msg_func)
self.log.info(
"Profile has been registered with result {0:d}".format(res))
# GetDeviceInfo module is for discovering devices and PTS Dongle address
# Initialize GetDeviceInfo and register it with callbacks
# First parameter is PTS executable directory
self.pts_library.InitGetDevInfoWithCallbacks.argtypes = [
ctypes.c_char_p, self.DEVICE_SEARCH_MSG_FUNC, self.DONGLE_MSG_FUNC
]
res = self.pts_library.InitGetDevInfoWithCallbacks(
self.pts_installation_directory.encode(), self.dev_search_msg_func,
self.dongle_msg_func)
self.log.info(
"GetDevInfo has been initialized with result {0:d}".format(res))
# Initialize PTS dongle
res = self.pts_library.VerifyDongleEx()
self.log.info(
"PTS dongle has been initialized with result {0:d}".format(res))
# Find PTS dongle address
self.pts_library.GetDongleBDAddress.restype = ctypes.c_ulonglong
self.address = self.pts_library.GetDongleBDAddress()
self.address_str = "{0:012X}".format(self.address)
self.log.info("PTS BD Address 0x{0:s}".format(self.address_str))
# Initialize Bluetooth Protocol Viewer communication module
self.pts_library.SnifferInitializeEx()
# If Bluetooth Protocol Viewer is not running, start it
if not self.is_sniffer_running():
self.log.info("Starting Protocol Viewer")
args = [
"{}\Executables\Core\FTS.exe".format(
self.pts_sniffer_directory),
'/PTS Protocol Viewer=Generic',
'/OEMTitle=Bluetooth Protocol Viewer', '/OEMKey=Virtual'
]
subprocess.Popen(args)
sniffer_timeout = 10
while not self.is_sniffer_running():
time.sleep(sniffer_timeout)
# Register to recieve Bluetooth Protocol Viewer notofications
self.pts_library.SnifferRegisterNotificationEx()
self.pts_library.SetParameterEx.argtypes = [
ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p
]
for ics_name in self.ics:
res = self.pts_library.SetParameterEx(
ics_name, b'BOOLEAN', self.ics[ics_name],
self.profile_under_test.encode())
if res:
self.log.info("ICS {0:s} set successfully".format(
str(ics_name)))
else:
self.log.error("Setting ICS {0:s} value failed".format(
str(ics_name)))
for ixit_name in self.ixit:
res = self.pts_library.SetParameterEx(
ixit_name, (self.ixit[ixit_name])[0],
(self.ixit[ixit_name])[1], self.profile_under_test.encode())
if res:
self.log.info("IXIT {0:s} set successfully".format(
str(ixit_name)))
else:
self.log.error("Setting IXIT {0:s} value failed".format(
str(ixit_name)))
# Prepare directory to store Bluetooth Protocol Viewer output
if not os.path.exists(self.log_directory):
os.makedirs(self.log_directory)
address_b = self.address_str.encode("utf-8")
self.pts_library.InitEtsEx.argtypes = [
ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p
]
implicit_send_path = "{}\\implicit_send3.dll".format(
self.pts_installation_directory).encode()
res = self.pts_library.InitEtsEx(self.profile_under_test.encode(),
self.log_directory.encode(),
implicit_send_path, address_b)
self.log.info("ETS has been initialized with result {0:s}".format(
str(res)))
# Initialize Host Stack DLL
self.pts_library.InitStackEx.argtypes = [ctypes.c_char_p]
res = self.pts_library.InitStackEx(self.profile_under_test.encode())
self.log.info("Stack has been initialized with result {0:s}".format(
str(res)))
# Select to receive Log messages after test is done
self.pts_library.SetPostLoggingEx.argtypes = [
ctypes.c_bool, ctypes.c_char_p
]
self.pts_library.SetPostLoggingEx(True,
self.profile_under_test.encode())
# Clear Bluetooth Protocol Viewer. Dongle message callback will update
# sniffer_ready automatically. No need to fail setup if the timeout
# is exceeded since the logs will still be available just not starting
# from a clean slate. Just post a warning.
self.sniffer_ready = False
self.pts_library.SnifferClearEx()
end_time = time.time() + 10
while not self.sniffer_ready and time.time() < end_time:
time.sleep(1)
if not self.sniffer_ready:
self.log.warning("Sniffer not cleared. Continuing.")
def is_sniffer_running(self):
""" Looks for running Bluetooth Protocol Viewer process
Returns:
Returns True if finds one, False otherwise.
"""
prog = [
line.split()
for line in subprocess.check_output("tasklist").splitlines()
]
[prog.pop(e) for e in [0, 1, 2]]
for task in prog:
task_name = task[0].decode("utf-8")
if task_name == "Fts.exe":
self.log.info("Found FTS process successfully.")
# Sleep recommended by PTS.
time.sleep(1)
return True
return False
def UseAutoImplicitSend(self):
"""Callback method that defines Which ImplicitSend will be used.
Returns:
True always to inform PTS to use the local implementation.
"""
return True
def DongleMsg(self, msg_str):
""" Receives PTS dongle messages.
Specifically this receives the Bluetooth Protocol Viewer completed
save/clear operations.
Returns:
True if sniffer is ready, False otherwise.
"""
msg = (ctypes.c_char_p(msg_str).value).decode("utf-8")
self.log.info(msg)
# Sleep recommended by PTS.
time.sleep(1)
if SNIFFER_READY in msg:
self.sniffer_ready = True
return True
def DeviceSearchMsg(self, addr_str, name_str, cod_str):
""" Receives device search messages
Each device may return multiple messages
Each message will contain device address and may contain device name and
COD.
Returns:
True always and reports to the callback appropriately.
"""
addr = (ctypes.c_char_p(addr_str).value).replace(b'\xed',
b' ').decode("utf-8")
name = (ctypes.c_char_p(name_str).value).replace(b'\xed',
b' ').decode("utf-8")
cod = (ctypes.c_char_p(cod_str).value).replace(b'\xed',
b' ').decode("utf-8")
self.devices.append(
"Device address = {0:s} name = {1:s} cod = {2:s}".format(
addr, name, cod))
return True
def Log(self, log_time_str, log_descr_str, log_msg_str, log_type, project):
""" Receives PTS log messages.
Returns:
True always and reports to the callback appropriately.
"""
log_time = (ctypes.c_char_p(log_time_str).value).decode("utf-8")
log_descr = (ctypes.c_char_p(log_descr_str).value).decode("utf-8")
log_msg = (ctypes.c_char_p(log_msg_str).value).decode("utf-8")
if "Verdict Description" in log_descr:
self.xml_pts_running_summary.text += "\t- {}".format(log_msg)
if "Final Verdict" in log_descr:
self.xml_pts_running_summary.text += "{}{}\n".format(
log_descr.strip(), log_msg.strip())
full_log_msg = "{}{}{}".format(log_time, log_descr, log_msg)
self.xml_pts_running_log.text += "{}\n".format(str(full_log_msg))
if ctypes.c_int(log_type).value == LOG_TYPE_FINAL_VERDICT:
indx = log_msg.find(VERDICT)
if indx == 0:
if self.pts_test_result == VERDICT_STRINGS['RESULT_INCOMP']:
if VERDICT_STRINGS['RESULT_INCONC'] in log_msg:
self.pts_test_result = VERDICT_STRINGS['RESULT_INCONC']
elif VERDICT_STRINGS['RESULT_FAIL'] in log_msg:
self.pts_test_result = VERDICT_STRINGS['RESULT_FAIL']
elif VERDICT_STRINGS['RESULT_PASS'] in log_msg:
self.pts_test_result = VERDICT_STRINGS['RESULT_PASS']
elif VERDICT_STRINGS['RESULT_NONE'] in log_msg:
self.pts_test_result = VERDICT_STRINGS['RESULT_NONE']
return True
def ImplicitSend(self, description, style):
""" ImplicitSend callback
Implicit Send Styles:
MMI_Style_Ok_Cancel1 = 0x11041, Simple prompt | OK, Cancel buttons | Default: OK
MMI_Style_Ok_Cancel2 = 0x11141, Simple prompt | Cancel button | Default: Cancel
MMI_Style_Ok1 = 0x11040, Simple prompt | OK button | Default: OK
MMI_Style_Yes_No1 = 0x11044, Simple prompt | Yes, No buttons | Default: Yes
MMI_Style_Yes_No_Cancel1 = 0x11043, Simple prompt | Yes, No buttons | Default: Yes
MMI_Style_Abort_Retry1 = 0x11042, Simple prompt | Abort, Retry buttons | Default: Abort
MMI_Style_Edit1 = 0x12040, Request for data input | OK, Cancel buttons | Default: OK
MMI_Style_Edit2 = 0x12140, Select item from a list | OK, Cancel buttons | Default: OK
Handling
MMI_Style_Ok_Cancel1
OK = return "OK"
Cancel = return 0
MMI_Style_Ok_Cancel2
OK = return "OK"
Cancel = return 0
MMI_Style_Ok1
OK = return "OK", this version should not return 0
MMI_Style_Yes_No1
Yes = return "OK"
No = return 0
MMI_Style_Yes_No_Cancel1
Yes = return "OK"
No = return 0
Cancel = has been deprecated
MMI_Style_Abort_Retry1
Abort = return 0
Retry = return "OK"
MMI_Style_Edit1
OK = return expected string
Cancel = return 0
MMI_Style_Edit2
OK = return expected string
Cancel = return 0
Receives ImplicitSend messages
Description format is as following:
{MMI_ID,Test Name,Layer Name}MMI Action\n\nDescription: MMI Description
"""
descr_str = (ctypes.c_char_p(description).value).decode("utf-8")
# Sleep recommended by PTS.
time.sleep(1)
indx = descr_str.find('}')
implicit_send_info = descr_str[1:(indx)]
self.current_implicit_send_description = descr_str[(indx + 1):]
items = implicit_send_info.split(',')
implicit_send_info_id = items[0]
implicit_send_info_test_case = items[1]
self.pts_profile_mmi_request = items[2]
self.log.info(
"OnImplicitSend() has been called with the following parameters:\n"
)
self.log.info("\t\tproject_name = {0:s}".format(
self.pts_profile_mmi_request))
self.log.info("\t\tid = {0:s}".format(implicit_send_info_id))
self.log.info(
"\t\ttest_case = {0:s}".format(implicit_send_info_test_case))
self.log.info("\t\tdescription = {0:s}".format(
self.current_implicit_send_description))
self.log.info("\t\tstyle = {0:#X}".format(ctypes.c_int(style).value))
self.log.info("")
try:
self.next_action = int(implicit_send_info_id)
except Exception as err:
self.log.error(
"Setting verdict to RESULT_FAIL, exception found: {}".format(
err))
self.pts_test_result = VERDICT_STRINGS['RESULT_FAIL']
res = b'OK'
if len(self.extra_answers) > 0:
res = self.extra_answers.pop(0).encode()
self.log.info("Sending Response: {}".format(res))
return res
def log_results(self, test_name):
"""Log results.
Saves the sniffer results in cfa format and clears the sniffer.
Args:
test_name: string, name of the test run.
"""
self.pts_library.SnifferCanSaveEx.restype = ctypes.c_bool
canSave = ctypes.c_bool(self.pts_library.SnifferCanSaveEx()).value
self.pts_library.SnifferCanSaveAndClearEx.restype = ctypes.c_bool
canSaveClear = ctypes.c_bool(
self.pts_library.SnifferCanSaveAndClearEx()).value
file_name = "\\{}.cfa".format(self.test_log_prefix).encode()
path = self.test_log_directory.encode() + file_name
if canSave == True:
self.pts_library.SnifferSaveEx.argtypes = [ctypes.c_char_p]
self.pts_library.SnifferSaveEx(path)
else:
self.pts_library.SnifferSaveAndClearEx.argtypes = [ctypes.c_char_p]
self.pts_library.SnifferSaveAndClearEx(path)
end_time = time.time() + 60
while self.sniffer_ready == False and end_time > time.time():
self.log.info("Waiting for sniffer to be ready...")
time.sleep(1)
if self.sniffer_ready == False:
raise BluetoothPtsSnifferError(
"Sniffer not ready after 60 seconds.")
def execute_test(self, test_name, test_timeout=60):
"""Execute the input test name.
Preps PTS to run the test and waits up to 2 minutes for all steps
in the execution to finish. Cleanup of PTS related objects follows
any test verdict.
Args:
test_name: string, name of the test to execute.
"""
today = datetime.now()
self.write_xml_pts_pixit_values_for_current_test()
# TODO: Find out how to grab the PTS version. Temporarily
# hardcoded to v.7.4.1.2.
self.xml_pts_pixit.text = (
"Test Case Started: {} v.7.4.1.2, {} started on {}\n\n{}".format(
self.profile_under_test, test_name,
today.strftime("%A, %B %d, %Y, %H:%M:%S"),
self.xml_pts_pixit.text))
self.xml_pts_running_summary.text += "Test case : {} started\n".format(
test_name)
log_time_formatted = "{:%Y_%m_%d_%H_%M_%S}".format(datetime.now())
formatted_test_name = test_name.replace('/', '_')
formatted_test_name = formatted_test_name.replace('-', '_')
self.test_log_prefix = "{}_{}".format(formatted_test_name,
log_time_formatted)
self.test_log_directory = "{}\\{}\\{}".format(self.log_directory,
self.profile_under_test,
self.test_log_prefix)
os.makedirs(self.test_log_directory)
curr_test = test_name.encode()
self.pts_library.StartTestCaseEx.argtypes = [
ctypes.c_char_p, ctypes.c_char_p, ctypes.c_bool
]
res = self.pts_library.StartTestCaseEx(
curr_test, self.profile_under_test.encode(), True)
self.log.info("Test has been started with result {0:s}".format(
str(res)))
# Wait till verdict is received
self.log.info("Begin Test Execution... waiting for verdict.")
end_time = time.time() + test_timeout
while self.pts_test_result == VERDICT_STRINGS[
'RESULT_INCOMP'] and time.time() < end_time:
time.sleep(1)
self.log.info("End Test Execution... Verdict {}".format(
self.pts_test_result))
# Clean up after test is done
self.pts_library.TestCaseFinishedEx.argtypes = [
ctypes.c_char_p, ctypes.c_char_p
]
res = self.pts_library.TestCaseFinishedEx(
curr_test, self.profile_under_test.encode())
self.log_results(test_name)
self.xml_pts_running_summary.text += "{} finished\n".format(test_name)
# Add the log results to the XML output
self.xml_root.append(self.xml_pts_pixit)
self.xml_root.append(self.xml_pts_running_log)
self.xml_root.append(self.xml_pts_running_summary)
rough_string = ET.tostring(self.xml_root,
encoding='utf-8',
method='xml')
reparsed = minidom.parseString(rough_string)
with open(
"{}\\{}.xml".format(self.test_log_directory,
self.test_log_prefix), "w") as writter:
writter.write(
reparsed.toprettyxml(indent=" ", encoding="utf-8").decode())
if self.pts_test_result is VERDICT_STRINGS['RESULT_PASS']:
return True
return False
"""Observer functions"""
def bind_to(self, callback):
""" Callbacks to add to the observer.
This is used for DUTS automatic responses (ImplicitSends local
implementation).
"""
self._observers.append(callback)
@property
def next_action(self):
return self._next_action
@next_action.setter
def next_action(self, action):
self._next_action = action
for callback in self._observers:
callback(self._next_action)
"""End Observer functions"""