text: import datetime
  import os.path
  import re
  import string
  import sys
  
  from rust import RustHelperBackend
  from stone import ir
  from stone.backends.python_helpers import fmt_class as fmt_py_class
  
  
  class Permissions(object):
      @property
      def permissions(self):
          # For generating tests, make sure we include any internal
          # fields/structs if we're using internal specs. If we're not using
          # internal specs, this is a no-op, so just do it all the time.  Note
          # that this only needs to be done for json serialization, the struct
          # definitions will include all fields, all the time.
          return ['internal']
  
  
  class TestBackend(RustHelperBackend):
      def __init__(self, target_folder_path, args):
          super(TestBackend, self).__init__(target_folder_path, args)
  
          # Don't import other generators until here, otherwise stone.cli will
          # call them with its own arguments, in addition to the TestBackend.
          from stone.backends.python_types import PythonTypesBackend
          self.target_path = target_folder_path
          self.ref_path = os.path.join(target_folder_path, 'reference')
          self.reference = PythonTypesBackend(self.ref_path, args + ["--package", "reference"])
          self.reference_impls = {}
  
      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)
          sys.path.insert(1, "stone")
          from stone.backends.python_rsrc.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()
  
      def _emit_header(self):
          self.emit(u'// DO NOT EDIT')
          self.emit(u'// This file was generated by Stone')
          self.emit()
          self.emit(u'#![allow(bad_style)]')
          self.emit()
          self.emit(u'#![allow(')
          self.emit(u'    clippy::float_cmp,')
          self.emit(u'    clippy::unreadable_literal,')
          self.emit(u'    clippy::cognitive_complexity,')
          self.emit(u')]')
          self.emit()
  
      def _test_fn(self, name):
          self.emit(u'#[test]')
          return self.emit_rust_function_def(u'test_' + name)
  
  
  class TestField(object):
      def __init__(self, name, python_value, test_value, stone_type, option):
          self.name = name
          self.value = python_value
          self.test_value = test_value
          self.typ = stone_type
          self.option = option
  
      def emit_assert(self, codegen, expression_path):
          extra = ('.' + self.name) if self.name else ''
          if self.option:
              expression = '(*' + expression_path + extra + '.as_ref().unwrap())'
          else:
              expression = expression_path + extra
  
          if isinstance(self.test_value, TestValue):
              self.test_value.emit_asserts(codegen, expression)
          elif ir.is_string_type(self.typ):
              codegen.emit(u'assert_eq!({}.as_str(), r#"{}"#);'.format(
                  expression, self.value))
          elif ir.is_numeric_type(self.typ):
              codegen.emit(u'assert_eq!({}, {});'.format(
                  expression, self.value))
          elif ir.is_boolean_type(self.typ):
              codegen.emit(u'assert_eq!({}, {});'.format(
                  expression, 'true' if self.value else 'false'))
          elif ir.is_timestamp_type(self.typ):
              codegen.emit(u'assert_eq!({}.as_str(), "{}");'.format(
                  expression, self.value.strftime(self.typ.format)))
          elif ir.is_bytes_type(self.typ):
              codegen.emit(u'assert_eq!(&{}, &[{}]);'.format(
                  expression, ",".join(str(x) for x in self.value)))
          else:
              raise RuntimeError(u'Error: assetion unhandled for type {} of field {} with value {}'
                                 .format(self.typ, self.name, self.value))
  
  
  class TestValue(object):
      def __init__(self, rust_generator):
          self.rust_generator = rust_generator
          self.fields = []
          self.value = None
  
      def emit_asserts(self, codegen, expression_path):
          raise NotImplementedError('you\'re supposed to implement TestValue.emit_asserts')
  
  
  class TestStruct(TestValue):
      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 emit_asserts(self, codegen, expression_path):
          for field in self.fields:
              field.emit_assert(codegen, expression_path)
  
  
  class TestUnion(TestValue):
      def __init__(self, rust_generator, stone_type, reference_impls, variant):
          super(TestUnion, self).__init__(rust_generator)
          self._stone_type = stone_type
          self._reference_impls = reference_impls
          self._rust_name = rust_generator.enum_name(stone_type)
          self._rust_variant_name = rust_generator.enum_variant_name_raw(variant.name)
          self._rust_namespace_name = rust_generator.namespace_name(stone_type.namespace)
          self._variant_type = variant.data_type
  
          self._inner_value = make_test_field(
              None, self._variant_type, rust_generator, reference_impls)
  
          if self._inner_value is None:
              raise RuntimeError(u'Error generating union variant value for {}.{}'
                                 .format(stone_type.name, variant.name))
  
          self.value = self.get_from_inner_value(variant.name, self._inner_value)
  
      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 is_open(self):
          return len(self._stone_type.all_fields) > 1
  
      def emit_asserts(self, codegen, expression_path):
          if expression_path[0] == '(' and expression_path[-1] == ')':
                  expression_path = expression_path[1:-1]  # strip off superfluous parens
  
          with codegen.block(u'match {}'.format(expression_path)):
              if ir.is_void_type(self._variant_type):
                  codegen.emit(u'::dropbox_sdk::{}::{}::{} => (),'.format(
                      self._rust_namespace_name,
                      self._rust_name,
                      self._rust_variant_name))
              else:
                  with codegen.block(u'::dropbox_sdk::{}::{}::{}(ref v) =>'.format(
                          self._rust_namespace_name,
                          self._rust_name,
                          self._rust_variant_name)):
                      self._inner_value.emit_assert(codegen, '(*v)')
  
              if self.is_open():
                  codegen.emit(u'_ => panic!("wrong variant")')
  
  
  class TestPolymorphicStruct(TestUnion):
      def get_from_inner_value(self, variant_name, generated_field):
          return generated_field.value
  
      def is_open(self):
          return len(self._stone_type.get_enumerated_subtypes()) > 1
  
  
  class TestList(TestValue):
      def __init__(self, rust_generator, stone_type, reference_impls):
          super(TestList, self).__init__(rust_generator)
          self._stone_type = stone_type
          self._reference_impls = reference_impls
  
          self._inner_value = make_test_field(None, stone_type, rust_generator, reference_impls)
          if self._inner_value is None:
              raise RuntimeError(u'Error generating value for list of {}'.format(stone_type.name))
  
          self.value = self._inner_value.value
  
      def emit_asserts(self, codegen, expression_path):
          self._inner_value.emit_assert(codegen, expression_path + '[0]')
  
  
  class TestMap(TestValue):
      def __init__(self, rust_generator, stone_type, reference_impls):
          super(TestMap, self).__init__(rust_generator)
          self._stone_type = stone_type
          self._reference_impls = reference_impls
          self._key_value = make_test_field(None, stone_type.key_data_type, rust_generator,
                                            reference_impls)
          self._val_value = make_test_field(None, stone_type.value_data_type, rust_generator,
                                            reference_impls)
          self.value = {self._key_value.value: self._val_value.value}
  
      def emit_asserts(self, codegen, expression_path):
          key_str = u'["{}"]'.format(self._key_value.value)
          self._val_value.emit_assert(codegen, expression_path + key_str)
  
  
  def make_test_field(field_name, stone_type, rust_generator, reference_impls):
      rust_name = rust_generator.field_name_raw(field_name) if field_name is not None else None
      typ, option = ir.unwrap_nullable(stone_type)
  
      inner = None
      value = None
      if ir.is_struct_type(typ):
          if typ.has_enumerated_subtypes():
              variant = typ.get_enumerated_subtypes()[0]
              inner = TestPolymorphicStruct(rust_generator, typ, reference_impls, variant)
          else:
              inner = TestStruct(rust_generator, typ, reference_impls)
          value = inner.value
      elif ir.is_union_type(typ):
          # pick the first tag
          # TODO(wfraser) generate tests for them ALL!
          if len(typ.fields) == 0:
              # there must be a parent type; go for it
              variant = typ.all_fields[0]
          else:
              variant = typ.fields[0]
          inner = TestUnion(rust_generator, typ, reference_impls, variant)
          value = inner.value
      elif ir.is_list_type(typ):
          inner = TestList(rust_generator, typ.data_type, reference_impls)
          value = [inner.value]
      elif ir.is_map_type(typ):
          inner = TestMap(rust_generator, typ, reference_impls)
          value = inner.value
      elif ir.is_string_type(typ):
          if typ.pattern:
              value = Unregex(typ.pattern, typ.min_length).generate()
          elif typ.min_length:
              value = 'a' * typ.min_length
          else:
              value = 'something'
      elif ir.is_numeric_type(typ):
          value = typ.max_value or typ.maximum or 1e307
      elif ir.is_boolean_type(typ):
          value = True
      elif ir.is_timestamp_type(typ):
          value = datetime.datetime.utcfromtimestamp(2**33 - 1)
      elif ir.is_bytes_type(typ):
          value = bytes([0,1,2,3,4,5])
      elif not ir.is_void_type(typ):
          raise RuntimeError(u'Error: unhandled field type of {}: {}'.format(field_name, typ))
      return TestField(rust_name, value, inner, typ, option)
  
  
  class Unregex(object):
      """
      Generate a minimal string that passes a regex and optionally is of a given
      minimum length.
      """
      def __init__(self, regex_string, min_len=None):
          self._min_len = min_len
          self._group_refs = {}
          self._tokens = re.sre_parse.parse(regex_string)
  
      def generate(self):
          return self._generate(self._tokens)
  
      def _generate(self, tokens):
          result = ''
          for (opcode, argument) in tokens:
              opcode = str(opcode).lower()
              if opcode == 'literal':
                  result += chr(argument)
              elif opcode == 'at':
                  pass  # start or end anchor; nothing to add
              elif opcode == 'in':
                  if argument[0][0] == 'negate':
                      rejects = []
                      for opcode, reject in argument[1:]:
                          if opcode == 'literal':
                              rejects.append(chr(reject))
                          elif opcode == 'range':
                              for i in range(reject[0], reject[1]):
                                  rejects.append(chr(i))
                      choices = list(set(string.printable)
                                     .difference(string.whitespace)
                                     .difference(rejects))
                      result += choices[0]
                  else:
                      result += self._generate([argument[0]])
              elif opcode == 'any':
                  result += '*'
              elif opcode == 'range':
                  result += chr(argument[0])
              elif opcode == 'branch':
                  result += self._generate(argument[1][0])
              elif opcode == 'subpattern':
                  group_number, add_flags, del_flags, sub_tokens = argument
                  sub_result = self._generate(sub_tokens)
                  self._group_refs[group_number] = sub_result
                  result += sub_result
              elif opcode == 'groupref':
                  result += self._group_refs[argument]
              elif opcode == 'min_repeat' or opcode == 'max_repeat':
                  min_repeat, max_repeat, sub_tokens = argument
                  if self._min_len:
                      n = max(min_repeat, min(self._min_len, max_repeat))
                  else:
                      n = min_repeat
                  sub_result = self._generate(sub_tokens) if n != 0 else ''
                  result += str(sub_result) * n
              elif opcode == 'category':
                  if argument == 'category_digit':
                      result += '0'
                  elif argument == 'category_not_space':
                      result += '!'
                  else:
                      raise NotImplementedError('category {}'.format(argument))
              elif opcode == 'assert_not':
                  # let's just hope for the best...
                  pass
              elif opcode == 'assert' or opcode == 'negate':
                  # note: 'negate' is handled in the 'in' opcode
                  raise NotImplementedError('regex opcode {} not implemented'.format(opcode))
              else:
                  raise NotImplementedError('unknown regex opcode: {}'.format(opcode))
          return result
  