blob: b8f8a37526bb003711f0bc4e1cb4512584c85226 [file] [log] [blame]
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +01001"""
Mohammad Azim Khan95402612017-07-19 10:15:54 +01002 Greentea host test script for on-target tests.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +01003
Mohammad Azim Khan95402612017-07-19 10:15:54 +01004 Copyright (C) 2006-2017, ARM Limited, All Rights Reserved
5 SPDX-License-Identifier: Apache-2.0
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +01006
Mohammad Azim Khan95402612017-07-19 10:15:54 +01007 Licensed under the Apache License, Version 2.0 (the "License"); you may
8 not use this file except in compliance with the License.
9 You may obtain a copy of the License at
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010010
Mohammad Azim Khan95402612017-07-19 10:15:54 +010011 http://www.apache.org/licenses/LICENSE-2.0
12
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18
19 This file is part of mbed TLS (https://tls.mbed.org)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010020"""
21
22import re
23import os
Azim Khan663d4702017-07-07 15:40:26 +010024import binascii
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010025from mbed_host_tests import BaseHostTest, event_callback
26
27
28class TestDataParser(object):
29 """
30 parser for mbedtls test data files.
31 """
32
33 def __init__(self):
34 """
35 Constructor
36 """
37 self.tests = []
38
39 def parse(self, data_file):
40 """
41
42 """
43 with open(data_file, 'r') as f:
44 self.__parse(f)
45
46 @staticmethod
47 def __escaped_split(str, ch):
48 """
49 """
50 if len(ch) > 1:
51 raise ValueError('Expected split character. Found string!')
52 out = []
53 part = ''
54 escape = False
55 for i in range(len(str)):
56 if not escape and str[i] == ch:
57 out.append(part)
58 part = ''
59 else:
60 part += str[i]
61 escape = not escape and str[i] == '\\'
62 if len(part):
63 out.append(part)
64 return out
65
66 def __parse(self, file):
67 """
68 """
Azim Khan663d4702017-07-07 15:40:26 +010069 for line in file:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010070 line = line.strip()
71 if len(line) == 0:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010072 continue
73 # Read test name
74 name = line
75
76 # Check dependencies
77 deps = []
Azim Khan663d4702017-07-07 15:40:26 +010078 line = file.next().strip()
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010079 m = re.search('depends_on\:(.*)', line)
80 if m:
81 deps = [int(x) for x in m.group(1).split(':')]
Azim Khan663d4702017-07-07 15:40:26 +010082 line = file.next().strip()
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010083
84 # Read test vectors
Azim Khan663d4702017-07-07 15:40:26 +010085 line = line.replace('\\n', '\n')
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010086 parts = self.__escaped_split(line, ':')
87 function = int(parts[0])
88 x = parts[1:]
89 l = len(x)
90 assert l % 2 == 0, "Number of test arguments should be even: %s" % line
91 args = [(x[i * 2], x[(i * 2) + 1]) for i in range(len(x)/2)]
92 self.tests.append((name, function, deps, args))
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010093
94 def get_test_data(self):
95 """
96 """
97 return self.tests
98
99
100class MbedTlsTest(BaseHostTest):
101 """
Azim Khan663d4702017-07-07 15:40:26 +0100102 Event handler for mbedtls unit tests. This script is loaded at run time
103 by htrun while executing mbedtls unit tests.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100104 """
105 # From suites/helpers.function
106 DEPENDENCY_SUPPORTED = 0
107 KEY_VALUE_MAPPING_FOUND = DEPENDENCY_SUPPORTED
108 DISPATCH_TEST_SUCCESS = DEPENDENCY_SUPPORTED
109
110 KEY_VALUE_MAPPING_NOT_FOUND = -1
111 DEPENDENCY_NOT_SUPPORTED = -2
112 DISPATCH_TEST_FN_NOT_FOUND = -3
113 DISPATCH_INVALID_TEST_DATA = -4
114 DISPATCH_UNSUPPORTED_SUITE = -5
115
116 def __init__(self):
117 """
118 """
119 super(MbedTlsTest, self).__init__()
120 self.tests = []
121 self.test_index = -1
122 self.dep_index = 0
123 self.error_str = dict()
124 self.error_str[self.DEPENDENCY_SUPPORTED] = 'DEPENDENCY_SUPPORTED'
125 self.error_str[self.KEY_VALUE_MAPPING_NOT_FOUND] = 'KEY_VALUE_MAPPING_NOT_FOUND'
126 self.error_str[self.DEPENDENCY_NOT_SUPPORTED] = 'DEPENDENCY_NOT_SUPPORTED'
127 self.error_str[self.DISPATCH_TEST_FN_NOT_FOUND] = 'DISPATCH_TEST_FN_NOT_FOUND'
128 self.error_str[self.DISPATCH_INVALID_TEST_DATA] = 'DISPATCH_INVALID_TEST_DATA'
129 self.error_str[self.DISPATCH_UNSUPPORTED_SUITE] = 'DISPATCH_UNSUPPORTED_SUITE'
130
131 def setup(self):
132 """
133 """
134 binary_path = self.get_config_item('image_path')
135 script_dir = os.path.split(os.path.abspath(__file__))[0]
136 suite_name = os.path.splitext(os.path.basename(binary_path))[0]
137 data_file = ".".join((suite_name, 'data'))
138 data_file = os.path.join(script_dir, '..', 'mbedtls', suite_name, data_file)
139 if os.path.exists(data_file):
140 self.log("Running tests from %s" % data_file)
141 parser = TestDataParser()
142 parser.parse(data_file)
143 self.tests = parser.get_test_data()
144 self.print_test_info()
145 else:
146 self.log("Data file not found: %s" % data_file)
147 self.notify_complete(False)
148
149 def print_test_info(self):
150 """
151 """
152 self.log('{{__testcase_count;%d}}' % len(self.tests))
153 for name, _, _, _ in self.tests:
154 self.log('{{__testcase_name;%s}}' % name)
155
156 @staticmethod
157 def align_32bit(b):
158 """
159 4 byte aligns byte array.
160
161 :return:
162 """
163 b += bytearray((4 - (len(b))) % 4)
164
Azim Khand59391a2017-06-01 14:04:17 +0100165 @staticmethod
166 def hex_str_bytes(hex_str):
167 """
168 Converts Hex string representation to byte array
169
170 :param hex_str:
171 :return:
172 """
173 assert hex_str[0] == '"' and hex_str[len(hex_str) - 1] == '"', \
174 "HEX test parameter missing '\"': %s" % hex_str
175 hex_str = hex_str.strip('"')
176 assert len(hex_str) % 2 == 0, "HEX parameter len should be mod of 2: %s" % hex_str
Azim Khand59391a2017-06-01 14:04:17 +0100177
Azim Khan663d4702017-07-07 15:40:26 +0100178 b = binascii.unhexlify(hex_str)
Azim Khand59391a2017-06-01 14:04:17 +0100179 return b
180
Azim Khan663d4702017-07-07 15:40:26 +0100181 @staticmethod
182 def int32_to_bigendian_bytes(i):
183 """
184 Coverts i to bytearray in big endian format.
185
186 :param i:
187 :return:
188 """
189 b = bytearray([((i >> x) & 0xff) for x in [24, 16, 8, 0]])
190 return b
191
192 def test_vector_to_bytes(self, function_id, deps, parameters):
193 """
194 Converts test vector into a byte array that can be sent to the target.
195
196 :param function_id:
197 :param deps:
198 :param parameters:
199 :return:
200 """
201 b = bytearray([len(deps)])
202 if len(deps):
203 b += bytearray(deps)
204 b += bytearray([function_id, len(parameters)])
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100205 for typ, param in parameters:
206 if typ == 'int' or typ == 'exp':
207 i = int(param)
208 b += 'I' if typ == 'int' else 'E'
209 self.align_32bit(b)
Azim Khan663d4702017-07-07 15:40:26 +0100210 b += self.int32_to_bigendian_bytes(i)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100211 elif typ == 'char*':
212 param = param.strip('"')
213 i = len(param) + 1 # + 1 for null termination
214 b += 'S'
215 self.align_32bit(b)
Azim Khan663d4702017-07-07 15:40:26 +0100216 b += self.int32_to_bigendian_bytes(i)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100217 b += bytearray(list(param))
218 b += '\0' # Null terminate
Azim Khand59391a2017-06-01 14:04:17 +0100219 elif typ == 'hex':
220 hb = self.hex_str_bytes(param)
221 b += 'H'
222 self.align_32bit(b)
223 i = len(hb)
Azim Khan663d4702017-07-07 15:40:26 +0100224 b += self.int32_to_bigendian_bytes(i)
Azim Khand59391a2017-06-01 14:04:17 +0100225 b += hb
Azim Khan663d4702017-07-07 15:40:26 +0100226 length = self.int32_to_bigendian_bytes(len(b))
227 return b, length
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100228
229 def run_next_test(self):
230 """
231 Send next test function to the target.
232
233 """
234 self.test_index += 1
235 self.dep_index = 0
236 if self.test_index < len(self.tests):
Azim Khan663d4702017-07-07 15:40:26 +0100237 name, function_id, deps, args = self.tests[self.test_index]
238 self.run_test(name, function_id, deps, args)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100239 else:
240 self.notify_complete(True)
241
Azim Khan663d4702017-07-07 15:40:26 +0100242 def run_test(self, name, function_id, deps, args):
243 """
244 Runs the test.
245
246 :param name:
247 :param function_id:
248 :param deps:
249 :param args:
250 :return:
251 """
252 self.log("Running: %s" % name)
253
254 bytes, length = self.test_vector_to_bytes(function_id, deps, args)
255 self.send_kv(length, bytes)
256
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100257 @staticmethod
258 def get_result(value):
259 try:
260 return int(value)
261 except ValueError:
262 ValueError("Result should return error number. Instead received %s" % value)
263 return 0
264
265 @event_callback('GO')
266 def on_go(self, key, value, timestamp):
267 self.run_next_test()
268
269 @event_callback("R")
270 def on_result(self, key, value, timestamp):
271 """
272 Handle result.
273
274 """
275 int_val = self.get_result(value)
276 name, function, deps, args = self.tests[self.test_index]
Azim Khan5e7f8df2017-05-31 20:33:39 +0100277 self.log('{{__testcase_start;%s}}' % name)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100278 self.log('{{__testcase_finish;%s;%d;%d}}' % (name, int_val == 0,
279 int_val != 0))
280 self.run_next_test()
281
282 @event_callback("F")
283 def on_failure(self, key, value, timestamp):
284 """
285 Handles test execution failure. Hence marking test as skipped.
286
287 :param key:
288 :param value:
289 :param timestamp:
290 :return:
291 """
292 int_val = self.get_result(value)
293 name, function, deps, args = self.tests[self.test_index]
294 if int_val in self.error_str:
295 err = self.error_str[int_val]
296 else:
297 err = 'Unknown error'
298 # For skip status, do not write {{__testcase_finish;...}}
299 self.log("Error: %s" % err)
300 self.run_next_test()