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:
|
||||
ifndef WINDOWS
|
||||
rm -rf $(BINARIES) *.c *.datax TESTS
|
||||
rm -rf $(BINARIES) *.c *.datax
|
||||
rm -f src/*.o src/drivers/*.o src/libmbed*
|
||||
rm -f include/test/instrument_record_status.h
|
||||
else
|
||||
@ -184,9 +184,6 @@ else
|
||||
if exist src/drivers/*.o del /Q /F src/drivers/*.o
|
||||
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
|
||||
ifneq ($(wildcard TESTS/.*),)
|
||||
rmdir /Q /S TESTS
|
||||
endif
|
||||
endif
|
||||
|
||||
neat: clean
|
||||
@ -202,40 +199,6 @@ check: $(BINARIES)
|
||||
|
||||
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
|
||||
include/test/instrument_record_status.h: ../include/psa/crypto.h Makefile
|
||||
echo " Gen $@"
|
||||
|
@ -106,10 +106,6 @@ Platform file:
|
||||
Platform file contains platform specific setup code and test case
|
||||
dispatch code. For example, host_test.function reads test data
|
||||
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:
|
||||
---------
|
||||
|
@ -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…
Reference in New Issue
Block a user