#!/usr/bin/env python
#
# Copyright 2014 Docalytics Inc, 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 pytracts.messages."""
from tests import test_util

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

import new
import re
import sys
import unittest

from pytracts import message_types
from pytracts import messages


class ModuleInterfaceTest(test_util.ModuleInterfaceTest, test_util.TestCase):
    MODULE = messages


class ValidationErrorTest(test_util.TestCase):
    def test_str_no_field_name(self):
        """Test string version of ValidationError when no name provided."""
        self.assertEquals('Validation error',str(messages.ValidationError('Validation error')))

    def test_str_field_name(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(
            {'BLUE', 'GREEN', 'INDIGO', 'ORANGE', 'RED', 'VIOLET', 'YELLOW'},
            set(Color.names()))

    def testNumbers(self):
        """Tests that numbers iterates of enum numbers."""
        self.assertEquals({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),
                          {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))
        self.assertEquals(Color.RED, Color(20L))
        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(unicode, 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)


class FieldListTest(test_util.TestCase):
    def setUp(self):
        self.integer_field = messages.IntegerField(repeated=True)

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

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

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

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

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

    def testConstructor_InvalidValues(self):
        self.assertRaisesWithRegexpMatch(
            messages.ValidationError,
            re.escape("Expected type (<type 'int'>, <type 'long'>) "
                      "for IntegerField, found 1 (type <type 'str'>)"),
            messages.FieldList, None, self.integer_field, ["1", "2", "3"])

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

        self.assertRaisesWithRegexpMatch(
            messages.ValidationError,
            "IntegerField is repeated. Found: <listiterator object",
            messages.FieldList, None, self.integer_field, iter([1, 2, 3]))

    def testSetSlice(self):
        field_list = messages.FieldList(None, 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(None, self.integer_field, [1, 2, 3, 4, 5])

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

        self.assertRaisesWithRegexpMatch(
            messages.ValidationError,
            re.escape("Expected type (<type 'int'>, <type 'long'>) "
                      "for IntegerField, found 10 (type <type 'str'>)"),
            setslice)

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

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

        def setitem():
            field_list[0] = '10'

        self.assertRaisesWithRegexpMatch(
            messages.ValidationError,
            re.escape("Expected type (<type 'int'>, <type 'long'>) "
                      "for IntegerField, found 10 (type <type 'str'>)"),
            setitem)

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

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

        def append():
            field_list.append('10')

        self.assertRaisesWithRegexpMatch(
            messages.ValidationError,
            re.escape("Expected type (<type 'int'>, <type 'long'>) "
                      "for IntegerField, found 10 (type <type 'str'>)"),
            append)

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

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

        def extend():
            field_list.extend(['10'])

        self.assertRaisesWithRegexpMatch(
            messages.ValidationError,
            re.escape("Expected type (<type 'int'>, <type 'long'>) "
                      "for IntegerField, found 10 (type <type 'str'>)"),
            extend)

    def testInsert(self):
        field_list = messages.FieldList(None, 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(None, self.integer_field, [2, 3])

        def insert():
            field_list.insert(1, '10')

        self.assertRaisesWithRegexpMatch(
            messages.ValidationError,
            re.escape("Expected type (<type 'int'>, <type 'long'>) "
                      "for IntegerField, found 10 (type <type 'str'>)"),
            insert)


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 testRequiredAndRepeated(self):
        """Test setting the required and repeated fields."""

        def action(field_class):
            field_class(required=True)
            field_class(repeated=True)
            self.assertRaises(messages.FieldDefinitionError,
                              field_class,
                              required=True,
                              repeated=True)

        self.ActionOnAllFieldClasses(action)

    def testInvalidVariant(self):
        """Test field with invalid variants."""

        def action(field_class):
            if field_class is not messages.DateTimeISO8601Field and field_class is not messages.DateTimeMsIntegerField:
                self.assertRaises(messages.InvalidVariantError,
                                  field_class,
                                  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()
            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(variant=messages.Variant.UINT32)
        self.assertEquals(messages.Variant.UINT32, field.variant)

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

        def action(field_class):
            field_class(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,
            'Invalid default value for StringField: \211: '
            'Field encountered non-ASCII string \211:',
            messages.StringField, default='\x89')

    def testDefaultFields_InvalidSingle(self):
        """Test default field is correct type."""

        def action(field_class):
            self.assertRaises(messages.InvalidDefaultError,
                              field_class,
                              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, repeated=True, default=[1, 2, 3])

    def testDefaultFields_None(self):
        """Test none is always acceptable."""

        def action(field_class):
            field_class(default=None)
            field_class(required=True, default=None)
            field_class(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, default=Symbol.ALPHA)

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

    def testValidate_Valid(self):
        """Test validation of valid values."""
        values = {messages.IntegerField: 10,
                  messages.FloatField: 1.5,
                  messages.BooleanField: False,
                  messages.BytesField: 'abc',
                  messages.StringField: u'abc',
        }

        def action(field_class):
            # Optional.
            field = field_class()
            field.validate(values[field_class])

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

            # Repeated.
            field = field_class(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: 1,
                  messages.BooleanField: 0,
                  messages.BytesField: 10.20,
                  messages.StringField: 42,
        }

        def action(field_class):
            # Optional.
            field = field_class()
            self.assertRaises(messages.ValidationError,
                              field.validate,
                              values[field_class])

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

            # Repeated.
            field = field_class(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()
            field.validate(None)

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

            # Repeated.
            field = field_class(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,
                  messages.FloatField: 1.5,
                  messages.BooleanField: False,
                  messages.BytesField: 'abc',
                  messages.StringField: u'abc',
        }

        def action(field_class):
            # Optional.
            field = field_class(1)
            field.validate_element(values[field_class])

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

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

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

    def testReadOnly(self):
        """Test that objects are all read-only."""

        def action(field_class):
            field = field_class()
            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)

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

        class MyMessage(messages.Message):
            pass

        field = messages.MessageField(MyMessage)
        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')
                forward = messages.MessageField('ForwardMessage')
                nested = messages.MessageField('ForwardMessage.NestedMessage')
                inner = messages.MessageField('Inner')

                class Inner(messages.Message):
                    sibling = messages.MessageField('Sibling')

                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')

            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)
        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)

        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)

        instance = MyMessage()

        self.assertIs(instance, HasMessage.field.value_from_message(instance))

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

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

        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)

        instance = MyMessage()

        self.assertIs(instance, 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)

        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."""
        messages.IntegerField(default=long(10))

    def testMessageFieldValidate_Initialized(self):
        """Test validation on message field."""

        class MyMessage(messages.Message):
            field1 = messages.IntegerField(required=True)

        field = messages.MessageField(MyMessage)

        # 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)

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

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

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

        class Another(messages.Enum):
            VALUE = 1

        self.assertRaises(messages.InvalidDefaultError,
                          messages.EnumField,
                          Color,
                          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')
                nested = messages.EnumField('ForwardMessage.NestedEnum')
                inner = messages.EnumField('Inner')

                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')

            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()

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

    def testNoneAssignment(self):
        """Test that assigning None does changes comparison."""

        class MyMessage(messages.Message):
            my_field = messages.StringField()

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

    def testNonAsciiStr(self):
        """Test validation fails for non-ascii StringField values."""

        class Thing(messages.Message):
            string_field = messages.StringField()

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

    def testCoerceValue(self):
        class Person(messages.Message):
            weight = messages.IntegerField()

        p = Person()
        p.weight = Person.weight.coerce(123)
        self.assertEquals(123, p.weight)

        p.weight = Person.weight.coerce(123.45)
        self.assertEquals(123, p.weight)

        p.weight = Person.weight.coerce('123')
        self.assertEquals(123, p.weight)

        p.weight = Person.weight.coerce(None)
        self.assertEquals(None, p.weight)

    def testCoerceValueInvalid(self):
        class Person(messages.Message):
            weight = messages.IntegerField()

        self.assertRaises(ValueError, lambda: Person.weight.coerce({}))

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()
            b1 = messages.StringField()
            c2 = messages.StringField()

        return ComplexMessage

    def testStrictAssignment(self):
        """Tests that cannot assign to unknown or non-reserved attributes."""

        class SimpleMessage(messages.Message):
            field = messages.IntegerField()

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

    def testListAssignmentDoesNotCopy(self):
        class SimpleMessage(messages.Message):
            repeated = messages.IntegerField(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()

        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(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(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(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(required=True)

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

        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(required=True)

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

        class RepeatedMessage(messages.Message):
            simple = messages.MessageField(SimpleMessage, 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,