| #!/usr/bin/python |
| |
| # |
| # Copyright (C) 2014 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| """Unit tests for the TPM 2.0 code generator.""" |
| |
| from __future__ import print_function |
| |
| import StringIO |
| import unittest |
| |
| import generator |
| |
| |
| class TestGenerators(unittest.TestCase): |
| """Test generator classes.""" |
| |
| def testTypedef(self): |
| """Test generation of typedefs and dependencies.""" |
| typedef = generator.Typedef('int', 'INT') |
| defined_types = set(['int']) |
| typemap = {} |
| out_file = StringIO.StringIO() |
| # Expect this to just write the typedef. |
| typedef.OutputForward(out_file, defined_types, typemap) |
| # Expect this to know it has already been written. |
| typedef.Output(out_file, defined_types, typemap) |
| self.assertEqual(out_file.getvalue(), 'typedef int INT;\n') |
| self.assertIn('INT', defined_types) |
| typedef2 = generator.Typedef('TYPE1', 'TYPE2') |
| typemap = {'TYPE1': generator.Structure('TYPE1', False)} |
| defined_types = set([]) |
| out_file2 = StringIO.StringIO() |
| # Expect this to write first TYPE1 forward then TYPE2 typedef. |
| typedef2.Output(out_file2, defined_types, typemap) |
| output_re = r'struct TYPE1;\s+typedef TYPE1 TYPE2;\s+' |
| self.assertRegexpMatches(out_file2.getvalue(), output_re) |
| self.assertIn('TYPE2', defined_types) |
| out_file.close() |
| out_file2.close() |
| |
| def testTypedefSerialize(self): |
| """Test generation of serialization code for typedefs.""" |
| serialized_types = set(['int']) |
| typedef = generator.Typedef('int', 'INT') |
| typedef2 = generator.Typedef('INT', 'INT2') |
| typemap = {'INT': typedef} |
| out_file = StringIO.StringIO() |
| typedef2.OutputSerialize(out_file, serialized_types, typemap) |
| self.assertIn('INT', serialized_types) |
| self.assertIn('INT2', serialized_types) |
| out_file.close() |
| |
| def testConstant(self): |
| """Test generation of constant definitions and type dependencies.""" |
| constant = generator.Constant('INT', 'test', '1') |
| typemap = {'INT': generator.Structure('INT', False)} |
| defined_types = set([]) |
| out_file = StringIO.StringIO() |
| constant.Output(out_file, defined_types, typemap) |
| output_re = r'struct INT;\s+const INT test = 1;\s+' |
| self.assertRegexpMatches(out_file.getvalue(), output_re) |
| out_file.close() |
| |
| def testStructure(self): |
| """Test generation of structure declarations and field dependencies.""" |
| struct = generator.Structure('STRUCT', False) |
| struct.AddField('int', 'i') |
| struct.AddDependency('DEPEND') |
| union = generator.Structure('UNION', True) |
| union.AddField('STRUCT', 'inner') |
| depend = generator.Structure('DEPEND', False) |
| defined_types = set(['int']) |
| out_file = StringIO.StringIO() |
| typemap = {'STRUCT': struct, 'DEPEND': depend} |
| # Only output |union|, this will test the dependency logic. |
| union.OutputForward(out_file, defined_types, typemap) |
| union.OutputForward(out_file, defined_types, typemap) |
| union.Output(out_file, defined_types, typemap) |
| output_re = r'union UNION;\s+struct DEPEND {\s+};\s+' |
| output_re += r'struct STRUCT {\s+int i;\s+};\s+' |
| output_re += r'union UNION {\s+STRUCT inner;\s+};\s+' |
| self.assertRegexpMatches(out_file.getvalue(), output_re) |
| for t in ('STRUCT', 'DEPEND', 'UNION'): |
| self.assertIn(t, defined_types) |
| # Test serialize / parse code generation. |
| out_file.close() |
| |
| def testStructSerialize(self): |
| """Test generation of serialization code for typedefs.""" |
| serialized_types = set(['int', 'FOO', 'BAR', 'TPMI_ALG_SYM_OBJECT']) |
| struct = generator.Structure('TEST_STRUCT', False) |
| struct.fields = [('TPMI_ALG_SYM_OBJECT', 'selector'), |
| ('TPMU_SYM_MODE', 'mode'), |
| ('int', 'sizeOfFoo'), |
| ('int', 'foo[FOO_MAX]')] |
| # Choose TPMU_SYM_MODE because it exists in the selectors definition and it |
| # has few fields. |
| union = generator.Structure('TPMU_SYM_MODE', True) |
| union.fields = [('FOO', 'aes'), ('BAR', 'sm4')] |
| typemap = {'TPMU_SYM_MODE': union} |
| out_file = StringIO.StringIO() |
| struct.OutputSerialize(out_file, serialized_types, typemap) |
| self.assertIn('TPMU_SYM_MODE', serialized_types) |
| self.assertIn('TEST_STRUCT', serialized_types) |
| out_file.close() |
| |
| def testDefine(self): |
| """Test generation of preprocessor defines.""" |
| define = generator.Define('name', 'value') |
| out_file = StringIO.StringIO() |
| define.Output(out_file) |
| output_re = r'#if !defined\(name\)\s+#define name value\s+#endif\s+' |
| self.assertRegexpMatches(out_file.getvalue(), output_re) |
| out_file.close() |
| |
| def _MakeArg(self, arg_type, arg_name): |
| return {'type': arg_type, |
| 'name': arg_name, |
| 'command_code': None, |
| 'description': None} |
| |
| def testCommand(self): |
| """Test generation of command methods and callbacks.""" |
| command = generator.Command('TPM2_Test') |
| command.request_args = [self._MakeArg('int', 'input')] |
| command.response_args = [self._MakeArg('char', 'output')] |
| out_file = StringIO.StringIO() |
| command.OutputDeclarations(out_file) |
| expected_callback = """typedef base::Callback<void( |
| TPM_RC response_code, |
| const char& output)> TestResponse;""" |
| self.assertIn(expected_callback, out_file.getvalue()) |
| expected_serialize = """static TPM_RC SerializeCommand_Test( |
| const int& input, |
| std::string* serialized_command, |
| AuthorizationDelegate* authorization_delegate);""" |
| self.assertIn(expected_serialize, out_file.getvalue()) |
| expected_parse = """static TPM_RC ParseResponse_Test( |
| const std::string& response, |
| char* output, |
| AuthorizationDelegate* authorization_delegate);""" |
| self.assertIn(expected_parse, out_file.getvalue()) |
| expected_async = """virtual void Test( |
| const int& input, |
| AuthorizationDelegate* authorization_delegate, |
| const TestResponse& callback);""" |
| self.assertIn(expected_async, out_file.getvalue()) |
| expected_sync = """virtual TPM_RC TestSync( |
| const int& input, |
| char* output, |
| AuthorizationDelegate* authorization_delegate);""" |
| self.assertIn(expected_sync, out_file.getvalue()) |
| out_file.close() |
| |
| |
| class TestParsers(unittest.TestCase): |
| """Test parser classes.""" |
| |
| FAKE_TYPEDEF = '_BEGIN_TYPES\n_OLD_TYPE type1\n_NEW_TYPE type2\n_END\n' |
| FAKE_CONSTANT = ('_BEGIN_CONSTANTS\n_CONSTANTS (base_type) const_type\n' |
| '_TYPE base_type\n_NAME const_name\n_VALUE const_value\n' |
| '_END\n') |
| FAKE_STRUCTURE = ('_BEGIN_STRUCTURES\n_STRUCTURE struct_type\n' |
| '_TYPE field_type\n' |
| '_NAME field_name[sizeof(depend_type)]\n_END\n') |
| FAKE_DEFINE = '_BEGIN_DEFINES\n_NAME define_name\n_VALUE define_value\n_END' |
| FAKE_COMMAND = ('_BEGIN\n_INPUT_START TPM2_Test\n' |
| '_TYPE UINT32\n_NAME commandSize\n' |
| '_TYPE TPM_CC\n_NAME commandCode\n_COMMENT TPM_CC_Test\n' |
| '_TYPE UINT16\n_NAME input\n' |
| '_OUTPUT_START TPM2_Test\n_END\n') |
| |
| def testStructureParserWithBadData(self): |
| """Test the structure parser with invalid data.""" |
| input_data = 'bad_data' |
| in_file = StringIO.StringIO(input_data) |
| parser = generator.StructureParser(in_file) |
| types, constants, structs, defines, typemap = parser.Parse() |
| self.assertIsNotNone(types) |
| self.assertIsNotNone(constants) |
| self.assertIsNotNone(structs) |
| self.assertIsNotNone(defines) |
| self.assertIsNotNone(typemap) |
| |
| def testStructureParser(self): |
| """Test the structure parser with valid data.""" |
| input_data = (self.FAKE_TYPEDEF + self.FAKE_CONSTANT + self.FAKE_STRUCTURE + |
| self.FAKE_DEFINE) |
| in_file = StringIO.StringIO(input_data) |
| parser = generator.StructureParser(in_file) |
| types, constants, structs, defines, typemap = parser.Parse() |
| # Be flexible on these counts because the parser may add special cases. |
| self.assertGreaterEqual(len(types), 2) |
| self.assertGreaterEqual(len(constants), 1) |
| self.assertGreaterEqual(len(structs), 1) |
| self.assertGreaterEqual(len(defines), 1) |
| self.assertGreaterEqual(len(typemap), 3) |
| self.assertEqual(types[0].old_type, 'type1') |
| self.assertEqual(types[0].new_type, 'type2') |
| self.assertEqual(types[1].old_type, 'base_type') |
| self.assertEqual(types[1].new_type, 'const_type') |
| self.assertEqual(constants[0].const_type, 'const_type') |
| self.assertEqual(constants[0].name, 'const_name') |
| self.assertEqual(constants[0].value, 'const_value') |
| self.assertEqual(structs[0].name, 'struct_type') |
| self.assertEqual(structs[0].is_union, False) |
| self.assertEqual(len(structs[0].fields), 1) |
| self.assertEqual(structs[0].fields[0][0], 'field_type') |
| self.assertEqual(structs[0].fields[0][1], 'field_name[sizeof(depend_type)]') |
| self.assertEqual(len(structs[0].depends_on), 1) |
| self.assertEqual(structs[0].depends_on[0], 'depend_type') |
| self.assertEqual(defines[0].name, 'define_name') |
| self.assertEqual(defines[0].value, 'define_value') |
| |
| def testCommandParserWithBadData(self): |
| """Test the command parser with invalid data.""" |
| input_data = 'bad_data' |
| in_file = StringIO.StringIO(input_data) |
| parser = generator.CommandParser(in_file) |
| commands = parser.Parse() |
| self.assertIsNotNone(commands) |
| |
| def testCommandParser(self): |
| """Test the command parser with valid data.""" |
| input_data = self.FAKE_COMMAND |
| in_file = StringIO.StringIO(input_data) |
| parser = generator.CommandParser(in_file) |
| commands = parser.Parse() |
| self.assertEqual(len(commands), 1) |
| self.assertEqual(commands[0].name, 'TPM2_Test') |
| self.assertEqual(commands[0].command_code, 'TPM_CC_Test') |
| # We expect the 'commandSize' and 'commandCode' args to be filtered out. |
| self.assertEqual(len(commands[0].request_args), 1) |
| self.assertEqual(commands[0].request_args[0]['type'], 'UINT16') |
| self.assertEqual(commands[0].request_args[0]['name'], 'input') |
| self.assertIsNotNone(commands[0].response_args) |
| self.assertFalse(commands[0].response_args) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |