summaryrefslogtreecommitdiffstats
path: root/mojo
diff options
context:
space:
mode:
authordavemoore@chromium.org <davemoore@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-08 22:39:30 +0000
committerdavemoore@chromium.org <davemoore@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-08 22:39:30 +0000
commitb1057c433bb70fd9df337fe0851ac181b951d07b (patch)
treecb0e293035f352719b5c60c218c732e152baeea0 /mojo
parentd4c07bf4b5f415352d7f27f67735559c39fbf3c6 (diff)
downloadchromium_src-b1057c433bb70fd9df337fe0851ac181b951d07b.zip
chromium_src-b1057c433bb70fd9df337fe0851ac181b951d07b.tar.gz
chromium_src-b1057c433bb70fd9df337fe0851ac181b951d07b.tar.bz2
Initial support for mojo module bindings
BUG=None TEST=Included R=darin@chromium.org, darin Review URL: https://codereview.chromium.org/26361003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@227615 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'mojo')
-rw-r--r--mojo/public/bindings/generators/mojom.py131
-rw-r--r--mojo/public/bindings/generators/mojom_data.py164
-rw-r--r--mojo/public/bindings/generators/mojom_data_tests.py84
-rwxr-xr-xmojo/public/bindings/generators/mojom_test.py193
-rwxr-xr-xmojo/public/bindings/generators/mojom_tests.py33
-rwxr-xr-xmojo/public/bindings/generators/run_mojom_tests.py35
6 files changed, 640 insertions, 0 deletions
diff --git a/mojo/public/bindings/generators/mojom.py b/mojo/public/bindings/generators/mojom.py
new file mode 100644
index 0000000..b12ec31
--- /dev/null
+++ b/mojo/public/bindings/generators/mojom.py
@@ -0,0 +1,131 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# mojom's classes provide an interface to mojo modules. Modules are collections
+# of interfaces and structs to be used by mojo ipc clients and servers.
+#
+# A simple interface would be created this way:
+# module = mojom.Module('Foo')
+# interface = module.AddInterface('Bar')
+# method = interface.AddMethod('Tat', 0)
+# method.AddParameter('baz', 0, mojom.INT32)
+#
+class Kind(object):
+ def __init__(self, spec = None):
+ self.spec = spec
+
+
+# Initialize the set of primitive types. These can be accessed by clients.
+BOOL = Kind('b')
+INT8 = Kind('i8')
+INT16 = Kind('i16')
+INT32 = Kind('i32')
+INT64 = Kind('i64')
+UINT8 = Kind('u8')
+UINT16 = Kind('u16')
+UINT32 = Kind('u32')
+UINT64 = Kind('u64')
+FLOAT = Kind('f')
+DOUBLE = Kind('d')
+STRING = Kind('s')
+
+
+# Collection of all Primitive types
+PRIMITIVES = [
+ BOOL,
+ INT8,
+ INT16,
+ INT32,
+ INT64,
+ UINT8,
+ UINT16,
+ UINT32,
+ UINT64,
+ FLOAT,
+ DOUBLE,
+ STRING,
+ // TODO(davemoore): Add HANDLE.
+]
+
+
+class Field(object):
+ def __init__(self, name = None, kind = None, ordinal = None, default = None):
+ self.name = name
+ self.kind = kind
+ self.ordinal = ordinal
+ self.default = default
+
+
+class Struct(Kind):
+ def __init__(self, name = None):
+ self.name = name
+ if name != None:
+ spec = 'x:' + name
+ else:
+ spec = None
+ Kind.__init__(self, spec)
+ self.fields = []
+
+ def AddField(self, name, kind, ordinal = None, default = None):
+ field = Field(name, kind, ordinal, default)
+ self.fields.append(field)
+ return field
+
+
+class Array(Kind):
+ def __init__(self, kind = None):
+ self.kind = kind
+ if kind != None:
+ Kind.__init__(self, 'a:' + kind.spec)
+ else:
+ Kind.__init__(self)
+
+
+class Parameter(object):
+ def __init__(self, name = None, kind = None, ordinal = None, default = None):
+ self.name = name
+ self.ordinal = ordinal
+ self.kind = kind
+ self.default = default
+
+
+class Method(object):
+ def __init__(self, name = None, ordinal = None):
+ self.name = name
+ self.ordinal = ordinal
+ self.parameters = []
+
+ def AddParameter(self, name, kind, ordinal = None, default = None):
+ parameter = Parameter(name, kind, ordinal, default)
+ self.parameters.append(parameter)
+ return parameter
+
+
+class Interface(object):
+ def __init__(self, name = None):
+ self.name = name
+ self.methods = []
+
+ def AddMethod(self, name, ordinal = None):
+ method = Method(name, ordinal)
+ self.methods.append(method)
+ return method;
+
+
+class Module(object):
+ def __init__(self, name = None, namespace = None):
+ self.name = name
+ self.namespace = namespace
+ self.structs = []
+ self.interfaces = []
+
+ def AddInterface(self, name):
+ interface = Interface(name);
+ self.interfaces.append(interface)
+ return interface;
+
+ def AddStruct(self, name):
+ struct = Struct(name)
+ self.structs.append(struct)
+ return struct;
diff --git a/mojo/public/bindings/generators/mojom_data.py b/mojo/public/bindings/generators/mojom_data.py
new file mode 100644
index 0000000..3593511
--- /dev/null
+++ b/mojo/public/bindings/generators/mojom_data.py
@@ -0,0 +1,164 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import mojom
+
+# mojom_data provides a mechanism to turn mojom Modules to dictionaries and
+# back again. This can be used to persist a mojom Module created progromatically
+# or to read a dictionary from code or a file.
+# Example:
+# test_dict = {
+# 'name': 'test',
+# 'namespace': 'testspace',
+# 'structs': [{
+# 'name': 'teststruct',
+# 'fields': [
+# {'name': 'testfield1', 'kind': 'i32'},
+# {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}],
+# 'interfaces': [{
+# 'name': 'Server',
+# 'methods': [{
+# 'name': 'Foo',
+# 'parameters': [{
+# 'name': 'foo', 'kind': 'i32'},
+# {'name': 'bar', 'kind': 'a:x:teststruct'}],
+# 'ordinal': 42}]}]
+# }
+# test_module = mojom_data.ModuleFromData(test_dict)
+
+
+# Used to create a subclass of str that supports sorting by index, to make
+# pretty printing maintain the order.
+def istr(index, string):
+ class IndexedString(str):
+ def __lt__(self, other):
+ return self.__index__ < other.__index__
+
+ istr = IndexedString(string)
+ istr.__index__ = index
+ return istr
+
+def KindToData(kind):
+ return kind.spec
+
+def KindFromData(kinds, data):
+ if kinds.has_key(data):
+ return kinds[data]
+ if data.startswith('a:'):
+ kind = mojom.Array()
+ kind.kind = KindFromData(kinds, data[2:])
+ else:
+ kind = mojom.Kind()
+ kind.spec = data
+ kinds[data] = kind
+ return kind
+
+def StructToData(struct):
+ return {
+ istr(0, 'name'): struct.name,
+ istr(1, 'fields'): map(FieldToData, struct.fields)
+ }
+
+def StructFromData(kinds, data):
+ struct = mojom.Struct()
+ struct.name = data['name']
+ struct.spec = 'x:' + struct.name
+ kinds[struct.spec] = struct
+ struct.fields = map(lambda field: FieldFromData(kinds, field), data['fields'])
+ return struct
+
+def FieldToData(field):
+ data = {
+ istr(0, 'name'): field.name,
+ istr(1, 'kind'): KindToData(field.kind)
+ }
+ if field.ordinal != None:
+ data[istr(2, 'ordinal')] = field.ordinal
+ if field.default != None:
+ data[istr(3, 'default')] = field.default
+ return data
+
+def FieldFromData(kinds, data):
+ field = mojom.Field()
+ field.name = data['name']
+ field.kind = KindFromData(kinds, data['kind'])
+ field.ordinal = data.get('ordinal')
+ field.default = data.get('default')
+ return field
+
+def ParameterToData(parameter):
+ data = {
+ istr(0, 'name'): parameter.name,
+ istr(1, 'kind'): parameter.kind.spec
+ }
+ if parameter.ordinal != None:
+ data[istr(2, 'ordinal')] = parameter.ordinal
+ if parameter.default != None:
+ data[istr(3, 'default')] = parameter.default
+ return data
+
+def ParameterFromData(kinds, data):
+ parameter = mojom.Parameter()
+ parameter.name = data['name']
+ parameter.kind = KindFromData(kinds, data['kind'])
+ parameter.ordinal = data.get('ordinal')
+ parameter.default = data.get('default')
+ return parameter
+
+def MethodToData(method):
+ data = {
+ istr(0, 'name'): method.name,
+ istr(1, 'parameters'): map(ParameterToData, method.parameters)
+ }
+ if method.ordinal != None:
+ data[istr(2, 'ordinal')] = method.ordinal
+ return data
+
+def MethodFromData(kinds, data):
+ method = mojom.Method()
+ method.name = data['name']
+ method.ordinal = data.get('ordinal')
+ method.default = data.get('default')
+ method.parameters = map(
+ lambda parameter: ParameterFromData(kinds, parameter), data['parameters'])
+ return method
+
+def InterfaceToData(interface):
+ return {
+ istr(0, 'name'): interface.name,
+ istr(1, 'methods'): map(MethodToData, interface.methods)
+ }
+
+def InterfaceFromData(kinds, data):
+ interface = mojom.Interface()
+ interface.name = data['name']
+ interface.methods = map(
+ lambda method: MethodFromData(kinds, method), data['methods'])
+ return interface
+
+def ModuleToData(module):
+ return {
+ istr(0, 'name'): module.name,
+ istr(1, 'namespace'): module.namespace,
+ istr(2, 'structs'): map(StructToData, module.structs),
+ istr(3, 'interfaces'): map(InterfaceToData, module.interfaces)
+ }
+
+def ModuleFromData(data):
+ kinds = {}
+ for kind in mojom.PRIMITIVES:
+ kinds[kind.spec] = kind
+
+ module = mojom.Module()
+ module.name = data['name']
+ module.namespace = data['namespace']
+ module.structs = map(
+ lambda struct: StructFromData(kinds, struct), data['structs'])
+ module.interfaces = map(
+ lambda interface: InterfaceFromData(kinds, interface), data['interfaces'])
+ return module
+
+
+
+
diff --git a/mojo/public/bindings/generators/mojom_data_tests.py b/mojo/public/bindings/generators/mojom_data_tests.py
new file mode 100644
index 0000000..c891bd4
--- /dev/null
+++ b/mojo/public/bindings/generators/mojom_data_tests.py
@@ -0,0 +1,84 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import mojom_data
+import mojom_test
+import sys
+
+EXPECT_EQ = mojom_test.EXPECT_EQ
+EXPECT_TRUE = mojom_test.EXPECT_TRUE
+RunTest = mojom_test.RunTest
+
+
+def DeepEquals(d1, d2):
+ if d1 == d2:
+ return True
+ if d2.__class__ != d2.__class__:
+ return False
+ if isinstance(d1, dict):
+ if set(d1.keys()) != set(d2.keys()):
+ return False
+ for key in d1.keys():
+ if not DeepEquals(d1[key], d2[key]):
+ return False
+ return True
+ if isinstance(d1, (list, tuple)):
+ if len(d1) != len(d2):
+ return False
+ for i in range(len(d1)):
+ if not DeepEquals(d1[i], d2[i]):
+ return False
+ return True
+ return False
+
+
+test_dict = {
+ 'name': 'test',
+ 'namespace': 'testspace',
+ 'structs': [{
+ 'name': 'teststruct',
+ 'fields': [
+ {'name': 'testfield1', 'kind': 'i32'},
+ {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}],
+ 'interfaces': [{
+ 'name': 'Server',
+ 'methods': [{
+ 'name': 'Foo',
+ 'parameters': [
+ {'name': 'foo', 'kind': 'i32'},
+ {'name': 'bar', 'kind': 'a:x:teststruct'}],
+ 'ordinal': 42}]}]
+}
+
+
+def TestRead():
+ module = mojom_data.ModuleFromData(test_dict)
+ return mojom_test.TestTestModule(module)
+
+
+def TestWrite():
+ module = mojom_test.BuildTestModule()
+ d = mojom_data.ModuleToData(module)
+ return EXPECT_TRUE(DeepEquals(test_dict, d))
+
+
+def TestWriteRead():
+ module1 = mojom_test.BuildTestModule()
+
+ dict1 = mojom_data.ModuleToData(module1)
+ module2 = mojom_data.ModuleFromData(dict1)
+ return EXPECT_TRUE(mojom_test.ModulesAreEqual(module1, module2))
+
+
+def Main(args):
+ errors = 0
+ errors += RunTest(TestWriteRead)
+ errors += RunTest(TestRead)
+ errors += RunTest(TestWrite)
+
+ return errors
+
+
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/mojo/public/bindings/generators/mojom_test.py b/mojo/public/bindings/generators/mojom_test.py
new file mode 100755
index 0000000..40d9a33
--- /dev/null
+++ b/mojo/public/bindings/generators/mojom_test.py
@@ -0,0 +1,193 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import mojom
+import sys
+import traceback
+
+# Support for writing mojom test cases.
+# RunTest(fn) will execute fn, catching any exceptions. fn should return
+# the number of errors that are encountered.
+#
+# EXPECT_EQ(a, b) and EXPECT_TRUE(b) will print error information if the
+# expectations are not true and return a non zero value. This allows test cases
+# to be written like this
+#
+# def Foo():
+# errors = 0
+# errors += EXPECT_EQ('test', test())
+# ...
+# return errors
+#
+# RunTest(foo)
+
+def FieldsAreEqual(field1, field2):
+ if field1 == field2:
+ return True
+ return field1.name == field2.name and \
+ KindsAreEqual(field1.kind, field2.kind) and \
+ field1.ordinal == field2.ordinal and \
+ field1.default == field2.default
+
+
+def KindsAreEqual(kind1, kind2):
+ if kind1 == kind2:
+ return True
+ if kind1.__class__ != kind2.__class__ or kind1.spec != kind2.spec:
+ return False
+ if kind1.__class__ == mojom.Kind:
+ return kind1.spec == kind2.spec
+ if kind1.__class__ == mojom.Struct:
+ if kind1.name != kind2.name or \
+ kind1.spec != kind2.spec or \
+ len(kind1.fields) != len(kind2.fields):
+ return False
+ for i in range(len(kind1.fields)):
+ if not FieldsAreEqual(kind1.fields[i], kind2.fields[i]):
+ return False
+ return True
+ if kind1.__class__ == mojom.Array:
+ return KindsAreEqual(kind1.kind, kind2.kind)
+ print 'Unknown Kind class: ', kind1.__class__.__name__
+ return False
+
+
+def ParametersAreEqual(parameter1, parameter2):
+ if parameter1 == parameter2:
+ return True
+ return parameter1.name == parameter2.name and \
+ parameter1.ordinal == parameter2.ordinal and \
+ parameter1.default == parameter2.default and \
+ KindsAreEqual(parameter1.kind, parameter2.kind)
+
+
+def MethodsAreEqual(method1, method2):
+ if method1 == method2:
+ return True
+ if method1.name != method2.name or \
+ method1.ordinal != method2.ordinal or \
+ len(method1.parameters) != len(method2.parameters):
+ return False
+ for i in range(len(method1.parameters)):
+ if not ParametersAreEqual(method1.parameters[i], method2.parameters[i]):
+ return False
+ return True
+
+
+def InterfacesAreEqual(interface1, interface2):
+ if interface1 == interface2:
+ return True
+ if interface1.name != interface2.name or \
+ len(interface1.methods) != len(interface2.methods):
+ return False
+ for i in range(len(interface1.methods)):
+ if not MethodsAreEqual(interface1.methods[i], interface2.methods[i]):
+ return False
+ return True
+
+
+def ModulesAreEqual(module1, module2):
+ if module1 == module2:
+ return True
+ if module1.name != module2.name or \
+ module1.namespace != module2.namespace or \
+ len(module1.structs) != len(module2.structs) or \
+ len(module1.interfaces) != len(module2.interfaces):
+ return False
+ for i in range(len(module1.structs)):
+ if not KindsAreEqual(module1.structs[i], module2.structs[i]):
+ return False
+ for i in range(len(module1.interfaces)):
+ if not InterfacesAreEqual(module1.interfaces[i], module2.interfaces[i]):
+ return False
+ return True
+
+
+# Builds and returns a Module suitable for testing/
+def BuildTestModule():
+ module = mojom.Module('test', 'testspace')
+ struct = module.AddStruct('teststruct')
+ struct.AddField('testfield1', mojom.INT32)
+ struct.AddField('testfield2', mojom.Array(mojom.INT32), 42)
+
+ interface = module.AddInterface('Server')
+ method = interface.AddMethod('Foo', 42)
+ method.AddParameter('foo', mojom.INT32)
+ method.AddParameter('bar', mojom.Array(struct))
+
+ return module
+
+
+# Tests if |module| is as built by BuildTestModule(). Returns the number of
+# errors
+def TestTestModule(module):
+ errors = 0
+
+ errors += EXPECT_EQ('test', module.name)
+ errors += EXPECT_EQ('testspace', module.namespace)
+ errors += EXPECT_EQ(1, len(module.structs))
+ errors += EXPECT_EQ('teststruct', module.structs[0].name)
+ errors += EXPECT_EQ(2, len(module.structs[0].fields))
+ errors += EXPECT_EQ('testfield1', module.structs[0].fields[0].name)
+ errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[0].kind)
+ errors += EXPECT_EQ('testfield2', module.structs[0].fields[1].name)
+ errors += EXPECT_EQ(mojom.Array, module.structs[0].fields[1].kind.__class__)
+ errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[1].kind.kind)
+
+ errors += EXPECT_EQ(1, len(module.interfaces))
+ errors += EXPECT_EQ('Server', module.interfaces[0].name)
+ errors += EXPECT_EQ(1, len(module.interfaces[0].methods))
+ errors += EXPECT_EQ('Foo', module.interfaces[0].methods[0].name)
+ errors += EXPECT_EQ(2, len(module.interfaces[0].methods[0].parameters))
+ errors += EXPECT_EQ('foo', module.interfaces[0].methods[0].parameters[0].name)
+ errors += EXPECT_EQ(mojom.INT32,
+ module.interfaces[0].methods[0].parameters[0].kind)
+ errors += EXPECT_EQ('bar', module.interfaces[0].methods[0].parameters[1].name)
+ errors += EXPECT_EQ(
+ mojom.Array,
+ module.interfaces[0].methods[0].parameters[1].kind.__class__)
+ errors += EXPECT_EQ(
+ module.structs[0],
+ module.interfaces[0].methods[0].parameters[1].kind.kind)
+ return errors
+
+
+def PrintFailure(string):
+ stack = traceback.extract_stack()
+ frame = stack[len(stack)-3]
+ sys.stderr.write("ERROR at %s:%d, %s\n" % (frame[0], frame[1], string))
+ print "Traceback:"
+ for line in traceback.format_list(stack[:len(stack)-2]):
+ sys.stderr.write(line)
+
+
+def EXPECT_EQ(a, b):
+ if a != b:
+ PrintFailure("%s != %s" % (a, b))
+ return 1
+ return 0
+
+
+def EXPECT_TRUE(a):
+ if not a:
+ PrintFailure('Expecting True')
+ return 1
+ return 0
+
+
+def RunTest(fn):
+ sys.stdout.write('Running %s...' % fn.__name__)
+ success = True;
+ try:
+ errors = fn()
+ except:
+ traceback.print_exc(sys.stderr)
+ errors = 1
+ if errors == 0:
+ sys.stdout.write('OK\n')
+ elif errors == 1:
+ sys.stdout.write('1 ERROR\n')
+ else:
+ sys.stdout.write('%d ERRORS\n' % errors)
+ return errors
diff --git a/mojo/public/bindings/generators/mojom_tests.py b/mojo/public/bindings/generators/mojom_tests.py
new file mode 100755
index 0000000..a5a5ed0
--- /dev/null
+++ b/mojo/public/bindings/generators/mojom_tests.py
@@ -0,0 +1,33 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import mojom_test
+import sys
+
+EXPECT_EQ = mojom_test.EXPECT_EQ
+EXPECT_TRUE = mojom_test.EXPECT_TRUE
+RunTest = mojom_test.RunTest
+ModulesAreEqual = mojom_test.ModulesAreEqual
+BuildTestModule = mojom_test.BuildTestModule
+TestTestModule = mojom_test.TestTestModule
+
+
+def BuildAndTestModule():
+ return TestTestModule(BuildTestModule())
+
+
+def TestModulesEqual():
+ return EXPECT_TRUE(ModulesAreEqual(BuildTestModule(), BuildTestModule()))
+
+
+def Main(args):
+ errors = 0
+ errors += RunTest(BuildAndTestModule)
+ errors += RunTest(TestModulesEqual)
+
+ return errors
+
+
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/mojo/public/bindings/generators/run_mojom_tests.py b/mojo/public/bindings/generators/run_mojom_tests.py
new file mode 100755
index 0000000..ad41183
--- /dev/null
+++ b/mojo/public/bindings/generators/run_mojom_tests.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+""" Test runner for Mojom """
+
+import subprocess
+import sys
+
+def TestMojom(testname, args):
+ print '\nRunning unit tests for %s.' % testname
+ try:
+ args = [sys.executable, testname] + args
+ subprocess.check_call(args, stdout=sys.stdout)
+ print 'Succeeded'
+ return 0
+ except subprocess.CalledProcessError as err:
+ print 'Failed with %s.' % str(err)
+ return 1
+
+
+def main(args):
+ errors = 0
+ errors += TestMojom('mojom_tests.py', ['--test'])
+ errors += TestMojom('mojom_data_tests.py', ['--test'])
+
+ if errors:
+ print '\nFailed tests.'
+ return errors
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
+