1#!/usr/bin/env python3
2# Unit test for generate_test_code.py
3#
4# Copyright The Mbed TLS Contributors
5# SPDX-License-Identifier: Apache-2.0
6#
7# 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
10#
11# 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"""
20Unit tests for generate_test_code.py
21"""
22
23from io import StringIO
24from unittest import TestCase, main as unittest_main
25from unittest.mock import patch
26
27from generate_test_code import gen_dependencies, gen_dependencies_one_line
28from generate_test_code import gen_function_wrapper, gen_dispatch
29from generate_test_code import parse_until_pattern, GeneratorInputError
30from generate_test_code import parse_suite_dependencies
31from generate_test_code import parse_function_dependencies
32from generate_test_code import parse_function_arguments, parse_function_code
33from generate_test_code import parse_functions, END_HEADER_REGEX
34from generate_test_code import END_SUITE_HELPERS_REGEX, escaped_split
35from generate_test_code import parse_test_data, gen_dep_check
36from generate_test_code import gen_expression_check, write_dependencies
37from generate_test_code import write_parameters, gen_suite_dep_checks
38from generate_test_code import gen_from_test_data
39
40
41class GenDep(TestCase):
42    """
43    Test suite for function gen_dep()
44    """
45
46    def test_dependencies_list(self):
47        """
48        Test that gen_dep() correctly creates dependencies for given
49        dependency list.
50        :return:
51        """
52        dependencies = ['DEP1', 'DEP2']
53        dep_start, dep_end = gen_dependencies(dependencies)
54        preprocessor1, preprocessor2 = dep_start.splitlines()
55        endif1, endif2 = dep_end.splitlines()
56        self.assertEqual(preprocessor1, '#if defined(DEP1)',
57                         'Preprocessor generated incorrectly')
58        self.assertEqual(preprocessor2, '#if defined(DEP2)',
59                         'Preprocessor generated incorrectly')
60        self.assertEqual(endif1, '#endif /* DEP2 */',
61                         'Preprocessor generated incorrectly')
62        self.assertEqual(endif2, '#endif /* DEP1 */',
63                         'Preprocessor generated incorrectly')
64
65    def test_disabled_dependencies_list(self):
66        """
67        Test that gen_dep() correctly creates dependencies for given
68        dependency list.
69        :return:
70        """
71        dependencies = ['!DEP1', '!DEP2']
72        dep_start, dep_end = gen_dependencies(dependencies)
73        preprocessor1, preprocessor2 = dep_start.splitlines()
74        endif1, endif2 = dep_end.splitlines()
75        self.assertEqual(preprocessor1, '#if !defined(DEP1)',
76                         'Preprocessor generated incorrectly')
77        self.assertEqual(preprocessor2, '#if !defined(DEP2)',
78                         'Preprocessor generated incorrectly')
79        self.assertEqual(endif1, '#endif /* !DEP2 */',
80                         'Preprocessor generated incorrectly')
81        self.assertEqual(endif2, '#endif /* !DEP1 */',
82                         'Preprocessor generated incorrectly')
83
84    def test_mixed_dependencies_list(self):
85        """
86        Test that gen_dep() correctly creates dependencies for given
87        dependency list.
88        :return:
89        """
90        dependencies = ['!DEP1', 'DEP2']
91        dep_start, dep_end = gen_dependencies(dependencies)
92        preprocessor1, preprocessor2 = dep_start.splitlines()
93        endif1, endif2 = dep_end.splitlines()
94        self.assertEqual(preprocessor1, '#if !defined(DEP1)',
95                         'Preprocessor generated incorrectly')
96        self.assertEqual(preprocessor2, '#if defined(DEP2)',
97                         'Preprocessor generated incorrectly')
98        self.assertEqual(endif1, '#endif /* DEP2 */',
99                         'Preprocessor generated incorrectly')
100        self.assertEqual(endif2, '#endif /* !DEP1 */',
101                         'Preprocessor generated incorrectly')
102
103    def test_empty_dependencies_list(self):
104        """
105        Test that gen_dep() correctly creates dependencies for given
106        dependency list.
107        :return:
108        """
109        dependencies = []
110        dep_start, dep_end = gen_dependencies(dependencies)
111        self.assertEqual(dep_start, '', 'Preprocessor generated incorrectly')
112        self.assertEqual(dep_end, '', 'Preprocessor generated incorrectly')
113
114    def test_large_dependencies_list(self):
115        """
116        Test that gen_dep() correctly creates dependencies for given
117        dependency list.
118        :return:
119        """
120        dependencies = []
121        count = 10
122        for i in range(count):
123            dependencies.append('DEP%d' % i)
124        dep_start, dep_end = gen_dependencies(dependencies)
125        self.assertEqual(len(dep_start.splitlines()), count,
126                         'Preprocessor generated incorrectly')
127        self.assertEqual(len(dep_end.splitlines()), count,
128                         'Preprocessor generated incorrectly')
129
130
131class GenDepOneLine(TestCase):
132    """
133    Test Suite for testing gen_dependencies_one_line()
134    """
135
136    def test_dependencies_list(self):
137        """
138        Test that gen_dep() correctly creates dependencies for given
139        dependency list.
140        :return:
141        """
142        dependencies = ['DEP1', 'DEP2']
143        dep_str = gen_dependencies_one_line(dependencies)
144        self.assertEqual(dep_str, '#if defined(DEP1) && defined(DEP2)',
145                         'Preprocessor generated incorrectly')
146
147    def test_disabled_dependencies_list(self):
148        """
149        Test that gen_dep() correctly creates dependencies for given
150        dependency list.
151        :return:
152        """
153        dependencies = ['!DEP1', '!DEP2']
154        dep_str = gen_dependencies_one_line(dependencies)
155        self.assertEqual(dep_str, '#if !defined(DEP1) && !defined(DEP2)',
156                         'Preprocessor generated incorrectly')
157
158    def test_mixed_dependencies_list(self):
159        """
160        Test that gen_dep() correctly creates dependencies for given
161        dependency list.
162        :return:
163        """
164        dependencies = ['!DEP1', 'DEP2']
165        dep_str = gen_dependencies_one_line(dependencies)
166        self.assertEqual(dep_str, '#if !defined(DEP1) && defined(DEP2)',
167                         'Preprocessor generated incorrectly')
168
169    def test_empty_dependencies_list(self):
170        """
171        Test that gen_dep() correctly creates dependencies for given
172        dependency list.
173        :return:
174        """
175        dependencies = []
176        dep_str = gen_dependencies_one_line(dependencies)
177        self.assertEqual(dep_str, '', 'Preprocessor generated incorrectly')
178
179    def test_large_dependencies_list(self):
180        """
181        Test that gen_dep() correctly creates dependencies for given
182        dependency list.
183        :return:
184        """
185        dependencies = []
186        count = 10
187        for i in range(count):
188            dependencies.append('DEP%d' % i)
189        dep_str = gen_dependencies_one_line(dependencies)
190        expected = '#if ' + ' && '.join(['defined(%s)' %
191                                         x for x in dependencies])
192        self.assertEqual(dep_str, expected,
193                         'Preprocessor generated incorrectly')
194
195
196class GenFunctionWrapper(TestCase):
197    """
198    Test Suite for testing gen_function_wrapper()
199    """
200
201    def test_params_unpack(self):
202        """
203        Test that params are properly unpacked in the function call.
204
205        :return:
206        """
207        code = gen_function_wrapper('test_a', '', ('a', 'b', 'c', 'd'))
208        expected = '''
209void test_a_wrapper( void ** params )
210{
211
212    test_a( a, b, c, d );
213}
214'''
215        self.assertEqual(code, expected)
216
217    def test_local(self):
218        """
219        Test that params are properly unpacked in the function call.
220
221        :return:
222        """
223        code = gen_function_wrapper('test_a',
224                                    'int x = 1;', ('x', 'b', 'c', 'd'))
225        expected = '''
226void test_a_wrapper( void ** params )
227{
228int x = 1;
229    test_a( x, b, c, d );
230}
231'''
232        self.assertEqual(code, expected)
233
234    def test_empty_params(self):
235        """
236        Test that params are properly unpacked in the function call.
237
238        :return:
239        """
240        code = gen_function_wrapper('test_a', '', ())
241        expected = '''
242void test_a_wrapper( void ** params )
243{
244    (void)params;
245
246    test_a(  );
247}
248'''
249        self.assertEqual(code, expected)
250
251
252class GenDispatch(TestCase):
253    """
254    Test suite for testing gen_dispatch()
255    """
256
257    def test_dispatch(self):
258        """
259        Test that dispatch table entry is generated correctly.
260        :return:
261        """
262        code = gen_dispatch('test_a', ['DEP1', 'DEP2'])
263        expected = '''
264#if defined(DEP1) && defined(DEP2)
265    test_a_wrapper,
266#else
267    NULL,
268#endif
269'''
270        self.assertEqual(code, expected)
271
272    def test_empty_dependencies(self):
273        """
274        Test empty dependency list.
275        :return:
276        """
277        code = gen_dispatch('test_a', [])
278        expected = '''
279    test_a_wrapper,
280'''
281        self.assertEqual(code, expected)
282
283
284class StringIOWrapper(StringIO):
285    """
286    file like class to mock file object in tests.
287    """
288    def __init__(self, file_name, data, line_no=0):
289        """
290        Init file handle.
291
292        :param file_name:
293        :param data:
294        :param line_no:
295        """
296        super(StringIOWrapper, self).__init__(data)
297        self.line_no = line_no
298        self.name = file_name
299
300    def next(self):
301        """
302        Iterator method. This method overrides base class's
303        next method and extends the next method to count the line
304        numbers as each line is read.
305
306        :return: Line read from file.
307        """
308        parent = super(StringIOWrapper, self)
309        line = parent.__next__()
310        return line
311
312    def readline(self, _length=0):
313        """
314        Wrap the base class readline.
315
316        :param length:
317        :return:
318        """
319        line = super(StringIOWrapper, self).readline()
320        if line is not None:
321            self.line_no += 1
322        return line
323
324
325class ParseUntilPattern(TestCase):
326    """
327    Test Suite for testing parse_until_pattern().
328    """
329
330    def test_suite_headers(self):
331        """
332        Test that suite headers are parsed correctly.
333
334        :return:
335        """
336        data = '''#include "mbedtls/ecp.h"
337
338#define ECP_PF_UNKNOWN     -1
339/* END_HEADER */
340'''
341        expected = '''#line 1 "test_suite_ut.function"
342#include "mbedtls/ecp.h"
343
344#define ECP_PF_UNKNOWN     -1
345'''
346        stream = StringIOWrapper('test_suite_ut.function', data, line_no=0)
347        headers = parse_until_pattern(stream, END_HEADER_REGEX)
348        self.assertEqual(headers, expected)
349
350    def test_line_no(self):
351        """
352        Test that #line is set to correct line no. in source .function file.
353
354        :return:
355        """
356        data = '''#include "mbedtls/ecp.h"
357
358#define ECP_PF_UNKNOWN     -1
359/* END_HEADER */
360'''
361        offset_line_no = 5
362        expected = '''#line %d "test_suite_ut.function"
363#include "mbedtls/ecp.h"
364
365#define ECP_PF_UNKNOWN     -1
366''' % (offset_line_no + 1)
367        stream = StringIOWrapper('test_suite_ut.function', data,
368                                 offset_line_no)
369        headers = parse_until_pattern(stream, END_HEADER_REGEX)
370        self.assertEqual(headers, expected)
371
372    def test_no_end_header_comment(self):
373        """
374        Test that InvalidFileFormat is raised when end header comment is
375        missing.
376        :return:
377        """
378        data = '''#include "mbedtls/ecp.h"
379
380#define ECP_PF_UNKNOWN     -1
381
382'''
383        stream = StringIOWrapper('test_suite_ut.function', data)
384        self.assertRaises(GeneratorInputError, parse_until_pattern, stream,
385                          END_HEADER_REGEX)
386
387
388class ParseSuiteDependencies(TestCase):
389    """
390    Test Suite for testing parse_suite_dependencies().
391    """
392
393    def test_suite_dependencies(self):
394        """
395
396        :return:
397        """
398        data = '''
399 * depends_on:MBEDTLS_ECP_C
400 * END_DEPENDENCIES
401 */
402'''
403        expected = ['MBEDTLS_ECP_C']
404        stream = StringIOWrapper('test_suite_ut.function', data)
405        dependencies = parse_suite_dependencies(stream)
406        self.assertEqual(dependencies, expected)
407
408    def test_no_end_dep_comment(self):
409        """
410        Test that InvalidFileFormat is raised when end dep comment is missing.
411        :return:
412        """
413        data = '''
414* depends_on:MBEDTLS_ECP_C
415'''
416        stream = StringIOWrapper('test_suite_ut.function', data)
417        self.assertRaises(GeneratorInputError, parse_suite_dependencies,
418                          stream)
419
420    def test_dependencies_split(self):
421        """
422        Test that InvalidFileFormat is raised when end dep comment is missing.
423        :return:
424        """
425        data = '''
426 * depends_on:MBEDTLS_ECP_C:A:B:   C  : D :F : G: !H
427 * END_DEPENDENCIES
428 */
429'''
430        expected = ['MBEDTLS_ECP_C', 'A', 'B', 'C', 'D', 'F', 'G', '!H']
431        stream = StringIOWrapper('test_suite_ut.function', data)
432        dependencies = parse_suite_dependencies(stream)
433        self.assertEqual(dependencies, expected)
434
435
436class ParseFuncDependencies(TestCase):
437    """
438    Test Suite for testing parse_function_dependencies()
439    """
440
441    def test_function_dependencies(self):
442        """
443        Test that parse_function_dependencies() correctly parses function
444        dependencies.
445        :return:
446        """
447        line = '/* BEGIN_CASE ' \
448               'depends_on:MBEDTLS_ENTROPY_NV_SEED:MBEDTLS_FS_IO */'
449        expected = ['MBEDTLS_ENTROPY_NV_SEED', 'MBEDTLS_FS_IO']
450        dependencies = parse_function_dependencies(line)
451        self.assertEqual(dependencies, expected)
452
453    def test_no_dependencies(self):
454        """
455        Test that parse_function_dependencies() correctly parses function
456        dependencies.
457        :return:
458        """
459        line = '/* BEGIN_CASE */'
460        dependencies = parse_function_dependencies(line)
461        self.assertEqual(dependencies, [])
462
463    def test_tolerance(self):
464        """
465        Test that parse_function_dependencies() correctly parses function
466        dependencies.
467        :return:
468        """
469        line = '/* BEGIN_CASE depends_on:MBEDTLS_FS_IO: A : !B:C : F*/'
470        dependencies = parse_function_dependencies(line)
471        self.assertEqual(dependencies, ['MBEDTLS_FS_IO', 'A', '!B', 'C', 'F'])
472
473
474class ParseFuncSignature(TestCase):
475    """
476    Test Suite for parse_function_arguments().
477    """
478
479    def test_int_and_char_params(self):
480        """
481        Test int and char parameters parsing
482        :return:
483        """
484        line = 'void entropy_threshold( char * a, int b, int result )'
485        args, local, arg_dispatch = parse_function_arguments(line)
486        self.assertEqual(args, ['char*', 'int', 'int'])
487        self.assertEqual(local, '')
488        self.assertEqual(arg_dispatch, ['(char *) params[0]',
489                                        '*( (int *) params[1] )',
490                                        '*( (int *) params[2] )'])
491
492    def test_hex_params(self):
493        """
494        Test hex parameters parsing
495        :return:
496        """
497        line = 'void entropy_threshold( char * a, data_t * h, int result )'
498        args, local, arg_dispatch = parse_function_arguments(line)
499        self.assertEqual(args, ['char*', 'hex', 'int'])
500        self.assertEqual(local,
501                         '    data_t data1 = {(uint8_t *) params[1], '
502                         '*( (uint32_t *) params[2] )};\n')
503        self.assertEqual(arg_dispatch, ['(char *) params[0]',
504                                        '&data1',
505                                        '*( (int *) params[3] )'])
506
507    def test_unsupported_arg(self):
508        """
509        Test unsupported arguments (not among int, char * and data_t)
510        :return:
511        """
512        line = 'void entropy_threshold( char * a, data_t * h, char result )'
513        self.assertRaises(ValueError, parse_function_arguments, line)
514
515    def test_no_params(self):
516        """
517        Test no parameters.
518        :return:
519        """
520        line = 'void entropy_threshold()'
521        args, local, arg_dispatch = parse_function_arguments(line)
522        self.assertEqual(args, [])
523        self.assertEqual(local, '')
524        self.assertEqual(arg_dispatch, [])
525
526
527class ParseFunctionCode(TestCase):
528    """
529    Test suite for testing parse_function_code()
530    """
531
532    def test_no_function(self):
533        """
534        Test no test function found.
535        :return:
536        """
537        data = '''
538No
539test
540function
541'''
542        stream = StringIOWrapper('test_suite_ut.function', data)
543        err_msg = 'file: test_suite_ut.function - Test functions not found!'
544        self.assertRaisesRegex(GeneratorInputError, err_msg,
545                               parse_function_code, stream, [], [])
546
547    def test_no_end_case_comment(self):
548        """
549        Test missing end case.
550        :return:
551        """
552        data = '''
553void test_func()
554{
555}
556'''
557        stream = StringIOWrapper('test_suite_ut.function', data)
558        err_msg = r'file: test_suite_ut.function - '\
559                  'end case pattern .*? not found!'
560        self.assertRaisesRegex(GeneratorInputError, err_msg,
561                               parse_function_code, stream, [], [])
562
563    @patch("generate_test_code.parse_function_arguments")
564    def test_function_called(self,
565                             parse_function_arguments_mock):
566        """
567        Test parse_function_code()
568        :return:
569        """
570        parse_function_arguments_mock.return_value = ([], '', [])
571        data = '''
572void test_func()
573{
574}
575'''
576        stream = StringIOWrapper('test_suite_ut.function', data)
577        self.assertRaises(GeneratorInputError, parse_function_code,
578                          stream, [], [])
579        self.assertTrue(parse_function_arguments_mock.called)
580        parse_function_arguments_mock.assert_called_with('void test_func()\n')
581
582    @patch("generate_test_code.gen_dispatch")
583    @patch("generate_test_code.gen_dependencies")
584    @patch("generate_test_code.gen_function_wrapper")
585    @patch("generate_test_code.parse_function_arguments")
586    def test_return(self, parse_function_arguments_mock,
587                    gen_function_wrapper_mock,
588                    gen_dependencies_mock,
589                    gen_dispatch_mock):
590        """
591        Test generated code.
592        :return:
593        """
594        parse_function_arguments_mock.return_value = ([], '', [])
595        gen_function_wrapper_mock.return_value = ''
596        gen_dependencies_mock.side_effect = gen_dependencies
597        gen_dispatch_mock.side_effect = gen_dispatch
598        data = '''
599void func()
600{
601    ba ba black sheep
602    have you any wool
603}
604/* END_CASE */
605'''
606        stream = StringIOWrapper('test_suite_ut.function', data)
607        name, arg, code, dispatch_code = parse_function_code(stream, [], [])
608
609        self.assertTrue(parse_function_arguments_mock.called)
610        parse_function_arguments_mock.assert_called_with('void func()\n')
611        gen_function_wrapper_mock.assert_called_with('test_func', '', [])
612        self.assertEqual(name, 'test_func')
613        self.assertEqual(arg, [])
614        expected = '''#line 1 "test_suite_ut.function"
615
616void test_func()
617{
618    ba ba black sheep
619    have you any wool
620exit:
621    ;
622}
623'''
624        self.assertEqual(code, expected)
625        self.assertEqual(dispatch_code, "\n    test_func_wrapper,\n")
626
627    @patch("generate_test_code.gen_dispatch")
628    @patch("generate_test_code.gen_dependencies")
629    @patch("generate_test_code.gen_function_wrapper")
630    @patch("generate_test_code.parse_function_arguments")
631    def test_with_exit_label(self, parse_function_arguments_mock,
632                             gen_function_wrapper_mock,
633                             gen_dependencies_mock,
634                             gen_dispatch_mock):
635        """
636        Test when exit label is present.
637        :return:
638        """
639        parse_function_arguments_mock.return_value = ([], '', [])
640        gen_function_wrapper_mock.return_value = ''
641        gen_dependencies_mock.side_effect = gen_dependencies
642        gen_dispatch_mock.side_effect = gen_dispatch
643        data = '''
644void func()
645{
646    ba ba black sheep
647    have you any wool
648exit:
649    yes sir yes sir
650    3 bags full
651}
652/* END_CASE */
653'''
654        stream = StringIOWrapper('test_suite_ut.function', data)
655        _, _, code, _ = parse_function_code(stream, [], [])
656
657        expected = '''#line 1 "test_suite_ut.function"
658
659void test_func()
660{
661    ba ba black sheep
662    have you any wool
663exit:
664    yes sir yes sir
665    3 bags full
666}
667'''
668        self.assertEqual(code, expected)
669
670    def test_non_void_function(self):
671        """
672        Test invalid signature (non void).
673        :return:
674        """
675        data = 'int entropy_threshold( char * a, data_t * h, int result )'
676        err_msg = 'file: test_suite_ut.function - Test functions not found!'
677        stream = StringIOWrapper('test_suite_ut.function', data)
678        self.assertRaisesRegex(GeneratorInputError, err_msg,
679                               parse_function_code, stream, [], [])
680
681    @patch("generate_test_code.gen_dispatch")
682    @patch("generate_test_code.gen_dependencies")
683    @patch("generate_test_code.gen_function_wrapper")
684    @patch("generate_test_code.parse_function_arguments")
685    def test_function_name_on_newline(self, parse_function_arguments_mock,
686                                      gen_function_wrapper_mock,
687                                      gen_dependencies_mock,
688                                      gen_dispatch_mock):
689        """
690        Test with line break before the function name.
691        :return:
692        """
693        parse_function_arguments_mock.return_value = ([], '', [])
694        gen_function_wrapper_mock.return_value = ''
695        gen_dependencies_mock.side_effect = gen_dependencies
696        gen_dispatch_mock.side_effect = gen_dispatch
697        data = '''
698void
699
700
701func()
702{
703    ba ba black sheep
704    have you any wool
705exit:
706    yes sir yes sir
707    3 bags full
708}
709/* END_CASE */
710'''
711        stream = StringIOWrapper('test_suite_ut.function', data)
712        _, _, code, _ = parse_function_code(stream, [], [])
713
714        expected = '''#line 1 "test_suite_ut.function"
715
716void
717
718
719test_func()
720{
721    ba ba black sheep
722    have you any wool
723exit:
724    yes sir yes sir
725    3 bags full
726}
727'''
728        self.assertEqual(code, expected)
729
730    @patch("generate_test_code.gen_dispatch")
731    @patch("generate_test_code.gen_dependencies")
732    @patch("generate_test_code.gen_function_wrapper")
733    @patch("generate_test_code.parse_function_arguments")
734    def test_case_starting_with_comment(self, parse_function_arguments_mock,
735                                        gen_function_wrapper_mock,
736                                        gen_dependencies_mock,
737                                        gen_dispatch_mock):
738        """
739        Test with comments before the function signature
740        :return:
741        """
742        parse_function_arguments_mock.return_value = ([], '', [])
743        gen_function_wrapper_mock.return_value = ''
744        gen_dependencies_mock.side_effect = gen_dependencies
745        gen_dispatch_mock.side_effect = gen_dispatch
746        data = '''/* comment */
747/* more
748 * comment */
749// this is\\
750still \\
751a comment
752void func()
753{
754    ba ba black sheep
755    have you any wool
756exit:
757    yes sir yes sir
758    3 bags full
759}
760/* END_CASE */
761'''
762        stream = StringIOWrapper('test_suite_ut.function', data)
763        _, _, code, _ = parse_function_code(stream, [], [])
764
765        expected = '''#line 1 "test_suite_ut.function"
766
767
768
769
770
771
772void test_func()
773{
774    ba ba black sheep
775    have you any wool
776exit:
777    yes sir yes sir
778    3 bags full
779}
780'''
781        self.assertEqual(code, expected)
782
783    @patch("generate_test_code.gen_dispatch")
784    @patch("generate_test_code.gen_dependencies")
785    @patch("generate_test_code.gen_function_wrapper")
786    @patch("generate_test_code.parse_function_arguments")
787    def test_comment_in_prototype(self, parse_function_arguments_mock,
788                                  gen_function_wrapper_mock,
789                                  gen_dependencies_mock,
790                                  gen_dispatch_mock):
791        """
792        Test with comments in the function prototype
793        :return:
794        """
795        parse_function_arguments_mock.return_value = ([], '', [])
796        gen_function_wrapper_mock.return_value = ''
797        gen_dependencies_mock.side_effect = gen_dependencies
798        gen_dispatch_mock.side_effect = gen_dispatch
799        data = '''
800void func( int x, // (line \\
801                     comment)
802           int y /* lone closing parenthesis) */ )
803{
804    ba ba black sheep
805    have you any wool
806exit:
807    yes sir yes sir
808    3 bags full
809}
810/* END_CASE */
811'''
812        stream = StringIOWrapper('test_suite_ut.function', data)
813        _, _, code, _ = parse_function_code(stream, [], [])
814
815        expected = '''#line 1 "test_suite_ut.function"
816
817void test_func( int x,
818
819           int y                                 )
820{
821    ba ba black sheep
822    have you any wool
823exit:
824    yes sir yes sir
825    3 bags full
826}
827'''
828        self.assertEqual(code, expected)
829
830    @patch("generate_test_code.gen_dispatch")
831    @patch("generate_test_code.gen_dependencies")
832    @patch("generate_test_code.gen_function_wrapper")
833    @patch("generate_test_code.parse_function_arguments")
834    def test_line_comment_in_block_comment(self, parse_function_arguments_mock,
835                                           gen_function_wrapper_mock,
836                                           gen_dependencies_mock,
837                                           gen_dispatch_mock):
838        """
839        Test with line comment in block comment.
840        :return:
841        """
842        parse_function_arguments_mock.return_value = ([], '', [])
843        gen_function_wrapper_mock.return_value = ''
844        gen_dependencies_mock.side_effect = gen_dependencies
845        gen_dispatch_mock.side_effect = gen_dispatch
846        data = '''
847void func( int x /* // */ )
848{
849    ba ba black sheep
850    have you any wool
851exit:
852    yes sir yes sir
853    3 bags full
854}
855/* END_CASE */
856'''
857        stream = StringIOWrapper('test_suite_ut.function', data)
858        _, _, code, _ = parse_function_code(stream, [], [])
859
860        expected = '''#line 1 "test_suite_ut.function"
861
862void test_func( int x          )
863{
864    ba ba black sheep
865    have you any wool
866exit:
867    yes sir yes sir
868    3 bags full
869}
870'''
871        self.assertEqual(code, expected)
872
873    @patch("generate_test_code.gen_dispatch")
874    @patch("generate_test_code.gen_dependencies")
875    @patch("generate_test_code.gen_function_wrapper")
876    @patch("generate_test_code.parse_function_arguments")
877    def test_block_comment_in_line_comment(self, parse_function_arguments_mock,
878                                           gen_function_wrapper_mock,
879                                           gen_dependencies_mock,
880                                           gen_dispatch_mock):
881        """
882        Test with block comment in line comment.
883        :return:
884        """
885        parse_function_arguments_mock.return_value = ([], '', [])
886        gen_function_wrapper_mock.return_value = ''
887        gen_dependencies_mock.side_effect = gen_dependencies
888        gen_dispatch_mock.side_effect = gen_dispatch
889        data = '''
890// /*
891void func( int x )
892{
893    ba ba black sheep
894    have you any wool
895exit:
896    yes sir yes sir
897    3 bags full
898}
899/* END_CASE */
900'''
901        stream = StringIOWrapper('test_suite_ut.function', data)
902        _, _, code, _ = parse_function_code(stream, [], [])
903
904        expected = '''#line 1 "test_suite_ut.function"
905
906
907void test_func( int x )
908{
909    ba ba black sheep
910    have you any wool
911exit:
912    yes sir yes sir
913    3 bags full
914}
915'''
916        self.assertEqual(code, expected)
917
918
919class ParseFunction(TestCase):
920    """
921    Test Suite for testing parse_functions()
922    """
923
924    @patch("generate_test_code.parse_until_pattern")
925    def test_begin_header(self, parse_until_pattern_mock):
926        """
927        Test that begin header is checked and parse_until_pattern() is called.
928        :return:
929        """
930        def stop(*_unused):
931            """Stop when parse_until_pattern is called."""
932            raise Exception
933        parse_until_pattern_mock.side_effect = stop
934        data = '''/* BEGIN_HEADER */
935#include "mbedtls/ecp.h"
936
937#define ECP_PF_UNKNOWN     -1
938/* END_HEADER */
939'''
940        stream = StringIOWrapper('test_suite_ut.function', data)
941        self.assertRaises(Exception, parse_functions, stream)
942        parse_until_pattern_mock.assert_called_with(stream, END_HEADER_REGEX)
943        self.assertEqual(stream.line_no, 1)
944
945    @patch("generate_test_code.parse_until_pattern")
946    def test_begin_helper(self, parse_until_pattern_mock):
947        """
948        Test that begin helper is checked and parse_until_pattern() is called.
949        :return:
950        """
951        def stop(*_unused):
952            """Stop when parse_until_pattern is called."""
953            raise Exception
954        parse_until_pattern_mock.side_effect = stop
955        data = '''/* BEGIN_SUITE_HELPERS */
956void print_hello_world()
957{
958    printf("Hello World!\n");
959}
960/* END_SUITE_HELPERS */
961'''
962        stream = StringIOWrapper('test_suite_ut.function', data)
963        self.assertRaises(Exception, parse_functions, stream)
964        parse_until_pattern_mock.assert_called_with(stream,
965                                                    END_SUITE_HELPERS_REGEX)
966        self.assertEqual(stream.line_no, 1)
967
968    @patch("generate_test_code.parse_suite_dependencies")
969    def test_begin_dep(self, parse_suite_dependencies_mock):
970        """
971        Test that begin dep is checked and parse_suite_dependencies() is
972        called.
973        :return:
974        """
975        def stop(*_unused):
976            """Stop when parse_until_pattern is called."""
977            raise Exception
978        parse_suite_dependencies_mock.side_effect = stop
979        data = '''/* BEGIN_DEPENDENCIES
980 * depends_on:MBEDTLS_ECP_C
981 * END_DEPENDENCIES
982 */
983'''
984        stream = StringIOWrapper('test_suite_ut.function', data)
985        self.assertRaises(Exception, parse_functions, stream)
986        parse_suite_dependencies_mock.assert_called_with(stream)
987        self.assertEqual(stream.line_no, 1)
988
989    @patch("generate_test_code.parse_function_dependencies")
990    def test_begin_function_dep(self, func_mock):
991        """
992        Test that begin dep is checked and parse_function_dependencies() is
993        called.
994        :return:
995        """
996        def stop(*_unused):
997            """Stop when parse_until_pattern is called."""
998            raise Exception
999        func_mock.side_effect = stop
1000
1001        dependencies_str = '/* BEGIN_CASE ' \
1002            'depends_on:MBEDTLS_ENTROPY_NV_SEED:MBEDTLS_FS_IO */\n'
1003        data = '''%svoid test_func()
1004{
1005}
1006''' % dependencies_str
1007        stream = StringIOWrapper('test_suite_ut.function', data)
1008        self.assertRaises(Exception, parse_functions, stream)
1009        func_mock.assert_called_with(dependencies_str)
1010        self.assertEqual(stream.line_no, 1)
1011
1012    @patch("generate_test_code.parse_function_code")
1013    @patch("generate_test_code.parse_function_dependencies")
1014    def test_return(self, func_mock1, func_mock2):
1015        """
1016        Test that begin case is checked and parse_function_code() is called.
1017        :return:
1018        """
1019        func_mock1.return_value = []
1020        in_func_code = '''void test_func()
1021{
1022}
1023'''
1024        func_dispatch = '''
1025    test_func_wrapper,
1026'''
1027        func_mock2.return_value = 'test_func', [],\
1028            in_func_code, func_dispatch
1029        dependencies_str = '/* BEGIN_CASE ' \
1030            'depends_on:MBEDTLS_ENTROPY_NV_SEED:MBEDTLS_FS_IO */\n'
1031        data = '''%svoid test_func()
1032{
1033}
1034''' % dependencies_str
1035        stream = StringIOWrapper('test_suite_ut.function', data)
1036        suite_dependencies, dispatch_code, func_code, func_info = \
1037            parse_functions(stream)
1038        func_mock1.assert_called_with(dependencies_str)
1039        func_mock2.assert_called_with(stream, [], [])
1040        self.assertEqual(stream.line_no, 5)
1041        self.assertEqual(suite_dependencies, [])
1042        expected_dispatch_code = '''/* Function Id: 0 */
1043
1044    test_func_wrapper,
1045'''
1046        self.assertEqual(dispatch_code, expected_dispatch_code)
1047        self.assertEqual(func_code, in_func_code)
1048        self.assertEqual(func_info, {'test_func': (0, [])})
1049
1050    def test_parsing(self):
1051        """
1052        Test case parsing.
1053        :return:
1054        """
1055        data = '''/* BEGIN_HEADER */
1056#include "mbedtls/ecp.h"
1057
1058#define ECP_PF_UNKNOWN     -1
1059/* END_HEADER */
1060
1061/* BEGIN_DEPENDENCIES
1062 * depends_on:MBEDTLS_ECP_C
1063 * END_DEPENDENCIES
1064 */
1065
1066/* BEGIN_CASE depends_on:MBEDTLS_ENTROPY_NV_SEED:MBEDTLS_FS_IO */
1067void func1()
1068{
1069}
1070/* END_CASE */
1071
1072/* BEGIN_CASE depends_on:MBEDTLS_ENTROPY_NV_SEED:MBEDTLS_FS_IO */
1073void func2()
1074{
1075}
1076/* END_CASE */
1077'''
1078        stream = StringIOWrapper('test_suite_ut.function', data)
1079        suite_dependencies, dispatch_code, func_code, func_info = \
1080            parse_functions(stream)
1081        self.assertEqual(stream.line_no, 23)
1082        self.assertEqual(suite_dependencies, ['MBEDTLS_ECP_C'])
1083
1084        expected_dispatch_code = '''/* Function Id: 0 */
1085
1086#if defined(MBEDTLS_ECP_C) && defined(MBEDTLS_ENTROPY_NV_SEED) && defined(MBEDTLS_FS_IO)
1087    test_func1_wrapper,
1088#else
1089    NULL,
1090#endif
1091/* Function Id: 1 */
1092
1093#if defined(MBEDTLS_ECP_C) && defined(MBEDTLS_ENTROPY_NV_SEED) && defined(MBEDTLS_FS_IO)
1094    test_func2_wrapper,
1095#else
1096    NULL,
1097#endif
1098'''
1099        self.assertEqual(dispatch_code, expected_dispatch_code)
1100        expected_func_code = '''#if defined(MBEDTLS_ECP_C)
1101#line 2 "test_suite_ut.function"
1102#include "mbedtls/ecp.h"
1103
1104#define ECP_PF_UNKNOWN     -1
1105#if defined(MBEDTLS_ENTROPY_NV_SEED)
1106#if defined(MBEDTLS_FS_IO)
1107#line 13 "test_suite_ut.function"
1108void test_func1()
1109{
1110exit:
1111    ;
1112}
1113
1114void test_func1_wrapper( void ** params )
1115{
1116    (void)params;
1117
1118    test_func1(  );
1119}
1120#endif /* MBEDTLS_FS_IO */
1121#endif /* MBEDTLS_ENTROPY_NV_SEED */
1122#if defined(MBEDTLS_ENTROPY_NV_SEED)
1123#if defined(MBEDTLS_FS_IO)
1124#line 19 "test_suite_ut.function"
1125void test_func2()
1126{
1127exit:
1128    ;
1129}
1130
1131void test_func2_wrapper( void ** params )
1132{
1133    (void)params;
1134
1135    test_func2(  );
1136}
1137#endif /* MBEDTLS_FS_IO */
1138#endif /* MBEDTLS_ENTROPY_NV_SEED */
1139#endif /* MBEDTLS_ECP_C */
1140'''
1141        self.assertEqual(func_code, expected_func_code)
1142        self.assertEqual(func_info, {'test_func1': (0, []),
1143                                     'test_func2': (1, [])})
1144
1145    def test_same_function_name(self):
1146        """
1147        Test name conflict.
1148        :return:
1149        """
1150        data = '''/* BEGIN_HEADER */
1151#include "mbedtls/ecp.h"
1152
1153#define ECP_PF_UNKNOWN     -1
1154/* END_HEADER */
1155
1156/* BEGIN_DEPENDENCIES
1157 * depends_on:MBEDTLS_ECP_C
1158 * END_DEPENDENCIES
1159 */
1160
1161/* BEGIN_CASE depends_on:MBEDTLS_ENTROPY_NV_SEED:MBEDTLS_FS_IO */
1162void func()
1163{
1164}
1165/* END_CASE */
1166
1167/* BEGIN_CASE depends_on:MBEDTLS_ENTROPY_NV_SEED:MBEDTLS_FS_IO */
1168void func()
1169{
1170}
1171/* END_CASE */
1172'''
1173        stream = StringIOWrapper('test_suite_ut.function', data)
1174        self.assertRaises(GeneratorInputError, parse_functions, stream)
1175
1176
1177class EscapedSplit(TestCase):
1178    """
1179    Test suite for testing escaped_split().
1180    Note: Since escaped_split() output is used to write back to the
1181    intermediate data file. Any escape characters in the input are
1182    retained in the output.
1183    """
1184
1185    def test_invalid_input(self):
1186        """
1187        Test when input split character is not a character.
1188        :return:
1189        """
1190        self.assertRaises(ValueError, escaped_split, '', 'string')
1191
1192    def test_empty_string(self):
1193        """
1194        Test empty string input.
1195        :return:
1196        """
1197        splits = escaped_split('', ':')
1198        self.assertEqual(splits, [])
1199
1200    def test_no_escape(self):
1201        """
1202        Test with no escape character. The behaviour should be same as
1203        str.split()
1204        :return:
1205        """
1206        test_str = 'yahoo:google'
1207        splits = escaped_split(test_str, ':')
1208        self.assertEqual(splits, test_str.split(':'))
1209
1210    def test_escaped_input(self):
1211        """
1212        Test input that has escaped delimiter.
1213        :return:
1214        """
1215        test_str = r'yahoo\:google:facebook'
1216        splits = escaped_split(test_str, ':')
1217        self.assertEqual(splits, [r'yahoo\:google', 'facebook'])
1218
1219    def test_escaped_escape(self):
1220        """
1221        Test input that has escaped delimiter.
1222        :return:
1223        """
1224        test_str = r'yahoo\\:google:facebook'
1225        splits = escaped_split(test_str, ':')
1226        self.assertEqual(splits, [r'yahoo\\', 'google', 'facebook'])
1227
1228    def test_all_at_once(self):
1229        """
1230        Test input that has escaped delimiter.
1231        :return:
1232        """
1233        test_str = r'yahoo\\:google:facebook\:instagram\\:bbc\\:wikipedia'
1234        splits = escaped_split(test_str, ':')
1235        self.assertEqual(splits, [r'yahoo\\', r'google',
1236                                  r'facebook\:instagram\\',
1237                                  r'bbc\\', r'wikipedia'])
1238
1239
1240class ParseTestData(TestCase):
1241    """
1242    Test suite for parse test data.
1243    """
1244
1245    def test_parser(self):
1246        """
1247        Test that tests are parsed correctly from data file.
1248        :return:
1249        """
1250        data = """
1251Diffie-Hellman full exchange #1
1252dhm_do_dhm:10:"23":10:"5"
1253
1254Diffie-Hellman full exchange #2
1255dhm_do_dhm:10:"93450983094850938450983409623":10:"9345098304850938450983409622"
1256
1257Diffie-Hellman full exchange #3
1258dhm_do_dhm:10:"9345098382739712938719287391879381271":10:"9345098792137312973297123912791271"
1259
1260Diffie-Hellman selftest
1261dhm_selftest:
1262"""
1263        stream = StringIOWrapper('test_suite_ut.function', data)
1264        # List of (name, function_name, dependencies, args)
1265        tests = list(parse_test_data(stream))
1266        test1, test2, test3, test4 = tests
1267        self.assertEqual(test1[0], 'Diffie-Hellman full exchange #1')
1268        self.assertEqual(test1[1], 'dhm_do_dhm')
1269        self.assertEqual(test1[2], [])
1270        self.assertEqual(test1[3], ['10', '"23"', '10', '"5"'])
1271
1272        self.assertEqual(test2[0], 'Diffie-Hellman full exchange #2')
1273        self.assertEqual(test2[1], 'dhm_do_dhm')
1274        self.assertEqual(test2[2], [])
1275        self.assertEqual(test2[3], ['10', '"93450983094850938450983409623"',
1276                                    '10', '"9345098304850938450983409622"'])
1277
1278        self.assertEqual(test3[0], 'Diffie-Hellman full exchange #3')
1279        self.assertEqual(test3[1], 'dhm_do_dhm')
1280        self.assertEqual(test3[2], [])
1281        self.assertEqual(test3[3], ['10',
1282                                    '"9345098382739712938719287391879381271"',
1283                                    '10',
1284                                    '"9345098792137312973297123912791271"'])
1285
1286        self.assertEqual(test4[0], 'Diffie-Hellman selftest')
1287        self.assertEqual(test4[1], 'dhm_selftest')
1288        self.assertEqual(test4[2], [])
1289        self.assertEqual(test4[3], [])
1290
1291    def test_with_dependencies(self):
1292        """
1293        Test that tests with dependencies are parsed.
1294        :return:
1295        """
1296        data = """
1297Diffie-Hellman full exchange #1
1298depends_on:YAHOO
1299dhm_do_dhm:10:"23":10:"5"
1300
1301Diffie-Hellman full exchange #2
1302dhm_do_dhm:10:"93450983094850938450983409623":10:"9345098304850938450983409622"
1303
1304"""
1305        stream = StringIOWrapper('test_suite_ut.function', data)
1306        # List of (name, function_name, dependencies, args)
1307        tests = list(parse_test_data(stream))
1308        test1, test2 = tests
1309        self.assertEqual(test1[0], 'Diffie-Hellman full exchange #1')
1310        self.assertEqual(test1[1], 'dhm_do_dhm')
1311        self.assertEqual(test1[2], ['YAHOO'])
1312        self.assertEqual(test1[3], ['10', '"23"', '10', '"5"'])
1313
1314        self.assertEqual(test2[0], 'Diffie-Hellman full exchange #2')
1315        self.assertEqual(test2[1], 'dhm_do_dhm')
1316        self.assertEqual(test2[2], [])
1317        self.assertEqual(test2[3], ['10', '"93450983094850938450983409623"',
1318                                    '10', '"9345098304850938450983409622"'])
1319
1320    def test_no_args(self):
1321        """
1322        Test GeneratorInputError is raised when test function name and
1323        args line is missing.
1324        :return:
1325        """
1326        data = """
1327Diffie-Hellman full exchange #1
1328depends_on:YAHOO
1329
1330
1331Diffie-Hellman full exchange #2
1332dhm_do_dhm:10:"93450983094850938450983409623":10:"9345098304850938450983409622"
1333
1334"""
1335        stream = StringIOWrapper('test_suite_ut.function', data)
1336        err = None
1337        try:
1338            for _, _, _, _ in parse_test_data(stream):
1339                pass
1340        except GeneratorInputError as err:
1341            self.assertEqual(type(err), GeneratorInputError)
1342
1343    def test_incomplete_data(self):
1344        """
1345        Test GeneratorInputError is raised when test function name
1346        and args line is missing.
1347        :return:
1348        """
1349        data = """
1350Diffie-Hellman full exchange #1
1351depends_on:YAHOO
1352"""
1353        stream = StringIOWrapper('test_suite_ut.function', data)
1354        err = None
1355        try:
1356            for _, _, _, _ in parse_test_data(stream):
1357                pass
1358        except GeneratorInputError as err:
1359            self.assertEqual(type(err), GeneratorInputError)
1360
1361
1362class GenDepCheck(TestCase):
1363    """
1364    Test suite for gen_dep_check(). It is assumed this function is
1365    called with valid inputs.
1366    """
1367
1368    def test_gen_dep_check(self):
1369        """
1370        Test that dependency check code generated correctly.
1371        :return:
1372        """
1373        expected = """
1374        case 5:
1375            {
1376#if defined(YAHOO)
1377                ret = DEPENDENCY_SUPPORTED;
1378#else
1379                ret = DEPENDENCY_NOT_SUPPORTED;
1380#endif
1381            }
1382            break;"""
1383        out = gen_dep_check(5, 'YAHOO')
1384        self.assertEqual(out, expected)
1385
1386    def test_not_defined_dependency(self):
1387        """
1388        Test dependency with !.
1389        :return:
1390        """
1391        expected = """
1392        case 5:
1393            {
1394#if !defined(YAHOO)
1395                ret = DEPENDENCY_SUPPORTED;
1396#else
1397                ret = DEPENDENCY_NOT_SUPPORTED;
1398#endif
1399            }
1400            break;"""
1401        out = gen_dep_check(5, '!YAHOO')
1402        self.assertEqual(out, expected)
1403
1404    def test_empty_dependency(self):
1405        """
1406        Test invalid dependency input.
1407        :return:
1408        """
1409        self.assertRaises(GeneratorInputError, gen_dep_check, 5, '!')
1410
1411    def test_negative_dep_id(self):
1412        """
1413        Test invalid dependency input.
1414        :return:
1415        """
1416        self.assertRaises(GeneratorInputError, gen_dep_check, -1, 'YAHOO')
1417
1418
1419class GenExpCheck(TestCase):
1420    """
1421    Test suite for gen_expression_check(). It is assumed this function
1422    is called with valid inputs.
1423    """
1424
1425    def test_gen_exp_check(self):
1426        """
1427        Test that expression check code generated correctly.
1428        :return:
1429        """
1430        expected = """
1431        case 5:
1432            {
1433                *out_value = YAHOO;
1434            }
1435            break;"""
1436        out = gen_expression_check(5, 'YAHOO')
1437        self.assertEqual(out, expected)
1438
1439    def test_invalid_expression(self):
1440        """
1441        Test invalid expression input.
1442        :return:
1443        """
1444        self.assertRaises(GeneratorInputError, gen_expression_check, 5, '')
1445
1446    def test_negative_exp_id(self):
1447        """
1448        Test invalid expression id.
1449        :return:
1450        """
1451        self.assertRaises(GeneratorInputError, gen_expression_check,
1452                          -1, 'YAHOO')
1453
1454
1455class WriteDependencies(TestCase):
1456    """
1457    Test suite for testing write_dependencies.
1458    """
1459
1460    def test_no_test_dependencies(self):
1461        """
1462        Test when test dependencies input is empty.
1463        :return:
1464        """
1465        stream = StringIOWrapper('test_suite_ut.data', '')
1466        unique_dependencies = []
1467        dep_check_code = write_dependencies(stream, [], unique_dependencies)
1468        self.assertEqual(dep_check_code, '')
1469        self.assertEqual(len(unique_dependencies), 0)
1470        self.assertEqual(stream.getvalue(), '')
1471
1472    def test_unique_dep_ids(self):
1473        """
1474
1475        :return:
1476        """
1477        stream = StringIOWrapper('test_suite_ut.data', '')
1478        unique_dependencies = []
1479        dep_check_code = write_dependencies(stream, ['DEP3', 'DEP2', 'DEP1'],
1480                                            unique_dependencies)
1481        expect_dep_check_code = '''
1482        case 0:
1483            {
1484#if defined(DEP3)
1485                ret = DEPENDENCY_SUPPORTED;
1486#else
1487                ret = DEPENDENCY_NOT_SUPPORTED;
1488#endif
1489            }
1490            break;
1491        case 1:
1492            {
1493#if defined(DEP2)
1494                ret = DEPENDENCY_SUPPORTED;
1495#else
1496                ret = DEPENDENCY_NOT_SUPPORTED;
1497#endif
1498            }
1499            break;
1500        case 2:
1501            {
1502#if defined(DEP1)
1503                ret = DEPENDENCY_SUPPORTED;
1504#else
1505                ret = DEPENDENCY_NOT_SUPPORTED;
1506#endif
1507            }
1508            break;'''
1509        self.assertEqual(dep_check_code, expect_dep_check_code)
1510        self.assertEqual(len(unique_dependencies), 3)
1511        self.assertEqual(stream.getvalue(), 'depends_on:0:1:2\n')
1512
1513    def test_dep_id_repeat(self):
1514        """
1515
1516        :return:
1517        """
1518        stream = StringIOWrapper('test_suite_ut.data', '')
1519        unique_dependencies = []
1520        dep_check_code = ''
1521        dep_check_code += write_dependencies(stream, ['DEP3', 'DEP2'],
1522                                             unique_dependencies)
1523        dep_check_code += write_dependencies(stream, ['DEP2', 'DEP1'],
1524                                             unique_dependencies)
1525        dep_check_code += write_dependencies(stream, ['DEP1', 'DEP3'],
1526                                             unique_dependencies)
1527        expect_dep_check_code = '''
1528        case 0:
1529            {
1530#if defined(DEP3)
1531                ret = DEPENDENCY_SUPPORTED;
1532#else
1533                ret = DEPENDENCY_NOT_SUPPORTED;
1534#endif
1535            }
1536            break;
1537        case 1:
1538            {
1539#if defined(DEP2)
1540                ret = DEPENDENCY_SUPPORTED;
1541#else
1542                ret = DEPENDENCY_NOT_SUPPORTED;
1543#endif
1544            }
1545            break;
1546        case 2:
1547            {
1548#if defined(DEP1)
1549                ret = DEPENDENCY_SUPPORTED;
1550#else
1551                ret = DEPENDENCY_NOT_SUPPORTED;
1552#endif
1553            }
1554            break;'''
1555        self.assertEqual(dep_check_code, expect_dep_check_code)
1556        self.assertEqual(len(unique_dependencies), 3)
1557        self.assertEqual(stream.getvalue(),
1558                         'depends_on:0:1\ndepends_on:1:2\ndepends_on:2:0\n')
1559
1560
1561class WriteParams(TestCase):
1562    """
1563    Test Suite for testing write_parameters().
1564    """
1565
1566    def test_no_params(self):
1567        """
1568        Test with empty test_args
1569        :return:
1570        """
1571        stream = StringIOWrapper('test_suite_ut.data', '')
1572        unique_expressions = []
1573        expression_code = write_parameters(stream, [], [], unique_expressions)
1574        self.assertEqual(len(unique_expressions), 0)
1575        self.assertEqual(expression_code, '')
1576        self.assertEqual(stream.getvalue(), '\n')
1577
1578    def test_no_exp_param(self):
1579        """
1580        Test when there is no macro or expression in the params.
1581        :return:
1582        """
1583        stream = StringIOWrapper('test_suite_ut.data', '')
1584        unique_expressions = []
1585        expression_code = write_parameters(stream, ['"Yahoo"', '"abcdef00"',
1586                                                    '0'],
1587                                           ['char*', 'hex', 'int'],
1588                                           unique_expressions)
1589        self.assertEqual(len(unique_expressions), 0)
1590        self.assertEqual(expression_code, '')
1591        self.assertEqual(stream.getvalue(),
1592                         ':char*:"Yahoo":hex:"abcdef00":int:0\n')
1593
1594    def test_hex_format_int_param(self):
1595        """
1596        Test int parameter in hex format.
1597        :return:
1598        """
1599        stream = StringIOWrapper('test_suite_ut.data', '')
1600        unique_expressions = []
1601        expression_code = write_parameters(stream,
1602                                           ['"Yahoo"', '"abcdef00"', '0xAA'],
1603                                           ['char*', 'hex', 'int'],
1604                                           unique_expressions)
1605        self.assertEqual(len(unique_expressions), 0)
1606        self.assertEqual(expression_code, '')
1607        self.assertEqual(stream.getvalue(),
1608                         ':char*:"Yahoo":hex:"abcdef00":int:0xAA\n')
1609
1610    def test_with_exp_param(self):
1611        """
1612        Test when there is macro or expression in the params.
1613        :return:
1614        """
1615        stream = StringIOWrapper('test_suite_ut.data', '')
1616        unique_expressions = []
1617        expression_code = write_parameters(stream,
1618                                           ['"Yahoo"', '"abcdef00"', '0',
1619                                            'MACRO1', 'MACRO2', 'MACRO3'],
1620                                           ['char*', 'hex', 'int',
1621                                            'int', 'int', 'int'],
1622                                           unique_expressions)
1623        self.assertEqual(len(unique_expressions), 3)
1624        self.assertEqual(unique_expressions, ['MACRO1', 'MACRO2', 'MACRO3'])
1625        expected_expression_code = '''
1626        case 0:
1627            {
1628                *out_value = MACRO1;
1629            }
1630            break;
1631        case 1:
1632            {
1633                *out_value = MACRO2;
1634            }
1635            break;
1636        case 2:
1637            {
1638                *out_value = MACRO3;
1639            }
1640            break;'''
1641        self.assertEqual(expression_code, expected_expression_code)
1642        self.assertEqual(stream.getvalue(),
1643                         ':char*:"Yahoo":hex:"abcdef00":int:0:exp:0:exp:1'
1644                         ':exp:2\n')
1645
1646    def test_with_repeat_calls(self):
1647        """
1648        Test when write_parameter() is called with same macro or expression.
1649        :return:
1650        """
1651        stream = StringIOWrapper('test_suite_ut.data', '')
1652        unique_expressions = []
1653        expression_code = ''
1654        expression_code += write_parameters(stream,
1655                                            ['"Yahoo"', 'MACRO1', 'MACRO2'],
1656                                            ['char*', 'int', 'int'],
1657                                            unique_expressions)
1658        expression_code += write_parameters(stream,
1659                                            ['"abcdef00"', 'MACRO2', 'MACRO3'],
1660                                            ['hex', 'int', 'int'],
1661                                            unique_expressions)
1662        expression_code += write_parameters(stream,
1663                                            ['0', 'MACRO3', 'MACRO1'],
1664                                            ['int', 'int', 'int'],
1665                                            unique_expressions)
1666        self.assertEqual(len(unique_expressions), 3)
1667        self.assertEqual(unique_expressions, ['MACRO1', 'MACRO2', 'MACRO3'])
1668        expected_expression_code = '''
1669        case 0:
1670            {
1671                *out_value = MACRO1;
1672            }
1673            break;
1674        case 1:
1675            {
1676                *out_value = MACRO2;
1677            }
1678            break;
1679        case 2:
1680            {
1681                *out_value = MACRO3;
1682            }
1683            break;'''
1684        self.assertEqual(expression_code, expected_expression_code)
1685        expected_data_file = ''':char*:"Yahoo":exp:0:exp:1
1686:hex:"abcdef00":exp:1:exp:2
1687:int:0:exp:2:exp:0
1688'''
1689        self.assertEqual(stream.getvalue(), expected_data_file)
1690
1691
1692class GenTestSuiteDependenciesChecks(TestCase):
1693    """
1694    Test suite for testing gen_suite_dep_checks()
1695    """
1696    def test_empty_suite_dependencies(self):
1697        """
1698        Test with empty suite_dependencies list.
1699
1700        :return:
1701        """
1702        dep_check_code, expression_code = \
1703            gen_suite_dep_checks([], 'DEP_CHECK_CODE', 'EXPRESSION_CODE')
1704        self.assertEqual(dep_check_code, 'DEP_CHECK_CODE')
1705        self.assertEqual(expression_code, 'EXPRESSION_CODE')
1706
1707    def test_suite_dependencies(self):
1708        """
1709        Test with suite_dependencies list.
1710
1711        :return:
1712        """
1713        dep_check_code, expression_code = \
1714            gen_suite_dep_checks(['SUITE_DEP'], 'DEP_CHECK_CODE',
1715                                 'EXPRESSION_CODE')
1716        expected_dep_check_code = '''
1717#if defined(SUITE_DEP)
1718DEP_CHECK_CODE
1719#endif
1720'''
1721        expected_expression_code = '''
1722#if defined(SUITE_DEP)
1723EXPRESSION_CODE
1724#endif
1725'''
1726        self.assertEqual(dep_check_code, expected_dep_check_code)
1727        self.assertEqual(expression_code, expected_expression_code)
1728
1729    def test_no_dep_no_exp(self):
1730        """
1731        Test when there are no dependency and expression code.
1732        :return:
1733        """
1734        dep_check_code, expression_code = gen_suite_dep_checks([], '', '')
1735        self.assertEqual(dep_check_code, '')
1736        self.assertEqual(expression_code, '')
1737
1738
1739class GenFromTestData(TestCase):
1740    """
1741    Test suite for gen_from_test_data()
1742    """
1743
1744    @staticmethod
1745    @patch("generate_test_code.write_dependencies")
1746    @patch("generate_test_code.write_parameters")
1747    @patch("generate_test_code.gen_suite_dep_checks")
1748    def test_intermediate_data_file(func_mock1,
1749                                    write_parameters_mock,
1750                                    write_dependencies_mock):
1751        """
1752        Test that intermediate data file is written with expected data.
1753        :return:
1754        """
1755        data = '''
1756My test
1757depends_on:DEP1
1758func1:0
1759'''
1760        data_f = StringIOWrapper('test_suite_ut.data', data)
1761        out_data_f = StringIOWrapper('test_suite_ut.datax', '')
1762        func_info = {'test_func1': (1, ('int',))}
1763        suite_dependencies = []
1764        write_parameters_mock.side_effect = write_parameters
1765        write_dependencies_mock.side_effect = write_dependencies
1766        func_mock1.side_effect = gen_suite_dep_checks
1767        gen_from_test_data(data_f, out_data_f, func_info, suite_dependencies)
1768        write_dependencies_mock.assert_called_with(out_data_f,
1769                                                   ['DEP1'], ['DEP1'])
1770        write_parameters_mock.assert_called_with(out_data_f, ['0'],
1771                                                 ('int',), [])
1772        expected_dep_check_code = '''
1773        case 0:
1774            {
1775#if defined(DEP1)
1776                ret = DEPENDENCY_SUPPORTED;
1777#else
1778                ret = DEPENDENCY_NOT_SUPPORTED;
1779#endif
1780            }
1781            break;'''
1782        func_mock1.assert_called_with(
1783            suite_dependencies, expected_dep_check_code, '')
1784
1785    def test_function_not_found(self):
1786        """
1787        Test that AssertError is raised when function info in not found.
1788        :return:
1789        """
1790        data = '''
1791My test
1792depends_on:DEP1
1793func1:0
1794'''
1795        data_f = StringIOWrapper('test_suite_ut.data', data)
1796        out_data_f = StringIOWrapper('test_suite_ut.datax', '')
1797        func_info = {'test_func2': (1, ('int',))}
1798        suite_dependencies = []
1799        self.assertRaises(GeneratorInputError, gen_from_test_data,
1800                          data_f, out_data_f, func_info, suite_dependencies)
1801
1802    def test_different_func_args(self):
1803        """
1804        Test that AssertError is raised when no. of parameters and
1805        function args differ.
1806        :return:
1807        """
1808        data = '''
1809My test
1810depends_on:DEP1
1811func1:0
1812'''
1813        data_f = StringIOWrapper('test_suite_ut.data', data)
1814        out_data_f = StringIOWrapper('test_suite_ut.datax', '')
1815        func_info = {'test_func2': (1, ('int', 'hex'))}
1816        suite_dependencies = []
1817        self.assertRaises(GeneratorInputError, gen_from_test_data, data_f,
1818                          out_data_f, func_info, suite_dependencies)
1819
1820    def test_output(self):
1821        """
1822        Test that intermediate data file is written with expected data.
1823        :return:
1824        """
1825        data = '''
1826My test 1
1827depends_on:DEP1
1828func1:0:0xfa:MACRO1:MACRO2
1829
1830My test 2
1831depends_on:DEP1:DEP2
1832func2:"yahoo":88:MACRO1
1833'''
1834        data_f = StringIOWrapper('test_suite_ut.data', data)
1835        out_data_f = StringIOWrapper('test_suite_ut.datax', '')
1836        func_info = {'test_func1': (0, ('int', 'int', 'int', 'int')),
1837                     'test_func2': (1, ('char*', 'int', 'int'))}
1838        suite_dependencies = []
1839        dep_check_code, expression_code = \
1840            gen_from_test_data(data_f, out_data_f, func_info,
1841                               suite_dependencies)
1842        expected_dep_check_code = '''
1843        case 0:
1844            {
1845#if defined(DEP1)
1846                ret = DEPENDENCY_SUPPORTED;
1847#else
1848                ret = DEPENDENCY_NOT_SUPPORTED;
1849#endif
1850            }
1851            break;
1852        case 1:
1853            {
1854#if defined(DEP2)
1855                ret = DEPENDENCY_SUPPORTED;
1856#else
1857                ret = DEPENDENCY_NOT_SUPPORTED;
1858#endif
1859            }
1860            break;'''
1861        expected_data = '''My test 1
1862depends_on:0
18630:int:0:int:0xfa:exp:0:exp:1
1864
1865My test 2
1866depends_on:0:1
18671:char*:"yahoo":int:88:exp:0
1868
1869'''
1870        expected_expression_code = '''
1871        case 0:
1872            {
1873                *out_value = MACRO1;
1874            }
1875            break;
1876        case 1:
1877            {
1878                *out_value = MACRO2;
1879            }
1880            break;'''
1881        self.assertEqual(dep_check_code, expected_dep_check_code)
1882        self.assertEqual(out_data_f.getvalue(), expected_data)
1883        self.assertEqual(expression_code, expected_expression_code)
1884
1885
1886if __name__ == '__main__':
1887    unittest_main()
1888