mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2025-02-09 03:40:08 +00:00
Merge pull request #5116 from gilles-peskine-arm/remove-greentea-3.0
Remove on-target testing
This commit is contained in:
commit
774b4422e2
3
ChangeLog.d/remove-greentea-support.txt
Normal file
3
ChangeLog.d/remove-greentea-support.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Removals
|
||||||
|
* Remove the partial support for running unit tests via Greentea on Mbed OS,
|
||||||
|
which had been unmaintained since 2018.
|
@ -173,7 +173,7 @@ $(BINARIES): %$(EXEXT): %.c $(MBEDLIBS) $(TEST_OBJS_DEPS) $(MBEDTLS_TEST_OBJS)
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
ifndef WINDOWS
|
ifndef WINDOWS
|
||||||
rm -rf $(BINARIES) *.c *.datax TESTS
|
rm -rf $(BINARIES) *.c *.datax
|
||||||
rm -f src/*.o src/drivers/*.o src/libmbed*
|
rm -f src/*.o src/drivers/*.o src/libmbed*
|
||||||
rm -f include/test/instrument_record_status.h
|
rm -f include/test/instrument_record_status.h
|
||||||
else
|
else
|
||||||
@ -184,9 +184,6 @@ else
|
|||||||
if exist src/drivers/*.o del /Q /F src/drivers/*.o
|
if exist src/drivers/*.o del /Q /F src/drivers/*.o
|
||||||
if exist src/libmbed* del /Q /F src/libmed*
|
if exist src/libmbed* del /Q /F src/libmed*
|
||||||
if exist include/test/instrument_record_status.h del /Q /F include/test/instrument_record_status.h
|
if exist include/test/instrument_record_status.h del /Q /F include/test/instrument_record_status.h
|
||||||
ifneq ($(wildcard TESTS/.*),)
|
|
||||||
rmdir /Q /S TESTS
|
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
neat: clean
|
neat: clean
|
||||||
@ -202,40 +199,6 @@ check: $(BINARIES)
|
|||||||
|
|
||||||
test: check
|
test: check
|
||||||
|
|
||||||
# Create separate targets for generating embedded tests.
|
|
||||||
EMBEDDED_TESTS := $(addprefix embedded_,$(APPS))
|
|
||||||
|
|
||||||
# Generate test code for target.
|
|
||||||
|
|
||||||
.SECONDEXPANSION:
|
|
||||||
$(EMBEDDED_TESTS): embedded_%: suites/$$(firstword $$(subst ., ,$$*)).function suites/%.data scripts/generate_test_code.py suites/helpers.function suites/main_test.function suites/target_test.function
|
|
||||||
echo " Gen ./TESTS/mbedtls/$*/$*.c"
|
|
||||||
$(PYTHON) scripts/generate_test_code.py -f suites/$(firstword $(subst ., ,$*)).function \
|
|
||||||
-d suites/$*.data \
|
|
||||||
-t suites/main_test.function \
|
|
||||||
-p suites/target_test.function \
|
|
||||||
-s suites \
|
|
||||||
--helpers-file suites/helpers.function \
|
|
||||||
-o ./TESTS/mbedtls/$*
|
|
||||||
|
|
||||||
generate-target-tests: $(EMBEDDED_TESTS)
|
|
||||||
|
|
||||||
define copy_header_to_target
|
|
||||||
TESTS/mbedtls/$(1)/$(2): include/test/$(2)
|
|
||||||
echo " Copy ./$$@"
|
|
||||||
ifndef WINDOWS
|
|
||||||
mkdir -p $$(@D)
|
|
||||||
cp $$< $$@
|
|
||||||
else
|
|
||||||
mkdir $$(@D)
|
|
||||||
copy $$< $$@
|
|
||||||
endif
|
|
||||||
|
|
||||||
endef
|
|
||||||
$(foreach app, $(APPS), $(foreach file, $(notdir $(wildcard include/test/*.h)), \
|
|
||||||
$(eval $(call copy_header_to_target,$(app),$(file)))))
|
|
||||||
$(addprefix embedded_,$(filter test_suite_psa_%, $(APPS))): embedded_%: $(patsubst TESTS/mbedtls/%, include/test/%, $(wildcard include/test/*. include/test/*/*.h))
|
|
||||||
|
|
||||||
ifdef RECORD_PSA_STATUS_COVERAGE_LOG
|
ifdef RECORD_PSA_STATUS_COVERAGE_LOG
|
||||||
include/test/instrument_record_status.h: ../include/psa/crypto.h Makefile
|
include/test/instrument_record_status.h: ../include/psa/crypto.h Makefile
|
||||||
echo " Gen $@"
|
echo " Gen $@"
|
||||||
|
@ -106,10 +106,6 @@ Platform file:
|
|||||||
Platform file contains platform specific setup code and test case
|
Platform file contains platform specific setup code and test case
|
||||||
dispatch code. For example, host_test.function reads test data
|
dispatch code. For example, host_test.function reads test data
|
||||||
file from host's file system and dispatches tests.
|
file from host's file system and dispatches tests.
|
||||||
In case of on-target target_test.function tests are not dispatched
|
|
||||||
on target. Target code is kept minimum and only test functions are
|
|
||||||
dispatched. Test case dispatch is done on the host using tools like
|
|
||||||
Greentea.
|
|
||||||
|
|
||||||
Template file:
|
Template file:
|
||||||
---------
|
---------
|
||||||
|
@ -1,382 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# Greentea host test script for Mbed TLS on-target test suite testing.
|
|
||||||
#
|
|
||||||
# Copyright The Mbed TLS Contributors
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Mbed TLS on-target test suite tests are implemented as Greentea
|
|
||||||
tests. Greentea tests are implemented in two parts: target test and
|
|
||||||
host test. Target test is a C application that is built for the
|
|
||||||
target platform and executes on the target. Host test is a Python
|
|
||||||
class derived from mbed_host_tests.BaseHostTest. Target communicates
|
|
||||||
with the host over serial for the test data and sends back the result.
|
|
||||||
|
|
||||||
Python tool mbedgt (Greentea) is responsible for flashing the test
|
|
||||||
binary on to the target and dynamically loading this host test module.
|
|
||||||
|
|
||||||
Greentea documentation can be found here:
|
|
||||||
https://github.com/ARMmbed/greentea
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import binascii
|
|
||||||
|
|
||||||
from mbed_host_tests import BaseHostTest, event_callback # type: ignore # pylint: disable=import-error
|
|
||||||
|
|
||||||
|
|
||||||
class TestDataParserError(Exception):
|
|
||||||
"""Indicates error in test data, read from .data file."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TestDataParser:
|
|
||||||
"""
|
|
||||||
Parses test name, dependencies, test function name and test parameters
|
|
||||||
from the data file.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""
|
|
||||||
Constructor
|
|
||||||
"""
|
|
||||||
self.tests = []
|
|
||||||
|
|
||||||
def parse(self, data_file):
|
|
||||||
"""
|
|
||||||
Data file parser.
|
|
||||||
|
|
||||||
:param data_file: Data file path
|
|
||||||
"""
|
|
||||||
with open(data_file, 'r') as data_f:
|
|
||||||
self.__parse(data_f)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def __escaped_split(inp_str, split_char):
|
|
||||||
"""
|
|
||||||
Splits inp_str on split_char except when escaped.
|
|
||||||
|
|
||||||
:param inp_str: String to split
|
|
||||||
:param split_char: Split character
|
|
||||||
:return: List of splits
|
|
||||||
"""
|
|
||||||
split_colon_fn = lambda x: re.sub(r'\\' + split_char, split_char, x)
|
|
||||||
if len(split_char) > 1:
|
|
||||||
raise ValueError('Expected split character. Found string!')
|
|
||||||
out = list(map(split_colon_fn, re.split(r'(?<!\\)' + split_char, inp_str)))
|
|
||||||
out = [x for x in out if x]
|
|
||||||
return out
|
|
||||||
|
|
||||||
def __parse(self, data_f):
|
|
||||||
"""
|
|
||||||
Parses data file using supplied file object.
|
|
||||||
|
|
||||||
:param data_f: Data file object
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
for line in data_f:
|
|
||||||
line = line.strip()
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
# Read test name
|
|
||||||
name = line
|
|
||||||
|
|
||||||
# Check dependencies
|
|
||||||
dependencies = []
|
|
||||||
line = next(data_f).strip()
|
|
||||||
match = re.search('depends_on:(.*)', line)
|
|
||||||
if match:
|
|
||||||
dependencies = [int(x) for x in match.group(1).split(':')]
|
|
||||||
line = next(data_f).strip()
|
|
||||||
|
|
||||||
# Read test vectors
|
|
||||||
line = line.replace('\\n', '\n')
|
|
||||||
parts = self.__escaped_split(line, ':')
|
|
||||||
function_name = int(parts[0])
|
|
||||||
args = parts[1:]
|
|
||||||
args_count = len(args)
|
|
||||||
if args_count % 2 != 0:
|
|
||||||
err_str_fmt = "Number of test arguments({}) should be even: {}"
|
|
||||||
raise TestDataParserError(err_str_fmt.format(args_count, line))
|
|
||||||
grouped_args = [(args[i * 2], args[(i * 2) + 1])
|
|
||||||
for i in range(int(len(args)/2))]
|
|
||||||
self.tests.append((name, function_name, dependencies,
|
|
||||||
grouped_args))
|
|
||||||
|
|
||||||
def get_test_data(self):
|
|
||||||
"""
|
|
||||||
Returns test data.
|
|
||||||
"""
|
|
||||||
return self.tests
|
|
||||||
|
|
||||||
|
|
||||||
class MbedTlsTest(BaseHostTest):
|
|
||||||
"""
|
|
||||||
Host test for Mbed TLS unit tests. This script is loaded at
|
|
||||||
run time by Greentea for executing Mbed TLS test suites. Each
|
|
||||||
communication from the target is received in this object as
|
|
||||||
an event, which is then handled by the event handler method
|
|
||||||
decorated by the associated event. Ex: @event_callback('GO').
|
|
||||||
|
|
||||||
Target test sends requests for dispatching next test. It reads
|
|
||||||
tests from the intermediate data file and sends test function
|
|
||||||
identifier, dependency identifiers, expression identifiers and
|
|
||||||
the test data in binary form. Target test checks dependencies
|
|
||||||
, evaluate integer constant expressions and dispatches the test
|
|
||||||
function with received test parameters. After test function is
|
|
||||||
finished, target sends the result. This class handles the result
|
|
||||||
event and prints verdict in the form that Greentea understands.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# status/error codes from suites/helpers.function
|
|
||||||
DEPENDENCY_SUPPORTED = 0
|
|
||||||
KEY_VALUE_MAPPING_FOUND = DEPENDENCY_SUPPORTED
|
|
||||||
DISPATCH_TEST_SUCCESS = DEPENDENCY_SUPPORTED
|
|
||||||
|
|
||||||
KEY_VALUE_MAPPING_NOT_FOUND = -1 # Expression Id not found.
|
|
||||||
DEPENDENCY_NOT_SUPPORTED = -2 # Dependency not supported.
|
|
||||||
DISPATCH_TEST_FN_NOT_FOUND = -3 # Test function not found.
|
|
||||||
DISPATCH_INVALID_TEST_DATA = -4 # Invalid parameter type.
|
|
||||||
DISPATCH_UNSUPPORTED_SUITE = -5 # Test suite not supported/enabled.
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""
|
|
||||||
Constructor initialises test index to 0.
|
|
||||||
"""
|
|
||||||
super(MbedTlsTest, self).__init__()
|
|
||||||
self.tests = []
|
|
||||||
self.test_index = -1
|
|
||||||
self.dep_index = 0
|
|
||||||
self.suite_passed = True
|
|
||||||
self.error_str = dict()
|
|
||||||
self.error_str[self.DEPENDENCY_SUPPORTED] = \
|
|
||||||
'DEPENDENCY_SUPPORTED'
|
|
||||||
self.error_str[self.KEY_VALUE_MAPPING_NOT_FOUND] = \
|
|
||||||
'KEY_VALUE_MAPPING_NOT_FOUND'
|
|
||||||
self.error_str[self.DEPENDENCY_NOT_SUPPORTED] = \
|
|
||||||
'DEPENDENCY_NOT_SUPPORTED'
|
|
||||||
self.error_str[self.DISPATCH_TEST_FN_NOT_FOUND] = \
|
|
||||||
'DISPATCH_TEST_FN_NOT_FOUND'
|
|
||||||
self.error_str[self.DISPATCH_INVALID_TEST_DATA] = \
|
|
||||||
'DISPATCH_INVALID_TEST_DATA'
|
|
||||||
self.error_str[self.DISPATCH_UNSUPPORTED_SUITE] = \
|
|
||||||
'DISPATCH_UNSUPPORTED_SUITE'
|
|
||||||
|
|
||||||
def setup(self):
|
|
||||||
"""
|
|
||||||
Setup hook implementation. Reads test suite data file and parses out
|
|
||||||
tests.
|
|
||||||
"""
|
|
||||||
binary_path = self.get_config_item('image_path')
|
|
||||||
script_dir = os.path.split(os.path.abspath(__file__))[0]
|
|
||||||
suite_name = os.path.splitext(os.path.basename(binary_path))[0]
|
|
||||||
data_file = ".".join((suite_name, 'datax'))
|
|
||||||
data_file = os.path.join(script_dir, '..', 'mbedtls',
|
|
||||||
suite_name, data_file)
|
|
||||||
if os.path.exists(data_file):
|
|
||||||
self.log("Running tests from %s" % data_file)
|
|
||||||
parser = TestDataParser()
|
|
||||||
parser.parse(data_file)
|
|
||||||
self.tests = parser.get_test_data()
|
|
||||||
self.print_test_info()
|
|
||||||
else:
|
|
||||||
self.log("Data file not found: %s" % data_file)
|
|
||||||
self.notify_complete(False)
|
|
||||||
|
|
||||||
def print_test_info(self):
|
|
||||||
"""
|
|
||||||
Prints test summary read by Greentea to detect test cases.
|
|
||||||
"""
|
|
||||||
self.log('{{__testcase_count;%d}}' % len(self.tests))
|
|
||||||
for name, _, _, _ in self.tests:
|
|
||||||
self.log('{{__testcase_name;%s}}' % name)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def align_32bit(data_bytes):
|
|
||||||
"""
|
|
||||||
4 byte aligns input byte array.
|
|
||||||
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
data_bytes += bytearray((4 - (len(data_bytes))) % 4)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def hex_str_bytes(hex_str):
|
|
||||||
"""
|
|
||||||
Converts Hex string representation to byte array
|
|
||||||
|
|
||||||
:param hex_str: Hex in string format.
|
|
||||||
:return: Output Byte array
|
|
||||||
"""
|
|
||||||
if hex_str[0] != '"' or hex_str[len(hex_str) - 1] != '"':
|
|
||||||
raise TestDataParserError("HEX test parameter missing '\"':"
|
|
||||||
" %s" % hex_str)
|
|
||||||
hex_str = hex_str.strip('"')
|
|
||||||
if len(hex_str) % 2 != 0:
|
|
||||||
raise TestDataParserError("HEX parameter len should be mod of "
|
|
||||||
"2: %s" % hex_str)
|
|
||||||
|
|
||||||
data_bytes = binascii.unhexlify(hex_str)
|
|
||||||
return data_bytes
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def int32_to_big_endian_bytes(i):
|
|
||||||
"""
|
|
||||||
Coverts i to byte array in big endian format.
|
|
||||||
|
|
||||||
:param i: Input integer
|
|
||||||
:return: Output bytes array in big endian or network order
|
|
||||||
"""
|
|
||||||
data_bytes = bytearray([((i >> x) & 0xff) for x in [24, 16, 8, 0]])
|
|
||||||
return data_bytes
|
|
||||||
|
|
||||||
def test_vector_to_bytes(self, function_id, dependencies, parameters):
|
|
||||||
"""
|
|
||||||
Converts test vector into a byte array that can be sent to the target.
|
|
||||||
|
|
||||||
:param function_id: Test Function Identifier
|
|
||||||
:param dependencies: Dependency list
|
|
||||||
:param parameters: Test function input parameters
|
|
||||||
:return: Byte array and its length
|
|
||||||
"""
|
|
||||||
data_bytes = bytearray([len(dependencies)])
|
|
||||||
if dependencies:
|
|
||||||
data_bytes += bytearray(dependencies)
|
|
||||||
data_bytes += bytearray([function_id, len(parameters)])
|
|
||||||
for typ, param in parameters:
|
|
||||||
if typ in ('int', 'exp'):
|
|
||||||
i = int(param, 0)
|
|
||||||
data_bytes += b'I' if typ == 'int' else b'E'
|
|
||||||
self.align_32bit(data_bytes)
|
|
||||||
data_bytes += self.int32_to_big_endian_bytes(i)
|
|
||||||
elif typ == 'char*':
|
|
||||||
param = param.strip('"')
|
|
||||||
i = len(param) + 1 # + 1 for null termination
|
|
||||||
data_bytes += b'S'
|
|
||||||
self.align_32bit(data_bytes)
|
|
||||||
data_bytes += self.int32_to_big_endian_bytes(i)
|
|
||||||
data_bytes += bytearray(param, encoding='ascii')
|
|
||||||
data_bytes += b'\0' # Null terminate
|
|
||||||
elif typ == 'hex':
|
|
||||||
binary_data = self.hex_str_bytes(param)
|
|
||||||
data_bytes += b'H'
|
|
||||||
self.align_32bit(data_bytes)
|
|
||||||
i = len(binary_data)
|
|
||||||
data_bytes += self.int32_to_big_endian_bytes(i)
|
|
||||||
data_bytes += binary_data
|
|
||||||
length = self.int32_to_big_endian_bytes(len(data_bytes))
|
|
||||||
return data_bytes, length
|
|
||||||
|
|
||||||
def run_next_test(self):
|
|
||||||
"""
|
|
||||||
Fetch next test information and execute the test.
|
|
||||||
|
|
||||||
"""
|
|
||||||
self.test_index += 1
|
|
||||||
self.dep_index = 0
|
|
||||||
if self.test_index < len(self.tests):
|
|
||||||
name, function_id, dependencies, args = self.tests[self.test_index]
|
|
||||||
self.run_test(name, function_id, dependencies, args)
|
|
||||||
else:
|
|
||||||
self.notify_complete(self.suite_passed)
|
|
||||||
|
|
||||||
def run_test(self, name, function_id, dependencies, args):
|
|
||||||
"""
|
|
||||||
Execute the test on target by sending next test information.
|
|
||||||
|
|
||||||
:param name: Test name
|
|
||||||
:param function_id: function identifier
|
|
||||||
:param dependencies: Dependencies list
|
|
||||||
:param args: test parameters
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
self.log("Running: %s" % name)
|
|
||||||
|
|
||||||
param_bytes, length = self.test_vector_to_bytes(function_id,
|
|
||||||
dependencies, args)
|
|
||||||
self.send_kv(
|
|
||||||
''.join('{:02x}'.format(x) for x in length),
|
|
||||||
''.join('{:02x}'.format(x) for x in param_bytes)
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_result(value):
|
|
||||||
"""
|
|
||||||
Converts result from string type to integer
|
|
||||||
:param value: Result code in string
|
|
||||||
:return: Integer result code. Value is from the test status
|
|
||||||
constants defined under the MbedTlsTest class.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return int(value)
|
|
||||||
except ValueError:
|
|
||||||
ValueError("Result should return error number. "
|
|
||||||
"Instead received %s" % value)
|
|
||||||
|
|
||||||
@event_callback('GO')
|
|
||||||
def on_go(self, _key, _value, _timestamp):
|
|
||||||
"""
|
|
||||||
Sent by the target to start first test.
|
|
||||||
|
|
||||||
:param _key: Event key
|
|
||||||
:param _value: Value. ignored
|
|
||||||
:param _timestamp: Timestamp ignored.
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
self.run_next_test()
|
|
||||||
|
|
||||||
@event_callback("R")
|
|
||||||
def on_result(self, _key, value, _timestamp):
|
|
||||||
"""
|
|
||||||
Handle result. Prints test start, finish required by Greentea
|
|
||||||
to detect test execution.
|
|
||||||
|
|
||||||
:param _key: Event key
|
|
||||||
:param value: Value. ignored
|
|
||||||
:param _timestamp: Timestamp ignored.
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
int_val = self.get_result(value)
|
|
||||||
name, _, _, _ = self.tests[self.test_index]
|
|
||||||
self.log('{{__testcase_start;%s}}' % name)
|
|
||||||
self.log('{{__testcase_finish;%s;%d;%d}}' % (name, int_val == 0,
|
|
||||||
int_val != 0))
|
|
||||||
if int_val != 0:
|
|
||||||
self.suite_passed = False
|
|
||||||
self.run_next_test()
|
|
||||||
|
|
||||||
@event_callback("F")
|
|
||||||
def on_failure(self, _key, value, _timestamp):
|
|
||||||
"""
|
|
||||||
Handles test execution failure. That means dependency not supported or
|
|
||||||
Test function not supported. Hence marking test as skipped.
|
|
||||||
|
|
||||||
:param _key: Event key
|
|
||||||
:param value: Value. ignored
|
|
||||||
:param _timestamp: Timestamp ignored.
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
int_val = self.get_result(value)
|
|
||||||
if int_val in self.error_str:
|
|
||||||
err = self.error_str[int_val]
|
|
||||||
else:
|
|
||||||
err = 'Unknown error'
|
|
||||||
# For skip status, do not write {{__testcase_finish;...}}
|
|
||||||
self.log("Error: %s" % err)
|
|
||||||
self.run_next_test()
|
|
@ -1,449 +0,0 @@
|
|||||||
#line 2 "suites/target_test.function"
|
|
||||||
|
|
||||||
#include "greentea-client/test_env.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Increments pointer and asserts that it does not overflow.
|
|
||||||
*
|
|
||||||
* \param p Pointer to byte array
|
|
||||||
* \param start Pointer to start of byte array
|
|
||||||
* \param len Length of byte array
|
|
||||||
* \param step Increment size
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define INCR_ASSERT(p, start, len, step) do \
|
|
||||||
{ \
|
|
||||||
TEST_HELPER_ASSERT( ( p ) >= ( start ) ); \
|
|
||||||
TEST_HELPER_ASSERT( sizeof( *( p ) ) == sizeof( *( start ) ) ); \
|
|
||||||
/* <= is checked to support use inside a loop where \
|
|
||||||
pointer is incremented after reading data. */ \
|
|
||||||
TEST_HELPER_ASSERT( (uint32_t)( ( ( p ) - ( start ) ) + ( step ) ) <= ( len ) );\
|
|
||||||
( p ) += ( step ); \
|
|
||||||
} \
|
|
||||||
while( 0 )
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief 4 byte align unsigned char pointer
|
|
||||||
*
|
|
||||||
* \param p Pointer to byte array
|
|
||||||
* \param start Pointer to start of byte array
|
|
||||||
* \param len Length of byte array
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define ALIGN_32BIT(p, start, len) do \
|
|
||||||
{ \
|
|
||||||
uint32_t align = ( - (uintptr_t)( p ) ) % 4; \
|
|
||||||
INCR_ASSERT( ( p ), ( start ), ( len ), align );\
|
|
||||||
} \
|
|
||||||
while( 0 )
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Verify dependencies. Dependency identifiers are
|
|
||||||
* encoded in the buffer as 8 bit unsigned integers.
|
|
||||||
*
|
|
||||||
* \param count Number of dependencies.
|
|
||||||
* \param dep_p Pointer to buffer.
|
|
||||||
*
|
|
||||||
* \return DEPENDENCY_SUPPORTED if success else DEPENDENCY_NOT_SUPPORTED.
|
|
||||||
*/
|
|
||||||
int verify_dependencies( uint8_t count, uint8_t * dep_p )
|
|
||||||
{
|
|
||||||
uint8_t i;
|
|
||||||
for ( i = 0; i < count; i++ )
|
|
||||||
{
|
|
||||||
if ( dep_check( (int)(dep_p[i]) ) != DEPENDENCY_SUPPORTED )
|
|
||||||
return( DEPENDENCY_NOT_SUPPORTED );
|
|
||||||
}
|
|
||||||
return( DEPENDENCY_SUPPORTED );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Receives hex string on serial interface, and converts to a byte.
|
|
||||||
*
|
|
||||||
* \param none
|
|
||||||
*
|
|
||||||
* \return unsigned int8
|
|
||||||
*/
|
|
||||||
uint8_t receive_byte()
|
|
||||||
{
|
|
||||||
uint8_t byte;
|
|
||||||
uint8_t c[3];
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
c[0] = greentea_getc();
|
|
||||||
c[1] = greentea_getc();
|
|
||||||
c[2] = '\0';
|
|
||||||
|
|
||||||
TEST_HELPER_ASSERT( mbedtls_test_unhexify( &byte, sizeof( byte ),
|
|
||||||
c, &len ) == 0 );
|
|
||||||
TEST_HELPER_ASSERT( len != 2 );
|
|
||||||
|
|
||||||
return( byte );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Receives unsigned integer on serial interface.
|
|
||||||
* Integers are encoded in network order, and sent as hex ascii string.
|
|
||||||
*
|
|
||||||
* \param none
|
|
||||||
*
|
|
||||||
* \return unsigned int
|
|
||||||
*/
|
|
||||||
uint32_t receive_uint32()
|
|
||||||
{
|
|
||||||
uint32_t value;
|
|
||||||
size_t len;
|
|
||||||
const uint8_t c_be[8] = { greentea_getc(),
|
|
||||||
greentea_getc(),
|
|
||||||
greentea_getc(),
|
|
||||||
greentea_getc(),
|
|
||||||
greentea_getc(),
|
|
||||||
greentea_getc(),
|
|
||||||
greentea_getc(),
|
|
||||||
greentea_getc()
|
|
||||||
};
|
|
||||||
const uint8_t c[9] = { c_be[6], c_be[7], c_be[4], c_be[5], c_be[2],
|
|
||||||
c_be[3], c_be[0], c_be[1], '\0' };
|
|
||||||
|
|
||||||
TEST_HELPER_ASSERT( mbedtls_test_unhexify( (uint8_t*)&value, sizeof( value ),
|
|
||||||
c, &len ) == 0 );
|
|
||||||
TEST_HELPER_ASSERT( len != 8 );
|
|
||||||
|
|
||||||
return( value );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Parses out an unsigned 32 int value from the byte array.
|
|
||||||
* Integers are encoded in network order.
|
|
||||||
*
|
|
||||||
* \param p Pointer to byte array
|
|
||||||
*
|
|
||||||
* \return unsigned int
|
|
||||||
*/
|
|
||||||
uint32_t parse_uint32( uint8_t * p )
|
|
||||||
{
|
|
||||||
uint32_t value;
|
|
||||||
value = *p++ << 24;
|
|
||||||
value |= *p++ << 16;
|
|
||||||
value |= *p++ << 8;
|
|
||||||
value |= *p;
|
|
||||||
return( value );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Receives test data on serial as greentea key,value pair:
|
|
||||||
* {{<length>;<byte array>}}
|
|
||||||
*
|
|
||||||
* \param data_len Out pointer to hold received data length.
|
|
||||||
*
|
|
||||||
* \return Byte array.
|
|
||||||
*/
|
|
||||||
uint8_t * receive_data( uint32_t * data_len )
|
|
||||||
{
|
|
||||||
uint32_t i = 0, errors = 0;
|
|
||||||
char c;
|
|
||||||
uint8_t * data = NULL;
|
|
||||||
|
|
||||||
/* Read opening braces */
|
|
||||||
i = 0;
|
|
||||||
while ( i < 2 )
|
|
||||||
{
|
|
||||||
c = greentea_getc();
|
|
||||||
/* Ignore any prevous CR LF characters */
|
|
||||||
if ( c == '\n' || c == '\r' )
|
|
||||||
continue;
|
|
||||||
i++;
|
|
||||||
if ( c != '{' )
|
|
||||||
return( NULL );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read data length */
|
|
||||||
*data_len = receive_uint32();
|
|
||||||
data = (uint8_t *)malloc( *data_len );
|
|
||||||
TEST_HELPER_ASSERT( data != NULL );
|
|
||||||
|
|
||||||
greentea_getc(); // read ';' received after key i.e. *data_len
|
|
||||||
|
|
||||||
for( i = 0; i < *data_len; i++ )
|
|
||||||
data[i] = receive_byte();
|
|
||||||
|
|
||||||
/* Read closing braces */
|
|
||||||
for( i = 0; i < 2; i++ )
|
|
||||||
{
|
|
||||||
c = greentea_getc();
|
|
||||||
if ( c != '}' )
|
|
||||||
{
|
|
||||||
errors++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( errors )
|
|
||||||
{
|
|
||||||
free( data );
|
|
||||||
data = NULL;
|
|
||||||
*data_len = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return( data );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Parse the received byte array and count the number of arguments
|
|
||||||
* to the test function passed as type hex.
|
|
||||||
*
|
|
||||||
* \param count Parameter count
|
|
||||||
* \param data Received Byte array
|
|
||||||
* \param data_len Byte array length
|
|
||||||
*
|
|
||||||
* \return count of hex params
|
|
||||||
*/
|
|
||||||
uint32_t find_hex_count( uint8_t count, uint8_t * data, uint32_t data_len )
|
|
||||||
{
|
|
||||||
uint32_t i = 0, sz = 0;
|
|
||||||
char c;
|
|
||||||
uint8_t * p = NULL;
|
|
||||||
uint32_t hex_count = 0;
|
|
||||||
|
|
||||||
p = data;
|
|
||||||
|
|
||||||
for( i = 0; i < count; i++ )
|
|
||||||
{
|
|
||||||
c = (char)*p;
|
|
||||||
INCR_ASSERT( p, data, data_len, 1 );
|
|
||||||
|
|
||||||
/* Align p to 4 bytes for int, expression, string len or hex length */
|
|
||||||
ALIGN_32BIT( p, data, data_len );
|
|
||||||
|
|
||||||
/* Network to host conversion */
|
|
||||||
sz = (int32_t)parse_uint32( p );
|
|
||||||
|
|
||||||
INCR_ASSERT( p, data, data_len, sizeof( int32_t ) );
|
|
||||||
|
|
||||||
if ( c == 'H' || c == 'S' )
|
|
||||||
{
|
|
||||||
INCR_ASSERT( p, data, data_len, sz );
|
|
||||||
hex_count += ( c == 'H' )?1:0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return( hex_count );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Parses received byte array for test parameters.
|
|
||||||
*
|
|
||||||
* \param count Parameter count
|
|
||||||
* \param data Received Byte array
|
|
||||||
* \param data_len Byte array length
|
|
||||||
* \param error Parsing error out variable.
|
|
||||||
*
|
|
||||||
* \return Array of parsed parameters allocated on heap.
|
|
||||||
* Note: Caller has the responsibility to delete
|
|
||||||
* the memory after use.
|
|
||||||
*/
|
|
||||||
void ** parse_parameters( uint8_t count, uint8_t * data, uint32_t data_len,
|
|
||||||
int * error )
|
|
||||||
{
|
|
||||||
uint32_t i = 0, hex_count = 0;
|
|
||||||
char c;
|
|
||||||
void ** params = NULL;
|
|
||||||
void ** cur = NULL;
|
|
||||||
uint8_t * p = NULL;
|
|
||||||
|
|
||||||
hex_count = find_hex_count(count, data, data_len);
|
|
||||||
|
|
||||||
params = (void **)malloc( sizeof( void *) * ( count + hex_count ) );
|
|
||||||
TEST_HELPER_ASSERT( params != NULL );
|
|
||||||
cur = params;
|
|
||||||
|
|
||||||
p = data;
|
|
||||||
|
|
||||||
/* Parameters */
|
|
||||||
for( i = 0; i < count; i++ )
|
|
||||||
{
|
|
||||||
c = (char)*p;
|
|
||||||
INCR_ASSERT( p, data, data_len, 1 );
|
|
||||||
|
|
||||||
/* Align p to 4 bytes for int, expression, string len or hex length */
|
|
||||||
ALIGN_32BIT( p, data, data_len );
|
|
||||||
|
|
||||||
/* Network to host conversion */
|
|
||||||
*( (int32_t *)p ) = (int32_t)parse_uint32( p );
|
|
||||||
|
|
||||||
switch( c )
|
|
||||||
{
|
|
||||||
case 'E':
|
|
||||||
{
|
|
||||||
if ( get_expression( *( (int32_t *)p ), (int32_t *)p ) )
|
|
||||||
{
|
|
||||||
*error = KEY_VALUE_MAPPING_NOT_FOUND;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
} /* Intentional fall through */
|
|
||||||
case 'I':
|
|
||||||
{
|
|
||||||
*cur++ = (void *)p;
|
|
||||||
INCR_ASSERT( p, data, data_len, sizeof( int32_t ) );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'H': /* Intentional fall through */
|
|
||||||
case 'S':
|
|
||||||
{
|
|
||||||
uint32_t * sz = (uint32_t *)p;
|
|
||||||
INCR_ASSERT( p, data, data_len, sizeof( int32_t ) );
|
|
||||||
*cur++ = (void *)p;
|
|
||||||
if ( c == 'H' )
|
|
||||||
*cur++ = (void *)sz;
|
|
||||||
INCR_ASSERT( p, data, data_len, ( *sz ) );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
*error = DISPATCH_INVALID_TEST_DATA;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exit:
|
|
||||||
if ( *error )
|
|
||||||
{
|
|
||||||
free( params );
|
|
||||||
params = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return( params );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Sends greentea key and int value pair to host.
|
|
||||||
*
|
|
||||||
* \param key key string
|
|
||||||
* \param value integer value
|
|
||||||
*
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
void send_key_integer( char * key, int value )
|
|
||||||
{
|
|
||||||
char str[50];
|
|
||||||
snprintf( str, sizeof( str ), "%d", value );
|
|
||||||
greentea_send_kv( key, str );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Sends test setup failure to the host.
|
|
||||||
*
|
|
||||||
* \param failure Test set failure
|
|
||||||
*
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
void send_failure( int failure )
|
|
||||||
{
|
|
||||||
send_key_integer( "F", failure );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Sends test status to the host.
|
|
||||||
*
|
|
||||||
* \param status Test status (PASS=0/FAIL=!0)
|
|
||||||
*
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
void send_status( int status )
|
|
||||||
{
|
|
||||||
send_key_integer( "R", status );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Embedded implementation of execute_tests().
|
|
||||||
* Ignores command line and received test data
|
|
||||||
* on serial.
|
|
||||||
*
|
|
||||||
* \param argc not used
|
|
||||||
* \param argv not used
|
|
||||||
*
|
|
||||||
* \return Program exit status.
|
|
||||||
*/
|
|
||||||
int execute_tests( int args, const char ** argv )
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
uint32_t data_len = 0;
|
|
||||||
uint8_t count = 0, function_id;
|
|
||||||
void ** params = NULL;
|
|
||||||
uint8_t * data = NULL, * p = NULL;
|
|
||||||
|
|
||||||
GREENTEA_SETUP( 800, "mbedtls_test" );
|
|
||||||
greentea_send_kv( "GO", " " );
|
|
||||||
|
|
||||||
while ( 1 )
|
|
||||||
{
|
|
||||||
ret = 0;
|
|
||||||
mbedtls_test_info_reset( );
|
|
||||||
data_len = 0;
|
|
||||||
|
|
||||||
data = receive_data( &data_len );
|
|
||||||
if ( data == NULL )
|
|
||||||
continue;
|
|
||||||
p = data;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
/* Read dependency count */
|
|
||||||
count = *p;
|
|
||||||
TEST_HELPER_ASSERT( count < data_len );
|
|
||||||
INCR_ASSERT( p, data, data_len, sizeof( uint8_t ) );
|
|
||||||
ret = verify_dependencies( count, p );
|
|
||||||
if ( ret != DEPENDENCY_SUPPORTED )
|
|
||||||
break;
|
|
||||||
|
|
||||||
if ( count )
|
|
||||||
INCR_ASSERT( p, data, data_len, count );
|
|
||||||
|
|
||||||
/* Read function id */
|
|
||||||
function_id = *p;
|
|
||||||
INCR_ASSERT( p, data, data_len, sizeof( uint8_t ) );
|
|
||||||
if ( ( ret = check_test( function_id ) ) != DISPATCH_TEST_SUCCESS )
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Read number of parameters */
|
|
||||||
count = *p;
|
|
||||||
INCR_ASSERT( p, data, data_len, sizeof( uint8_t ) );
|
|
||||||
|
|
||||||
/* Parse parameters if present */
|
|
||||||
if ( count )
|
|
||||||
{
|
|
||||||
params = parse_parameters( count, p, data_len - ( p - data ), &ret );
|
|
||||||
if ( ret )
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = dispatch_test( function_id, params );
|
|
||||||
}
|
|
||||||
while ( 0 );
|
|
||||||
|
|
||||||
if ( data )
|
|
||||||
{
|
|
||||||
free( data );
|
|
||||||
data = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( params )
|
|
||||||
{
|
|
||||||
free( params );
|
|
||||||
params = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ret )
|
|
||||||
send_failure( ret );
|
|
||||||
else
|
|
||||||
send_status( mbedtls_test_info.result );
|
|
||||||
}
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user