blob: 8fd72613eb4a6cb72f96806c2c356878525ce138 [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#
Mohammad Azim Khan78befd92018-03-06 11:49:41 +00003# 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 Khanb31aa442018-07-03 11:57:54 +010022Mbed TLS on-target test suite tests are implemented as mbed-os 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
27with the host over serial for the test data.
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 Khan951a2c82018-06-29 03:47:08 +010030binary on to the target and dynamically loading the host test.
31
32This script contains the host test for handling target test's
33requests for test vectors. It also reports the test results
34in format understood by Greentea.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010035"""
36
Azim Khanf0e42fb2017-08-02 14:47:13 +010037
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010038import re
39import os
Azim Khan663d4702017-07-07 15:40:26 +010040import binascii
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010041from mbed_host_tests import BaseHostTest, event_callback
42
43
Azim Khanb98e6ee2018-06-28 17:11:33 +010044class TestDataParserError(Exception):
45 """Indicates error in test data, read from .data file."""
46 pass
47
48
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010049class TestDataParser(object):
50 """
Azim Khan951a2c82018-06-29 03:47:08 +010051 Parses test name, dependencies, test function name and test parameters
52 from the data file.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010053 """
54
55 def __init__(self):
56 """
57 Constructor
58 """
59 self.tests = []
60
61 def parse(self, data_file):
62 """
Azim Khanf0e42fb2017-08-02 14:47:13 +010063 Data file parser.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010064
Azim Khanf0e42fb2017-08-02 14:47:13 +010065 :param data_file: Data file path
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010066 """
Azim Khanb31aa442018-07-03 11:57:54 +010067 with open(data_file, 'r') as data_f:
68 self.__parse(data_f)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010069
70 @staticmethod
Azim Khanb31aa442018-07-03 11:57:54 +010071 def __escaped_split(inp_str, split_char):
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010072 """
Azim Khanb31aa442018-07-03 11:57:54 +010073 Splits inp_str on split_char except when escaped.
Azim Khanf0e42fb2017-08-02 14:47:13 +010074
Azim Khanb31aa442018-07-03 11:57:54 +010075 :param inp_str: String to split
76 :param split_char: Split character
Azim Khanf0e42fb2017-08-02 14:47:13 +010077 :return: List of splits
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010078 """
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!')
81 out = []
82 part = ''
83 escape = False
Azim Khanb31aa442018-07-03 11:57:54 +010084 for character in inp_str:
85 if not escape and character == split_char:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010086 out.append(part)
87 part = ''
88 else:
Azim Khanb31aa442018-07-03 11:57:54 +010089 part += character
90 escape = not escape and character == '\\'
91 if part:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010092 out.append(part)
93 return out
94
Azim Khanb31aa442018-07-03 11:57:54 +010095 def __parse(self, data_f):
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010096 """
Azim Khanf0e42fb2017-08-02 14:47:13 +010097 Parses data file using supplied file object.
98
Azim Khanb31aa442018-07-03 11:57:54 +010099 :param data_f: Data file object
Azim Khanf0e42fb2017-08-02 14:47:13 +0100100 :return:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100101 """
Azim Khanb31aa442018-07-03 11:57:54 +0100102 for line in data_f:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100103 line = line.strip()
Azim Khanb31aa442018-07-03 11:57:54 +0100104 if not line:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100105 continue
106 # Read test name
107 name = line
108
109 # Check dependencies
Azim Khanb31aa442018-07-03 11:57:54 +0100110 dependencies = []
111 line = data_f.next().strip()
112 match = re.search('depends_on:(.*)', line)
113 if match:
114 dependencies = [int(x) for x in match.group(1).split(':')]
115 line = data_f.next().strip()
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100116
117 # Read test vectors
Azim Khan663d4702017-07-07 15:40:26 +0100118 line = line.replace('\\n', '\n')
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100119 parts = self.__escaped_split(line, ':')
Azim Khanb31aa442018-07-03 11:57:54 +0100120 function_name = int(parts[0])
121 args = parts[1:]
122 args_count = len(args)
123 if args_count % 2 != 0:
Azim Khanb98e6ee2018-06-28 17:11:33 +0100124 raise TestDataParserError("Number of test arguments should "
125 "be even: %s" % line)
Azim Khanb31aa442018-07-03 11:57:54 +0100126 grouped_args = [(args[i * 2], args[(i * 2) + 1])
127 for i in range(len(args)/2)]
128 self.tests.append((name, function_name, dependencies,
129 grouped_args))
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100130
131 def get_test_data(self):
132 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100133 Returns test data.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100134 """
135 return self.tests
136
137
138class MbedTlsTest(BaseHostTest):
139 """
Azim Khanb31aa442018-07-03 11:57:54 +0100140 Host test for Mbed TLS unit tests. This script is loaded at
141 run time by Greentea for executing Mbed TLS test suites. Each
Azim Khan951a2c82018-06-29 03:47:08 +0100142 communication from the target is received in this object as
143 an event, which is then handled by the event handler method
144 decorated by the associated event. Ex: @event_callback('GO').
145
146 Target test sends requests for dispatching next test. It reads
147 tests from the intermediate data file and sends test function
148 identifier, dependency identifiers, expression identifiers and
Azim Khanb31aa442018-07-03 11:57:54 +0100149 the test data in binary form. Target test checks dependencies
Azim Khan951a2c82018-06-29 03:47:08 +0100150 , evaluate integer constant expressions and dispatches the test
151 function with received test parameters.
152
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100153 """
Azim Khan951a2c82018-06-29 03:47:08 +0100154 # status/error codes from suites/helpers.function
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100155 DEPENDENCY_SUPPORTED = 0
156 KEY_VALUE_MAPPING_FOUND = DEPENDENCY_SUPPORTED
157 DISPATCH_TEST_SUCCESS = DEPENDENCY_SUPPORTED
158
Azim Khan951a2c82018-06-29 03:47:08 +0100159 KEY_VALUE_MAPPING_NOT_FOUND = -1 # Expression Id not found.
160 DEPENDENCY_NOT_SUPPORTED = -2 # Dependency not supported.
161 DISPATCH_TEST_FN_NOT_FOUND = -3 # Test function not found.
162 DISPATCH_INVALID_TEST_DATA = -4 # Invalid parameter type.
163 DISPATCH_UNSUPPORTED_SUITE = -5 # Test suite not supported/enabled.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100164
165 def __init__(self):
166 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100167 Constructor initialises test index to 0.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100168 """
169 super(MbedTlsTest, self).__init__()
170 self.tests = []
171 self.test_index = -1
172 self.dep_index = 0
173 self.error_str = dict()
Azim Khanb31aa442018-07-03 11:57:54 +0100174 self.error_str[self.DEPENDENCY_SUPPORTED] = \
175 'DEPENDENCY_SUPPORTED'
176 self.error_str[self.KEY_VALUE_MAPPING_NOT_FOUND] = \
177 'KEY_VALUE_MAPPING_NOT_FOUND'
178 self.error_str[self.DEPENDENCY_NOT_SUPPORTED] = \
179 'DEPENDENCY_NOT_SUPPORTED'
180 self.error_str[self.DISPATCH_TEST_FN_NOT_FOUND] = \
181 'DISPATCH_TEST_FN_NOT_FOUND'
182 self.error_str[self.DISPATCH_INVALID_TEST_DATA] = \
183 'DISPATCH_INVALID_TEST_DATA'
184 self.error_str[self.DISPATCH_UNSUPPORTED_SUITE] = \
185 'DISPATCH_UNSUPPORTED_SUITE'
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100186
187 def setup(self):
188 """
Azim Khan951a2c82018-06-29 03:47:08 +0100189 Setup hook implementation. Reads test suite data file and parses out
190 tests.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100191 """
192 binary_path = self.get_config_item('image_path')
193 script_dir = os.path.split(os.path.abspath(__file__))[0]
194 suite_name = os.path.splitext(os.path.basename(binary_path))[0]
195 data_file = ".".join((suite_name, 'data'))
Azim Khan951a2c82018-06-29 03:47:08 +0100196 data_file = os.path.join(script_dir, '..', 'mbedtls',
197 suite_name, data_file)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100198 if os.path.exists(data_file):
199 self.log("Running tests from %s" % data_file)
200 parser = TestDataParser()
201 parser.parse(data_file)
202 self.tests = parser.get_test_data()
203 self.print_test_info()
204 else:
205 self.log("Data file not found: %s" % data_file)
206 self.notify_complete(False)
207
208 def print_test_info(self):
209 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100210 Prints test summary read by Greentea to detect test cases.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100211 """
212 self.log('{{__testcase_count;%d}}' % len(self.tests))
213 for name, _, _, _ in self.tests:
214 self.log('{{__testcase_name;%s}}' % name)
215
216 @staticmethod
Azim Khanb31aa442018-07-03 11:57:54 +0100217 def align_32bit(data_bytes):
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100218 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100219 4 byte aligns input byte array.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100220
221 :return:
222 """
Azim Khanb31aa442018-07-03 11:57:54 +0100223 data_bytes += bytearray((4 - (len(data_bytes))) % 4)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100224
Azim Khand59391a2017-06-01 14:04:17 +0100225 @staticmethod
226 def hex_str_bytes(hex_str):
227 """
228 Converts Hex string representation to byte array
229
Azim Khanf0e42fb2017-08-02 14:47:13 +0100230 :param hex_str: Hex in string format.
231 :return: Output Byte array
Azim Khand59391a2017-06-01 14:04:17 +0100232 """
Azim Khanb98e6ee2018-06-28 17:11:33 +0100233 if hex_str[0] != '"' or hex_str[len(hex_str) - 1] != '"':
234 raise TestDataParserError("HEX test parameter missing '\"':"
235 " %s" % hex_str)
Azim Khand59391a2017-06-01 14:04:17 +0100236 hex_str = hex_str.strip('"')
Azim Khanb98e6ee2018-06-28 17:11:33 +0100237 if len(hex_str) % 2 != 0:
238 raise TestDataParserError("HEX parameter len should be mod of "
239 "2: %s" % hex_str)
Azim Khand59391a2017-06-01 14:04:17 +0100240
Azim Khanb31aa442018-07-03 11:57:54 +0100241 data_bytes = binascii.unhexlify(hex_str)
242 return data_bytes
Azim Khand59391a2017-06-01 14:04:17 +0100243
Azim Khan663d4702017-07-07 15:40:26 +0100244 @staticmethod
Azim Khanb31aa442018-07-03 11:57:54 +0100245 def int32_to_big_endian_bytes(i):
Azim Khan663d4702017-07-07 15:40:26 +0100246 """
Azim Khanb31aa442018-07-03 11:57:54 +0100247 Coverts i to byte array in big endian format.
Azim Khan663d4702017-07-07 15:40:26 +0100248
Azim Khanf0e42fb2017-08-02 14:47:13 +0100249 :param i: Input integer
250 :return: Output bytes array in big endian or network order
Azim Khan663d4702017-07-07 15:40:26 +0100251 """
Azim Khanb31aa442018-07-03 11:57:54 +0100252 data_bytes = bytearray([((i >> x) & 0xff) for x in [24, 16, 8, 0]])
253 return data_bytes
Azim Khan663d4702017-07-07 15:40:26 +0100254
Azim Khanb31aa442018-07-03 11:57:54 +0100255 def test_vector_to_bytes(self, function_id, dependencies, parameters):
Azim Khan663d4702017-07-07 15:40:26 +0100256 """
257 Converts test vector into a byte array that can be sent to the target.
258
Azim Khanf0e42fb2017-08-02 14:47:13 +0100259 :param function_id: Test Function Identifier
Azim Khanb31aa442018-07-03 11:57:54 +0100260 :param dependencies: Dependency list
Azim Khanf0e42fb2017-08-02 14:47:13 +0100261 :param parameters: Test function input parameters
262 :return: Byte array and its length
Azim Khan663d4702017-07-07 15:40:26 +0100263 """
Azim Khanb31aa442018-07-03 11:57:54 +0100264 data_bytes = bytearray([len(dependencies)])
265 if dependencies:
266 data_bytes += bytearray(dependencies)
267 data_bytes += bytearray([function_id, len(parameters)])
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100268 for typ, param in parameters:
269 if typ == 'int' or typ == 'exp':
270 i = int(param)
Azim Khanb31aa442018-07-03 11:57:54 +0100271 data_bytes += 'I' if typ == 'int' else 'E'
272 self.align_32bit(data_bytes)
273 data_bytes += self.int32_to_big_endian_bytes(i)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100274 elif typ == 'char*':
275 param = param.strip('"')
276 i = len(param) + 1 # + 1 for null termination
Azim Khanb31aa442018-07-03 11:57:54 +0100277 data_bytes += 'S'
278 self.align_32bit(data_bytes)
279 data_bytes += self.int32_to_big_endian_bytes(i)
280 data_bytes += bytearray(list(param))
281 data_bytes += '\0' # Null terminate
Azim Khand59391a2017-06-01 14:04:17 +0100282 elif typ == 'hex':
Azim Khanb31aa442018-07-03 11:57:54 +0100283 binary_data = self.hex_str_bytes(param)
284 data_bytes += 'H'
285 self.align_32bit(data_bytes)
286 i = len(binary_data)
287 data_bytes += self.int32_to_big_endian_bytes(i)
288 data_bytes += binary_data
289 length = self.int32_to_big_endian_bytes(len(data_bytes))
290 return data_bytes, length
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100291
292 def run_next_test(self):
293 """
Azim Khan951a2c82018-06-29 03:47:08 +0100294 Fetch next test information and execute the test.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100295
296 """
297 self.test_index += 1
298 self.dep_index = 0
299 if self.test_index < len(self.tests):
Azim Khanb31aa442018-07-03 11:57:54 +0100300 name, function_id, dependencies, args = self.tests[self.test_index]
301 self.run_test(name, function_id, dependencies, args)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100302 else:
303 self.notify_complete(True)
304
Azim Khanb31aa442018-07-03 11:57:54 +0100305 def run_test(self, name, function_id, dependencies, args):
Azim Khan663d4702017-07-07 15:40:26 +0100306 """
Azim Khan951a2c82018-06-29 03:47:08 +0100307 Execute the test on target by sending next test information.
Azim Khan663d4702017-07-07 15:40:26 +0100308
Azim Khanf0e42fb2017-08-02 14:47:13 +0100309 :param name: Test name
310 :param function_id: function identifier
Azim Khanb31aa442018-07-03 11:57:54 +0100311 :param dependencies: Dependencies list
Azim Khanf0e42fb2017-08-02 14:47:13 +0100312 :param args: test parameters
Azim Khan663d4702017-07-07 15:40:26 +0100313 :return:
314 """
315 self.log("Running: %s" % name)
316
Azim Khanb31aa442018-07-03 11:57:54 +0100317 param_bytes, length = self.test_vector_to_bytes(function_id,
318 dependencies, args)
319 self.send_kv(length, param_bytes)
Azim Khan663d4702017-07-07 15:40:26 +0100320
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100321 @staticmethod
322 def get_result(value):
Azim Khanf0e42fb2017-08-02 14:47:13 +0100323 """
324 Converts result from string type to integer
325 :param value: Result code in string
326 :return: Integer result code
327 """
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100328 try:
329 return int(value)
330 except ValueError:
Azim Khanb31aa442018-07-03 11:57:54 +0100331 ValueError("Result should return error number. "
332 "Instead received %s" % value)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100333 return 0
334
335 @event_callback('GO')
Azim Khanb31aa442018-07-03 11:57:54 +0100336 def on_go(self, _key, _value, _timestamp):
Azim Khanf0e42fb2017-08-02 14:47:13 +0100337 """
Azim Khan951a2c82018-06-29 03:47:08 +0100338 Sent by the target to start first test.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100339
Azim Khanb31aa442018-07-03 11:57:54 +0100340 :param _key: Event key
341 :param _value: Value. ignored
342 :param _timestamp: Timestamp ignored.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100343 :return:
344 """
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100345 self.run_next_test()
346
347 @event_callback("R")
Azim Khanb31aa442018-07-03 11:57:54 +0100348 def on_result(self, _key, value, _timestamp):
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100349 """
Azim Khan951a2c82018-06-29 03:47:08 +0100350 Handle result. Prints test start, finish required by Greentea
351 to detect test execution.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100352
Azim Khanb31aa442018-07-03 11:57:54 +0100353 :param _key: Event key
Azim Khanf0e42fb2017-08-02 14:47:13 +0100354 :param value: Value. ignored
Azim Khanb31aa442018-07-03 11:57:54 +0100355 :param _timestamp: Timestamp ignored.
Azim Khanf0e42fb2017-08-02 14:47:13 +0100356 :return:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100357 """
358 int_val = self.get_result(value)
Azim Khanb31aa442018-07-03 11:57:54 +0100359 name, _, _, _ = self.tests[self.test_index]
Azim Khan5e7f8df2017-05-31 20:33:39 +0100360 self.log('{{__testcase_start;%s}}' % name)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100361 self.log('{{__testcase_finish;%s;%d;%d}}' % (name, int_val == 0,
362 int_val != 0))
363 self.run_next_test()
364
365 @event_callback("F")
Azim Khanb31aa442018-07-03 11:57:54 +0100366 def on_failure(self, _key, value, _timestamp):
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100367 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100368 Handles test execution failure. That means dependency not supported or
369 Test function not supported. Hence marking test as skipped.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100370
Azim Khanb31aa442018-07-03 11:57:54 +0100371 :param _key: Event key
Azim Khanf0e42fb2017-08-02 14:47:13 +0100372 :param value: Value. ignored
Azim Khanb31aa442018-07-03 11:57:54 +0100373 :param _timestamp: Timestamp ignored.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100374 :return:
375 """
376 int_val = self.get_result(value)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100377 if int_val in self.error_str:
378 err = self.error_str[int_val]
379 else:
380 err = 'Unknown error'
381 # For skip status, do not write {{__testcase_finish;...}}
382 self.log("Error: %s" % err)
383 self.run_next_test()