# MySQL Connector/Python - MySQL driver written in Python.
# Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.

# MySQL Connector/Python is licensed under the terms of the GPLv2
# <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
# MySQL Connectors. There are special exceptions to the terms and
# conditions of the GPLv2 as it is applied to this software, see the
# FOSS License Exception
# <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

"""Unittests for mysql.connector.protocol
"""

import struct
import datetime
import decimal

import tests
from mysql.connector import (protocol, errors)
from mysql.connector.constants import (ClientFlag, FieldType, FieldFlag)

OK_PACKET = bytearray(b'\x07\x00\x00\x01\x00\x01\x00\x00\x00\x01\x00')
OK_PACKET_RESULT = {
    'insert_id': 0,
    'affected_rows': 1,
    'field_count': 0,
    'warning_count': 1,
    'status_flag': 0
}

ERR_PACKET = bytearray(
    b'\x47\x00\x00\x02\xff\x15\x04\x23\x32\x38\x30\x30\x30'
    b'\x41\x63\x63\x65\x73\x73\x20\x64\x65\x6e\x69\x65\x64'
    b'\x20\x66\x6f\x72\x20\x75\x73\x65\x72\x20\x27\x68\x61'
    b'\x6d\x27\x40\x27\x6c\x6f\x63\x61\x6c\x68\x6f\x73\x74'
    b'\x27\x20\x28\x75\x73\x69\x6e\x67\x20\x70\x61\x73\x73'
    b'\x77\x6f\x72\x64\x3a\x20\x59\x45\x53\x29'
)

EOF_PACKET = bytearray(b'\x01\x00\x00\x00\xfe\x00\x00\x00\x00')
EOF_PACKET_RESULT = {'status_flag': 0, 'warning_count': 0}

SEED = bytearray(
    b'\x3b\x55\x78\x7d\x2c\x5f\x7c\x72\x49\x52'
    b'\x3f\x28\x47\x6f\x77\x28\x5f\x28\x46\x69'
)


class MySQLProtocolTests(tests.MySQLConnectorTests):
    def setUp(self):
        self._protocol = protocol.MySQLProtocol()

    def test_make_auth(self):
        """Make a MySQL authentication packet"""
        exp = {
            'allset': bytearray(
                b'\x8d\xa2\x03\x00\x00\x00\x00\x40'
                b'\x21\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
                b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
                b'\x68\x61\x6d\x00\x14\x3a\x07\x66\xba\xba\x01\xce'
                b'\xbe\x55\xe6\x29\x88\xaa\xae\xdb\x00\xb3\x4d\x91'
                b'\x5b\x74\x65\x73\x74\x00'),
            'nopass': bytearray(
                b'\x8d\xa2\x03\x00\x00\x00\x00\x40'
                b'\x21\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
                b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
                b'\x68\x61\x6d\x00\x00\x74\x65\x73\x74\x00'),
            'nouser': bytearray(
                b'\x8d\xa2\x03\x00\x00\x00\x00\x40'
                b'\x21\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
                b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
                b'\x00\x14\x3a\x07\x66\xba\xba\x01\xce'
                b'\xbe\x55\xe6\x29\x88\xaa\xae\xdb\x00\xb3\x4d\x91'
                b'\x5b\x74\x65\x73\x74\x00'),
            'nodb': bytearray(
                b'\x8d\xa2\x03\x00\x00\x00\x00\x40'
                b'\x21\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
                b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
                b'\x68\x61\x6d\x00\x14\x3a\x07\x66\xba\xba\x01\xce'
                b'\xbe\x55\xe6\x29\x88\xaa\xae\xdb\x00\xb3\x4d\x91'
                b'\x5b\x00'),
        }
        flags = ClientFlag.get_default()
        kwargs = {
            'handshake': None,
            'username': 'ham',
            'password': 'spam',
            'database': 'test',
            'charset': 33,
            'client_flags': flags
        }

        self.assertRaises(errors.ProgrammingError,
                          self._protocol.make_auth, **kwargs)

        kwargs['handshake'] = {'auth_data': SEED}
        self.assertRaises(errors.ProgrammingError,
                          self._protocol.make_auth, **kwargs)

        kwargs['handshake'] = {
            'auth_data': SEED,
            'auth_plugin': 'mysql_native_password'
        }
        res = self._protocol.make_auth(**kwargs)
        self.assertEqual(exp['allset'], res)

        kwargs['password'] = None
        res = self._protocol.make_auth(**kwargs)
        self.assertEqual(exp['nopass'], res)

        kwargs['password'] = 'spam'
        kwargs['database'] = None
        res = self._protocol.make_auth(**kwargs)
        self.assertEqual(exp['nodb'], res)

        kwargs['username'] = None
        kwargs['database'] = 'test'
        res = self._protocol.make_auth(**kwargs)
        self.assertEqual(exp['nouser'], res)

    def test_make_auth_ssl(self):
        """Make a SSL authentication packet"""
        cases = [
            ({},
             b'\x00\x00\x00\x00\x00\x00\x00\x40\x21\x00\x00\x00\x00\x00'
             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
             b'\x00\x00\x00\x00'),
            ({'charset': 8},
             b'\x00\x00\x00\x00\x00\x00\x00\x40\x08\x00\x00\x00\x00\x00'
             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
             b'\x00\x00\x00\x00'),
            ({'client_flags': 240141},
             b'\x0d\xaa\x03\x00\x00\x00\x00\x40\x21\x00\x00\x00\x00\x00'
             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
             b'\x00\x00\x00\x00'),
            ({'charset': 8, 'client_flags': 240141,
              'max_allowed_packet': 2147483648},
             b'\x0d\xaa\x03\x00\x00\x00\x00\x80\x08\x00\x00\x00\x00\x00'
             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
             b'\x00\x00\x00\x00'),
        ]
        for kwargs, exp in cases:
            self.assertEqual(exp, self._protocol.make_auth_ssl(**kwargs))

    def test_make_command(self):
        """Make a generic MySQL command packet"""
        exp = bytearray(b'\x01\x68\x61\x6d')
        arg = 'ham'.encode('utf-8')
        res = self._protocol.make_command(1, arg)
        self.assertEqual(exp, res)
        res = self._protocol.make_command(1, argument=arg)
        self.assertEqual(exp, res)

        exp = b'\x03'
        res = self._protocol.make_command(3)
        self.assertEqual(exp, res)

    def test_make_changeuser(self):
        """Make a change user MySQL packet"""
        exp = {
            'allset': bytearray(
                b'\x11\x68\x61\x6d\x00\x14\x3a\x07'
                b'\x66\xba\xba\x01\xce\xbe\x55\xe6\x29\x88\xaa\xae'
                b'\xdb\x00\xb3\x4d\x91\x5b\x74\x65\x73\x74\x00\x08'
                b'\x00'),
            'nopass': bytearray(
                b'\x11\x68\x61\x6d\x00\x00\x74\x65'
                b'\x73\x74\x00\x08\x00'),
        }
        kwargs = {
            'handshake': None,
            'username': 'ham',
            'password': 'spam',
            'database': 'test',
            'charset': 8,
            'client_flags': ClientFlag.get_default()
        }
        self.assertRaises(errors.ProgrammingError,
                          self._protocol.make_change_user, **kwargs)

        kwargs['handshake'] = {'auth_data': SEED}
        self.assertRaises(errors.ProgrammingError,
                          self._protocol.make_change_user, **kwargs)

        kwargs['handshake'] = {
            'auth_data': SEED,
            'auth_plugin': 'mysql_native_password'
        }
        res = self._protocol.make_change_user(**kwargs)
        self.assertEqual(exp['allset'], res)

        kwargs['password'] = None
        res = self._protocol.make_change_user(**kwargs)
        self.assertEqual(exp['nopass'], res)

    def test_parse_handshake(self):
        """Parse handshake-packet sent by MySQL"""
        handshake = bytearray(
            b'\x47\x00\x00\x00\x0a\x35\x2e\x30\x2e\x33\x30\x2d'
            b'\x65\x6e\x74\x65\x72\x70\x72\x69\x73\x65\x2d\x67'
            b'\x70\x6c\x2d\x6c\x6f\x67\x00\x09\x01\x00\x00\x68'
            b'\x34\x69\x36\x6f\x50\x21\x4f\x00\x2c\xa2\x08\x02'
            b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
            b'\x00\x00\x4c\x6e\x67\x39\x26\x50\x44\x40\x57\x72'
            b'\x59\x48\x00'
        )
        exp = {
            'protocol': 10,
            'server_version_original': '5.0.30-enterprise-gpl-log',
            'charset': 8,
            'server_threadid': 265,
            'capabilities': 41516,
            'server_status': 2,
            'auth_data': b'h4i6oP!OLng9&PD@WrYH',
            'auth_plugin': 'mysql_native_password',
        }

        res = self._protocol.parse_handshake(handshake)
        self.assertEqual(exp, res)

        # Test when end byte \x00 is not present for server 5.5.8
        handshake = handshake[:-1]
        res = self._protocol.parse_handshake(handshake)
        self.assertEqual(exp, res)

    def test_parse_ok(self):
        """Parse OK-packet sent by MySQL"""
        res = self._protocol.parse_ok(OK_PACKET)
        self.assertEqual(OK_PACKET_RESULT, res)

        okpkt = OK_PACKET + b'\x04spam'
        exp = OK_PACKET_RESULT.copy()
        exp['info_msg'] = 'spam'
        res = self._protocol.parse_ok(okpkt)
        self.assertEqual(exp, res)

    def test_parse_column_count(self):
        """Parse the number of columns"""
        packet = bytearray(b'\x01\x00\x00\x01\x03')
        res = self._protocol.parse_column_count(packet)
        self.assertEqual(3, res)

        packet = bytearray(b'\x01\x00')
        self.assertRaises(errors.InterfaceError,
                          self._protocol.parse_column_count, packet)

    def test_parse_column(self):
        """Parse field-packet sent by MySQL"""
        column_packet = bytearray(
            b'\x1a\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x04'
            b'\x53\x70\x61\x6d\x00\x0c\x21\x00\x09\x00\x00\x00'
            b'\xfd\x01\x00\x1f\x00\x00')
        exp = ('Spam', 253, None, None, None, None, 0, 1)
        res = self._protocol.parse_column(column_packet)
        self.assertEqual(exp, res)

    def test_parse_eof(self):
        """Parse EOF-packet sent by MySQL"""
        res = self._protocol.parse_eof(EOF_PACKET)
        self.assertEqual(EOF_PACKET_RESULT, res)

    def test_read_text_result(self):
        # Tested by MySQLConnectionTests.test_get_rows() and .test_get_row()
        pass

    def test_parse_binary_prepare_ok(self):
        """Parse Prepare OK packet"""
        cases = [
            # SELECT CONCAT(?, ?) AS c1
            (bytearray(b'\x0c\x00\x00\x01'
                       b'\x00\x01\x00\x00\x00\x01\x00\x02\x00\x00\x00\x00'),
             {'num_params': 2,
              'statement_id': 1,
              'warning_count': 0,
              'num_columns': 1
             }
            ),
            # DO 1
            (bytearray(b'\x0c\x00\x00\x01'
                       b'\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'),
             {'num_params': 0,
              'statement_id': 1,
              'warning_count': 0,
              'num_columns': 0
             }
            ),
        ]
        for packet, exp in cases:
            self.assertEqual(exp,
                             self._protocol.parse_binary_prepare_ok(packet))

    def test__parse_binary_integer(self):
        """Parse an integer from a binary packet"""
        # Case = Expected value; pack format; field type; field flag
        cases = [
            (-128, 'b', FieldType.TINY, 0),
            (-32768, 'h', FieldType.SHORT, 0),
            (-2147483648, 'i', FieldType.LONG, 0),
            (-9999999999, 'q', FieldType.LONGLONG, 0),
            (255, 'B', FieldType.TINY, FieldFlag.UNSIGNED),
            (65535, 'H', FieldType.SHORT, FieldFlag.UNSIGNED),
            (4294967295, 'I', FieldType.LONG, FieldFlag.UNSIGNED),
            (9999999999, 'Q', FieldType.LONGLONG, FieldFlag.UNSIGNED),
        ]
        field_info = [None] * 8
        field_info[0] = 'c1'
        for exp, fmt, field_type, flag in cases:
            field_info[1] = field_type
            field_info[7] = flag
            data = struct.pack(fmt, exp) + b'\x00\x00'
            res = self._protocol._parse_binary_integer(data, field_info)
            self.assertEqual((b'\x00\x00', exp), res,
                             "Failed parsing binary integer '{0}'".format(exp))

    def test__parse_binary_float(self):
        """Parse a float/double from a binary packet"""
        # Case = Expected value; data; field type
        cases = [
            (-3.14159, bytearray(b'\x6e\x86\x1b\xf0\xf9\x21\x09\xc0'),
             FieldType.DOUBLE),
            (3.14159, bytearray(b'\x6e\x86\x1b\xf0\xf9\x21\x09\x40'),
             FieldType.DOUBLE),
            (-3.14, bytearray(b'\xc3\xf5\x48\xc0'), FieldType.FLOAT),
            (3.14, bytearray(b'\xc3\xf5\x48\x40'), FieldType.FLOAT),
        ]
        field_info = [None] * 8
        field_info[0] = 'c1'
        for exp, data, field_type in cases:
            field_info[1] = field_type
            res = self._protocol._parse_binary_float(data + b'\x00\x00',
                                                     field_info)
            self.assertEqual(bytearray(b'\x00\x00'), res[0],
                             "Failed parsing binary float '{0}'".format(exp))
            self.assertAlmostEqual(
                exp, res[1], places=5,
                msg="Failed parsing binary float '{0}'".format(exp))

    def test__parse_binary_timestamp(self):
        """Parse a timestamp from a binary packet"""
        # Case = Expected value; data
        cases = [
            (datetime.date(1977, 6, 14), bytearray(b'\x04\xb9\x07\x06\x0e')),
            (datetime.datetime(1977, 6, 14, 21, 33, 14),
             bytearray(b'\x07\xb9\x07\x06\x0e\x15\x21\x0e')),
            (datetime.datetime(1977, 6, 14, 21, 33, 14, 345),
             bytearray(b'\x0b\xb9\x07\x06\x0e\x15\x21\x0e\x59\x01\x00\x00'))
        ]
        for exp, data in cases:
            res = self._protocol._parse_binary_timestamp(data + b'\x00\x00',
                                                         None)
            self.assertEqual((b'\x00\x00', exp), res,
                             "Failed parsing timestamp '{0}'".format(exp))

    def test__parse_binary_time(self):
        """Parse a time value from a binary packet"""
        cases = [
            (datetime.timedelta(0, 44130),
             bytearray(b'\x08\x00\x00\x00\x00\x00\x0c\x0f\x1e')),
            (datetime.timedelta(14, 15330),
             bytearray(b'\x08\x00\x0e\x00\x00\x00\x04\x0f\x1e')),
            (datetime.timedelta(-14, 15330),
             bytearray(b'\x08\x01\x0e\x00\x00\x00\x04\x0f\x1e')),
            (datetime.timedelta(10, 58530, 230000),
             bytearray(b'\x0c\x00\x0a\x00\x00\x00'
                       b'\x10\x0f\x1e\x70\x82\x03\x00')),
        ]
        for exp, data in cases:
            res = self._protocol._parse_binary_time(data + b'\x00\x00', None)
            self.assertEqual((bytearray(b'\x00\x00'), exp), res,
                             "Failed parsing time '{0}'".format(exp))

    def test__parse_binary_values(self):
        """Parse values from a binary result packet"""
        # The packet in this test is result of the following query:
        #       SELECT 'abc' AS aStr,"
        #        "3.14 AS aFloat,"
        #        "-3.14159 AS aDouble, "
        #        "MAKEDATE(2003, 31) AS aDate, "
        #        "TIMESTAMP('1977-06-14', '21:33:14') AS aDateTime, "
        #        "MAKETIME(256,15,30.23) AS aTime, "
        #        "NULL AS aNull"
        #

        fields = [('aStr', 253, None, None, None, None, 0, 1),
                  ('aFloat', 246, None, None, None, None, 0, 129),
                  ('aDouble', 246, None, None, None, None, 0, 129),
                  ('aDate', 10, None, None, None, None, 1, 128),
                  ('aDateTime', 12, None, None, None, None, 1, 128),
                  ('aTime', 11, None, None, None, None, 1, 128),
                  ('aNull', 6, None, None, None, None, 1, 128)]

        packet = bytearray(b'\x00\x01\x03\x61\x62\x63\x04\x33\x2e\x31\x34\x08'
                           b'\x2d\x33\x2e\x31\x34\x31\x35\x39\x04\xd3\x07'
                           b'\x01\x1f\x07\xb9\x07\x06\x0e\x15\x21\x0e\x0c'
                           b'\x00\x0a\x00\x00\x00\x10\x0f\x1e\x70\x82\x03\x00')

        # float/double are returned as DECIMAL by MySQL
        exp = (bytearray(b'abc'),
               bytearray(b'3.14'),
               bytearray(b'-3.14159'),
               datetime.date(2003, 1, 31),
               datetime.datetime(1977, 6, 14, 21, 33, 14),
               datetime.timedelta(10, 58530, 230000),
               None)
        res = self._protocol._parse_binary_values(fields, packet)
        self.assertEqual(exp, res)

    def test_read_binary_result(self):
        """Read MySQL binary protocol result"""

    def test__prepare_binary_integer(self):
        """Prepare an integer for the MySQL binary protocol"""
        # Case = Data; expected value
        cases = [
            (-128, (struct.pack('b', -128), FieldType.TINY, 0)),
            (-32768, (struct.pack('h', -32768), FieldType.SHORT, 0)),
            (-2147483648,
             (struct.pack('i', -2147483648), FieldType.LONG, 0)),
            (-9999999999,
             (struct.pack('q', -9999999999), FieldType.LONGLONG, 0)),

            (255, (struct.pack('B', 255), FieldType.TINY, 128)),
            (65535, (struct.pack('H', 65535), FieldType.SHORT, 128)),
            (4294967295,
             (struct.pack('I', 4294967295), FieldType.LONG, 128)),
            (9999999999,
             (struct.pack('Q', 9999999999), FieldType.LONGLONG, 128)),
        ]
        for data, exp in cases:
            res = self._protocol._prepare_binary_integer(data)
            self.assertEqual(exp, res,
                             "Failed preparing value '{0}'".format(data))

    def test__prepare_binary_timestamp(self):
        """Prepare a timestamp object for the MySQL binary protocol"""
        cases = [
            (datetime.date(1977, 6, 14),
             (bytearray(b'\x04\xb9\x07\x06\x0e'), 10)),
            (datetime.datetime(1977, 6, 14),
             (bytearray(b'\x07\xb9\x07\x06\x0e\x00\x00\x00'), 12)),
            (datetime.datetime(1977, 6, 14, 21, 33, 14),
             (bytearray(b'\x07\xb9\x07\x06\x0e\x15\x21\x0e'), 12)),
            (datetime.datetime(1977, 6, 14, 21, 33, 14, 345),
             (bytearray(b'\x0b\xb9\x07\x06\x0e\x15'
                        b'\x21\x0e\x59\x01\x00\x00'), 12)),
        ]
        for data, exp in cases:
            self.assertEqual(exp,
                             self._protocol._prepare_binary_timestamp(data),
                             "Failed preparing value '{0}'".format(data))

        # Raise an error
        self.assertRaises(ValueError,
                          self._protocol._prepare_binary_timestamp, 'spam')

    def test__prepare_binary_time(self):
        """Prepare a time object for the MySQL binary protocol"""
        cases = [
            (datetime.timedelta(hours=123, minutes=45, seconds=16),
             (bytearray(b'\x08\x00\x05\x00\x00\x00\x03\x2d\x10'), 11)),
            (datetime.timedelta(hours=-123, minutes=45, seconds=16),
             (bytearray(b'\x08\x01\x06\x00\x00\x00\x15\x2d\x10'), 11)),
            (datetime.timedelta(hours=123, minutes=45, seconds=16,
                                microseconds=345),
             (bytearray(b'\x0c\x00\x05\x00\x00\x00\x03'
                        b'\x2d\x10\x59\x01\x00\x00'), 11)),
            (datetime.timedelta(days=123, minutes=45, seconds=16),
             (bytearray(b'\x08\x00\x7b\x00\x00\x00\x00\x2d\x10'), 11)),
            (datetime.time(14, 53, 36),
             (bytearray(b'\x08\x00\x00\x00\x00\x00\x0e\x35\x24'), 11)),
            (datetime.time(14, 53, 36, 345),
             (bytearray(b'\x0c\x00\x00\x00\x00\x00\x0e'
                        b'\x35\x24\x59\x01\x00\x00'), 11))
        ]
        for data, exp in cases:
            self.assertEqual(exp,
                             self._protocol._prepare_binary_time(data),
                             "Failed preparing value '{0}'".format(data))

        # Raise an error
        self.assertRaises(ValueError,
                          self._protocol._prepare_binary_time, 'spam')

    def test_make_stmt_execute(self):
        """Make a MySQL packet with the STMT_EXECUTE command"""
        statement_id = 1
        self.assertRaises(errors.InterfaceError,
                          self._protocol.make_stmt_execute, statement_id,
                          ('ham', 'spam'), (1, 2, 3))

        data = ('ham', 'spam')
        exp = bytearray(
            b'\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x0f'
            b'\x00\x0f\x00\x03\x68\x61\x6d\x04\x73\x70\x61\x6d'
        )
        res = self._protocol.make_stmt_execute(statement_id, data, (1, 2))
        self.assertEqual(exp, res)

        # Testing types
        cases = [
            ('ham',
             bytearray(b'\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00'
                       b'\x01\x0f\x00\x03\x68\x61\x6d')),
            (decimal.Decimal('3.14'),
             bytearray(b'\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00'
                       b'\x01\x00\x00\x04\x33\x2e\x31\x34')),
            (255,
             bytearray(b'\x01\x00\x00\x00\x80\x01\x00'
                       b'\x00\x00\x00\x01\x01\x80\xff')),
            (-128,
             bytearray(b'\x01\x00\x00\x00\x00\x01\x00'
                       b'\x00\x00\x00\x01\x01\x00\x80')),
            (datetime.datetime(1977, 6, 14, 21, 20, 30),
             bytearray(b'\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00'
                       b'\x01\x0c\x00\x07\xb9\x07\x06\x0e\x15\x14\x1e')),
            (datetime.time(14, 53, 36, 345),
             bytearray(b'\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x0b\x00'
                       b'\x0c\x00\x00\x00\x00\x00\x0e\x35\x24\x59\x01\x00\x00')),
            (3.14,
             bytearray(b'\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x05\x00'
                       b'\x1f\x85\xeb\x51\xb8\x1e\x09\x40')),
        ]
        for data, exp in cases:
            res = self._protocol.make_stmt_execute(statement_id, (data,), (1,))
            self.assertEqual(
                exp, res, "Failed preparing statement with '{0}'".format(data))

        # Testing null bitmap
        data = (None, None)
        exp = bytearray(b'\x01\x00\x00\x00\x00\x01\x00\x00\x00\x03\x01\x06'
                        b'\x00\x06\x00')
        res = self._protocol.make_stmt_execute(statement_id, data, (1, 2))
        self.assertEqual(exp, res)

        data = (None, 'Ham')
        exp = bytearray(
            b'\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x01\x06\x00\x0f\x00'
            b'\x03\x48\x61\x6d'
        )
        res = self._protocol.make_stmt_execute(statement_id, data, (1, 2))
        self.assertEqual(exp, res)

        data = ('a',) * 11
        exp = bytearray(
            b'\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01'
            b'\x0f\x00\x0f\x00\x0f\x00\x0f\x00\x0f\x00\x0f\x00\x0f\x00'
            b'\x0f\x00\x0f\x00\x0f\x00\x0f\x00\x01\x61\x01\x61\x01\x61'
            b'\x01\x61\x01\x61\x01\x61\x01\x61\x01\x61\x01\x61\x01\x61'
            b'\x01\x61'
        )
        res = self._protocol.make_stmt_execute(statement_id, data, (1,) * 11)
        self.assertEqual(exp, res)

        # Raise an error passing an unsupported object as parameter value
        class UnSupportedObject(object):
            pass

        data = (UnSupportedObject(), UnSupportedObject())
        self.assertRaises(errors.ProgrammingError,
                          self._protocol.make_stmt_execute,
                          statement_id, data, (1, 2))
