# -*- coding: utf-8 -*-
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc.  All rights reserved.
#
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd

"""Test decoder."""

import io
import unittest

from google.protobuf import message
from google.protobuf.internal import api_implementation
from google.protobuf.internal import decoder
from google.protobuf.internal import message_set_extensions_pb2
from google.protobuf.internal import testing_refleaks
from google.protobuf.internal import wire_format

from absl.testing import parameterized

_INPUT_BYTES = b'\x84r\x12'
_EXPECTED = (14596, 18)


@testing_refleaks.TestCase
class DecoderTest(parameterized.TestCase):

  def test_decode_varint_bytes(self):
    size, pos = decoder._DecodeVarint(_INPUT_BYTES, 0)
    self.assertEqual(size, _EXPECTED[0])
    self.assertEqual(pos, 2)

    size, pos = decoder._DecodeVarint(_INPUT_BYTES, 2)
    self.assertEqual(size, _EXPECTED[1])
    self.assertEqual(pos, 3)

  def test_decode_varint_bytes_empty(self):
    with self.assertRaises(IndexError) as context:
      decoder._DecodeVarint(b'', 0)
    self.assertIn('index out of range', str(context.exception))

  def test_decode_varint_bytesio(self):
    index = 0
    input_io = io.BytesIO(_INPUT_BYTES)
    while True:
      size = decoder._DecodeVarint(input_io)
      if size is None:
        break
      self.assertEqual(size, _EXPECTED[index])
      index += 1
    self.assertEqual(index, len(_EXPECTED))

  def test_decode_varint_bytesio_empty(self):
    input_io = io.BytesIO(b'')
    size = decoder._DecodeVarint(input_io)
    self.assertIsNone(size)

  def test_decode_unknown_group_field(self):
    data = memoryview(b'\013\020\003\014\040\005')
    parsed, pos = decoder._DecodeUnknownField(
        data, 1, len(data), 1, wire_format.WIRETYPE_START_GROUP
    )

    self.assertEqual(pos, 4)
    self.assertEqual(len(parsed), 1)
    self.assertEqual(parsed[0].field_number, 2)
    self.assertEqual(parsed[0].data, 3)

  def test_decode_unknown_group_field_nested(self):
    data = memoryview(b'\013\023\013\030\004\014\024\014\050\006')
    parsed, pos = decoder._DecodeUnknownField(
        data, 1, len(data), 1, wire_format.WIRETYPE_START_GROUP
    )

    self.assertEqual(pos, 8)
    self.assertEqual(len(parsed), 1)
    self.assertEqual(parsed[0].field_number, 2)
    self.assertEqual(len(parsed[0].data), 1)
    self.assertEqual(parsed[0].data[0].field_number, 1)
    self.assertEqual(len(parsed[0].data[0].data), 1)
    self.assertEqual(parsed[0].data[0].data[0].field_number, 3)
    self.assertEqual(parsed[0].data[0].data[0].data, 4)

  def test_decode_unknown_group_field_too_many_levels(self):
    data = memoryview(b'\023' * 5_000_000)
    self.assertRaisesRegex(
        message.DecodeError,
        'Error parsing message',
        decoder._DecodeUnknownField,
        data,
        1,
        len(data),
        1,
        wire_format.WIRETYPE_START_GROUP,
    )

  def test_decode_unknown_mismatched_end_group(self):
    self.assertRaisesRegex(
        message.DecodeError,
        'Missing group end tag.*',
        decoder._DecodeUnknownField,
        memoryview(b'\013\024'),
        1,
        2,
        1,
        wire_format.WIRETYPE_START_GROUP,
    )

  def test_decode_unknown_mismatched_end_group_nested(self):
    self.assertRaisesRegex(
        message.DecodeError,
        'Missing group end tag.*',
        decoder._DecodeUnknownField,
        memoryview(b'\013\023\034\024\014'),
        1,
        5,
        1,
        wire_format.WIRETYPE_START_GROUP,
    )

  def test_decode_message_set_unknown_mismatched_end_group(self):
    proto = message_set_extensions_pb2.TestMessageSet()
    self.assertRaisesRegex(
        message.DecodeError,
        'Unexpected end-group tag.'
        if api_implementation.Type() == 'python'
        else '.*',
        proto.ParseFromString,
        b'\013\054\014',
    )

  def test_unknown_message_set_decoder_mismatched_end_group(self):
    # This behavior isn't actually reachable in practice, but it's good to
    # test anyway.
    decode = decoder.UnknownMessageSetItemDecoder()
    self.assertRaisesRegex(
        message.DecodeError,
        'Unexpected end-group tag.',
        decode,
        memoryview(b'\054\014'),
    )

  @parameterized.parameters(int(0), float(0.0), False, '')
  def test_default_scalar(self, value):
    self.assertTrue(decoder.IsDefaultScalarValue(value))

  @parameterized.parameters(int(1), float(-0.0), float(1.0), True, 'a')
  def test_not_default_scalar(self, value):
    self.assertFalse(decoder.IsDefaultScalarValue(value))


if __name__ == '__main__':
  unittest.main()
