blob: fa5b507062b9e2d42ce66903aac3af45bfb00e4e [file] [log] [blame]
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +01001"""
2mbed SDK
3Copyright (c) 2011-2013 ARM Limited
4
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
20import time
21from 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 """
65 line = file.readline().strip()
66 while line:
67 line = line.strip()
68 if len(line) == 0:
69 line = file.readline()
70 continue
71 # Read test name
72 name = line
73
74 # Check dependencies
75 deps = []
76 line = file.readline().strip()
77 m = re.search('depends_on\:(.*)', line)
78 if m:
79 deps = [int(x) for x in m.group(1).split(':')]
80 line = file.readline().strip()
81
82 # Read test vectors
83 line = line.replace('\\n', '\n#')
84 parts = self.__escaped_split(line, ':')
85 function = int(parts[0])
86 x = parts[1:]
87 l = len(x)
88 assert l % 2 == 0, "Number of test arguments should be even: %s" % line
89 args = [(x[i * 2], x[(i * 2) + 1]) for i in range(len(x)/2)]
90 self.tests.append((name, function, deps, args))
91 line = file.readline()
92
93 def get_test_data(self):
94 """
95 """
96 return self.tests
97
98
99class MbedTlsTest(BaseHostTest):
100 """
101 Host test for mbed-tls target tests.
102 """
103 # From suites/helpers.function
104 DEPENDENCY_SUPPORTED = 0
105 KEY_VALUE_MAPPING_FOUND = DEPENDENCY_SUPPORTED
106 DISPATCH_TEST_SUCCESS = DEPENDENCY_SUPPORTED
107
108 KEY_VALUE_MAPPING_NOT_FOUND = -1
109 DEPENDENCY_NOT_SUPPORTED = -2
110 DISPATCH_TEST_FN_NOT_FOUND = -3
111 DISPATCH_INVALID_TEST_DATA = -4
112 DISPATCH_UNSUPPORTED_SUITE = -5
113
114 def __init__(self):
115 """
116 """
117 super(MbedTlsTest, self).__init__()
118 self.tests = []
119 self.test_index = -1
120 self.dep_index = 0
121 self.error_str = dict()
122 self.error_str[self.DEPENDENCY_SUPPORTED] = 'DEPENDENCY_SUPPORTED'
123 self.error_str[self.KEY_VALUE_MAPPING_NOT_FOUND] = 'KEY_VALUE_MAPPING_NOT_FOUND'
124 self.error_str[self.DEPENDENCY_NOT_SUPPORTED] = 'DEPENDENCY_NOT_SUPPORTED'
125 self.error_str[self.DISPATCH_TEST_FN_NOT_FOUND] = 'DISPATCH_TEST_FN_NOT_FOUND'
126 self.error_str[self.DISPATCH_INVALID_TEST_DATA] = 'DISPATCH_INVALID_TEST_DATA'
127 self.error_str[self.DISPATCH_UNSUPPORTED_SUITE] = 'DISPATCH_UNSUPPORTED_SUITE'
128
129 def setup(self):
130 """
131 """
132 binary_path = self.get_config_item('image_path')
133 script_dir = os.path.split(os.path.abspath(__file__))[0]
134 suite_name = os.path.splitext(os.path.basename(binary_path))[0]
135 data_file = ".".join((suite_name, 'data'))
136 data_file = os.path.join(script_dir, '..', 'mbedtls', suite_name, data_file)
137 if os.path.exists(data_file):
138 self.log("Running tests from %s" % data_file)
139 parser = TestDataParser()
140 parser.parse(data_file)
141 self.tests = parser.get_test_data()
142 self.print_test_info()
143 else:
144 self.log("Data file not found: %s" % data_file)
145 self.notify_complete(False)
146
147 def print_test_info(self):
148 """
149 """
150 self.log('{{__testcase_count;%d}}' % len(self.tests))
151 for name, _, _, _ in self.tests:
152 self.log('{{__testcase_name;%s}}' % name)
153
154 @staticmethod
155 def align_32bit(b):
156 """
157 4 byte aligns byte array.
158
159 :return:
160 """
161 b += bytearray((4 - (len(b))) % 4)
162
Azim Khand59391a2017-06-01 14:04:17 +0100163 @staticmethod
164 def hex_str_bytes(hex_str):
165 """
166 Converts Hex string representation to byte array
167
168 :param hex_str:
169 :return:
170 """
171 assert hex_str[0] == '"' and hex_str[len(hex_str) - 1] == '"', \
172 "HEX test parameter missing '\"': %s" % hex_str
173 hex_str = hex_str.strip('"')
174 assert len(hex_str) % 2 == 0, "HEX parameter len should be mod of 2: %s" % hex_str
175 b = bytearray()
176
177 for i in xrange(len(hex_str) / 2):
178 h = hex_str[i * 2] + hex_str[(i * 2) + 1]
179 try:
180 b += bytearray([int(h, 16)])
181 except ValueError:
182 raise ValueError("Invalid HEX value: %s" % hex_str)
183 return b
184
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100185 def parameters_to_bytes(self, b, parameters):
186 for typ, param in parameters:
187 if typ == 'int' or typ == 'exp':
188 i = int(param)
189 b += 'I' if typ == 'int' else 'E'
190 self.align_32bit(b)
191 b += bytearray([((i >> x) & 0xff) for x in [24, 16, 8, 0]])
192 elif typ == 'char*':
193 param = param.strip('"')
194 i = len(param) + 1 # + 1 for null termination
195 b += 'S'
196 self.align_32bit(b)
197 b += bytearray([((i >> x) & 0xff) for x in [24, 16, 8, 0]])
198 b += bytearray(list(param))
199 b += '\0' # Null terminate
Azim Khand59391a2017-06-01 14:04:17 +0100200 elif typ == 'hex':
201 hb = self.hex_str_bytes(param)
202 b += 'H'
203 self.align_32bit(b)
204 i = len(hb)
205 b += bytearray([((i >> x) & 0xff) for x in [24, 16, 8, 0]])
206 b += hb
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100207 return b
208
209 def run_next_test(self):
210 """
211 Send next test function to the target.
212
213 """
214 self.test_index += 1
215 self.dep_index = 0
216 if self.test_index < len(self.tests):
217 name, function, deps, args = self.tests[self.test_index]
218 self.log("Running: %s" % name)
219 bytes = bytearray([len(deps)])
220 if len(deps):
221 bytes += bytearray(deps)
222 bytes += bytearray([function, len(args)])
223 self.parameters_to_bytes(bytes, args)
224 key = bytearray([((len(bytes) >> x) & 0xff) for x in [24, 16, 8, 0]])
225 #self.log("Bytes: " + " ".join(["%x '%c'" % (x, x) for x in bytes]))
226 self.send_kv(key, bytes)
227 else:
228 self.notify_complete(True)
229
230 @staticmethod
231 def get_result(value):
232 try:
233 return int(value)
234 except ValueError:
235 ValueError("Result should return error number. Instead received %s" % value)
236 return 0
237
238 @event_callback('GO')
239 def on_go(self, key, value, timestamp):
240 self.run_next_test()
241
242 @event_callback("R")
243 def on_result(self, key, value, timestamp):
244 """
245 Handle result.
246
247 """
248 int_val = self.get_result(value)
249 name, function, deps, args = self.tests[self.test_index]
Azim Khan5e7f8df2017-05-31 20:33:39 +0100250 self.log('{{__testcase_start;%s}}' % name)
Mohammad Azim Khan7a0d84f2017-04-01 03:18:20 +0100251 self.log('{{__testcase_finish;%s;%d;%d}}' % (name, int_val == 0,
252 int_val != 0))
253 self.run_next_test()
254
255 @event_callback("F")
256 def on_failure(self, key, value, timestamp):
257 """
258 Handles test execution failure. Hence marking test as skipped.
259
260 :param key:
261 :param value:
262 :param timestamp:
263 :return:
264 """
265 int_val = self.get_result(value)
266 name, function, deps, args = self.tests[self.test_index]
267 if int_val in self.error_str:
268 err = self.error_str[int_val]
269 else:
270 err = 'Unknown error'
271 # For skip status, do not write {{__testcase_finish;...}}
272 self.log("Error: %s" % err)
273 self.run_next_test()