twister: Add configurable flashing timeout

Use explicit flash command execution timeout at DeviceHandler
instead of the hardcoded value of 60 sec.

When a HW platform executes test cases right after the flash command,
the test case remaining timeout is affected by how much time the flash
operation consumes. Some simulation platforms need to spend significant
amount of time on each 'flash' cycle, usually adding the same delay on
each test case.

This improvement adds two new command line options and device map fields:

  --device-flash-timeout - for the flash operation timeout
  ('flash-timeout' device map field).

  --device-flash-with-test - to indicate that the platform flash
  command also runs a test case, so the overall timeout should be
  calculated as a sum of the flash timeout and the current test case
  timeout to receive all console output from the platform
  ('flash-with-test' device map field).

The device map field values override command line values for the
particular platform where configured.

Default behavior is backward compatible: flash operation fixed timeout
is 60 sec. not including the test case timeout.

Signed-off-by: Dmitrii Golovanov <dmitrii.golovanov@intel.com>
This commit is contained in:
Dmitrii Golovanov 2023-04-03 10:24:46 +02:00 committed by Carles Cufí
commit f31aab58d5
5 changed files with 62 additions and 11 deletions

View file

@ -360,7 +360,7 @@ min_flash: <integer>
compared with information provided by the board metadata.
timeout: <number of seconds>
Length of time to run test in QEMU before automatically killing it.
Length of time to run test before automatically killing it.
Default to 60 seconds.
arch_allow: <list of arches, such as x86, arm, arc>
@ -680,6 +680,13 @@ In this case you can run twister with the following options:
The script is user-defined and handles delivering the messages which can be
used by twister to determine the test execution status.
The ``--device-flash-timeout`` option allows to set explicit timeout on the
device flash operation, for example when device flashing takes significantly
large time.
The ``--device-flash-with-test`` option indicates that on the platform
the flash operation also executes a test case, so the flash timeout is
increased by a test case timeout.
Executing tests on multiple devices
===================================
@ -820,6 +827,10 @@ on those platforms.
with the hardware map features. Boards that require other runners to flash the
Zephyr binary are still work in progress.
Hardware map allows to set ``--device-flash-timeout`` and ``--device-flash-with-test``
command line options as ``flash-timeout`` and ``flash-with-test`` fields respectively.
These hardware map values override command line options for the particular platform.
Serial PTY support using ``--device-serial-pty`` can also be used in the
hardware map::

View file

@ -164,6 +164,15 @@ Artificially long but functional example:
for testing on hardware that is listed in the file.
""")
parser.add_argument("--device-flash-timeout", type=int, default=60,
help="""Set timeout for the device flash operation in seconds.
""")
parser.add_argument("--device-flash-with-test", action="store_true",
help="""Add a test case timeout to the flash operation timeout
when flash operation also executes test case on the platform.
""")
test_or_build.add_argument(
"-b", "--build-only", action="store_true", default="--prep-artifacts-for-testing" in sys.argv,
help="Only build the code, do not attempt to run the code on targets.")
@ -681,6 +690,14 @@ def parse_arguments(parser, args, options = None):
only one platform is allowed""")
sys.exit(1)
if options.device_flash_timeout and options.device_testing is None:
logger.error("--device-flash-timeout requires --device-testing")
sys.exit(1)
if options.device_flash_with_test and options.device_testing is None:
logger.error("--device-flash-with-test requires --device-testing")
sys.exit(1)
if options.coverage_formats and (options.coverage_tool != "gcovr"):
logger.error("""--coverage-formats can only be used when coverage
tool is set to gcovr""")

View file

@ -523,6 +523,10 @@ class DeviceHandler(Handler):
if pre_script:
self.run_custom_script(pre_script, 30)
flash_timeout = hardware.flash_timeout
if hardware.flash_with_test:
flash_timeout += self.timeout
try:
ser = serial.Serial(
serial_device,
@ -530,7 +534,7 @@ class DeviceHandler(Handler):
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=self.timeout
timeout=max(flash_timeout, self.timeout) # the worst case of no serial input
)
except serial.SerialException as e:
self.instance.status = "failed"
@ -557,8 +561,8 @@ class DeviceHandler(Handler):
t = threading.Thread(target=self.monitor_serial, daemon=True,
args=(ser, halt_monitor_evt, harness))
t.start()
start_time = time.time()
t.start()
d_log = "{}/device.log".format(self.instance.build_dir)
logger.debug('Flash command: %s', command)
@ -567,7 +571,7 @@ class DeviceHandler(Handler):
stdout = stderr = None
with subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE) as proc:
try:
(stdout, stderr) = proc.communicate(timeout=60)
(stdout, stderr) = proc.communicate(timeout=flash_timeout)
# ignore unencodable unicode chars
logger.debug(stdout.decode(errors = "ignore"))
@ -599,6 +603,7 @@ class DeviceHandler(Handler):
self.run_custom_script(post_flash_script, 30)
if not flash_error:
# Always wait at most the test timeout here after flashing.
t.join(self.timeout)
else:
# When the flash error is due exceptions,

View file

@ -46,7 +46,9 @@ class DUT(object):
pre_script=None,
post_script=None,
post_flash_script=None,
runner=None):
runner=None,
flash_timeout=60,
flash_with_test=False):
self.serial = serial
self.baud = serial_baud or 115200
@ -68,7 +70,8 @@ class DUT(object):
self.notes = None
self.lock = Lock()
self.match = False
self.flash_timeout = flash_timeout
self.flash_with_test = flash_with_test
@property
def available(self):
@ -172,7 +175,9 @@ class HardwareMap:
self.options.platform[0],
self.options.pre_script,
False,
baud=self.options.device_serial_baud
baud=self.options.device_serial_baud,
flash_timeout=self.options.device_flash_timeout,
flash_with_test=self.options.device_flash_with_test
)
else:
self.add_device(self.options.device_serial_pty,
@ -198,9 +203,10 @@ class HardwareMap:
print(tabulate(table, headers=header, tablefmt="github"))
def add_device(self, serial, platform, pre_script, is_pty, baud=None):
device = DUT(platform=platform, connected=True, pre_script=pre_script, serial_baud=baud)
def add_device(self, serial, platform, pre_script, is_pty, baud=None, flash_timeout=60, flash_with_test=False):
device = DUT(platform=platform, connected=True, pre_script=pre_script, serial_baud=baud,
flash_timeout=flash_timeout, flash_with_test=flash_with_test
)
if is_pty:
device.serial_pty = serial
else:
@ -215,6 +221,10 @@ class HardwareMap:
pre_script = dut.get('pre_script')
post_script = dut.get('post_script')
post_flash_script = dut.get('post_flash_script')
flash_timeout = dut.get('flash_timeout') or self.options.device_flash_timeout
flash_with_test = dut.get('flash_with_test')
if flash_with_test is None:
flash_with_test = self.options.device_flash_with_test
platform = dut.get('platform')
id = dut.get('id')
runner = dut.get('runner')
@ -238,7 +248,9 @@ class HardwareMap:
connected=connected,
pre_script=pre_script,
post_script=post_script,
post_flash_script=post_flash_script)
post_flash_script=post_flash_script,
flash_timeout=flash_timeout,
flash_with_test=flash_with_test)
new_dut.fixtures = fixtures
new_dut.counter = 0
self.duts.append(new_dut)

View file

@ -55,3 +55,9 @@ sequence:
required: false
sequence:
- type: str
"flash_timeout":
type: int
required: false
"flash_with_test":
type: bool
required: false