def __init__(self, rust_generator, stone_type, reference_impls): super(TestStruct, self).__init__(rust_generator) if stone_type.has_enumerated_subtypes(): stone_type = stone_type.get_enumerated_subtypes()[0].data_type self._stone_type = stone_type self._reference_impls = reference_impls py_name = fmt_py_class(stone_type.name) try: self.value = reference_impls[ stone_type.namespace.name].__dict__[py_name]() except Exception as e: raise RuntimeError(u'Error instantiating value for {}: {}'.format( stone_type.name, e)) for field in stone_type.all_fields: field_value = make_test_field(field.name, field.data_type, rust_generator, reference_impls) if field_value is None: raise RuntimeError( u'Error: incomplete type generated: {}'.format( stone_type.name)) self.fields.append(field_value) try: setattr(self.value, field.name, field_value.value) except Exception as e: raise RuntimeError( u'Error generating value for {}.{}: {}'.format( stone_type.name, field.name, e))
def get_from_inner_value(self, variant_name, generated_field): pyname = fmt_py_class(self._stone_type.name) try: return self._reference_impls[self._stone_type.namespace.name] \ .__dict__[pyname](variant_name, generated_field.value) except Exception as e: raise RuntimeError(u'Error generating value for {}.{}: {}'.format( self._stone_type.name, variant_name, e))
def _emit_tests(self, ns, typ, json_encode): ns_name = self.namespace_name(ns) type_name = self.struct_name(typ) # The general idea here is to instantiate each type using the reference # Python code, put some random data in the fields, serialize it to # JSON, emit the JSON into the Rust test, have Rust deserialize it, and # emit assertions that the fields match. Then have Rust re-serialize to # JSON and desereialize it again, then check the fields of the # newly-deserialized struct. This verifies Rust's serializer. for test_value in self.make_test_value(typ): pyname = fmt_py_class(typ.name) json = json_encode( self.reference_impls[ns.name].__dict__[pyname + '_validator'], test_value.value, Permissions()) # "other" is a hardcoded, special-cased tag used by Stone for the # catch-all variant of open unions. Let's rewrite it to something # else, to test that the unknown variant logic actually works. # Unfortunately this requires mega-hax of rewriting the JSON text, # because the Python serializer won't let us give an arbitrary # variant name. json = json.replace( '{".tag": "other"', '{".tag": "dropbox-sdk-rust-bogus-test-variant"') with self._test_fn(type_name + test_value.test_suffix()): self.emit(u'let json = r#"{}"#;'.format(json)) self.emit( u'let x = ::serde_json::from_str::<::dropbox_sdk::{}::{}>(json).unwrap();' .format(ns_name, self.struct_name(typ))) test_value.emit_asserts(self, 'x') self.emit(u'assert_eq!(x, x.clone());') if test_value.is_serializable(): # now serialize it back to JSON, deserialize it again, and # test it again. self.emit() self.emit( u'let json2 = ::serde_json::to_string(&x).unwrap();') de = u'::serde_json::from_str::<::dropbox_sdk::{}::{}>(&json2).unwrap()' \ .format(ns_name, self.struct_name(typ)) if typ.all_fields: self.emit(u'let x2 = {};'.format(de)) test_value.emit_asserts(self, 'x2') self.emit(u'assert_eq!(x, x2);') else: self.emit(u'{};'.format(de)) else: # assert that serializing it returns an error self.emit( u'assert!(::serde_json::to_string(&x).is_err());') self.emit()
def generate(self, api): print(u'Generating Python reference code') self.reference.generate(api) with self.output_to_relative_path('reference/__init__.py'): self.emit(u'# this is the Stone-generated reference Python SDK') print(u'Loading reference code:') sys.path.insert(0, self.target_path) from reference.stone_serializers import json_encode for ns in api.namespaces: print('\t' + ns) python_ns = ns if ns == 'async': # hack to work around 'async' being a Python3 keyword python_ns = 'async_' self.reference_impls[ns] = __import__('reference.'+python_ns).__dict__[python_ns] print(u'Generating test code') for ns in api.namespaces.values(): ns_name = self.namespace_name(ns) with self.output_to_relative_path(ns_name + '.rs'): self._emit_header() for typ in ns.data_types: type_name = self.struct_name(typ) # the general idea here is to instantiate each type using # the reference Python code, put some random data in the # fields, serialize it to JSON, emit the JSON into the Rust # test, have Rust deserialize it, and emit assertions that # the fields match. Then have Rust re-serialize to JSON and # desereialize it again, then check the fields of the # newly-deserialized struct. This verifies Rust's # serializer. is_serializable = True test_value = None if ir.is_struct_type(typ): if typ.has_enumerated_subtypes(): # TODO: generate tests for all variants # for now, just pick the first variant variant = typ.get_enumerated_subtypes()[0] test_value = TestPolymorphicStruct( self, typ, self.reference_impls, variant) else: test_value = TestStruct(self, typ, self.reference_impls) elif ir.is_union_type(typ): # TODO: generate tests for all variants # for now, just pick the first variant # prefer choosing from this type and not the parent if we can variants = [field for field in typ.fields if not field.catch_all] if len(variants) == 0: # fall back to parent type's variants variants = [field for field in typ.all_fields if not field.catch_all] if not variants: # Rust code will refuse to serialize a type with no variants (or only # the catch-all variant), so don't bother testing that is_serializable = False variant = typ.all_fields[0] # this assumes there's at least one else: variant = variants[0] test_value = TestUnion(self, typ, self.reference_impls, variant) else: raise RuntimeError(u'ERROR: type {} is neither struct nor union' .format(typ)) pyname = fmt_py_class(typ.name) json = json_encode( self.reference_impls[ns.name].__dict__[pyname + '_validator'], test_value.value, Permissions()) with self._test_fn(type_name): self.emit(u'let json = r#"{}"#;'.format(json)) self.emit(u'let x = ::serde_json::from_str::<::dropbox_sdk::{}::{}>(json).unwrap();' .format(ns_name, self.struct_name(typ))) test_value.emit_asserts(self, 'x') if is_serializable: # now serialize it back to JSON, deserialize it again, and test # it again. self.emit() self.emit(u'let json2 = ::serde_json::to_string(&x).unwrap();') de = u'::serde_json::from_str::<::dropbox_sdk::{}::{}>(&json2).unwrap()' \ .format(ns_name, self.struct_name(typ)) if typ.all_fields: self.emit(u'let x2 = {};'.format(de)) test_value.emit_asserts(self, 'x2') else: self.emit(u'{};'.format(de)) else: # assert that serializing it returns an error self.emit(u'assert!(::serde_json::to_string(&x).is_err());') self.emit() # for typ # .rs test file # for ns with self.output_to_relative_path('mod.rs'): self._emit_header() for ns in api.namespaces: self.emit(u'#[cfg(feature = "dbx_{}")]'.format(ns)) self.emit(u'mod {};'.format(self.namespace_name_raw(ns))) self.emit()