blob: 52fd0a9c285b2fc3911298c277ecbf35989823d4 [file] [log] [blame]
Azim Khanf0e42fb2017-08-02 14:47:13 +01001# Greentea host test script for on-target tests.
2#
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#
18# This file is part of mbed TLS (https://tls.mbed.org)
19
20
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010021"""
Azim Khanf0e42fb2017-08-02 14:47:13 +010022Greentea host test script for on-target tests.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010023
Azim Khanf0e42fb2017-08-02 14:47:13 +010024Host test script for testing mbed TLS test suites on target. Implements
25BaseHostTest to handle key,value pairs (events) coming from mbed TLS
26tests. Reads data file corresponding to the executing binary and dispatches
27test cases.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010028"""
29
Azim Khanf0e42fb2017-08-02 14:47:13 +010030
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010031import re
32import os
Azim Khan663d4702017-07-07 15:40:26 +010033import binascii
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010034from mbed_host_tests import BaseHostTest, event_callback
35
36
Azim Khanb98e6ee2018-06-28 17:11:33 +010037class TestDataParserError(Exception):
38 """Indicates error in test data, read from .data file."""
39 pass
40
41
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010042class TestDataParser(object):
43 """
44 parser for mbedtls test data files.
45 """
46
47 def __init__(self):
48 """
49 Constructor
50 """
51 self.tests = []
52
53 def parse(self, data_file):
54 """
Azim Khanf0e42fb2017-08-02 14:47:13 +010055 Data file parser.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010056
Azim Khanf0e42fb2017-08-02 14:47:13 +010057 :param data_file: Data file path
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010058 """
59 with open(data_file, 'r') as f:
60 self.__parse(f)
61
62 @staticmethod
63 def __escaped_split(str, ch):
64 """
Azim Khanf0e42fb2017-08-02 14:47:13 +010065 Splits str on ch except when escaped.
66
67 :param str: String to split
68 :param ch: Split character
69 :return: List of splits
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010070 """
71 if len(ch) > 1:
72 raise ValueError('Expected split character. Found string!')
73 out = []
74 part = ''
75 escape = False
76 for i in range(len(str)):
77 if not escape and str[i] == ch:
78 out.append(part)
79 part = ''
80 else:
81 part += str[i]
82 escape = not escape and str[i] == '\\'
83 if len(part):
84 out.append(part)
85 return out
86
87 def __parse(self, file):
88 """
Azim Khanf0e42fb2017-08-02 14:47:13 +010089 Parses data file using supplied file object.
90
91 :param file: Data file object
92 :return:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010093 """
Azim Khan663d4702017-07-07 15:40:26 +010094 for line in file:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010095 line = line.strip()
96 if len(line) == 0:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010097 continue
98 # Read test name
99 name = line
100
101 # Check dependencies
102 deps = []
Azim Khan663d4702017-07-07 15:40:26 +0100103 line = file.next().strip()
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100104 m = re.search('depends_on\:(.*)', line)
105 if m:
106 deps = [int(x) for x in m.group(1).split(':')]
Azim Khan663d4702017-07-07 15:40:26 +0100107 line = file.next().strip()
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100108
109 # Read test vectors
Azim Khan663d4702017-07-07 15:40:26 +0100110 line = line.replace('\\n', '\n')
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100111 parts = self.__escaped_split(line, ':')
112 function = int(parts[0])
113 x = parts[1:]
114 l = len(x)
Azim Khanb98e6ee2018-06-28 17:11:33 +0100115 if l % 2 != 0:
116 raise TestDataParserError("Number of test arguments should "
117 "be even: %s" % line)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100118 args = [(x[i * 2], x[(i * 2) + 1]) for i in range(len(x)/2)]
119 self.tests.append((name, function, deps, 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 Khan663d4702017-07-07 15:40:26 +0100130 Event handler for mbedtls unit tests. This script is loaded at run time
131 by htrun while executing mbedtls unit tests.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100132 """
133 # From suites/helpers.function
134 DEPENDENCY_SUPPORTED = 0
135 KEY_VALUE_MAPPING_FOUND = DEPENDENCY_SUPPORTED
136 DISPATCH_TEST_SUCCESS = DEPENDENCY_SUPPORTED
137
138 KEY_VALUE_MAPPING_NOT_FOUND = -1
139 DEPENDENCY_NOT_SUPPORTED = -2
140 DISPATCH_TEST_FN_NOT_FOUND = -3
141 DISPATCH_INVALID_TEST_DATA = -4
142 DISPATCH_UNSUPPORTED_SUITE = -5
143
144 def __init__(self):
145 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100146 Constructor initialises test index to 0.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100147 """
148 super(MbedTlsTest, self).__init__()
149 self.tests = []
150 self.test_index = -1
151 self.dep_index = 0
152 self.error_str = dict()
153 self.error_str[self.DEPENDENCY_SUPPORTED] = 'DEPENDENCY_SUPPORTED'
154 self.error_str[self.KEY_VALUE_MAPPING_NOT_FOUND] = 'KEY_VALUE_MAPPING_NOT_FOUND'
155 self.error_str[self.DEPENDENCY_NOT_SUPPORTED] = 'DEPENDENCY_NOT_SUPPORTED'
156 self.error_str[self.DISPATCH_TEST_FN_NOT_FOUND] = 'DISPATCH_TEST_FN_NOT_FOUND'
157 self.error_str[self.DISPATCH_INVALID_TEST_DATA] = 'DISPATCH_INVALID_TEST_DATA'
158 self.error_str[self.DISPATCH_UNSUPPORTED_SUITE] = 'DISPATCH_UNSUPPORTED_SUITE'
159
160 def setup(self):
161 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100162 Setup hook implementation. Reads test suite data file and parses out tests.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100163 """
164 binary_path = self.get_config_item('image_path')
165 script_dir = os.path.split(os.path.abspath(__file__))[0]
166 suite_name = os.path.splitext(os.path.basename(binary_path))[0]
167 data_file = ".".join((suite_name, 'data'))
168 data_file = os.path.join(script_dir, '..', 'mbedtls', suite_name, data_file)
169 if os.path.exists(data_file):
170 self.log("Running tests from %s" % data_file)
171 parser = TestDataParser()
172 parser.parse(data_file)
173 self.tests = parser.get_test_data()
174 self.print_test_info()
175 else:
176 self.log("Data file not found: %s" % data_file)
177 self.notify_complete(False)
178
179 def print_test_info(self):
180 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100181 Prints test summary read by Greentea to detect test cases.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100182 """
183 self.log('{{__testcase_count;%d}}' % len(self.tests))
184 for name, _, _, _ in self.tests:
185 self.log('{{__testcase_name;%s}}' % name)
186
187 @staticmethod
188 def align_32bit(b):
189 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100190 4 byte aligns input byte array.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100191
192 :return:
193 """
194 b += bytearray((4 - (len(b))) % 4)
195
Azim Khand59391a2017-06-01 14:04:17 +0100196 @staticmethod
197 def hex_str_bytes(hex_str):
198 """
199 Converts Hex string representation to byte array
200
Azim Khanf0e42fb2017-08-02 14:47:13 +0100201 :param hex_str: Hex in string format.
202 :return: Output Byte array
Azim Khand59391a2017-06-01 14:04:17 +0100203 """
Azim Khanb98e6ee2018-06-28 17:11:33 +0100204 if hex_str[0] != '"' or hex_str[len(hex_str) - 1] != '"':
205 raise TestDataParserError("HEX test parameter missing '\"':"
206 " %s" % hex_str)
Azim Khand59391a2017-06-01 14:04:17 +0100207 hex_str = hex_str.strip('"')
Azim Khanb98e6ee2018-06-28 17:11:33 +0100208 if len(hex_str) % 2 != 0:
209 raise TestDataParserError("HEX parameter len should be mod of "
210 "2: %s" % hex_str)
Azim Khand59391a2017-06-01 14:04:17 +0100211
Azim Khan663d4702017-07-07 15:40:26 +0100212 b = binascii.unhexlify(hex_str)
Azim Khand59391a2017-06-01 14:04:17 +0100213 return b
214
Azim Khan663d4702017-07-07 15:40:26 +0100215 @staticmethod
216 def int32_to_bigendian_bytes(i):
217 """
218 Coverts i to bytearray in big endian format.
219
Azim Khanf0e42fb2017-08-02 14:47:13 +0100220 :param i: Input integer
221 :return: Output bytes array in big endian or network order
Azim Khan663d4702017-07-07 15:40:26 +0100222 """
223 b = bytearray([((i >> x) & 0xff) for x in [24, 16, 8, 0]])
224 return b
225
226 def test_vector_to_bytes(self, function_id, deps, parameters):
227 """
228 Converts test vector into a byte array that can be sent to the target.
229
Azim Khanf0e42fb2017-08-02 14:47:13 +0100230 :param function_id: Test Function Identifier
231 :param deps: Dependency list
232 :param parameters: Test function input parameters
233 :return: Byte array and its length
Azim Khan663d4702017-07-07 15:40:26 +0100234 """
235 b = bytearray([len(deps)])
236 if len(deps):
237 b += bytearray(deps)
238 b += bytearray([function_id, len(parameters)])
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100239 for typ, param in parameters:
240 if typ == 'int' or typ == 'exp':
241 i = int(param)
242 b += 'I' if typ == 'int' else 'E'
243 self.align_32bit(b)
Azim Khan663d4702017-07-07 15:40:26 +0100244 b += self.int32_to_bigendian_bytes(i)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100245 elif typ == 'char*':
246 param = param.strip('"')
247 i = len(param) + 1 # + 1 for null termination
248 b += 'S'
249 self.align_32bit(b)
Azim Khan663d4702017-07-07 15:40:26 +0100250 b += self.int32_to_bigendian_bytes(i)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100251 b += bytearray(list(param))
252 b += '\0' # Null terminate
Azim Khand59391a2017-06-01 14:04:17 +0100253 elif typ == 'hex':
254 hb = self.hex_str_bytes(param)
255 b += 'H'
256 self.align_32bit(b)
257 i = len(hb)
Azim Khan663d4702017-07-07 15:40:26 +0100258 b += self.int32_to_bigendian_bytes(i)
Azim Khand59391a2017-06-01 14:04:17 +0100259 b += hb
Azim Khan663d4702017-07-07 15:40:26 +0100260 length = self.int32_to_bigendian_bytes(len(b))
261 return b, length
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100262
263 def run_next_test(self):
264 """
265 Send next test function to the target.
266
267 """
268 self.test_index += 1
269 self.dep_index = 0
270 if self.test_index < len(self.tests):
Azim Khan663d4702017-07-07 15:40:26 +0100271 name, function_id, deps, args = self.tests[self.test_index]
272 self.run_test(name, function_id, deps, args)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100273 else:
274 self.notify_complete(True)
275
Azim Khan663d4702017-07-07 15:40:26 +0100276 def run_test(self, name, function_id, deps, args):
277 """
278 Runs the test.
279
Azim Khanf0e42fb2017-08-02 14:47:13 +0100280 :param name: Test name
281 :param function_id: function identifier
282 :param deps: Dependencies list
283 :param args: test parameters
Azim Khan663d4702017-07-07 15:40:26 +0100284 :return:
285 """
286 self.log("Running: %s" % name)
287
288 bytes, length = self.test_vector_to_bytes(function_id, deps, args)
289 self.send_kv(length, bytes)
290
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100291 @staticmethod
292 def get_result(value):
Azim Khanf0e42fb2017-08-02 14:47:13 +0100293 """
294 Converts result from string type to integer
295 :param value: Result code in string
296 :return: Integer result code
297 """
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100298 try:
299 return int(value)
300 except ValueError:
301 ValueError("Result should return error number. Instead received %s" % value)
302 return 0
303
304 @event_callback('GO')
305 def on_go(self, key, value, timestamp):
Azim Khanf0e42fb2017-08-02 14:47:13 +0100306 """
307 Called on key "GO". Kicks off test execution.
308
309 :param key: Event key
310 :param value: Value. ignored
311 :param timestamp: Timestamp ignored.
312 :return:
313 """
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100314 self.run_next_test()
315
316 @event_callback("R")
317 def on_result(self, key, value, timestamp):
318 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100319 Handle result. Prints test start, finish prints required by Greentea to detect test execution.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100320
Azim Khanf0e42fb2017-08-02 14:47:13 +0100321 :param key: Event key
322 :param value: Value. ignored
323 :param timestamp: Timestamp ignored.
324 :return:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100325 """
326 int_val = self.get_result(value)
327 name, function, deps, args = self.tests[self.test_index]
Azim Khan5e7f8df2017-05-31 20:33:39 +0100328 self.log('{{__testcase_start;%s}}' % name)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100329 self.log('{{__testcase_finish;%s;%d;%d}}' % (name, int_val == 0,
330 int_val != 0))
331 self.run_next_test()
332
333 @event_callback("F")
334 def on_failure(self, key, value, timestamp):
335 """
Azim Khanf0e42fb2017-08-02 14:47:13 +0100336 Handles test execution failure. That means dependency not supported or
337 Test function not supported. Hence marking test as skipped.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100338
Azim Khanf0e42fb2017-08-02 14:47:13 +0100339 :param key: Event key
340 :param value: Value. ignored
341 :param timestamp: Timestamp ignored.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100342 :return:
343 """
344 int_val = self.get_result(value)
345 name, function, deps, args = self.tests[self.test_index]
346 if int_val in self.error_str:
347 err = self.error_str[int_val]
348 else:
349 err = 'Unknown error'
350 # For skip status, do not write {{__testcase_finish;...}}
351 self.log("Error: %s" % err)
352 self.run_next_test()