Skip to content

Commit

Permalink
Merge pull request #78 from ARMmbed/devel_transport
Browse files Browse the repository at this point in the history
[devel_transport] Async host test execition support

Road to version 0.2.0.
  • Loading branch information
PrzemekWirkus committed Mar 2, 2016
2 parents 694c3b9 + 91492a0 commit c5cad93
Show file tree
Hide file tree
Showing 12 changed files with 979 additions and 504 deletions.
65 changes: 65 additions & 0 deletions mbed_greentea/mbed_coverage_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""
mbed SDK
Copyright (c) 2011-2016 ARM Limited
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.
Author: Przemyslaw Wirkus <[email protected]>
"""

import os

"""
def __default_coverage_start_callback(self, key, value, timestamp):
# {{__coverage_start;PATH;PAYLOAD}}
# PAYLAODED is HEX coded string
lcov_path, lcov_payload = value.split(';')
try:
bin_payload = coverage_pack_hex_payload(lcov_payload)
coverage_dump_file(lcov_path, bin_payload)
self.log("dumped %d bytes to '%s'"% (len(bin_payload), lcov_path))
except Exception as e:
self.log("LCOV:" + str(e))
"""

def coverage_pack_hex_payload(payload):
"""! Convert a block of hex string data back to binary and return the binary data
@param payload String with hex encoded ascii data, e.g.: '6164636772...'
@return bytearray with payload with data
"""
# This payload might be packed with dot compression
# where byte value 0x00 is coded as ".", and not as "00"
payload = payload.replace('.', '00')

hex_pairs = map(''.join, zip(*[iter(payload)] * 2)) # ['61', '64', '63', '67', '72', ... ]
bin_payload = bytearray([int(s, 16) for s in hex_pairs])
return bin_payload

def coverage_dump_file(path, payload):
"""! Creates file and dumps payload to it on specified path (even if path doesn't exist)
@param path Path to file
@param payload Binary data to store in a file
@return True if operation was completed
"""
result = True
try:
d = os.path.dirname(path)
if not os.path.exists(d):
os.makedirs(d)
with open(path, "wb") as f:
f.write(payload)
except IOError as e:
print str(e)
result = False
return result
218 changes: 191 additions & 27 deletions mbed_greentea/mbed_greentea_cli.py

Large diffs are not rendered by default.

175 changes: 175 additions & 0 deletions mbed_greentea/mbed_greentea_hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
"""
mbed SDK
Copyright (c) 2011-2015 ARM Limited
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.
Author: Przemyslaw Wirkus <[email protected]>
"""

import re
import json
from subprocess import Popen, PIPE
from mbed_greentea.mbed_greentea_log import gt_logger

"""
List of available hooks:
"""


class GreenteaTestHook():
"""! 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('\[.*?\]', 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 GreenteaHooks():
"""! 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:
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)
Loading

0 comments on commit c5cad93

Please sign in to comment.