blob: 5f86a71ee87a37e569f52d8c331c2b227b5d1775 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2014 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unit tests for the TPM 2.0 code generator."""
from __future__ import print_function
import io
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 = io.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 = io.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.assertRegex(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 = io.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 = io.StringIO()
constant.Output(out_file, defined_types, typemap)
output_re = r"struct INT;\s+constexpr INT test = 1;\s+"
self.assertRegex(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 = io.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.assertRegex(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 = io.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 = io.StringIO()
define.Output(out_file)
output_re = r"#if !defined\(name\)\s+#define name value\s+#endif\s+"
self.assertRegex(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 = io.StringIO()
command.OutputDeclarations(out_file)
expected_callback = """typedef base::OnceCallback<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,
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 = io.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 = io.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 = io.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 = io.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()