mirror of https://github.com/ARMmbed/mbed-os.git
247 lines
9.9 KiB
Python
247 lines
9.9 KiB
Python
# Copyright (c) 2018, Arm Limited and affiliates.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
# 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 os
|
|
import re
|
|
import json
|
|
from subprocess import Popen, PIPE
|
|
from .mbed_greentea_log import gt_logger
|
|
|
|
"""
|
|
List of available hooks:
|
|
"""
|
|
|
|
|
|
class GreenteaTestHook(object):
|
|
"""! Class used to define
|
|
"""
|
|
name = None
|
|
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
def run(self, format=None):
|
|
pass
|
|
|
|
class GreenteaCliTestHook(GreenteaTestHook):
|
|
"""! Class used to define a hook which will call command line program
|
|
"""
|
|
cmd = None
|
|
|
|
def __init__(self, name, cmd):
|
|
GreenteaTestHook.__init__(self, name)
|
|
self.cmd = cmd
|
|
|
|
def run_cli_process(self, cmd):
|
|
"""! Runs command as a process and return stdout, stderr and ret code
|
|
@param cmd Command to execute
|
|
@return Tuple of (stdout, stderr, returncode)
|
|
"""
|
|
_stdout, _stderr, ret = None, None, -1
|
|
try:
|
|
p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True)
|
|
_stdout, _stderr = p.communicate()
|
|
ret = p.returncode
|
|
except OSError as e:
|
|
gt_logger.gt_log_err(str(e))
|
|
ret = -1
|
|
return _stdout, _stderr, ret
|
|
|
|
def run(self, format=None):
|
|
"""! Runs hook after command is formated with in-place {tags}
|
|
@format Pass format dictionary to replace hook {tags} with real values
|
|
@param format Used to format string with cmd, notation used is e.g: {build_name}
|
|
"""
|
|
gt_logger.gt_log("hook '%s' execution"% self.name)
|
|
cmd = self.format_before_run(self.cmd, format)
|
|
gt_logger.gt_log_tab("hook command: %s"% cmd)
|
|
(_stdout, _stderr, ret) = self.run_cli_process(cmd)
|
|
if _stdout:
|
|
print(_stdout)
|
|
if ret:
|
|
gt_logger.gt_log_err("hook exited with error: %d, dumping stderr..."% ret)
|
|
print(_stderr)
|
|
return ret
|
|
|
|
@staticmethod
|
|
def format_before_run(cmd, format, verbose=False):
|
|
if format:
|
|
# We will expand first
|
|
cmd_expand = GreenteaCliTestHook.expand_parameters(cmd, format)
|
|
if cmd_expand:
|
|
cmd = cmd_expand
|
|
if verbose:
|
|
gt_logger.gt_log_tab("hook expanded: %s"% cmd)
|
|
|
|
cmd = cmd.format(**format)
|
|
if verbose:
|
|
gt_logger.gt_log_tab("hook formated: %s"% cmd)
|
|
return cmd
|
|
|
|
@staticmethod
|
|
def expand_parameters(expr, expandables, delimiter=' '):
|
|
"""! Expands lists for multiple parameters in hook command
|
|
@param expr Expression to expand
|
|
@param expandables Dictionary of token: list_to_expand See details for more info
|
|
@param delimiter Delimiter used to combine expanded strings, space by default
|
|
@details
|
|
test_name_list = ['mbed-drivers-test-basic', 'mbed-drivers-test-hello', 'mbed-drivers-test-time_us']
|
|
build_path_list = ['./build/frdm-k64f-gcc', './build/frdm-k64f-armcc']
|
|
expandables = {
|
|
"{test_name_list}": test_name_list,
|
|
"{build_path_list}": build_path_list
|
|
}
|
|
expr = "lcov --gcov-tool arm-none-eabi-gcov [-a {build_path_list}/test/{test_name_list}.info] --output-file result.info"
|
|
'expr' expression [-a {build_path_list}/test/{test_name_list}.info] will expand to:
|
|
[
|
|
"-a ./build/frdm-k64f-gcc/test/mbed-drivers-test-basic.info",
|
|
"-a ./build/frdm-k64f-armcc/test/mbed-drivers-test-basic.info",
|
|
"-a ./build/frdm-k64f-gcc/test/mbed-drivers-test-hello.info",
|
|
"-a ./build/frdm-k64f-armcc/test/mbed-drivers-test-hello.info",
|
|
"-a ./build/frdm-k64f-gcc/test/mbed-drivers-test-time_us.info",
|
|
"-a ./build/frdm-k64f-armcc/test/mbed-drivers-test-time_us.info"
|
|
]
|
|
"""
|
|
result = None
|
|
if expandables:
|
|
expansion_result = []
|
|
m = re.search(r'\[.*?\]', expr)
|
|
if m:
|
|
expr_str_orig = m.group(0)
|
|
expr_str_base = m.group(0)[1:-1]
|
|
expr_str_list = [expr_str_base]
|
|
for token in expandables:
|
|
# We will expand only values which are lists (of strings)
|
|
if type(expandables[token]) is list:
|
|
# Use tokens with curly braces (Python string format like)
|
|
format_token = '{' + token + '}'
|
|
for expr_str in expr_str_list:
|
|
if format_token in expr_str:
|
|
patterns = expandables[token]
|
|
for pattern in patterns:
|
|
s = expr_str
|
|
s = s.replace(format_token, pattern)
|
|
expr_str_list.append(s)
|
|
# Nothing to extend/change in this string
|
|
if not any('{' + p + '}' in s for p in expandables.keys() if type(expandables[p]) is list):
|
|
expansion_result.append(s)
|
|
expansion_result.sort()
|
|
result = expr.replace(expr_str_orig, delimiter.join(expansion_result))
|
|
return result
|
|
|
|
class LcovHook(GreenteaCliTestHook):
|
|
"""! Class used to define a LCOV hook
|
|
"""
|
|
lcov_hooks = {
|
|
"hooks": {
|
|
"hook_test_end": "$lcov --gcov-tool gcov --capture --directory ./build --output-file {build_path}/{test_name}.info",
|
|
"hook_post_all_test_end": "$lcov --gcov-tool gcov [(-a << {build_path}/{test_name_list}.info>>)] --output-file result.info"
|
|
}
|
|
}
|
|
|
|
def __init__(self, name, cmd):
|
|
GreenteaCliTestHook.__init__(self, name, cmd)
|
|
|
|
@staticmethod
|
|
def format_before_run(cmd, format, verbose=False):
|
|
if format:
|
|
# We will expand first
|
|
cmd_expand = GreenteaCliTestHook.expand_parameters(cmd, format)
|
|
if cmd_expand:
|
|
cmd = cmd_expand
|
|
if verbose:
|
|
gt_logger.gt_log_tab("hook expanded: %s"% cmd)
|
|
|
|
cmd = cmd.format(**format)
|
|
cmd = LcovHook.check_if_file_exists_or_is_empty(cmd)
|
|
if verbose:
|
|
gt_logger.gt_log_tab("hook formated: %s"% cmd)
|
|
return cmd
|
|
|
|
@staticmethod
|
|
def check_if_file_exists_or_is_empty(expr):
|
|
"""! Check Expression for specific characters in hook command
|
|
@param expr Expression to check
|
|
@details
|
|
expr = "lcov --gcov-tool gcov (-a <<{build_path}/test/{test_name}.info>>) --output-file result.info"
|
|
where:
|
|
(...) -> specify part to check
|
|
<<...>> -> specify part which is a path to a file
|
|
'expr' expression (-a <<{build_path}/test/{test_name}.info>>) will be either:
|
|
"-a ./build/frdm-k64f-gcc/test/test_name.info"
|
|
or will be removed from command
|
|
It is also possible to use it in combination with expand_parameters:
|
|
expr = "lcov --gcov-tool gcov [(-a <<./build/{yotta_target_name}/{test_name_list}.info>>)] --output-file result.info"
|
|
"""
|
|
result = expr
|
|
expr_strs_orig = re.findall(r'\(.*?\)', expr)
|
|
for expr_str_orig in expr_strs_orig:
|
|
expr_str_base = expr_str_orig[1:-1]
|
|
result = result.replace(expr_str_orig, expr_str_base)
|
|
m = re.search(r'\<<.*?\>>', expr_str_base)
|
|
if m:
|
|
expr_str_path = m.group(0)[2:-2]
|
|
# Remove option if file not exists OR if file exists but empty
|
|
if not os.path.exists(expr_str_path):
|
|
result = result.replace(expr_str_base, '')
|
|
elif os.path.getsize(expr_str_path) == 0:
|
|
result = result.replace(expr_str_base, '')
|
|
|
|
# Remove path limiter
|
|
result = result.replace('<<', '')
|
|
result = result.replace('>>', '')
|
|
return result
|
|
|
|
class GreenteaHooks(object):
|
|
"""! Class used to store all hooks
|
|
@details Hooks command starts with '$' dollar sign
|
|
"""
|
|
HOOKS = {}
|
|
def __init__(self, path_to_hooks):
|
|
"""! Opens JSON file with
|
|
"""
|
|
try:
|
|
if path_to_hooks == 'lcov':
|
|
hooks = LcovHook.lcov_hooks
|
|
for hook in hooks['hooks']:
|
|
hook_name = hook
|
|
hook_expression = hooks['hooks'][hook]
|
|
self.HOOKS[hook_name] = LcovHook(hook_name, hook_expression[1:])
|
|
else:
|
|
with open(path_to_hooks, 'r') as data_file:
|
|
hooks = json.load(data_file)
|
|
if 'hooks' in hooks:
|
|
for hook in hooks['hooks']:
|
|
hook_name = hook
|
|
hook_expression = hooks['hooks'][hook]
|
|
# This is a command line hook
|
|
if hook_expression.startswith('$'):
|
|
self.HOOKS[hook_name] = GreenteaCliTestHook(hook_name, hook_expression[1:])
|
|
except IOError as e:
|
|
print(str(e))
|
|
self.HOOKS = None
|
|
|
|
def is_hooked_to(self, hook_name):
|
|
return hook_name in self.HOOKS
|
|
|
|
def run_hook(self, hook_name, format):
|
|
if hook_name in self.HOOKS:
|
|
return self.HOOKS[hook_name].run(format)
|
|
|
|
def run_hook_ext(self, hook_name, format):
|
|
if self.is_hooked_to(hook_name):
|
|
# We can execute this test hook just after all tests are finished ('hook_post_test_end')
|
|
self.run_hook(hook_name, format)
|