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()
示例#4
0
    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()