sanitycheck: Add serial_pty script command line option

Add option to provide to sanitycheck argument for creating
pseudoterminal. Used with hardware without serial console connected.
A pseudoterminal is used to make a sanitycheck believe that it
interacts with a terminal although it actually interacts with the
script.

E.g "sanitycheck --device-testing --device-serial-pty <script>"

Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
This commit is contained in:
Andrei Emeltchenko 2020-03-31 13:22:30 +03:00 committed by Anas Nashif
commit d8b845be4e
2 changed files with 67 additions and 14 deletions

View file

@ -26,6 +26,7 @@ import glob
import concurrent
import xml.etree.ElementTree as ET
import logging
import pty
from pathlib import Path
from distutils.spawn import find_executable
from colorama import Fore
@ -522,7 +523,7 @@ class DeviceHandler(Handler):
for i in self.suite.connected_hardware:
if fixture and fixture not in i.get('fixtures', []):
continue
if i['platform'] == device and i['available'] and i['serial']:
if i['platform'] == device and i['available'] and (i['serial'] or i['serial_pty']):
return True
return False
@ -530,7 +531,7 @@ class DeviceHandler(Handler):
def get_available_device(self, instance):
device = instance.platform.name
for i in self.suite.connected_hardware:
if i['platform'] == device and i['available'] and i['serial']:
if i['platform'] == device and i['available'] and (i['serial'] or i['serial_pty']):
i['available'] = False
i['counter'] += 1
return i
@ -540,7 +541,7 @@ class DeviceHandler(Handler):
def make_device_available(self, serial):
with hw_map_local:
for i in self.suite.connected_hardware:
if i['serial'] == serial:
if i['serial'] == serial or i['serial_pty']:
i['available'] = True
@staticmethod
@ -612,7 +613,21 @@ class DeviceHandler(Handler):
elif runner == "jlink":
command.append("--tool-opt=-SelectEmuBySN %s" % (board_id))
serial_device = hardware['serial']
serial_pty = hardware['serial_pty']
if serial_pty:
master, slave = pty.openpty()
try:
ser_pty_process = subprocess.Popen(serial_pty, stdout=master, stdin=master, stderr=master)
except subprocess.CalledProcessError as error:
logger.error("Failed to run subprocess {}, error {}".format(serial_pty, error.output))
return
serial_device = os.ttyname(slave)
else:
serial_device = hardware['serial']
logger.debug("Using serial device {}".format(serial_device))
try:
ser = serial.Serial(
@ -627,6 +642,12 @@ class DeviceHandler(Handler):
self.set_state("failed", 0)
self.instance.reason = "Failed"
logger.error("Serial device error: %s" % (str(e)))
if serial_pty:
ser_pty_process.terminate()
outs, errs = ser_pty_process.communicate()
logger.debug("Process {} terminated outs: {} errs {}".format(serial_pty, outs, errs))
self.make_device_available(serial_device)
return
@ -677,7 +698,6 @@ class DeviceHandler(Handler):
if post_flash_script:
self.run_custom_script(post_flash_script, 30)
t.join(self.timeout)
if t.is_alive():
logger.debug("Timed out while monitoring serial output on {}".format(self.instance.platform.name))
@ -686,6 +706,11 @@ class DeviceHandler(Handler):
if ser.isOpen():
ser.close()
if serial_pty:
ser_pty_process.terminate()
outs, errs = ser_pty_process.communicate()
logger.debug("Process {} terminated outs: {} errs {}".format(serial_pty, outs, errs))
os.close(write_pipe)
os.close(read_pipe)
@ -3427,14 +3452,21 @@ class HardwareMap:
self.detected = []
self.connected_hardware = []
def load_device_from_cmdline(self, serial, platform):
def load_device_from_cmdline(self, serial, platform, is_pty):
device = {
"serial": serial,
"serial": None,
"platform": platform,
"serial_pty": None,
"counter": 0,
"available": True,
"connected": True
}
if is_pty:
device['serial_pty'] = serial
else:
device['serial'] = serial
self.connected_hardware.append(device)
def load_hardware_map(self, map_file):

View file

@ -575,9 +575,21 @@ structure in the main Zephyr tree: boards/<arch>/<board_name>/""")
parser.add_argument(
"-X", "--fixture", action="append", default=[],
help="Specify a fixture that a board might support")
parser.add_argument(
"--device-serial",
help="Serial device for accessing the board (e.g., /dev/ttyACM0)")
serial = parser.add_mutually_exclusive_group()
serial.add_argument("--device-serial",
help="""Serial device for accessing the board
(e.g., /dev/ttyACM0)
""")
serial.add_argument("--device-serial-pty",
help="""Script for controlling pseudoterminal.
Sanitycheck believes that it interacts with a terminal
when it actually interacts with the script.
E.g "sanitycheck --device-testing
--device-serial-pty <script>
""")
parser.add_argument("--generate-hardware-map",
help="""Probe serial devices connected to this platform
@ -837,13 +849,22 @@ def main():
if platform['connected']:
options.platform.append(platform['platform'])
elif options.device_serial: # back-ward compatibility
elif options.device_serial or options.device_serial_pty:
if options.platform and len(options.platform) == 1:
hwm.load_device_from_cmdline(options.device_serial, options.platform[0])
if options.device_serial:
hwm.load_device_from_cmdline(options.device_serial,
options.platform[0],
False)
else:
hwm.load_device_from_cmdline(options.device_serial_pty,
options.platform[0],
True)
suite.connected_hardware = hwm.connected_hardware
else:
logger.error("""When --device-testing is used with --device-serial, only one
platform is allowed""")
logger.error("""When --device-testing is used with
--device-serial or --device-serial-pty,
only one platform is allowed""")
if suite.load_errors:
sys.exit(1)