#!/usr/bin/env python
#
# Copyright 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""Tests for protorpc.messages."""
import six

__author__ = 'rafek@google.com (Rafe Kaplan)'


import pickle
import re
import sys
import types
import unittest

from protorpc import descriptor
from protorpc import message_types
from protorpc import messages
from protorpc import test_util


class ModuleInterfaceTest(test_util.ModuleInterfaceTest,
                          test_util.TestCase):

  MODULE = messages


class ValidationErrorTest(test_util.TestCase):

  def testStr_NoFieldName(self):
    """Test string version of ValidationError when no name provided."""
    self.assertEquals('Validation error',
                      str(messages.ValidationError('Validation error')))

  def testStr_FieldName(self):
    """Test string version of ValidationError when no name provided."""
    validation_error = messages.ValidationError('Validation error')
    validation_error.field_name = 'a_field'
    self.assertEquals('Validation error', str(validation_error))


class EnumTest(test_util.TestCase):

  def setUp(self):
    """Set up tests."""
    # Redefine Color class in case so that changes to it (an error) in one test
    # does not affect other tests.
    global Color
    class Color(messages.Enum):
      RED = 20
      ORANGE = 2
      YELLOW = 40
      GREEN = 4
      BLUE = 50
      INDIGO = 5
      VIOLET = 80

  def testNames(self):
    """Test that names iterates over enum names."""
    self.assertEquals(
        set(['BLUE', 'GREEN', 'INDIGO', 'ORANGE', 'RED', 'VIOLET', 'YELLOW']),
        set(Color.names()))

  def testNumbers(self):
    """Tests that numbers iterates of enum numbers."""
    self.assertEquals(set([2, 4, 5, 20, 40, 50, 80]), set(Color.numbers()))

  def testIterate(self):
    """Test that __iter__ iterates over all enum values."""
    self.assertEquals(set(Color),
                      set([Color.RED,
                           Color.ORANGE,
                           Color.YELLOW,
                           Color.GREEN,
                           Color.BLUE,
                           Color.INDIGO,
                           Color.VIOLET]))

  def testNaturalOrder(self):
    """Test that natural order enumeration is in numeric order."""
    self.assertEquals([Color.ORANGE,
                       Color.GREEN,
                       Color.INDIGO,
                       Color.RED,
                       Color.YELLOW,
                       Color.BLUE,
                       Color.VIOLET],
                      sorted(Color))

  def testByName(self):
    """Test look-up by name."""
    self.assertEquals(Color.RED, Color.lookup_by_name('RED'))
    self.assertRaises(KeyError, Color.lookup_by_name, 20)
    self.assertRaises(KeyError, Color.lookup_by_name, Color.RED)

  def testByNumber(self):
    """Test look-up by number."""
    self.assertRaises(KeyError, Color.lookup_by_number, 'RED')
    self.assertEquals(Color.RED, Color.lookup_by_number(20))
    self.assertRaises(KeyError, Color.lookup_by_number, Color.RED)

  def testConstructor(self):
    """Test that constructor look-up by name or number."""
    self.assertEquals(Color.RED, Color('RED'))
    self.assertEquals(Color.RED, Color(u'RED'))
    self.assertEquals(Color.RED, Color(20))
    if six.PY2:
        self.assertEquals(Color.RED, Color(long(20)))
    self.assertEquals(Color.RED, Color(Color.RED))
    self.assertRaises(TypeError, Color, 'Not exists')
    self.assertRaises(TypeError, Color, 'Red')
    self.assertRaises(TypeError, Color, 100)
    self.assertRaises(TypeError, Color, 10.0)

  def testLen(self):
    """Test that len function works to count enums."""
    self.assertEquals(7, len(Color))

  def testNoSubclasses(self):
    """Test that it is not possible to sub-class enum classes."""
    def declare_subclass():
      class MoreColor(Color):
        pass
    self.assertRaises(messages.EnumDefinitionError,
                      declare_subclass)

  def testClassNotMutable(self):
    """Test that enum classes themselves are not mutable."""
    self.assertRaises(AttributeError,
                      setattr,
                      Color,
                      'something_new',
                      10)

  def testInstancesMutable(self):
    """Test that enum instances are not mutable."""
    self.assertRaises(TypeError,
                      setattr,
                      Color.RED,
                      'something_new',
                      10)

  def testDefEnum(self):
    """Test def_enum works by building enum class from dict."""
    WeekDay = messages.Enum.def_enum({'Monday': 1,
                                      'Tuesday': 2,
                                      'Wednesday': 3,
                                      'Thursday': 4,
                                      'Friday': 6,
                                      'Saturday': 7,
                                      'Sunday': 8},
                                     'WeekDay')
    self.assertEquals('Wednesday', WeekDay(3).name)
    self.assertEquals(6, WeekDay('Friday').number)
    self.assertEquals(WeekDay.Sunday, WeekDay('Sunday'))

  def testNonInt(self):
    """Test that non-integer values rejection by enum def."""
    self.assertRaises(messages.EnumDefinitionError,
                      messages.Enum.def_enum,
                      {'Bad': '1'},
                      'BadEnum')

  def testNegativeInt(self):
    """Test that negative numbers rejection by enum def."""
    self.assertRaises(messages.EnumDefinitionError,
                      messages.Enum.def_enum,
                      {'Bad': -1},
                      'BadEnum')

  def testLowerBound(self):
    """Test that zero is accepted by enum def."""
    class NotImportant(messages.Enum):
      """Testing for value zero"""
      VALUE = 0

    self.assertEquals(0, int(NotImportant.VALUE))

  def testTooLargeInt(self):
    """Test that numbers too large are rejected."""
    self.assertRaises(messages.EnumDefinitionError,
                      messages.Enum.def_enum,
                      {'Bad': (2 ** 29)},
                      'BadEnum')

  def testRepeatedInt(self):
    """Test duplicated numbers are forbidden."""
    self.assertRaises(messages.EnumDefinitionError,
                      messages.Enum.def_enum,
                      {'Ok': 1, 'Repeated': 1},
                      'BadEnum')

  def testStr(self):
    """Test converting to string."""
    self.assertEquals('RED', str(Color.RED))
    self.assertEquals('ORANGE', str(Color.ORANGE))

  def testInt(self):
    """Test converting to int."""
    self.assertEquals(20, int(Color.RED))
    self.assertEquals(2, int(Color.ORANGE))

  def testRepr(self):
    """Test enum representation."""
    self.assertEquals('Color(RED, 20)', repr(Color.RED))
    self.assertEquals('Color(YELLOW, 40)', repr(Color.YELLOW))

  def testDocstring(self):
    """Test that docstring is supported ok."""
    class NotImportant(messages.Enum):
      """I have a docstring."""

      VALUE1 = 1

    self.assertEquals('I have a docstring.', NotImportant.__doc__)

  def testDeleteEnumValue(self):
    """Test that enum values cannot be deleted."""
    self.assertRaises(TypeError, delattr, Color, 'RED')

  def testEnumName(self):
    """Test enum name."""
    module_name = test_util.get_module_name(EnumTest)
    self.assertEquals('%s.Color' % module_name, Color.definition_name())
    self.assertEquals(module_name, Color.outer_definition_name())
    self.assertEquals(module_name, Color.definition_package())

  def testDefinitionName_OverrideModule(self):
    """Test enum module is overriden by module package name."""
    global package
    try:
      package = 'my.package'
      self.assertEquals('my.package.Color', Color.definition_name())
      self.assertEquals('my.package', Color.outer_definition_name())
      self.assertEquals('my.package', Color.definition_package())
    finally:
      del package

  def testDefinitionName_NoModule(self):
    """Test what happens when there is no module for enum."""
    class Enum1(messages.Enum):
      pass

    original_modules = sys.modules
    sys.modules = dict(sys.modules)
    try:
      del sys.modules[__name__]
      self.assertEquals('Enum1', Enum1.definition_name())
      self.assertEquals(None, Enum1.outer_definition_name())
      self.assertEquals(None, Enum1.definition_package())
      self.assertEquals(six.text_type, type(Enum1.definition_name()))
    finally:
      sys.modules = original_modules

  def testDefinitionName_Nested(self):
    """Test nested Enum names."""
    class MyMessage(messages.Message):

      class NestedEnum(messages.Enum):

        pass

      class NestedMessage(messages.Message):

        class NestedEnum(messages.Enum):

          pass

    module_name = test_util.get_module_name(EnumTest)
    self.assertEquals('%s.MyMessage.NestedEnum' % module_name,
                      MyMessage.NestedEnum.definition_name())
    self.assertEquals('%s.MyMessage' % module_name,
                      MyMessage.NestedEnum.outer_definition_name())
    self.assertEquals(module_name,
                      MyMessage.NestedEnum.definition_package())

    self.assertEquals('%s.MyMessage.NestedMessage.NestedEnum' % module_name,
                      MyMessage.NestedMessage.NestedEnum.definition_name())
    self.assertEquals(
      '%s.MyMessage.NestedMessage' % module_name,
      MyMessage.NestedMessage.NestedEnum.outer_definition_name())
    self.assertEquals(module_name,
                      MyMessage.NestedMessage.NestedEnum.definition_package())

  def testMessageDefinition(self):
    """Test that enumeration knows its enclosing message definition."""
    class OuterEnum(messages.Enum):
      pass

    self.assertEquals(None, OuterEnum.message_definition())

    class OuterMessage(messages.Message):

      class InnerEnum(messages.Enum):
        pass

    self.assertEquals(OuterMessage, OuterMessage.InnerEnum.message_definition())

  def testComparison(self):
    """Test comparing various enums to different types."""
    class Enum1(messages.Enum):
      VAL1 = 1
      VAL2 = 2

    class Enum2(messages.Enum):
      VAL1 = 1

    self.assertEquals(Enum1.VAL1, Enum1.VAL1)
    self.assertNotEquals(Enum1.VAL1, Enum1.VAL2)
    self.assertNotEquals(Enum1.VAL1, Enum2.VAL1)
    self.assertNotEquals(Enum1.VAL1, 'VAL1')
    self.assertNotEquals(Enum1.VAL1, 1)
    self.assertNotEquals(Enum1.VAL1, 2)
    self.assertNotEquals(Enum1.VAL1, None)
    self.assertNotEquals(Enum1.VAL1, Enum2.VAL1)

    self.assertTrue(Enum1.VAL1 < Enum1.VAL2)
    self.assertTrue(Enum1.VAL2 > Enum1.VAL1)

    self.assertNotEquals(1, Enum2.VAL1)

  def testPickle(self):
    """Testing pickling and unpickling of Enum instances."""
    colors = list(Color)
    unpickled = pickle.loads(pickle.dumps(colors))
    self.assertEquals(colors, unpickled)
    # Unpickling shouldn't create new enum instances.
    for i, color in enumerate(colors):
      self.assertTrue(color is unpickled[i])


class FieldListTest(test_util.TestCase):

  def setUp(self):
    self.integer_field = messages.IntegerField(1, repeated=True)

  def testConstructor(self):
    self.assertEquals([1, 2, 3],
                      messages.FieldList(self.integer_field, [1, 2, 3]))
    self.assertEquals([1, 2, 3],
                      messages.FieldList(self.integer_field, (1, 2, 3)))
    self.assertEquals([], messages.FieldList(self.integer_field, []))

  def testNone(self):
    self.assertRaises(TypeError, messages.FieldList, self.integer_field, None)

  def testDoNotAutoConvertString(self):
    string_field = messages.StringField(1, repeated=True)
    self.assertRaises(messages.ValidationError,
                      messages.FieldList, string_field, 'abc')

  def testConstructorCopies(self):
    a_list = [1, 3, 6]
    field_list = messages.FieldList(self.integer_field, a_list)
    self.assertFalse(a_list is field_list)
    self.assertFalse(field_list is
                     messages.FieldList(self.integer_field, field_list))

  def testNonRepeatedField(self):
    self.assertRaisesWithRegexpMatch(
      messages.FieldDefinitionError,
      'FieldList may only accept repeated fields',
      messages.FieldList,
      messages.IntegerField(1),
      [])

  def testConstructor_InvalidValues(self):
    self.assertRaisesWithRegexpMatch(
      messages.ValidationError,
      re.escape("Expected type %r "
                "for IntegerField, found 1 (type %r)"
               % (six.integer_types, str)),
      messages.FieldList, self.integer_field, ["1", "2", "3"])

  def testConstructor_Scalars(self):
    self.assertRaisesWithRegexpMatch(
      messages.ValidationError,
      "IntegerField is repeated. Found: 3",
      messages.FieldList, self.integer_field, 3)

    self.assertRaisesWithRegexpMatch(
      messages.ValidationError,
      "IntegerField is repeated. Found: <(list[_]?|sequence)iterator object",
      messages.FieldList, self.integer_field, iter([1, 2, 3]))

  def testSetSlice(self):
    field_list = messages.FieldList(self.integer_field, [1, 2, 3, 4, 5])
    field_list[1:3] = [10, 20]
    self.assertEquals([1, 10, 20, 4, 5], field_list)

  def testSetSlice_InvalidValues(self):
    field_list = messages.FieldList(self.integer_field, [1, 2, 3, 4, 5])

    def setslice():
      field_list[1:3] = ['10', '20']

    msg_re = re.escape("Expected type %r "
                       "for IntegerField, found 10 (type %r)"
                             % (six.integer_types, str))
    self.assertRaisesWithRegexpMatch(
      messages.ValidationError,
      msg_re,
      setslice)

  def testSetItem(self):
    field_list = messages.FieldList(self.integer_field, [2])
    field_list[0] = 10
    self.assertEquals([10], field_list)

  def testSetItem_InvalidValues(self):
    field_list = messages.FieldList(self.integer_field, [2])

    def setitem():
      field_list[0] = '10'
    self.assertRaisesWithRegexpMatch(
      messages.ValidationError,
      re.escape("Expected type %r "
                "for IntegerField, found 10 (type %r)"
               % (six.integer_types, str)),
      setitem)

  def testAppend(self):
    field_list = messages.FieldList(self.integer_field, [2])
    field_list.append(10)
    self.assertEquals([2, 10], field_list)

  def testAppend_InvalidValues(self):
    field_list = messages.FieldList(self.integer_field, [2])
    field_list.name = 'a_field'

    def append():
      field_list.append('10')
    self.assertRaisesWithRegexpMatch(
      messages.ValidationError,
      re.escape("Expected type %r "
                "for IntegerField, found 10 (type %r)"
               % (six.integer_types, str)),
      append)

  def testExtend(self):
    field_list = messages.FieldList(self.integer_field, [2])
    field_list.extend([10])
    self.assertEquals([2, 10], field_list)

  def testExtend_InvalidValues(self):
    field_list = messages.FieldList(self.integer_field, [2])

    def extend():
      field_list.extend(['10'])
    self.assertRaisesWithRegexpMatch(
      messages.ValidationError,
      re.escape("Expected type %r "
                "for IntegerField, found 10 (type %r)"
               % (six.integer_types, str)),
      extend)

  def testInsert(self):
    field_list = messages.FieldList(self.integer_field, [2, 3])
    field_list.insert(1, 10)
    self.assertEquals([2, 10, 3], field_list)

  def testInsert_InvalidValues(self):
    field_list = messages.FieldList(self.integer_field, [2, 3])

    def insert():
      field_list.insert(1, '10')
    self.assertRaisesWithRegexpMatch(
      messages.ValidationError,
      re.escape("Expected type %r "
                "for IntegerField, found 10 (type %r)"
               % (six.integer_types, str)),
      insert)

  def testPickle(self):
    """Testing pickling and unpickling of disconnected FieldList instances."""
    field_list = messages.FieldList(self.integer_field, [1, 2, 3, 4, 5])
    unpickled = pickle.loads(pickle.dumps(field_list))
    self.assertEquals(field_list, unpickled)
    self.assertIsInstance(unpickled.field, messages.IntegerField)
    self.assertEquals(1, unpickled.field.number)
    self.assertTrue(unpickled.field.repeated)


class FieldTest(test_util.TestCase):

  def ActionOnAllFieldClasses(self, action):
    """Test all field classes except Message and Enum.

    Message and Enum require separate tests.

    Args:
      action: Callable that takes the field class as a parameter.
    """
    for field_class in (messages.IntegerField,
                        messages.FloatField,
                        messages.BooleanField,
                        messages.BytesField,
                        messages.StringField,
                       ):
      action(field_class)

  def testNumberAttribute(self):
    """Test setting the number attribute."""
    def action(field_class):
      # Check range.
      self.assertRaises(messages.InvalidNumberError,
                        field_class,
                        0)
      self.assertRaises(messages.InvalidNumberError,
                        field_class,
                        -1)
      self.assertRaises(messages.InvalidNumberError,
                        field_class,
                        messages.MAX_FIELD_NUMBER + 1)

      # Check reserved.
      self.assertRaises(messages.InvalidNumberError,
                        field_class,
                        messages.FIRST_RESERVED_FIELD_NUMBER)
      self.assertRaises(messages.InvalidNumberError,
                        field_class,
                        messages.LAST_RESERVED_FIELD_NUMBER)
      self.assertRaises(messages.InvalidNumberError,
                        field_class,
                        '1')

      # This one should work.
      field_class(number=1)
    self.ActionOnAllFieldClasses(action)

  def testRequiredAndRepeated(self):
    """Test setting the required and repeated fields."""
    def action(field_class):
      field_class(1, required=True)
      field_class(1, repeated=True)
      self.assertRaises(messages.FieldDefinitionError,
                        field_class,
                        1,
                        required=True,
                        repeated=True)
    self.ActionOnAllFieldClasses(action)

  def testInvalidVariant(self):
    """Test field with invalid variants."""
    def action(field_class):
      if field_class is not message_types.DateTimeField:
        self.assertRaises(messages.InvalidVariantError,
                          field_class,
                          1,
                          variant=messages.Variant.ENUM)
    self.ActionOnAllFieldClasses(action)

  def testDefaultVariant(self):
    """Test that default variant is used when not set."""
    def action(field_class):
      field = field_class(1)
      self.assertEquals(field_class.DEFAULT_VARIANT, field.variant)

    self.ActionOnAllFieldClasses(action)

  def testAlternateVariant(self):
    """Test that default variant is used when not set."""
    field = messages.IntegerField(1, variant=messages.Variant.UINT32)
    self.assertEquals(messages.Variant.UINT32, field.variant)

  def testDefaultFields_Single(self):
    """Test default field is correct type (single)."""
    defaults = {messages.IntegerField: 10,
                messages.FloatField: 1.5,
                messages.BooleanField: False,
                messages.BytesField: b'abc',
                messages.StringField: u'abc',
               }

    def action(field_class):
      field_class(1, default=defaults[field_class])
    self.ActionOnAllFieldClasses(action)

    # Run defaults test again checking for str/unicode compatiblity.
    defaults[messages.StringField] = 'abc'
    self.ActionOnAllFieldClasses(action)

  def testStringField_BadUnicodeInDefault(self):
    """Test binary values in string field."""
    self.assertRaisesWithRegexpMatch(
      messages.InvalidDefaultError,
      r"Invalid default value for StringField:.*: "
      r"Field encountered non-ASCII string .*: "
      r"'ascii' codec can't decode byte 0x89 in position 0: "
      r"ordinal not in range",
      messages.StringField, 1, default=b'\x89')

  def testDefaultFields_InvalidSingle(self):
    """Test default field is correct type (invalid single)."""
    def action(field_class):
      self.assertRaises(messages.InvalidDefaultError,
                        field_class,
                        1,
                        default=object())
    self.ActionOnAllFieldClasses(action)

  def testDefaultFields_InvalidRepeated(self):
    """Test default field does not accept defaults."""
    self.assertRaisesWithRegexpMatch(
      messages.FieldDefinitionError,
      'Repeated fields may not have defaults',
      messages.StringField, 1, repeated=True, default=[1, 2, 3])

  def testDefaultFields_None(self):
    """Test none is always acceptable."""
    def action(field_class):
      field_class(1, default=None)
      field_class(1, required=True, default=None)
      field_class(1, repeated=True, default=None)
    self.ActionOnAllFieldClasses(action)

  def testDefaultFields_Enum(self):
    """Test the default for enum fields."""
    class Symbol(messages.Enum):

      ALPHA = 1
      BETA = 2
      GAMMA = 3

    field = messages.EnumField(Symbol, 1, default=Symbol.ALPHA)

    self.assertEquals(Symbol.ALPHA, field.default)

  def testDefaultFields_EnumStringDelayedResolution(self):
    """Test that enum fields resolve default strings."""
    field = messages.EnumField('protorpc.descriptor.FieldDescriptor.Label',
                               1,
                               default='OPTIONAL')

    self.assertEquals(descriptor.FieldDescriptor.Label.OPTIONAL, field.default)

  def testDefaultFields_EnumIntDelayedResolution(self):
    """Test that enum fields resolve default integers."""
    field = messages.EnumField('protorpc.descriptor.FieldDescriptor.Label',
                               1,
                               default=2)

    self.assertEquals(descriptor.FieldDescriptor.Label.REQUIRED, field.default)

  def testDefaultFields_EnumOkIfTypeKnown(self):
    """Test that enum fields accept valid default values when type is known."""
    field = messages.EnumField(descriptor.FieldDescriptor.Label,
                               1,
                               default='REPEATED')

    self.assertEquals(descriptor.FieldDescriptor.Label.REPEATED, field.default)

  def testDefaultFields_EnumForceCheckIfTypeKnown(self):
    """Test that enum fields validate default values if type is known."""
    self.assertRaisesWithRegexpMatch(TypeError,
                                     'No such value for NOT_A_LABEL in '
                                     'Enum Label',
                                     messages.EnumField,
                                     descriptor.FieldDescriptor.Label,
                                     1,
                                     default='NOT_A_LABEL')

  def testDefaultFields_EnumInvalidDelayedResolution(self):
    """Test that enum fields raise errors upon delayed resolution error."""
    field = messages.EnumField('protorpc.descriptor.FieldDescriptor.Label',
                               1,
                               default=200)

    self.assertRaisesWithRegexpMatch(TypeError,
                                     'No such value for 200 in Enum Label',
                                     getattr,
                                     field,
                                     'default')

  def testValidate_Valid(self):
    """Test validation of valid values."""
    values = {messages.IntegerField: 10,
              messages.FloatField: 1.5,
              messages.BooleanField: False,
              messages.BytesField: b'abc',
              messages.StringField: u'abc',
             }
    def action(field_class):
      # Optional.
      field = field_class(1)
      field.validate(values[field_class])

      # Required.
      field = field_class(1, required=True)
      field.validate(values[field_class])

      # Repeated.
      field = field_class(1, repeated=True)
      field.validate([])
      field.validate(())
      field.validate([values[field_class]])
      field.validate((values[field_class],))

      # Right value, but not repeated.
      self.assertRaises(messages.ValidationError,
                        field.validate,
                        values[field_class])
      self.assertRaises(messages.ValidationError,
                        field.validate,
                        values[field_class])

    self.ActionOnAllFieldClasses(action)

  def testValidate_Invalid(self):
    """Test validation of valid values."""
    values = {messages.IntegerField: "10",
              messages.FloatField: "blah",
              messages.BooleanField: 0,
              messages.BytesField: 10.20,
              messages.StringField: 42,
             }
    def action(field_class):
      # Optional.
      field = field_class(1)
      self.assertRaises(messages.ValidationError,
                        field.validate,
                        values[field_class])

      # Required.
      field = field_class(1, required=True)
      self.assertRaises(messages.ValidationError,
                        field.validate,
                        values[field_class])

      # Repeated.
      field = field_class(1, repeated=True)
      self.assertRaises(messages.ValidationError,
                        field.validate,
                        [values[field_class]])
      self.assertRaises(messages.ValidationError,
                        field.validate,
                        (values[field_class],))
    self.ActionOnAllFieldClasses(action)

  def testValidate_None(self):
    """Test that None is valid for non-required fields."""
    def action(field_class):
      # Optional.
      field = field_class(1)
      field.validate(None)

      # Required.
      field = field_class(1, required=True)
      self.assertRaisesWithRegexpMatch(messages.ValidationError,
                                       'Required field is missing',
                                       field.validate,
                                       None)

      # Repeated.
      field = field_class(1, repeated=True)
      field.validate(None)
      self.assertRaisesWithRegexpMatch(messages.ValidationError,
                                       'Repeated values for %s may '
                                       'not be None' % field_class.__name__,
                                       field.validate,
                                       [None])
      self.assertRaises(messages.ValidationError,
                        field.validate,
                        (None,))
    self.ActionOnAllFieldClasses(action)

  def testValidateElement(self):
    """Test validation of valid values."""
    values = {messages.IntegerField: (10, -1, 0),
              messages.FloatField: (1.5, -1.5, 3),  # for json it is all a number
              messages.BooleanField: (True, False),
              messages.BytesField: (b'abc',),
              messages.StringField: (u'abc',),
             }
    def action(field_class):
      # Optional.
      field = field_class(1)
      for value in values[field_class]:
        field.validate_element(value)

      # Required.
      field = field_class(1, required=True)
      for value in values[field_class]:
        field.validate_element(value)

      # Repeated.
      field = field_class(1, repeated=True)
      self.assertRaises(messages.ValidationError,
                        field.validate_element,
                        [])
      self.assertRaises(messages.ValidationError,
                        field.validate_element,
                        ())
      for value in values[field_class]:
        field.validate_element(value)

      # Right value, but repeated.
      self.assertRaises(messages.ValidationError,
                        field.validate_element,
                        list(values[field_class]))  # testing list
      self.assertRaises(messages.ValidationError,
                        field.validate_element,
                        values[field_class])  # testing tuple
    self.ActionOnAllFieldClasses(action)

  def testValidateCastingElement(self):
    field = messages.FloatField(1)
    self.assertEquals(type(field.validate_element(12)), float)
    self.assertEquals(type(field.validate_element(12.0)), float)
    field = messages.IntegerField(1)
    self.assertEquals(type(field.validate_element(12)), int)
    self.assertRaises(messages.ValidationError,
                      field.validate_element,
                      12.0)  # should fail from float to int

  def testReadOnly(self):
    """Test that objects are all read-only."""
    def action(field_class):
      field = field_class(10)
      self.assertRaises(AttributeError,
                        setattr,
                        field,
                        'number',
                        20)
      self.assertRaises(AttributeError,
                        setattr,
                        field,
                        'anything_else',
                        'whatever')
    self.ActionOnAllFieldClasses(action)

  def testMessageField(self):
    """Test the construction of message fields."""
    self.assertRaises(messages.FieldDefinitionError,
                      messages.MessageField,
                      str,
                      10)

    self.assertRaises(messages.FieldDefinitionError,
                      messages.MessageField,
                      messages.Message,
                      10)

    class MyMessage(messages.Message):
      pass

    field = messages.MessageField(MyMessage, 10)
    self.assertEquals(MyMessage, field.type)

  def testMessageField_ForwardReference(self):
    """Test the construction of forward reference message fields."""
    global MyMessage
    global ForwardMessage
    try:
      class MyMessage(messages.Message):

        self_reference = messages.MessageField('MyMessage', 1)
        forward = messages.MessageField('ForwardMessage', 2)
        nested = messages.MessageField('ForwardMessage.NestedMessage', 3)
        inner = messages.MessageField('Inner', 4)

        class Inner(messages.Message):

          sibling = messages.MessageField('Sibling', 1)

        class Sibling(messages.Message):

          pass

      class ForwardMessage(messages.Message):

        class NestedMessage(messages.Message):

          pass

      self.assertEquals(MyMessage,
                        MyMessage.field_by_name('self_reference').type)

      self.assertEquals(ForwardMessage,
                        MyMessage.field_by_name('forward').type)

      self.assertEquals(ForwardMessage.NestedMessage,
                        MyMessage.field_by_name('nested').type)

      self.assertEquals(MyMessage.Inner,
                        MyMessage.field_by_name('inner').type)

      self.assertEquals(MyMessage.Sibling,
                        MyMessage.Inner.field_by_name('sibling').type)
    finally:
      try:
        del MyMessage
        del ForwardMessage
      except:
        pass

  def testMessageField_WrongType(self):
    """Test that forward referencing the wrong type raises an error."""
    global AnEnum
    try:
      class AnEnum(messages.Enum):
        pass

      class AnotherMessage(messages.Message):

        a_field = messages.MessageField('AnEnum', 1)

      self.assertRaises(messages.FieldDefinitionError,
                        getattr,
                        AnotherMessage.field_by_name('a_field'),
                        'type')
    finally:
      del AnEnum

  def testMessageFieldValidate(self):
    """Test validation on message field."""
    class MyMessage(messages.Message):
      pass

    class AnotherMessage(messages.Message):
      pass

    field = messages.MessageField(MyMessage, 10)
    field.validate(MyMessage())

    self.assertRaises(messages.ValidationError,
                      field.validate,
                      AnotherMessage())

  def testMessageFieldMessageType(self):
    """Test message_type property."""
    class MyMessage(messages.Message):
      pass

    class HasMessage(messages.Message):
      field = messages.MessageField(MyMessage, 1)

    self.assertEqual(HasMessage.field.type, HasMessage.field.message_type)

  def testMessageFieldValueFromMessage(self):
    class MyMessage(messages.Message):
      pass

    class HasMessage(messages.Message):
      field = messages.MessageField(MyMessage, 1)

    instance = MyMessage()

    self.assertTrue(instance is HasMessage.field.value_from_message(instance))

  def testMessageFieldValueFromMessageWrongType(self):
    class MyMessage(messages.Message):
      pass

    class HasMessage(messages.Message):
      field = messages.MessageField(MyMessage, 1)

    self.assertRaisesWithRegexpMatch(
        messages.DecodeError,
        'Expected type MyMessage, got int: 10',
        HasMessage.field.value_from_message, 10)

  def testMessageFieldValueToMessage(self):
    class MyMessage(messages.Message):
      pass

    class HasMessage(messages.Message):
      field = messages.MessageField(MyMessage, 1)

    instance = MyMessage()

    self.assertTrue(instance is HasMessage.field.value_to_message(instance))

  def testMessageFieldValueToMessageWrongType(self):
    class MyMessage(messages.Message):
      pass

    class MyOtherMessage(messages.Message):
      pass

    class HasMessage(messages.Message):
      field = messages.MessageField(MyMessage, 1)

    instance = MyOtherMessage()

    self.assertRaisesWithRegexpMatch(
        messages.EncodeError,
        'Expected type MyMessage, got MyOtherMessage: <MyOtherMessage>',
        HasMessage.field.value_to_message, instance)

  def testIntegerField_AllowLong(self):
    """Test that the integer field allows for longs."""
    if six.PY2:
        messages.IntegerField(10, default=long(10))

  def testMessageFieldValidate_Initialized(self):
    """Test validation on message field."""
    class MyMessage(messages.Message):
      field1 = messages.IntegerField(1, required=True)

    field = messages.MessageField(MyMessage, 10)

    # Will validate messages where is_initialized() is False.
    message = MyMessage()
    field.validate(message)
    message.field1 = 20
    field.validate(message)

  def testEnumField(self):
    """Test the construction of enum fields."""
    self.assertRaises(messages.FieldDefinitionError,
                      messages.EnumField,
                      str,
                      10)

    self.assertRaises(messages.FieldDefinitionError,
                      messages.EnumField,
                      messages.Enum,
                      10)

    class Color(messages.Enum):
      RED = 1
      GREEN = 2
      BLUE = 3

    field = messages.EnumField(Color, 10)
    self.assertEquals(Color, field.type)

    class Another(messages.Enum):
      VALUE = 1

    self.assertRaises(messages.InvalidDefaultError,
                      messages.EnumField,
                      Color,
                      10,
                      default=Another.VALUE)

  def testEnumField_ForwardReference(self):
    """Test the construction of forward reference enum fields."""
    global MyMessage
    global ForwardEnum
    global ForwardMessage
    try:
      class MyMessage(messages.Message):

        forward = messages.EnumField('ForwardEnum', 1)
        nested = messages.EnumField('ForwardMessage.NestedEnum', 2)
        inner = messages.EnumField('Inner', 3)

        class Inner(messages.Enum):
          pass

      class ForwardEnum(messages.Enum):
        pass

      class ForwardMessage(messages.Message):

        class NestedEnum(messages.Enum):
          pass

      self.assertEquals(ForwardEnum,
                        MyMessage.field_by_name('forward').type)

      self.assertEquals(ForwardMessage.NestedEnum,
                        MyMessage.field_by_name('nested').type)

      self.assertEquals(MyMessage.Inner,
                        MyMessage.field_by_name('inner').type)
    finally:
      try:
        del MyMessage
        del ForwardEnum
        del ForwardMessage
      except:
        pass

  def testEnumField_WrongType(self):
    """Test that forward referencing the wrong type raises an error."""
    global AMessage
    try:
      class AMessage(messages.Message):
        pass

      class AnotherMessage(messages.Message):

        a_field = messages.EnumField('AMessage', 1)

      self.assertRaises(messages.FieldDefinitionError,
                        getattr,
                        AnotherMessage.field_by_name('a_field'),
                        'type')
    finally:
      del AMessage

  def testMessageDefinition(self):
    """Test that message definition is set on fields."""
    class MyMessage(messages.Message):

      my_field = messages.StringField(1)

    self.assertEquals(MyMessage,
                      MyMessage.field_by_name('my_field').message_definition())

  def testNoneAssignment(self):
    """Test that assigning None does not change comparison."""
    class MyMessage(messages.Message):

      my_field = messages.StringField(1)

    m1 = MyMessage()
    m2 = MyMessage()
    m2.my_field = None
    self.assertEquals(m1, m2)

  def testNonAsciiStr(self):
    """Test validation fails for non-ascii StringField values."""
    class Thing(messages.Message):
      string_field = messages.StringField(2)

    thing = Thing()
    self.assertRaisesWithRegexpMatch(
      messages.ValidationError,
      'Field string_field encountered non-ASCII string',
      setattr, thing, 'string_field', test_util.BINARY)


class MessageTest(test_util.TestCase):
  """Tests for message class."""

  def CreateMessageClass(self):
    """Creates a simple message class with 3 fields.

    Fields are defined in alphabetical order but with conflicting numeric
    order.
    """
    class ComplexMessage(messages.Message):
      a3 = messages.IntegerField(3)
      b1 = messages.StringField(1)
      c2 = messages.StringField(2)

    return ComplexMessage

  def testSameNumbers(self):
    """Test that cannot assign two fields with same numbers."""

    def action():
      class BadMessage(messages.Message):
        f1 = messages.IntegerField(1)
        f2 = messages.IntegerField(1)
    self.assertRaises(messages.DuplicateNumberError,
                      action)

  def testStrictAssignment(self):
    """Tests that cannot assign to unknown or non-reserved attributes."""
    class SimpleMessage(messages.Message):
      field = messages.IntegerField(1)

    simple_message = SimpleMessage()
    self.assertRaises(AttributeError,
                      setattr,
                      simple_message,
                      'does_not_exist',
                      10)

  def testListAssignmentDoesNotCopy(self):
    class SimpleMessage(messages.Message):
      repeated = messages.IntegerField(1, repeated=True)

    message = SimpleMessage()
    original = message.repeated
    message.repeated = []
    self.assertFalse(original is message.repeated)

  def testValidate_Optional(self):
    """Tests validation of optional fields."""
    class SimpleMessage(messages.Message):
      non_required = messages.IntegerField(1)

    simple_message = SimpleMessage()
    simple_message.check_initialized()
    simple_message.non_required = 10
    simple_message.check_initialized()

  def testValidate_Required(self):
    """Tests validation of required fields."""
    class SimpleMessage(messages.Message):
      required = messages.IntegerField(1, required=True)

    simple_message = SimpleMessage()
    self.assertRaises(messages.ValidationError,
                      simple_message.check_initialized)
    simple_message.required = 10
    simple_message.check_initialized()

  def testValidate_Repeated(self):
    """Tests validation of repeated fields."""
    class SimpleMessage(messages.Message):
      repeated = messages.IntegerField(1, repeated=True)

    simple_message = SimpleMessage()

    # Check valid values.
    for valid_value in [], [10], [10, 20], (), (10,), (10, 20):
      simple_message.repeated = valid_value
      simple_message.check_initialized()

    # Check cleared.
    simple_message.repeated = []
    simple_message.check_initialized()

    # Check invalid values.
    for invalid_value in 10, ['10', '20'], [None], (None,):
      self.assertRaises(messages.ValidationError,
                        setattr, simple_message, 'repeated', invalid_value)

  def testIsInitialized(self):
    """Tests is_initialized."""
    class SimpleMessage(messages.Message):
      required = messages.IntegerField(1, required=True)

    simple_message = SimpleMessage()
    self.assertFalse(simple_message.is_initialized())

    simple_message.required = 10

    self.assertTrue(simple_message.is_initialized())

  def testIsInitializedNestedField(self):
    """Tests is_initialized for nested fields."""
    class SimpleMessage(messages.Message):
      required = messages.IntegerField(1, required=True)

    class NestedMessage(messages.Message):
      simple = messages.MessageField(SimpleMessage, 1)

    simple_message = SimpleMessage()
    self.assertFalse(simple_message.is_initialized())
    nested_message = NestedMessage(simple=simple_message)
    self.assertFalse(nested_message.is_initialized())

    simple_message.required = 10

    self.assertTrue(simple_message.is_initialized())
    self.assertTrue(nested_message.is_initialized())

  def testInitializeNestedFieldFromDict(self):
    """Tests initializing nested fields from dict."""
    class SimpleMessage(messages.Message):
      required = messages.IntegerField(1, required=True)

    class NestedMessage(messages.Message):
      simple = messages.MessageField(SimpleMessage, 1)

    class RepeatedMessage(messages.Message):
      simple = messages.MessageField(SimpleMessage, 1, repeated=True)

    nested_message1 = NestedMessage(simple={'required': 10})
    self.assertTrue(nested_message1.is_initialized())
    self.assertTrue(nested_message1.simple.is_initialized())

    nested_message2 = NestedMessage()
    nested_message2.simple = {'required': 10}
    self.assertTrue(nested_message2.is_initialized())
    self.assertTrue(nested_message2.simple.is_initialized())

    repeated_values = [{}, {'required': 10}, SimpleMessage(required=20)]

    repeated_message1 = RepeatedMessage(simple=repeated_values)
    self.assertEquals(3, len(repeated_message1.simple))
    self.assertFalse(repeated_message1.is_initialized())

    repeated_message1.simple[0].required = 0
    self.assertTrue(repeated_message1.is_initialized())

    repeated_message2 = RepeatedMessage()
    repeated_message2.simple = repeated_values
    self.assertEquals(3, len(repeated_message2.simple))
    self.assertFalse(repeated_message2.is_initialized())

    repeated_message2.simple[0].required = 0
    self.assertTrue(repeated_message2.is_initialized())

  def testNestedMethodsNotAllowed(self):
    """Test that method definitions on Message classes are not allowed."""
    def action():
      class WithMethods(messages.Message):
        def not_allowed(self):
          pass

    self.assertRaises(messages.MessageDefinitionError,
                      action)

  def testNestedAttributesNotAllowed(self):
    """Test that attribute assignment on Message classes are not allowed."""
    def int_attribute():
      class WithMethods(messages.Message):
        not_allowed = 1

    def string_attribute():
      class WithMethods(messages.Message):
        not_allowed = 'not allowed'

    def enum_attribute():
      class WithMethods(messages.Message):
        not_allowed = Color.RED

    for action in (int_attribute, string_attribute, enum_attribute):
      self.assertRaises(messages.MessageDefinitionError,
                        action)

  def testNameIsSetOnFields(self):
    """Make sure name is set on fields after Message class init."""
    class HasNamedFields(messages.Message):
      field = messages.StringField(1)

    self.assertEquals('field', HasNamedFields.field_by_number(1).name)

  def testSubclassingMessageDisallowed(self):
    """Not permitted to create sub-classes of message classes."""
    class SuperClass(messages.Message):
      pass

    def action():
      class SubClass(SuperClass):
        pass

    self.assertRaises(messages.MessageDefinitionError,
                      action)

  def testAllFields(self):
    """Test all_fields method."""
    ComplexMessage = self.CreateMessageClass()
    fields = list(ComplexMessage.all_fields())

    # Order does not matter, so sort now.
    fields = sorted(fields, key=lambda f: f.name)

    self.assertEquals(3, len(fields))
    self.assertEquals('a3', fields[0].name)
    self.assertEquals('b1', fields[1].name)
    self.assertEquals('c2', fields[2].name)

  def testFieldByName(self):
    """Test getting field by name."""
    ComplexMessage = self.CreateMessageClass()

    self.assertEquals(3, ComplexMessage.field_by_name('a3').number)
    self.assertEquals(1, ComplexMessage.field_by_name('b1').number)
    self.assertEquals(2, ComplexMessage.field_by_name('c2').number)

    self.assertRaises(KeyError,
                      ComplexMessage.field_by_name,
                      'unknown')

  def testFieldByNumber(self):
    """Test getting field by number."""
    ComplexMessage = self.CreateMessageClass()

    self.assertEquals('a3', ComplexMessage.field_by_number(3).name)
    self.assertEquals('b1', ComplexMessage.field_by_number(1).name)
    self.assertEquals('c2', ComplexMessage.field_by_number(2).name)

    self.assertRaises(KeyError,
                      ComplexMessage.field_by_number,
                      4)

  def testGetAssignedValue(self):
    """Test getting the assigned value of a field."""
    class SomeMessage(messages.Message):
      a_value = messages.StringField(1, default=u'a default')

    message = SomeMessage()
    self.assertEquals(None, message.get_assigned_value('a_value'))

    message.a_value = u'a string'
    self.assertEquals(u'a string', message.get_assigned_value('a_value'))

    message.a_value = u'a default'
    self.assertEquals(u'a default', message.get_assigned_value('a_value'))

    self.assertRaisesWithRegexpMatch(
        AttributeError,
        'Message SomeMessage has no field no_such_field',
        message.get_assigned_value,
        'no_such_field')

  def testReset(self):
    """Test resetting a field value."""
    class SomeMessage(messages.Message):
      a_value = messages.StringField(1, default=u'a default')
      repeated = messages.IntegerField(2, repeated=True)

    message = SomeMessage()

    self.assertRaises(AttributeError, message.reset, 'unknown')

    self.assertEquals(u'a default', message.a_value)
    message.reset('a_value')
    self.assertEquals(u'a default', message.a_value)

    message.a_value = u'a new value'
    self.assertEquals(u'a new value', message.a_value)
    message.reset('a_value')
    self.assertEquals(u'a default', message.a_value)

    message.repeated = [1, 2, 3]
    self.assertEquals([1, 2, 3], message.repeated)
    saved = message.repeated
    message.reset('repeated')
    self.assertEquals([], message.repeated)
    self.assertIsInstance(message.repeated, messages.FieldList)
    self.assertEquals([1, 2, 3], saved)

  def testAllowNestedEnums(self):
    """Test allowing nested enums in a message definition."""
    class Trade(messages.Message):
      class Duration(messages.Enum):
        GTC = 1
        DAY = 2

      class Currency(messages.Enum):
        USD = 1
        GBP = 2
        INR = 3

    # Sorted by name order seems to be the only feasible option.
    self.assertEquals(['Currency', 'Duration'], Trade.__enums__)

    # Message definition will now be set on Enumerated objects.
    self.assertEquals(Trade, Trade.Duration.message_definition())

  def testAllowNestedMessages(self):
    """Test allowing nested messages in a message definition."""
    class Trade(messages.Message):
      class Lot(messages.Message):
        pass

      class Agent(messages.Message):
        pass

    # Sorted by name order seems to be the only feasible option.
    self.assertEquals(['Agent', 'Lot'], Trade.__messages__)
    self.assertEquals(Trade, Trade.Agent.message_definition())
    self.assertEquals(Trade, Trade.Lot.message_definition())

    # But not Message itself.
    def action():
      class Trade(messages.Message):
        NiceTry = messages.Message
    self.assertRaises(messages.MessageDefinitionError, action)

  def testDisallowClassAssignments(self):
    """Test setting class attributes may not happen."""
    class MyMessage(messages.Message):
      pass

    self.assertRaises(AttributeError,
                      setattr,
                      MyMessage,
                      'x',
                      'do not assign')

  def testEquality(self):
    """Test message class equality."""
    # Comparison against enums must work.
    class MyEnum(messages.Enum):
      val1 = 1
      val2 = 2

    # Comparisons against nested messages must work.
    class AnotherMessage(messages.Message):
      string = messages.StringField(1)

    class MyMessage(messages.Message):
      field1 = messages.IntegerField(1)
      field2 = messages.EnumField(MyEnum, 2)
      field3 = messages.MessageField(AnotherMessage, 3)

    message1 = MyMessage()

    self.assertNotEquals('hi', message1)
    self.assertNotEquals(AnotherMessage(), message1)
    self.assertEquals(message1, message1)

    message2 = MyMessage()

    self.assertEquals(message1, message2)

    message1.field1 = 10
    self.assertNotEquals(message1, message2)

    message2.field1 = 20
    self.assertNotEquals(message1, message2)

    message2.field1 = 10
    self.assertEquals(message1, message2)

    message1.field2 = MyEnum.val1
    self.assertNotEquals(message1, message2)

    message2.field2 = MyEnum.val2
    self.assertNotEquals(message1, message2)

    message2.field2 = MyEnum.val1
    self.assertEquals(message1, message2)

    message1.field3 = AnotherMessage()
    message1.field3.string = u'value1'
    self.assertNotEquals(message1, message2)

    message2.field3 = AnotherMessage()
    message2.field3.string = u'value2'
    self.assertNotEquals(message1, message2)

    message2.field3.string = u'value1'
    self.assertEquals(message1, message2)

  def testEqualityWithUnknowns(self):
    """Test message class equality with unknown fields."""

    class MyMessage(messages.Message):
      field1 = messages.IntegerField(1)

    message1 = MyMessage()
    message2 = MyMessage()
    self.assertEquals(message1, message2)
    message1.set_unrecognized_field('unknown1', 'value1',
                                    messages.Variant.STRING)
    self.assertEquals(message1, message2)

    message1.set_unrecognized_field('unknown2', ['asdf', 3],
                                    messages.Variant.STRING)
    message1.set_unrecognized_field('unknown3', 4.7,
                                    messages.Variant.DOUBLE)
    self.assertEquals(message1, message2)

  def testUnrecognizedFieldInvalidVariant(self):
    class MyMessage(messages.Message):
      field1 = messages.IntegerField(1)

    message1 = MyMessage()
    self.assertRaises(TypeError, message1.set_unrecognized_field, 'unknown4',
                      {'unhandled': 'type'}, None)
    self.assertRaises(TypeError, message1.set_unrecognized_field, 'unknown4',
                      {'unhandled': 'type'}, 123)

  def testRepr(self):
    """Test represtation of Message object."""
    class MyMessage(messages.Message):
      integer_value = messages.IntegerField(1)
      string_value = messages.StringField(2)
      unassigned = messages.StringField(3)
      unassigned_with_default = messages.StringField(4, default=u'a default')

    my_message = MyMessage()
    my_message.integer_value = 42
    my_message.string_value = u'A string'

    pat = re.compile(r"<MyMessage\n integer_value: 42\n"
                      " string_value: [u]?'A string'>")
    self.assertTrue(pat.match(repr(my_message)) is not None)

  def testValidation(self):
    """Test validation of message values."""
    # Test optional.
    class SubMessage(messages.Message):
      pass

    class Message(messages.Message):
      val = messages.MessageField(SubMessage, 1)

    message = Message()

    message_field = messages.MessageField(Message, 1)
    message_field.validate(message)
    message.val = SubMessage()
    message_field.validate(message)
    self.assertRaises(messages.ValidationError,
                      setattr, message, 'val', [SubMessage()])

    # Test required.
    class Message(messages.Message):
      val = messages.MessageField(SubMessage, 1, required=True)

    message = Message()

    message_field = messages.MessageField(Message, 1)
    message_field.validate(message)
    message.val = SubMessage()
    message_field.validate(message)
    self.assertRaises(messages.ValidationError,
                      setattr, message, 'val', [SubMessage()])

    # Test repeated.
    class Message(messages.Message):
      val = messages.MessageField(SubMessage, 1, repeated=True)

    message = Message()

    message_field = messages.MessageField(Message, 1)
    message_field.validate(message)
    self.assertRaisesWithRegexpMatch(
      messages.ValidationError,
      "Field val is repeated. Found: <SubMessage>",
      setattr, message, 'val', SubMessage())
    message.val = [SubMessage()]
    message_field.validate(message)

  def testDefinitionName(self):
    """Test message name."""
    class MyMessage(messages.Message):
      pass

    module_name = test_util.get_module_name(FieldTest)
    self.assertEquals('%s.MyMessage' % module_name,
                      MyMessage.definition_name())
    self.assertEquals(module_name, MyMessage.outer_definition_name())
    self.assertEquals(module_name, MyMessage.definition_package())

    self.assertEquals(six.text_type, type(MyMessage.definition_name()))
    self.assertEquals(six.text_type, type(MyMessage.outer_definition_name()))
    self.assertEquals(six.text_type, type(MyMessage.definition_package()))

  def testDefinitionName_OverrideModule(self):
    """Test message module is overriden by module package name."""
    class MyMessage(messages.Message):
      pass

    global package
    package = 'my.package'

    try:
      self.assertEquals('my.package.MyMessage', MyMessage.definition_name())
      self.assertEquals('my.package', MyMessage.outer_definition_name())
      self.assertEquals('my.package', MyMessage.definition_package())

      self.assertEquals(six.text_type, type(MyMessage.definition_name()))
      self.assertEquals(six.text_type, type(MyMessage.outer_definition_name()))
      self.assertEquals(six.text_type, type(MyMessage.definition_package()))
    finally:
      del package

  def testDefinitionName_NoModule(self):
    """Test what happens when there is no module for message."""
    class MyMessage(messages.Message):
      pass

    original_modules = sys.modules
    sys.modules = dict(sys.modules)
    try:
      del sys.modules[__name__]
      self.assertEquals('MyMessage', MyMessage.definition_name())
      self.assertEquals(None, MyMessage.outer_definition_name())
      self.assertEquals(None, MyMessage.definition_package())

      self.assertEquals(six.text_type, type(MyMessage.definition_name()))
    finally:
      sys.modules = original_modules

  def testDefinitionName_Nested(self):
    """Test nested message names."""
    class MyMessage(messages.Message):

      class NestedMessage(messages.Message):

        class NestedMessage(messages.Message):

          pass

    module_name = test_util.get_module_name(MessageTest)
    self.assertEquals('%s.MyMessage.NestedMessage' % module_name,
                      MyMessage.NestedMessage.definition_name())
    self.assertEquals('%s.MyMessage' % module_name,
                      MyMessage.NestedMessage.outer_definition_name())
    self.assertEquals(module_name,
                      MyMessage.NestedMessage.definition_package())

    self.assertEquals('%s.MyMessage.NestedMessage.NestedMessage' % module_name,
                      MyMessage.NestedMessage.NestedMessage.definition_name())
    self.assertEquals(
      '%s.MyMessage.NestedMessage' % module_name,
      MyMessage.NestedMessage.NestedMessage.outer_definition_name())
    self.assertEquals(
      module_name,
      MyMessage.NestedMessage.NestedMessage.definition_package())


  def testMessageDefinition(self):
    """Test that enumeration knows its enclosing message definition."""
    class OuterMessage(messages.Message):

      class InnerMessage(messages.Message):
        pass

    self.assertEquals(None, OuterMessage.message_definition())
    self.assertEquals(OuterMessage,
                      OuterMessage.InnerMessage.message_definition())

  def testConstructorKwargs(self):
    """Test kwargs via constructor."""
    class SomeMessage(messages.Message):
      name = messages.StringField(1)
      number = messages.IntegerField(2)

    expected = SomeMessage()
    expected.name = 'my name'
    expected.number = 200
    self.assertEquals(expected, SomeMessage(name='my name', number=200))

  def testConstructorNotAField(self):
    """Test kwargs via constructor with wrong names."""
    class SomeMessage(messages.Message):
      pass

    self.assertRaisesWithRegexpMatch(
      AttributeError,
      'May not assign arbitrary value does_not_exist to message SomeMessage',
      SomeMessage,
      does_not_exist=10)

  def testGetUnsetRepeatedValue(self):
    class SomeMessage(messages.Message):
      repeated = messages.IntegerField(1, repeated=True)

    instance = SomeMessage()
    self.assertEquals([], instance.repeated)
    self.assertTrue(isinstance(instance.repeated, messages.FieldList))

  def testCompareAutoInitializedRepeatedFields(self):
    class SomeMessage(messages.Message):
      repeated = messages.IntegerField(1, repeated=True)

    message1 = SomeMessage(repeated=[])
    message2 = SomeMessage()
    self.assertEquals(message1, message2)

  def testUnknownValues(self):
    """Test message class equality with unknown fields."""
    class MyMessage(messages.Message):
      field1 = messages.IntegerField(1)

    message = MyMessage()
    self.assertEquals([], message.all_unrecognized_fields())
    self.assertEquals((None, None),
                      message.get_unrecognized_field_info('doesntexist'))
    self.assertEquals((None, None),
                      message.get_unrecognized_field_info(
                          'doesntexist', None, None))
    self.assertEquals(('defaultvalue', 'defaultwire'),
                      message.get_unrecognized_field_info(
                          'doesntexist', 'defaultvalue', 'defaultwire'))
    self.assertEquals((3, None),
                      message.get_unrecognized_field_info(
                          'doesntexist', value_default=3))

    message.set_unrecognized_field('exists', 9.5, messages.Variant.DOUBLE)
    self.assertEquals(1, len(message.all_unrecognized_fields()))
    self.assertTrue('exists' in message.all_unrecognized_fields())
    self.assertEquals((9.5, messages.Variant.DOUBLE),
                      message.get_unrecognized_field_info('exists'))
    self.assertEquals((9.5, messages.Variant.DOUBLE),
                      message.get_unrecognized_field_info('exists', 'type',
                                                          1234))
    self.assertEquals((1234, None),
                      message.get_unrecognized_field_info('doesntexist', 1234))

    message.set_unrecognized_field('another', 'value', messages.Variant.STRING)
    self.assertEquals(2, len(message.all_unrecognized_fields()))
    self.assertTrue('exists' in message.all_unrecognized_fields())
    self.assertTrue('another' in message.all_unrecognized_fields())
    self.assertEquals((9.5, messages.Variant.DOUBLE),
                      message.get_unrecognized_field_info('exists'))
    self.assertEquals(('value', messages.Variant.STRING),
                      message.get_unrecognized_field_info('another'))

    message.set_unrecognized_field('typetest1', ['list', 0, ('test',)],
                                   messages.Variant.STRING)
    self.assertEquals((['list', 0, ('test',)], messages.Variant.STRING),
                      message.get_unrecognized_field_info('typetest1'))
    message.set_unrecognized_field('typetest2', '', messages.Variant.STRING)
    self.assertEquals(('', messages.Variant.STRING),
                      message.get_unrecognized_field_info('typetest2'))

  def testPickle(self):
    """Testing pickling and unpickling of Message instances."""
    global MyEnum
    global AnotherMessage
    global MyMessage

    class MyEnum(messages.Enum):
      val1 = 1
      val2 = 2

    class AnotherMessage(messages.Message):
      string = messages.StringField(1, repeated=True)

    class MyMessage(messages.Message):
      field1 = messages.IntegerField(1)
      field2 = messages.EnumField(MyEnum, 2)
      field3 = messages.MessageField(AnotherMessage, 3)

    message = MyMessage(field1=1, field2=MyEnum.val2,
                        field3=AnotherMessage(string=['a', 'b', 'c']))
    message.set_unrecognized_field('exists', 'value', messages.Variant.STRING)
    message.set_unrecognized_field('repeated', ['list', 0, ('test',)],
                                   messages.Variant.STRING)
    unpickled = pickle.loads(pickle.dumps(message))
    self.assertEquals(message, unpickled)
    self.assertTrue(AnotherMessage.string is unpickled.field3.string.field)
    self.assertTrue('exists' in message.all_unrecognized_fields())
    self.assertEquals(('value', messages.Variant.STRING),
                      message.get_unrecognized_field_info('exists'))
    self.assertEquals((['list', 0, ('test',)], messages.Variant.STRING),
                      message.get_unrecognized_field_info('repeated'))


class FindDefinitionTest(test_util.TestCase):
  """Test finding definitions relative to various definitions and modules."""

  def setUp(self):
    """Set up module-space.  Starts off empty."""
    self.modules = {}

  def DefineModule(self, name):
    """Define a module and its parents in module space.

    Modules that are already defined in self.modules are not re-created.

    Args:
      name: Fully qualified name of modules to create.

    Returns:
      Deepest nested module.  For example:

        DefineModule('a.b.c')  # Returns c.
    """
    name_path = name.split('.')
    full_path = []
    for node in name_path:
      full_path.append(node)
      full_name = '.'.join(full_path)
      self.modules.setdefault(full_name, types.ModuleType(full_name))
    return self.modules[name]

  def DefineMessage(self, module, name, children={}, add_to_module=True):
    """Define a new Message class in the context of a module.

    Used for easily describing complex Message hierarchy.  Message is defined
    including all child definitions.

    Args:
      module: Fully qualified name of module to place Message class in.
      name: Name of Message to define within module.
      children: Define any level of nesting of children definitions.  To define
        a message, map the name to another dictionary.  The dictionary can
        itself contain additional definitions, and so on.  To map to an Enum,
        define the Enum class separately and map it by name.
      add_to_module: If True, new Message class is added to module.  If False,
        new Message is not added.
    """
    # Make sure module exists.
    module_instance = self.DefineModule(module)

    # Recursively define all child messages.
    for attribute, value in children.items():
      if isinstance(value, dict):
        children[attribute] = self.DefineMessage(
            module, attribute, value, False)

    # Override default __module__ variable.
    children['__module__'] = module

    # Instantiate and possibly add to module.
    message_class = type(name, (messages.Message,), dict(children))
    if add_to_module:
      setattr(module_instance, name, message_class)
    return message_class

  def Importer(self, module, globals='', locals='', fromlist=None):
    """Importer function.

    Acts like __import__.  Only loads modules from self.modules.  Does not
    try to load real modules defined elsewhere.  Does not try to handle relative
    imports.

    Args:
      module: Fully qualified name of module to load from self.modules.
    """
    if fromlist is None:
      module = module.split('.')[0]
    try:
      return self.modules[module]
    except KeyError:
      raise ImportError()

  def testNoSuchModule(self):
    """Test searching for definitions that do no exist."""
    self.assertRaises(messages.DefinitionNotFoundError,
                      messages.find_definition,
                      'does.not.exist',
                      importer=self.Importer)

  def testRefersToModule(self):
    """Test that referring to a module does not return that module."""
    self.DefineModule('i.am.a.module')
    self.assertRaises(messages.DefinitionNotFoundError,
                      messages.find_definition,
                      'i.am.a.module',
                      importer=self.Importer)

  def testNoDefinition(self):
    """Test not finding a definition in an existing module."""
    self.DefineModule('i.am.a.module')
    self.assertRaises(messages.DefinitionNotFoundError,
                      messages.find_definition,
                      'i.am.a.module.MyMessage',
                      importer=self.Importer)

  def testNotADefinition(self):
    """Test trying to fetch something that is not a definition."""
    module = self.DefineModule('i.am.a.module')
    setattr(module, 'A', 'a string')
    self.assertRaises(messages.DefinitionNotFoundError,
                      messages.find_definition,
                      'i.am.a.module.A',
                      importer=self.Importer)

  def testGlobalFind(self):
    """Test finding definitions from fully qualified module names."""
    A = self.DefineMessage('a.b.c', 'A', {})
    self.assertEquals(A, messages.find_definition('a.b.c.A',
                                                  importer=self.Importer))
    B = self.DefineMessage('a.b.c', 'B', {'C':{}})
    self.assertEquals(B.C, messages.find_definition('a.b.c.B.C',
                                                    importer=self.Importer))

  def testRelativeToModule(self):
    """Test finding definitions relative to modules."""
    # Define modules.
    a = self.DefineModule('a')
    b = self.DefineModule('a.b')
    c = self.DefineModule('a.b.c')

    # Define messages.
    A = self.DefineMessage('a', 'A')
    B = self.DefineMessage('a.b', 'B')
    C = self.DefineMessage('a.b.c', 'C')
    D = self.DefineMessage('a.b.d', 'D')

    # Find A, B, C and D relative to a.
    self.assertEquals(A, messages.find_definition(
        'A', a, importer=self.Importer))
    self.assertEquals(B, messages.find_definition(
        'b.B', a, importer=self.Importer))
    self.assertEquals(C, messages.find_definition(
        'b.c.C', a, importer=self.Importer))
    self.assertEquals(D, messages.find_definition(
        'b.d.D', a, importer=self.Importer))

    # Find A, B, C and D relative to b.
    self.assertEquals(A, messages.find_definition(
        'A', b, importer=self.Importer))
    self.assertEquals(B, messages.find_definition(
        'B', b, importer=self.Importer))
    self.assertEquals(C, messages.find_definition(
        'c.C', b, importer=self.Importer))
    self.assertEquals(D, messages.find_definition(
        'd.D', b, importer=self.Importer))

    # Find A, B, C and D relative to c.  Module d is the same case as c.
    self.assertEquals(A, messages.find_definition(
        'A', c, importer=self.Importer))
    self.assertEquals(B, messages.find_definition(
        'B', c, importer=self.Importer))
    self.assertEquals(C, messages.find_definition(
        'C', c, importer=self.Importer))
    self.assertEquals(D, messages.find_definition(
        'd.D', c, importer=self.Importer))

  def testRelativeToMessages(self):
    """Test finding definitions relative to Message definitions."""
    A = self.DefineMessage('a.b', 'A', {'B': {'C': {}, 'D': {}}})
    B = A.B
    C = A.B.C
    D = A.B.D

    # Find relative to A.
    self.assertEquals(A, messages.find_definition(
        'A', A, importer=self.Importer))
    self.assertEquals(B, messages.find_definition(
        'B', A, importer=self.Importer))
    self.assertEquals(C, messages.find_definition(
        'B.C', A, importer=self.Importer))
    self.assertEquals(D, messages.find_definition(
        'B.D', A, importer=self.Importer))

    # Find relative to B.
    self.assertEquals(A, messages.find_definition(
        'A', B, importer=self.Importer))
    self.assertEquals(B, messages.find_definition(
        'B', B, importer=self.Importer))
    self.assertEquals(C, messages.find_definition(
        'C', B, importer=self.Importer))
    self.assertEquals(D, messages.find_definition(
        'D', B, importer=self.Importer))

    # Find relative to C.
    self.assertEquals(A, messages.find_definition(
        'A', C, importer=self.Importer))
    self.assertEquals(B, messages.find_definition(
        'B', C, importer=self.Importer))
    self.assertEquals(C, messages.find_definition(
        'C', C, importer=self.Importer))
    self.assertEquals(D, messages.find_definition(
        'D', C, importer=self.Importer))

    # Find relative to C searching from c.
    self.assertEquals(A, messages.find_definition(
        'b.A', C, importer=self.Importer))
    self.assertEquals(B, messages.find_definition(
        'b.A.B', C, importer=self.Importer))
    self.assertEquals(C, messages.find_definition(
        'b.A.B.C', C, importer=self.Importer))
    self.assertEquals(D, messages.find_definition(
        'b.A.B.D', C, importer=self.Importer))

  def testAbsoluteReference(self):
    """Test finding absolute definition names."""
    # Define modules.
    a = self.DefineModule('a')
    b = self.DefineModule('a.a')

    # Define messages.
    aA = self.DefineMessage('a', 'A')
    aaA = self.DefineMessage('a.a', 'A')

    # Always find a.A.
    self.assertEquals(aA, messages.find_definition('.a.A', None,
                                                   importer=self.Importer))
    self.assertEquals(aA, messages.find_definition('.a.A', a,
                                                   importer=self.Importer))
    self.assertEquals(aA, messages.find_definition('.a.A', aA,
                                                   importer=self.Importer))
    self.assertEquals(aA, messages.find_definition('.a.A', aaA,
                                                   importer=self.Importer))

  def testFindEnum(self):
    """Test that Enums are found."""
    class Color(messages.Enum):
      pass
    A = self.DefineMessage('a', 'A', {'Color': Color})

    self.assertEquals(
        Color,
        messages.find_definition('Color', A, importer=self.Importer))

  def testFalseScope(self):
    """Test that Message definitions nested in strange objects are hidden."""
    global X
    class X(object):
      class A(messages.Message):
        pass

    self.assertRaises(TypeError, messages.find_definition, 'A', X)
    self.assertRaises(messages.DefinitionNotFoundError,
                      messages.find_definition,
                      'X.A', sys.modules[__name__])

  def testSearchAttributeFirst(self):
    """Make sure not faked out by module, but continues searching."""
    A = self.DefineMessage('a', 'A')
    module_A = self.DefineModule('a.A')

    self.assertEquals(A, messages.find_definition(
        'a.A', None, importer=self.Importer))


class FindDefinitionUnicodeTests(test_util.TestCase):

  # TODO(craigcitro): Fix this test and re-enable it.
  def notatestUnicodeString(self):
    """Test using unicode names."""
    from protorpc import registry
    self.assertEquals('ServiceMapping',
                      messages.find_definition(
                        u'protorpc.registry.ServiceMapping',
                        None).__name__)


def main():
  unittest.main()


if __name__ == '__main__':
  main()
