From 599cd247e6120d9279022e1e369b637479390703 Mon Sep 17 00:00:00 2001 From: Azim Khan Date: Thu, 6 Jul 2017 17:34:27 +0100 Subject: [PATCH] Update unit tests for code generator and make code generator more testable. --- tests/scripts/generate_code.py | 163 +++++++----- tests/scripts/generate_code_ut.py | 421 +++++++++++++++++++++++++++++- 2 files changed, 520 insertions(+), 64 deletions(-) diff --git a/tests/scripts/generate_code.py b/tests/scripts/generate_code.py index bc44b8cc0..7af6fdf29 100644 --- a/tests/scripts/generate_code.py +++ b/tests/scripts/generate_code.py @@ -347,6 +347,8 @@ def parse_functions(funcs_f): def escaped_split(str, ch): """ Split str on character ch but ignore escaped \{ch} + Since return value is used to write back to the intermediate data file. + Any escape characters in the input are retained in the output. :param str: :param ch: @@ -458,15 +460,98 @@ else return exp_code -def find_unique_id(val, vals): +def write_deps(out_data_f, test_deps, unique_deps): """ - Check if val already in vals. Gives a unique Identifier for the val. - :param val: - :param vals: + Write dependencies to intermediate test data file. + It also returns dependency check code. + + :param out_data_f: + :param dep: + :param unique_deps: :return: """ - if val not in vals: - vals.append(val) + dep_check_code = '' + if len(test_deps): + out_data_f.write('depends_on') + for dep in test_deps: + if dep not in unique_deps: + unique_deps.append(dep) + dep_id = unique_deps.index(dep) + dep_check_code += gen_dep_check(dep_id, dep) + else: + dep_id = unique_deps.index(dep) + out_data_f.write(':' + str(dep_id)) + out_data_f.write('\n') + return dep_check_code + + +def write_parameters(out_data_f, test_args, func_args, unique_expressions): + """ + Writes test parameters to the intermediate data file. + Also generates expression code. + + :param out_data_f: + :param test_args: + :param func_args: + :param unique_expressions: + :return: + """ + expression_code = '' + for i in xrange(len(test_args)): + typ = func_args[i] + val = test_args[i] + + # check if val is a non literal int val + if typ == 'int' and not re.match('(\d+$)|((0x)?[0-9a-fA-F]+$)', val): # its an expression + typ = 'exp' + if val not in unique_expressions: + unique_expressions.append(val) + # exp_id can be derived from len(). But for readability and consistency with case of existing let's + # use index(). + exp_id = unique_expressions.index(val) + expression_code += gen_expression_check(exp_id, val) + val = exp_id + else: + val = unique_expressions.index(val) + out_data_f.write(':' + typ + ':' + str(val)) + out_data_f.write('\n') + return expression_code + + +def gen_suite_deps_checks(suite_deps, dep_check_code, expression_code): + """ + Adds preprocessor checks for test suite dependencies. + + :param suite_deps: + :param dep_check_code: + :param expression_code: + :return: + """ + # void unused params + if len(dep_check_code) == 0: + dep_check_code = '(void) dep_id;\n' + if len(expression_code) == 0: + expression_code = '(void) exp_id;\n' + expression_code += '(void) out_value;\n' + + if len(suite_deps): + ifdef = gen_deps_one_line(suite_deps) + dep_check_code = ''' +{ifdef} +{code} +#else +(void) dep_id; +#endif +'''.format(ifdef=ifdef, code=dep_check_code) + expression_code = ''' +{ifdef} +{code} +#else +(void) exp_id; +(void) out_value; +#endif +'''.format(ifdef=ifdef, code=expression_code) + return dep_check_code, expression_code def gen_from_test_data(data_f, out_data_f, func_info, suite_deps): @@ -486,64 +571,24 @@ def gen_from_test_data(data_f, out_data_f, func_info, suite_deps): for test_name, function_name, test_deps, test_args in parse_test_data(data_f): out_data_f.write(test_name + '\n') - func_id, func_args = func_info['test_' + function_name] - if len(test_deps): - out_data_f.write('depends_on') - for dep in test_deps: - if dep not in unique_deps: - unique_deps.append(dep) - dep_id = unique_deps.index(dep) - dep_check_code += gen_dep_check(dep_id, dep) - else: - dep_id = unique_deps.index(dep) - out_data_f.write(':' + str(dep_id)) - out_data_f.write('\n') + # Write deps + dep_check_code += write_deps(out_data_f, test_deps, unique_deps) + # Write test function name + test_function_name = 'test_' + function_name + assert test_function_name in func_info, "Function %s not found!" % test_function_name + func_id, func_args = func_info[test_function_name] + out_data_f.write(str(func_id)) + + # Write parameters assert len(test_args) == len(func_args), \ "Invalid number of arguments in test %s. See function %s signature." % (test_name, function_name) - out_data_f.write(str(func_id)) - for i in xrange(len(test_args)): - typ = func_args[i] - val = test_args[i] + expression_code += write_parameters(out_data_f, test_args, func_args, unique_expressions) - # check if val is a non literal int val - if typ == 'int' and not re.match('\d+', val): # its an expression # FIXME: Handle hex format. Tip: instead try converting int(str, 10) and int(str, 16) - typ = 'exp' - if val not in unique_expressions: - unique_expressions.append(val) - # exp_id can be derived from len(). But for readability and consistency with case of existing let's - # use index(). - exp_id = unique_expressions.index(val) - expression_code += gen_expression_check(exp_id, val) - val = exp_id - else: - val = unique_expressions.index(val) - out_data_f.write(':' + typ + ':' + str(val)) - out_data_f.write('\n\n') + # Write a newline as test case separator + out_data_f.write('\n') - # void unused params - if len(dep_check_code) == 0: - dep_check_code = '(void) dep_id;\n' - if len(expression_code) == 0: - expression_code = '(void) exp_id;\n' - expression_code += '(void) out_value;\n' - ifdef = gen_deps_one_line(suite_deps) - if len(suite_deps): - dep_check_code = ''' -{ifdef} -{code} -#else -(void) dep_id; -#endif -'''.format(ifdef=ifdef, code=dep_check_code) - expression_code = ''' -{ifdef} -{code} -#else -(void) exp_id; -(void) out_value; -#endif -'''.format(ifdef=ifdef, code=expression_code) + dep_check_code, expression_code = gen_suite_deps_checks(suite_deps, dep_check_code, expression_code) return dep_check_code, expression_code diff --git a/tests/scripts/generate_code_ut.py b/tests/scripts/generate_code_ut.py index c261b2742..8545b4a0c 100644 --- a/tests/scripts/generate_code_ut.py +++ b/tests/scripts/generate_code_ut.py @@ -840,7 +840,9 @@ void func() class ExcapedSplit(TestCase): """ - Test suite for testing escaped_split() + Test suite for testing escaped_split(). + Note: Since escaped_split() output is used to write back to the intermediate data file. Any escape characters + in the input are retained in the output. """ def test_invalid_input(self): @@ -874,7 +876,7 @@ class ExcapedSplit(TestCase): """ s = 'yahoo\:google:facebook' splits = escaped_split(s, ':') - self.assertEqual(splits, ['yahoo:google', 'facebook']) + self.assertEqual(splits, ['yahoo\:google', 'facebook']) def test_escaped_escape(self): """ @@ -883,7 +885,7 @@ class ExcapedSplit(TestCase): """ s = 'yahoo\\\:google:facebook' splits = escaped_split(s, ':') - self.assertEqual(splits, ['yahoo\\', 'google', 'facebook']) + self.assertEqual(splits, ['yahoo\\\\', 'google', 'facebook']) def test_all_at_once(self): """ @@ -892,7 +894,8 @@ class ExcapedSplit(TestCase): """ s = 'yahoo\\\:google:facebook\:instagram\\\:bbc\\\\:wikipedia' splits = escaped_split(s, ':') - self.assertEqual(splits, ['yahoo\\', 'google', 'facebook:instagram\\', 'bbc\\', 'wikipedia']) + self.assertEqual(splits, ['yahoo\\\\', 'google', 'facebook\:instagram\\\\', 'bbc\\\\', 'wikipedia']) + class ParseTestData(TestCase): """ @@ -1102,12 +1105,420 @@ else self.assertRaises(AssertionError, gen_expression_check, -1, 'YAHOO') +class WriteDeps(TestCase): + """ + Test suite for testing write_deps. + """ + + def test_no_test_deps(self): + """ + Test when test_deps is empty. + :return: + """ + s = StringIOWrapper('test_suite_ut.data', '') + unique_deps = [] + dep_check_code = write_deps(s, [], unique_deps) + self.assertEqual(dep_check_code, '') + self.assertEqual(len(unique_deps), 0) + self.assertEqual(s.getvalue(), '') + + def test_unique_dep_ids(self): + """ + + :return: + """ + s = StringIOWrapper('test_suite_ut.data', '') + unique_deps = [] + dep_check_code = write_deps(s, ['DEP3', 'DEP2', 'DEP1'], unique_deps) + expect_dep_check_code = ''' +if ( dep_id == 0 ) +{ +#if defined(DEP3) + return( DEPENDENCY_SUPPORTED ); +#else + return( DEPENDENCY_NOT_SUPPORTED ); +#endif +} +else + +if ( dep_id == 1 ) +{ +#if defined(DEP2) + return( DEPENDENCY_SUPPORTED ); +#else + return( DEPENDENCY_NOT_SUPPORTED ); +#endif +} +else + +if ( dep_id == 2 ) +{ +#if defined(DEP1) + return( DEPENDENCY_SUPPORTED ); +#else + return( DEPENDENCY_NOT_SUPPORTED ); +#endif +} +else +''' + self.assertEqual(dep_check_code, expect_dep_check_code) + self.assertEqual(len(unique_deps), 3) + self.assertEqual(s.getvalue(), 'depends_on:0:1:2\n') + + def test_dep_id_repeat(self): + """ + + :return: + """ + s = StringIOWrapper('test_suite_ut.data', '') + unique_deps = [] + dep_check_code = '' + dep_check_code += write_deps(s, ['DEP3', 'DEP2'], unique_deps) + dep_check_code += write_deps(s, ['DEP2', 'DEP1'], unique_deps) + dep_check_code += write_deps(s, ['DEP1', 'DEP3'], unique_deps) + expect_dep_check_code = ''' +if ( dep_id == 0 ) +{ +#if defined(DEP3) + return( DEPENDENCY_SUPPORTED ); +#else + return( DEPENDENCY_NOT_SUPPORTED ); +#endif +} +else + +if ( dep_id == 1 ) +{ +#if defined(DEP2) + return( DEPENDENCY_SUPPORTED ); +#else + return( DEPENDENCY_NOT_SUPPORTED ); +#endif +} +else + +if ( dep_id == 2 ) +{ +#if defined(DEP1) + return( DEPENDENCY_SUPPORTED ); +#else + return( DEPENDENCY_NOT_SUPPORTED ); +#endif +} +else +''' + self.assertEqual(dep_check_code, expect_dep_check_code) + self.assertEqual(len(unique_deps), 3) + self.assertEqual(s.getvalue(), 'depends_on:0:1\ndepends_on:1:2\ndepends_on:2:0\n') + + +class WriteParams(TestCase): + """ + Test Suite for testing write_parameters(). + """ + + def test_no_params(self): + """ + Test with empty test_args + :return: + """ + s = StringIOWrapper('test_suite_ut.data', '') + unique_expressions = [] + expression_code = write_parameters(s, [], [], unique_expressions) + self.assertEqual(len(unique_expressions), 0) + self.assertEqual(expression_code, '') + self.assertEqual(s.getvalue(), '\n') + + def test_no_exp_param(self): + """ + Test when there is no macro or expression in the params. + :return: + """ + s = StringIOWrapper('test_suite_ut.data', '') + unique_expressions = [] + expression_code = write_parameters(s, ['"Yahoo"', '"abcdef00"', '0'], ['char*', 'hex', 'int'], + unique_expressions) + self.assertEqual(len(unique_expressions), 0) + self.assertEqual(expression_code, '') + self.assertEqual(s.getvalue(), ':char*:"Yahoo":hex:"abcdef00":int:0\n') + + def test_hex_format_int_param(self): + """ + Test int parameter in hex format. + :return: + """ + s = StringIOWrapper('test_suite_ut.data', '') + unique_expressions = [] + expression_code = write_parameters(s, ['"Yahoo"', '"abcdef00"', '0xAA'], ['char*', 'hex', 'int'], + unique_expressions) + self.assertEqual(len(unique_expressions), 0) + self.assertEqual(expression_code, '') + self.assertEqual(s.getvalue(), ':char*:"Yahoo":hex:"abcdef00":int:0xAA\n') + + def test_with_exp_param(self): + """ + Test when there is macro or expression in the params. + :return: + """ + s = StringIOWrapper('test_suite_ut.data', '') + unique_expressions = [] + expression_code = write_parameters(s, ['"Yahoo"', '"abcdef00"', '0', 'MACRO1', 'MACRO2', 'MACRO3'], + ['char*', 'hex', 'int', 'int', 'int', 'int'], + unique_expressions) + self.assertEqual(len(unique_expressions), 3) + self.assertEqual(unique_expressions, ['MACRO1', 'MACRO2', 'MACRO3']) + expected_expression_code = ''' +if ( exp_id == 0 ) +{ + *out_value = MACRO1; +} +else + +if ( exp_id == 1 ) +{ + *out_value = MACRO2; +} +else + +if ( exp_id == 2 ) +{ + *out_value = MACRO3; +} +else +''' + self.assertEqual(expression_code, expected_expression_code) + self.assertEqual(s.getvalue(), ':char*:"Yahoo":hex:"abcdef00":int:0:exp:0:exp:1:exp:2\n') + + def test_with_repeate_calls(self): + """ + Test when write_parameter() is called with same macro or expression. + :return: + """ + s = StringIOWrapper('test_suite_ut.data', '') + unique_expressions = [] + expression_code = '' + expression_code += write_parameters(s, ['"Yahoo"', 'MACRO1', 'MACRO2'], ['char*', 'int', 'int'], + unique_expressions) + expression_code += write_parameters(s, ['"abcdef00"', 'MACRO2', 'MACRO3'], ['hex', 'int', 'int'], + unique_expressions) + expression_code += write_parameters(s, ['0', 'MACRO3', 'MACRO1'], ['int', 'int', 'int'], + unique_expressions) + self.assertEqual(len(unique_expressions), 3) + self.assertEqual(unique_expressions, ['MACRO1', 'MACRO2', 'MACRO3']) + expected_expression_code = ''' +if ( exp_id == 0 ) +{ + *out_value = MACRO1; +} +else + +if ( exp_id == 1 ) +{ + *out_value = MACRO2; +} +else + +if ( exp_id == 2 ) +{ + *out_value = MACRO3; +} +else +''' + self.assertEqual(expression_code, expected_expression_code) + expected_data_file = ''':char*:"Yahoo":exp:0:exp:1 +:hex:"abcdef00":exp:1:exp:2 +:int:0:exp:2:exp:0 +''' + self.assertEqual(s.getvalue(), expected_data_file) + + +class GenTestSuiteDepsChecks(TestCase): + """ + + """ + def test_empty_suite_deps(self): + """ + Test with empty suite_deps list. + + :return: + """ + dep_check_code, expression_code = gen_suite_deps_checks([], 'DEP_CHECK_CODE', 'EXPRESSION_CODE') + self.assertEqual(dep_check_code, 'DEP_CHECK_CODE') + self.assertEqual(expression_code, 'EXPRESSION_CODE') + + def test_suite_deps(self): + """ + Test with suite_deps list. + + :return: + """ + dep_check_code, expression_code = gen_suite_deps_checks(['SUITE_DEP'], 'DEP_CHECK_CODE', 'EXPRESSION_CODE') + exprectd_dep_check_code = ''' +#if defined(SUITE_DEP) +DEP_CHECK_CODE +#else +(void) dep_id; +#endif +''' + expected_expression_code = ''' +#if defined(SUITE_DEP) +EXPRESSION_CODE +#else +(void) exp_id; +(void) out_value; +#endif +''' + self.assertEqual(dep_check_code, exprectd_dep_check_code) + self.assertEqual(expression_code, expected_expression_code) + + def test_no_dep_no_exp(self): + """ + Test when there are no dependency and expression code. + :return: + """ + dep_check_code, expression_code = gen_suite_deps_checks([], '', '') + self.assertEqual(dep_check_code, '(void) dep_id;\n') + self.assertEqual(expression_code, '(void) exp_id;\n(void) out_value;\n') + + class GenFromTestData(TestCase): """ Test suite for gen_from_test_data() """ - pass + @patch("generate_code.write_deps") + @patch("generate_code.write_parameters") + @patch("generate_code.gen_suite_deps_checks") + def test_intermediate_data_file(self, gen_suite_deps_checks_mock, write_parameters_mock, write_deps_mock): + """ + Test that intermediate data file is written with expected data. + :return: + """ + data = ''' +My test +depends_on:DEP1 +func1:0 +''' + data_f = StringIOWrapper('test_suite_ut.data', data) + out_data_f = StringIOWrapper('test_suite_ut.datax', '') + func_info = {'test_func1': (1, ('int',))} + suite_deps = [] + write_parameters_mock.side_effect = write_parameters + write_deps_mock.side_effect = write_deps + gen_suite_deps_checks_mock.side_effect = gen_suite_deps_checks + gen_from_test_data(data_f, out_data_f, func_info, suite_deps) + write_deps_mock.assert_called_with(out_data_f, ['DEP1'], ['DEP1']) + write_parameters_mock.assert_called_with(out_data_f, ['0'], ('int',), []) + expected_dep_check_code = ''' +if ( dep_id == 0 ) +{ +#if defined(DEP1) + return( DEPENDENCY_SUPPORTED ); +#else + return( DEPENDENCY_NOT_SUPPORTED ); +#endif +} +else +''' + gen_suite_deps_checks_mock.assert_called_with(suite_deps, expected_dep_check_code, '') + + def test_function_not_found(self): + """ + Test that AssertError is raised when function info in not found. + :return: + """ + data = ''' +My test +depends_on:DEP1 +func1:0 +''' + data_f = StringIOWrapper('test_suite_ut.data', data) + out_data_f = StringIOWrapper('test_suite_ut.datax', '') + func_info = {'test_func2': (1, ('int',))} + suite_deps = [] + self.assertRaises(AssertionError, gen_from_test_data, data_f, out_data_f, func_info, suite_deps) + + def test_different_func_args(self): + """ + Test that AssertError is raised when no. of parameters and function args differ. + :return: + """ + data = ''' +My test +depends_on:DEP1 +func1:0 +''' + data_f = StringIOWrapper('test_suite_ut.data', data) + out_data_f = StringIOWrapper('test_suite_ut.datax', '') + func_info = {'test_func2': (1, ('int','hex'))} + suite_deps = [] + self.assertRaises(AssertionError, gen_from_test_data, data_f, out_data_f, func_info, suite_deps) + + def test_output(self): + """ + Test that intermediate data file is written with expected data. + :return: + """ + data = ''' +My test 1 +depends_on:DEP1 +func1:0:0xfa:MACRO1:MACRO2 + +My test 2 +depends_on:DEP1:DEP2 +func2:"yahoo":88:MACRO1 +''' + data_f = StringIOWrapper('test_suite_ut.data', data) + out_data_f = StringIOWrapper('test_suite_ut.datax', '') + func_info = {'test_func1': (0, ('int', 'int', 'int', 'int')), 'test_func2': (1, ('char*', 'int', 'int'))} + suite_deps = [] + dep_check_code, expression_code = gen_from_test_data(data_f, out_data_f, func_info, suite_deps) + expected_dep_check_code = ''' +if ( dep_id == 0 ) +{ +#if defined(DEP1) + return( DEPENDENCY_SUPPORTED ); +#else + return( DEPENDENCY_NOT_SUPPORTED ); +#endif +} +else + +if ( dep_id == 1 ) +{ +#if defined(DEP2) + return( DEPENDENCY_SUPPORTED ); +#else + return( DEPENDENCY_NOT_SUPPORTED ); +#endif +} +else +''' + expecrted_data = '''My test 1 +depends_on:0 +0:int:0:int:0xfa:exp:0:exp:1 + +My test 2 +depends_on:0:1 +1:char*:"yahoo":int:88:exp:0 + +''' + expected_expression_code = ''' +if ( exp_id == 0 ) +{ + *out_value = MACRO1; +} +else + +if ( exp_id == 1 ) +{ + *out_value = MACRO2; +} +else +''' + self.assertEqual(dep_check_code, expected_dep_check_code) + self.assertEqual(out_data_f.getvalue(), expecrted_data) + self.assertEqual(expression_code, expected_expression_code) if __name__=='__main__':