blob: ea85700af4bd8af764b7b986d34f7401f284a9de [file] [log] [blame]
Azim Khan951a2c82018-06-29 03:47:08 +01001# Greentea host test script for Mbed TLS on-target test suite testing.
Azim Khanf0e42fb2017-08-02 14:47:13 +01002#
Azim Khan8d686bf2018-07-04 23:29:46 +01003# Copyright (C) 2018, Arm Limited, All Rights Reserved
Azim Khanf0e42fb2017-08-02 14:47:13 +01004# SPDX-License-Identifier: Apache-2.0
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
Azim Khanb31aa442018-07-03 11:57:54 +010018# This file is part of Mbed TLS (https://tls.mbed.org)
Azim Khanf0e42fb2017-08-02 14:47:13 +010019
20
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010021"""
Azim Khan8d686bf2018-07-04 23:29:46 +010022Mbed TLS on-target test suite tests are implemented as Greentea
Azim Khan951a2c82018-06-29 03:47:08 +010023tests. Greentea tests are implemented in two parts: target test and
24host test. Target test is a C application that is built for the
25target platform and executes on the target. Host test is a Python
26class derived from mbed_host_tests.BaseHostTest. Target communicates
Azim Khan8d686bf2018-07-04 23:29:46 +010027with the host over serial for the test data and sends back the result.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010028
Azim Khanb31aa442018-07-03 11:57:54 +010029Python tool mbedgt (Greentea) is responsible for flashing the test
Azim Khan8d686bf2018-07-04 23:29:46 +010030binary on to the target and dynamically loading this host test module.
Azim Khan951a2c82018-06-29 03:47:08 +010031
Azim Khan8d686bf2018-07-04 23:29:46 +010032Greentea documentation can be found here:
33https://github.com/ARMmbed/greentea
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010034"""
35
Azim Khanf0e42fb2017-08-02 14:47:13 +010036
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010037import re
38import os
Azim Khan663d4702017-07-07 15:40:26 +010039import binascii
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010040from mbed_host_tests import BaseHostTest, event_callback
41
42
Azim Khanb98e6ee2018-06-28 17:11:33 +010043class TestDataParserError(Exception):
44 """Indicates error in test data, read from .data file."""
45 pass
46
47
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010048class TestDataParser(object):
49 """
Azim Khan951a2c82018-06-29 03:47:08 +010050 Parses test name, dependencies, test function name and test parameters
51 from the data file.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010052 """
53
54 def __init__(self):
55 """
56 Constructor
57 """
58 self.tests = []
59
60 def parse(self, data_file):
61 """
Azim Khanf0e42fb2017-08-02 14:47:13 +010062 Data file parser.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010063
Azim Khanf0e42fb2017-08-02 14:47:13 +010064 :param data_file: Data file path
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010065 """
Azim Khanb31aa442018-07-03 11:57:54 +010066 with open(data_file, 'r') as data_f:
67 self.__parse(data_f)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010068
69 @staticmethod
Azim Khanb31aa442018-07-03 11:57:54 +010070 def __escaped_split(inp_str, split_char):
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010071 """
Azim Khanb31aa442018-07-03 11:57:54 +010072 Splits inp_str on split_char except when escaped.
Azim Khanf0e42fb2017-08-02 14:47:13 +010073
Azim Khanb31aa442018-07-03 11:57:54 +010074 :param inp_str: String to split
75 :param split_char: Split character
Azim Khanf0e42fb2017-08-02 14:47:13 +010076 :return: List of splits
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010077 """
Ron Eldor111ba0e2018-11-18 17:05:05 +020078 split_colon_fn = lambda x: re.sub(r'\\' + split_char, split_char, x)
Azim Khanb31aa442018-07-03 11:57:54 +010079 if len(split_char) > 1:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010080 raise ValueError('Expected split character. Found string!')
Ron Eldor111ba0e2018-11-18 17:05:05 +020081 out = map(split_colon_fn, re.split(r'(?<!\\)' + split_char, inp_str))
Mohammad Azim Khan32cbcda2018-07-06 00:29:09 +010082 out = [x for x in out if x]
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010083 return out
84
Azim Khanb31aa442018-07-03 11:57:54 +010085 def __parse(self, data_f):
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010086 """
Azim Khanf0e42fb2017-08-02 14:47:13 +010087 Parses data file using supplied file object.
88
Azim Khanb31aa442018-07-03 11:57:54 +010089 :param data_f: Data file object
Azim Khanf0e42fb2017-08-02 14:47:13 +010090 :return:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010091 """
Azim Khanb31aa442018-07-03 11:57:54 +010092 for line in data_f:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010093 line = line.strip()
Azim Khanb31aa442018-07-03 11:57:54 +010094 if not line:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010095 continue
96 # Read test name
97 name = line
98
99 # Check dependencies
Azim Khanb31aa442018-07-03 11:57:54 +0100100 dependencies = []
101 line = data_f.next().strip()
102 match = re.search('depends_on:(.*)', line)
103 if match:
104 dependencies = [int(x) for x in match.group(1).split(':')]
105 line = data_f.next().strip()
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100106
107 # Read test vectors
Azim Khan663d4702017-07-07 15:40:26 +0100108 line = line.replace('\\n', '\n')
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100109 parts = self.__escaped_split(line, ':')
Azim Khanb31aa442018-07-03 11:57:54 +0100110 function_name = int(parts[0])
111 args = parts[1:]
112 args_count = len(args)
113 if args_count % 2 != 0:
Ron Eldor111ba0e2018-11-18 17:05:05 +0200114 err_str_fmt = "Number of test arguments({}) should be even: {}"
115 raise TestDataParserError(err_str_fmt.format(args_count, line))
Azim Khanb31aa442018-07-03 11:57:54 +0100116 grouped_args = [(args[i * 2], args[(i * 2) + 1])
117 for i in range(len(args)/2)]
118 self.tests.append((name, function_name, dependencies,
119 grouped_args))
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100120
121 def get_test_data(self):
122 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100123 Returns test data.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100124 """
125 return self.tests
126
127
128class MbedTlsTest(BaseHostTest):
129 """
Azim Khanb31aa442018-07-03 11:57:54 +0100130 Host test for Mbed TLS unit tests. This script is loaded at
131 run time by Greentea for executing Mbed TLS test suites. Each
Azim Khan951a2c82018-06-29 03:47:08 +0100132 communication from the target is received in this object as
133 an event, which is then handled by the event handler method
134 decorated by the associated event. Ex: @event_callback('GO').
135
136 Target test sends requests for dispatching next test. It reads
137 tests from the intermediate data file and sends test function
138 identifier, dependency identifiers, expression identifiers and
Azim Khanb31aa442018-07-03 11:57:54 +0100139 the test data in binary form. Target test checks dependencies
Azim Khan951a2c82018-06-29 03:47:08 +0100140 , evaluate integer constant expressions and dispatches the test
Azim Khan8d686bf2018-07-04 23:29:46 +0100141 function with received test parameters. After test function is
142 finished, target sends the result. This class handles the result
143 event and prints verdict in the form that Greentea understands.
Azim Khan951a2c82018-06-29 03:47:08 +0100144
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100145 """
Azim Khan951a2c82018-06-29 03:47:08 +0100146 # status/error codes from suites/helpers.function
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100147 DEPENDENCY_SUPPORTED = 0
148 KEY_VALUE_MAPPING_FOUND = DEPENDENCY_SUPPORTED
149 DISPATCH_TEST_SUCCESS = DEPENDENCY_SUPPORTED
150
Azim Khan951a2c82018-06-29 03:47:08 +0100151 KEY_VALUE_MAPPING_NOT_FOUND = -1 # Expression Id not found.
152 DEPENDENCY_NOT_SUPPORTED = -2 # Dependency not supported.
153 DISPATCH_TEST_FN_NOT_FOUND = -3 # Test function not found.
154 DISPATCH_INVALID_TEST_DATA = -4 # Invalid parameter type.
155 DISPATCH_UNSUPPORTED_SUITE = -5 # Test suite not supported/enabled.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100156
157 def __init__(self):
158 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100159 Constructor initialises test index to 0.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100160 """
161 super(MbedTlsTest, self).__init__()
162 self.tests = []
163 self.test_index = -1
164 self.dep_index = 0
165 self.error_str = dict()
Azim Khanb31aa442018-07-03 11:57:54 +0100166 self.error_str[self.DEPENDENCY_SUPPORTED] = \
167 'DEPENDENCY_SUPPORTED'
168 self.error_str[self.KEY_VALUE_MAPPING_NOT_FOUND] = \
169 'KEY_VALUE_MAPPING_NOT_FOUND'
170 self.error_str[self.DEPENDENCY_NOT_SUPPORTED] = \
171 'DEPENDENCY_NOT_SUPPORTED'
172 self.error_str[self.DISPATCH_TEST_FN_NOT_FOUND] = \
173 'DISPATCH_TEST_FN_NOT_FOUND'
174 self.error_str[self.DISPATCH_INVALID_TEST_DATA] = \
175 'DISPATCH_INVALID_TEST_DATA'
176 self.error_str[self.DISPATCH_UNSUPPORTED_SUITE] = \
177 'DISPATCH_UNSUPPORTED_SUITE'
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100178
179 def setup(self):
180 """
Azim Khan951a2c82018-06-29 03:47:08 +0100181 Setup hook implementation. Reads test suite data file and parses out
182 tests.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100183 """
184 binary_path = self.get_config_item('image_path')
185 script_dir = os.path.split(os.path.abspath(__file__))[0]
186 suite_name = os.path.splitext(os.path.basename(binary_path))[0]
187 data_file = ".".join((suite_name, 'data'))
Azim Khan951a2c82018-06-29 03:47:08 +0100188 data_file = os.path.join(script_dir, '..', 'mbedtls',
189 suite_name, data_file)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100190 if os.path.exists(data_file):
191 self.log("Running tests from %s" % data_file)
192 parser = TestDataParser()
193 parser.parse(data_file)
194 self.tests = parser.get_test_data()
195 self.print_test_info()
196 else:
197 self.log("Data file not found: %s" % data_file)
198 self.notify_complete(False)
199
200 def print_test_info(self):
201 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100202 Prints test summary read by Greentea to detect test cases.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100203 """
204 self.log('{{__testcase_count;%d}}' % len(self.tests))
205 for name, _, _, _ in self.tests:
206 self.log('{{__testcase_name;%s}}' % name)
207
208 @staticmethod
Azim Khanb31aa442018-07-03 11:57:54 +0100209 def align_32bit(data_bytes):
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100210 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100211 4 byte aligns input byte array.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100212
213 :return:
214 """
Azim Khanb31aa442018-07-03 11:57:54 +0100215 data_bytes += bytearray((4 - (len(data_bytes))) % 4)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100216
Azim Khand59391a2017-06-01 14:04:17 +0100217 @staticmethod
218 def hex_str_bytes(hex_str):
219 """
220 Converts Hex string representation to byte array
221
Azim Khanf0e42fb2017-08-02 14:47:13 +0100222 :param hex_str: Hex in string format.
223 :return: Output Byte array
Azim Khand59391a2017-06-01 14:04:17 +0100224 """
Azim Khanb98e6ee2018-06-28 17:11:33 +0100225 if hex_str[0] != '"' or hex_str[len(hex_str) - 1] != '"':
226 raise TestDataParserError("HEX test parameter missing '\"':"
227 " %s" % hex_str)
Azim Khand59391a2017-06-01 14:04:17 +0100228 hex_str = hex_str.strip('"')
Azim Khanb98e6ee2018-06-28 17:11:33 +0100229 if len(hex_str) % 2 != 0:
230 raise TestDataParserError("HEX parameter len should be mod of "
231 "2: %s" % hex_str)
Azim Khand59391a2017-06-01 14:04:17 +0100232
Azim Khanb31aa442018-07-03 11:57:54 +0100233 data_bytes = binascii.unhexlify(hex_str)
234 return data_bytes
Azim Khand59391a2017-06-01 14:04:17 +0100235
Azim Khan663d4702017-07-07 15:40:26 +0100236 @staticmethod
Azim Khanb31aa442018-07-03 11:57:54 +0100237 def int32_to_big_endian_bytes(i):
Azim Khan663d4702017-07-07 15:40:26 +0100238 """
Azim Khanb31aa442018-07-03 11:57:54 +0100239 Coverts i to byte array in big endian format.
Azim Khan663d4702017-07-07 15:40:26 +0100240
Azim Khanf0e42fb2017-08-02 14:47:13 +0100241 :param i: Input integer
242 :return: Output bytes array in big endian or network order
Azim Khan663d4702017-07-07 15:40:26 +0100243 """
Azim Khanb31aa442018-07-03 11:57:54 +0100244 data_bytes = bytearray([((i >> x) & 0xff) for x in [24, 16, 8, 0]])
245 return data_bytes
Azim Khan663d4702017-07-07 15:40:26 +0100246
Azim Khanb31aa442018-07-03 11:57:54 +0100247 def test_vector_to_bytes(self, function_id, dependencies, parameters):
Azim Khan663d4702017-07-07 15:40:26 +0100248 """
249 Converts test vector into a byte array that can be sent to the target.
250
Azim Khanf0e42fb2017-08-02 14:47:13 +0100251 :param function_id: Test Function Identifier
Azim Khanb31aa442018-07-03 11:57:54 +0100252 :param dependencies: Dependency list
Azim Khanf0e42fb2017-08-02 14:47:13 +0100253 :param parameters: Test function input parameters
254 :return: Byte array and its length
Azim Khan663d4702017-07-07 15:40:26 +0100255 """
Azim Khanb31aa442018-07-03 11:57:54 +0100256 data_bytes = bytearray([len(dependencies)])
257 if dependencies:
258 data_bytes += bytearray(dependencies)
259 data_bytes += bytearray([function_id, len(parameters)])
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100260 for typ, param in parameters:
261 if typ == 'int' or typ == 'exp':
262 i = int(param)
Azim Khanb31aa442018-07-03 11:57:54 +0100263 data_bytes += 'I' if typ == 'int' else 'E'
264 self.align_32bit(data_bytes)
265 data_bytes += self.int32_to_big_endian_bytes(i)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100266 elif typ == 'char*':
267 param = param.strip('"')
268 i = len(param) + 1 # + 1 for null termination
Azim Khanb31aa442018-07-03 11:57:54 +0100269 data_bytes += 'S'
270 self.align_32bit(data_bytes)
271 data_bytes += self.int32_to_big_endian_bytes(i)
272 data_bytes += bytearray(list(param))
273 data_bytes += '\0' # Null terminate
Azim Khand59391a2017-06-01 14:04:17 +0100274 elif typ == 'hex':
Azim Khanb31aa442018-07-03 11:57:54 +0100275 binary_data = self.hex_str_bytes(param)
276 data_bytes += 'H'
277 self.align_32bit(data_bytes)
278 i = len(binary_data)
279 data_bytes += self.int32_to_big_endian_bytes(i)
280 data_bytes += binary_data
281 length = self.int32_to_big_endian_bytes(len(data_bytes))
282 return data_bytes, length
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100283
284 def run_next_test(self):
285 """
Azim Khan951a2c82018-06-29 03:47:08 +0100286 Fetch next test information and execute the test.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100287
288 """
289 self.test_index += 1
290 self.dep_index = 0
291 if self.test_index < len(self.tests):
Azim Khanb31aa442018-07-03 11:57:54 +0100292 name, function_id, dependencies, args = self.tests[self.test_index]
293 self.run_test(name, function_id, dependencies, args)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100294 else:
295 self.notify_complete(True)
296
Azim Khanb31aa442018-07-03 11:57:54 +0100297 def run_test(self, name, function_id, dependencies, args):
Azim Khan663d4702017-07-07 15:40:26 +0100298 """
Azim Khan951a2c82018-06-29 03:47:08 +0100299 Execute the test on target by sending next test information.
Azim Khan663d4702017-07-07 15:40:26 +0100300
Azim Khanf0e42fb2017-08-02 14:47:13 +0100301 :param name: Test name
302 :param function_id: function identifier
Azim Khanb31aa442018-07-03 11:57:54 +0100303 :param dependencies: Dependencies list
Azim Khanf0e42fb2017-08-02 14:47:13 +0100304 :param args: test parameters
Azim Khan663d4702017-07-07 15:40:26 +0100305 :return:
306 """
307 self.log("Running: %s" % name)
308
Azim Khanb31aa442018-07-03 11:57:54 +0100309 param_bytes, length = self.test_vector_to_bytes(function_id,
310 dependencies, args)
311 self.send_kv(length, param_bytes)
Azim Khan663d4702017-07-07 15:40:26 +0100312
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100313 @staticmethod
314 def get_result(value):
Azim Khanf0e42fb2017-08-02 14:47:13 +0100315 """
316 Converts result from string type to integer
317 :param value: Result code in string
Azim Khan8d686bf2018-07-04 23:29:46 +0100318 :return: Integer result code. Value is from the test status
319 constants defined under the MbedTlsTest class.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100320 """
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100321 try:
322 return int(value)
323 except ValueError:
Azim Khanb31aa442018-07-03 11:57:54 +0100324 ValueError("Result should return error number. "
325 "Instead received %s" % value)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100326
327 @event_callback('GO')
Azim Khanb31aa442018-07-03 11:57:54 +0100328 def on_go(self, _key, _value, _timestamp):
Azim Khanf0e42fb2017-08-02 14:47:13 +0100329 """
Azim Khan951a2c82018-06-29 03:47:08 +0100330 Sent by the target to start first test.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100331
Azim Khanb31aa442018-07-03 11:57:54 +0100332 :param _key: Event key
333 :param _value: Value. ignored
334 :param _timestamp: Timestamp ignored.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100335 :return:
336 """
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100337 self.run_next_test()
338
339 @event_callback("R")
Azim Khanb31aa442018-07-03 11:57:54 +0100340 def on_result(self, _key, value, _timestamp):
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100341 """
Azim Khan951a2c82018-06-29 03:47:08 +0100342 Handle result. Prints test start, finish required by Greentea
343 to detect test execution.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100344
Azim Khanb31aa442018-07-03 11:57:54 +0100345 :param _key: Event key
Azim Khanf0e42fb2017-08-02 14:47:13 +0100346 :param value: Value. ignored
Azim Khanb31aa442018-07-03 11:57:54 +0100347 :param _timestamp: Timestamp ignored.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100348 :return:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100349 """
350 int_val = self.get_result(value)
Azim Khanb31aa442018-07-03 11:57:54 +0100351 name, _, _, _ = self.tests[self.test_index]
Azim Khan5e7f8df2017-05-31 20:33:39 +0100352 self.log('{{__testcase_start;%s}}' % name)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100353 self.log('{{__testcase_finish;%s;%d;%d}}' % (name, int_val == 0,
354 int_val != 0))
355 self.run_next_test()
356
357 @event_callback("F")
Azim Khanb31aa442018-07-03 11:57:54 +0100358 def on_failure(self, _key, value, _timestamp):
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100359 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100360 Handles test execution failure. That means dependency not supported or
361 Test function not supported. Hence marking test as skipped.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100362
Azim Khanb31aa442018-07-03 11:57:54 +0100363 :param _key: Event key
Azim Khanf0e42fb2017-08-02 14:47:13 +0100364 :param value: Value. ignored
Azim Khanb31aa442018-07-03 11:57:54 +0100365 :param _timestamp: Timestamp ignored.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100366 :return:
367 """
368 int_val = self.get_result(value)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100369 if int_val in self.error_str:
370 err = self.error_str[int_val]
371 else:
372 err = 'Unknown error'
373 # For skip status, do not write {{__testcase_finish;...}}
374 self.log("Error: %s" % err)
375 self.run_next_test()