blob: b43e613efe38777003b3b1429db3b66a8629b6c1 [file] [log] [blame]
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +01001"""
2mbed SDK
Azim Khan663d4702017-07-07 15:40:26 +01003Copyright (c) 2017 ARM Limited
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +01004
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16"""
17
18import re
19import os
Azim Khan663d4702017-07-07 15:40:26 +010020import binascii
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010021from mbed_host_tests import BaseHostTest, event_callback
22
23
24class TestDataParser(object):
25 """
26 parser for mbedtls test data files.
27 """
28
29 def __init__(self):
30 """
31 Constructor
32 """
33 self.tests = []
34
35 def parse(self, data_file):
36 """
37
38 """
39 with open(data_file, 'r') as f:
40 self.__parse(f)
41
42 @staticmethod
43 def __escaped_split(str, ch):
44 """
45 """
46 if len(ch) > 1:
47 raise ValueError('Expected split character. Found string!')
48 out = []
49 part = ''
50 escape = False
51 for i in range(len(str)):
52 if not escape and str[i] == ch:
53 out.append(part)
54 part = ''
55 else:
56 part += str[i]
57 escape = not escape and str[i] == '\\'
58 if len(part):
59 out.append(part)
60 return out
61
62 def __parse(self, file):
63 """
64 """
Azim Khan663d4702017-07-07 15:40:26 +010065 for line in file:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010066 line = line.strip()
67 if len(line) == 0:
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010068 continue
69 # Read test name
70 name = line
71
72 # Check dependencies
73 deps = []
Azim Khan663d4702017-07-07 15:40:26 +010074 line = file.next().strip()
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010075 m = re.search('depends_on\:(.*)', line)
76 if m:
77 deps = [int(x) for x in m.group(1).split(':')]
Azim Khan663d4702017-07-07 15:40:26 +010078 line = file.next().strip()
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010079
80 # Read test vectors
Azim Khan663d4702017-07-07 15:40:26 +010081 line = line.replace('\\n', '\n')
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010082 parts = self.__escaped_split(line, ':')
83 function = int(parts[0])
84 x = parts[1:]
85 l = len(x)
86 assert l % 2 == 0, "Number of test arguments should be even: %s" % line
87 args = [(x[i * 2], x[(i * 2) + 1]) for i in range(len(x)/2)]
88 self.tests.append((name, function, deps, args))
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +010089
90 def get_test_data(self):
91 """
92 """
93 return self.tests
94
95
96class MbedTlsTest(BaseHostTest):
97 """
Azim Khan663d4702017-07-07 15:40:26 +010098 Event handler for mbedtls unit tests. This script is loaded at run time
99 by htrun while executing mbedtls unit tests.
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100100 """
101 # From suites/helpers.function
102 DEPENDENCY_SUPPORTED = 0
103 KEY_VALUE_MAPPING_FOUND = DEPENDENCY_SUPPORTED
104 DISPATCH_TEST_SUCCESS = DEPENDENCY_SUPPORTED
105
106 KEY_VALUE_MAPPING_NOT_FOUND = -1
107 DEPENDENCY_NOT_SUPPORTED = -2
108 DISPATCH_TEST_FN_NOT_FOUND = -3
109 DISPATCH_INVALID_TEST_DATA = -4
110 DISPATCH_UNSUPPORTED_SUITE = -5
111
112 def __init__(self):
113 """
114 """
115 super(MbedTlsTest, self).__init__()
116 self.tests = []
117 self.test_index = -1
118 self.dep_index = 0
119 self.error_str = dict()
120 self.error_str[self.DEPENDENCY_SUPPORTED] = 'DEPENDENCY_SUPPORTED'
121 self.error_str[self.KEY_VALUE_MAPPING_NOT_FOUND] = 'KEY_VALUE_MAPPING_NOT_FOUND'
122 self.error_str[self.DEPENDENCY_NOT_SUPPORTED] = 'DEPENDENCY_NOT_SUPPORTED'
123 self.error_str[self.DISPATCH_TEST_FN_NOT_FOUND] = 'DISPATCH_TEST_FN_NOT_FOUND'
124 self.error_str[self.DISPATCH_INVALID_TEST_DATA] = 'DISPATCH_INVALID_TEST_DATA'
125 self.error_str[self.DISPATCH_UNSUPPORTED_SUITE] = 'DISPATCH_UNSUPPORTED_SUITE'
126
127 def setup(self):
128 """
129 """
130 binary_path = self.get_config_item('image_path')
131 script_dir = os.path.split(os.path.abspath(__file__))[0]
132 suite_name = os.path.splitext(os.path.basename(binary_path))[0]
133 data_file = ".".join((suite_name, 'data'))
134 data_file = os.path.join(script_dir, '..', 'mbedtls', suite_name, data_file)
135 if os.path.exists(data_file):
136 self.log("Running tests from %s" % data_file)
137 parser = TestDataParser()
138 parser.parse(data_file)
139 self.tests = parser.get_test_data()
140 self.print_test_info()
141 else:
142 self.log("Data file not found: %s" % data_file)
143 self.notify_complete(False)
144
145 def print_test_info(self):
146 """
147 """
148 self.log('{{__testcase_count;%d}}' % len(self.tests))
149 for name, _, _, _ in self.tests:
150 self.log('{{__testcase_name;%s}}' % name)
151
152 @staticmethod
153 def align_32bit(b):
154 """
155 4 byte aligns byte array.
156
157 :return:
158 """
159 b += bytearray((4 - (len(b))) % 4)
160
Azim Khand59391a2017-06-01 14:04:17 +0100161 @staticmethod
162 def hex_str_bytes(hex_str):
163 """
164 Converts Hex string representation to byte array
165
166 :param hex_str:
167 :return:
168 """
169 assert hex_str[0] == '"' and hex_str[len(hex_str) - 1] == '"', \
170 "HEX test parameter missing '\"': %s" % hex_str
171 hex_str = hex_str.strip('"')
172 assert len(hex_str) % 2 == 0, "HEX parameter len should be mod of 2: %s" % hex_str
Azim Khand59391a2017-06-01 14:04:17 +0100173
Azim Khan663d4702017-07-07 15:40:26 +0100174 b = binascii.unhexlify(hex_str)
Azim Khand59391a2017-06-01 14:04:17 +0100175 return b
176
Azim Khan663d4702017-07-07 15:40:26 +0100177 @staticmethod
178 def int32_to_bigendian_bytes(i):
179 """
180 Coverts i to bytearray in big endian format.
181
182 :param i:
183 :return:
184 """
185 b = bytearray([((i >> x) & 0xff) for x in [24, 16, 8, 0]])
186 return b
187
188 def test_vector_to_bytes(self, function_id, deps, parameters):
189 """
190 Converts test vector into a byte array that can be sent to the target.
191
192 :param function_id:
193 :param deps:
194 :param parameters:
195 :return:
196 """
197 b = bytearray([len(deps)])
198 if len(deps):
199 b += bytearray(deps)
200 b += bytearray([function_id, len(parameters)])
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100201 for typ, param in parameters:
202 if typ == 'int' or typ == 'exp':
203 i = int(param)
204 b += 'I' if typ == 'int' else 'E'
205 self.align_32bit(b)
Azim Khan663d4702017-07-07 15:40:26 +0100206 b += self.int32_to_bigendian_bytes(i)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100207 elif typ == 'char*':
208 param = param.strip('"')
209 i = len(param) + 1 # + 1 for null termination
210 b += 'S'
211 self.align_32bit(b)
Azim Khan663d4702017-07-07 15:40:26 +0100212 b += self.int32_to_bigendian_bytes(i)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100213 b += bytearray(list(param))
214 b += '\0' # Null terminate
Azim Khand59391a2017-06-01 14:04:17 +0100215 elif typ == 'hex':
216 hb = self.hex_str_bytes(param)
217 b += 'H'
218 self.align_32bit(b)
219 i = len(hb)
Azim Khan663d4702017-07-07 15:40:26 +0100220 b += self.int32_to_bigendian_bytes(i)
Azim Khand59391a2017-06-01 14:04:17 +0100221 b += hb
Azim Khan663d4702017-07-07 15:40:26 +0100222 length = self.int32_to_bigendian_bytes(len(b))
223 return b, length
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100224
225 def run_next_test(self):
226 """
227 Send next test function to the target.
228
229 """
230 self.test_index += 1
231 self.dep_index = 0
232 if self.test_index < len(self.tests):
Azim Khan663d4702017-07-07 15:40:26 +0100233 name, function_id, deps, args = self.tests[self.test_index]
234 self.run_test(name, function_id, deps, args)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100235 else:
236 self.notify_complete(True)
237
Azim Khan663d4702017-07-07 15:40:26 +0100238 def run_test(self, name, function_id, deps, args):
239 """
240 Runs the test.
241
242 :param name:
243 :param function_id:
244 :param deps:
245 :param args:
246 :return:
247 """
248 self.log("Running: %s" % name)
249
250 bytes, length = self.test_vector_to_bytes(function_id, deps, args)
251 self.send_kv(length, bytes)
252
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100253 @staticmethod
254 def get_result(value):
255 try:
256 return int(value)
257 except ValueError:
258 ValueError("Result should return error number. Instead received %s" % value)
259 return 0
260
261 @event_callback('GO')
262 def on_go(self, key, value, timestamp):
263 self.run_next_test()
264
265 @event_callback("R")
266 def on_result(self, key, value, timestamp):
267 """
268 Handle result.
269
270 """
271 int_val = self.get_result(value)
272 name, function, deps, args = self.tests[self.test_index]
Azim Khan5e7f8df2017-05-31 20:33:39 +0100273 self.log('{{__testcase_start;%s}}' % name)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100274 self.log('{{__testcase_finish;%s;%d;%d}}' % (name, int_val == 0,
275 int_val != 0))
276 self.run_next_test()
277
278 @event_callback("F")
279 def on_failure(self, key, value, timestamp):
280 """
281 Handles test execution failure. Hence marking test as skipped.
282
283 :param key:
284 :param value:
285 :param timestamp:
286 :return:
287 """
288 int_val = self.get_result(value)
289 name, function, deps, args = self.tests[self.test_index]
290 if int_val in self.error_str:
291 err = self.error_str[int_val]
292 else:
293 err = 'Unknown error'
294 # For skip status, do not write {{__testcase_finish;...}}
295 self.log("Error: %s" % err)
296 self.run_next_test()